guppylang-internals 0.21.1__tar.gz → 0.22.0__tar.gz

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 (100) hide show
  1. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/CHANGELOG.md +19 -0
  2. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/PKG-INFO +1 -1
  3. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/pyproject.toml +1 -1
  4. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/__init__.py +1 -1
  5. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/ast_util.py +9 -2
  6. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/checker/stmt_checker.py +19 -2
  7. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/compiler/core.py +84 -1
  8. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/compiler/stmt_compiler.py +22 -11
  9. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/definition/pytket_circuits.py +97 -42
  10. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/nodes.py +21 -1
  11. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/std/_internal/checker.py +2 -44
  12. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/std/_internal/compiler/tket_exts.py +3 -0
  13. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/tracing/builtins_mock.py +24 -6
  14. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/tracing/function.py +1 -2
  15. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/.gitignore +0 -0
  16. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/LICENCE +0 -0
  17. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/README.md +0 -0
  18. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/cfg/__init__.py +0 -0
  19. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/cfg/analysis.py +0 -0
  20. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/cfg/bb.py +0 -0
  21. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/cfg/builder.py +0 -0
  22. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/cfg/cfg.py +0 -0
  23. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/checker/__init__.py +0 -0
  24. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/checker/cfg_checker.py +0 -0
  25. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/checker/core.py +0 -0
  26. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/checker/errors/__init__.py +0 -0
  27. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/checker/errors/comptime_errors.py +0 -0
  28. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/checker/errors/generic.py +0 -0
  29. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/checker/errors/linearity.py +0 -0
  30. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/checker/errors/type_errors.py +0 -0
  31. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/checker/errors/wasm.py +0 -0
  32. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/checker/expr_checker.py +0 -0
  33. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/checker/func_checker.py +0 -0
  34. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/checker/linearity_checker.py +0 -0
  35. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/compiler/__init__.py +0 -0
  36. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/compiler/cfg_compiler.py +0 -0
  37. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/compiler/expr_compiler.py +0 -0
  38. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/compiler/func_compiler.py +0 -0
  39. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/compiler/hugr_extension.py +0 -0
  40. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/compiler/qtm_platform_extension.py +0 -0
  41. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/decorator.py +0 -0
  42. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/definition/__init__.py +0 -0
  43. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/definition/common.py +0 -0
  44. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/definition/const.py +0 -0
  45. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/definition/custom.py +0 -0
  46. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/definition/declaration.py +0 -0
  47. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/definition/extern.py +0 -0
  48. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/definition/function.py +0 -0
  49. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/definition/overloaded.py +0 -0
  50. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/definition/parameter.py +0 -0
  51. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/definition/struct.py +0 -0
  52. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/definition/traced.py +0 -0
  53. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/definition/ty.py +0 -0
  54. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/definition/value.py +0 -0
  55. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/definition/wasm.py +0 -0
  56. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/diagnostic.py +0 -0
  57. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/dummy_decorator.py +0 -0
  58. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/engine.py +0 -0
  59. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/error.py +0 -0
  60. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/experimental.py +0 -0
  61. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/ipython_inspect.py +0 -0
  62. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/py.typed +0 -0
  63. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/span.py +0 -0
  64. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/std/__init__.py +0 -0
  65. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/std/_internal/__init__.py +0 -0
  66. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/std/_internal/compiler/__init__.py +0 -0
  67. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/std/_internal/compiler/arithmetic.py +0 -0
  68. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/std/_internal/compiler/array.py +0 -0
  69. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/std/_internal/compiler/either.py +0 -0
  70. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/std/_internal/compiler/frozenarray.py +0 -0
  71. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/std/_internal/compiler/futures.py +0 -0
  72. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/std/_internal/compiler/list.py +0 -0
  73. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/std/_internal/compiler/mem.py +0 -0
  74. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/std/_internal/compiler/option.py +0 -0
  75. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/std/_internal/compiler/prelude.py +0 -0
  76. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/std/_internal/compiler/qsystem.py +0 -0
  77. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/std/_internal/compiler/quantum.py +0 -0
  78. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/std/_internal/compiler/tket_bool.py +0 -0
  79. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/std/_internal/compiler/wasm.py +0 -0
  80. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/std/_internal/compiler.py +0 -0
  81. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/std/_internal/debug.py +0 -0
  82. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/std/_internal/util.py +0 -0
  83. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/tracing/__init__.py +0 -0
  84. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/tracing/frozenlist.py +0 -0
  85. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/tracing/object.py +0 -0
  86. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/tracing/state.py +0 -0
  87. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/tracing/unpacking.py +0 -0
  88. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/tracing/util.py +0 -0
  89. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/tys/__init__.py +0 -0
  90. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/tys/arg.py +0 -0
  91. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/tys/builtin.py +0 -0
  92. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/tys/common.py +0 -0
  93. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/tys/const.py +0 -0
  94. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/tys/errors.py +0 -0
  95. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/tys/param.py +0 -0
  96. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/tys/parsing.py +0 -0
  97. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/tys/printing.py +0 -0
  98. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/tys/subst.py +0 -0
  99. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/tys/ty.py +0 -0
  100. {guppylang_internals-0.21.1 → guppylang_internals-0.22.0}/src/guppylang_internals/tys/var.py +0 -0
@@ -3,6 +3,25 @@
3
3
  First release of `guppylang_internals` package containing refactored out internal components
