Skip to content

fix: add wasmtime flags! macro support for WIT flags types#1327

Open
jsturtevant wants to merge 1 commit into
mainfrom
rally/1318-wasm-guest-bindgen-macro-doesn-t-support-wit-flags
Open

fix: add wasmtime flags! macro support for WIT flags types#1327
jsturtevant wants to merge 1 commit into
mainfrom
rally/1318-wasm-guest-bindgen-macro-doesn-t-support-wit-flags

Conversation

@jsturtevant

@jsturtevant jsturtevant commented Mar 19, 2026

Copy link
Copy Markdown
Contributor

When is_wasmtime_guest is true, emit wasmtime::component::flags! macro invocations instead of plain structs with bool fields. This enables WIT flags types (e.g. from wasi:filesystem) to satisfy the wasmtime::component::Lift and wasmtime::component::Lower trait bounds. See https://docs.rs/wasmtime/latest/wasmtime/component/macro.flags.html

The non-Wasmtime code path used by host_bindgen! and Hyperlight guest_bindgen! in this repo is unchanged. The Wasmtime path is used by downstream Wasmtime guest-bindgen consumers.

WIT flags input

The tests use the existing WIT flags in src/tests/rust_guests/witguest/guest.wit:

interface roundtrip {
    flags smallflags {
        flag-a,
        flag-b,
        flag-c,
    }

    roundtrip-flags-small: func(x: smallflags) -> smallflags;
}

Expected non-Wasmtime Rust output

host_bindgen! and Hyperlight guest_bindgen! keep generating a bool-field struct:

#[derive(Debug, Clone, PartialEq)]
pub struct Smallflags {
    pub flag_a: bool,
    pub flag_b: bool,
    pub flag_c: bool,
}

Expected Wasmtime guest Rust output

The Wasmtime guest generator emits the real Wasmtime flags macro and preserves the original WIT names on each constant:

::wasmtime::component::flags! {
    Smallflags {
        #[component(name = "flag-a")]
        const FLAG_A;

        #[component(name = "flag-b")]
        const FLAG_B;

        #[component(name = "flag-c")]
        const FLAG_C;
    }
}

Expected marshal/unmarshal output

Because the type produced by wasmtime::component::flags! is bitflag-style, the Hyperlight-generated Wasmtime guest marshal/unmarshal code cannot access fields like value.flag_a.

For non-Wasmtime bindings, Hyperlight still generates code that reads and writes bool fields:

// Unmarshal bytes into the bool-field struct.
(
    Smallflags {
        flag_a: (bytes[0] >> 0) & 0x1 == 1,
        flag_b: (bytes[0] >> 1) & 0x1 == 1,
        flag_c: (bytes[0] >> 2) & 0x1 == 1,
    },
    1,
)

// Marshal the bool-field struct into bytes.
let mut bytes = [0; 1];
bytes[0] |= (if value.flag_a { 1 } else { 0 }) << 0;
bytes[0] |= (if value.flag_b { 1 } else { 0 }) << 1;
bytes[0] |= (if value.flag_c { 1 } else { 0 }) << 2;
alloc::vec::Vec::from(bytes)

For Wasmtime guest bindings, Hyperlight now generates code that uses the Wasmtime flags API:

// Unmarshal bytes into the Wasmtime flags value.
{
    let mut value_flags = Smallflags::empty();
    if (bytes[0] >> 0) & 0x1 == 1 {
        value_flags |= Smallflags::FLAG_A;
    }
    if (bytes[0] >> 1) & 0x1 == 1 {
        value_flags |= Smallflags::FLAG_B;
    }
    if (bytes[0] >> 2) & 0x1 == 1 {
        value_flags |= Smallflags::FLAG_C;
    }
    (value_flags, 1)
}

// Marshal the Wasmtime flags value into bytes.
let mut bytes = [0; 1];
bytes[0] |= (if value.contains(Smallflags::FLAG_A) { 1 } else { 0 }) << 0;
bytes[0] |= (if value.contains(Smallflags::FLAG_B) { 1 } else { 0 }) << 1;
bytes[0] |= (if value.contains(Smallflags::FLAG_C) { 1 } else { 0 }) << 2;
alloc::vec::Vec::from(bytes)

