guppylang-internals 0.26.0__tar.gz → 0.27.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 (107) hide show
  1. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/CHANGELOG.md +30 -0
  2. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/PKG-INFO +2 -2
  3. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/pyproject.toml +2 -2
  4. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/__init__.py +1 -1
  5. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/cfg/builder.py +3 -0
  6. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/checker/errors/linearity.py +6 -2
  7. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/checker/expr_checker.py +26 -11
  8. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/compiler/expr_compiler.py +30 -8
  9. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/compiler/modifier_compiler.py +5 -2
  10. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/decorator.py +3 -1
  11. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/definition/function.py +20 -7
  12. guppylang_internals-0.27.0/src/guppylang_internals/definition/metadata.py +87 -0
  13. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/definition/overloaded.py +11 -2
  14. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/definition/pytket_circuits.py +6 -2
  15. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/diagnostic.py +72 -15
  16. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/engine.py +1 -10
  17. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/nodes.py +32 -0
  18. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/std/_internal/compiler/array.py +36 -1
  19. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/std/_internal/compiler/either.py +14 -2
  20. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/std/_internal/compiler/tket_bool.py +1 -6
  21. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/std/_internal/compiler/tket_exts.py +1 -1
  22. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/tracing/object.py +4 -0
  23. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/wasm_util.py +2 -2
  24. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/.gitignore +0 -0
  25. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/LICENCE +0 -0
  26. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/README.md +0 -0
  27. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/ast_util.py +0 -0
  28. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/cfg/__init__.py +0 -0
  29. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/cfg/analysis.py +0 -0
  30. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/cfg/bb.py +0 -0
  31. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/cfg/cfg.py +0 -0
  32. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/checker/__init__.py +0 -0
  33. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/checker/cfg_checker.py +0 -0
  34. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/checker/core.py +0 -0
  35. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/checker/errors/__init__.py +0 -0
  36. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/checker/errors/comptime_errors.py +0 -0
  37. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/checker/errors/generic.py +0 -0
  38. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/checker/errors/type_errors.py +0 -0
  39. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/checker/errors/wasm.py +0 -0
  40. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/checker/func_checker.py +0 -0
  41. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/checker/linearity_checker.py +0 -0
  42. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/checker/modifier_checker.py +0 -0
  43. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/checker/stmt_checker.py +0 -0
  44. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/checker/unitary_checker.py +0 -0
  45. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/compiler/__init__.py +0 -0
  46. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/compiler/cfg_compiler.py +0 -0
  47. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/compiler/core.py +0 -0
  48. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/compiler/func_compiler.py +0 -0
  49. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/compiler/hugr_extension.py +0 -0
  50. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/compiler/qtm_platform_extension.py +0 -0
  51. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/compiler/stmt_compiler.py +0 -0
  52. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/definition/__init__.py +0 -0
  53. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/definition/common.py +0 -0
  54. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/definition/const.py +0 -0
  55. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/definition/custom.py +0 -0
  56. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/definition/declaration.py +0 -0
  57. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/definition/extern.py +0 -0
  58. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/definition/parameter.py +0 -0
  59. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/definition/struct.py +0 -0
  60. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/definition/traced.py +0 -0
  61. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/definition/ty.py +0 -0
  62. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/definition/value.py +0 -0
  63. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/definition/wasm.py +0 -0
  64. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/dummy_decorator.py +0 -0
  65. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/error.py +0 -0
  66. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/experimental.py +0 -0
  67. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/ipython_inspect.py +0 -0
  68. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/py.typed +0 -0
  69. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/span.py +0 -0
  70. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/std/__init__.py +0 -0
  71. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/std/_internal/__init__.py +0 -0
  72. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/std/_internal/checker.py +0 -0
  73. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/std/_internal/compiler/__init__.py +0 -0
  74. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/std/_internal/compiler/arithmetic.py +0 -0
  75. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/std/_internal/compiler/frozenarray.py +0 -0
  76. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/std/_internal/compiler/futures.py +0 -0
  77. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/std/_internal/compiler/list.py +0 -0
  78. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/std/_internal/compiler/mem.py +0 -0
  79. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/std/_internal/compiler/option.py +0 -0
  80. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/std/_internal/compiler/platform.py +0 -0
  81. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/std/_internal/compiler/prelude.py +0 -0
  82. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/std/_internal/compiler/qsystem.py +0 -0
  83. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/std/_internal/compiler/quantum.py +0 -0
  84. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/std/_internal/compiler/wasm.py +0 -0
  85. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/std/_internal/compiler.py +0 -0
  86. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/std/_internal/debug.py +0 -0
  87. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/std/_internal/util.py +0 -0
  88. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/tracing/__init__.py +0 -0
  89. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/tracing/builtins_mock.py +0 -0
  90. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/tracing/frozenlist.py +0 -0
  91. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/tracing/function.py +0 -0
  92. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/tracing/state.py +0 -0
  93. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/tracing/unpacking.py +0 -0
  94. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/tracing/util.py +0 -0
  95. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/tys/__init__.py +0 -0
  96. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/tys/arg.py +0 -0
  97. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/tys/builtin.py +0 -0
  98. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/tys/common.py +0 -0
  99. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/tys/const.py +0 -0
  100. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/tys/errors.py +0 -0
  101. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/tys/param.py +0 -0
  102. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/tys/parsing.py +0 -0
  103. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/tys/printing.py +0 -0
  104. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/tys/qubit.py +0 -0
  105. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/tys/subst.py +0 -0
  106. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/tys/ty.py +0 -0
  107. {guppylang_internals-0.26.0 → guppylang_internals-0.27.0}/src/guppylang_internals/tys/var.py +0 -0
