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,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())
|