export const description = ` Validation tests for const declarations `; import { makeTestGroup } from '../../../../common/framework/test_group.js'; import { keysOf } from '../../../../common/util/data_tables.js'; import { ShaderValidationTest } from '../shader_validation_test.js'; export const g = makeTestGroup(ShaderValidationTest); g.test('no_direct_recursion') .desc('Test that direct recursion of const declarations is rejected') .params(u => u.combine('target', ['a', 'b'])) .fn(t => { const wgsl = ` const a : i32 = 42; const b : i32 = ${t.params.target}; `; t.expectCompileResult(t.params.target === 'a', wgsl); }); g.test('no_indirect_recursion') .desc('Test that indirect recursion of const declarations is rejected') .params(u => u.combine('target', ['a', 'b'])) .fn(t => { const wgsl = ` const a : i32 = 42; const b : i32 = c; const c : i32 = ${t.params.target}; `; t.expectCompileResult(t.params.target === 'a', wgsl); }); g.test('no_indirect_recursion_via_array_size') .desc('Test that indirect recursion of const declarations via array size expressions is rejected') .params(u => u.combine('target', ['a', 'b'])) .fn(t => { const wgsl = ` const a = 4; const b = c[0]; const c = array(4, 4, 4, 4); `; t.expectCompileResult(t.params.target === 'a', wgsl); }); g.test('no_indirect_recursion_via_struct_attribute') .desc('Test that indirect recursion of const declarations via struct members is rejected') .params(u => u // .combine('target', ['a', 'b']) .combine('attribute', ['align', 'location', 'size']) ) .fn(t => { const wgsl = ` struct S { @${t.params.attribute}(${t.params.target}) a : i32 } const a = 4; const b = S(4).a; `; t.expectCompileResult(t.params.target === 'a', wgsl); }); const kTypeCases = { bool: { code: `const x : bool = true;`, valid: true, }, i32: { code: `const x : i32 = 1i;`, valid: true, }, u32: { code: `const x : u32 = 1u;`, valid: true, }, f32: { code: `const x : f32 = 1f;`, valid: true, }, f16: { code: `enable f16;\nconst x : f16 = 1h;`, valid: true, }, abstract_int: { code: ` const x = 0xffffffffff; const_assert x == 0xffffffffff;`, valid: true, }, abstract_float: { code: ` const x = 3937509.87755102; const_assert x != 3937510.0; const_assert x != 3937509.75;`, valid: true, }, vec2i: { code: `const x : vec2i = vec2i();`, valid: true, }, vec3u: { code: `const x : vec3u = vec3u();`, valid: true, }, vec4f: { code: `const x : vec4f = vec4f();`, valid: true, }, mat2x2: { code: `const x : mat2x2f = mat2x2f();`, valid: true, }, mat4x3f: { code: `const x : mat4x3 = mat4x3();`, valid: true, }, array_sized: { code: `const x : array = array(1,2,3,4);`, valid: true, }, array_runtime: { code: `const x : array = array(1,2,3);`, valid: false, }, struct: { code: `struct S { x : u32 }\nconst x : S = S(0);`, valid: true, }, atomic: { code: `const x : atomic = 0;`, valid: false, }, vec_abstract_int: { code: ` const x = vec2(0xffffffffff,0xfffffffff0); const_assert x.x == 0xffffffffff; const_assert x.y == 0xfffffffff0;`, valid: true, }, array_abstract_int: { code: ` const x = array(0xffffffffff,0xfffffffff0); const_assert x[0] == 0xffffffffff; const_assert x[1] == 0xfffffffff0;`, valid: true, }, }; g.test('type') .desc('Test const types') .params(u => u.combine('case', keysOf(kTypeCases))) .fn(t => { if (t.params.case === 'f16') { t.skipIfDeviceDoesNotHaveFeature('shader-f16'); } const testcase = kTypeCases[t.params.case]; const code = testcase.code; const expect = testcase.valid; t.expectCompileResult(expect, code); }); const kInitCases = { no_init: { code: `const x : u32;`, valid: false, }, no_type: { code: `const x = 0;`, valid: true, }, no_init_no_type: { code: `const x;`, valid: false, }, init_matching_type: { code: `const x : i32 = 1i;`, valid: true, }, init_mismatch_type: { code: `const x : u32 = 1i;`, valid: false, }, abs_int_init_convert: { code: `const x : u32 = 1;`, valid: true, }, abs_float_init_convert: { code: `const x : f32 = 1.0;`, valid: true, }, init_const_expr: { code: `const x = 0;\nconst y = x + 2;`, valid: true, }, init_override_expr: { code: `override x : u32;\nconst y = x * 2;`, valid: false, }, init_runtime_expr: { code: `var x = 1i;\nconst y = x - 1;`, valid: false, }, init_func: { code: `const x = max(1,2);`, valid: true, }, init_non_const_func: { code: `const x = foo(1); fn foo(p : i32) -> i32 { return p; }`, valid: false, }, }; g.test('initializer') .desc('Test const initializers') .params(u => u.combine('case', keysOf(kInitCases))) .fn(t => { const testcase = kInitCases[t.params.case]; const code = testcase.code; const expect = testcase.valid; t.expectCompileResult(expect, code); }); g.test('function_scope') .desc('Test that const declarations are allowed in functions') .fn(t => { const code = `fn foo() { const x = 0; }`; t.expectCompileResult(true, code); }); g.test('immutable') .desc('Test that const declarations are immutable') .fn(t => { const code = ` const x = 0; fn foo() { x = 1; }`; t.expectCompileResult(false, code); }); g.test('assert') .desc('Test value can be checked by a const_assert') .fn(t => { const code = ` const x = 0; const_assert x == 0;`; t.expectCompileResult(true, code); }); g.test('placement') .desc('Tests @const is not allowed to appear') .params(u => u.combine('scope', [ 'private-var', 'storage-var', 'struct-member', 'fn-decl', 'fn-param', 'fn-var', 'fn-return', 'while-stmt', undefined, ] as const) ) .fn(t => { const scope = t.params.scope; const attr = '@const'; const code = ` ${scope === 'private-var' ? attr : ''} var priv_var : i32; ${scope === 'storage-var' ? attr : ''} @group(0) @binding(0) var stor_var : i32; struct A { ${scope === 'struct-member' ? attr : ''} a : i32, } @vertex ${scope === 'fn-decl' ? attr : ''} fn f( ${scope === 'fn-param' ? attr : ''} @location(0) b : i32, ) -> ${scope === 'fn-return' ? attr : ''} @builtin(position) vec4f { ${scope === 'fn-var' ? attr : ''} var func_v : i32; ${scope === 'while-stmt' ? attr : ''} while false {} return vec4(1, 1, 1, 1); } `; t.expectCompileResult(scope === undefined, code); });