export const description = `Validation tests for array access expressions* Index type* Result type* Early-evaluation errors`;import { makeTestGroup } from '../../../../../common/framework/test_group.js';import { keysOf, objectsToRecord } from '../../../../../common/util/data_tables.js';import {Type,elementTypeOf,kConcreteNumericScalarsAndVectors,kAllBoolScalarsAndVectors,} 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 = array(1,2,3);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);});const kTypes = objectsToRecord([...kConcreteNumericScalarsAndVectors,...kAllBoolScalarsAndVectors,]);const kTypeKeys = keysOf(kTypes);g.test('result_type').desc('Tests that correct result type is produced for an access expression').params(u =>u.combine('type', kTypeKeys).combine('elements', [0, 4] as const).filter(t => {const ty = kTypes[t.type];if (t.elements === 0) {if (elementTypeOf(ty) === Type.bool) {return false;}}return true;})).fn(t => {const ty = kTypes[t.params.type];const enable = ty.requiresF16() ? 'enable f16;' : '';const arrayTy = Type['array'](t.params.elements, ty);const module_decl =t.params.elements === 0? `@group(0) @binding(0) var<storage> x : ${arrayTy.toString()};`: ``;const function_decl = t.params.elements === 0 ? `` : `var x : ${arrayTy.toString()};`;const code = `${enable}${module_decl}fn foo() {${function_decl}let tmp1 : ${ty.toString()} = x[0];let tmp2 : ${ty.toString()} = x[1];let tmp3 : ${ty.toString()} = x[2];}`;t.expectCompileResult(true, code);});interface OutOfBoundsCase {code: string;result: boolean;pipeline?: boolean;value?: number;}const kOutOfBoundsCases: Record<string, OutOfBoundsCase> = {const_module_in_bounds: {code: `const x = array(1,2,3)[0];`,result: true,},const_module_oob_neg: {code: `const x = array(1,2,3)[-1];`,result: false,},const_module_oob_pos: {code: `const x = array(1,2,3)[3];`,result: false,},const_func_in_bounds: {code: `fn foo() {const x = array(1,2,3)[0];}`,result: true,},const_func_oob_neg: {code: `fn foo {const x = array(1,2,3)[-1];}`,result: false,},const_func_oob_pos: {code: `fn foo {const x = array(1,2,3)[3];}`,result: false,},override_in_bounds: {code: `override x : i32;fn y() -> u32 {let tmp = array(1,2,3)[x];return 0;}`,result: true,pipeline: true,value: 0,},override_oob_neg: {code: `override x : i32;fn y() -> u32 {let tmp = array(1,2,3)[x];return 0;}`,result: false,pipeline: true,value: -1,},override_oob_pos: {code: `override x : i32;fn y() -> u32 {let tmp = array(1,2,3)[x];return 0;}`,result: false,pipeline: true,value: 3,},runtime_in_bounds: {code: `fn foo() {let idx = 0;let x = array(1,2,3)[idx];}`,result: true,},runtime_oob_neg: {code: `fn foo() {let idx = -1;let x = array(1,2,3)[idx];}`,result: true,},runtime_oob_pos: {code: `fn foo() {let idx = 3;let x = array(1,2,3)[idx];}`,result: true,},runtime_array_const_oob_neg: {code: `@group(0) @binding(0) var<storage> x : array<u32>;fn y() -> u32 {let tmp = x[-1];return 0;}`,result: false,},runtime_array_override_oob_neg: {code: `@group(0) @binding(0) var<storage> v : array<u32>;override x : i32;fn y() -> u32 {let tmp = v[x];return 0;}`,result: false,pipeline: true,value: -1,},runtime_nested_array_override_oob_neg: {code: `@group(0) @binding(0) var<storage> v : array<array<u32, 4>>;override x : i32;override w = 0u;fn y() -> u32 {let tmp = v[w][x];return 0;}`,result: false,pipeline: true,value: -1,},runtime_nested_array_override_oob_pos: {code: `@group(0) @binding(0) var<storage> v : array<array<u32,4>, 5>;override x : i32;override w = 0u;fn y() -> u32 {let tmp = v[w][x];return 0;}`,result: false,pipeline: true,value: 4,},runtime_nested_array_override_pos: {code: `@group(0) @binding(0) var<storage> v : array<array<u32,10>, 2>;override x : i32;override w = 0u;fn y() -> u32 {let tmp = v[w][x];return 0;}`,result: true,pipeline: true,value: 9,},runtime_deep_nested_array_override_oob_pos: {code: `@group(0) @binding(0) var<storage> v : array<array<array<u32, 3>, 4>, 5>;override x : i32;override w = 0u;override u = 0u;fn y() -> u32 {let tmp = v[w][u][x];return 0;}`,result: false,pipeline: true,value: 3,},runtime_deep_nested_array_override_pos: {code: `@group(0) @binding(0) var<storage> v : array<array<array<u32, 3>, 4>, 5>;override x : i32;override w = 4u;override u = 3u;fn y() -> u32 {let tmp = v[w][u][x];return 0;}`,result: true,pipeline: true,value: 2,},runtime_structure_array_override_oob_neg: {code: `override x : i32;struct S {w : array<u32>}@group(0) @binding(0) var<storage> v : S;fn y() -> u32 {let tmp : u32 = v.w[x];return 0;}`,result: false,pipeline: true,value: -1,},runtime_structure_array_override_pos: {code: `override x : i32;struct S {w : array<u32>}@group(0) @binding(0) var<storage> v : S;fn y() -> u32 {let tmp : u32 = v.w[x];return 0;}`,result: true,pipeline: true,value: 1,},runtime_structure_array_override_oob_pos: {code: `override x : i32;struct S {w : array<u32, 5>}@group(0) @binding(0) var<storage> v : S;fn y() -> u32 {let tmp : u32 = v.w[x];return 0;}`,result: false,pipeline: true,value: 5,},runtime_nested_structure_array_override_oob_pos: {code: `override x : i32;struct S {w : array<u32, 5>}struct S2 {r : S}@group(0) @binding(0) var<storage> v : S2;fn y() -> u32 {let tmp : u32 = v.r.w[x];return 0;}`,result: false,pipeline: true,value: 5,},runtime_nested_structure_array_override_pos: {code: `override x : i32;struct S {w : array<u32, 6>}struct S2 {r : S}@group(0) @binding(0) var<storage> v : S2;fn y() -> u32 {let tmp : u32 = v.r.w[x];return 0;}`,result: true,pipeline: true,value: 5,},override_array_cnt_size_zero_unsigned: {code: `override x : u32;var<workgroup> v : array<u32,x>;fn y() -> u32 {return v[0];}`,result: false,pipeline: true,value: 0,},override_array_cnt_size_zero_signed: {code: `override x : i32;var<workgroup> v : array<u32,x>;fn y() -> u32 {return v[0];}`,result: false,pipeline: true,value: 0,},override_array_cnt_size_neg: {code: `override x : i32;var<workgroup> v : array<u32,x>;fn y() -> u32 {return v[0];}`,result: false,pipeline: true,value: -1,},override_array_cnt_size_one: {code: `override x : i32;var<workgroup> v : array<u32,x>;fn y() -> u32 {return v[0];}`,result: true,pipeline: true,value: 1,},override_array_dynamic_type_checked_oob_pos: {code: `@group(0) @binding(0) var<storage> v : array<array<array<u32, 3>, 4>, 5>;override x : i32;override w = 0u;fn y() -> u32 {var u = 0;let tmp = v[w][u][x];return 0;}`,result: false,pipeline: true,value: 3,},override_array_dynamic_type_checked_oob_neg: {code: `@group(0) @binding(0) var<storage> v : array<array<array<u32, 3>, 4>, 5>;override x : i32;override w = 0u;fn y() -> u32 {var u = 0;let tmp = v[w][u][x];return 0;}`,result: false,pipeline: true,value: -1,},override_array_dynamic_type_checked_bounds: {code: `@group(0) @binding(0) var<storage> v : array<array<array<u32, 3>, 4>, 5>;override x : i32;override w = 0u;fn y() -> u32 {var u = 0;let tmp = v[w][u][x];return 0;}`,result: true,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_array_concrete_index').desc('Tests that a concrete index type on an abstract array remains abstract').fn(t => {const code = `const idx = 0i;const_assert array(0xfffffffff,2,3)[idx] == 0xfffffffff;`;t.expectCompileResult(true, code);});