blob: 6072f33dd83484f512e540ba535f2390b1860c96 [file] [log] [blame]
export const description = `Validation for phony assignment statements`;
import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { keysOf } from '../../../../common/util/data_tables.js';
import { scalarTypeOf, Type } from '../../../util/conversion.js';
import { ShaderValidationTest } from '../shader_validation_test.js';
export const g = makeTestGroup(ShaderValidationTest);
interface Case {
value: string;
pass: boolean;
usesF16?: boolean;
gdecl?: string;
ldecl?: string;
}
const kConstructibleTypes = [
'bool',
'i32',
'u32',
'f32',
'f16',
'vec2f',
'vec3h',
'vec4u',
'vec3b',
'mat2x3f',
'mat4x2h',
'abstractInt',
'abstractFloat',
] as const;
const kConstructibleCases = {
...kConstructibleTypes.reduce(
(acc, t) => ({
...acc,
[t]: {
value: Type[t].create(1).wgsl(),
pass: true,
usesF16: scalarTypeOf(Type[t]).kind === 'f16',
},
}),
{}
),
array: { value: 'array(1,2,3)', pass: true },
struct: { value: 'S(1,2)', pass: true, gdecl: 'struct S{ a:u32, b:u32}' },
atomic_u32: { value: 'xu', pass: false, gdecl: 'var<workgroup> xu: atomic<u32>;' },
atomic_i32: { value: 'xi', pass: false, gdecl: 'var<workgroup> xi: atomic<i32>;' },
} as const;
g.test('rhs_constructible')
.desc(`Test that the rhs of 'phony assignment' can be a constructible.`)
.params(u => u.combine('type', keysOf(kConstructibleCases)))
.fn(t => {
const { value, pass, usesF16, gdecl } = kConstructibleCases[t.params.type] as Case;
const code = `
${usesF16 ? 'enable f16;' : ''}
${gdecl ?? ''}
fn f() {
_ = ${value};
}`;
t.expectCompileResult(pass, code);
});
const kVarCases = {
storage: { value: 'x', gdecl: '@group(0) @binding(0) var<storage> x: array<u32,1>;', pass: true },
storage_unsized: {
value: 'x',
gdecl: '@group(0) @binding(0) var<storage> x: array<u32>;',
pass: false,
},
storage_atomic: {
value: 'x',
gdecl: '@group(0) @binding(0) var<storage,read_write> x: atomic<u32>;',
pass: false,
},
uniform: { value: 'x', gdecl: '@group(0) @binding(0) var<uniform> x: u32;', pass: true },
texture: { value: 'x', gdecl: '@group(0) @binding(0) var x: texture_2d<f32>;', pass: true },
sampler: { value: 'x', gdecl: '@group(0) @binding(0) var x: sampler;', pass: true },
sampler_comparison: {
value: 'x',
gdecl: '@group(0) @binding(0) var x: sampler_comparison;',
pass: true,
},
private: { value: 'x', gdecl: 'var<private> x: u32;', pass: true },
workgroup: { value: 'x', gdecl: 'var<workgroup> x: u32;', pass: true },
workgroup_atomic: { value: 'x', gdecl: 'var<workgroup> x: atomic<u32>;', pass: false },
override: { value: 'o', gdecl: 'override o: u32;', pass: true },
function_var: { value: 'x', ldecl: 'var x: u32;', pass: true },
let: { value: 'v', ldecl: 'let v = 1;', pass: true },
const: { value: 'c', gdecl: 'const c = 1;', pass: true },
function_const: { value: 'c', ldecl: 'const c = 1;', pass: true },
ptr: { value: '&x', ldecl: 'var x: u32;', pass: true },
ptr_to_unsized: {
value: '&x',
gdecl: '@group(0) @binding(0) var<storage> x: array<u32>;',
pass: true,
},
indexed: {
value: 'x[0]',
gdecl: '@group(0) @binding(0) var<storage> x: array<u32>;',
pass: true,
},
user_fn: { value: 'f', pass: false },
builtin: { value: 'max', pass: false },
builtin_call: { value: 'max(1,1)', pass: true },
user_call: { value: 'callee()', pass: true, gdecl: 'fn callee() -> i32 { return 0; }' },
undeclared: { value: 'does_not_exist', pass: false },
} as const;
g.test('rhs_with_decl')
.desc(`Test rhs of 'phony assignment' involving declared objects.`)
.params(u => u.combine('test', keysOf(kVarCases)))
.fn(t => {
const { value, pass, gdecl, ldecl } = kVarCases[t.params.test] as Case;
const code = `
${gdecl ?? ''}
@compute @workgroup_size(1)
fn f() {
${ldecl ?? ''}
_ = ${value};
}`;
t.expectCompileResult(pass, code);
});
const kTests = {
literal: { wgsl: `_ = 1;`, pass: true },
expr: { wgsl: `_ = (1+v);`, pass: true },
var: { wgsl: `_ = v;`, pass: true },
in_for_init: { wgsl: `for (_ = v;false;) {}`, pass: true },
in_for_init_semi: { wgsl: `for (_ = v;;false;) {}`, pass: false },
in_for_update: { wgsl: `for (;false; _ = v) {}`, pass: true },
in_for_update_semi: { wgsl: `for (;false; _ = v;) {}`, pass: false },
in_block: { wgsl: `{_ = v;}`, pass: true },
in_continuing: { wgsl: `loop { continuing { _ = v; break if true;}}`, pass: true },
in_paren: { wgsl: `(_ = v;)`, pass: false },
underscore: { wgsl: `_`, pass: false },
underscore_semi: { wgsl: `_;`, pass: false },
underscore_equal: { wgsl: `_=`, pass: false },
underscore_equal_semi: { wgsl: `_=;`, pass: false },
underscore_equal_underscore_semi: { wgsl: `_=_;`, pass: false },
paren_underscore_paren: { wgsl: `(_) = 1;`, pass: false },
// LHS is not a reference type
star_ampersand_undsscore: { wgsl: `*&_ = 1;`, pass: false },
compound: { wgsl: `_ += 1;`, pass: false },
equality: { wgsl: `_ == 1;`, pass: false },
block: { wgsl: `_ = {};`, pass: false },
return: { wgsl: `_ = return;`, pass: false },
};
g.test('parse')
.desc(`Test that 'phony assignment' statements are parsed correctly.`)
.params(u => u.combine('test', keysOf(kTests)))
.fn(t => {
const code = `
fn f() {
var v: u32;
${kTests[t.params.test].wgsl}
}`;
t.expectCompileResult(kTests[t.params.test].pass, code);
});
g.test('module_scope')
.desc(`Phony assignment is not valid at module scope`)
.fn(t => {
const code = `_ = 1; `;
t.expectCompileResult(false, code);
});