guppylang-internals 0.21.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. guppylang_internals/__init__.py +3 -0
  2. guppylang_internals/ast_util.py +350 -0
  3. guppylang_internals/cfg/__init__.py +0 -0
  4. guppylang_internals/cfg/analysis.py +230 -0
  5. guppylang_internals/cfg/bb.py +221 -0
  6. guppylang_internals/cfg/builder.py +606 -0
  7. guppylang_internals/cfg/cfg.py +117 -0
  8. guppylang_internals/checker/__init__.py +0 -0
  9. guppylang_internals/checker/cfg_checker.py +388 -0
  10. guppylang_internals/checker/core.py +550 -0
  11. guppylang_internals/checker/errors/__init__.py +0 -0
  12. guppylang_internals/checker/errors/comptime_errors.py +106 -0
  13. guppylang_internals/checker/errors/generic.py +45 -0
  14. guppylang_internals/checker/errors/linearity.py +300 -0
  15. guppylang_internals/checker/errors/type_errors.py +344 -0
  16. guppylang_internals/checker/errors/wasm.py +34 -0
  17. guppylang_internals/checker/expr_checker.py +1413 -0
  18. guppylang_internals/checker/func_checker.py +269 -0
  19. guppylang_internals/checker/linearity_checker.py +821 -0
  20. guppylang_internals/checker/stmt_checker.py +447 -0
  21. guppylang_internals/compiler/__init__.py +0 -0
  22. guppylang_internals/compiler/cfg_compiler.py +233 -0
  23. guppylang_internals/compiler/core.py +613 -0
  24. guppylang_internals/compiler/expr_compiler.py +989 -0
  25. guppylang_internals/compiler/func_compiler.py +97 -0
  26. guppylang_internals/compiler/hugr_extension.py +224 -0
  27. guppylang_internals/compiler/qtm_platform_extension.py +0 -0
  28. guppylang_internals/compiler/stmt_compiler.py +212 -0
  29. guppylang_internals/decorator.py +246 -0
  30. guppylang_internals/definition/__init__.py +0 -0
  31. guppylang_internals/definition/common.py +214 -0
  32. guppylang_internals/definition/const.py +74 -0
  33. guppylang_internals/definition/custom.py +492 -0
  34. guppylang_internals/definition/declaration.py +171 -0
  35. guppylang_internals/definition/extern.py +89 -0
  36. guppylang_internals/definition/function.py +302 -0
  37. guppylang_internals/definition/overloaded.py +150 -0
  38. guppylang_internals/definition/parameter.py +82 -0
  39. guppylang_internals/definition/pytket_circuits.py +405 -0
  40. guppylang_internals/definition/struct.py +392 -0
  41. guppylang_internals/definition/traced.py +151 -0
  42. guppylang_internals/definition/ty.py +51 -0
  43. guppylang_internals/definition/value.py +115 -0
  44. guppylang_internals/definition/wasm.py +61 -0
  45. guppylang_internals/diagnostic.py +523 -0
  46. guppylang_internals/dummy_decorator.py +76 -0
  47. guppylang_internals/engine.py +295 -0
  48. guppylang_internals/error.py +107 -0
  49. guppylang_internals/experimental.py +92 -0
  50. guppylang_internals/ipython_inspect.py +28 -0
  51. guppylang_internals/nodes.py +427 -0
  52. guppylang_internals/py.typed +0 -0
  53. guppylang_internals/span.py +150 -0
  54. guppylang_internals/std/__init__.py +0 -0
  55. guppylang_internals/std/_internal/__init__.py +0 -0
  56. guppylang_internals/std/_internal/checker.py +573 -0
  57. guppylang_internals/std/_internal/compiler/__init__.py +0 -0
  58. guppylang_internals/std/_internal/compiler/arithmetic.py +136 -0
  59. guppylang_internals/std/_internal/compiler/array.py +569 -0
  60. guppylang_internals/std/_internal/compiler/either.py +131 -0
  61. guppylang_internals/std/_internal/compiler/frozenarray.py +68 -0
  62. guppylang_internals/std/_internal/compiler/futures.py +30 -0
  63. guppylang_internals/std/_internal/compiler/list.py +348 -0
  64. guppylang_internals/std/_internal/compiler/mem.py +13 -0
  65. guppylang_internals/std/_internal/compiler/option.py +78 -0
  66. guppylang_internals/std/_internal/compiler/prelude.py +271 -0
  67. guppylang_internals/std/_internal/compiler/qsystem.py +48 -0
  68. guppylang_internals/std/_internal/compiler/quantum.py +118 -0
  69. guppylang_internals/std/_internal/compiler/tket_bool.py +55 -0
  70. guppylang_internals/std/_internal/compiler/tket_exts.py +59 -0
  71. guppylang_internals/std/_internal/compiler/wasm.py +135 -0
  72. guppylang_internals/std/_internal/compiler.py +0 -0
  73. guppylang_internals/std/_internal/debug.py +95 -0
  74. guppylang_internals/std/_internal/util.py +271 -0
  75. guppylang_internals/tracing/__init__.py +0 -0
  76. guppylang_internals/tracing/builtins_mock.py +62 -0
  77. guppylang_internals/tracing/frozenlist.py +57 -0
  78. guppylang_internals/tracing/function.py +186 -0
  79. guppylang_internals/tracing/object.py +551 -0
  80. guppylang_internals/tracing/state.py +69 -0
  81. guppylang_internals/tracing/unpacking.py +194 -0
  82. guppylang_internals/tracing/util.py +86 -0
  83. guppylang_internals/tys/__init__.py +0 -0
  84. guppylang_internals/tys/arg.py +115 -0
  85. guppylang_internals/tys/builtin.py +382 -0
  86. guppylang_internals/tys/common.py +110 -0
  87. guppylang_internals/tys/const.py +114 -0
  88. guppylang_internals/tys/errors.py +178 -0
  89. guppylang_internals/tys/param.py +251 -0
  90. guppylang_internals/tys/parsing.py +425 -0
  91. guppylang_internals/tys/printing.py +174 -0
  92. guppylang_internals/tys/subst.py +112 -0
  93. guppylang_internals/tys/ty.py +876 -0
  94. guppylang_internals/tys/var.py +49 -0
  95. guppylang_internals-0.21.0.dist-info/METADATA +253 -0
  96. guppylang_internals-0.21.0.dist-info/RECORD +98 -0
  97. guppylang_internals-0.21.0.dist-info/WHEEL +4 -0
  98. guppylang_internals-0.21.0.dist-info/licenses/LICENCE +201 -0
