| 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<string, Case> = { |
| 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<u32,1>(1u)[0]`, |
| valid: true, |
| }, |
| array_absint_to_i32: { |
| expr: `array(1)[0] == array<i32,1>(1i)[0]`, |
| valid: true, |
| }, |
| array_absint_to_f32: { |
| expr: `array(1)[0] == array<f32,1>(1f)[0]`, |
| valid: true, |
| }, |
| array_absint_to_f16: { |
| expr: `array(1)[0] == array<f16,1>(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<u32,1>(1u)[0]`, |
| valid: false, |
| }, |
| array_absfloat_to_i32: { |
| expr: `array(1.0)[0] == array<i32,1>(1i)[0]`, |
| valid: false, |
| }, |
| array_absfloat_to_f32: { |
| expr: `array(1.0)[0] == array<f32,1>(1f)[0]`, |
| valid: true, |
| }, |
| array_absfloat_to_f16: { |
| expr: `array(1.0)[0] == array<f16,1>(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); |
| }); |