blob: 11466c03d1ad8590ab2483a75fad9432356e225e [file] [log] [blame]
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<i32, ${t.params.target}>(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<f32> = mat4x3<f32>();`,
valid: true,
},
array_sized: {
code: `const x : array<u32, 4> = array(1,2,3,4);`,
valid: true,
},
array_runtime: {
code: `const x : array<u32> = array(1,2,3);`,
valid: false,
},
struct: {
code: `struct S { x : u32 }\nconst x : S = S(0);`,
valid: true,
},
atomic: {
code: `const x : atomic<u32> = 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<private> 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<private> priv_var : i32;
${scope === 'storage-var' ? attr : ''}
@group(0) @binding(0)
var<storage> 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<function> func_v : i32;
${scope === 'while-stmt' ? attr : ''}
while false {}
return vec4(1, 1, 1, 1);
}
`;
t.expectCompileResult(scope === undefined, code);
});