Summary
writeSwizzle in MSL backend is missing needsParensInContext check, producing invalid MSL when a swizzle is applied to a binary expression result.
Generated MSL (incorrect)
metal::float3 world_normal = _e18 * metal::float4(in.normal, 0.0).xyz;
C++ operator precedence: . (precedence 2) binds tighter than * (precedence 5), so Metal compiler parses this as mat4x4 * float3 = type mismatch.
Expected MSL (correct)
metal::float3 world_normal = (_e18 * metal::float4(in.normal, 0.0)).xyz;
WGSL source (valid)
let world_normal = (object.normal_model * vec4<f32>(in.normal, 0.0)).xyz;
The WGSL is correct — parentheses are present. The naga MSL codegen drops them.
Root cause
expressions.go:1098 — writeSwizzle calls writeExpression(swizzle.Vector) without needsParensInContext check.
Compare with writeAccess at line 886 which correctly has:
needParens := w.needsParensInContext(access.Base)
if needParens {
w.write("(")
}
Fix
Add the same needsParensInContext guard to writeSwizzle:
func (w *Writer) writeSwizzle(swizzle ir.ExprSwizzle) error {
needsUnpack := w.isPackedVec3Access(swizzle.Vector)
// ...existing unpack logic...
needParens := w.needsParensInContext(swizzle.Vector) // ADD
if needParens { // ADD
w.write("(") // ADD
} // ADD
if err := w.writeExpression(swizzle.Vector); err != nil {
return err
}
if needParens { // ADD
w.write(")") // ADD
} // ADD
// ...rest of swizzle...
}
Reproduction
Any WGSL shader with (mat4 * vec4).xyz pattern. g3d's standard.wgsl triggers this:
let world_normal = (object.normal_model * vec4<f32>(in.normal, 0.0)).xyz;
Reported by user on Apple M5 (macOS 26.5.2) but reproducible on any macOS version — the generated MSL is syntactically invalid regardless of Metal compiler version.
Impact
- g3d
standard.wgsl fails on Metal backend (g3d#7)
basic.wgsl works (no swizzle on binary expression)
- All non-MSL backends work (SPIR-V, GLSL, HLSL, DXIL)
Summary
writeSwizzlein MSL backend is missingneedsParensInContextcheck, producing invalid MSL when a swizzle is applied to a binary expression result.Generated MSL (incorrect)
C++ operator precedence:
.(precedence 2) binds tighter than*(precedence 5), so Metal compiler parses this asmat4x4 * float3= type mismatch.Expected MSL (correct)
WGSL source (valid)
The WGSL is correct — parentheses are present. The naga MSL codegen drops them.
Root cause
expressions.go:1098—writeSwizzlecallswriteExpression(swizzle.Vector)withoutneedsParensInContextcheck.Compare with
writeAccessat line 886 which correctly has:Fix
Add the same
needsParensInContextguard towriteSwizzle:Reproduction
Any WGSL shader with
(mat4 * vec4).xyzpattern. g3d'sstandard.wgsltriggers this:Reported by user on Apple M5 (macOS 26.5.2) but reproducible on any macOS version — the generated MSL is syntactically invalid regardless of Metal compiler version.
Impact
standard.wgslfails on Metal backend (g3d#7)basic.wgslworks (no swizzle on binary expression)