[naga wgsl] Implement local `const` declarations
Connections Fixes #4493
Description
For wgsl-in I handled const declarations similarly as let by adding expressions to directly to named_expressions (first commit). For wgsl-out I simply check if expr in start_named_expr call is constant expr or not and based on that write const/let (see second commit), this exposed some problems as naga report some expr as constant although they are not actually impl as const, workaround is in third commit by introducing ExpressionKind::ImplConst for expressions that are also impl as const in naga, alternatively we could always print them as let statements and provide WriterFlags::CONST that will constify all wgsl const expr.
Testing Some new manually written tests and I plan to also test this against CTS via servo.
Checklist
- [x] Run
cargo fmt. - [x] Run
cargo clippy. If applicable, add:- [ ]
--target wasm32-unknown-unknown - [ ]
--target wasm32-unknown-emscripten
- [ ]
- [x] Run
cargo xtask testto run tests. - [ ] Add change to
CHANGELOG.md. See simple instructions inside file.
Error validating tests/out/wgsl/functions.wgsl: Could not parse WGSL: error: Not implemented as constant expression: Dot built-in function ┌─ tests/out/wgsl/functions.wgsl:11:18 │ 11 │ const c_2_ = dot(a_2_, b_2_); │ ^^^ see msg
I think the problem is that ExpressionKindTracker detects dot as const, but constevaler doesn't: https://github.com/gfx-rs/wgpu/blob/fac49ee97c5dd8e37419d425e4cdf79fbe760f8f/naga/src/proc/constant_evaluator.rs#L1243
rebased!
OK /_webgpu/webgpu/cts.https.html?q=webgpu:shader,validation,decl,const:function_scope:*
PASS [expected FAIL] subtest: :
OK /_webgpu/webgpu/cts.https.html?q=webgpu:shader,validation,expression,access,array:early_eval_errors:*
PASS [expected FAIL] subtest: :case="const_func_in_bounds"
OK /_webgpu/webgpu/cts.https.html?q=webgpu:shader,validation,expression,access,matrix:early_eval_errors:*
PASS [expected FAIL] subtest: :case="const_func_in_bounds"
OK /_webgpu/webgpu/cts.https.html?q=webgpu:shader,validation,expression,access,vector:abstract:*
FAIL [expected PASS] subtest: :vector_width=2;abstract_type="float";concrete_type="u32"
FAIL [expected PASS] subtest: :vector_width=2;abstract_type="float";concrete_type="i32"
FAIL [expected PASS] subtest: :vector_width=3;abstract_type="float";concrete_type="u32"
FAIL [expected PASS] subtest: :vector_width=3;abstract_type="float";concrete_type="i32"
FAIL [expected PASS] subtest: :vector_width=4;abstract_type="float";concrete_type="u32"
FAIL [expected PASS] subtest: :vector_width=4;abstract_type="float";concrete_type="i32"
OK /_webgpu/webgpu/cts.https.html?q=webgpu:shader,validation,expression,binary,bitwise_shift:shift_left_concrete:*
PASS [expected FAIL] subtest: :case={"lhs":"0u","rhs":"31u","pass":true};vectorize="_undef_"
PASS [expected FAIL] subtest: :case={"lhs":"0u","rhs":"31u","pass":true};vectorize=2
PASS [expected FAIL] subtest: :case={"lhs":"0u","rhs":"31u","pass":true};vectorize=3
PASS [expected FAIL] subtest: :case={"lhs":"0u","rhs":"31u","pass":true};vectorize=4
PASS [expected FAIL] subtest: :case={"lhs":"0i","rhs":"31u","pass":true};vectorize="_undef_"
PASS [expected FAIL] subtest: :case={"lhs":"0i","rhs":"31u","pass":true};vectorize=2
PASS [expected FAIL] subtest: :case={"lhs":"0i","rhs":"31u","pass":true};vectorize=3
PASS [expected FAIL] subtest: :case={"lhs":"0i","rhs":"31u","pass":true};vectorize=4
FAIL [expected PASS] subtest: :case={"lhs":"1073741824i","rhs":"1u","pass":false};vectorize="_undef_"
FAIL [expected PASS] subtest: :case={"lhs":"1073741824i","rhs":"1u","pass":false};vectorize=2
FAIL [expected PASS] subtest: :case={"lhs":"1073741824i","rhs":"1u","pass":false};vectorize=3
FAIL [expected PASS] subtest: :case={"lhs":"1073741824i","rhs":"1u","pass":false};vectorize=4
FAIL [expected PASS] subtest: :case={"lhs":"2147483647i","rhs":"1u","pass":false};vectorize="_undef_"
FAIL [expected PASS] subtest: :case={"lhs":"2147483647i","rhs":"1u","pass":false};vectorize=2
FAIL [expected PASS] subtest: :case={"lhs":"2147483647i","rhs":"1u","pass":false};vectorize=3
FAIL [expected PASS] subtest: :case={"lhs":"2147483647i","rhs":"1u","pass":false};vectorize=4
FAIL [expected PASS] subtest: :case={"lhs":"1i","rhs":"31u","pass":false};vectorize="_undef_"
FAIL [expected PASS] subtest: :case={"lhs":"1i","rhs":"31u","pass":false};vectorize=2
FAIL [expected PASS] subtest: :case={"lhs":"1i","rhs":"31u","pass":false};vectorize=3
FAIL [expected PASS] subtest: :case={"lhs":"1i","rhs":"31u","pass":false};vectorize=4
PASS [expected FAIL] subtest: :case={"lhs":"1073741824u","rhs":"1u","pass":true};vectorize="_undef_"
PASS [expected FAIL] subtest: :case={"lhs":"1073741824u","rhs":"1u","pass":true};vectorize=2
PASS [expected FAIL] subtest: :case={"lhs":"1073741824u","rhs":"1u","pass":true};vectorize=3
PASS [expected FAIL] subtest: :case={"lhs":"1073741824u","rhs":"1u","pass":true};vectorize=4
PASS [expected FAIL] subtest: :case={"lhs":"2147483647u","rhs":"1u","pass":true};vectorize="_undef_"
PASS [expected FAIL] subtest: :case={"lhs":"2147483647u","rhs":"1u","pass":true};vectorize=2
PASS [expected FAIL] subtest: :case={"lhs":"2147483647u","rhs":"1u","pass":true};vectorize=3
PASS [expected FAIL] subtest: :case={"lhs":"2147483647u","rhs":"1u","pass":true};vectorize=4
PASS [expected FAIL] subtest: :case={"lhs":"1u","rhs":"31u","pass":true};vectorize="_undef_"
PASS [expected FAIL] subtest: :case={"lhs":"1u","rhs":"31u","pass":true};vectorize=2
PASS [expected FAIL] subtest: :case={"lhs":"1u","rhs":"31u","pass":true};vectorize=3
PASS [expected FAIL] subtest: :case={"lhs":"1u","rhs":"31u","pass":true};vectorize=4
FAIL [expected PASS] subtest: :case={"lhs":"3221225472u","rhs":"1u","pass":false};vectorize="_undef_"
FAIL [expected PASS] subtest: :case={"lhs":"3221225472u","rhs":"1u","pass":false};vectorize=2
FAIL [expected PASS] subtest: :case={"lhs":"3221225472u","rhs":"1u","pass":false};vectorize=3
FAIL [expected PASS] subtest: :case={"lhs":"3221225472u","rhs":"1u","pass":false};vectorize=4
FAIL [expected PASS] subtest: :case={"lhs":"4294967295u","rhs":"1u","pass":false};vectorize="_undef_"
FAIL [expected PASS] subtest: :case={"lhs":"4294967295u","rhs":"1u","pass":false};vectorize=2
FAIL [expected PASS] subtest: :case={"lhs":"4294967295u","rhs":"1u","pass":false};vectorize=3
FAIL [expected PASS] subtest: :case={"lhs":"4294967295u","rhs":"1u","pass":false};vectorize=4
FAIL [expected PASS] subtest: :case={"lhs":"4294967295u","rhs":"31u","pass":false};vectorize="_undef_"
FAIL [expected PASS] subtest: :case={"lhs":"4294967295u","rhs":"31u","pass":false};vectorize=2
FAIL [expected PASS] subtest: :case={"lhs":"4294967295u","rhs":"31u","pass":false};vectorize=3
FAIL [expected PASS] subtest: :case={"lhs":"4294967295u","rhs":"31u","pass":false};vectorize=4
PASS [expected FAIL] subtest: :case={"lhs":"-1073741824i","rhs":"1u","pass":true};vectorize="_undef_"
PASS [expected FAIL] subtest: :case={"lhs":"-1073741824i","rhs":"1u","pass":true};vectorize=2
PASS [expected FAIL] subtest: :case={"lhs":"-1073741824i","rhs":"1u","pass":true};vectorize=3
PASS [expected FAIL] subtest: :case={"lhs":"-1073741824i","rhs":"1u","pass":true};vectorize=4
PASS [expected FAIL] subtest: :case={"lhs":"-1i","rhs":"1u","pass":true};vectorize="_undef_"
PASS [expected FAIL] subtest: :case={"lhs":"-1i","rhs":"1u","pass":true};vectorize=2
PASS [expected FAIL] subtest: :case={"lhs":"-1i","rhs":"1u","pass":true};vectorize=3
PASS [expected FAIL] subtest: :case={"lhs":"-1i","rhs":"1u","pass":true};vectorize=4
PASS [expected FAIL] subtest: :case={"lhs":"-1i","rhs":"31u","pass":true};vectorize="_undef_"
PASS [expected FAIL] subtest: :case={"lhs":"-1i","rhs":"31u","pass":true};vectorize=2
PASS [expected FAIL] subtest: :case={"lhs":"-1i","rhs":"31u","pass":true};vectorize=3
PASS [expected FAIL] subtest: :case={"lhs":"-1i","rhs":"31u","pass":true};vectorize=4
OK /_webgpu/webgpu/cts.https.html?q=webgpu:shader,validation,expression,binary,bitwise_shift:shift_right_concrete:*
PASS [expected FAIL] subtest: :case={"lhs":"0u","rhs":"31u","pass":true};vectorize="_undef_"
PASS [expected FAIL] subtest: :case={"lhs":"0u","rhs":"31u","pass":true};vectorize=2
PASS [expected FAIL] subtest: :case={"lhs":"0u","rhs":"31u","pass":true};vectorize=3
PASS [expected FAIL] subtest: :case={"lhs":"0u","rhs":"31u","pass":true};vectorize=4
PASS [expected FAIL] subtest: :case={"lhs":"0i","rhs":"31u","pass":true};vectorize="_undef_"
PASS [expected FAIL] subtest: :case={"lhs":"0i","rhs":"31u","pass":true};vectorize=2
PASS [expected FAIL] subtest: :case={"lhs":"0i","rhs":"31u","pass":true};vectorize=3
PASS [expected FAIL] subtest: :case={"lhs":"0i","rhs":"31u","pass":true};vectorize=4
OK /_webgpu/webgpu/cts.https.html?q=webgpu:shader,validation,expression,call,builtin,textureGather:component_argument,non_const:*
PASS [expected FAIL] subtest: :textureType="texture_2d";sampleType="vec4%3Cf32%3E";varType="c"
PASS [expected FAIL] subtest: :textureType="texture_2d";sampleType="vec4%3Ci32%3E";varType="c"
PASS [expected FAIL] subtest: :textureType="texture_2d";sampleType="vec4%3Cu32%3E";varType="c"
PASS [expected FAIL] subtest: :textureType="texture_2d_array";sampleType="vec4%3Cf32%3E";varType="c"
PASS [expected FAIL] subtest: :textureType="texture_2d_array";sampleType="vec4%3Ci32%3E";varType="c"
PASS [expected FAIL] subtest: :textureType="texture_2d_array";sampleType="vec4%3Cu32%3E";varType="c"
PASS [expected FAIL] subtest: :textureType="texture_cube";sampleType="vec4%3Cf32%3E";varType="c"
PASS [expected FAIL] subtest: :textureType="texture_cube";sampleType="vec4%3Ci32%3E";varType="c"
PASS [expected FAIL] subtest: :textureType="texture_cube";sampleType="vec4%3Cu32%3E";varType="c"
PASS [expected FAIL] subtest: :textureType="texture_cube_array";sampleType="vec4%3Cf32%3E";varType="c"
PASS [expected FAIL] subtest: :textureType="texture_cube_array";sampleType="vec4%3Ci32%3E";varType="c"
PASS [expected FAIL] subtest: :textureType="texture_cube_array";sampleType="vec4%3Cu32%3E";varType="c"
OK /_webgpu/webgpu/cts.https.html?q=webgpu:shader,validation,expression,matrix,mul:overflow_scalar_f32:*
PASS [expected FAIL] subtest: :rhs=1;c=2;r=2
PASS [expected FAIL] subtest: :rhs=1;c=2;r=3
PASS [expected FAIL] subtest: :rhs=1;c=2;r=4
PASS [expected FAIL] subtest: :rhs=1;c=3;r=2
PASS [expected FAIL] subtest: :rhs=1;c=3;r=3
PASS [expected FAIL] subtest: :rhs=1;c=3;r=4
PASS [expected FAIL] subtest: :rhs=1;c=4;r=2
PASS [expected FAIL] subtest: :rhs=1;c=4;r=3
PASS [expected FAIL] subtest: :rhs=1;c=4;r=4
OK /_webgpu/webgpu/cts.https.html?q=webgpu:shader,validation,parse,diagnostic:invalid_locations:*
PASS [expected FAIL] subtest: :type="attribute";location="function_const"
PASS [expected FAIL] subtest: :type="directive";location="function_const"
OK /_webgpu/webgpu/cts.https.html?q=webgpu:shader,validation,parse,identifiers:function_const_name:*
PASS [expected FAIL] subtest: :ident="foo"
PASS [expected FAIL] subtest: :ident="Foo"
PASS [expected FAIL] subtest: :ident="FOO"
PASS [expected FAIL] subtest: :ident="_0"
PASS [expected FAIL] subtest: :ident="_foo0"
PASS [expected FAIL] subtest: :ident="_0foo"
PASS [expected FAIL] subtest: :ident="foo__0"
PASS [expected FAIL] subtest: :ident="%CE%94%CE%AD%CE%BB%CF%84%CE%B1"
PASS [expected FAIL] subtest: :ident="r%C3%A9flexion"
PASS [expected FAIL] subtest: :ident="%D0%9A%D1%8B%D0%B7%D1%8B%D0%BB"
PASS [expected FAIL] subtest: :ident="%F0%90%B0%93%F0%90%B0%8F%F0%90%B0%87"
PASS [expected FAIL] subtest: :ident="%E6%9C%9D%E7%84%BC%E3%81%91"
PASS [expected FAIL] subtest: :ident="%D8%B3%D9%84%D8%A7%D9%85"
PASS [expected FAIL] subtest: :ident="%EA%B2%80%EC%A0%95"
PASS [expected FAIL] subtest: :ident="%D7%A9%D6%B8%D7%81%D7%9C%D7%95%D6%B9%D7%9D"
PASS [expected FAIL] subtest: :ident="%E0%A4%97%E0%A5%81%E0%A4%B2%E0%A4%BE%E0%A4%AC%E0%A5%80"
PASS [expected FAIL] subtest: :ident="%D6%83%D5%AB%D6%80%D5%B8%D6%82%D5%A6"
PASS [expected FAIL] subtest: :ident="bf16"
PASS [expected FAIL] subtest: :ident="f64"
PASS [expected FAIL] subtest: :ident="i16"
PASS [expected FAIL] subtest: :ident="i8"
PASS [expected FAIL] subtest: :ident="quat"
PASS [expected FAIL] subtest: :ident="u16"
PASS [expected FAIL] subtest: :ident="u8"
PASS [expected FAIL] subtest: :ident="unsigned"
FAIL [expected PASS] subtest: :ident="const_assert"
FAIL [expected PASS] subtest: :ident="diagnostic"
FAIL [expected PASS] subtest: :ident="require"
OK /_webgpu/webgpu/cts.https.html?q=webgpu:shader,validation,parse,semicolon:after_fn_const_decl:*
PASS [expected FAIL] subtest: :
OK /_webgpu/webgpu/cts.https.html?q=webgpu:shader,validation,statement,compound:parse:*
PASS [expected FAIL] subtest: :stmt="decl"
OK /_webgpu/webgpu/cts.https.html?q=webgpu:shader,validation,statement,continuing:placement:*
PASS [expected FAIL] subtest: :stmt="continuing_const"
OK /_webgpu/webgpu/cts.https.html?q=webgpu:shader,validation,statement,for:parse:*
PASS [expected FAIL] subtest: :test="init_const"
PASS [expected FAIL] subtest: :test="init_const_type"
OK /_webgpu/webgpu/cts.https.html?q=webgpu:shader,validation,statement,phony:rhs_with_decl:*
PASS [expected FAIL] subtest: :test="function_const"
OK /_webgpu/webgpu/cts.https.html?q=webgpu:shader,validation,statement,switch:parse:*
PASS [expected FAIL] subtest: :test="L_default"
PASS [expected FAIL] subtest: :test="L_paren_default"
PASS [expected FAIL] subtest: :test="L_case_1_2_default"
PASS [expected FAIL] subtest: :test="L_case_1_case_2_default"
PASS [expected FAIL] subtest: :test="L_case_1_colon_case_2_colon_default_colon"
PASS [expected FAIL] subtest: :test="L_case_1_colon_default_colon"
PASS [expected FAIL] subtest: :test="L_case_1_colon_default"
PASS [expected FAIL] subtest: :test="L_case_1_default_2"
PASS [expected FAIL] subtest: :test="L_case_1_default_case_2"
PASS [expected FAIL] subtest: :test="L_case_1_default_colon"
PASS [expected FAIL] subtest: :test="L_case_1_default"
PASS [expected FAIL] subtest: :test="L_case_2_1_default"
PASS [expected FAIL] subtest: :test="L_case_2_case_1_default"
PASS [expected FAIL] subtest: :test="L_case_2_default_case_1"
PASS [expected FAIL] subtest: :test="L_case_builtin_default"
PASS [expected FAIL] subtest: :test="L_case_default_1"
PASS [expected FAIL] subtest: :test="L_case_default_2_1"
PASS [expected FAIL] subtest: :test="L_case_default_2_case_1"
PASS [expected FAIL] subtest: :test="L_case_default"
PASS [expected FAIL] subtest: :test="L_case_expr_default"
PASS [expected FAIL] subtest: :test="L_default_break"
PASS [expected FAIL] subtest: :test="L_default_case_1_2"
PASS [expected FAIL] subtest: :test="L_default_case_1_break"
PASS [expected FAIL] subtest: :test="L_default_case_1_case_2"
PASS [expected FAIL] subtest: :test="L_default_case_1_colon_break"
PASS [expected FAIL] subtest: :test="L_default_case_2_case_1"
PASS [expected FAIL] subtest: :test="L_default_colon_break"
PASS [expected FAIL] subtest: :test="L_default_colon"
webgpu:shader,validation,expression,access,vector:abstract:* failures are https://github.com/gfx-rs/wgpu/issues/4400
webgpu:shader,validation,expression,binary,bitwise_shift:shift_left_concrete:* fails because https://github.com/gfx-rs/wgpu/issues/6175
@sagudev: I suspect that const. eval. is actually rejecting that code properly, but we're not treating the error as fatal in WGSL parsing. I can only find checked_{shl,shr} calls for bit shifting operations in naga/src/proc/constant_evaluator.rs: https://github.com/gfx-rs/wgpu/blob/5deaef3b67259008844b15e1fd1d3a8924d1cde0/naga/src/proc/constant_evaluator.rs#L1782-L1815
I suspect that const. eval. is actually rejecting that code properly, but we're not treating the error as fatal in WGSL parsing
That would be true, if I didn't write < (less) operator instead of << (shift left) operator, my bad.
Now const c = 1073741824i << 1u; produces const c: i32 = i32(-2147483648);, instead of error.
Per https://gpuweb.github.io/gpuweb/wgsl/#bit-expr we need to report overflow when value goes into sign bits, while rust's checked_shl only reports overflow when its out of bits.
@sagudev:
Per gpuweb.github.io/gpuweb/wgsl#bit-expr we need to report overflow when value goes into sign bits, while rust's
checked_shlonly reports overflow when its out of bits.
…but this doesn't need to block this PR from getting merged, right? ~~Should we file a separate issue to track this?~~ EDIT: D'oh, of course we have one already, heh 😅: https://github.com/gfx-rs/wgpu/issues/6175
…but this doesn't need to block this PR from getting merged, right?
Right.