Repository navigation
translator: accepts [method]/[static] case-folded name collisions CM validation requires rejecting (inherited wasmparser gap) #238
Description
Summary
The translator accepts components that Component Model validation requires
rejecting: two [method] (or [static]) extern names in one scope that are
identical after case-folding, e.g. [method]r.a-b + [method]r.a-B.
Explainer.md §Name Uniqueness defines strongly-unique as lowercase-the-acronyms
→ strip the [...] prefix → compare, and its example list explicitly calls
adding [method]foo.BAR next to [method]foo.bar "a validation error";
Binary.md:280-283 applies strongly-unique to all instance/component
import/export names. Our translator inherits a wasmparser gap and validates
such components, so they flow through to the runtime, where the conventions
layer silently routes both members to the same JS name (the unguarded
methods/statics paths of #185). Per the parity policy (docs/architecture.md
§1: spec is the semantic tie-breaker, wasmtime corroborating — never the
other way), wasmtime agreeing with wasmparser here does not make it correct.
Root cause (upstream)
wasmparser folds case for plain labels — KebabStr's Eq/Hash compare
to_ascii_lowercase() — but ResourceFunc (the a.b payload of
method/static names) derives Eq/Hash/Ord on the raw string
(src/validator/names.rs; identical in 0.251/0.252/0.256). So plain-label
and record-field folded collisions are rejected, method/static ones are not.
Recorded in upstream-component-model-repo-findings.md §Out of scope as a
candidate bytecodealliance/wasm-tools issue (filing is the operator's call).
wit-parser's text parser checks none of this either, so the invalid pair is
reachable from WIT source, not just hand-written WAT:
interface i {
resource r {
a-b: func();
a-B: func();
}
}Evidence (2026-08-22, wasm-tools 1.247 / wasmtime 47 / translator-shim @ wasmparser 0.252)
| probe (same scope) | wasm-tools validate | wasmtime compile | our translator |
|---|---|---|---|
instance exports a-b + a-B |
reject | reject | reject ([validation]) |
record fields a-b + a-B |
reject | — | reject |
[method]r.a-b + [method]r.a-B |
accept | accept | accept (plan produced) |
Repro for row 3:
(component
(import "test:i/i" (instance
(export "r" (type $r (sub resource)))
(export "[method]r.a-b" (func (param "self" (borrow $r))))
(export "[method]r.a-B" (func (param "self" (borrow $r))))
))
)
deno run --allow-read --allow-write tools/translate/main.ts <that>.wasm
produces a plan; it should fail with a phase: "validation" error.
Suggested fix
Add our own strongly-unique enforcement for [method]/[static] extern
names in translator-shim, after wasmparser validation (walk every
instance-type/component-type scope; fold per Explainer.md §Name Uniqueness;
report through the existing phase: "validation" error taxonomy,
crates/translator-shim/src/error.rs:6-9). Scope it to exactly the wasmparser
gap — plain labels and record/flag/variant/enum labels are already covered —
and remove it when upstream ResourceFunc folds case (upstream-tracking).
Relation to #185
Complementary, not overlapping: #185 extends the conventions-layer
NameCollisionError guards, which are still required for spec-valid
collisions (is-XML vs is-x-m-l both camelCase to isXML — validation
correctly accepts those, only the casing layer can refuse). This issue
restores validation parity so spec-invalid components stop reaching the
runtime at all. Validation record:
#185 (comment)
Provenance
Found validating #185 against the spec and wasmtime/wasm-tools (2026-08-22).
Audit/testing regime: #175. Tracker entry: PR #237.
Activity
lannbot commentedon Aug 25, 2026
Upstream search + scope extension (2026-08-25)
No existing issue covers this divergence in bytecodealliance/wasm-tools or
bytecodealliance/wasmtime (searched: strongly-unique / case-insensitive /
uniqueness / kebab / acronym / casing variants, plus PR history of
crates/wasmparser/src/validator/names.rs). wasm-toolsmainstill carriesthe raw-string derives. Nearest miss: wasm-tools#2138 implemented the
lvs[*]l.lrule (CM#494) but did not touch case-folding.Scope extension — interface names have the same gap.
InterfaceNamederives
Eq/Hash/Ordon the raw string exactly likeResourceFunc, sovalidates in wasm-tools 1.247, compiles in wasmtime 47, and produces a plan
through our translator — while the spec folds acronyms in all names before
comparing. The translator-shim enforcement this issue proposes should cover
interface names too, not just
[method]/[static].Spec-side movement, conclusion unchanged. WebAssembly/component-model#703
(merged 2026-08-18 — after our submodule pin) rewrote strongly-unique as
canonicalize-then-compare, fixing the non-transitivity reported in CM#702
(
foo=FOO,FOO=[constructor]foo, butfoo!=[constructor]foo).The acronym fold is retained as canonicalization step 1; the invalid-examples
list now also names
[static]foo-BAR.FOO-barandfoo:bar/BAZ. Twoconsequences for the fix here: (a) implement the post-#703 algorithm
(canonicalize each name, then compare), not the pin's three-rule form — it is
strictly simpler and transitive; (b) bump the submodule or cite #703 when
writing the enforcement's spec reference. Note #703 also makes some
previously-valid pairs invalid (e.g.
foovs[constructor]FOO), which theenforcement inherits for free from canonicalize-first.
CM#118 (open) is adjacent but distinct: it covers spec-valid names colliding
after language-casing conversion — that is #185's territory, not this issue's.
findings: wasmparser gap extends to interface names; CM#703 reworded …
lannbot commentedon Aug 25, 2026
Deferred until the wasmtime 49 toolchain bump (operator decision, 2026-08-25)
CM#703 merged a week ago and upstream is actively reworking this exact corner
(CM#702 was filed by an implementer); rather than build shim-side enforcement
that upstream may obsolete within a release cycle, we wait for the wasmtime 49
/ wasmparser bump and re-verify then.
At revisit:
post-#703 strongly-unique (
ResourceFuncandInterfaceNameinvalidator/names.rs, or probe:[method]r.a-b+[method]r.a-Bandtest:i/x-y+test:i/x-Y— repros in this issue and the tracker entry).candidate-upstream-issue entry.
(post-#703 canonicalize-then-compare), and the upstream filing question
goes back to the operator.
Caveat noted for the bet: wasm-tools
mainstill had the raw derives as of2026-08-25, so a 49-cycle fix is plausible but not guaranteed.
findings: park the wasmparser case-folding entry until the wasmtime 4…