export const description = ` Validation tests for matrix access expressions * Index type * Result type * Early-evaluation errors `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { keysOf } from '../../../../../common/util/data_tables.js'; import { Type } from '../../../../util/conversion.js'; import { ShaderValidationTest } from '../../shader_validation_test.js'; export const g = makeTestGroup(ShaderValidationTest); g.test('index_type') .desc('Tests valid index types for array access expressions') .params(u => u.combine('type', [ 'bool', 'u32', 'i32', 'abstract-int', 'f32', 'f16', 'abstract-float', 'vec2i', ] as const) ) .fn(t => { const ty = Type[t.params.type]; const enable = ty.requiresF16() ? 'enable f16;' : ''; const code = `${enable} fn foo() { var x = mat2x2(1,2,3,4); let tmp = x[${ty.create(0).wgsl()}]; }`; const expect = t.params.type === 'i32' || t.params.type === 'u32' || t.params.type === 'abstract-int'; t.expectCompileResult(expect, code); }); g.test('result_type') .desc('Tests that correct result type is produced for an access expression') .params(u => u .combine('element', ['f16', 'f32'] as const) .combine('columns', [2, 3, 4] as const) .beginSubcases() .combine('rows', [2, 3, 4] as const) .combine('decl', ['function', 'module'] as const) ) .fn(t => { const enable = t.params.element === 'f16' ? 'enable f16;' : ''; const scalarTy = Type[t.params.element]; const vectorTy = Type['vec'](t.params.rows, scalarTy); const matrixTy = Type['mat'](t.params.columns, t.params.rows, scalarTy); const module_decl = t.params.decl === 'module' ? `@group(0) @binding(0) var x : ${matrixTy.toString()};` : ``; const function_decl = t.params.decl === 'module' ? `` : `var x : ${matrixTy.toString()};`; const code = `${enable} ${module_decl} fn foo() { ${function_decl} let tmp1 : ${vectorTy.toString()} = x[0]; let tmp2 : ${vectorTy.toString()} = x[1]; }`; t.expectCompileResult(true, code); }); interface OutOfBoundsCase { code: string; result: boolean; pipeline?: boolean; value?: number; } const kOutOfBoundsCases: Record = { const_module_in_bounds: { code: `const x = mat2x2(1,2,3,4)[0];`, result: true, }, const_module_oob_neg: { code: `const x = mat2x2(1,2,3,4)[-1];`, result: false, }, const_module_oob_pos: { code: `const x = mat2x2(1,2,3,4)[2];`, result: false, }, const_func_in_bounds: { code: `fn foo() { const x = mat2x2(1,2,3,4)[0]; }`, result: true, }, const_func_oob_neg: { code: `fn foo { const x = mat2x2(1,2,3,4)[-1]; }`, result: false, }, const_func_oob_pos: { code: `fn foo { const x = mat2x2(1,2,3,4)[2]; }`, result: false, }, override_in_bounds: { code: `override x : i32; fn y() -> u32 { let tmp = mat2x2(1,2,3,4)[x]; return 0; }`, result: true, pipeline: true, value: 0, }, override_oob_neg: { code: `override x : i32; fn y() -> u32 { let tmp = mat2x2(1,2,3,4)[x]; return 0; }`, result: false, pipeline: true, value: -1, }, override_oob_pos: { code: `override x : i32; fn y() -> u32 { let tmp = mat2x2(1,2,3,4)[x]; return 0; }`, result: false, pipeline: true, value: 2, }, runtime_in_bounds: { code: `fn foo() { let idx = 0; let x = mat2x2(1,2,3,4)[idx]; }`, result: true, }, runtime_oob_neg: { code: `fn foo() { let idx = -1; let x = mat2x2(1,2,3,4)[idx]; }`, result: true, }, runtime_oob_pos: { code: `fn foo() { let idx = 3; let x = mat2x2(1,2,3,4)[idx]; }`, result: true, }, runtime_array_const_oob_neg: { code: `@group(0) @binding(0) var x : mat2x2; fn y() -> u32 { let tmp = x[-1]; return 0; }`, result: false, }, runtime_array_override_oob_neg: { code: `@group(0) @binding(0) var v : mat2x2; override x : i32; fn y() -> u32 { let tmp = v[x]; return 0; }`, result: false, pipeline: true, value: -1, }, }; g.test('early_eval_errors') .desc('Tests early evaluation errors for out-of-bounds indexing') .params(u => u.combine('case', keysOf(kOutOfBoundsCases))) .fn(t => { const testcase = kOutOfBoundsCases[t.params.case]; if (testcase.pipeline) { const v: number = testcase.value ?? 0; t.expectPipelineResult({ expectedResult: testcase.result, code: testcase.code, constants: { x: v }, reference: ['y()'], }); } else { t.expectCompileResult(testcase.result, testcase.code); } }); g.test('abstract_matrix_concrete_index') .desc('Tests that a concrete index type on an abstract array remains abstract') .fn(t => { const code = ` const idx = 0i; const_assert mat2x2(1.11001100110011008404,1,1,1)[0i][0i] == 1.11001100110011008404;`; t.expectCompileResult(true, code); });