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,178 @@
1
+ from dataclasses import dataclass
2
+ from typing import TYPE_CHECKING, ClassVar
3
+
4
+ from guppylang_internals.diagnostic import Error, Help, Note
5
+
6
+ if TYPE_CHECKING:
7
+ from guppylang_internals.definition.parameter import ParamDef
8
+ from guppylang_internals.tys.ty import Type
9
+
10
+
11
+ @dataclass(frozen=True)
12
+ class WrongNumberOfTypeArgsError(Error):
13
+ title: ClassVar[str] = "" # Custom implementation in `rendered_title`
14
+ expected: int
15
+ actual: int
16
+ type_name: str
17
+
18
+ @property
19
+ def rendered_title(self) -> str:
20
+ if self.expected == 0:
21
+ return "Non-parametric type"
22
+ elif self.expected > self.actual:
23
+ return "Missing type arguments"
24
+ else:
25
+ return "Too many type arguments"
26
+
27
+ @property
28
+ def rendered_span_label(self) -> str:
29
+ if self.expected == 0:
30
+ return f"Type `{self.type_name}` is not parametric"
31
+ diff = self.expected - self.actual
32
+ msg = "Unexpected " if diff < 0 else "Missing "
33
+ msg += "type arguments " if abs(diff) > 1 else "type argument "
34
+ msg += (
35
+ f"for type `{self.type_name}` (expected {self.expected}, got {self.actual})"
36
+ )
37
+ return msg
38
+
39
+
40
+ @dataclass(frozen=True)
41
+ class InvalidTypeArgError(Error):
42
+ title: ClassVar[str] = "Invalid type argument"
43
+ span_label: ClassVar[str] = "Not a valid type argument"
44
+
45
+
46
+ @dataclass(frozen=True)
47
+ class IllegalComptimeTypeArgError(Error):
48
+ title: ClassVar[str] = "Invalid type argument"
49
+ span_label: ClassVar[str] = (
50
+ "Comptime expression evaluating to `{obj}` is not a valid type argument"
51
+ )
52
+ obj: object
53
+
54
+
55
+ @dataclass(frozen=True)
56
+ class ModuleMemberNotFoundError(Error):
57
+ # TODO: Unify with the definition in expression checker once merged
58
+ title: ClassVar[str] = "Not found in module"
59
+ span_label: ClassVar[str] = "Module `{module_name}` has no member `{member}`"
60
+ module_name: str
61
+ member: str
62
+
63
+
64
+ @dataclass(frozen=True)
65
+ class HigherKindedTypeVarError(Error):
66
+ title: ClassVar[str] = "Not parametric"
67
+ span_label: ClassVar[str] = (
68
+ "Type variable `{var_def.name}` doesn't take type arguments"
69
+ )
70
+ var_def: "ParamDef"
71
+
72
+ @dataclass(frozen=True)
73
+ class Explain(Note):
74
+ message: ClassVar[str] = "Higher-kinded types are not supported"
75
+
76
+ def __post_init__(self) -> None:
77
+ self.add_sub_diagnostic(HigherKindedTypeVarError.Explain(None))
78
+
79
+
80
+ @dataclass(frozen=True)
81
+ class FreeTypeVarError(Error):
82
+ title: ClassVar[str] = "Free type variable"
83
+ span_label: ClassVar[str] = "Type variable `{var_def.name}` is unbound"
84
+ var_def: "ParamDef"
85
+
86
+ @dataclass(frozen=True)
87
+ class Explain(Note):
88
+ message: ClassVar[str] = (
89
+ "Only struct and function definitions can be generic. Other generic values "
90
+ "or nested types are not supported."
91
+ )
92
+
93
+ def __post_init__(self) -> None:
94
+ self.add_sub_diagnostic(FreeTypeVarError.Explain(None))
95
+
96
+
97
+ @dataclass(frozen=True)
98
+ class InvalidTypeError(Error):
99
+ title: ClassVar[str] = "Invalid type"
100
+ span_label: ClassVar[str] = "Not a valid type"
101
+
102
+
103
+ @dataclass(frozen=True)
104
+ class InvalidCallableTypeError(Error):
105
+ title: ClassVar[str] = "Invalid type"
106
+ span_label: ClassVar[str] = "Invalid function type"
107
+
108
+ @dataclass(frozen=True)
109
+ class Explain(Help):
110
+ message: ClassVar[str] = (
111
+ "Function types are specified as follows: "
112
+ "`Callable[[<arguments>], <return type>]`"
113
+ )
114
+
115
+ def __post_init__(self) -> None:
116
+ self.add_sub_diagnostic(InvalidCallableTypeError.Explain(None))
117
+
118
+
119
+ @dataclass(frozen=True)
120
+ class NonLinearOwnedError(Error):
121
+ title: ClassVar[str] = "Invalid annotation"
122
+ span_label: ClassVar[str] = "Classical type `{ty}` cannot be owned"
123
+ ty: "Type"
124
+
125
+
126
+ @dataclass(frozen=True)
127
+ class LinearConstParamError(Error):
128
+ title: ClassVar[str] = "Invalid parameter"
129
+ span_label: ClassVar[str] = (
130
+ "Non-{thing} type `{ty}` is not allowed as a generic parameter"
131
+ )
132
+ ty: "Type"
133
+
134
+ @property
135
+ def thing(self) -> str:
136
+ return "copyable" if not self.ty.copyable else "droppable"
137
+
138
+
139
+ @dataclass(frozen=True)
140
+ class LinearComptimeError(Error):
141
+ title: ClassVar[str] = "Invalid annotation"
142
+ span_label: ClassVar[str] = (
143
+ "Non-{thing} type `{ty}` is not allowed as a comptime argument"
144
+ )
145
+ ty: "Type"
146
+
147
+ @property
148
+ def thing(self) -> str:
149
+ return "copyable" if not self.ty.copyable else "droppable"
150
+
151
+
152
+ @dataclass(frozen=True)
153
+ class CallableComptimeError(Error):
154
+ title: ClassVar[str] = "Invalid annotation"
155
+ span_label: ClassVar[str] = (
156
+ "Comptime annotations are only allowed for named top-level function arguments"
157
+ )
158
+
159
+
160
+ @dataclass(frozen=True)
161
+ class ComptimeArgShadowError(Error):
162
+ title: ClassVar[str] = "Shadowed type parameter"
163
+ span_label: ClassVar[str] = (
164
+ "Comptime argument `{arg}` shadows previously used parameter with the same name"
165
+ )
166
+ arg: str
167
+
168
+
169
+ @dataclass(frozen=True)
170
+ class InvalidFlagError(Error):
171
+ title: ClassVar[str] = "Invalid annotation"
172
+ span_label: ClassVar[str] = "Invalid type annotation"
173
+
174
+
175
+ @dataclass(frozen=True)
176
+ class FlagNotAllowedError(Error):
177
+ title: ClassVar[str] = "Invalid annotation"
178
+ span_label: ClassVar[str] = "`@` type annotations are not allowed in this position"
@@ -0,0 +1,251 @@
1
+ from abc import ABC, abstractmethod
2
+ from collections.abc import Sequence
3
+ from dataclasses import dataclass, field
4
+ from typing import TYPE_CHECKING, TypeAlias
5
+
6
+ from hugr import tys as ht
7
+ from typing_extensions import Self
8
+
9
+ from guppylang_internals.ast_util import AstNode
10
+ from guppylang_internals.checker.errors.generic import ExpectedError
11
+ from guppylang_internals.checker.errors.type_errors import TypeMismatchError
12
+ from guppylang_internals.error import GuppyError, GuppyTypeError, InternalGuppyError
13
+ from guppylang_internals.tys.arg import Argument, ConstArg, TypeArg
14
+ from guppylang_internals.tys.common import ToHugr, ToHugrContext
15
+ from guppylang_internals.tys.const import BoundConstVar, ExistentialConstVar
16
+ from guppylang_internals.tys.errors import WrongNumberOfTypeArgsError
17
+ from guppylang_internals.tys.var import ExistentialVar
18
+
19
+ if TYPE_CHECKING:
20
+ from guppylang_internals.tys.ty import Type
21
+
22
+
23
+ # We define the `Parameter` type as a union of all `ParameterBase` subclasses defined
24
+ # below. This models an algebraic data type and enables exhaustiveness checking in
25
+ # pattern matches etc.
26
+ # Note that this might become obsolete in case the `@sealed` decorator is added:
27
+ # * https://peps.python.org/pep-0622/#sealed-classes-as-algebraic-data-types
28
+ # * https://github.com/johnthagen/sealed-typing-pep
29
+ Parameter: TypeAlias = "TypeParam | ConstParam"
30
+
31
+
32
+ @dataclass(frozen=True)
33
+ class ParameterBase(ToHugr[ht.TypeParam], ABC):
34
+ """Abstract base class for parameters used in function and type definitions.
35
+
36
+ For example, when defining a struct type
37
+
38
+ ```
39
+ @guppy.struct
40
+ class Foo[T, n: int]:
41
+ ...
42
+ ```
43
+
44
+ we generate an `StructDef` that depends on the parameters `T` and `n`. From
45
+ this, we obtain a proper `StructType` by providing arguments that are substituted
46
+ for the parameters (for example `Foo[int, 42]`).
47
+ """
48
+
49
+ idx: int
50
+ name: str
51
+
52
+ @abstractmethod
53
+ def with_idx(self, idx: int) -> Self:
54
+ """Returns a copy of the parameter with a new index."""
55
+
56
+ @abstractmethod
57
+ def check_arg(self, arg: Argument, loc: AstNode | None = None) -> Argument:
58
+ """Checks that this parameter can be instantiated with a given argument.
59
+
60
+ Raises a user error if the argument is not valid.
61
+ """
62
+
63
+ @abstractmethod
64
+ def to_existential(self) -> tuple[Argument, ExistentialVar]:
65
+ """Creates a fresh existential variable that can be instantiated for this
66
+ parameter.
67
+
68
+ Returns both the argument and the created variable.
69
+ """
70
+
71
+ @abstractmethod
72
+ def to_bound(self, idx: int | None = None) -> Argument:
73
+ """Creates a bound variable with a given index that can be instantiated for this
74
+ parameter.
75
+ """
76
+
77
+
78
+ @dataclass(frozen=True)
79
+ class TypeParam(ParameterBase):
80
+ """A parameter of kind type. Used to define generic functions and types."""
81
+
82
+ must_be_copyable: bool
83
+ must_be_droppable: bool
84
+
85
+ @property
86
+ def can_be_linear(self) -> bool:
87
+ """Whether this type should be treated linearly."""
88
+ return not self.must_be_copyable and not self.must_be_droppable
89
+
90
+ def with_idx(self, idx: int) -> "TypeParam":
91
+ """Returns a copy of the parameter with a new index."""
92
+ return TypeParam(idx, self.name, self.must_be_copyable, self.must_be_droppable)
93
+
94
+ def check_arg(self, arg: Argument, loc: AstNode | None = None) -> TypeArg:
95
+ """Checks that this parameter can be instantiated with a given argument.
96
+
97
+ Raises a user error if the argument is not valid.
98
+ """
99
+ match arg:
100
+ case ConstArg(const):
101
+ err = ExpectedError(loc, "a type", got=f"value of type `{const.ty}`")
102
+ raise GuppyTypeError(err)
103
+ case TypeArg(ty):
104
+ if self.must_be_copyable and not ty.copyable:
105
+ err = ExpectedError(
106
+ loc,
107
+ "a copyable type",
108
+ got=f"type `{ty}` which is not implicitly copyable",
109
+ )
110
+ raise GuppyTypeError(err)
111
+ if self.must_be_droppable and not ty.droppable:
112
+ err = ExpectedError(
113
+ loc,
114
+ "a droppable type",
115
+ got=f"type `{ty}` which is not implicitly droppable",
116
+ )
117
+ raise GuppyTypeError(err)
118
+ return arg
119
+
120
+ def to_existential(self) -> tuple[Argument, ExistentialVar]:
121
+ """Creates a fresh existential variable that can be instantiated for this
122
+ parameter.
123
+
124
+ Returns both the argument and the created variable.
125
+ """
126
+ from guppylang_internals.tys.ty import ExistentialTypeVar
127
+
128
+ var = ExistentialTypeVar.fresh(
129
+ self.name, self.must_be_copyable, self.must_be_droppable
130
+ )
131
+ return TypeArg(var), var
132
+
133
+ def to_bound(self, idx: int | None = None) -> TypeArg:
134
+ """Creates a bound variable with a given index that can be instantiated for this
135
+ parameter.
136
+ """
137
+ from guppylang_internals.tys.ty import BoundTypeVar
138
+
139
+ if idx is None:
140
+ idx = self.idx
141
+ return TypeArg(
142
+ BoundTypeVar(self.name, idx, self.must_be_copyable, self.must_be_droppable)
143
+ )
144
+
145
+ def to_hugr(self, ctx: ToHugrContext) -> ht.TypeParam:
146
+ """Computes the Hugr representation of the parameter."""
147
+ return ht.TypeTypeParam(
148
+ bound=ht.TypeBound.Linear if self.can_be_linear else ht.TypeBound.Copyable
149
+ )
150
+
151
+ def __str__(self) -> str:
152
+ """User-facing string representation of the parameter."""
153
+ return self.name
154
+
155
+
156
+ @dataclass(frozen=True)
157
+ class ConstParam(ParameterBase):
158
+ """A parameter of kind constant. Used to define fixed-size arrays etc."""
159
+
160
+ ty: "Type"
161
+
162
+ #: Marker to annotate if this parameter was implicitly generated by a `@comptime`
163
+ #: annotated argument in a function signature.
164
+ from_comptime_arg: bool = field(default=False, kw_only=True)
165
+
166
+ def __post_init__(self) -> None:
167
+ if self.ty.unsolved_vars:
168
+ raise InternalGuppyError(
169
+ "Attempted to create constant param with unsolved type"
170
+ )
171
+
172
+ def with_idx(self, idx: int) -> "ConstParam":
173
+ """Returns a copy of the parameter with a new index."""
174
+ return ConstParam(idx, self.name, self.ty)
175
+
176
+ def check_arg(self, arg: Argument, loc: AstNode | None = None) -> ConstArg:
177
+ """Checks that this parameter can be instantiated with a given argument.
178
+
179
+ Raises a user error if the argument is not valid.
180
+ """
181
+ match arg:
182
+ case ConstArg(const):
183
+ if const.ty != self.ty:
184
+ raise GuppyTypeError(
185
+ TypeMismatchError(loc, self.ty, const.ty, kind="argument")
186
+ )
187
+ return arg
188
+ case TypeArg(ty=ty):
189
+ err = ExpectedError(
190
+ loc, f"expression of type `{self.ty}`", got=f"type `{ty}`"
191
+ )
192
+ raise GuppyTypeError(err)
193
+
194
+ def to_existential(self) -> tuple[Argument, ExistentialVar]:
195
+ """Creates a fresh existential variable that can be instantiated for this
196
+ parameter.
197
+
198
+ Returns both the argument and the created variable.
199
+ """
200
+ var = ExistentialConstVar.fresh(self.name, self.ty)
201
+ return ConstArg(var), var
202
+
203
+ def to_bound(self, idx: int | None = None) -> ConstArg:
204
+ """Creates a bound variable with a given index that can be instantiated for this
205
+ parameter.
206
+ """
207
+ if idx is None:
208
+ idx = self.idx
209
+ return ConstArg(BoundConstVar(self.ty, self.name, idx))
210
+
211
+ def to_hugr(self, ctx: ToHugrContext) -> ht.TypeParam:
212
+ """Computes the Hugr representation of the parameter."""
213
+ from guppylang_internals.tys.ty import NumericType
214
+
215
+ match self.ty:
216
+ case NumericType(kind=NumericType.Kind.Nat):
217
+ return ht.BoundedNatParam(upper_bound=None)
218
+ case _:
219
+ raise InternalGuppyError(
220
+ "Tried to convert non-nat const type parameter to Hugr. This "
221
+ "should have been monomorphized away."
222
+ )
223
+
224
+ def __str__(self) -> str:
225
+ """User-facing string representation of the parameter."""
226
+ return f"{self.name}: {self.ty}"
227
+
228
+
229
+ def check_all_args(
230
+ params: Sequence[Parameter],
231
+ args: Sequence[Argument],
232
+ type_name: str,
233
+ loc: AstNode | None = None,
234
+ ) -> None:
235
+ """Checks a list of arguments against the given parameters.
236
+
237
+ Raises a user error if number of arguments doesn't match or one of the argument is
238
+ invalid.
239
+ """
240
+ exp, act = len(params), len(args)
241
+ if exp != act:
242
+ # TODO: Adjust the error span to only point to the offending arguments (similar
243
+ # to how we deal with call args in the expression checker). This requires
244
+ # threading the type arg spans down to this point
245
+ raise GuppyError(WrongNumberOfTypeArgsError(loc, exp, act, type_name))
246
+
247
+ # Now check that the kinds match up
248
+ for param, arg in zip(params, args, strict=True):
249
+ # TODO: The error location is bad. We want the location of `arg`, not of the
250
+ # whole thing.
251
+ param.check_arg(arg, loc)