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,131 @@
1
+ from abc import ABC
2
+ from collections.abc import Sequence
3
+
4
+ from hugr import Wire, ops
5
+ from hugr import tys as ht
6
+
7
+ from guppylang_internals.definition.custom import (
8
+ CustomCallCompiler,
9
+ CustomInoutCallCompiler,
10
+ )
11
+ from guppylang_internals.definition.value import CallReturnWires
12
+ from guppylang_internals.error import InternalGuppyError
13
+ from guppylang_internals.std._internal.compiler.prelude import (
14
+ build_unwrap_left,
15
+ build_unwrap_right,
16
+ )
17
+ from guppylang_internals.std._internal.compiler.tket_bool import (
18
+ OPAQUE_FALSE,
19
+ OPAQUE_TRUE,
20
+ )
21
+ from guppylang_internals.tys.arg import Argument, TypeArg
22
+ from guppylang_internals.tys.common import ToHugrContext
23
+ from guppylang_internals.tys.ty import type_to_row
24
+
25
+
26
+ def either_to_hugr(type_args: Sequence[Argument], ctx: ToHugrContext) -> ht.Either:
27
+ match type_args:
28
+ case [TypeArg(left_ty), TypeArg(right_ty)]:
29
+ left_tys = [ty.to_hugr(ctx) for ty in type_to_row(left_ty)]
30
+ right_tys = [ty.to_hugr(ctx) for ty in type_to_row(right_ty)]
31
+ return ht.Either(left_tys, right_tys)
32
+ case _:
33
+ raise InternalGuppyError("Invalid type args for Either type")
34
+
35
+
36
+ class EitherCompiler(CustomInoutCallCompiler, ABC):
37
+ """Abstract base class for compilers for `Either` methods."""
38
+
39
+ @property
40
+ def left_tys(self) -> list[ht.Type]:
41
+ match self.type_args:
42
+ case [TypeArg(left_ty), TypeArg()]:
43
+ return [ty.to_hugr(self.ctx) for ty in type_to_row(left_ty)]
44
+ case _:
45
+ raise InternalGuppyError("Invalid type args for Either op")
46
+
47
+ @property
48
+ def right_tys(self) -> list[ht.Type]:
49
+ match self.type_args:
50
+ case [TypeArg(), TypeArg(right_ty)]:
51
+ return [ty.to_hugr(self.ctx) for ty in type_to_row(right_ty)]
52
+ case _:
53
+ raise InternalGuppyError("Invalid type args for Either op")
54
+
55
+ @property
56
+ def either_ty(self) -> ht.Either:
57
+ return either_to_hugr(self.type_args, self.ctx)
58
+
59
+
60
+ class EitherConstructor(EitherCompiler, CustomCallCompiler):
61
+ """Compiler for the `Option` constructors `nothing` and `some`."""
62
+
63
+ def __init__(self, tag: int):
64
+ self.tag = tag
65
+
66
+ def compile(self, args: list[Wire]) -> list[Wire]:
67
+ ty = self.either_ty
68
+ if self.tag == 1:
69
+ # In the `right` case, the type args are swapped around since `R` occurs
70
+ # first in the signature :(
71
+ ty.variant_rows = [ty.variant_rows[1], ty.variant_rows[0]]
72
+ return [self.builder.add_op(ops.Tag(self.tag, ty), *args)]
73
+
74
+
75
+ class EitherTestCompiler(EitherCompiler):
76
+ """Compiler for the `Either.is_left` and `Either.is_right` methods."""
77
+
78
+ def __init__(self, tag: int):
79
+ self.tag = tag
80
+
81
+ def compile_with_inouts(self, args: list[Wire]) -> CallReturnWires:
82
+ [either] = args
83
+ cond = self.builder.add_conditional(either)
84
+ for i in [0, 1]:
85
+ with cond.add_case(i) as case:
86
+ val = OPAQUE_TRUE if i == self.tag else OPAQUE_FALSE
87
+ either = case.add_op(ops.Tag(i, self.either_ty), *case.inputs())
88
+ case.set_outputs(case.load(val), either)
89
+ [res, either] = cond.outputs()
90
+ return CallReturnWires(regular_returns=[res], inout_returns=[either])
91
+
92
+
93
+ class EitherToOptionCompiler(EitherCompiler, CustomCallCompiler):
94
+ """Compiler for the `Either.left` and `Either.right` methods."""
95
+
96
+ def __init__(self, tag: int):
97
+ self.tag = tag
98
+
99
+ def compile(self, args: list[Wire]) -> list[Wire]:
100
+ [either] = args
101
+ cond = self.builder.add_conditional(either)
102
+ target_tys = self.left_tys if self.tag == 0 else self.right_tys
103
+ for i in [0, 1]:
104
+ with cond.add_case(i) as case:
105
+ if i == self.tag:
106
+ out = case.add_op(
107
+ ops.Tag(1, ht.Option(*target_tys)), *case.inputs()
108
+ )
109
+ else:
110
+ out = case.add_op(ops.Tag(0, ht.Option(*target_tys)))
111
+ case.set_outputs(out)
112
+ return list(cond.outputs())
113
+
114
+
115
+ class EitherUnwrapCompiler(EitherCompiler, CustomCallCompiler):
116
+ """Compiler for the `Either.unwrap_left` and `Either.unwrap_right` methods."""
117
+
118
+ def __init__(self, tag: int):
119
+ self.tag = tag
120
+
121
+ def compile(self, args: list[Wire]) -> list[Wire]:
122
+ [either] = args
123
+ if self.tag == 0:
124
+ out = build_unwrap_left(
125
+ self.builder, either, "Either.unwrap_left: value is `right`"
126
+ )
127
+ else:
128
+ out = build_unwrap_right(
129
+ self.builder, either, "Either.unwrap_right: value is `left`"
130
+ )
131
+ return list(out)
@@ -0,0 +1,68 @@
1
+ """Compilers building frozenarray functions on top of hugr standard operations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Final
6
+
7
+ from hugr import Wire, ops
8
+ from hugr import tys as ht
9
+ from hugr.std.collections.static_array import EXTENSION, StaticArray
10
+
11
+ from guppylang_internals.compiler.core import (
12
+ GlobalConstId,
13
+ )
14
+ from guppylang_internals.definition.custom import CustomCallCompiler
15
+ from guppylang_internals.std._internal.compiler.arithmetic import (
16
+ INT_T,
17
+ convert_itousize,
18
+ )
19
+ from guppylang_internals.std._internal.compiler.prelude import (
20
+ build_unwrap,
21
+ )
22
+ from guppylang_internals.tys.arg import TypeArg
23
+
24
+ if TYPE_CHECKING:
25
+ from hugr.build import function as hf
26
+
27
+
28
+ def static_array_get(elem_ty: ht.Type) -> ops.ExtOp:
29
+ """Returns the static array `get` operation."""
30
+ assert elem_ty.type_bound() == ht.TypeBound.Copyable
31
+ arr_ty = StaticArray(elem_ty)
32
+ return EXTENSION.get_op("get").instantiate(
33
+ [ht.TypeTypeArg(elem_ty)],
34
+ ht.FunctionType([arr_ty, ht.USize()], [ht.Option(elem_ty)]),
35
+ )
36
+
37
+
38
+ FROZENARRAY_GETITEM: Final[GlobalConstId] = GlobalConstId.fresh(
39
+ "frozenarray.__getitem__"
40
+ )
41
+
42
+
43
+ class FrozenarrayGetitemCompiler(CustomCallCompiler):
44
+ def getitem_func(self) -> hf.Function:
45
+ var = ht.Variable(0, ht.TypeBound.Copyable)
46
+ sig = ht.PolyFuncType(
47
+ params=[ht.TypeTypeParam(ht.TypeBound.Copyable)],
48
+ body=ht.FunctionType([StaticArray(var), INT_T], [var]),
49
+ )
50
+ func, already_exists = self.ctx.declare_global_func(FROZENARRAY_GETITEM, sig)
51
+ if not already_exists:
52
+ [arr, idx] = func.inputs()
53
+ idx = func.add_op(convert_itousize(), idx)
54
+ elem_opt = func.add_op(static_array_get(var), arr, idx)
55
+ elem = build_unwrap(func, elem_opt, "Frozenarray index out of bounds")
56
+ func.set_outputs(elem)
57
+ return func
58
+
59
+ def compile(self, args: list[Wire]) -> list[Wire]:
60
+ [ty_arg, _] = self.type_args
61
+ assert isinstance(ty_arg, TypeArg)
62
+ elem_ty = ty_arg.ty.to_hugr(self.ctx)
63
+ inst = ht.FunctionType([StaticArray(elem_ty), INT_T], [elem_ty])
64
+ type_args = [ht.TypeTypeArg(elem_ty)]
65
+ elem = self.builder.call(
66
+ self.getitem_func(), *args, instantiation=inst, type_args=type_args
67
+ )
68
+ return [elem]
@@ -0,0 +1,30 @@
1
+ from collections.abc import Callable, Sequence
2
+
3
+ from hugr import ops
4
+ from hugr import tys as ht
5
+
6
+ from guppylang_internals.error import InternalGuppyError
7
+ from guppylang_internals.std._internal.compiler.tket_exts import FUTURES_EXTENSION
8
+ from guppylang_internals.tys.arg import Argument, TypeArg
9
+ from guppylang_internals.tys.common import ToHugrContext
10
+ from guppylang_internals.tys.subst import Inst
11
+
12
+
13
+ def future_to_hugr(type_args: Sequence[Argument], ctx: ToHugrContext) -> ht.Type:
14
+ match type_args:
15
+ case [TypeArg(ty)]:
16
+ type_def = FUTURES_EXTENSION.get_type("Future")
17
+ return type_def.instantiate([ht.TypeTypeArg(ty.to_hugr(ctx))])
18
+ case _:
19
+ raise InternalGuppyError("Invalid type args for Future type")
20
+
21
+
22
+ def future_op(
23
+ op_name: str,
24
+ ) -> Callable[[ht.FunctionType, Inst, ToHugrContext], ops.DataflowOp]:
25
+ op_def = FUTURES_EXTENSION.get_op(op_name)
26
+
27
+ def op(concrete: ht.FunctionType, args: Inst, ctx: ToHugrContext) -> ops.DataflowOp:
28
+ return op_def.instantiate([arg.to_hugr(ctx) for arg in args], concrete)
29
+
30
+ return op
@@ -0,0 +1,348 @@
1
+ """Compilers building list functions on top of hugr standard operations, that involve
2
+ multiple nodes.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import TYPE_CHECKING
8
+
9
+ import hugr.std.collections.list
10
+ from hugr import Wire, ops
11
+ from hugr import tys as ht
12
+ from hugr.std.collections.list import List, ListVal
13
+
14
+ from guppylang_internals.definition.custom import CustomCallCompiler
15
+ from guppylang_internals.definition.value import CallReturnWires
16
+ from guppylang_internals.error import InternalGuppyError
17
+ from guppylang_internals.std._internal.compiler.arithmetic import (
18
+ convert_ifromusize,
19
+ convert_itousize,
20
+ )
21
+ from guppylang_internals.std._internal.compiler.prelude import (
22
+ build_unwrap,
23
+ build_unwrap_left,
24
+ build_unwrap_right,
25
+ )
26
+ from guppylang_internals.tys.arg import TypeArg
27
+
28
+ if TYPE_CHECKING:
29
+ from hugr.build.dfg import DfBase
30
+
31
+
32
+ # ------------------------------------------------------
33
+ # --------- std.collections operations -----------------
34
+ # ------------------------------------------------------
35
+
36
+
37
+ def _instantiate_list_op(
38
+ name: str, elem_type: ht.Type, inp: list[ht.Type], out: list[ht.Type]
39
+ ) -> ops.ExtOp:
40
+ op_def = hugr.std.collections.list.EXTENSION.get_op(name)
41
+ return ops.ExtOp(
42
+ op_def,
43
+ ht.FunctionType(inp, out),
44
+ [ht.TypeTypeArg(elem_type)],
45
+ )
46
+
47
+
48
+ def list_pop(elem_type: ht.Type) -> ops.ExtOp:
49
+ """Returns a list `pop` operation."""
50
+ list_type = List(elem_type)
51
+ return _instantiate_list_op(
52
+ "pop", elem_type, [list_type], [list_type, ht.Option(elem_type)]
53
+ )
54
+
55
+
56
+ def list_push(elem_type: ht.Type) -> ops.ExtOp:
57
+ """Returns a list `push` operation."""
58
+ list_type = List(elem_type)
59
+ return _instantiate_list_op("push", elem_type, [list_type, elem_type], [list_type])
60
+
61
+
62
+ def list_get(elem_type: ht.Type) -> ops.ExtOp:
63
+ """Returns a list `get` operation."""
64
+ list_type = List(elem_type)
65
+ return _instantiate_list_op(
66
+ "get", elem_type, [list_type, ht.USize()], [ht.Option(elem_type)]
67
+ )
68
+
69
+
70
+ def list_set(elem_type: ht.Type) -> ops.ExtOp:
71
+ """Returns a list `set` operation."""
72
+ list_type = List(elem_type)
73
+ return _instantiate_list_op(
74
+ "set",
75
+ elem_type,
76
+ [list_type, ht.USize(), elem_type],
77
+ [list_type, ht.Either([elem_type], [elem_type])],
78
+ )
79
+
80
+
81
+ def list_insert(elem_type: ht.Type) -> ops.ExtOp:
82
+ """Returns a list `insert` operation."""
83
+ list_type = List(elem_type)
84
+ return _instantiate_list_op(
85
+ "insert",
86
+ elem_type,
87
+ [list_type, ht.USize(), elem_type],
88
+ [list_type, ht.Either([elem_type], [ht.Unit])],
89
+ )
90
+
91
+
92
+ def list_length(elem_type: ht.Type) -> ops.ExtOp:
93
+ """Returns a list `length` operation."""
94
+ list_type = List(elem_type)
95
+ return _instantiate_list_op(
96
+ "length", elem_type, [list_type], [list_type, ht.USize()]
97
+ )
98
+
99
+
100
+ # ------------------------------------------------------
101
+ # --------- Custom compilers for non-native ops --------
102
+ # ------------------------------------------------------
103
+
104
+
105
+ class ListGetitemCompiler(CustomCallCompiler):
106
+ """Compiler for the `list.__getitem__` function."""
107
+
108
+ def build_classical_getitem(
109
+ self,
110
+ list_wire: Wire,
111
+ idx: Wire,
112
+ elem_ty: ht.Type,
113
+ ) -> CallReturnWires:
114
+ """Lowers a call to `list.__getitem__` for classical lists."""
115
+ idx = self.builder.add_op(convert_itousize(), idx)
116
+ result = self.builder.add_op(list_get(elem_ty), list_wire, idx)
117
+ elem = build_unwrap(self.builder, result, "List index out of bounds")
118
+ return CallReturnWires(regular_returns=[elem], inout_returns=[list_wire])
119
+
120
+ def build_linear_getitem(
121
+ self,
122
+ list_wire: Wire,
123
+ idx: Wire,
124
+ elem_ty: ht.Type,
125
+ ) -> CallReturnWires:
126
+ """Lowers a call to `list.__getitem__` for linear lists."""
127
+ # Swap out the element at the given index with `None`. The `to_hugr`
128
+ # implementation of the list type ensures that linear element types are turned
129
+ # into optionals.
130
+ elem_opt_ty = ht.Option(elem_ty)
131
+ none = self.builder.add_op(ops.Tag(0, elem_opt_ty))
132
+ idx = self.builder.add_op(convert_itousize(), idx)
133
+ list_wire, result = self.builder.add_op(
134
+ list_set(elem_opt_ty), list_wire, idx, none
135
+ )
136
+ elem_opt = build_unwrap_right(self.builder, result, "List index out of bounds")
137
+ elem = build_unwrap(
138
+ self.builder, elem_opt, "Linear list element has already been used"
139
+ )
140
+ return CallReturnWires(regular_returns=[elem], inout_returns=[list_wire])
141
+
142
+ def compile_with_inouts(self, args: list[Wire]) -> CallReturnWires:
143
+ [list_wire, idx] = args
144
+ [elem_ty_arg] = self.type_args
145
+ assert isinstance(elem_ty_arg, TypeArg)
146
+ if elem_ty_arg.ty.linear:
147
+ return self.build_linear_getitem(
148
+ list_wire, idx, elem_ty_arg.ty.to_hugr(self.ctx)
149
+ )
150
+ else:
151
+ return self.build_classical_getitem(
152
+ list_wire, idx, elem_ty_arg.ty.to_hugr(self.ctx)
153
+ )
154
+
155
+ def compile(self, args: list[Wire]) -> list[Wire]:
156
+ raise InternalGuppyError("Call compile_with_inouts instead")
157
+
158
+
159
+ class ListSetitemCompiler(CustomCallCompiler):
160
+ """Compiler for the `list.__setitem__` function."""
161
+
162
+ def build_classical_setitem(
163
+ self,
164
+ list_wire: Wire,
165
+ idx: Wire,
166
+ elem: Wire,
167
+ elem_ty: ht.Type,
168
+ ) -> CallReturnWires:
169
+ """Lowers a call to `list.__setitem__` for classical lists."""
170
+ idx = self.builder.add_op(convert_itousize(), idx)
171
+ list_wire, result = self.builder.add_op(list_set(elem_ty), list_wire, idx, elem)
172
+ # Unwrap the result, but we don't have to hold onto the returned old value
173
+ build_unwrap_right(self.builder, result, "List index out of bounds")
174
+ return CallReturnWires(regular_returns=[], inout_returns=[list_wire])
175
+
176
+ def build_linear_setitem(
177
+ self,
178
+ list_wire: Wire,
179
+ idx: Wire,
180
+ elem: Wire,
181
+ elem_ty: ht.Type,
182
+ ) -> CallReturnWires:
183
+ """Lowers a call to `array.__setitem__` for linear arrays."""
184
+ # Embed the element into an optional
185
+ elem_opt_ty = ht.Option(elem_ty)
186
+ elem = self.builder.add_op(ops.Some(elem_ty), elem)
187
+ idx = self.builder.add_op(convert_itousize(), idx)
188
+ list_wire, result = self.builder.add_op(
189
+ list_set(elem_opt_ty), list_wire, idx, elem
190
+ )
191
+ old_elem_opt = build_unwrap_right(
192
+ self.builder, result, "List index out of bounds"
193
+ )
194
+ # Check that the old element was `None`
195
+ build_unwrap_left(
196
+ self.builder, old_elem_opt, "Linear list element has not been used"
197
+ )
198
+ return CallReturnWires(regular_returns=[], inout_returns=[list_wire])
199
+
200
+ def compile_with_inouts(self, args: list[Wire]) -> CallReturnWires:
201
+ [list_wire, idx, elem] = args
202
+ [elem_ty_arg] = self.type_args
203
+ assert isinstance(elem_ty_arg, TypeArg)
204
+ if elem_ty_arg.ty.linear:
205
+ return self.build_linear_setitem(
206
+ list_wire, idx, elem, elem_ty_arg.ty.to_hugr(self.ctx)
207
+ )
208
+ else:
209
+ return self.build_classical_setitem(
210
+ list_wire, idx, elem, elem_ty_arg.ty.to_hugr(self.ctx)
211
+ )
212
+
213
+ def compile(self, args: list[Wire]) -> list[Wire]:
214
+ raise InternalGuppyError("Call compile_with_inouts instead")
215
+
216
+
217
+ class ListPopCompiler(CustomCallCompiler):
218
+ """Compiler for the `list.pop` function."""
219
+
220
+ def build_classical_pop(
221
+ self,
222
+ list_wire: Wire,
223
+ elem_ty: ht.Type,
224
+ ) -> CallReturnWires:
225
+ """Lowers a call to `list.pop` for classical lists."""
226
+ list_wire, result = self.builder.add_op(list_pop(elem_ty), list_wire)
227
+ elem = build_unwrap(self.builder, result, "List index out of bounds")
228
+ return CallReturnWires(regular_returns=[elem], inout_returns=[list_wire])
229
+
230
+ def build_linear_pop(
231
+ self,
232
+ list_wire: Wire,
233
+ elem_ty: ht.Type,
234
+ ) -> CallReturnWires:
235
+ """Lowers a call to `list.pop` for linear lists."""
236
+ elem_opt_ty = ht.Option(elem_ty)
237
+ list_wire, result = self.builder.add_op(list_pop(elem_opt_ty), list_wire)
238
+ elem_opt = build_unwrap(self.builder, result, "List index out of bounds")
239
+ elem = build_unwrap(
240
+ self.builder, elem_opt, "Linear list element has already been used"
241
+ )
242
+ return CallReturnWires(regular_returns=[elem], inout_returns=[list_wire])
243
+
244
+ def compile_with_inouts(self, args: list[Wire]) -> CallReturnWires:
245
+ [list_wire] = args
246
+ [elem_ty_arg] = self.type_args
247
+ assert isinstance(elem_ty_arg, TypeArg)
248
+ if elem_ty_arg.ty.linear:
249
+ return self.build_linear_pop(list_wire, elem_ty_arg.ty.to_hugr(self.ctx))
250
+ else:
251
+ return self.build_classical_pop(list_wire, elem_ty_arg.ty.to_hugr(self.ctx))
252
+
253
+ def compile(self, args: list[Wire]) -> list[Wire]:
254
+ raise InternalGuppyError("Call compile_with_inouts instead")
255
+
256
+
257
+ class ListPushCompiler(CustomCallCompiler):
258
+ """Compiler for the `list.push` function."""
259
+
260
+ def build_classical_push(
261
+ self,
262
+ list_wire: Wire,
263
+ elem: Wire,
264
+ elem_ty: ht.Type,
265
+ ) -> CallReturnWires:
266
+ """Lowers a call to `list.push` for classical lists."""
267
+ list_wire = self.builder.add_op(list_push(elem_ty), list_wire, elem)
268
+ return CallReturnWires(regular_returns=[], inout_returns=[list_wire])
269
+
270
+ def build_linear_push(
271
+ self,
272
+ list_wire: Wire,
273
+ elem: Wire,
274
+ elem_ty: ht.Type,
275
+ ) -> CallReturnWires:
276
+ """Lowers a call to `list.push` for linear lists."""
277
+ # Wrap element into an optional
278
+ elem_opt_ty = ht.Option(elem_ty)
279
+ elem_opt = self.builder.add_op(ops.Some(elem_ty), elem)
280
+ list_wire = self.builder.add_op(list_push(elem_opt_ty), list_wire, elem_opt)
281
+ return CallReturnWires(regular_returns=[], inout_returns=[list_wire])
282
+
283
+ def compile_with_inouts(self, args: list[Wire]) -> CallReturnWires:
284
+ [list_wire, elem] = args
285
+ [elem_ty_arg] = self.type_args
286
+ assert isinstance(elem_ty_arg, TypeArg)
287
+ if elem_ty_arg.ty.linear:
288
+ return self.build_linear_push(
289
+ list_wire, elem, elem_ty_arg.ty.to_hugr(self.ctx)
290
+ )
291
+ else:
292
+ return self.build_classical_push(
293
+ list_wire, elem, elem_ty_arg.ty.to_hugr(self.ctx)
294
+ )
295
+
296
+ def compile(self, args: list[Wire]) -> list[Wire]:
297
+ raise InternalGuppyError("Call compile_with_inouts instead")
298
+
299
+
300
+ class ListLengthCompiler(CustomCallCompiler):
301
+ """Compiler for the `list.__len__` function."""
302
+
303
+ def compile_with_inouts(self, args: list[Wire]) -> CallReturnWires:
304
+ [list_wire] = args
305
+ [elem_ty_arg] = self.type_args
306
+ assert isinstance(elem_ty_arg, TypeArg)
307
+ elem_ty = elem_ty_arg.ty.to_hugr(self.ctx)
308
+ if elem_ty_arg.ty.linear:
309
+ elem_ty = ht.Option(elem_ty)
310
+ list_wire, length = self.builder.add_op(list_length(elem_ty), list_wire)
311
+ length = self.builder.add_op(convert_ifromusize(), length)
312
+ return CallReturnWires(regular_returns=[length], inout_returns=[list_wire])
313
+
314
+ def compile(self, args: list[Wire]) -> list[Wire]:
315
+ raise InternalGuppyError("Call compile_with_inouts instead")
316
+
317
+
318
+ def list_new(
319
+ builder: DfBase[ops.DfParentOp], elem_type: ht.Type, args: list[Wire]
320
+ ) -> Wire:
321
+ if elem_type.type_bound() == ht.TypeBound.Linear:
322
+ return _list_new_linear(builder, elem_type, args)
323
+ else:
324
+ return _list_new_classical(builder, elem_type, args)
325
+
326
+
327
+ def _list_new_classical(
328
+ builder: DfBase[ops.DfParentOp], elem_type: ht.Type, args: list[Wire]
329
+ ) -> Wire:
330
+ # This may be simplified in the future with a `new` or `with_capacity` list op
331
+ # See https://github.com/CQCL/hugr/issues/1508
332
+ lst = builder.load(ListVal([], elem_ty=elem_type))
333
+ push_op = list_push(elem_type)
334
+ for elem in args:
335
+ lst = builder.add_op(push_op, lst, elem)
336
+ return lst
337
+
338
+
339
+ def _list_new_linear(
340
+ builder: DfBase[ops.DfParentOp], elem_type: ht.Type, args: list[Wire]
341
+ ) -> Wire:
342
+ elem_opt_ty = ht.Option(elem_type)
343
+ lst = builder.load(ListVal([], elem_ty=elem_opt_ty))
344
+ push_op = list_push(elem_opt_ty)
345
+ for elem in args:
346
+ elem_opt = builder.add_op(ops.Some(elem_type), elem)
347
+ lst = builder.add_op(push_op, lst, elem_opt)
348
+ return lst
@@ -0,0 +1,13 @@
1
+ from hugr import Wire, ops
2
+
3
+ from guppylang_internals.definition.custom import CustomInoutCallCompiler
4
+ from guppylang_internals.definition.value import CallReturnWires
5
+
6
+
7
+ class WithOwnedCompiler(CustomInoutCallCompiler):
8
+ """Compiler for the `with_owned` function."""
9
+
10
+ def compile_with_inouts(self, args: list[Wire]) -> CallReturnWires:
11
+ [val, func] = args
12
+ [out, val] = self.builder.add_op(ops.CallIndirect(), func, val)
13
+ return CallReturnWires(regular_returns=[out], inout_returns=[val])
@@ -0,0 +1,78 @@
1
+ from abc import ABC
2
+
3
+ from hugr import Wire, ops
4
+ from hugr import tys as ht
5
+
6
+ from guppylang_internals.definition.custom import (
7
+ CustomCallCompiler,
8
+ CustomInoutCallCompiler,
9
+ )
10
+ from guppylang_internals.definition.value import CallReturnWires
11
+ from guppylang_internals.error import InternalGuppyError
12
+ from guppylang_internals.std._internal.compiler.prelude import (
13
+ build_expect_none,
14
+ build_unwrap,
15
+ )
16
+ from guppylang_internals.std._internal.compiler.tket_bool import (
17
+ OPAQUE_FALSE,
18
+ OPAQUE_TRUE,
19
+ )
20
+ from guppylang_internals.tys.arg import TypeArg
21
+
22
+
23
+ class OptionCompiler(CustomInoutCallCompiler, ABC):
24
+ """Abstract base class for compilers for `Option` methods."""
25
+
26
+ @property
27
+ def option_ty(self) -> ht.Option:
28
+ match self.type_args:
29
+ case [TypeArg(ty)]:
30
+ return ht.Option(ty.to_hugr(self.ctx))
31
+ case _:
32
+ raise InternalGuppyError("Invalid type args for Option op")
33
+
34
+
35
+ class OptionConstructor(OptionCompiler, CustomCallCompiler):
36
+ """Compiler for the `Option` constructors `nothing` and `some`."""
37
+
38
+ def __init__(self, tag: int):
39
+ self.tag = tag
40
+
41
+ def compile(self, args: list[Wire]) -> list[Wire]:
42
+ return [self.builder.add_op(ops.Tag(self.tag, self.option_ty), *args)]
43
+
44
+
45
+ class OptionTestCompiler(OptionCompiler):
46
+ """Compiler for the `Option.is_nothing` and `Option.is_some` methods."""
47
+
48
+ def __init__(self, tag: int):
49
+ self.tag = tag
50
+
51
+ def compile_with_inouts(self, args: list[Wire]) -> CallReturnWires:
52
+ [opt] = args
53
+ cond = self.builder.add_conditional(opt)
54
+ for i in [0, 1]:
55
+ with cond.add_case(i) as case:
56
+ val = OPAQUE_TRUE if i == self.tag else OPAQUE_FALSE
57
+ opt = case.add_op(ops.Tag(i, self.option_ty), *case.inputs())
58
+ case.set_outputs(case.load(val), opt)
59
+ [res, opt] = cond.outputs()
60
+ return CallReturnWires(regular_returns=[res], inout_returns=[opt])
61
+
62
+
63
+ class OptionUnwrapCompiler(OptionCompiler, CustomCallCompiler):
64
+ """Compiler for the `Option.unwrap` method."""
65
+
66
+ def compile(self, args: list[Wire]) -> list[Wire]:
67
+ [opt] = args
68
+ err = "Option.unwrap: value is `Nothing`"
69
+ return list(build_unwrap(self.builder, opt, err).outputs())
70
+
71
+
72
+ class OptionUnwrapNothingCompiler(OptionCompiler, CustomCallCompiler):
73
+ """Compiler for the `Option.unwrap_nothing` method."""
74
+
75
+ def compile(self, args: list[Wire]) -> list[Wire]:
76
+ [opt] = args
77
+ err = "Option.unwrap: value is `Some`"
78
+ return list(build_expect_none(self.builder, opt, err).outputs())