RESOLVED FIXED323543
[WebGPU][WGSL] Array value constructor named through a type alias emits invalid MSL, silently producing wrong compute results
https://bugs.webkit.org/show_bug.cgi?id=323543
Summary [WebGPU][WGSL] Array value constructor named through a type alias emits inval...
sohamzemse143
Reported 2026-09-06 02:12:15 PDT
Created attachment 481306 [details] Minimal reproduction: seven WGSL cases plus a dispatch and read-back A WGSL array value constructor whose type is named through a type alias is translated to Metal as `array<T, N>(a, b, ..)`, with parentheses. `metal::array` is an aggregate with no such constructor, so the Metal compile fails and every pipeline built from the module is invalid. Spelling the identical type inline, as `array<u32, 4>(a, b, ..)`, works. So does an alias of a vector, an alias of a struct, and the same array alias used in a module-scope `const`. Only this one spelling is affected. STEPS TO REPRODUCE Build a compute pipeline from this module inside a validation error scope: alias Four = array<u32, 4>; @group(0) @binding(0) var<storage, read_write> out: array<u32>; fn f(a: u32) -> Four { return Four(a, a + 1u, a + 2u, a + 3u); } @compute @workgroup_size(1) fn main() { let v = f(out[0]); out[0] = v[0]; out[1] = v[1]; out[2] = v[2]; out[3] = v[3]; } A self-contained page that runs this case and six controls, plus a dispatch-and-read-back case, is attached as webkit-wgsl-alias-bug.html. It needs no build: save it and serve it over http (WebGPU needs a secure context), for example `python3 -m http.server`. EXPECTED The pipeline builds. This is valid WGSL and Chrome 152 builds it. ACTUAL `getCompilationInfo()` reports no messages. `createComputePipeline()` raises a GPUValidationError: error program_source:43:12: error: no matching constructor for initialization of 'array<unsigned int, 4>' return array<unsigned, 4>(parameter0, (parameter0 + 1u), (parameter0 + 2u), (parameter0 + 3u)); BISECT, Safari 26.6 against Chrome 152, same M2 Max 1. array constructor through an alias Safari REJECTED, Chrome ok 2. same alias in the signature, constructor spelled out both ok 3. no alias anywhere both ok 4. alias, built with `var r: Four; r[0] = ..` both ok 5. alias of a vector, constructor through the alias both ok 6. alias of a struct, constructor through the alias both ok 7. array alias, constructor in a module-scope const both ok Cases 2 and 3 are what make this a spelling bug rather than a constructor bug: same type, same arguments, same position. WHY THIS IS WORSE THAN A REJECTED PIPELINE Case 8 on the attached page runs case 1 with no error scope, dispatches it, and reads the buffer back: [0,0,0,0] where the answer is [0,1,2,3]. An invalid pipeline invalidates its command buffer, an invalid command buffer makes submit a no-op, and a caller that does not wrap pipeline creation in an error scope gets a silently wrong answer rather than a failure. This was found in a WebGPU Groth16 prover, whose generated WGSL uses `alias Fr = array<u32, 8>` for a field element. Every kernel in it failed to build, so every proof was computed from buffers nothing had written. It produced a wrong proof in 3 ms against Chrome's 290 ms, and reported no error, because the crate registered its handler through `GPUDevice.onuncapturederror` (see the companion bug). Type aliases over array types are the normal idiom in generated numeric WGSL, so this is likely to be reachable by others. POSSIBLE CAUSE Source/WebGPU/WGSL/Metal/MetalFunctionWriter.cpp, in FunctionDefinitionWriter::visit(const Type*, AST::CallExpression&), on main as of 2026-09-06: auto isArray = is<AST::ArrayTypeExpression>(call.target()); auto isStruct = !isArray && std::holds_alternative<Types::Struct>(*call.target().inferredType()); if (call.isConstructor() && (isArray || isStruct)) { // emits Type({ a, b, c, }), the brace form, which compiles } The two lines disagree about what they inspect. `isStruct` asks the inferred type, which resolves through an alias, which is why case 6 works. `isArray` asks the syntactic node, and `Four(..)` parses as an AST::IdentifierExpression rather than an AST::ArrayTypeExpression, so it is false; `isStruct` is then also false because the inferred type is an Array. The block is skipped and the constructor falls through to the generic path at the end of the same function, `visit(type); visitArguments(this, call);`, which emits the alias-resolved type followed by parenthesised arguments. Making isArray semantic, the way isStruct already is, may be the whole fix: auto isArray = std::holds_alternative<Types::Array>(*call.target().inferredType()); Offered as a reading of the source and not a tested patch; I have not built WebKit. Worth confirming inferredType() is non-null for an ArrayTypeExpression target, since the existing isStruct line only dereferences it on the branch where isArray was false. ENVIRONMENT Safari 26.6 (WebKit 605.1.15), macOS on Apple M2 Max. Control: Chrome 152.0.7977.66 on the same machine builds all seven cases and returns [0,1,2,3].
Attachments
Minimal reproduction: seven WGSL cases plus a dispatch and read-back (6.67 KB, text/html)
2026-09-06 02:12 PDT, sohamzemse143
no flags
Radar WebKit Bug Importer
Comment 1 2026-09-06 12:45:29 PDT
Mike Wyrzykowski
Comment 2 2026-09-06 17:27:23 PDT
EWS
Comment 3 2026-09-08 17:09:29 PDT
Committed 320690@main (5f7bc716873f): <https://commits.webkit.org/320690@main> Reviewed commits have been landed. Closing PR #73375 and removing active labels.
Note You need to log in before you can comment on or make changes to this bug.