@@ -3,6 +3,36 @@
3
3
  First release of `guppylang_internals` package containing refactored out internal components
4
4
  from `guppylang`.
5
5
 
6
+ ## [0.27.0](https://github.com/Quantinuum/guppylang/compare/guppylang-internals-v0.26.0...guppylang-internals-v0.27.0) (2026-01-08)
7
+
8
+
9
+ ### ⚠ BREAKING CHANGES
10
+
11
+ * The first argument to `add_unitarity_metadata` is now named `node`
12
+ instead of `func`, since its type was raised to allow for more HUGR
13
+ nodes to be fed. Migration is trivial.
14
+ * `DiagnosticsRenderer.PREFIX_CONTEXT_LINES` constant has been removed.
15
+
16
+ ### Features
17
+
18
+ * Add qubit hints on Guppy functions, allowing elision when building emulators ([#1378](https://github.com/Quantinuum/guppylang/issues/1378)) ([b7f10c6](https://github.com/Quantinuum/guppylang/commit/b7f10c6798aa20841fae844084d8a1606661fd7b)), closes [#1297](https://github.com/Quantinuum/guppylang/issues/1297)
19
+ * Add unsafe array take and put operations ([#1165](https://github.com/Quantinuum/guppylang/issues/1165)) ([7f342e7](https://github.com/Quantinuum/guppylang/commit/7f342e788e2f179382bab46dcc7e69a24dd64de3))
20
+ * **internals:** update to hugr-py 0.15 ([#1418](https://github.com/Quantinuum/guppylang/issues/1418)) ([cf970ba](https://github.com/Quantinuum/guppylang/commit/cf970ba7403a126fbd5d2fd53445e65270581df4))
21
+
22
+
23
+ ### Bug Fixes
24
+
25
+ * Add a line break for printing WASM files ([#1386](https://github.com/Quantinuum/guppylang/issues/1386)) ([495aba5](https://github.com/Quantinuum/guppylang/commit/495aba5b9bb2218224193c7b2da2c5586744505a))
26
+ * added deepcopy in `OverloadedFunctionDef.{check_call,synthesize_call}` ([#1426](https://github.com/Quantinuum/guppylang/issues/1426)) ([9be6fef](https://github.com/Quantinuum/guppylang/commit/9be6fefcdfb9fc9eb1025774d2dd2727b3e719b1))
27
+ * **checker:** handle imported ParamDef with aliases in expr_checker ([#1385](https://github.com/Quantinuum/guppylang/issues/1385)) ([f2838a3](https://github.com/Quantinuum/guppylang/commit/f2838a34a315599c5b46eae92b72e9758e428a16))
28
+ * Convert symbolic pytket circuits angle inputs into rotations ([#1425](https://github.com/Quantinuum/guppylang/issues/1425)) ([4724d90](https://github.com/Quantinuum/guppylang/commit/4724d9039d8dffae8fd939f62ae80ec307d8918a))
29
+ * Ensure errors from `[@wasm](https://github.com/wasm)_module` are rendered correctly ([#1398](https://github.com/Quantinuum/guppylang/issues/1398)) ([a6a539f](https://github.com/Quantinuum/guppylang/commit/a6a539fe07cc94f4a788fef506969e4c9027faee)), closes [#1397](https://github.com/Quantinuum/guppylang/issues/1397)
30
+ * Fix another wasm diagnostics rendering issue ([#1399](https://github.com/Quantinuum/guppylang/issues/1399)) ([6604175](https://github.com/Quantinuum/guppylang/commit/660417542f2b36c387e73765f8647c11cd3d1a7b))
31
+ * Fix Hugr generation for tuples in `Result` and `Either` ([#1395](https://github.com/Quantinuum/guppylang/issues/1395)) ([f8b0d47](https://github.com/Quantinuum/guppylang/commit/f8b0d47eb275aae3f5ba804dfeb3640c4a3baef6)), closes [#1388](https://github.com/Quantinuum/guppylang/issues/1388)
32
+ * improve diagnostics rendering ([#1382](https://github.com/Quantinuum/guppylang/issues/1382)) ([e7ce7f6](https://github.com/Quantinuum/guppylang/commit/e7ce7f6d1a4f2b12ff680a6e54dae96637c5fa92))
33
+ * Stop parsing entrypoints twice ([#1410](https://github.com/Quantinuum/guppylang/issues/1410)) ([4a167e5](https://github.com/Quantinuum/guppylang/commit/4a167e5642cedc8f47ad027ed08483caa1558830))
34
+ * Support comptime expressions in generic argument applications ([#1409](https://github.com/Quantinuum/guppylang/issues/1409)) ([c1aad34](https://github.com/Quantinuum/guppylang/commit/c1aad346adb15e3636e5586987422d74e36189a1)), closes [#1087](https://github.com/Quantinuum/guppylang/issues/1087)
35
+
6
36
  ## [0.26.0](https://github.com/Quantinuum/guppylang/compare/guppylang-internals-v0.25.0...guppylang-internals-v0.26.0) (2025-12-11)
7
37
 
8
38
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: guppylang-internals
3
- Version: 0.26.0
3
+ Version: 0.27.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>
@@ -219,7 +219,7 @@ Classifier: Programming Language :: Python :: 3.13
219
219
  Classifier: Programming Language :: Python :: 3.14
220
220
  Classifier: Topic :: Software Development :: Compilers
221
221
  Requires-Python: <4,>=3.10
222
- Requires-Dist: hugr~=0.14.1
222
+ Requires-Dist: hugr~=0.15.0
223
223
  Requires-Dist: tket-exts~=0.12.0
224
224
  Requires-Dist: typing-extensions<5,>=4.9.0
225
225
  Requires-Dist: wasmtime~=v38.0.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "guppylang-internals"
3
- version = "0.26.0"
3
+ version = "0.27.0"
4
4
  requires-python = ">=3.10,<4"
5
5
  description = "Compiler internals for `guppylang` package."
6
6
  license = { file = "LICENCE" }
@@ -35,7 +35,7 @@ classifiers = [
35
35
  dependencies = [
36
36
  "typing-extensions >=4.9.0,<5",
37
37
  "tket-exts ~= 0.12.0",
38
- "hugr ~= 0.14.1",
38
+ "hugr ~= 0.15.0",
39
39
  "wasmtime ~= v38.0.0",
40
40
  ]
41
41
 
@@ -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.26.0"
3
+ __version__ = "0.27.0"
@@ -668,6 +668,9 @@ def is_comptime_expression(node: ast.AST) -> ComptimeExpr | None:
668
668
 
669
669
  Otherwise, returns `None`.
670
670
  """
671
+ if isinstance(node, ComptimeExpr):
672
+ return node
673
+
671
674
  if (
672
675
  isinstance(node, ast.Call)
673
676
  and isinstance(node.func, ast.Name)
@@ -33,7 +33,9 @@ class AlreadyUsedError(Error):
33
33
 
34
34
  @dataclass(frozen=True)
35
35
  class PrevUse(Note):
36
- span_label: ClassVar[str] = "since it was already {prev_kind.subjunctive} here"
36
+ span_label: ClassVar[str] = (
37
+ "{place.describe} already {prev_kind.subjunctive} here"
38
+ )
37
39
  prev_kind: UseKind
38
40
 
39
41
  @dataclass(frozen=True)
@@ -55,7 +57,9 @@ class ComprAlreadyUsedError(Error):
55
57
 
56
58
  @dataclass(frozen=True)
57
59
  class PrevUse(Note):
58
- span_label: ClassVar[str] = "since it was already {prev_kind.subjunctive} here"
60
+ span_label: ClassVar[str] = (
61
+ "{place.describe} already {prev_kind.subjunctive} here"
62
+ )
59
63
  prev_kind: UseKind
60
64
 
61
65
 
@@ -85,6 +85,7 @@ from guppylang_internals.checker.errors.type_errors import (
85
85
  WrongNumberOfArgsError,
86
86
  )
87
87
  from guppylang_internals.definition.common import Definition
88
+ from guppylang_internals.definition.parameter import ParamDef
88
89
  from guppylang_internals.definition.ty import TypeDef
89
90
  from guppylang_internals.definition.value import CallableDef, ValueDef
90
91
  from guppylang_internals.error import (
@@ -407,23 +408,27 @@ class ExprSynthesizer(AstVisitor[tuple[ast.expr, Type]]):
407
408
  raise GuppyError(IllegalConstant(node, type(node.value)))
408
409
  return node, ty
409
410
 
411
+ def _check_generic_param(self, name: str, node: ast.expr) -> tuple[ast.expr, Type]:
412
+ """Helper method to check a generic parameter (ConstParam or TypeParam)."""
413
+ param = self.ctx.generic_params[name]
414
+ match param:
415
+ case ConstParam() as param:
416
+ ast_node = with_loc(node, GenericParamValue(id=name, param=param))
417
+ return ast_node, param.ty
418
+ case TypeParam() as param:
419
+ raise GuppyError(
420
+ ExpectedError(node, "a value", got=f"type `{param.name}`")
421
+ )
422
+ case _:
423
+ return assert_never(param)
424
+
410
425
  def visit_Name(self, node: ast.Name) -> tuple[ast.expr, Type]:
411
426
  x = node.id
412
427
  if x in self.ctx.locals:
413
428
  var = self.ctx.locals[x]
414
429
  return with_loc(node, PlaceNode(place=var)), var.ty
415
430
  elif x in self.ctx.generic_params:
416
- param = self.ctx.generic_params[x]
417
- match param:
418
- case ConstParam() as param:
419
- ast_node = with_loc(node, GenericParamValue(id=x, param=param))
420
- return ast_node, param.ty
421
- case TypeParam() as param:
422
- raise GuppyError(
423
- ExpectedError(node, "a value", got=f"type `{param.name}`")
424
- )
425
- case _:
426
- return assert_never(param)
431
+ return self._check_generic_param(x, node)
427
432
  elif x in self.ctx.globals:
428
433
  match self.ctx.globals[x]:
429
434
  case Definition() as defn:
@@ -454,6 +459,16 @@ class ExprSynthesizer(AstVisitor[tuple[ast.expr, Type]]):
454
459
  defn, "__new__"
455
460
  ):
456
461
  return with_loc(node, GlobalName(id=name, def_id=constr.id)), constr.ty
462
+ # Handle parameter definitions (e.g., nat_var) that may be imported
463
+ case ParamDef():
464
+ # Check if this parameter is in our generic_params
465
+ # (e.g., used in type signature)
466
+ if name in self.ctx.generic_params:
467
+ return self._check_generic_param(name, node)
468
+ # If not in generic_params, it's being used outside its scope
469
+ raise GuppyError(
470
+ ExpectedError(node, "a value", got=f"{defn.description} `{name}`")
471
+ )
457
472
  case defn:
458
473
  raise GuppyError(
459
474
  ExpectedError(node, "a value", got=f"{defn.description} `{name}`")
@@ -325,14 +325,7 @@ class ExprCompiler(CompilerBase, AstVisitor[Wire]):
325
325
 
326
326
  def _pack_returns(self, returns: Sequence[Wire], return_ty: Type) -> Wire:
327
327
  """Groups function return values into a tuple"""
328
- if isinstance(return_ty, TupleType | NoneType) and not return_ty.preserve:
329
- types = type_to_row(return_ty)
330
- assert len(returns) == len(types)
331
- return self._pack_tuple(returns, types)
332
- assert (
333
- len(returns) == 1
334
- ), f"Expected a single return value. Got {returns}. return type {return_ty}"
335
- return returns[0]
328
+ return pack_returns(returns, return_ty, self.builder, self.ctx)
336
329
 
337
330
  def _update_inout_ports(
338
331
  self,
@@ -760,6 +753,35 @@ def expr_to_row(expr: ast.expr) -> list[ast.expr]:
760
753
  return expr.elts if isinstance(expr, ast.Tuple) else [expr]
761
754
 
762
755
 
756
+ def pack_returns(
757
+ returns: Sequence[Wire],
758
+ return_ty: Type,
759
+ builder: DfBase[ops.DfParentOp],
760
+ ctx: CompilerContext,
761
+ ) -> Wire:
762
+ """Groups function return values into a tuple"""
763
+ if isinstance(return_ty, TupleType | NoneType) and not return_ty.preserve:
764
+ types = type_to_row(return_ty)
765
+ assert len(returns) == len(types)
766
+ hugr_tys = [t.to_hugr(ctx) for t in types]
767
+ return builder.add_op(ops.MakeTuple(hugr_tys), *returns)
768
+ assert (
769
+ len(returns) == 1
770
+ ), f"Expected a single return value. Got {returns}. return type {return_ty}"
771
+ return returns[0]
772
+
773
+
774
+ def unpack_wire(
775
+ wire: Wire, return_ty: Type, builder: DfBase[ops.DfParentOp], ctx: CompilerContext
776
+ ) -> list[Wire]:
777
+ """The inverse of `pack_returns`"""
778
+ if isinstance(return_ty, TupleType | NoneType) and not return_ty.preserve:
779
+ types = type_to_row(return_ty)
780
+ hugr_tys = [t.to_hugr(ctx) for t in types]
781
+ return list(builder.add_op(ops.UnpackTuple(hugr_tys), wire).outputs())
782
+ return [wire]
783
+
784
+
763
785
  def instantiation_needs_unpacking(func_ty: FunctionType, inst: Inst) -> bool:
764
786
  """Checks if instantiating a polymorphic makes it return a row."""
765
787
  if isinstance(func_ty.output, BoundTypeVar):
@@ -8,7 +8,7 @@ from guppylang_internals.checker.modifier_checker import non_copyable_front_othe
8
8
  from guppylang_internals.compiler.cfg_compiler import compile_cfg
9
9
  from guppylang_internals.compiler.core import CompilerContext, DFContainer
10
10
  from guppylang_internals.compiler.expr_compiler import ExprCompiler
11
- from guppylang_internals.definition.function import add_unitarity_metadata
11
+ from guppylang_internals.definition.metadata import add_metadata
12
12
  from guppylang_internals.nodes import CheckedModifiedBlock, PlaceNode
13
13
  from guppylang_internals.std._internal.compiler.array import (
14
14
  array_new,
@@ -57,7 +57,10 @@ def compile_modified_block(
57
57
  func_builder = dfg.builder.module_root_builder().define_function(
58
58
  str(modified_block), hugr_ty.input, hugr_ty.output
59
59
  )
60
- add_unitarity_metadata(func_builder, modified_block.ty.unitary_flags)
60
+ add_metadata(
61
+ func_builder,
62
+ additional_metadata={"unitary": modified_block.ty.unitary_flags.value},
63
+ )
61
64
 
62
65
  # compile body
63
66
  cfg = compile_cfg(modified_block.cfg, func_builder, func_builder.inputs(), ctx)
@@ -26,7 +26,7 @@ from guppylang_internals.definition.ty import OpaqueTypeDef, TypeDef
26
26
  from guppylang_internals.definition.wasm import RawWasmFunctionDef
27
27
  from guppylang_internals.dummy_decorator import _dummy_custom_decorator, sphinx_running
28
28
  from guppylang_internals.engine import DEF_STORE
29
- from guppylang_internals.error import GuppyError
29
+ from guppylang_internals.error import GuppyError, pretty_errors
30
30
  from guppylang_internals.std._internal.checker import WasmCallChecker
31
31
  from guppylang_internals.std._internal.compiler.wasm import (
32
32
  WasmModuleCallCompiler,
@@ -207,6 +207,7 @@ def custom_type(
207
207
  return dec
208
208
 
209
209
 
210
+ @pretty_errors
210
211
  def wasm_module(
211
212
  filename: str,
212
213
  ) -> Callable[[builtins.type[T]], GuppyDefinition]:
@@ -252,6 +253,7 @@ def ext_module_decorator(
252
253
  def fun(
253
254
  filename: str, module: str | None
254
255
  ) -> Callable[[builtins.type[T]], GuppyDefinition]:
256
+ @pretty_errors
255
257
  def dec(cls: builtins.type[T]) -> GuppyDefinition:
256
258
  # N.B. Only one module per file and vice-versa
257
259
  ext_module = type_def(
@@ -33,6 +33,7 @@ from guppylang_internals.definition.common import (
33
33
  ParsableDef,
34
34
  UnknownSourceError,
35
35
  )
36
+ from guppylang_internals.definition.metadata import GuppyMetadata, add_metadata
36
37
  from guppylang_internals.definition.value import (
37
38
  CallableDef,
38
39
  CallReturnWires,
@@ -72,13 +73,22 @@ class RawFunctionDef(ParsableDef):
72
73
 
73
74
  unitary_flags: UnitaryFlags = field(default=UnitaryFlags.NoFlags, kw_only=True)
74
75
 
76
+ metadata: GuppyMetadata | None = field(default=None, kw_only=True)
77
+
75
78
  def parse(self, globals: Globals, sources: SourceMap) -> "ParsedFunctionDef":
76
79
  """Parses and checks the user-provided signature of the function."""
77
80
  func_ast, docstring = parse_py_func(self.python_func, sources)
78
81
  ty = check_signature(
79
82
  func_ast, globals, self.id, unitary_flags=self.unitary_flags
80
83
  )
81
- return ParsedFunctionDef(self.id, self.name, func_ast, ty, docstring)
84
+ return ParsedFunctionDef(
85
+ self.id,
86
+ self.name,
87
+ func_ast,
88
+ ty,
89
+ docstring,
90
+ metadata=self.metadata,
91
+ )
82
92
 
83
93
 
84
94
  @dataclass(frozen=True)
@@ -103,6 +113,8 @@ class ParsedFunctionDef(CheckableDef, CallableDef):
103
113
 
104
114
  description: str = field(default="function", init=False)
105
115
 
116
+ metadata: GuppyMetadata | None = field(default=None, kw_only=True)
117
+
106
118
  def check(self, globals: Globals) -> "CheckedFunctionDef":
107
119
  """Type checks the body of the function."""
108
120
  # Add python variable scope to the globals
@@ -114,6 +126,7 @@ class ParsedFunctionDef(CheckableDef, CallableDef):
114
126
  self.ty,
115
127
  self.docstring,
116
128
  cfg,
129
+ metadata=self.metadata,
117
130
  )
118
131
 
119
132
  def check_call(
@@ -177,7 +190,11 @@ class CheckedFunctionDef(ParsedFunctionDef, MonomorphizableDef):
177
190
  func_def = module.module_root_builder().define_function(
178
191
  self.name, hugr_ty.body.input, hugr_ty.body.output, hugr_ty.params
179
192
  )
180
- add_unitarity_metadata(func_def, self.ty.unitary_flags)
193
+ add_metadata(
194
+ func_def,
195
+ self.metadata,
196
+ additional_metadata={"unitary": self.ty.unitary_flags.value},
197
+ )
181
198
  return CompiledFunctionDef(
182
199
  self.id,
183
200
  self.name,
@@ -187,6 +204,7 @@ class CheckedFunctionDef(ParsedFunctionDef, MonomorphizableDef):
187
204
  self.docstring,
188
205
  self.cfg,
189
206
  func_def,
207
+ metadata=self.metadata,
190
208
  )
191
209
 
192
210
 
@@ -305,8 +323,3 @@ def parse_source(source_lines: list[str], line_offset: int) -> tuple[str, ast.AS
305
323
  else:
306
324
  node = ast.parse(source).body[0]
307
325
  return source, node, line_offset
308
-
309
-
310
- def add_unitarity_metadata(func: hf.Function, flags: UnitaryFlags) -> None:
311
- """Stores unitarity annotations in the metadate of a Hugr function definition."""
312
- func.metadata["unitary"] = flags.value
@@ -0,0 +1,87 @@
1
+ """Metadata attached to objects within the Guppy compiler, both for internal use and to
2
+ attach to HUGR nodes for lower-level processing."""
3
+
4
+ from abc import ABC
5
+ from dataclasses import dataclass, field, fields
6
+ from typing import Any, ClassVar, Generic, TypeVar
7
+
8
+ from hugr.hugr.node_port import ToNode
9
+
10
+ from guppylang_internals.diagnostic import Fatal
11
+ from guppylang_internals.error import GuppyError
12
+
13
+ T = TypeVar("T")
14
+
15
+
16
+ @dataclass(init=True, kw_only=True)
17
+ class GuppyMetadataValue(ABC, Generic[T]):
18
+ """A template class for a metadata value within the scope of the Guppy compiler.
19
+ Implementations should provide the `key` in reverse-URL format."""
20
+
21
+ key: ClassVar[str]
22
+ value: T | None = None
23
+
24
+
25
+ class MetadataMaxQubits(GuppyMetadataValue[int]):
26
+ key = "tket.hint.max_qubits"
27
+
28
+
29
+ @dataclass(frozen=True, init=True, kw_only=True)
30
+ class GuppyMetadata:
31
+ """DTO for metadata within the scope of the guppy compiler for attachment to HUGR
32
+ nodes. See `add_metadata`."""
33
+
34
+ max_qubits: MetadataMaxQubits = field(default_factory=MetadataMaxQubits, init=False)
35
+
36
+ @classmethod
37
+ def reserved_keys(cls) -> set[str]:
38
+ return {f.type.key for f in fields(GuppyMetadata)}
39
+
40
+
41
+ @dataclass(frozen=True)
42
+ class MetadataAlreadySetError(Fatal):
43
+ title: ClassVar[str] = "Metadata key already set"
44
+ message: ClassVar[str] = "Received two values for the metadata key `{key}`"
45
+ key: str
46
+
47
+
48
+ @dataclass(frozen=True)
49
+ class ReservedMetadataKeysError(Fatal):
50
+ title: ClassVar[str] = "Metadata key is reserved"
51
+ message: ClassVar[str] = (
52
+ "The following metadata keys are reserved by Guppy but also provided in "
53
+ "additional metadata: `{keys}`"
54
+ )
55
+ keys: set[str]
56
+
57
+
58
+ def add_metadata(
59
+ node: ToNode,
60
+ metadata: GuppyMetadata | None = None,
61
+ *,
62
+ additional_metadata: dict[str, Any] | None = None,
63
+ ) -> None:
64
+ """Adds metadata to the given node using the keys defined through inheritors of
65
+ `GuppyMetadataValue` defined in the `GuppyMetadata` class.
66
+
67
+ Additional metadata is forwarded as is, although the given dictionary may not
68
+ contain any keys already reserved by fields in `GuppyMetadata`.
69
+ """
70
+ if metadata is not None:
71
+ for f in fields(GuppyMetadata):
72
+ data: GuppyMetadataValue[Any] = getattr(metadata, f.name)
73
+ if data.key in node.metadata:
74
+ raise GuppyError(MetadataAlreadySetError(None, data.key))
75
+ if data.value is not None:
76
+ node.metadata[data.key] = data.value
77
+
78
+ if additional_metadata is not None:
79
+ reserved_keys = GuppyMetadata.reserved_keys()
80
+ used_reserved_keys = reserved_keys.intersection(additional_metadata.keys())
81
+ if len(used_reserved_keys) > 0:
82
+ raise GuppyError(ReservedMetadataKeysError(None, keys=used_reserved_keys))
83
+
84
+ for key, value in additional_metadata.items():
85
+ if key in node.metadata:
86
+ raise GuppyError(MetadataAlreadySetError(None, key))
87
+ node.metadata[key] = value
@@ -1,4 +1,5 @@
1
1
  import ast
2
+ import copy
2
3
  from contextlib import suppress
3
4
  from dataclasses import dataclass, field
4
5
  from typing import ClassVar, NoReturn
@@ -86,7 +87,11 @@ class OverloadedFunctionDef(CompiledCallableDef, CallableDef):
86
87
  assert isinstance(defn, CallableDef)
87
88
  available_sigs.append(defn.ty)
88
89
  with suppress(GuppyError):
89
- return defn.check_call(args, ty, node, ctx)
90
+ # check_call may modify args and node,
91
+ # thus we deepcopy them before passing in the function
92
+ node_copy = copy.deepcopy(node)
93
+ args_copy = copy.deepcopy(args)
94
+ return defn.check_call(args_copy, ty, node_copy, ctx)
90
95
  return self._call_error(args, node, ctx, available_sigs, ty)
91
96
 
92
97
  def synthesize_call(
@@ -98,7 +103,11 @@ class OverloadedFunctionDef(CompiledCallableDef, CallableDef):
98
103
  assert isinstance(defn, CallableDef)
99
104
  available_sigs.append(defn.ty)
100
105
  with suppress(GuppyError):
101
- return defn.synthesize_call(args, node, ctx)
106
+ # synthesize_call may modify args and node,
107
+ # thus we deepcopy them before passing in the function
108
+ node_copy = copy.deepcopy(node)
109
+ args_copy = copy.deepcopy(args)
110
+ return defn.synthesize_call(args_copy, node_copy, ctx)
102
111
  return self._call_error(args, node, ctx, available_sigs)
103
112
 
104
113
  def _call_error(
@@ -46,6 +46,7 @@ from guppylang_internals.std._internal.compiler.array import (
46
46
  array_new,
47
47
  array_unpack,
48
48
  )
49
+ from guppylang_internals.std._internal.compiler.quantum import from_halfturns_unchecked
49
50
  from guppylang_internals.std._internal.compiler.tket_bool import OpaqueBool, make_opaque
50
51
  from guppylang_internals.tys.builtin import array_type, bool_type, float_type
51
52
  from guppylang_internals.tys.subst import Inst, Subst
@@ -235,12 +236,15 @@ class ParsedPytketDef(CallableDef, CompilableDef):
235
236
  lex_names = sorted(param_order)
236
237
  name_to_param = dict(zip(lex_names, lex_params, strict=True))
237
238
  angle_wires = [name_to_param[name] for name in param_order]
238
- # Need to convert all angles to floats.
239
+ # Need to convert all angles to rotations.
239
240
  for angle in angle_wires:
240
241
  [halfturns] = outer_func.add_op(
241
242
  ops.UnpackTuple([FLOAT_T]), angle
242
243
  )
243
- param_wires.append(halfturns)
244
+ rotation = outer_func.add_op(
245
+ from_halfturns_unchecked(), halfturns
246
+ )
247
+ param_wires.append(rotation)
244
248
 
245
249
  # Pass all arguments to call node.
246
250
  call_node = outer_func.call(
@@ -208,7 +208,8 @@ class DiagnosticsRenderer:
208
208
  MAX_MESSAGE_LINE_LEN: Final[int] = 80
209
209
 
210
210
  #: Number of preceding source lines we show to give additional context
211
- PREFIX_CONTEXT_LINES: Final[int] = 2
211
+ PREFIX_ERROR_CONTEXT_LINES: Final[int] = 2
212
+ PREFIX_NOTE_CONTEXT_LINES: Final[int] = 1
212
213
 
213
214
  def __init__(self, source: SourceMap) -> None:
214
215
  self.buffer = []
@@ -243,31 +244,84 @@ class DiagnosticsRenderer:
243
244
  else:
244
245
  span = to_span(diag.span)
245
246
  level = self.level_str(diag.level)
246
- all_spans = [span] + [
247
- to_span(child.span) for child in diag.children if child.span
247
+
248
+ children_with_span = [
249
+ (child, to_span(child.span)) for child in diag.children if child.span
248
250
  ]
251
+ all_spans = [span] + [span for _, span in children_with_span]
249
252
  max_lineno = max(s.end.line for s in all_spans)
253
+
250
254
  self.buffer.append(f"{level}: {diag.rendered_title} (at {span.start})")
255
+
256
+ # Render main error span first
251
257
  self.render_snippet(
252
258
  span,
253
259
  diag.rendered_span_label,
254
260
  max_lineno,
255
261
  is_primary=True,
256
- prefix_lines=self.PREFIX_CONTEXT_LINES,
262
+ prefix_lines=self.PREFIX_ERROR_CONTEXT_LINES,
257
263
  )
258
- # First render all sub-diagnostics that come with a span
259
- for sub_diag in diag.children:
260
- if sub_diag.span:
264
+
265
+ match children_with_span:
266
+ case []:
267
+ pass
268
+ case [(only_child, span)]:
269
+ self.buffer.append("\nNote:")
261
270
  self.render_snippet(
262
- to_span(sub_diag.span),
263
- sub_diag.rendered_span_label,
271
+ span,
272
+ only_child.rendered_span_label,
264
273
  max_lineno,
265
- is_primary=False,
274
+ prefix_lines=self.PREFIX_NOTE_CONTEXT_LINES,
275
+ print_pad_line=True,
266
276
  )
277
+ case [(first_child, first_span), *children_with_span]:
278
+ self.buffer.append("\nNotes:")
279
+ self.render_snippet(
280
+ first_span,
281
+ first_child.rendered_span_label,
282
+ max_lineno,
283
+ prefix_lines=self.PREFIX_NOTE_CONTEXT_LINES,
284
+ print_pad_line=True,
285
+ )
286
+
287
+ prev_span_end_lineno = first_span.end.line
288
+
289
+ for sub_diag, span in children_with_span:
290
+ span_start_lineno = span.start.line
291
+ span_end_lineno = span.end.line
292
+
293
+ # If notes are on the same line, render them together
294
+ if span_start_lineno == prev_span_end_lineno:
295
+ prefix_lines = 0
296
+ print_pad_line = True
297
+ # if notes are close enough, render them adjacently
298
+ elif (
299
+ span_start_lineno - self.PREFIX_NOTE_CONTEXT_LINES
300
+ <= prev_span_end_lineno + 1
301
+ ):
302
+ prefix_lines = span_start_lineno - prev_span_end_lineno - 1
303
+ print_pad_line = False
304
+ # otherwise we render a separator between notes
305
+ else:
306
+ self.buffer.append("")
307
+ prefix_lines = self.PREFIX_NOTE_CONTEXT_LINES
308
+ print_pad_line = False
309
+
310
+ self.render_snippet(
311
+ span,
312
+ sub_diag.rendered_span_label,
313
+ max_lineno,
314
+ prefix_lines=prefix_lines,
315
+ print_pad_line=print_pad_line,
316
+ )
317
+ prev_span_end_lineno = span_end_lineno
318
+
319
+ # Render the main diagnostic message if present
267
320
  if diag.rendered_message:
268
321
  self.buffer.append("")
269
322
  self.buffer += wrap(diag.rendered_message, self.MAX_MESSAGE_LINE_LEN)
270
- # Finally, render all sub-diagnostics that have a non-span message
323
+
324
+ # Render all sub-diagnostics that have a non-span message
271
325
  for sub_diag in diag.children:
272
326
  if sub_diag.rendered_message:
273
327
  self.buffer.append("")
@@ -281,8 +335,9 @@ class DiagnosticsRenderer:
281
335
  span: Span,
282
336
  label: str | None,
283
337
  max_lineno: int,
284
- is_primary: bool,
338
+ is_primary: bool = False,
285
339
  prefix_lines: int = 0,
340
+ print_pad_line: bool = False,
286
341
  ) -> None:
287
342
  """Renders the source associated with a span together with an optional label.
288
343
 
@@ -315,7 +370,8 @@ class DiagnosticsRenderer:
315
370
  Optionally includes up to `prefix_lines` preceding source lines to give
316
371
  additional context.
317
372
  """
318
- # Check how much space we need to reserve for the leading line numbers
373
+ # Check how much horizontal space we need to reserve for the leading
374
+ # line numbers
319
375
  ll_length = len(str(max_lineno))
320
376
  highlight_char = "^" if is_primary else "-"
321
377
 
@@ -324,8 +380,9 @@ class DiagnosticsRenderer:
324
380
  ll = "" if line_number is None else str(line_number)
325
381
  self.buffer.append(" " * (ll_length - len(ll)) + ll + " | " + line)
326
382
 
327
- # One line of padding
328
- render_line("")
383
+ # One line of padding (primary span, first note or between same line notes)
384
+ if is_primary or print_pad_line:
385
+ render_line("")
329
386
 
330
387
  # Grab all lines we want to display and remove excessive leading whitespace
331
388
  prefix_lines = min(prefix_lines, span.start.line - 1)
@@ -220,21 +220,12 @@ class CompilationEngine:
220
220
 
221
221
  This is the main driver behind `guppy.check()`.
222
222
  """
223
- from guppylang_internals.checker.core import Globals
224
-
225
223
  # Clear previous compilation cache.
226
224
  # TODO: In order to maintain results from the previous `check` call we would
227
225
  # need to store and check if any dependencies have changed.
228
226
  self.reset()
229
227
 
230
- defn = DEF_STORE.raw_defs[id]
231
- self.to_check_worklist = {
232
- defn.id: (
233
- defn.parse(Globals(DEF_STORE.frames[defn.id]), DEF_STORE.sources)
234
- if isinstance(defn, ParsableDef)
235
- else defn
236
- )
237
- }
228
+ self.to_check_worklist[id] = self.get_parsed(id)
238
229
  while self.types_to_check_worklist or self.to_check_worklist:
239
230
  # Types need to be checked first. This is because parsing e.g. a function
240
231
  # definition requires instantiating the types in its signature which can