Closes #1318

Signed-off-by: James Sturtevant jsturtevant@gmail.com

@jsturtevant jsturtevant added the kind/bugfix For PRs that fix bugs label Mar 19, 2026
@jsturtevant jsturtevant marked this pull request as draft March 20, 2026 20:42
@jsturtevant

Copy link
Copy Markdown
Contributor Author

moved this to draft, I was integrating into hyperlight-wasm and found the usage of flags needs to be slightly different. than this. will push an update shortly

@jsturtevant jsturtevant force-pushed the rally/1318-wasm-guest-bindgen-macro-doesn-t-support-wit-flags branch 6 times, most recently from f0c8e64 to b68803b Compare June 19, 2026 15:48
Comment thread src/tests/rust_guests/witguest/bindgen-test-cases/world.wit Outdated
@jsturtevant jsturtevant force-pushed the rally/1318-wasm-guest-bindgen-macro-doesn-t-support-wit-flags branch 9 times, most recently from b319154 to 564c59e Compare June 19, 2026 16:49
@jsturtevant jsturtevant marked this pull request as ready for review June 19, 2026 16:49
Copilot AI review requested due to automatic review settings June 19, 2026 16:49
Signed-off-by: James Sturtevant <jsturtevant@gmail.com>
@jsturtevant jsturtevant force-pushed the rally/1318-wasm-guest-bindgen-macro-doesn-t-support-wit-flags branch from 564c59e to 6e6617d Compare June 19, 2026 16:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the Hyperlight component Rust code generator to correctly support WIT flags types when generating Wasmtime guest bindings, by emitting wasmtime::component::flags! (and updating marshal/unmarshal codegen accordingly) so the generated types satisfy Wasmtime’s Lift/Lower trait bounds. Non-Wasmtime generation paths remain unchanged.

Changes:

  • Emit ::wasmtime::component::flags! { ... } for WIT flags types when is_wasmtime_guest is enabled (instead of a pub struct with bool fields).
  • Update Hyperlight marshal/unmarshal codegen for Wasmtime guests to use the Wasmtime flags API (empty(), |=, .contains(...)) rather than field access.
  • Add an integration test validating the Wasmtime flags macro emission and wire it into just test-integration.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/hyperlight_component_util/tests/wasmtime_guest_codegen.rs Adds an integration test to validate Wasmtime guest codegen emits wasmtime::component::flags! for WIT flags.
src/hyperlight_component_util/src/rtypes.rs Switches Wasmtime-guest flags type emission from bool-field structs to the Wasmtime flags! macro, preserving original WIT names.
src/hyperlight_component_util/src/hl.rs Updates marshal/unmarshal codegen for flags in the Wasmtime-guest path to use flags operations (empty, `
src/hyperlight_component_util/src/emit.rs Introduces kebab_to_flags_const helper for generating SCREAMING_SNAKE_CASE flag constant identifiers.
Justfile Ensures test-integration runs after generating WIT-derived .wasm inputs and runs the new component-util integration test.

Comment on lines +36 to +46
assert!(generated.contains("::wasmtime::component::flags! {"));
assert!(generated.contains("Smallflags {"));
assert!(generated.contains("\"flag-a\""));
assert!(generated.contains("const FLAG_A;"));
assert!(generated.contains("\"flag-b\""));
assert!(generated.contains("const FLAG_B;"));
assert!(generated.contains("\"flag-c\""));
assert!(generated.contains("const FLAG_C;"));
assert!(!generated.contains("pub flag_a: bool"));
assert!(!generated.contains("pub flag_b: bool"));
assert!(!generated.contains("pub flag_c: bool"));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

eh, I considered this but feels not quite right. There isn't really a great way to test this e2e in this repo since it really requires a whole wasmtime guest. What do others think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/bugfix For PRs that fix bugs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

wasm_guest_bindgen! macro doesn't support WIT flags types (needed for wasi:filesystem)

2 participants