export const description = `Validation tests for implicit conversions and overload resolution`; import { makeTestGroup } from '../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../common/util/data_tables.js'; import { kAllNumericScalarsAndVectors, isConvertible, VectorType, } from '../../../util/conversion.js'; import { ShaderValidationTest } from '../shader_validation_test.js'; export const g = makeTestGroup(ShaderValidationTest); interface Case { expr: string; valid: boolean; f16?: boolean; } const kImplicitConversionCases: Record = { absint_to_bool: { expr: `any(1)`, valid: false, }, absint_to_u32: { expr: `1 == 1u`, valid: true, }, absint_to_i32: { expr: `1 == 1i`, valid: true, }, absint_to_f32: { expr: `1 == 1f`, valid: true, }, absint_to_f16: { expr: `1 == 1h`, valid: true, f16: true, }, absfloat_to_bool: { expr: `any(1.0)`, valid: false, }, absfloat_to_u32: { expr: `1.0 == 1u`, valid: false, }, absfloat_to_i32: { expr: `1.0 == 1i`, valid: false, }, absfloat_to_f32: { expr: `1.0 == 1f`, valid: true, }, absfloat_to_f16: { expr: `1.0 == 1h`, valid: true, f16: true, }, vector_absint_to_bool: { expr: `any(vec2(1))`, valid: false, }, vector_absint_to_u32: { expr: `all(vec2(1) == vec2u(1u))`, valid: true, }, vector_absint_to_i32: { expr: `all(vec3(1) == vec3i(1i))`, valid: true, }, vector_absint_to_f32: { expr: `all(vec4(1) == vec4f(1f))`, valid: true, }, vector_absint_to_f16: { expr: `all(vec2(1) == vec2h(1h))`, valid: true, f16: true, }, vector_absfloat_to_bool: { expr: `any(vec2(1.0))`, valid: false, }, vector_absfloat_to_u32: { expr: `all(vec2(1.0) == vec2u(1u))`, valid: false, }, vector_absfloat_to_i32: { expr: `all(vec3(1.0) == vec2i(1i))`, valid: false, }, vector_absfloat_to_f32: { expr: `all(vec4(1.0) == vec4f(1f))`, valid: true, }, vector_absfloat_to_f16: { expr: `all(vec2(1.0) == vec2h(1h))`, valid: true, f16: true, }, vector_swizzle_integer: { expr: `vec2(1).x == 1i`, valid: true, }, vector_swizzle_float: { expr: `vec2(1).y == 1f`, valid: true, }, vector_default_ctor_integer: { expr: `all(vec3().xy == vec2i())`, valid: true, }, vector_default_ctor_abstract: { expr: `all(vec3().xy == vec2())`, valid: true, }, vector_swizzle_abstract: { expr: `vec4(1f).x == 1`, valid: true, }, vector_abstract_to_integer: { expr: `all(vec4(1) == vec4i(1))`, valid: true, }, vector_wrong_result_i32: { expr: `vec2(1,2f).x == 1i`, valid: false, }, vector_wrong_result_f32: { expr: `vec2(1,2i).y == 2f`, valid: false, }, vector_wrong_result_splat: { expr: `vec2(1.0).x == 1i`, valid: false, }, array_absint_to_bool: { expr: `any(array(1)[0])`, valid: false, }, array_absint_to_u32: { expr: `array(1)[0] == array(1u)[0]`, valid: true, }, array_absint_to_i32: { expr: `array(1)[0] == array(1i)[0]`, valid: true, }, array_absint_to_f32: { expr: `array(1)[0] == array(1f)[0]`, valid: true, }, array_absint_to_f16: { expr: `array(1)[0] == array(1h)[0]`, valid: true, f16: true, }, array_absfloat_to_bool: { expr: `any(array(1.0)[0])`, valid: false, }, array_absfloat_to_u32: { expr: `array(1.0)[0] == array(1u)[0]`, valid: false, }, array_absfloat_to_i32: { expr: `array(1.0)[0] == array(1i)[0]`, valid: false, }, array_absfloat_to_f32: { expr: `array(1.0)[0] == array(1f)[0]`, valid: true, }, array_absfloat_to_f16: { expr: `array(1.0)[0] == array(1h)[0]`, valid: true, f16: true, }, mat2x2_index_absint: { expr: `all(mat2x2(1,2,3,4)[0] == vec2(1,2))`, valid: true, }, mat2x2_index_absfloat: { expr: `all(mat2x2(1,2,3,4)[1] == vec2(3.0,4.0))`, valid: true, }, mat2x2_index_float: { expr: `all(mat2x2(0,0,0,0)[1] == vec2f())`, valid: true, }, mat2x2_wrong_result: { expr: `all(mat2x2(0f,0,0,0)[0] == vec2h())`, valid: false, f16: true, }, }; g.test('implicit_conversions') .desc('Test implicit conversions') .params(u => u.combine('case', keysOf(kImplicitConversionCases))) .fn(t => { const testcase = kImplicitConversionCases[t.params.case]; const code = `${testcase.f16 ? 'enable f16;' : ''} const_assert ${testcase.expr};`; t.expectCompileResult(testcase.valid, code); }); const kTypes = objectsToRecord(kAllNumericScalarsAndVectors); const kTypeKeys = keysOf(kTypes); g.test('overload_resolution') .desc('Test overload resolution') .params(u => u .combine('arg1', kTypeKeys) .combine('arg2', kTypeKeys) .beginSubcases() .combine('op', ['min', 'max'] as const) .filter(t => { if (t.arg1 === t.arg2) { return false; } const t1 = kTypes[t.arg1]; const t2 = kTypes[t.arg2]; const t1IsVector = t1 instanceof VectorType; const t2IsVector = t2 instanceof VectorType; if (t1IsVector !== t2IsVector) { return false; } if (t1IsVector && t2IsVector && t1.size !== t2.size) { return false; } return true; }) ) .fn(t => { const t1 = kTypes[t.params.arg1]; const t2 = kTypes[t.params.arg2]; const resTy = isConvertible(t1, t2) ? t2 : t1; const enable = `${t1.requiresF16() || t2.requiresF16() ? 'enable f16;' : ''}`; const min = 50; const max = 100; const res = t.params.op === 'min' ? min : max; const v1 = t1.create(min).wgsl(); const v2 = t2.create(max).wgsl(); const resV = resTy.create(res).wgsl(); const expr = `${t.params.op}(${v1}, ${v2}) == ${resV}`; const assertExpr = t1 instanceof VectorType ? `all(${expr})` : expr; const code = `${enable} const_assert ${assertExpr};`; t.expectCompileResult(isConvertible(t1, t2) || isConvertible(t2, t1), code); });