-
Notifications
You must be signed in to change notification settings - Fork 18
feat: Declare WASM modules in guppy #942
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #942 +/- ##
==========================================
+ Coverage 92.17% 92.26% +0.08%
==========================================
Files 106 110 +4
Lines 10509 10702 +193
==========================================
+ Hits 9687 9874 +187
- Misses 822 828 +6 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
mark-koch
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Craig! I left a bunch of nits and suggestions while looking through the code, but my main points are:
- I'd prefer to use an
OpaqueTypeinstead of addingWasmModuleTypeto the core type system - Instead of
CustomFunctionDefs , I think it would be cleaner to add aWasmFunctionDefthat takes care of checking the signature instead of doing it at the call-site. You could follow the pattern inguppylang.definition.functionfor how to set it up and mostly copy the code you already have from theCustomCallCheckers andCustomCallCompilers.
Also, it would be good to have some tests for the new error messages you added
| from guppylang.tys.ty import Type | ||
|
|
||
|
|
||
| class WasmError(Error): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| class WasmError(Error): | |
| @dataclass(frozen=True) | |
| class WasmError(Error): |
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class WasmTypeConversionError(Error): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be in errors/wasm.py?
guppylang/definition/custom.py
Outdated
| case FuncInput(ty=ty, flags=InputFlags.Inout) if isinstance( | ||
| ty, WasmModuleType | ||
| ): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| case FuncInput(ty=ty, flags=InputFlags.Inout) if isinstance( | |
| ty, WasmModuleType | |
| ): | |
| case FuncInput(ty=WasmModuleType(), flags=InputFlags.Inout): |
guppylang/definition/custom.py
Outdated
| case FuncInput(ty=ty): | ||
| raise GuppyError(FirstArgNotModule(self.node, ty)) | ||
| for inp in self.func.ty.inputs[1:]: | ||
| assert isinstance(inp, FuncInput) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assert seems unnecessary?
guppylang/tys/constarg.py
Outdated
| class ConstStringArg(ConstArg): | ||
| def to_hugr(self) -> ht.TypeArg: | ||
| match self.const: | ||
| case ConstValue(value=v, ty=ty) if is_string_type(ty): | ||
| assert isinstance(v, str) | ||
| return ht.StringArg(v) | ||
| case _: | ||
| return super().to_hugr() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why a new subclass instead of adding this logic to ConstArg.to_hugr?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes I meant to highlight this - adding the logic to ConstArg.to_hugr makes sense, but introduces a cyclic dependency guppylang.tys.arg.py <-> guppylang.tys.builtin.py
guppylang/tys/printing.py
Outdated
|
|
||
| @_visit.register | ||
| def _visit_WasmModuleType(self, w: WasmModuleType, inside_row: bool) -> str: | ||
| return f"WasmModuleType(defn={w.defn!s})" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the type that is printed to users. It should be the name that the user gave to the class defining the module
guppylang/tys/ty.py
Outdated
| ty = wasm().get_type("context") | ||
| return ty.instantiate([]) | ||
|
|
||
| # TODO: I don't know what to write here |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What you wrote is valid since WasmModuleType contains no nested types 👍
guppylang/tys/ty.py
Outdated
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class WasmModuleType(TypeBase): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really don't like the idea of adding wasm to the core type system.
Especially since you're just wrapping a definition and no other logic, why not use an OpaqueType? Everywhere you check for a WasmModuleType, you could just check that it's opaque and the definition matches?
guppylang/module.py
Outdated
| _sources: SourceMap | ||
|
|
||
| # Live WASM contexts | ||
| _next_wasm_context: int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could also do
_wasm_ids = itertools.count()Then, you can just do next(self._wasm_ids) and the counter goes up automatically
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or even better, move the counter to WasmModuleDef instead of sticking it to the module
|
I think I've addressed all of the comments (apart from the
|
| return self.ty | ||
|
|
||
|
|
||
| class WasmModuleTypeDef(OpaqueTypeDef): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turn this into a dataclass, then you don't need to define __init__
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, maybe we should move this into a definitions/wasm.py file, doesn't really feel like a builtin?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dataclass magic doesn't work here because it inherits from OpaqueTypeDef which has a default argument
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I see, one workaround is setting your new fields to keyword-only:
@dataclass(frozen=True)
class WasmModuleTypeDef(OpaqueTypeDef):
wasm_file: str = field(kw_only=True)
wasm_hash: int = field(kw_only=True)or remove the default from OpaqueTypeDef.bound,
or leave it as is :D - your call!
guppylang/tys/builtin.py
Outdated
| raise InternalGuppyError("Unexpected type args") | ||
|
|
||
|
|
||
| class ConstStringArg(ConstArg): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a duplicate of the same thing in constarg.py
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops!
guppylang/tys/constarg.py
Outdated
| from guppylang.tys.const import ConstValue | ||
|
|
||
|
|
||
| class ConstStringArg(ConstArg): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I can tell, this is only used in WasmModuleCallCompiler.compile_with_inouts, i.e. not in any place that actually concerns the Guppy type system, so I'm not sure why this is needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it's just for passing into the wasm.lookup op - I put it in a general place in case it was useful in the future 🤷
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But it shouldn't be a subclass of ConstArg, just an instace with the ty field set to string. If it were needed for this PR it would be ok as a hack, but if it's not needed I'd prefer to get rid of it and do it properly in the future
guppylang/tys/constarg.py
Outdated
| return super().to_hugr() | ||
|
|
||
|
|
||
| def const_string_arg(s: str) -> ConstStringArg: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be unused
guppylang/checker/errors/wasm.py
Outdated
| class FirstArgNotModule(WasmError): | ||
| span_label: ClassVar[str] = ( | ||
| "First argument to WASM function should be a reference to a WASM module." | ||
| " Found {ty} instead" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| " Found {ty} instead" | |
| " Found `{ty}` instead" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Backticks still missing here
| # Compiler for initialising WASM modules | ||
| class WasmModuleInitCompiler(CustomInoutCallCompiler): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you turn this into a docstring? (Same for the othert classes below)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a test for calling wasm methods and spawning contexts in comptime functions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure where this diff is coming from, you didn't change pyproject.toml?
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ First argument to WASM function should be a reference to a | ||
| | WASM module. Found int instead |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you change the error location to point to the first argument?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not really sure how - the FunctionType that comes out of parsing CustomFunctionDefs doesn't have location info...
(We'd want to highlight the signature of the function as written, rather than the call site, which would be easier)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see, yeah this hard without writing your own signature parsing - happy to leave it as is then
Co-authored-by: Mark Koch <[email protected]>
Co-authored-by: Mark Koch <[email protected]>
mark-koch
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! Just a few nits, happy to merge 👍
guppylang/definition/wasm.py
Outdated
| if isinstance(parsed.ty, FunctionType): | ||
| self.sanitise_type(parsed.defined_at, parsed.ty) | ||
| else: | ||
| raise GuppyError(NonFunctionWasmType(parsed.defined_at, self.func.ty)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't CustomFunctionDef.ty already annotated as FunctionType? The isinstance check should be unnecessary
| ) | ||
|
|
||
| # Get the WASM module information from the type | ||
| assert isinstance(self.node, GlobalCall | LocalCall) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this assert is needed anymore?
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ First argument to WASM function should be a reference to a | ||
| | WASM module. Found int instead |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see, yeah this hard without writing your own signature parsing - happy to leave it as is then
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's still a big diff here?
guppylang/checker/errors/wasm.py
Outdated
| class FirstArgNotModule(WasmError): | ||
| span_label: ClassVar[str] = ( | ||
| "First argument to WASM function should be a reference to a WASM module." | ||
| " Found {ty} instead" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Backticks still missing here
🤖 I have created a release *beep* *boop* --- ## [0.21.0](guppylang-v0.20.0...guppylang-v0.21.0) (2025-08-04) ### ⚠ BREAKING CHANGES * All compiler-internal and non-userfacing functionality is moved into a new `guppylang_internals` package * `guppy.compile(foo)` and `guppy.check(foo)` replaced with `foo.check()` and `foo.compile()` * default HUGR output uses compressed binary encoding. * `guppylang.tracing.object.GuppyDefinition` moved to `guppylang.defs.GuppyDefinition` `guppylang.tracing.object.TypeVarGuppyDefinition` moved and renamed to `guppylang.defs.GuppyTypeVarDefinition` * All `to_hugr` methods on types, arguments, and parameters now require a `ToHugrContext` `CompileableDef.compile_outer` now requires a `ToHugrContext` `guppy.hugr_op` now passes the compiler context to the function generating the op `CheckedFunctionDef` now implements `MonomorphizableDef` instead of `CompileableDef` `CompilerContext.build_compiled_def` now requires an instantiation for the definition's type parameters The `ToHugrContext` protocol now requires two additional methods: `type_var_to_hugr` and `const_var_to_hugr` `CompilerContext.{compiled, worklist}` and `CompilationEngine.compiled` are now indexed by a tuple of `DefId` and optional `PartiallyMonomorphizedArgs` * comptime code that previously used constant integers outside i64 will now fail to compile. * Capturing closures are now disabled by default. Enabling them requires calling `guppylang.enable_experimental_features()`, however note that they are not supported throughout the stack. ### Features * Add `Future` type ([#1075](#1075)) ([5ad7673](5ad7673)) * add error when constant integer out of bounds ([#1084](#1084)) ([eee77ae](eee77ae)) * Add guppy version metadata to hugr entrypoint ([#1039](#1039)) ([0eafbd9](0eafbd9)), closes [#1037](#1037) * Add manual registration of extensions ([#1045](#1045)) ([4b42936](4b42936)) * add qsystem op for measure leaked ([#1057](#1057)) ([c555727](c555727)) * add selene via optional feature and use for testing ([#1081](#1081)) ([cefc70e](cefc70e)) * Add support for V and Vdg. ([#1094](#1094)) ([6b0d44a](6b0d44a)) * Allow indexing on tuples ([#1038](#1038)) ([0e9097e](0e9097e)), closes [#711](#711) * Declare WASM modules in guppy ([#942](#942)) ([e1240fb](e1240fb)) * Extend comptime arguments to arbitrary non-linear types ([#1110](#1110)) ([384dd8c](384dd8c)) * Make decorator return types more precise ([#1115](#1115)) ([c84e8b1](c84e8b1)) * set hugr entrypoint to compiled function ([#1063](#1063)) ([16bd267](16bd267)) * store used extensions and versions in HUGR metadata ([#1049](#1049)) ([a9a300c](a9a300c)), closes [#1048](#1048) * Support arbitrary const generics via monomorphisation ([#1033](#1033)) ([bcf9865](bcf9865)) * Support Python 3.12 generic syntax ([#1051](#1051)) ([ab2e118](ab2e118)), closes [#823](#823) * Top level compile + emulate Interface ([#1127](#1127)) ([5e2f595](5e2f595)) * update to hugr-py v0.13 ([#1083](#1083)) ([8f071c8](8f071c8)) * use `core.` prefix for metadata keys ([#1055](#1055)) ([2bf0d68](2bf0d68)) ### Bug Fixes * Allow array comprehension syntax in comptime functions ([#1068](#1068)) ([da8f04a](da8f04a)), closes [#1067](#1067) * Allow struct redefinitions for Python < 3.13 ([#1108](#1108)) ([959a4e4](959a4e4)), closes [#1107](#1107) * Correctly detect `[@Custom](https://github.com/custom)_guppy_decorator` in nested scopes ([#1086](#1086)) ([678583c](678583c)) * Fix diagnostics rendering for comptime entrypoints ([#1099](#1099)) ([fdd2676](fdd2676)), closes [#1097](#1097) * Fix frame lookup for Python 3.12 annotation scopes ([#1120](#1120)) ([a69e489](a69e489)), closes [#1116](#1116) * Fix hugr conversion and bounds checks on numeric literals ([#1100](#1100)) ([73d5e92](73d5e92)) * Fix Jupyter notebook diagnostic rendering ([#1109](#1109)) ([6002474](6002474)) * Fix nested function definitions in Python 3.12 ([#1064](#1064)) ([090f920](090f920)) * Stop showing temporary variables in comptime diagnostics ([#1112](#1112)) ([63854c5](63854c5)), closes [#1111](#1111) * support comptime entrypoint ([#1079](#1079)) ([721e3dd](721e3dd)) * Turn capturing closures into experimental feature ([#1065](#1065)) ([a959b18](a959b18)) ### Documentation * add docstrings for emulator module ([#1131](#1131)) ([b33e065](b33e065)) * add quantum and qsystem gate definitions ([#912](#912)) ([32a4bbc](32a4bbc)) * Fix docstrings ([#1128](#1128)) ([0aded85](0aded85)) * Improve RNG docs ([#1043](#1043)) ([8640f06](8640f06)) * replace `compile_module` usage in README ([#1041](#1041)) ([03ccf3a](03ccf3a)) * Update guppy examples ([#1121](#1121)) ([b994655](b994655)) ### Code Refactoring * Split up into `guppylang_internals` package ([#1126](#1126)) ([81d50c0](81d50c0)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: Seyon Sivarajah <[email protected]>
No description provided.