blob: 9b0ba456f74a60463c40ed61d4d13ba338df6f37 [file] [log] [blame]
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);
});