@@ -0,0 +1,194 @@
1
+ from typing import Any, TypeVar
2
+
3
+ from hugr import ops
4
+ from hugr import tys as ht
5
+ from hugr.build.dfg import DfBase
6
+
7
+ from guppylang_internals.ast_util import AstNode
8
+ from guppylang_internals.checker.errors.comptime_errors import (
9
+ IllegalComptimeExpressionError,
10
+ )
11
+ from guppylang_internals.checker.expr_checker import python_value_to_guppy_type
12
+ from guppylang_internals.compiler.core import CompilerContext
13
+ from guppylang_internals.compiler.expr_compiler import python_value_to_hugr
14
+ from guppylang_internals.error import GuppyComptimeError, GuppyError
15
+ from guppylang_internals.std._internal.compiler.array import array_new, unpack_array
16
+ from guppylang_internals.std._internal.compiler.prelude import build_unwrap
17
+ from guppylang_internals.tracing.frozenlist import frozenlist
18
+ from guppylang_internals.tracing.object import (
19
+ GuppyObject,
20
+ GuppyStructObject,
21
+ TracingDefMixin,
22
+ )
23
+ from guppylang_internals.tracing.state import get_tracing_state
24
+ from guppylang_internals.tys.builtin import (
25
+ array_type,
26
+ get_array_length,
27
+ get_element_type,
28
+ is_array_type,
29
+ )
30
+ from guppylang_internals.tys.const import ConstValue
31
+ from guppylang_internals.tys.ty import NoneType, StructType, TupleType
32
+
33
+ P = TypeVar("P", bound=ops.DfParentOp)
34
+
35
+
36
+ def unpack_guppy_object(
37
+ obj: GuppyObject, builder: DfBase[P], frozen: bool = False
38
+ ) -> Any:
39
+ """Tries to turn as much of the structure of a GuppyObject into Python objects.
40
+
41
+ For example, Guppy tuples are turned into Python tuples and Guppy arrays are turned
42
+ into Python lists. This is achieved by inserting unpacking operations into the Hugr
43
+ to get individual wires to be used in those Python objects.
44
+
45
+ Setting `frozen=True` ensures that the resulting Python objects are not mutable in-
46
+ place. This should be set for objects that originate from function inputs that are
47
+ not borrowed.
48
+ """
49
+ match obj._ty:
50
+ case NoneType():
51
+ return None
52
+ case TupleType(element_types=tys):
53
+ unpack = builder.add_op(ops.UnpackTuple(), obj._use_wire(None))
54
+ return tuple(
55
+ unpack_guppy_object(GuppyObject(ty, wire), builder, frozen)
56
+ for ty, wire in zip(tys, unpack.outputs(), strict=True)
57
+ )
58
+ case StructType() as ty:
59
+ unpack = builder.add_op(ops.UnpackTuple(), obj._use_wire(None))
60
+ field_values = [
61
+ unpack_guppy_object(GuppyObject(field.ty, wire), builder, frozen)
62
+ for field, wire in zip(ty.fields, unpack.outputs(), strict=True)
63
+ ]
64
+ return GuppyStructObject(ty, field_values, frozen)
65
+ case ty if is_array_type(ty):
66
+ length = get_array_length(ty)
67
+ if isinstance(length, ConstValue):
68
+ if length.value == 0:
69
+ # Zero-length lists cannot be turned back ito Guppy objects since
70
+ # there is no way to infer the type. Therefore, we should leave
71
+ # them as Guppy objects here
72
+ return obj
73
+ elem_ty = get_element_type(ty)
74
+ opt_elems = unpack_array(builder, obj._use_wire(None))
75
+ err = "Non-copyable array element has already been used"
76
+ elems = [build_unwrap(builder, opt_elem, err) for opt_elem in opt_elems]
77
+ obj_list = [
78
+ unpack_guppy_object(GuppyObject(elem_ty, wire), builder, frozen)
79
+ for wire in elems
80
+ ]
81
+ return frozenlist(obj_list) if frozen else obj_list
82
+ else:
83
+ # Cannot handle generic sizes
84
+ return obj
85
+ case _:
86
+ return obj
87
+
88
+
89
+ def guppy_object_from_py(
90
+ v: Any, builder: DfBase[P], node: AstNode, ctx: CompilerContext
91
+ ) -> GuppyObject:
92
+ """Constructs a Guppy object from a Python value.
93
+
94
+ Essentially undoes the `unpack_guppy_object` operation.
95
+ """
96
+ match v:
97
+ case GuppyObject() as obj:
98
+ return obj
99
+ case TracingDefMixin() as defn:
100
+ return defn.to_guppy_object()
101
+ case None:
102
+ return GuppyObject(NoneType(), builder.add_op(ops.MakeTuple()))
103
+ case tuple(vs):
104
+ objs = [guppy_object_from_py(v, builder, node, ctx) for v in vs]
105
+ return GuppyObject(
106
+ TupleType([obj._ty for obj in objs]),
107
+ builder.add_op(ops.MakeTuple(), *(obj._use_wire(None) for obj in objs)),
108
+ )
109
+ case GuppyStructObject(_ty=struct_ty, _field_values=values):
110
+ wires = []
111
+ for f in struct_ty.fields:
112
+ obj = guppy_object_from_py(values[f.name], builder, node, ctx)
113
+ # Check that the field still has the correct type. Since we allow users
114
+ # to mutate structs unchecked, this needs to be checked here
115
+ if obj._ty != f.ty:
116
+ raise GuppyComptimeError(
117
+ f"Field `{f.name}` of object with type `{struct_ty}` has an "
118
+ f"unexpected type. Expected `{f.ty}`, got `{obj._ty}`."
119
+ )
120
+ wires.append(obj._use_wire(None))
121
+ return GuppyObject(struct_ty, builder.add_op(ops.MakeTuple(), *wires))
122
+ case list(vs) if len(vs) > 0:
123
+ objs = [guppy_object_from_py(v, builder, node, ctx) for v in vs]
124
+ elem_ty = objs[0]._ty
125
+ for i, obj in enumerate(objs[1:]):
126
+ if obj._ty != elem_ty:
127
+ raise GuppyComptimeError(
128
+ f"Element at index {i + 1} does not match the type of "
129
+ f"previous elements. Expected `{elem_ty}`, got `{obj._ty}`."
130
+ )
131
+ hugr_elem_ty = ht.Option(elem_ty.to_hugr(ctx))
132
+ wires = [
133
+ builder.add_op(ops.Tag(1, hugr_elem_ty), obj._use_wire(None))
134
+ for obj in objs
135
+ ]
136
+ return GuppyObject(
137
+ array_type(elem_ty, len(vs)),
138
+ builder.add_op(array_new(hugr_elem_ty, len(vs)), *wires),
139
+ )
140
+ case []:
141
+ # Empty lists are tricky since we can't infer the element type here
142
+ # TODO: Propagate type information?
143
+ raise GuppyComptimeError("Cannot infer the type of empty list")
144
+ case v:
145
+ ty = python_value_to_guppy_type(v, node, get_tracing_state().globals)
146
+ if ty is None:
147
+ raise GuppyError(IllegalComptimeExpressionError(node, type(v)))
148
+ hugr_val = python_value_to_hugr(v, ty, ctx)
149
+ assert hugr_val is not None
150
+ return GuppyObject(ty, builder.load(hugr_val))
151
+
152
+
153
+ def update_packed_value(v: Any, obj: "GuppyObject", builder: DfBase[P]) -> None:
154
+ """Given a Python value `v` and a `GuppyObject` `obj` that was constructed from `v`
155
+ using `guppy_object_from_py`, updates the wires of any `GuppyObjects` contained in
156
+ `v` to the new wires specified by `obj`.
157
+
158
+ Also resets the used flag on any of those updated wires. This corresponds to making
159
+ the object available again since it now corresponds to a fresh wire.
160
+ """
161
+ match v:
162
+ case GuppyObject() as v_obj:
163
+ assert v_obj._ty == obj._ty
164
+ v_obj._wire = obj._use_wire(None)
165
+ if not v_obj._ty.droppable and v_obj._used:
166
+ state = get_tracing_state()
167
+ state.unused_undroppable_objs[v_obj._id] = v_obj
168
+ v_obj._used = None
169
+ case None:
170
+ assert isinstance(obj._ty, NoneType)
171
+ case tuple(vs):
172
+ assert isinstance(obj._ty, TupleType)
173
+ wires = builder.add_op(ops.UnpackTuple(), obj._use_wire(None)).outputs()
174
+ for v, ty, wire in zip(vs, obj._ty.element_types, wires, strict=True):
175
+ update_packed_value(v, GuppyObject(ty, wire), builder)
176
+ case GuppyStructObject(_ty=ty, _field_values=values):
177
+ assert obj._ty == ty
178
+ wires = builder.add_op(ops.UnpackTuple(), obj._use_wire(None)).outputs()
179
+ for (
180
+ field,
181
+ wire,
182
+ ) in zip(ty.fields, wires, strict=True):
183
+ v = values[field.name]
184
+ update_packed_value(v, GuppyObject(field.ty, wire), builder)
185
+ case list(vs) if len(vs) > 0:
186
+ assert is_array_type(obj._ty)
187
+ elem_ty = get_element_type(obj._ty)
188
+ opt_wires = unpack_array(builder, obj._use_wire(None))
189
+ err = "Non-droppable array element has already been used"
190
+ for v, opt_wire in zip(vs, opt_wires, strict=True):
191
+ (wire,) = build_unwrap(builder, opt_wire, err).outputs()
192
+ update_packed_value(v, GuppyObject(elem_ty, wire), builder)
193
+ case _:
194
+ pass
@@ -0,0 +1,86 @@
1
+ import functools
2
+ import inspect
3
+ import sys
4
+ from collections.abc import Callable
5
+ from types import FrameType, ModuleType, TracebackType
6
+ from typing import ParamSpec, TypeVar
7
+
8
+ from guppylang_internals.error import GuppyComptimeError, GuppyError, exception_hook
9
+
10
+ P = ParamSpec("P")
11
+ T = TypeVar("T")
12
+
13
+
14
+ def capture_guppy_errors(f: Callable[P, T]) -> Callable[P, T]:
15
+ """Context manager that captures Guppy errors and turns them into runtime
16
+ `GuppyComptimeException`s."""
17
+
18
+ @functools.wraps(f)
19
+ def wrapped(*args: P.args, **kwargs: P.kwargs) -> T:
20
+ try:
21
+ return f(*args, **kwargs)
22
+ except GuppyError as err:
23
+ diagnostic = err.error
24
+ msg = diagnostic.rendered_title
25
+ if diagnostic.rendered_span_label:
26
+ msg += f": {diagnostic.rendered_span_label}"
27
+ if diagnostic.rendered_message:
28
+ msg += f"\n{diagnostic.rendered_message}"
29
+ raise GuppyComptimeError(msg) from None
30
+
31
+ return wrapped
32
+
33
+
34
+ def hide_trace(f: Callable[P, T]) -> Callable[P, T]:
35
+ """Function decorator that hides compiler-internal frames from the traceback of any
36
+ exception thrown by the decorated function."""
37
+
38
+ @functools.wraps(f)
39
+ def wrapped(*args: P.args, **kwargs: P.kwargs) -> T:
40
+ with exception_hook(tracing_except_hook):
41
+ return f(*args, **kwargs)
42
+
43
+ return wrapped
44
+
45
+
46
+ def tracing_except_hook(
47
+ excty: type[BaseException], err: BaseException, traceback: TracebackType | None
48
+ ) -> None:
49
+ """Except hook that removes all compiler-internal frames from the traceback."""
50
+ traceback = remove_internal_frames(traceback)
51
+ try:
52
+ # Check if we're inside a jupyter notebook since it uses its own exception
53
+ # hook. If we're in a regular interpreter, this line will raise a `NameError`
54
+ ipython_shell = get_ipython() # type: ignore[name-defined]
55
+ ipython_shell.excepthook(excty, err, traceback)
56
+ except NameError:
57
+ sys.__excepthook__(excty, err, traceback)
58
+
59
+
60
+ def get_calling_frame() -> FrameType | None:
61
+ """Finds the first frame that called this function outside the compiler."""
62
+ frame = inspect.currentframe()
63
+ while frame:
64
+ module = inspect.getmodule(frame)
65
+ if module is None or not is_compiler_module(module):
66
+ return frame
67
+ frame = frame.f_back
68
+ return None
69
+
70
+
71
+ def remove_internal_frames(tb: TracebackType | None) -> TracebackType | None:
72
+ """Removes internal frames relating to the Guppy compiler from a traceback."""
73
+ if tb:
74
+ module = inspect.getmodule(tb.tb_frame)
75
+ if module is not None and is_compiler_module(module):
76
+ return remove_internal_frames(tb.tb_next)
77
+ if tb.tb_next:
78
+ tb.tb_next = remove_internal_frames(tb.tb_next)
79
+ return tb
80
+
81
+
82
+ def is_compiler_module(module: ModuleType) -> bool:
83
+ """Checks whether a given Python module belongs to the Guppy compiler."""
84
+ return module.__name__.startswith(
85
+ "guppylang_internals."
86
+ ) or module.__name__.startswith("guppylang.")
File without changes
@@ -0,0 +1,115 @@
1
+ from abc import ABC, abstractmethod
2
+ from dataclasses import dataclass
3
+ from typing import TYPE_CHECKING, TypeAlias
4
+
5
+ from hugr import tys as ht
6
+
7
+ from guppylang_internals.error import InternalGuppyError
8
+ from guppylang_internals.tys.common import (
9
+ ToHugr,
10
+ ToHugrContext,
11
+ Transformable,
12
+ Transformer,
13
+ Visitor,
14
+ )
15
+ from guppylang_internals.tys.const import (
16
+ BoundConstVar,
17
+ Const,
18
+ ConstValue,
19
+ ExistentialConstVar,
20
+ )
21
+ from guppylang_internals.tys.var import ExistentialVar
22
+
23
+ if TYPE_CHECKING:
24
+ from guppylang_internals.tys.ty import Type
25
+
26
+
27
+ # We define the `Argument` type as a union of all `ArgumentBase` subclasses defined
28
+ # below. This models an algebraic data type and enables exhaustiveness checking in
29
+ # pattern matches etc.
30
+ # Note that this might become obsolete in case the `@sealed` decorator is added:
31
+ # * https://peps.python.org/pep-0622/#sealed-classes-as-algebraic-data-types
32
+ # * https://github.com/johnthagen/sealed-typing-pep
33
+ Argument: TypeAlias = "TypeArg | ConstArg"
34
+
35
+
36
+ @dataclass(frozen=True)
37
+ class ArgumentBase(ToHugr[ht.TypeArg], Transformable["Argument"], ABC):
38
+ """Abstract base class for arguments of parametrized types.
39
+
40
+ For example, in the type `array[int, 42]` we have two arguments `int` and `42`.
41
+ """
42
+
43
+ @property
44
+ @abstractmethod
45
+ def unsolved_vars(self) -> set[ExistentialVar]:
46
+ """The existential type variables contained in this argument."""
47
+
48
+
49
+ @dataclass(frozen=True)
50
+ class TypeArg(ArgumentBase):
51
+ """Argument that can be instantiated for a `TypeParameter`."""
52
+
53
+ # The type to instantiate
54
+ ty: "Type"
55
+
56
+ @property
57
+ def unsolved_vars(self) -> set[ExistentialVar]:
58
+ """The existential type variables contained in this argument."""
59
+ return self.ty.unsolved_vars
60
+
61
+ def to_hugr(self, ctx: ToHugrContext) -> ht.TypeTypeArg:
62
+ """Computes the Hugr representation of the argument."""
63
+ ty: ht.Type = self.ty.to_hugr(ctx)
64
+ return ty.type_arg()
65
+
66
+ def visit(self, visitor: Visitor) -> None:
67
+ """Accepts a visitor on this argument."""
68
+ if not visitor.visit(self):
69
+ self.ty.visit(visitor)
70
+
71
+ def transform(self, transformer: Transformer) -> Argument:
72
+ """Accepts a transformer on this argument."""
73
+ return transformer.transform(self) or TypeArg(self.ty.transform(transformer))
74
+
75
+
76
+ @dataclass(frozen=True)
77
+ class ConstArg(ArgumentBase):
78
+ """Argument that can be substituted for a `ConstParam`."""
79
+
80
+ const: Const
81
+
82
+ @property
83
+ def unsolved_vars(self) -> set[ExistentialVar]:
84
+ """The existential const variables contained in this argument."""
85
+ return self.const.unsolved_vars
86
+
87
+ def to_hugr(self, ctx: ToHugrContext) -> ht.TypeArg:
88
+ """Computes the Hugr representation of this argument."""
89
+ from guppylang_internals.tys.ty import NumericType
90
+
91
+ match self.const:
92
+ case ConstValue(value=v, ty=NumericType(kind=NumericType.Kind.Nat)):
93
+ assert isinstance(v, int)
94
+ return ht.BoundedNatArg(n=v)
95
+ case BoundConstVar() as var:
96
+ return ctx.const_var_to_hugr(var)
97
+ case ConstValue():
98
+ raise InternalGuppyError(
99
+ "Tried to convert non-nat const type argument to Hugr. This should "
100
+ "have been monomorphized away."
101
+ )
102
+ case ExistentialConstVar():
103
+ raise InternalGuppyError(
104
+ "Tried to convert unsolved constant variable to Hugr"
105
+ )
106
+
107
+ def visit(self, visitor: Visitor) -> None:
108
+ """Accepts a visitor on this argument."""
109
+ visitor.visit(self)
110
+
111
+ def transform(self, transformer: Transformer) -> Argument:
112
+ """Accepts a transformer on this argument."""
113
+ return transformer.transform(self) or ConstArg(
114
+ self.const.transform(transformer)
115
+ )