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.
- guppylang_internals/__init__.py +3 -0
- guppylang_internals/ast_util.py +350 -0
- guppylang_internals/cfg/__init__.py +0 -0
- guppylang_internals/cfg/analysis.py +230 -0
- guppylang_internals/cfg/bb.py +221 -0
- guppylang_internals/cfg/builder.py +606 -0
- guppylang_internals/cfg/cfg.py +117 -0
- guppylang_internals/checker/__init__.py +0 -0
- guppylang_internals/checker/cfg_checker.py +388 -0
- guppylang_internals/checker/core.py +550 -0
- guppylang_internals/checker/errors/__init__.py +0 -0
- guppylang_internals/checker/errors/comptime_errors.py +106 -0
- guppylang_internals/checker/errors/generic.py +45 -0
- guppylang_internals/checker/errors/linearity.py +300 -0
- guppylang_internals/checker/errors/type_errors.py +344 -0
- guppylang_internals/checker/errors/wasm.py +34 -0
- guppylang_internals/checker/expr_checker.py +1413 -0
- guppylang_internals/checker/func_checker.py +269 -0
- guppylang_internals/checker/linearity_checker.py +821 -0
- guppylang_internals/checker/stmt_checker.py +447 -0
- guppylang_internals/compiler/__init__.py +0 -0
- guppylang_internals/compiler/cfg_compiler.py +233 -0
- guppylang_internals/compiler/core.py +613 -0
- guppylang_internals/compiler/expr_compiler.py +989 -0
- guppylang_internals/compiler/func_compiler.py +97 -0
- guppylang_internals/compiler/hugr_extension.py +224 -0
- guppylang_internals/compiler/qtm_platform_extension.py +0 -0
- guppylang_internals/compiler/stmt_compiler.py +212 -0
- guppylang_internals/decorator.py +246 -0
- guppylang_internals/definition/__init__.py +0 -0
- guppylang_internals/definition/common.py +214 -0
- guppylang_internals/definition/const.py +74 -0
- guppylang_internals/definition/custom.py +492 -0
- guppylang_internals/definition/declaration.py +171 -0
- guppylang_internals/definition/extern.py +89 -0
- guppylang_internals/definition/function.py +302 -0
- guppylang_internals/definition/overloaded.py +150 -0
- guppylang_internals/definition/parameter.py +82 -0
- guppylang_internals/definition/pytket_circuits.py +405 -0
- guppylang_internals/definition/struct.py +392 -0
- guppylang_internals/definition/traced.py +151 -0
- guppylang_internals/definition/ty.py +51 -0
- guppylang_internals/definition/value.py +115 -0
- guppylang_internals/definition/wasm.py +61 -0
- guppylang_internals/diagnostic.py +523 -0
- guppylang_internals/dummy_decorator.py +76 -0
- guppylang_internals/engine.py +295 -0
- guppylang_internals/error.py +107 -0
- guppylang_internals/experimental.py +92 -0
- guppylang_internals/ipython_inspect.py +28 -0
- guppylang_internals/nodes.py +427 -0
- guppylang_internals/py.typed +0 -0
- guppylang_internals/span.py +150 -0
- guppylang_internals/std/__init__.py +0 -0
- guppylang_internals/std/_internal/__init__.py +0 -0
- guppylang_internals/std/_internal/checker.py +573 -0
- guppylang_internals/std/_internal/compiler/__init__.py +0 -0
- guppylang_internals/std/_internal/compiler/arithmetic.py +136 -0
- guppylang_internals/std/_internal/compiler/array.py +569 -0
- guppylang_internals/std/_internal/compiler/either.py +131 -0
- guppylang_internals/std/_internal/compiler/frozenarray.py +68 -0
- guppylang_internals/std/_internal/compiler/futures.py +30 -0
- guppylang_internals/std/_internal/compiler/list.py +348 -0
- guppylang_internals/std/_internal/compiler/mem.py +13 -0
- guppylang_internals/std/_internal/compiler/option.py +78 -0
- guppylang_internals/std/_internal/compiler/prelude.py +271 -0
- guppylang_internals/std/_internal/compiler/qsystem.py +48 -0
- guppylang_internals/std/_internal/compiler/quantum.py +118 -0
- guppylang_internals/std/_internal/compiler/tket_bool.py +55 -0
- guppylang_internals/std/_internal/compiler/tket_exts.py +59 -0
- guppylang_internals/std/_internal/compiler/wasm.py +135 -0
- guppylang_internals/std/_internal/compiler.py +0 -0
- guppylang_internals/std/_internal/debug.py +95 -0
- guppylang_internals/std/_internal/util.py +271 -0
- guppylang_internals/tracing/__init__.py +0 -0
- guppylang_internals/tracing/builtins_mock.py +62 -0
- guppylang_internals/tracing/frozenlist.py +57 -0
- guppylang_internals/tracing/function.py +186 -0
- guppylang_internals/tracing/object.py +551 -0
- guppylang_internals/tracing/state.py +69 -0
- guppylang_internals/tracing/unpacking.py +194 -0
- guppylang_internals/tracing/util.py +86 -0
- guppylang_internals/tys/__init__.py +0 -0
- guppylang_internals/tys/arg.py +115 -0
- guppylang_internals/tys/builtin.py +382 -0
- guppylang_internals/tys/common.py +110 -0
- guppylang_internals/tys/const.py +114 -0
- guppylang_internals/tys/errors.py +178 -0
- guppylang_internals/tys/param.py +251 -0
- guppylang_internals/tys/parsing.py +425 -0
- guppylang_internals/tys/printing.py +174 -0
- guppylang_internals/tys/subst.py +112 -0
- guppylang_internals/tys/ty.py +876 -0
- guppylang_internals/tys/var.py +49 -0
- guppylang_internals-0.21.0.dist-info/METADATA +253 -0
- guppylang_internals-0.21.0.dist-info/RECORD +98 -0
- guppylang_internals-0.21.0.dist-info/WHEEL +4 -0
- 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
|
+
)
|