4
4
  from `guppylang`.
5
5
 
6
+ ## [0.22.0](https://github.com/CQCL/guppylang/compare/guppylang-internals-v0.21.2...guppylang-internals-v0.22.0) (2025-08-11)
7
+
8
+
9
+ ### ⚠ BREAKING CHANGES
10
+
11
+ * RangeChecker has been deleted.
12
+
13
+ ### Features
14
+
15
+ * Add float parameter inputs to symbolic pytket circuits ([#1105](https://github.com/CQCL/guppylang/issues/1105)) ([34c546c](https://github.com/CQCL/guppylang/commit/34c546c3b5787beb839687fdbf4db8bc94f36c4a)), closes [#1076](https://github.com/CQCL/guppylang/issues/1076)
16
+ * Allow custom start and step in `range` ([#1157](https://github.com/CQCL/guppylang/issues/1157)) ([a1b9333](https://github.com/CQCL/guppylang/commit/a1b9333712c74270d5efaaa72f83d6b09047c068))
17
+ * Improve codegen for array unpacking ([#1106](https://github.com/CQCL/guppylang/issues/1106)) ([f375097](https://github.com/CQCL/guppylang/commit/f3750973a719b03d27668a3ae39f58c8424deffc))
18
+ * Insert drop ops for affine values ([#1090](https://github.com/CQCL/guppylang/issues/1090)) ([083133e](https://github.com/CQCL/guppylang/commit/083133e809873fce265bb78547fc3e519cb66ea1))
19
+
20
+
21
+ ### Bug Fixes
22
+
23
+ * Fix builtins mock escaping the tracing scope ([#1161](https://github.com/CQCL/guppylang/issues/1161)) ([a27a5c1](https://github.com/CQCL/guppylang/commit/a27a5c19560d76e46678f846476ea86e873ac8ac))
24
+
6
25
  ## [0.21.1](https://github.com/CQCL/guppylang/compare/guppylang-internals-v0.21.0...guppylang-internals-v0.21.1) (2025-08-05)
7
26
 
8
27
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: guppylang-internals
3
- Version: 0.21.1
3
+ Version: 0.22.0
4
4
  Summary: Compiler internals for `guppylang` package.
5
5
  Author-email: Mark Koch <mark.koch@quantinuum.com>, TKET development team <tket-support@quantinuum.com>
6
6
  Maintainer-email: Mark Koch <mark.koch@quantinuum.com>, TKET development team <tket-support@quantinuum.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "guppylang-internals"
3
- version = "0.21.1"
3
+ version = "0.22.0"
4
4
  requires-python = ">=3.10,<4"
5
5
  description = "Compiler internals for `guppylang` package."
6
6
  license = { file = "LICENCE" }
@@ -1,3 +1,3 @@
1
1
  # This is updated by our release-please workflow, triggered by this
2
2
  # annotation: x-release-please-version
3
- __version__ = "0.21.1"
3
+ __version__ = "0.22.0"
@@ -131,7 +131,10 @@ class ContextAdjuster(ast.NodeTransformer):
131
131
  def visit_Name(self, node: ast.Name) -> ast.Name:
132
132
  return with_loc(node, ast.Name(id=node.id, ctx=self.ctx))
133
133
 
134
- def visit_Starred(self, node: ast.Starred) -> ast.Starred:
134
+ def visit_Starred(
135
+ self,
136
+ node: ast.Starred,
137
+ ) -> ast.Starred:
135
138
  return with_loc(node, ast.Starred(value=self.visit(node.value), ctx=self.ctx))
136
139
 
137
140
  def visit_Tuple(self, node: ast.Tuple) -> ast.Tuple:
@@ -234,7 +237,11 @@ def set_location_from(node: ast.AST, loc: ast.AST) -> None:
234
237
 
235
238
 
236
239
  def annotate_location(
237
- node: ast.AST, source: str, file: str, line_offset: int, recurse: bool = True
240
+ node: ast.AST,
241
+ source: str,
242
+ file: str,
243
+ line_offset: int,
244
+ recurse: bool = True,
238
245
  ) -> None:
239
246
  node.line_offset = line_offset # type: ignore[attr-defined]
240
247
  node.file = file # type: ignore[attr-defined]
@@ -54,6 +54,7 @@ from guppylang_internals.checker.expr_checker import (
54
54
  from guppylang_internals.error import GuppyError, GuppyTypeError, InternalGuppyError
55
55
  from guppylang_internals.nodes import (
56
56
  AnyUnpack,
57
+ ArrayUnpack,
57
58
  DesugaredArrayComp,
58
59
  IterableUnpack,
59
60
  MakeIter,
@@ -65,6 +66,7 @@ from guppylang_internals.nodes import (
65
66
  from guppylang_internals.span import Span, to_span
66
67
  from guppylang_internals.tys.builtin import (
67
68
  array_type,
69
+ get_array_length,
68
70
  get_element_type,
69
71
  get_iter_size,
70
72
  is_array_type,
@@ -260,8 +262,10 @@ class StmtChecker(AstVisitor[BBStatement]):
260
262
  assert all_equal(starred_tys)
261
263
  if starred_tys:
262
264
  starred_ty, *_ = starred_tys
263
- # Starred part could be empty. If it's an iterable unpack, we're still fine
264
- # since we know the yielded type
265
+ # Starred part could be empty. If it's an array or iterable unpack, we're
266
+ # still fine since we know the yielded type
267
+ elif isinstance(unpack, ArrayUnpack):
268
+ starred_ty = unpack.elt_type
265
269
  elif isinstance(unpack, IterableUnpack):
266
270
  starred_ty = unpack.compr.elt_ty
267
271
  # For tuple unpacks, there is no way to infer a type for the empty starred
@@ -304,6 +308,19 @@ class StmtChecker(AstVisitor[BBStatement]):
304
308
  elts = expr.elts if isinstance(expr, ast.Tuple) else [expr] * len(tys)
305
309
  return TupleUnpack(pattern), elts, tys
306
310
 
311
+ elif is_array_type(ty):
312
+ match get_array_length(ty):
313
+ case ConstValue(value=int(size)):
314
+ elt_ty = get_element_type(ty)
315
+ unpack = ArrayUnpack(pattern, size, elt_ty)
316
+ return unpack, size * [expr], size * [elt_ty]
317
+ case generic_size:
318
+ err = UnpackableError(expr, get_type(expr))
319
+ err.add_sub_diagnostic(
320
+ UnpackableError.GenericSize(None, generic_size)
321
+ )
322
+ raise GuppyError(err)
323
+
307
324
  elif self.ctx.globals.get_instance_func(ty, "__iter__"):
308
325
  size = check_iter_unpack_has_static_size(expr, self.ctx)
309
326
  # Create a dummy variable and assign the expression to it. This helps us to
@@ -8,12 +8,14 @@ from typing import TYPE_CHECKING, Any, ClassVar, cast
8
8
 
9
9
  import tket_exts
10
10
  from hugr import Hugr, Node, Wire, ops
11
+ from hugr import ext as he
11
12
  from hugr import tys as ht
12
13
  from hugr.build import function as hf
13
14
  from hugr.build.dfg import DP, DefinitionBuilder, DfBase
14
15
  from hugr.hugr.base import OpVarCov
15
16
  from hugr.hugr.node_port import ToNode
16
17
  from hugr.std import PRELUDE
18
+ from hugr.std.collections.array import EXTENSION as ARRAY_EXTENSION
17
19
  from typing_extensions import assert_never
18
20
 
19
21
  from guppylang_internals.checker.core import (
@@ -36,6 +38,7 @@ from guppylang_internals.definition.value import CompiledCallableDef
36
38
  from guppylang_internals.diagnostic import Error
37
39
  from guppylang_internals.engine import ENGINE
38
40
  from guppylang_internals.error import GuppyError, InternalGuppyError
41
+ from guppylang_internals.std._internal.compiler.tket_exts import GUPPY_EXTENSION
39
42
  from guppylang_internals.tys.arg import ConstArg, TypeArg
40
43
  from guppylang_internals.tys.builtin import nat_type
41
44
  from guppylang_internals.tys.common import ToHugrContext
@@ -114,7 +117,7 @@ class EntryMonomorphizeError(Error):
114
117
  title: ClassVar[str] = "Invalid entry point"
115
118
  span_label: ClassVar[str] = (
116
119
  "Function `{name}` is not a valid compilation entry point since the value of "
117
- "generic paramater `{param}` is not known"
120
+ "generic parameter `{param}` is not known"
118
121
  )
119
122
  name: str
120
123
  param: Parameter
@@ -242,6 +245,11 @@ class CompilerContext(ToHugrContext):
242
245
  with track_hugr_side_effects(), self.set_monomorphized_args(mono_args):
243
246
  next_def.compile_inner(self)
244
247
 
248
+ # Insert explicit drops for affine types
249
+ # TODO: This is a quick workaround until we can properly insert these drops
250
+ # during linearity checking. See https://github.com/CQCL/guppylang/issues/1082
251
+ insert_drops(self.module.hugr)
252
+
245
253
  return entry_compiled
246
254
 
247
255
  def build_compiled_instance_func(
@@ -611,3 +619,78 @@ def track_hugr_side_effects() -> Iterator[None]:
611
619
  yield
612
620
  finally:
613
621
  Hugr.add_node = hugr_add_node # type: ignore[method-assign]
622
+
623
+
624
+ def qualified_name(type_def: he.TypeDef) -> str:
625
+ """Returns the qualified name of a Hugr extension type.
626
+ TODO: Remove once upstreamed, see https://github.com/CQCL/hugr/issues/2426
627
+ """
628
+ if type_def._extension is not None:
629
+ return f"{type_def._extension.name}.{type_def.name}"
630
+ return type_def.name
631
+
632
+
633
+ #: List of linear extension types that correspond to affine Guppy types and thus require
634
+ #: insertion of an explicit drop operation.
635
+ AFFINE_EXTENSION_TYS: list[str] = [
636
+ qualified_name(ARRAY_EXTENSION.get_type("array")),
637
+ ]
638
+
639
+
640
+ def requires_drop(ty: ht.Type) -> bool:
641
+ """Checks if a Hugr type requires an implicit drop op insertion.
642
+ This is the case for linear Hugr types that correspond to affine Guppy types, or
643
+ any other type containing one of those. See `AFFINE_EXTENSION_TYS`.
644
+ """
645
+ match ty:
646
+ case ht.ExtType(type_def=type_def, args=args):
647
+ return qualified_name(type_def) in AFFINE_EXTENSION_TYS or any(
648
+ requires_drop(arg.ty) for arg in args if isinstance(arg, ht.TypeTypeArg)
649
+ )
650
+ case ht.Opaque(id=name, extension=extension, args=args):
651
+ qualified = f"{extension}.{name}" if extension else name
652
+ return qualified in AFFINE_EXTENSION_TYS or any(
653
+ requires_drop(arg.ty) for arg in args if isinstance(arg, ht.TypeTypeArg)
654
+ )
655
+ case ht.Sum(variant_rows=rows):
656
+ return any(requires_drop(ty) for row in rows for ty in row)
657
+ case ht.Variable(bound=bound):
658
+ return bound == ht.TypeBound.Linear
659
+ case ht.FunctionType():
660
+ return False
661
+ case ht.Alias():
662
+ raise InternalGuppyError("Alias should not be emitted!")
663
+ case _:
664
+ return False
665
+
666
+
667
+ def drop_op(ty: ht.Type) -> ops.ExtOp:
668
+ """Returns the operation to drop affine values."""
669
+ return GUPPY_EXTENSION.get_op("drop").instantiate(
670
+ [ht.TypeTypeArg(ty)], ht.FunctionType([ty], [])
671
+ )
672
+
673
+
674
+ def insert_drops(hugr: Hugr[OpVarCov]) -> None:
675
+ """Inserts explicit drop ops for unconnected ports into the Hugr.
676
+ TODO: This is a quick workaround until we can properly insert these drops during
677
+ linearity checking. See https://github.com/CQCL/guppylang/issues/1082
678
+ """
679
+ for node in hugr:
680
+ data = hugr[node]
681
+ # Iterating over `node.outputs()` doesn't work reliably since it sometimes
682
+ # raises an `IncompleteOp` exception. Instead, we query the number of out ports
683
+ # and look them up by index. However, this method is *also* broken when
684
+ # isnpecting `FuncDefn` nodes due to https://github.com/CQCL/hugr/issues/2438.
685
+ if isinstance(data.op, ops.FuncDefn):
686
+ continue
687
+ for i in range(hugr.num_out_ports(node)):
688
+ port = node.out(i)
689
+ kind = hugr.port_kind(port)
690
+ if (
691
+ next(iter(hugr.linked_ports(port)), None) is None
692
+ and isinstance(kind, ht.ValueKind)
693
+ and requires_drop(kind.ty)
694
+ ):
695
+ drop = hugr.add_node(drop_op(kind.ty), parent=data.parent)
696
+ hugr.add_link(port, drop.inp(0))
@@ -17,6 +17,7 @@ from guppylang_internals.compiler.core import (
17
17
  from guppylang_internals.compiler.expr_compiler import ExprCompiler
18
18
  from guppylang_internals.error import InternalGuppyError
19
19
  from guppylang_internals.nodes import (
20
+ ArrayUnpack,
20
21
  CheckedNestedFunctionDef,
21
22
  IterableUnpack,
22
23
  PlaceNode,
@@ -124,15 +125,12 @@ class StmtCompiler(CompilerBase, AstVisitor[None]):
124
125
  self._assign(starred, array)
125
126
 
126
127
  @_assign.register
127
- def _assign_iterable(self, lhs: IterableUnpack, port: Wire) -> None:
128
- """Handles assignment where the RHS is an iterable that should be unpacked."""
129
- # Given an assignment pattern `left, *starred, right`, collect the RHS into an
130
- # array and pop from the left and right, leaving us with the starred array in
131
- # the middle
132
- assert isinstance(lhs.compr.length, ConstValue)
133
- length = lhs.compr.length.value
134
- assert isinstance(length, int)
135
- opt_elt_ty = ht.Option(lhs.compr.elt_ty.to_hugr(self.ctx))
128
+ def _assign_array(self, lhs: ArrayUnpack, port: Wire) -> None:
129
+ """Handles assignment where the RHS is an array that should be unpacked."""
130
+ # Given an assignment pattern `left, *starred, right`, pop from the left and
131
+ # right, leaving us with the starred array in the middle
132
+ length = lhs.length
133
+ opt_elt_ty = ht.Option(lhs.elt_type.to_hugr(self.ctx))
136
134
 
137
135
  def pop(
138
136
  array: Wire, length: int, pats: list[ast.expr], from_left: bool
@@ -159,8 +157,7 @@ class StmtCompiler(CompilerBase, AstVisitor[None]):
159
157
  self._assign(pat, elt)
160
158
  return array, length - num_pats
161
159
 
162
- self.dfg[lhs.rhs_var.place] = port
163
- array = self.expr_compiler.visit_DesugaredArrayComp(lhs.compr)
160
+ array = port
164
161
  array, length = pop(array, length, lhs.pattern.left, True)
165
162
  array, length = pop(array, length, lhs.pattern.right, False)
166
163
  if lhs.pattern.starred:
@@ -169,6 +166,20 @@ class StmtCompiler(CompilerBase, AstVisitor[None]):
169
166
  assert length == 0
170
167
  self.builder.add_op(array_discard_empty(opt_elt_ty), array)
171
168
 
169
+ @_assign.register
170
+ def _assign_iterable(self, lhs: IterableUnpack, port: Wire) -> None:
171
+ """Handles assignment where the RHS is an iterable that should be unpacked."""
172
+ # Collect the RHS into an array by building the comprehension and then fall back
173
+ # to the array case above
174
+ assert isinstance(lhs.compr.length, ConstValue)
175
+ length = lhs.compr.length.value
176
+ assert isinstance(length, int)
177
+
178
+ self.dfg[lhs.rhs_var.place] = port
179
+ array = self.expr_compiler.visit_DesugaredArrayComp(lhs.compr)
180
+ unpack = ArrayUnpack(lhs.pattern, length, lhs.compr.elt_ty)
181
+ self._assign_array(unpack, array)
182
+
172
183
  def visit_Assign(self, node: ast.Assign) -> None:
173
184
  [target] = node.targets
174
185
  port = self.expr_compiler.compile(node.value, self.dfg)
@@ -7,6 +7,7 @@ from hugr import Node, Wire, envelope, ops, val
7
7
  from hugr import tys as ht
8
8
  from hugr.build.dfg import DefinitionBuilder, OpVar
9
9
  from hugr.envelope import EnvelopeConfig
10
+ from hugr.std.float import FLOAT_T
10
11
 
11
12
  from guppylang_internals.ast_util import AstNode, has_empty_body, with_loc
12
13
  from guppylang_internals.checker.core import Context, Globals
@@ -37,17 +38,17 @@ from guppylang_internals.definition.value import (
37
38
  CompiledCallableDef,
38
39
  CompiledHugrNodeDef,
39
40
  )
41
+ from guppylang_internals.engine import ENGINE
40
42
  from guppylang_internals.error import GuppyError, InternalGuppyError
41
43
  from guppylang_internals.nodes import GlobalCall
42
44
  from guppylang_internals.span import SourceMap, Span, ToSpan
43
45
  from guppylang_internals.std._internal.compiler.array import (
44
- array_discard_empty,
45
46
  array_new,
46
- array_pop,
47
+ array_unpack,
47
48
  )
48
49
  from guppylang_internals.std._internal.compiler.prelude import build_unwrap
49
50
  from guppylang_internals.std._internal.compiler.tket_bool import OpaqueBool, make_opaque
50
- from guppylang_internals.tys.builtin import array_type, bool_type
51
+ from guppylang_internals.tys.builtin import array_type, bool_type, float_type
51
52
  from guppylang_internals.tys.subst import Inst, Subst
52
53
  from guppylang_internals.tys.ty import (
53
54
  FuncInput,
@@ -87,9 +88,7 @@ class RawPytketDef(ParsableDef):
87
88
  stub_signature = check_signature(func_ast, globals)
88
89
 
89
90
  # Compare signatures.
90
- circuit_signature = _signature_from_circuit(
91
- self.input_circuit, globals, self.defined_at
92
- )
91
+ circuit_signature = _signature_from_circuit(self.input_circuit, self.defined_at)
93
92
  if not (
94
93
  circuit_signature.inputs == stub_signature.inputs
95
94
  and circuit_signature.output == stub_signature.output
@@ -126,7 +125,7 @@ class RawLoadPytketDef(ParsableDef):
126
125
  def parse(self, globals: Globals, sources: SourceMap) -> "ParsedPytketDef":
127
126
  """Creates a function signature based on the user-provided circuit."""
128
127
  circuit_signature = _signature_from_circuit(
129
- self.input_circuit, globals, self.source_span, self.use_arrays
128
+ self.input_circuit, self.source_span, self.use_arrays
130
129
  )
131
130
 
132
131
  return ParsedPytketDef(
@@ -174,6 +173,7 @@ class ParsedPytketDef(CallableDef, CompilableDef):
174
173
  circ = envelope.read_envelope(
175
174
  Tk2Circuit(self.input_circuit).to_bytes(EnvelopeConfig.TEXT)
176
175
  ).modules[0]
176
+
177
177
  mapping = module.hugr.insert_hugr(circ)
178
178
  hugr_func = mapping[circ.entrypoint]
179
179
 
@@ -182,47 +182,87 @@ class ParsedPytketDef(CallableDef, CompilableDef):
182
182
  self.name, func_type.body.input, func_type.body.output
183
183
  )
184
184
 
185
- # Initialise every input bit in the circuit as false.
186
- # TODO: Provide the option for the user to pass this input as well.
187
- bool_wires = [
188
- outer_func.load(val.FALSE) for _ in range(self.input_circuit.n_bits)
189
- ]
185
+ # Number of qubit inputs in the outer function.
186
+ offset = (
187
+ len(self.input_circuit.q_registers)
188
+ if self.use_arrays
189
+ else self.input_circuit.n_qubits
190
+ )
190
191
 
191
- input_list = []
192
+ input_list: list[Wire] = []
192
193
  if self.use_arrays:
193
194
  # If the input is given as arrays, we need to unpack each element in
194
195
  # them into separate wires.
195
- # TODO: Replace with actual unpack HUGR op once
196
- # https://github.com/CQCL/hugr/issues/1947 is done.
197
- def unpack(
198
- array: Wire, elem_ty: ht.Type, length: int
199
- ) -> list[Wire]:
200
- err = "Internal error: unpacking of array failed"
201
- elts: list[Wire] = []
202
- for i in range(length):
203
- res = outer_func.add_op(
204
- array_pop(elem_ty, length - i, True), array
196
+ for i, q_reg in enumerate(self.input_circuit.q_registers):
197
+ reg_wire = outer_func.inputs()[i]
198
+ opt_elem_wires = outer_func.add_op(
199
+ array_unpack(ht.Option(ht.Qubit), q_reg.size), reg_wire
200
+ )
201
+ elem_wires = [
202
+ build_unwrap(
203
+ outer_func,
204
+ opt_elem,
205
+ "Internal error: unwrapping of array element failed",
205
206
  )
206
- [elt_opt, array] = build_unwrap(outer_func, res, err)
207
- [elt] = build_unwrap(outer_func, elt_opt, err)
208
- elts.append(elt)
209
- outer_func.add_op(array_discard_empty(elem_ty), array)
210
- return elts
211
-
212
- # Must be same length due to earlier signature computation /
213
- # comparison.
214
- for q_reg, wire in zip(
215
- self.input_circuit.q_registers,
216
- list(outer_func.inputs()),
217
- strict=True,
218
- ):
219
- input_list.extend(unpack(wire, ht.Option(ht.Qubit), q_reg.size))
207
+ for opt_elem in opt_elem_wires
208
+ ]
209
+ input_list.extend(elem_wires)
220
210
 
221
211
  else:
222
212
  # Otherwise pass inputs directly.
223
- input_list = list(outer_func.inputs())
213
+ input_list = list(outer_func.inputs()[:offset])
224
214
 
225
- call_node = outer_func.call(hugr_func, *(input_list + bool_wires))
215
+ # Initialise every input bit in the circuit as false.
216
+ # TODO: Provide the option for the user to pass this input as well.
217
+ bool_wires = [
218
+ outer_func.load(val.FALSE) for _ in range(self.input_circuit.n_bits)
219
+ ]
220
+
221
+ # Symbolic parameters (if present) get passed after qubits and bools.
222
+ has_params = len(self.input_circuit.free_symbols()) != 0
223
+ if has_params and "TKET1.input_parameters" not in hugr_func.metadata:
224
+ raise InternalGuppyError(
225
+ "Parameter metadata is missing from pytket circuit HUGR"
226
+ ) from None
227
+ param_wires: list[Wire] = []
228
+ # We assume they are given in lexicographic order by the user, then we
229
+ # wire them up according to the metadata order.
230
+ if has_params:
231
+ lex_params: list[Wire] = list(outer_func.inputs()[offset:])
232
+ if self.use_arrays:
233
+ opt_param_wires = outer_func.add_op(
234
+ array_unpack(
235
+ ht.Option(ht.Tuple(float_type().to_hugr(ctx))),
236
+ q_reg.size,
237
+ ),
238
+ lex_params[0],
239
+ )
240
+ lex_params = [
241
+ build_unwrap(
242
+ outer_func,
243
+ opt_param,
244
+ "Internal error: unwrapping of array element failed",
245
+ )
246
+ for opt_param in opt_param_wires
247
+ ]
248
+ param_order = cast(
249
+ list[str], hugr_func.metadata["TKET1.input_parameters"]
250
+ )
251
+ lex_names = sorted(param_order)
252
+ assert len(lex_names) == len(lex_params)
253
+ name_to_param = dict(zip(lex_names, lex_params, strict=True))
254
+ angle_wires = [name_to_param[name] for name in param_order]
255
+ # Need to convert all angles to floats.
256
+ for angle in angle_wires:
257
+ [halfturns] = outer_func.add_op(
258
+ ops.UnpackTuple([FLOAT_T]), angle
259
+ )
260
+ param_wires.append(halfturns)
261
+
262
+ # Pass all arguments to call node.
263
+ call_node = outer_func.call(
264
+ hugr_func, *(input_list + bool_wires + param_wires)
265
+ )
226
266
 
227
267
  # Pytket circuit hugr has qubit and bool wires in the opposite
228
268
  # order to Guppy output wires.
@@ -354,7 +394,6 @@ class CompiledPytketDef(ParsedPytketDef, CompiledCallableDef, CompiledHugrNodeDe
354
394
 
355
395
  def _signature_from_circuit(
356
396
  input_circuit: Any,
357
- globals: Globals,
358
397
  defined_at: ToSpan | None,
359
398
  use_arrays: bool = False,
360
399
  ) -> FunctionType:
@@ -367,16 +406,28 @@ def _signature_from_circuit(
367
406
  import tket # type: ignore[import-untyped, import-not-found, unused-ignore] # noqa: F401
368
407
 
369
408
  from guppylang.defs import GuppyDefinition
409
+ from guppylang.std.angles import angle
370
410
  from guppylang.std.quantum import qubit
371
411
 
372
412
  assert isinstance(qubit, GuppyDefinition)
373
413
  qubit_ty = cast(TypeDef, qubit.wrapped).check_instantiate([])
374
414
 
415
+ angle_defn = ENGINE.get_checked(angle.id) # type: ignore[attr-defined]
416
+ assert isinstance(angle_defn, TypeDef)
417
+ angle_ty = angle_defn.check_instantiate([])
418
+
375
419
  if use_arrays:
376
420
  inputs = [
377
421
  FuncInput(array_type(qubit_ty, q_reg.size), InputFlags.Inout)
378
422
  for q_reg in input_circuit.q_registers
379
423
  ]
424
+ if len(input_circuit.free_symbols()) != 0:
425
+ inputs.append(
426
+ FuncInput(
427
+ array_type(angle_ty, len(input_circuit.free_symbols())),
428
+ InputFlags.NoFlags,
429
+ )
430
+ )
380
431
  outputs = [
381
432
  array_type(bool_type(), c_reg.size)
382
433
  for c_reg in input_circuit.c_registers
@@ -386,9 +437,13 @@ def _signature_from_circuit(
386
437
  row_to_type(outputs),
387
438
  )
388
439
  else:
440
+ param_inputs = [
441
+ FuncInput(angle_ty, InputFlags.NoFlags)
442
+ for _ in range(len(input_circuit.free_symbols()))
443
+ ]
389
444
  circuit_signature = FunctionType(
390
- [FuncInput(qubit_ty, InputFlags.Inout)]
391
- * input_circuit.n_qubits,
445
+ [FuncInput(qubit_ty, InputFlags.Inout)] * input_circuit.n_qubits
446
+ + param_inputs,
392
447
  row_to_type([bool_type()] * input_circuit.n_bits),
393
448
  )
394
449
  except ImportError:
@@ -363,6 +363,26 @@ class TupleUnpack(ast.expr):
363
363
  _fields = ("pattern",)
364
364
 
365
365
 
366
+ class ArrayUnpack(ast.expr):
367
+ """The LHS of an unpacking assignment of an array."""
368
+
369
+ #: The (possibly starred) unpacking pattern
370
+ pattern: UnpackPattern
371
+
372
+ #: Length of the array
373
+ length: int
374
+
375
+ #: Element type of the array
376
+ elt_type: Type
377
+
378
+ _fields = ("pattern",)
379
+
380
+ def __init__(self, pattern: UnpackPattern, length: int, elt_type: Type) -> None:
381
+ super().__init__(pattern)
382
+ self.length = length
383
+ self.elt_type = elt_type
384
+
385
+
366
386
  class IterableUnpack(ast.expr):
367
387
  """The LHS of an unpacking assignment of an iterable type."""
368
388
 
@@ -388,7 +408,7 @@ class IterableUnpack(ast.expr):
388
408
 
389
409
 
390
410
  #: Any unpacking operation.
391
- AnyUnpack = TupleUnpack | IterableUnpack
411
+ AnyUnpack = TupleUnpack | ArrayUnpack | IterableUnpack
392
412
 
393
413
 
394
414
  class NestedFunctionDef(ast.FunctionDef):
@@ -1,10 +1,10 @@
1
1
  import ast
2
2
  from dataclasses import dataclass
3
- from typing import ClassVar, cast
3
+ from typing import ClassVar
4
4
 
5
5
  from typing_extensions import assert_never
6
6
 
7
- from guppylang_internals.ast_util import get_type, with_loc, with_type
7
+ from guppylang_internals.ast_util import get_type, with_loc
8
8
  from guppylang_internals.checker.core import ComptimeVariable, Context
9
9
  from guppylang_internals.checker.errors.generic import ExpectedError, UnsupportedError
10
10
  from guppylang_internals.checker.errors.type_errors import (
@@ -23,7 +23,6 @@ from guppylang_internals.checker.expr_checker import (
23
23
  from guppylang_internals.definition.custom import (
24
24
  CustomCallChecker,
25
25
  )
26
- from guppylang_internals.definition.struct import CheckedStructDef, RawStructDef
27
26
  from guppylang_internals.diagnostic import Error, Note
28
27
  from guppylang_internals.error import GuppyError, GuppyTypeError, InternalGuppyError
29
28
  from guppylang_internals.nodes import (
@@ -31,7 +30,6 @@ from guppylang_internals.nodes import (
31
30
  DesugaredArrayComp,
32
31
  DesugaredGeneratorExpr,
33
32
  ExitKind,
34
- GenericParamValue,
35
33
  GlobalCall,
36
34
  MakeIter,
37
35
  PanicExpr,
@@ -61,7 +59,6 @@ from guppylang_internals.tys.ty import (
61
59
  InputFlags,
62
60
  NoneType,
63
61
  NumericType,
64
- StructType,
65
62
  Type,
66
63
  unify,
67
64
  )
@@ -495,45 +492,6 @@ class ExitChecker(CustomCallChecker):
495
492
  return expr, {}
496
493
 
497
494
 
498
- class RangeChecker(CustomCallChecker):
499
- """Call checker for the `range` function."""
500
-
501
- def synthesize(self, args: list[ast.expr]) -> tuple[ast.expr, Type]:
502
- check_num_args(1, len(args), self.node)
503
- [stop] = args
504
- stop_checked, _ = ExprChecker(self.ctx).check(stop, int_type(), "argument")
505
- range_iter, range_ty = self.make_range(stop_checked)
506
- # Check if `stop` is a statically known value. Note that we need to do this on
507
- # the original `stop` instead of `stop_checked` to avoid any previously inserted
508
- # `int` coercions.
509
- if (static_stop := self.check_static(stop)) is not None:
510
- return to_sized_iter(range_iter, range_ty, static_stop, self.ctx)
511
- return range_iter, range_ty
512
-
513
- def check_static(self, stop: ast.expr) -> "int | Const | None":
514
- stop, _ = ExprSynthesizer(self.ctx).synthesize(stop, allow_free_vars=True)
515
- if isinstance(stop, ast.Constant) and isinstance(stop.value, int):
516
- return stop.value
517
- if isinstance(stop, GenericParamValue) and stop.param.ty == nat_type():
518
- return stop.param.to_bound().const
519
- return None
520
-
521
- def range_ty(self) -> StructType:
522
- from guppylang.std.builtins import Range
523
- from guppylang_internals.engine import ENGINE
524
-
525
- def_id = cast(RawStructDef, Range).id
526
- range_type_def = ENGINE.get_checked(def_id)
527
- assert isinstance(range_type_def, CheckedStructDef)
528
- return StructType([], range_type_def)
529
-
530
- def make_range(self, stop: ast.expr) -> tuple[ast.expr, Type]:
531
- make_range = self.ctx.globals.get_instance_func(self.range_ty(), "__new__")
532
- assert make_range is not None
533
- start = with_type(int_type(), with_loc(self.node, ast.Constant(value=0)))
534
- return make_range.synthesize_call([start, stop], self.node, self.ctx)
535
-
536
-
537
495
  def to_sized_iter(
538
496
  iterator: ast.expr, range_ty: Type, size: "int | Const", ctx: Context
539
497
  ) -> tuple[ast.expr, Type]:
@@ -4,6 +4,7 @@ from hugr import val
4
4
  from tket_exts import (
5
5
  debug,
6
6
  futures,
7
+ guppy,
7
8
  opaque_bool,
8
9
  qsystem,
9
10
  qsystem_random,
@@ -17,6 +18,7 @@ from tket_exts import (
17
18
  BOOL_EXTENSION = opaque_bool()
18
19
  DEBUG_EXTENSION = debug()
19
20
  FUTURES_EXTENSION = futures()
21
+ GUPPY_EXTENSION = guppy()
20
22
  QSYSTEM_EXTENSION = qsystem()
21
23
  QSYSTEM_RANDOM_EXTENSION = qsystem_random()
22
24
  QSYSTEM_UTILS_EXTENSION = qsystem_utils()
@@ -29,6 +31,7 @@ TKET_EXTENSIONS = [
29
31
  BOOL_EXTENSION,
30
32
  DEBUG_EXTENSION,
31
33
  FUTURES_EXTENSION,
34
+ GUPPY_EXTENSION,
32
35
  QSYSTEM_EXTENSION,
33
36
  QSYSTEM_RANDOM_EXTENSION,
34
37
  QSYSTEM_UTILS_EXTENSION,
@@ -7,7 +7,8 @@ the builtins to avoid raising the `TypeError` in that case.
7
7
  """
8
8
 
9
9
  import builtins
10
- from collections.abc import Callable
10
+ from collections.abc import Callable, Iterator
11
+ from contextlib import contextmanager
11
12
  from typing import Any
12
13
 
13
14
  from guppylang_internals.tracing.object import GuppyObject, GuppyStructObject
@@ -46,10 +47,10 @@ class float(builtins.float, metaclass=_mock_meta(builtins.float)): # type: igno
46
47
 
47
48
 
48
49
  class int(builtins.int, metaclass=_mock_meta(builtins.int)): # type: ignore[misc]
49
- def __new__(cls, x: Any = 0, /, **kwargs: Any) -> Any:
50
+ def __new__(cls, x: Any = 0, /, *args: Any, **kwargs: Any) -> Any:
50
51
  if isinstance(x, GuppyObject):
51
- return x.__int__(**kwargs)
52
- return builtins.int(x, **kwargs)
52
+ return x.__int__(*args, **kwargs)
53
+ return builtins.int(x, *args, **kwargs)
53
54
 
54
55
 
55
56
  def len(x: Any) -> Any:
@@ -58,5 +59,22 @@ def len(x: Any) -> Any:
58
59
  return builtins.len(x)
59
60
 
60
61
 
61
- def mock_builtins(f: Callable[..., Any]) -> None:
62
- f.__globals__.update({"float": float, "int": int, "len": len})
62
+ @contextmanager
63
+ def mock_builtins(f: Callable[..., Any]) -> Iterator[None]:
64
+ # References to builtins inside `f` are looked up in `f.__builtins__`.
65
+ # Unfortunately, this is a readonly attribute so we can't assign a new dict to it.
66
+ # Mutating in-place is also a bad idea since this would also mutate the `builtins`
67
+ # module, making us loose an escape hatch to query the original. Instead, let's
68
+ # mutate `f.__globals__` which makes Python believe that the builtins are shadowed.
69
+ mock = {"float": float, "int": int, "len": len}
70
+ old = {x: f.__globals__[x] for x in mock if x in f.__globals__}
71
+ f.__globals__.update(mock)
72
+ try:
73
+ yield
74
+ finally:
75
+ # Reset back to the prior state since mutating `f.__globals__` also mutated the
76
+ # outer globals
77
+ for x in mock:
78
+ if x not in old:
79
+ del f.__globals__[x]
80
+ f.__globals__.update(old)
@@ -70,8 +70,7 @@ def trace_function(
70
70
  for wire, inp in zip(builder.inputs(), ty.inputs, strict=True)
71
71
  ]
72
72
 
73
- with exception_hook(tracing_except_hook):
74
- mock_builtins(python_func)
73
+ with exception_hook(tracing_except_hook), mock_builtins(python_func):
75
74
  py_out = python_func(*inputs)
76
75
 
77
76
  try: