wit-bindgen-cpp encodes wrong enum discriminant for enum payload inside variant inside option, producing incorrect values under jco
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 on jco regardless of provider language.
When a C++ consumer lowers option<list> whose variant case carries an enum payload, the generated binding writes canonical-ABI bytes in place over the source vector's own memory. It stores the variant discriminant (1 for c1) at byte 0 first, clobbering the enum payload stored there by std::variant's layout, then reads the now-corrupted payload — so e3 becomes e1. Any provider language (C, C++, Rust) receives the wrong value.
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-wit-bindgen-cpp-wrong-enum-discriminant-in-option-variant-payload
-
Clone this repository:
git clone <THIS_REPO_URL>
cd <THIS_REPO_DIR>
-
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
-
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
-
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
-
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:
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
wit-bindgen-cpp encodes wrong enum discriminant for enum payload inside variant inside option, producing incorrect values under jco
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 on jco regardless of provider language.
When a C++ consumer lowers option<list> whose variant case carries an enum payload, the generated binding writes canonical-ABI bytes in place over the source vector's own memory. It stores the variant discriminant (1 for c1) at byte 0 first, clobbering the enum payload stored there by std::variant's layout, then reads the now-corrupted payload — so e3 becomes e1. Any provider language (C, C++, Rust) receives the wrong value.
Environment
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-wit-bindgen-cpp-wrong-enum-discriminant-in-option-variant-payload
Clone this repository:
Compile the provider component from the C sources in
src/provider-C/(toolchain versions in the table above; the WIT package is inwit/):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.oCompile 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.oCompose the two components into one runnable component with wac 0.8.1:
wac plug components/consumer.wasm --plug components/provider.wasm -o composed.wasmRun the composed component with jco:
Expected result
The composed component should print exactly the following, then exit 0.
Actual result
Under jco the same
composed.wasmexits with rc=0. stdout:stderr:
wasmtime on the exact same file: rc=0