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,269 @@
1
+ """Type checking code for top-level and nested function definitions.
2
+
3
+ For top-level functions, we take the `DefinedFunction` containing the `ast.FunctionDef`
4
+ node straight from the Python AST. We build a CFG, check it, and return a
5
+ `CheckedFunction` containing a `CheckedCFG` with type annotations.
6
+ """
7
+
8
+ import ast
9
+ import sys
10
+ from dataclasses import dataclass
11
+ from typing import TYPE_CHECKING, ClassVar
12
+
13
+ from guppylang_internals.ast_util import return_nodes_in_ast, with_loc
14
+ from guppylang_internals.cfg.bb import BB
15
+ from guppylang_internals.cfg.builder import CFGBuilder
16
+ from guppylang_internals.checker.cfg_checker import CheckedCFG, check_cfg
17
+ from guppylang_internals.checker.core import Context, Globals, Place, Variable
18
+ from guppylang_internals.checker.errors.generic import UnsupportedError
19
+ from guppylang_internals.definition.common import DefId
20
+ from guppylang_internals.diagnostic import Error, Help, Note
21
+ from guppylang_internals.engine import DEF_STORE, ENGINE
22
+ from guppylang_internals.error import GuppyError
23
+ from guppylang_internals.experimental import check_capturing_closures_enabled
24
+ from guppylang_internals.nodes import CheckedNestedFunctionDef, NestedFunctionDef
25
+ from guppylang_internals.tys.parsing import parse_function_io_types
26
+ from guppylang_internals.tys.ty import FunctionType, InputFlags, NoneType
27
+
28
+ if sys.version_info >= (3, 12):
29
+ from guppylang_internals.tys.parsing import parse_parameter
30
+
31
+ if TYPE_CHECKING:
32
+ from guppylang_internals.tys.param import Parameter
33
+
34
+
35
+ @dataclass(frozen=True)
36
+ class IllegalAssignError(Error):
37
+ title: ClassVar[str] = "Illegal assignment"
38
+ span_label: ClassVar[str] = (
39
+ "Variable `{var}` may not be assigned to since `{var}` is captured from an "
40
+ "outer scope"
41
+ )
42
+ var: str
43
+
44
+ @dataclass(frozen=True)
45
+ class DefHint(Note):
46
+ span_label: ClassVar[str] = "`{var}` defined here"
47
+ var: str
48
+
49
+
50
+ @dataclass(frozen=True)
51
+ class MissingArgAnnotationError(Error):
52
+ title: ClassVar[str] = "Missing type annotation"
53
+ span_label: ClassVar[str] = "Argument requires a type annotation"
54
+
55
+
56
+ @dataclass(frozen=True)
57
+ class MissingReturnAnnotationError(Error):
58
+ title: ClassVar[str] = "Missing type annotation"
59
+ span_label: ClassVar[str] = "Return type must be annotated"
60
+
61
+ @dataclass(frozen=True)
62
+ class ReturnNone(Help):
63
+ message: ClassVar[str] = (
64
+ "Looks like `{func}` doesn't return anything. Consider annotating it with "
65
+ "`-> None`."
66
+ )
67
+ func: str
68
+
69
+
70
+ def check_global_func_def(
71
+ func_def: ast.FunctionDef, ty: FunctionType, globals: Globals
72
+ ) -> CheckedCFG[Place]:
73
+ """Type checks a top-level function definition."""
74
+ args = func_def.args.args
75
+ returns_none = isinstance(ty.output, NoneType)
76
+ assert ty.input_names is not None
77
+
78
+ cfg = CFGBuilder().build(func_def.body, returns_none, globals)
79
+ inputs = [
80
+ Variable(x, inp.ty, loc, inp.flags, is_func_input=True)
81
+ for x, inp, loc in zip(ty.input_names, ty.inputs, args, strict=True)
82
+ # Comptime inputs are turned into generic args, so are not included here
83
+ if InputFlags.Comptime not in inp.flags
84
+ ]
85
+ generic_params = {
86
+ param.name: param.with_idx(i) for i, param in enumerate(ty.params)
87
+ }
88
+ return check_cfg(cfg, inputs, ty.output, generic_params, func_def.name, globals)
89
+
90
+
91
+ def check_nested_func_def(
92
+ func_def: NestedFunctionDef, bb: BB, ctx: Context
93
+ ) -> CheckedNestedFunctionDef:
94
+ """Type checks a local (nested) function definition."""
95
+ func_ty = check_signature(func_def, ctx.globals)
96
+ assert func_ty.input_names is not None
97
+
98
+ if func_ty.parametrized:
99
+ raise GuppyError(
100
+ UnsupportedError(func_def, "Nested generic function definitions")
101
+ )
102
+
103
+ # We've already built the CFG for this function while building the CFG of the
104
+ # enclosing function
105
+ cfg = func_def.cfg
106
+
107
+ # Find captured variables
108
+ parent_cfg = bb.containing_cfg
109
+ def_ass_before = set(func_ty.input_names) | ctx.locals.keys()
110
+ maybe_ass_before = def_ass_before | parent_cfg.maybe_ass_before[bb]
111
+ inout_vars = inout_var_names(func_ty)
112
+ cfg.analyze(def_ass_before, maybe_ass_before, inout_vars)
113
+ captured = {
114
+ x: (ctx.locals[x], using_bb.vars.used[x])
115
+ for x, using_bb in cfg.live_before[cfg.entry_bb].items()
116
+ if x not in func_ty.input_names and x in ctx.locals
117
+ }
118
+
119
+ # Capturing closures are an experimental features since they aren't supported
120
+ # further down the stack yet
121
+ if captured:
122
+ _, loc = captured[next(iter(captured.keys()))]
123
+ check_capturing_closures_enabled(loc)
124
+
125
+ # Captured variables may never be assigned to
126
+ for bb in cfg.bbs:
127
+ for v, _ in captured.values():
128
+ x = v.name
129
+ if x in bb.vars.assigned:
130
+ err = IllegalAssignError(bb.vars.assigned[x], x)
131
+ err.add_sub_diagnostic(IllegalAssignError.DefHint(v.defined_at, x))
132
+ raise GuppyError(err)
133
+
134
+ # Construct inputs for checking the body CFG
135
+ inputs = [v for v, _ in captured.values()] + [
136
+ Variable(x, inp.ty, func_def.args.args[i], inp.flags, is_func_input=True)
137
+ for i, (x, inp) in enumerate(
138
+ zip(func_ty.input_names, func_ty.inputs, strict=True)
139
+ )
140
+ # Comptime inputs are turned into generic args, so are not included here
141
+ if InputFlags.Comptime not in inp.flags
142
+ ]
143
+ def_id = DefId.fresh()
144
+ globals = ctx.globals
145
+
146
+ # Check if the body contains a free (recursive) occurrence of the function name.
147
+ # By checking if the name is free at the entry BB, we avoid false positives when
148
+ # a user shadows the name with a local variable
149
+ if func_def.name in cfg.live_before[cfg.entry_bb]:
150
+ if not captured:
151
+ # If there are no captured vars, we treat the function like a global name
152
+ from guppylang.defs import GuppyDefinition
153
+ from guppylang_internals.definition.function import ParsedFunctionDef
154
+
155
+ func = ParsedFunctionDef(def_id, func_def.name, func_def, func_ty, None)
156
+ DEF_STORE.register_def(func, None)
157
+ ENGINE.parsed[def_id] = func
158
+ globals.f_locals[func_def.name] = GuppyDefinition(func)
159
+ else:
160
+ # Otherwise, we treat it like a local name
161
+ inputs.append(Variable(func_def.name, func_def.ty, func_def))
162
+
163
+ checked_cfg = check_cfg(cfg, inputs, func_ty.output, {}, func_def.name, globals)
164
+ checked_def = CheckedNestedFunctionDef(
165
+ def_id,
166
+ checked_cfg,
167
+ func_ty,
168
+ captured,
169
+ name=func_def.name,
170
+ args=func_def.args,
171
+ body=func_def.body,
172
+ decorator_list=func_def.decorator_list,
173
+ returns=func_def.returns,
174
+ type_comment=func_def.type_comment,
175
+ )
176
+ return with_loc(func_def, checked_def)
177
+
178
+
179
+ def check_signature(func_def: ast.FunctionDef, globals: Globals) -> FunctionType:
180
+ """Checks the signature of a function definition and returns the corresponding
181
+ Guppy type."""
182
+ if len(func_def.args.posonlyargs) != 0:
183
+ raise GuppyError(
184
+ UnsupportedError(func_def.args.posonlyargs[0], "Positional-only parameters")
185
+ )
186
+ if len(func_def.args.kwonlyargs) != 0:
187
+ raise GuppyError(
188
+ UnsupportedError(func_def.args.kwonlyargs[0], "Keyword-only parameters")
189
+ )
190
+ if func_def.args.vararg is not None:
191
+ raise GuppyError(UnsupportedError(func_def.args.vararg, "Variadic args"))
192
+ if func_def.args.kwarg is not None:
193
+ raise GuppyError(UnsupportedError(func_def.args.kwarg, "Keyword args"))
194
+ if func_def.args.defaults:
195
+ raise GuppyError(
196
+ UnsupportedError(func_def.args.defaults[0], "Default arguments")
197
+ )
198
+ if func_def.returns is None:
199
+ err = MissingReturnAnnotationError(func_def)
200
+ # TODO: Error location is incorrect
201
+ if all(r.value is None for r in return_nodes_in_ast(func_def)):
202
+ err.add_sub_diagnostic(
203
+ MissingReturnAnnotationError.ReturnNone(None, func_def.name)
204
+ )
205
+ raise GuppyError(err)
206
+
207
+ # Prepopulate parameter mapping when using Python 3.12 style generic syntax
208
+ param_var_mapping: dict[str, Parameter] = {}
209
+ if sys.version_info >= (3, 12):
210
+ for i, param_node in enumerate(func_def.type_params):
211
+ param = parse_parameter(param_node, i, globals)
212
+ param_var_mapping[param.name] = param
213
+
214
+ input_nodes = []
215
+ input_names = []
216
+ for inp in func_def.args.args:
217
+ ty_ast = inp.annotation
218
+ if ty_ast is None:
219
+ raise GuppyError(MissingArgAnnotationError(inp))
220
+ input_nodes.append(ty_ast)
221
+ input_names.append(inp.arg)
222
+ inputs, output = parse_function_io_types(
223
+ input_nodes,
224
+ func_def.returns,
225
+ input_names,
226
+ func_def,
227
+ globals,
228
+ param_var_mapping,
229
+ True,
230
+ )
231
+ return FunctionType(
232
+ inputs,
233
+ output,
234
+ input_names,
235
+ sorted(param_var_mapping.values(), key=lambda v: v.idx),
236
+ )
237
+
238
+
239
+ def parse_function_with_docstring(
240
+ func_ast: ast.FunctionDef,
241
+ ) -> tuple[ast.FunctionDef, str | None]:
242
+ """Check if the first line of a function is a docstring.
243
+
244
+ If it is, return the function with the docstring removed, plus the docstring.
245
+ Else, return the original function and `None`
246
+ """
247
+ docstring = None
248
+ match func_ast.body:
249
+ case [doc, *xs]:
250
+ if (
251
+ isinstance(doc, ast.Expr)
252
+ and isinstance(doc.value, ast.Constant)
253
+ and isinstance(doc.value.value, str)
254
+ ):
255
+ docstring = doc.value.value
256
+ func_ast.body = xs
257
+ case _:
258
+ pass
259
+ return func_ast, docstring
260
+
261
+
262
+ def inout_var_names(func_ty: FunctionType) -> list[str]:
263
+ """Returns the names of all borrowed arguments in a function type."""
264
+ assert func_ty.input_names is not None
265
+ return [
266
+ x
267
+ for inp, x in zip(func_ty.inputs, func_ty.input_names, strict=True)
268
+ if InputFlags.Inout in inp.flags
269
+ ]