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,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
|
+
]
|