Summary
WGSL swizzle expressions on a local var used as an assignment target (lvalue) fail during SPIR-V generation. Reading a swizzle from a var (rvalue) works correctly. This is distinct from #45, which was about swizzles on uniform struct members and was fixed in v0.17.4.
Environment
- naga version: v0.18.0 (
github.com/gogpu/naga v0.18.0)
- Go version: 1.26
- Platform: Linux x86_64
- Backend: SPIR-V (Vulkan)
Reproduction
Minimal failing shader
@fragment
fn fs_main() -> @location(0) vec4<f32> {
var result = vec4<f32>(0.0, 0.0, 0.0, 0.0);
let c = vec4<f32>(1.0, 0.5, 0.25, 0.8);
result.rgb = c.rgb;
return result;
}
$ nagac test.wgsl
Compilation error: SPIR-V generation error: SPIR-V generation error: expression ir.ExprSwizzle is not a pointer expression
Also fails with compound assignment
@fragment
fn fs_main() -> @location(0) vec4<f32> {
var result = vec4<f32>(0.0, 0.0, 0.0, 0.0);
let c = vec4<f32>(1.0, 0.5, 0.25, 0.8);
result.rgb += c.rgb * c.a;
return result;
}
Works correctly (rvalue swizzle on var)
@fragment
fn fs_main() -> @location(0) vec4<f32> {
var result = vec4<f32>(0.0, 0.0, 0.0, 0.0);
let x = result.rgb; // reading .rgb from var works
return vec4<f32>(x, 1.0);
}
Works correctly (uniform member swizzle — #45 fix)
struct U {
v: vec4<f32>,
}
@group(0) @binding(0) var<uniform> u: U;
@fragment
fn fs_main() -> @location(0) vec4<f32> {
return u.v.xyxy;
}
Expected behavior
All four shaders above should compile to valid SPIR-V. WGSL allows swizzle on any vector expression, including when used as an assignment target on a mutable local variable.
Actual behavior
When a swizzle appears on the left-hand side of an assignment (= or +=) targeting a local var, the SPIR-V backend panics with:
expression ir.ExprSwizzle is not a pointer expression
This suggests the SPIR-V codegen expects the assignment target to resolve to a pointer (for OpStore / OpAccessChain), but encounters an ExprSwizzle node that it doesn't know how to lower to a pointer expression. In SPIR-V, writing through a swizzle requires decomposing into per-component stores or using OpVectorShuffle + load/store patterns.
Workaround
Decompose swizzle assignments into scalar component operations:
// Instead of: result.rgb += c.rgb * c.a * (1.0 - result.a);
let oneMinusA = 1.0 - aa;
rr += c.r * c.a * oneMinusA;
gg += c.g * c.a * oneMinusA;
bb += c.b * c.a * oneMinusA;
Context
Found while implementing per-pixel linked-list OIT in ironwail-go (Quake engine port using gogpu). The resolve shader sorts and blends translucent fragments, accumulating results via result.rgb += ... which triggered this error.
Summary
WGSL swizzle expressions on a local
varused as an assignment target (lvalue) fail during SPIR-V generation. Reading a swizzle from avar(rvalue) works correctly. This is distinct from #45, which was about swizzles on uniform struct members and was fixed in v0.17.4.Environment
github.com/gogpu/naga v0.18.0)Reproduction
Minimal failing shader
Also fails with compound assignment
Works correctly (rvalue swizzle on var)
Works correctly (uniform member swizzle — #45 fix)
Expected behavior
All four shaders above should compile to valid SPIR-V. WGSL allows swizzle on any vector expression, including when used as an assignment target on a mutable local variable.
Actual behavior
When a swizzle appears on the left-hand side of an assignment (
=or+=) targeting a localvar, the SPIR-V backend panics with:This suggests the SPIR-V codegen expects the assignment target to resolve to a pointer (for
OpStore/OpAccessChain), but encounters anExprSwizzlenode that it doesn't know how to lower to a pointer expression. In SPIR-V, writing through a swizzle requires decomposing into per-component stores or usingOpVectorShuffle+ load/store patterns.Workaround
Decompose swizzle assignments into scalar component operations:
Context
Found while implementing per-pixel linked-list OIT in ironwail-go (Quake engine port using gogpu). The resolve shader sorts and blends translucent fragments, accumulating results via
result.rgb += ...which triggered this error.