Skip to content

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

Draft
wants to merge 17 commits into
base: main
Choose a base branch
from
Draft

feat: Declare WASM modules in guppy #942

wants to merge 17 commits into from

Conversation

croyzor
Copy link
Collaborator

@croyzor croyzor commented May 7, 2025

TODO:

  • Add a parsing step to the WasmModuleDef class to check for parameters like
@wasm_module(blah)
class Foo(Generic[T]):
    ...

(this is required for generic size arrays to work)

  • A method for getting n contexts

@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 77.35192% with 65 lines in your changes missing coverage. Please review.

Project coverage is 92.43%. Comparing base (ba2eaab) to head (9b3bf0b).

Files with missing lines Patch % Lines
guppylang/std/qsystem/wasm.py 0.00% 37 Missing ⚠️
guppylang/definition/custom.py 84.78% 21 Missing ⚠️
guppylang/decorator.py 93.75% 2 Missing ⚠️
guppylang/tys/constarg.py 83.33% 2 Missing ⚠️
guppylang/tys/ty.py 90.00% 2 Missing ⚠️
guppylang/tys/printing.py 66.66% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #942      +/-   ##
==========================================
- Coverage   92.85%   92.43%   -0.43%     
==========================================
  Files          85       88       +3     
  Lines        9901    10180     +279     
==========================================
+ Hits         9194     9410     +216     
- Misses        707      770      +63     

☔ 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 +600 to +602
def wasm_module(
self, filename: str, filehash: int
) -> Decorator[PyClass, GuppyDefinition]:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Note that your decorator doesn't work with explicit modules.

I think that's fine as long as we merge #983 first

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

@@ -150,6 +151,10 @@ def _visit_ConstArg(self, arg: ConstArg, inside_row: bool) -> str:
def _visit_ConstValue(self, c: ConstValue, inside_row: bool) -> str:
return str(c.value)

@_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 👍

@@ -663,6 +664,30 @@ def transform(self, transformer: Transformer) -> "Type":
)


@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?

@@ -82,6 +82,9 @@ class GuppyModule:
# Storage for source code that has been read by the compiler
_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

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
4 participants