Skip to content

wit-bindgen C++ passes the wrong enum value in option<list<variant>> #1683

Open

Description

@xiaozzzZZzzz240

Summary:wit-bindgen-cpp encodes wrong enum discriminant for enum payload inside variant inside option, producing incorrect values.

Bug report: C++ consumers generated by wit-bindgen-cpp lift/lower the enum case inside an option-wrapped variant with a wrong discriminant (e3 becomes e1), yielding wrong output values regardless of provider language.

Environment

component version
OS macOS 15.7 (Darwin 24.6.0), x86_64
jco 1.16.1 (source checkout, tag jco-v1.16.1)
Node.js v22.22.2
wac wac-cli 0.8.1
wasi-sdk 27.0 (wasm32-wasip2-clang/clang++)

Steps to reproduce

The full chain is: compile the guest sources into a provider component and a consumer component, compose them with wac, then run the composed component. Prebuilt copies of every artifact are included (components/provider.wasm, components/consumer.wasm, composed.wasm), so you can also skip straight to step 5.

Github repo:https://github.com/xiaozzzZZzzz240/G028-Cplusplus-bindings-return-e1-instead-of-e3-for-enum-payload-in-option

  1. Clone this repository:

    git clone <THIS_REPO_URL>
    cd <THIS_REPO_DIR>
  2. Compile the provider component from the C sources in src/provider-C/ (toolchain versions in the table above; the WIT package is in wit/):

    wasm32-wasip2-clang -o components/provider.wasm -mexec-model=reactor src/provider-C/world9-wi7n9u47_generated.c src/provider-C/world9_wi7n9u47.c src/provider-C/world9_wi7n9u47_component_type.o
  3. Compile the consumer component from the C++ sources in src/consumer-Cpp/:

    wasm32-wasip2-clang++ -std=c++23 -fno-exceptions -o components/consumer.wasm -mexec-model=reactor src/consumer-Cpp/world9b-mul38v8w_generated.cpp src/consumer-Cpp/world9b_mul38v8w.cpp src/consumer-Cpp/world9b_mul38v8w_component_type.o
  4. Compose the two components into one runnable component with wac 0.8.1:

    wac plug components/consumer.wasm --plug components/provider.wasm -o composed.wasm
  5. Run the composed component with jco:

    npm install -g @bytecodealliance/jco@1.16.1
    jco run composed.wasm

Expected result

The composed component should print exactly the following, then exit 0:

"resinst0"
"resinst1"
"resinst2"
"resinst0"
resource("temres36")
"c2"
true
"c0"
resource("temres39")
(resource("temres40"), "bar")
"c2"
true
"temres39"
"temres44"
some([c1(enum(e3))])
resource("temres6")
"c0"
true
7
true

Actual result

Under jco the same composed.wasm exits with rc=0. stdout:

"resinst0"
"resinst1"
"resinst2"
"resinst0"
resource("temres36")
"c2"
true
"c0"
resource("temres39")
(resource("temres40"), "bar")
"c2"
true
"temres39"
"temres44"
some([c1(enum(e1))])
resource("temres6")
"c0"
true
7
true

stderr:

(empty)

wasmtime on the exact same file: rc=0

"resinst0"
"resinst1"
"resinst2"
"resinst0"
resource("temres36")
"c2"
true
"c0"
resource("temres39")
(resource("temres40"), "bar")
"c2"
true
"temres39"
"temres44"
some([c1(enum(e1))])
resource("temres6")
"c0"
true
7
true

Both executing result in jco and wasmtime are not as expected.

Activity

  1. tschneidereit commented on Aug 14, 2026

    @tschneidereit
    Member

    @xiaozzzZZzzz240 thank you for the report. Please note though our AI Tool Use Policy: this report seems pretty clearly fully tool-generated, including the title. Please make sure that you can take on full responsibility for everything you post, and that everything is structured and worded in a way that's suited for consumption by humans.

    In this particular case, please at least adapt the title, and make sure you properly reduce the report. If I understand correctly, this has nothing to do with JCO, but given that the report makes heavy mention of it, I'm not entirely sure.

  2. changed the title [-]G028-wit-bindgen-cpp-wrong-enum-discriminant-in-option-variant-payload[/-] [+]G028-C++-bindings-return-e1-instead-of-e3-for-enum-payload-in-option[/+] on Aug 17, 2026
  3. changed the title [-]G028-C++-bindings-return-e1-instead-of-e3-for-enum-payload-in-option[/-] [+]G028-C++ bindings return e1 instead of e3 for enum payload in option[/+] on Aug 17, 2026
  4. xiaozzzZZzzz240 commented on Aug 17, 2026

    @xiaozzzZZzzz240
    Author

    @xiaozzzZZzzz240 thank you for the report. Please note though our AI Tool Use Policy: this report seems pretty clearly fully tool-generated, including the title. Please make sure that you can take on full responsibility for everything you post, and that everything is structured and worded in a way that's suited for consumption by humans.

    In this particular case, please at least adapt the title, and make sure you properly reduce the report. If I understand correctly, this has nothing to do with JCO, but given that the report makes heavy mention of it, I'm not entirely sure.

    Thanks for your reply!
    I have reviewed this report once again, revised the title, and made the report content more concise.

    Minimized Bug Report

    Summary

    The C++ bindings from wit-bindgen have a bug. It happens with an option<list<variant>> argument when a variant case holds an enum value.
    The generated code prepares the call data inside the caller's own array. It does not copy the list to a new buffer first. It writes the case tag into the first byte of each element. But the enum value lives in that same byte. So the enum value gets destroyed. Then the code reads the enum value from that byte. It gets the case tag instead.

    In this repo, the consumer sends some([c1(e3)]). The provider receives some([c1(e1)]). wasmtime and jco both print the same wrong value.

    Environment

    component version
    OS macOS 15.7 (Darwin 24.6.0), x86_64
    wit-bindgen wit-bindgen-cli 0.51.0
    wasi-sdk 27.0 (wasm32-wasip2-clang/clang++ on PATH)
    wac wac-cli 0.8.1
    wasmtime 41.0.0
    jco 1.16.1 (Node.js v22.22.2)

    Reproduce steps

    Github repo:https://github.com/xiaozzzZZzzz240/wit-bindgen-1683-repo

    Run every command from the repository root.

    1. Generate C provider bindings:

      wit-bindgen c wit --world provider --out-dir src/provider-C
    2. Generate C++ consumer bindings:

      wit-bindgen cpp wit --world consumer --out-dir src/consumer-Cpp
    3. Build the provider component:

      wasm32-wasip2-clang -o components/provider.wasm -mexec-model=reactor src/provider-C/provider_impl.c src/provider-C/provider.c src/provider-C/provider_component_type.o
    4. Build the consumer component:

      wasm32-wasip2-clang++ -std=c++23 -fno-exceptions -o components/consumer.wasm -mexec-model=reactor src/consumer-Cpp/consumer_impl.cpp src/consumer-Cpp/consumer.cpp src/consumer-Cpp/consumer_component_type.o
    5. Compose both components:

      wac plug components/consumer.wasm --plug components/provider.wasm -o composed.wasm
    6. Run with wasmtime:

      wasmtime run composed.wasm
    7. Run with jco:

      jco run composed.wasm

    Expected result

    some([c1(enum(e3))])
    

    Actual result

    wasmtime, rc=0:

    some([c1(enum(e1))])
    

    jco, rc=0:

    some([c1(enum(e1))])
    
  5. changed the title [-]G028-C++ bindings return e1 instead of e3 for enum payload in option[/-] [+]C++ bindings picks the wrong enum[/+] on Aug 18, 2026
  6. changed the title [-]C++ bindings picks the wrong enum[/-] [+]wit-bindgen C++ passes the wrong enum value in option<list<variant>>[/+] on Aug 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions