Skip to content

Conversation

@croyzor
Copy link
Collaborator

@croyzor croyzor commented May 7, 2025

No description provided.

@qartik qartik mentioned this pull request May 7, 2025
@qartik qartik linked an issue May 9, 2025 that may be closed by this pull request
@codecov-commenter
Copy link

codecov-commenter commented May 9, 2025

Codecov Report

Attention: Patch coverage is 95.93909% with 8 lines in your changes missing coverage. Please review.

Project coverage is 92.26%. Comparing base (2bf0d68) to head (4565fb9).

Files with missing lines Patch % Lines
guppylang/std/_internal/compiler/tket2_exts.py 80.95% 4 Missing ⚠️
guppylang/std/_internal/checker.py 71.42% 2 Missing ⚠️
guppylang/std/qsystem/wasm.py 88.88% 1 Missing ⚠️
guppylang/tys/builtin.py 94.11% 1 Missing ⚠️
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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@croyzor croyzor changed the title 🚧 Wasm WIP 🚧 feat: Declare WASM modules in guppy May 28, 2025
@croyzor croyzor requested a review from mark-koch May 28, 2025 13:15
Copy link
Collaborator

@mark-koch mark-koch left a 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 OpaqueType instead of adding WasmModuleType to the core type system
  • Instead of CustomFunctionDefs , I think it would be cleaner to add a WasmFunctionDef that takes care of checking the signature instead of doing it at the call-site. You could follow the pattern in guppylang.definition.function for how to set it up and mostly copy the code you already have from the CustomCallCheckers and CustomCallCompilers.

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):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class WasmError(Error):
@dataclass(frozen=True)
class WasmError(Error):



@dataclass(frozen=True)
class WasmTypeConversionError(Error):
Copy link
Collaborator

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?

Comment on lines 351 to 353
case FuncInput(ty=ty, flags=InputFlags.Inout) if isinstance(
ty, WasmModuleType
):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
case FuncInput(ty=ty, flags=InputFlags.Inout) if isinstance(
ty, WasmModuleType
):
case FuncInput(ty=WasmModuleType(), flags=InputFlags.Inout):

case FuncInput(ty=ty):
raise GuppyError(FirstArgNotModule(self.node, ty))
for inp in self.func.ty.inputs[1:]:
assert isinstance(inp, FuncInput)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assert seems unnecessary?

Comment on lines 8 to 15
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()
Copy link
Collaborator

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?

Copy link
Collaborator Author

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


@_visit.register
def _visit_WasmModuleType(self, w: WasmModuleType, inside_row: bool) -> str:
return f"WasmModuleType(defn={w.defn!s})"
Copy link
Collaborator

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

ty = wasm().get_type("context")
return ty.instantiate([])

# TODO: I don't know what to write here
Copy link
Collaborator

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 👍



@dataclass(frozen=True)
class WasmModuleType(TypeBase):
Copy link
Collaborator

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?

_sources: SourceMap

# Live WASM contexts
_next_wasm_context: int
Copy link
Collaborator

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

Copy link
Collaborator

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

@croyzor croyzor requested a review from mark-koch June 23, 2025 13:59
@croyzor
Copy link
Collaborator Author

croyzor commented Jun 23, 2025

I think I've addressed all of the comments (apart from the ConstStringArg business, which is annoying.

  • Counting the number of guppy contexts has been replaced by introducing the spawn_wasm_contexts op which (thanks to recent guppy changes) can now take a nat @comptime arg to say how many contexts to spawn, which should be the preferred way to create wasm contexts.
  • Have removed WasmModule from the type system, using a subclass of OpaqueTypeDef instead, which has extra fields to keep track of the WASM metadata
  • Added tests of error messages
  • Cleaned up a fair bit

return self.ty


class WasmModuleTypeDef(OpaqueTypeDef):
Copy link
Collaborator

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__

Copy link
Collaborator

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?

Copy link
Collaborator Author

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

Copy link
Collaborator

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!

raise InternalGuppyError("Unexpected type args")


class ConstStringArg(ConstArg):
Copy link
Collaborator

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops!

from guppylang.tys.const import ConstValue


class ConstStringArg(ConstArg):
Copy link
Collaborator

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?

Copy link
Collaborator Author

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 🤷

Copy link
Collaborator

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

return super().to_hugr()


def const_string_arg(s: str) -> ConstStringArg:
Copy link
Collaborator

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

class FirstArgNotModule(WasmError):
span_label: ClassVar[str] = (
"First argument to WASM function should be a reference to a WASM module."
" Found {ty} instead"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
" Found {ty} instead"
" Found `{ty}` instead"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Backticks still missing here

Comment on lines 26 to 27
# Compiler for initialising WASM modules
class WasmModuleInitCompiler(CustomInoutCallCompiler):
Copy link
Collaborator

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)

Copy link
Collaborator

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

Copy link
Collaborator

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?

Comment on lines 6 to 7
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ First argument to WASM function should be a reference to a
| WASM module. Found int instead
Copy link
Collaborator

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?

Copy link
Collaborator Author

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)

Copy link
Collaborator

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

@croyzor croyzor requested a review from mark-koch June 24, 2025 15:24
Copy link
Collaborator

@mark-koch mark-koch left a 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 👍

Comment on lines 58 to 61
if isinstance(parsed.ty, FunctionType):
self.sanitise_type(parsed.defined_at, parsed.ty)
else:
raise GuppyError(NonFunctionWasmType(parsed.defined_at, self.func.ty))
Copy link
Collaborator

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)
Copy link
Collaborator

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?

Comment on lines 6 to 7
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ First argument to WASM function should be a reference to a
| WASM module. Found int instead
Copy link
Collaborator

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

Copy link
Collaborator

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?

class FirstArgNotModule(WasmError):
span_label: ClassVar[str] = (
"First argument to WASM function should be a reference to a WASM module."
" Found {ty} instead"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Backticks still missing here

@croyzor croyzor enabled auto-merge June 26, 2025 08:31
@croyzor croyzor added this pull request to the merge queue Jun 26, 2025
Merged via the queue into main with commit e1240fb Jun 26, 2025
3 checks passed
@croyzor croyzor deleted the cr/wasm-wip branch June 26, 2025 08:39
github-merge-queue bot pushed a commit that referenced this pull request Aug 4, 2025
🤖 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 &lt; 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]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for wasm

5 participants