Skip to content

translator: accepts [method]/[static] case-folded name collisions CM validation requires rejecting (inherited wasmparser gap) #238

Description

@lannbot

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingp2Minor bugs; desirable lower-priority featuresspec-divergenceBehavior diverges from the pinned CM reference; fix or adjudicate as a named divergenceupstream-trackingBlocked on or watching an external project (spec, wasmtime, engines, toolchains)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions