export const description = `Validation tests for const_assert`;
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);
* Builds a const_assert() statement.
* @param expr the constant expression
* @param scope module-scope or function-scope constant expression
* @returns the WGSL code
*/
function buildStaticAssert(expr: string, scope: 'module' | 'function') {
const stmt = `const_assert (${expr});`;
return scope === 'module' ? stmt : `fn f() { ${stmt} }`;
}
interface Condition {
expr: string;
val: boolean;
}
interface MappedCondition {
[key: string]: Condition;
}
const kConditionCases: MappedCondition = {
any_false: { expr: `any(vec3(false, false, false))`, val: false },
any_true: { expr: `any(vec3(false, true, false))`, val: true },
binary_op_eq_const_false: { expr: `one + 5 == two`, val: false },
binary_op_eq_const_true: { expr: `one + 1 == two`, val: true },
const_eq_literal_float_false: { expr: `one == 0.0`, val: false },
const_eq_literal_float_true: { expr: `one == 1.0`, val: true },
const_eq_literal_int_false: { expr: `one == 10`, val: false },
const_eq_literal_int_true: { expr: `one == 1`, val: true },
literal_false: { expr: `false`, val: false },
literal_not_false: { expr: `!false`, val: true },
literal_not_true: { expr: `!true`, val: false },
literal_true: { expr: `true`, val: true },
min_max_false: { expr: `min(three, max(two, one)) == 3`, val: false },
min_max_true: { expr: `min(three, max(two, one)) == 2`, val: true },
variable_false: { expr: `is_false`, val: false },
variable_not_false: { expr: `!is_false`, val: true },
variable_not_true: { expr: `!is_true`, val: false },
variable_true: { expr: `is_true`, val: true },
};
const kConditionConstants = `
const one = 1;
const two = 2;
const three = 3;
const is_true = true;
const is_false = false;
`;
g.test('constant_expression_no_assert')
.desc(`Test that const_assert does not assert on a true conditional expression`)
.params(u =>
u
.combine('scope', ['module', 'function'] as const)
.beginSubcases()
.combine('case', keysOf(kConditionCases))
)
.fn(t => {
const expr = kConditionCases[t.params.case].expr;
const val = kConditionCases[t.params.case].val;
t.expectCompileResult(
true,
kConditionConstants + buildStaticAssert(val ? expr : `!(${expr})`, t.params.scope)
);
});
g.test('constant_expression_assert')
.desc(`Test that const_assert does assert on a false conditional expression`)
.params(u =>
u
.combine('scope', ['module', 'function'] as const)
.beginSubcases()
.combine('case', keysOf(kConditionCases))
)
.fn(t => {
const expr = kConditionCases[t.params.case].expr;
const val = kConditionCases[t.params.case].val;
t.expectCompileResult(
false,
kConditionConstants + buildStaticAssert(val ? `!(${expr})` : expr, t.params.scope)
);
});
g.test('constant_expression_logical_or_no_assert')
.desc(
`Test that const_assert does not assert on a condition expression that contains a logical-or which evaluates to true`
)
.params(u =>
u
.combine('scope', ['module', 'function'] as const)
.beginSubcases()
.combine('lhs', keysOf(kConditionCases))
.combine('rhs', keysOf(kConditionCases))
)
.fn(t => {
const expr = `(${kConditionCases[t.params.lhs].expr}) || (${
kConditionCases[t.params.rhs].expr
})`;
const val = kConditionCases[t.params.lhs].val || kConditionCases[t.params.rhs].val;
t.expectCompileResult(
true,
kConditionConstants + buildStaticAssert(val ? expr : `!(${expr})`, t.params.scope)
);
});
g.test('constant_expression_logical_or_assert')
.desc(
`Test that const_assert does assert on a condition expression that contains a logical-or which evaluates to false`
)
.params(u =>
u
.combine('scope', ['module', 'function'] as const)
.beginSubcases()
.combine('lhs', keysOf(kConditionCases))
.combine('rhs', keysOf(kConditionCases))
)
.fn(t => {
const expr = `(${kConditionCases[t.params.lhs].expr}) || (${
kConditionCases[t.params.rhs].expr
})`;
const val = kConditionCases[t.params.lhs].val || kConditionCases[t.params.rhs].val;
t.expectCompileResult(
false,
kConditionConstants + buildStaticAssert(val ? `!(${expr})` : expr, t.params.scope)
);
});
g.test('constant_expression_logical_and_no_assert')
.desc(
`Test that const_assert does not assert on a condition expression that contains a logical-and which evaluates to true`
)
.params(u =>
u
.combine('scope', ['module', 'function'] as const)
.beginSubcases()
.combine('lhs', keysOf(kConditionCases))
.combine('rhs', keysOf(kConditionCases))
)
.fn(t => {
const expr = `(${kConditionCases[t.params.lhs].expr}) && (${
kConditionCases[t.params.rhs].expr
})`;
const val = kConditionCases[t.params.lhs].val && kConditionCases[t.params.rhs].val;
t.expectCompileResult(
true,
kConditionConstants + buildStaticAssert(val ? expr : `!(${expr})`, t.params.scope)
);
});
g.test('constant_expression_logical_and_assert')
.desc(
`Test that const_assert does assert on a condition expression that contains a logical-and which evaluates to false`
)
.params(u =>
u
.combine('scope', ['module', 'function'] as const)
.beginSubcases()
.combine('lhs', keysOf(kConditionCases))
.combine('rhs', keysOf(kConditionCases))
)
.fn(t => {
const expr = `(${kConditionCases[t.params.lhs].expr}) && (${
kConditionCases[t.params.rhs].expr
})`;
const val = kConditionCases[t.params.lhs].val && kConditionCases[t.params.rhs].val;
t.expectCompileResult(
false,
kConditionConstants + buildStaticAssert(val ? `!(${expr})` : expr, t.params.scope)
);
});
g.test('evaluation_stage')
.desc(`Test that the const_assert expression must be a constant expression.`)
.params(u =>
u
.combine('scope', ['module', 'function'] as const)
.combine('stage', ['constant', 'override', 'runtime'] as const)
.beginSubcases()
)
.fn(t => {
const staticAssert = buildStaticAssert('value', t.params.scope);
switch (t.params.stage) {
case 'constant':
t.expectCompileResult(true, `const value = true;\n${staticAssert}`);
break;
case 'override':
t.expectCompileResult(false, `override value = true;\n${staticAssert}`);
break;
case 'runtime':
t.expectCompileResult(false, `var<private> value = true;\n${staticAssert}`);
break;
}
});