tint: Fix const eval of type conversions
These were quite spectacularly broken.
Also:
* Fix the definition of 'scalar' in `intrinsics.def`. This was in part why conversions were broken, as abstracts were materialized before reaching the converter builtin when they shouldn't have been.
* Implement `ScalarArgsFrom()` helper in `const_eval_test.cc`. This is used by the new conversion tests, and also implements part of the suggestion to improve tint:1709.
Fixed: tint:1707
Bug: tint:1709
Change-Id: Iab962b671305e868f92710912d2ed07e3338c680
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/105261
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@chromium.org>
diff --git a/src/tint/intrinsics.def b/src/tint/intrinsics.def
index 28b78ff..493d224 100644
--- a/src/tint/intrinsics.def
+++ b/src/tint/intrinsics.def
@@ -175,13 +175,13 @@
// A type matcher that can match one or more types. //
////////////////////////////////////////////////////////////////////////////////
-match abstract_or_scalar: ia | fa | f32 | f16 | i32 | u32 | bool
-match scalar: f32 | f16 | i32 | u32 | bool
-match scalar_no_f32: i32 | f16 | u32 | bool
-match scalar_no_f16: f32 | i32 | u32 | bool
-match scalar_no_i32: f32 | f16 | u32 | bool
-match scalar_no_u32: f32 | f16 | i32 | bool
-match scalar_no_bool: f32 | f16 | i32 | u32
+match scalar: ia | fa | f32 | f16 | i32 | u32 | bool
+match concrete_scalar: f32 | f16 | i32 | u32 | bool
+match scalar_no_f32: ia | fa | i32 | f16 | u32 | bool
+match scalar_no_f16: ia | fa | f32 | i32 | u32 | bool
+match scalar_no_i32: ia | fa | f32 | f16 | u32 | bool
+match scalar_no_u32: ia | fa | f32 | f16 | i32 | bool
+match scalar_no_bool: ia | fa | f32 | f16 | i32 | u32
match fia_fiu32_f16: fa | ia | f32 | i32 | u32 | f16
match fia_fi32_f16: fa | ia | f32 | i32 | f16
match fia_fiu32: fa | ia | f32 | i32 | u32
@@ -524,9 +524,9 @@
fn round<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
fn saturate<T: f32_f16>(T) -> T
fn saturate<T: f32_f16, N: num>(vec<N, T>) -> vec<N, T>
-@const("select_bool") fn select<T: abstract_or_scalar>(T, T, bool) -> T
-@const("select_bool") fn select<T: abstract_or_scalar, N: num>(vec<N, T>, vec<N, T>, bool) -> vec<N, T>
-@const("select_boolvec") fn select<N: num, T: abstract_or_scalar>(vec<N, T>, vec<N, T>, vec<N, bool>) -> vec<N, T>
+@const("select_bool") fn select<T: scalar>(T, T, bool) -> T
+@const("select_bool") fn select<T: scalar, N: num>(vec<N, T>, vec<N, T>, bool) -> vec<N, T>
+@const("select_boolvec") fn select<N: num, T: scalar>(vec<N, T>, vec<N, T>, vec<N, bool>) -> vec<N, T>
fn sign<T: f32_f16>(T) -> T
fn sign<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
fn sin<T: f32_f16>(T) -> T
@@ -720,9 +720,9 @@
@const("Zero") ctor f32() -> f32
@const("Zero") ctor f16() -> f16
@const("Zero") ctor bool() -> bool
-@const("Zero") ctor vec2<T: scalar>() -> vec2<T>
-@const("Zero") ctor vec3<T: scalar>() -> vec3<T>
-@const("Zero") ctor vec4<T: scalar>() -> vec4<T>
+@const("Zero") ctor vec2<T: concrete_scalar>() -> vec2<T>
+@const("Zero") ctor vec3<T: concrete_scalar>() -> vec3<T>
+@const("Zero") ctor vec4<T: concrete_scalar>() -> vec4<T>
@const("Zero") ctor mat2x2<T: f32_f16>() -> mat2x2<T>
@const("Zero") ctor mat2x3<T: f32_f16>() -> mat2x3<T>
@const("Zero") ctor mat2x4<T: f32_f16>() -> mat2x4<T>
@@ -739,9 +739,9 @@
@const("Identity") ctor f32(f32) -> f32
@const("Identity") ctor f16(f16) -> f16
@const("Identity") ctor bool(bool) -> bool
-@const("Identity") ctor vec2<T: scalar>(vec2<T>) -> vec2<T>
-@const("Identity") ctor vec3<T: scalar>(vec3<T>) -> vec3<T>
-@const("Identity") ctor vec4<T: scalar>(vec4<T>) -> vec4<T>
+@const("Identity") ctor vec2<T: concrete_scalar>(vec2<T>) -> vec2<T>
+@const("Identity") ctor vec3<T: concrete_scalar>(vec3<T>) -> vec3<T>
+@const("Identity") ctor vec4<T: concrete_scalar>(vec4<T>) -> vec4<T>
@const("Identity") ctor mat2x2<T: f32_f16>(mat2x2<T>) -> mat2x2<T>
@const("Identity") ctor mat2x3<T: f32_f16>(mat2x3<T>) -> mat2x3<T>
@const("Identity") ctor mat2x4<T: f32_f16>(mat2x4<T>) -> mat2x4<T>
@@ -753,24 +753,24 @@
@const("Identity") ctor mat4x4<T: f32_f16>(mat4x4<T>) -> mat4x4<T>
// Vector constructors (splat)
-@const("VecSplat") ctor vec2<T: abstract_or_scalar>(T) -> vec2<T>
-@const("VecSplat") ctor vec3<T: abstract_or_scalar>(T) -> vec3<T>
-@const("VecSplat") ctor vec4<T: abstract_or_scalar>(T) -> vec4<T>
+@const("VecSplat") ctor vec2<T: scalar>(T) -> vec2<T>
+@const("VecSplat") ctor vec3<T: scalar>(T) -> vec3<T>
+@const("VecSplat") ctor vec4<T: scalar>(T) -> vec4<T>
// Vector constructors (scalar)
-@const("VecCtorS") ctor vec2<T: abstract_or_scalar>(x: T, y: T) -> vec2<T>
-@const("VecCtorS") ctor vec3<T: abstract_or_scalar>(x: T, y: T, z: T) -> vec3<T>
-@const("VecCtorS") ctor vec4<T: abstract_or_scalar>(x: T, y: T, z: T, w: T) -> vec4<T>
+@const("VecCtorS") ctor vec2<T: scalar>(x: T, y: T) -> vec2<T>
+@const("VecCtorS") ctor vec3<T: scalar>(x: T, y: T, z: T) -> vec3<T>
+@const("VecCtorS") ctor vec4<T: scalar>(x: T, y: T, z: T, w: T) -> vec4<T>
// Vector constructors (mixed)
-@const("VecCtorM") ctor vec3<T: abstract_or_scalar>(xy: vec2<T>, z: T) -> vec3<T>
-@const("VecCtorM") ctor vec3<T: abstract_or_scalar>(x: T, yz: vec2<T>) -> vec3<T>
-@const("VecCtorM") ctor vec4<T: abstract_or_scalar>(xy: vec2<T>, z: T, w: T) -> vec4<T>
-@const("VecCtorM") ctor vec4<T: abstract_or_scalar>(x: T, yz: vec2<T>, w: T) -> vec4<T>
-@const("VecCtorM") ctor vec4<T: abstract_or_scalar>(x: T, y: T, zw: vec2<T>) -> vec4<T>
-@const("VecCtorM") ctor vec4<T: abstract_or_scalar>(xy: vec2<T>, zw: vec2<T>) -> vec4<T>
-@const("VecCtorM") ctor vec4<T: abstract_or_scalar>(xyz: vec3<T>, w: T) -> vec4<T>
-@const("VecCtorM") ctor vec4<T: abstract_or_scalar>(x: T, zyw: vec3<T>) -> vec4<T>
+@const("VecCtorM") ctor vec3<T: scalar>(xy: vec2<T>, z: T) -> vec3<T>
+@const("VecCtorM") ctor vec3<T: scalar>(x: T, yz: vec2<T>) -> vec3<T>
+@const("VecCtorM") ctor vec4<T: scalar>(xy: vec2<T>, z: T, w: T) -> vec4<T>
+@const("VecCtorM") ctor vec4<T: scalar>(x: T, yz: vec2<T>, w: T) -> vec4<T>
+@const("VecCtorM") ctor vec4<T: scalar>(x: T, y: T, zw: vec2<T>) -> vec4<T>
+@const("VecCtorM") ctor vec4<T: scalar>(xy: vec2<T>, zw: vec2<T>) -> vec4<T>
+@const("VecCtorM") ctor vec4<T: scalar>(xyz: vec3<T>, w: T) -> vec4<T>
+@const("VecCtorM") ctor vec4<T: scalar>(x: T, zyw: vec3<T>) -> vec4<T>
// Matrix constructors (scalar)
@const("MatCtorS")
@@ -952,11 +952,11 @@
op && (bool, bool) -> bool
op || (bool, bool) -> bool
-@const op == <T: abstract_or_scalar>(T, T) -> bool
-@const op == <T: abstract_or_scalar, N: num> (vec<N, T>, vec<N, T>) -> vec<N, bool>
+@const op == <T: scalar>(T, T) -> bool
+@const op == <T: scalar, N: num> (vec<N, T>, vec<N, T>) -> vec<N, bool>
-@const op != <T: abstract_or_scalar>(T, T) -> bool
-@const op != <T: abstract_or_scalar, N: num> (vec<N, T>, vec<N, T>) -> vec<N, bool>
+@const op != <T: scalar>(T, T) -> bool
+@const op != <T: scalar, N: num> (vec<N, T>, vec<N, T>) -> vec<N, bool>
@const op < <T: fia_fiu32_f16>(T, T) -> bool
@const op < <T: fia_fiu32_f16, N: num> (vec<N, T>, vec<N, T>) -> vec<N, bool>
diff --git a/src/tint/resolver/const_eval.cc b/src/tint/resolver/const_eval.cc
index 9ddf24f..d9fde25 100644
--- a/src/tint/resolver/const_eval.cc
+++ b/src/tint/resolver/const_eval.cc
@@ -231,27 +231,29 @@
return this;
}
return ZeroTypeDispatch(target_ty, [&](auto zero_to) -> ImplResult {
- // `T` is the source type, `value` is the source value.
+ // `value` is the source value.
+ // `FROM` is the source type.
// `TO` is the target type.
using TO = std::decay_t<decltype(zero_to)>;
+ using FROM = T;
if constexpr (std::is_same_v<TO, bool>) {
// [x -> bool]
return builder.create<Element<TO>>(target_ty, !IsPositiveZero(value));
- } else if constexpr (std::is_same_v<T, bool>) {
+ } else if constexpr (std::is_same_v<FROM, bool>) {
// [bool -> x]
return builder.create<Element<TO>>(target_ty, TO(value ? 1 : 0));
} else if (auto conv = CheckedConvert<TO>(value)) {
// Conversion success
return builder.create<Element<TO>>(target_ty, conv.Get());
// --- Below this point are the failure cases ---
- } else if constexpr (IsAbstract<T>) {
+ } else if constexpr (IsAbstract<FROM>) {
// [abstract-numeric -> x] - materialization failure
std::stringstream ss;
ss << "value " << value << " cannot be represented as ";
ss << "'" << builder.FriendlyName(target_ty) << "'";
builder.Diagnostics().add_error(tint::diag::System::Resolver, ss.str(), source);
return utils::Failure;
- } else if constexpr (IsFloatingPoint<UnwrapNumber<TO>>) {
+ } else if constexpr (IsFloatingPoint<TO>) {
// [x -> floating-point] - number not exactly representable
// https://www.w3.org/TR/WGSL/#floating-point-conversion
switch (conv.Failure()) {
@@ -260,8 +262,8 @@
case ConversionFailure::kExceedsPositiveLimit:
return builder.create<Element<TO>>(target_ty, TO::Inf());
}
- } else {
- // [x -> integer] - number not exactly representable
+ } else if constexpr (IsFloatingPoint<FROM>) {
+ // [floating-point -> integer] - number not exactly representable
// https://www.w3.org/TR/WGSL/#floating-point-conversion
switch (conv.Failure()) {
case ConversionFailure::kExceedsNegativeLimit:
@@ -269,6 +271,10 @@
case ConversionFailure::kExceedsPositiveLimit:
return builder.create<Element<TO>>(target_ty, TO::Highest());
}
+ } else if constexpr (IsIntegral<FROM>) {
+ // [integer -> integer] - number not exactly representable
+ // Static cast
+ return builder.create<Element<TO>>(target_ty, static_cast<TO>(value));
}
return nullptr; // Expression is not constant.
});
@@ -842,11 +848,7 @@
return nullptr; // Single argument is not constant.
}
- if (auto conv = Convert(ty, args[0], source)) {
- return conv.Get();
- }
-
- return nullptr;
+ return Convert(ty, args[0], source);
}
ConstEval::Result ConstEval::Zero(const sem::Type* ty,
diff --git a/src/tint/resolver/const_eval_test.cc b/src/tint/resolver/const_eval_test.cc
index 8bded4c..b966097 100644
--- a/src/tint/resolver/const_eval_test.cc
+++ b/src/tint/resolver/const_eval_test.cc
@@ -44,6 +44,30 @@
template <typename T>
const auto k3PiOver4 = T(UnwrapNumber<T>(2.356194490192344928846));
+/// Walks the sem::Constant @p c, accumulating all the inner-most scalar values into @p args
+void CollectScalarArgs(const sem::Constant* c, builder::ScalarArgs& args) {
+ Switch(
+ c->Type(), //
+ [&](const sem::Bool*) { args.values.Push(c->As<bool>()); },
+ [&](const sem::I32*) { args.values.Push(c->As<i32>()); },
+ [&](const sem::U32*) { args.values.Push(c->As<u32>()); },
+ [&](const sem::F32*) { args.values.Push(c->As<f32>()); },
+ [&](const sem::F16*) { args.values.Push(c->As<f16>()); },
+ [&](Default) {
+ size_t i = 0;
+ while (auto* child = c->Index(i++)) {
+ CollectScalarArgs(child, args);
+ }
+ });
+}
+
+/// Walks the sem::Constant @p c, returning all the inner-most scalar values.
+builder::ScalarArgs ScalarArgsFrom(const sem::Constant* c) {
+ builder::ScalarArgs out;
+ CollectScalarArgs(c, out);
+ return out;
+}
+
template <typename T>
constexpr auto Negate(const Number<T>& v) {
if constexpr (std::is_integral_v<T>) {
@@ -1211,283 +1235,6 @@
EXPECT_EQ(sem->ConstantValue()->Index(2)->As<bool>(), false);
}
-TEST_F(ResolverConstEvalTest, Vec3_Convert_f32_to_i32) {
- auto* expr = vec3<i32>(vec3<f32>(1.1_f, 2.2_f, 3.3_f));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::I32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Convert_u32_to_f32) {
- auto* expr = vec3<f32>(vec3<u32>(10_u, 20_u, 30_u));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 10.f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 20.f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 30.f);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Convert_f16_to_i32) {
- Enable(ast::Extension::kF16);
-
- auto* expr = vec3<i32>(vec3<f16>(1.1_h, 2.2_h, 3.3_h));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::I32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1_i);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2_i);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3_i);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Convert_u32_to_f16) {
- Enable(ast::Extension::kF16);
-
- auto* expr = vec3<f16>(vec3<u32>(10_u, 20_u, 30_u));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F16>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 10.f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 20.f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 30.f);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Convert_Large_f32_to_i32) {
- auto* expr = vec3<i32>(vec3<f32>(1e10_f, -1e20_f, 1e30_f));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::I32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), i32::Highest());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), i32::Lowest());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), i32::Highest());
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Convert_Large_f32_to_u32) {
- auto* expr = vec3<u32>(vec3<f32>(1e10_f, -1e20_f, 1e30_f));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::U32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), u32::Highest());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), u32::Lowest());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), u32::Highest());
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Convert_Large_f32_to_f16) {
- Enable(ast::Extension::kF16);
-
- auto* expr = vec3<f16>(vec3<f32>(1e10_f, -1e20_f, 1e30_f));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- constexpr auto kInfinity = std::numeric_limits<double>::infinity();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F16>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), kInfinity);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), -kInfinity);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), kInfinity);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Convert_Small_f32_to_f16) {
- Enable(ast::Extension::kF16);
-
- auto* expr = vec3<f16>(vec3<f32>(1e-20_f, -2e-30_f, 3e-40_f));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F16>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 0.0);
- EXPECT_FALSE(std::signbit(sem->ConstantValue()->Index(0)->As<AFloat>().value));
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), -0.0);
- EXPECT_TRUE(std::signbit(sem->ConstantValue()->Index(1)->As<AFloat>().value));
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 0.0);
- EXPECT_FALSE(std::signbit(sem->ConstantValue()->Index(2)->As<AFloat>().value));
-}
-
TEST_F(ResolverConstEvalTest, Mat2x3_ZeroInit_f32) {
auto* expr = mat2x3<f32>();
WrapInFunction(expr);
@@ -2475,6 +2222,519 @@
}
////////////////////////////////////////////////////////////////////////////////////////////////////
+// Conversion
+////////////////////////////////////////////////////////////////////////////////////////////////////
+namespace conv {
+
+using Scalar = std::variant< //
+ builder::Value<AInt>,
+ builder::Value<AFloat>,
+ builder::Value<u32>,
+ builder::Value<i32>,
+ builder::Value<f32>,
+ builder::Value<f16>,
+ builder::Value<bool>>;
+
+static std::ostream& operator<<(std::ostream& o, const Scalar& scalar) {
+ std::visit(
+ [&](auto&& v) {
+ using ValueType = std::decay_t<decltype(v)>;
+ o << ValueType::DataType::Name() << "(";
+ for (auto& a : v.args.values) {
+ o << std::get<typename ValueType::ElementType>(a);
+ if (&a != &v.args.values.Back()) {
+ o << ", ";
+ }
+ }
+ o << ")";
+ },
+ scalar);
+ return o;
+}
+
+enum class Kind {
+ kScalar,
+ kVector,
+};
+
+static std::ostream& operator<<(std::ostream& o, const Kind& k) {
+ switch (k) {
+ case Kind::kScalar:
+ return o << "scalar";
+ case Kind::kVector:
+ return o << "vector";
+ }
+ return o << "<unknown>";
+}
+
+struct Case {
+ Scalar input;
+ Scalar expected;
+ builder::CreatePtrs type;
+ bool unrepresentable = false;
+};
+
+static std::ostream& operator<<(std::ostream& o, const Case& c) {
+ if (c.unrepresentable) {
+ o << "[unrepresentable] input: " << c.input;
+ } else {
+ o << "input: " << c.input << ", expected: " << c.expected;
+ }
+ return o << ", type: " << c.type;
+}
+
+template <typename TO, typename FROM>
+Case Success(FROM input, TO expected) {
+ return {builder::Val(input), builder::Val(expected), builder::CreatePtrsFor<TO>()};
+}
+
+template <typename TO, typename FROM>
+Case Unrepresentable(FROM input) {
+ return {builder::Val(input), builder::Val(0_i), builder::CreatePtrsFor<TO>(),
+ /* unrepresentable */ true};
+}
+
+using ResolverConstEvalConvTest = ResolverTestWithParam<std::tuple<Kind, Case>>;
+
+TEST_P(ResolverConstEvalConvTest, Test) {
+ const auto& kind = std::get<0>(GetParam());
+ const auto& input = std::get<1>(GetParam()).input;
+ const auto& expected = std::get<1>(GetParam()).expected;
+ const auto& type = std::get<1>(GetParam()).type;
+ const auto unrepresentable = std::get<1>(GetParam()).unrepresentable;
+
+ auto* input_val = std::visit([&](auto val) { return val.Expr(*this); }, input);
+ auto* expr = Construct(type.ast(*this), input_val);
+ if (kind == Kind::kVector) {
+ expr = Construct(ty.vec(nullptr, 3), expr);
+ }
+ WrapInFunction(expr);
+
+ auto* target_sem_ty = type.sem(*this);
+ if (kind == Kind::kVector) {
+ target_sem_ty = create<sem::Vector>(target_sem_ty, 3u);
+ }
+
+ if (unrepresentable) {
+ ASSERT_FALSE(r()->Resolve());
+ EXPECT_THAT(r()->error(), testing::HasSubstr("cannot be represented as"));
+ } else {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TYPE(sem->Type(), target_sem_ty);
+ ASSERT_NE(sem->ConstantValue(), nullptr);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), target_sem_ty);
+
+ auto expected_values = std::visit([&](auto&& val) { return val.args; }, expected);
+ if (kind == Kind::kVector) {
+ expected_values.values.Push(expected_values.values[0]);
+ expected_values.values.Push(expected_values.values[0]);
+ }
+ auto got_values = ScalarArgsFrom(sem->ConstantValue());
+ EXPECT_EQ(expected_values, got_values);
+ }
+}
+INSTANTIATE_TEST_SUITE_P(ScalarAndVector,
+ ResolverConstEvalConvTest,
+ testing::Combine(testing::Values(Kind::kScalar, Kind::kVector),
+ testing::ValuesIn({
+ // TODO(crbug.com/tint/1502): Add f16 tests
+ // i32 -> u32
+ Success(0_i, 0_u),
+ Success(1_i, 1_u),
+ Success(-1_i, 0xffffffff_u),
+ Success(2_i, 2_u),
+ Success(-2_i, 0xfffffffe_u),
+ // i32 -> f32
+ Success(0_i, 0_f),
+ Success(1_i, 1_f),
+ Success(-1_i, -1_f),
+ Success(2_i, 2_f),
+ Success(-2_i, -2_f),
+ // i32 -> bool
+ Success(0_i, false),
+ Success(1_i, true),
+ Success(-1_i, true),
+ Success(2_i, true),
+ Success(-2_i, true),
+ // u32 -> i32
+ Success(0_u, 0_i),
+ Success(1_u, 1_i),
+ Success(0xffffffff_u, -1_i),
+ Success(2_u, 2_i),
+ Success(0xfffffffe_u, -2_i),
+ // u32 -> f32
+ Success(0_u, 0_f),
+ Success(1_u, 1_f),
+ Success(2_u, 2_f),
+ Success(0xffffffff_u, 0xffffffff_f),
+ // u32 -> bool
+ Success(0_u, false),
+ Success(1_u, true),
+ Success(2_u, true),
+ Success(0xffffffff_u, true),
+ // f32 -> i32
+ Success(0_f, 0_i),
+ Success(1_f, 1_i),
+ Success(2_f, 2_i),
+ Success(1e20_f, i32::Highest()),
+ Success(-1e20_f, i32::Lowest()),
+ // f32 -> u32
+ Success(0_f, 0_i),
+ Success(1_f, 1_i),
+ Success(-1_f, u32::Lowest()),
+ Success(2_f, 2_i),
+ Success(1e20_f, u32::Highest()),
+ Success(-1e20_f, u32::Lowest()),
+ // f32 -> bool
+ Success(0_f, false),
+ Success(1_f, true),
+ Success(-1_f, true),
+ Success(2_f, true),
+ Success(1e20_f, true),
+ Success(-1e20_f, true),
+ // abstract-int -> i32
+ Success(0_a, 0_i),
+ Success(1_a, 1_i),
+ Success(-1_a, -1_i),
+ Success(0x7fffffff_a, i32::Highest()),
+ Success(-0x80000000_a, i32::Lowest()),
+ Unrepresentable<i32>(0x80000000_a),
+ // abstract-int -> u32
+ Success(0_a, 0_u),
+ Success(1_a, 1_u),
+ Success(0xffffffff_a, 0xffffffff_u),
+ Unrepresentable<u32>(0x100000000_a),
+ Unrepresentable<u32>(-1_a),
+ // abstract-int -> f32
+ Success(0_a, 0_f),
+ Success(1_a, 1_f),
+ Success(0xffffffff_a, 0xffffffff_f),
+ Success(0x100000000_a, 0x100000000_f),
+ Success(-0x100000000_a, -0x100000000_f),
+ Success(0x7fffffffffffffff_a, 0x7fffffffffffffff_f),
+ Success(-0x7fffffffffffffff_a, -0x7fffffffffffffff_f),
+ // abstract-int -> bool
+ Success(0_a, false),
+ Success(1_a, true),
+ Success(0xffffffff_a, true),
+ Success(0x100000000_a, true),
+ Success(-0x100000000_a, true),
+ Success(0x7fffffffffffffff_a, true),
+ Success(-0x7fffffffffffffff_a, true),
+ // abstract-float -> i32
+ Success(0.0_a, 0_i),
+ Success(1.0_a, 1_i),
+ Success(-1.0_a, -1_i),
+ Success(AFloat(0x7fffffff), i32::Highest()),
+ Success(-AFloat(0x80000000), i32::Lowest()),
+ Unrepresentable<i32>(0x80000000_a),
+ // abstract-float -> u32
+ Success(0.0_a, 0_u),
+ Success(1.0_a, 1_u),
+ Success(AFloat(0xffffffff), 0xffffffff_u),
+ Unrepresentable<u32>(AFloat(0x100000000)),
+ Unrepresentable<u32>(AFloat(-1)),
+ // abstract-float -> f32
+ Success(0.0_a, 0_f),
+ Success(1.0_a, 1_f),
+ Success(AFloat(0xffffffff), 0xffffffff_f),
+ Success(AFloat(0x100000000), 0x100000000_f),
+ Success(-AFloat(0x100000000), -0x100000000_f),
+ Unrepresentable<f32>(1e40_a),
+ Unrepresentable<f32>(-1e40_a),
+ // abstract-float -> bool
+ Success(0.0_a, false),
+ Success(1.0_a, true),
+ Success(AFloat(0xffffffff), true),
+ Success(AFloat(0x100000000), true),
+ Success(-AFloat(0x100000000), true),
+ Success(1e40_a, true),
+ Success(-1e40_a, true),
+ })));
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_f32_to_i32) {
+ auto* expr = vec3<i32>(vec3<f32>(1.1_f, 2.2_f, 3.3_f));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::I32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_u32_to_f32) {
+ auto* expr = vec3<f32>(vec3<u32>(10_u, 20_u, 30_u));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 10.f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 20.f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 30.f);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_f16_to_i32) {
+ Enable(ast::Extension::kF16);
+
+ auto* expr = vec3<i32>(vec3<f16>(1.1_h, 2.2_h, 3.3_h));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::I32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1_i);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2_i);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3_i);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_u32_to_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* expr = vec3<f16>(vec3<u32>(10_u, 20_u, 30_u));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F16>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 10.f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 20.f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 30.f);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_Large_f32_to_i32) {
+ auto* expr = vec3<i32>(vec3<f32>(1e10_f, -1e20_f, 1e30_f));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::I32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), i32::Highest());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), i32::Lowest());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), i32::Highest());
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_Large_f32_to_u32) {
+ auto* expr = vec3<u32>(vec3<f32>(1e10_f, -1e20_f, 1e30_f));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::U32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), u32::Highest());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), u32::Lowest());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), u32::Highest());
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_Large_f32_to_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* expr = vec3<f16>(vec3<f32>(1e10_f, -1e20_f, 1e30_f));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ constexpr auto kInfinity = std::numeric_limits<double>::infinity();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F16>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), kInfinity);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), -kInfinity);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), kInfinity);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_Small_f32_to_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* expr = vec3<f16>(vec3<f32>(1e-20_f, -2e-30_f, 3e-40_f));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F16>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 0.0);
+ EXPECT_FALSE(std::signbit(sem->ConstantValue()->Index(0)->As<AFloat>().value));
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), -0.0);
+ EXPECT_TRUE(std::signbit(sem->ConstantValue()->Index(1)->As<AFloat>().value));
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 0.0);
+ EXPECT_FALSE(std::signbit(sem->ConstantValue()->Index(2)->As<AFloat>().value));
+}
+
+} // namespace conv
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
// Indexing
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/tint/resolver/intrinsic_table.inl b/src/tint/resolver/intrinsic_table.inl
index 67cb23f..426b78b 100644
--- a/src/tint/resolver/intrinsic_table.inl
+++ b/src/tint/resolver/intrinsic_table.inl
@@ -1550,8 +1550,8 @@
return "__atomic_compare_exchange_result<" + T + ">";
}
-/// TypeMatcher for 'match abstract_or_scalar'
-class AbstractOrScalar : public TypeMatcher {
+/// TypeMatcher for 'match scalar'
+class Scalar : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules, and returns the
/// expected, canonicalized type on success.
@@ -1566,7 +1566,7 @@
std::string String(MatchState* state) const override;
};
-const sem::Type* AbstractOrScalar::Match(MatchState& state, const sem::Type* ty) const {
+const sem::Type* Scalar::Match(MatchState& state, const sem::Type* ty) const {
if (match_ia(state, ty)) {
return build_ia(state);
}
@@ -1591,7 +1591,7 @@
return nullptr;
}
-std::string AbstractOrScalar::String(MatchState*) const {
+std::string Scalar::String(MatchState*) const {
std::stringstream ss;
// Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
// template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
@@ -1599,8 +1599,8 @@
return ss.str();
}
-/// TypeMatcher for 'match scalar'
-class Scalar : public TypeMatcher {
+/// TypeMatcher for 'match concrete_scalar'
+class ConcreteScalar : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules, and returns the
/// expected, canonicalized type on success.
@@ -1615,7 +1615,7 @@
std::string String(MatchState* state) const override;
};
-const sem::Type* Scalar::Match(MatchState& state, const sem::Type* ty) const {
+const sem::Type* ConcreteScalar::Match(MatchState& state, const sem::Type* ty) const {
if (match_i32(state, ty)) {
return build_i32(state);
}
@@ -1634,7 +1634,7 @@
return nullptr;
}
-std::string Scalar::String(MatchState*) const {
+std::string ConcreteScalar::String(MatchState*) const {
std::stringstream ss;
// Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
// template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
@@ -1659,6 +1659,12 @@
};
const sem::Type* ScalarNoF32::Match(MatchState& state, const sem::Type* ty) const {
+ if (match_ia(state, ty)) {
+ return build_ia(state);
+ }
+ if (match_fa(state, ty)) {
+ return build_fa(state);
+ }
if (match_i32(state, ty)) {
return build_i32(state);
}
@@ -1678,7 +1684,7 @@
std::stringstream ss;
// Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
// template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
- ss << I32().String(nullptr) << ", " << F16().String(nullptr) << ", " << U32().String(nullptr) << " or " << Bool().String(nullptr);
+ ss << Ia().String(nullptr) << ", " << Fa().String(nullptr) << ", " << I32().String(nullptr) << ", " << F16().String(nullptr) << ", " << U32().String(nullptr) << " or " << Bool().String(nullptr);
return ss.str();
}
@@ -1699,6 +1705,12 @@
};
const sem::Type* ScalarNoF16::Match(MatchState& state, const sem::Type* ty) const {
+ if (match_ia(state, ty)) {
+ return build_ia(state);
+ }
+ if (match_fa(state, ty)) {
+ return build_fa(state);
+ }
if (match_i32(state, ty)) {
return build_i32(state);
}
@@ -1718,7 +1730,7 @@
std::stringstream ss;
// Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
// template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
- ss << F32().String(nullptr) << ", " << I32().String(nullptr) << ", " << U32().String(nullptr) << " or " << Bool().String(nullptr);
+ ss << Ia().String(nullptr) << ", " << Fa().String(nullptr) << ", " << F32().String(nullptr) << ", " << I32().String(nullptr) << ", " << U32().String(nullptr) << " or " << Bool().String(nullptr);
return ss.str();
}
@@ -1739,6 +1751,12 @@
};
const sem::Type* ScalarNoI32::Match(MatchState& state, const sem::Type* ty) const {
+ if (match_ia(state, ty)) {
+ return build_ia(state);
+ }
+ if (match_fa(state, ty)) {
+ return build_fa(state);
+ }
if (match_u32(state, ty)) {
return build_u32(state);
}
@@ -1758,7 +1776,7 @@
std::stringstream ss;
// Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
// template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
- ss << F32().String(nullptr) << ", " << F16().String(nullptr) << ", " << U32().String(nullptr) << " or " << Bool().String(nullptr);
+ ss << Ia().String(nullptr) << ", " << Fa().String(nullptr) << ", " << F32().String(nullptr) << ", " << F16().String(nullptr) << ", " << U32().String(nullptr) << " or " << Bool().String(nullptr);
return ss.str();
}
@@ -1779,6 +1797,12 @@
};
const sem::Type* ScalarNoU32::Match(MatchState& state, const sem::Type* ty) const {
+ if (match_ia(state, ty)) {
+ return build_ia(state);
+ }
+ if (match_fa(state, ty)) {
+ return build_fa(state);
+ }
if (match_i32(state, ty)) {
return build_i32(state);
}
@@ -1798,7 +1822,7 @@
std::stringstream ss;
// Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
// template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
- ss << F32().String(nullptr) << ", " << F16().String(nullptr) << ", " << I32().String(nullptr) << " or " << Bool().String(nullptr);
+ ss << Ia().String(nullptr) << ", " << Fa().String(nullptr) << ", " << F32().String(nullptr) << ", " << F16().String(nullptr) << ", " << I32().String(nullptr) << " or " << Bool().String(nullptr);
return ss.str();
}
@@ -1819,6 +1843,12 @@
};
const sem::Type* ScalarNoBool::Match(MatchState& state, const sem::Type* ty) const {
+ if (match_ia(state, ty)) {
+ return build_ia(state);
+ }
+ if (match_fa(state, ty)) {
+ return build_fa(state);
+ }
if (match_i32(state, ty)) {
return build_i32(state);
}
@@ -1838,7 +1868,7 @@
std::stringstream ss;
// Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
// template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
- ss << F32().String(nullptr) << ", " << F16().String(nullptr) << ", " << I32().String(nullptr) << " or " << U32().String(nullptr);
+ ss << Ia().String(nullptr) << ", " << Fa().String(nullptr) << ", " << F32().String(nullptr) << ", " << F16().String(nullptr) << ", " << I32().String(nullptr) << " or " << U32().String(nullptr);
return ss.str();
}
@@ -2580,8 +2610,8 @@
FrexpResult FrexpResult_;
FrexpResultVec FrexpResultVec_;
AtomicCompareExchangeResult AtomicCompareExchangeResult_;
- AbstractOrScalar AbstractOrScalar_;
Scalar Scalar_;
+ ConcreteScalar ConcreteScalar_;
ScalarNoF32 ScalarNoF32_;
ScalarNoF16 ScalarNoF16_;
ScalarNoI32 ScalarNoI32_;
@@ -2666,8 +2696,8 @@
/* [47] */ &FrexpResult_,
/* [48] */ &FrexpResultVec_,
/* [49] */ &AtomicCompareExchangeResult_,
- /* [50] */ &AbstractOrScalar_,
- /* [51] */ &Scalar_,
+ /* [50] */ &Scalar_,
+ /* [51] */ &ConcreteScalar_,
/* [52] */ &ScalarNoF32_,
/* [53] */ &ScalarNoF16_,
/* [54] */ &ScalarNoI32_,
@@ -14348,9 +14378,9 @@
},
{
/* [67] */
- /* fn select<T : abstract_or_scalar>(T, T, bool) -> T */
- /* fn select<T : abstract_or_scalar, N : num>(vec<N, T>, vec<N, T>, bool) -> vec<N, T> */
- /* fn select<N : num, T : abstract_or_scalar>(vec<N, T>, vec<N, T>, vec<N, bool>) -> vec<N, T> */
+ /* fn select<T : scalar>(T, T, bool) -> T */
+ /* fn select<T : scalar, N : num>(vec<N, T>, vec<N, T>, bool) -> vec<N, T> */
+ /* fn select<N : num, T : scalar>(vec<N, T>, vec<N, T>, vec<N, bool>) -> vec<N, T> */
/* num overloads */ 3,
/* overloads */ &kOverloads[276],
},
@@ -14876,15 +14906,15 @@
},
{
/* [10] */
- /* op ==<T : abstract_or_scalar>(T, T) -> bool */
- /* op ==<T : abstract_or_scalar, N : num>(vec<N, T>, vec<N, T>) -> vec<N, bool> */
+ /* op ==<T : scalar>(T, T) -> bool */
+ /* op ==<T : scalar, N : num>(vec<N, T>, vec<N, T>) -> vec<N, bool> */
/* num overloads */ 2,
/* overloads */ &kOverloads[408],
},
{
/* [11] */
- /* op !=<T : abstract_or_scalar>(T, T) -> bool */
- /* op !=<T : abstract_or_scalar, N : num>(vec<N, T>, vec<N, T>) -> vec<N, bool> */
+ /* op !=<T : scalar>(T, T) -> bool */
+ /* op !=<T : scalar, N : num>(vec<N, T>, vec<N, T>) -> vec<N, bool> */
/* num overloads */ 2,
/* overloads */ &kOverloads[394],
},
@@ -14995,10 +15025,10 @@
},
{
/* [5] */
- /* ctor vec2<T : scalar>() -> vec2<T> */
- /* ctor vec2<T : scalar>(vec2<T>) -> vec2<T> */
- /* ctor vec2<T : abstract_or_scalar>(T) -> vec2<T> */
- /* ctor vec2<T : abstract_or_scalar>(x: T, y: T) -> vec2<T> */
+ /* ctor vec2<T : concrete_scalar>() -> vec2<T> */
+ /* ctor vec2<T : concrete_scalar>(vec2<T>) -> vec2<T> */
+ /* ctor vec2<T : scalar>(T) -> vec2<T> */
+ /* ctor vec2<T : scalar>(x: T, y: T) -> vec2<T> */
/* conv vec2<T : f32, U : scalar_no_f32>(vec2<U>) -> vec2<f32> */
/* conv vec2<T : f16, U : scalar_no_f16>(vec2<U>) -> vec2<f16> */
/* conv vec2<T : i32, U : scalar_no_i32>(vec2<U>) -> vec2<i32> */
@@ -15009,12 +15039,12 @@
},
{
/* [6] */
- /* ctor vec3<T : scalar>() -> vec3<T> */
- /* ctor vec3<T : scalar>(vec3<T>) -> vec3<T> */
- /* ctor vec3<T : abstract_or_scalar>(T) -> vec3<T> */
- /* ctor vec3<T : abstract_or_scalar>(x: T, y: T, z: T) -> vec3<T> */
- /* ctor vec3<T : abstract_or_scalar>(xy: vec2<T>, z: T) -> vec3<T> */
- /* ctor vec3<T : abstract_or_scalar>(x: T, yz: vec2<T>) -> vec3<T> */
+ /* ctor vec3<T : concrete_scalar>() -> vec3<T> */
+ /* ctor vec3<T : concrete_scalar>(vec3<T>) -> vec3<T> */
+ /* ctor vec3<T : scalar>(T) -> vec3<T> */
+ /* ctor vec3<T : scalar>(x: T, y: T, z: T) -> vec3<T> */
+ /* ctor vec3<T : scalar>(xy: vec2<T>, z: T) -> vec3<T> */
+ /* ctor vec3<T : scalar>(x: T, yz: vec2<T>) -> vec3<T> */
/* conv vec3<T : f32, U : scalar_no_f32>(vec3<U>) -> vec3<f32> */
/* conv vec3<T : f16, U : scalar_no_f16>(vec3<U>) -> vec3<f16> */
/* conv vec3<T : i32, U : scalar_no_i32>(vec3<U>) -> vec3<i32> */
@@ -15025,16 +15055,16 @@
},
{
/* [7] */
- /* ctor vec4<T : scalar>() -> vec4<T> */
- /* ctor vec4<T : scalar>(vec4<T>) -> vec4<T> */
- /* ctor vec4<T : abstract_or_scalar>(T) -> vec4<T> */
- /* ctor vec4<T : abstract_or_scalar>(x: T, y: T, z: T, w: T) -> vec4<T> */
- /* ctor vec4<T : abstract_or_scalar>(xy: vec2<T>, z: T, w: T) -> vec4<T> */
- /* ctor vec4<T : abstract_or_scalar>(x: T, yz: vec2<T>, w: T) -> vec4<T> */
- /* ctor vec4<T : abstract_or_scalar>(x: T, y: T, zw: vec2<T>) -> vec4<T> */
- /* ctor vec4<T : abstract_or_scalar>(xy: vec2<T>, zw: vec2<T>) -> vec4<T> */
- /* ctor vec4<T : abstract_or_scalar>(xyz: vec3<T>, w: T) -> vec4<T> */
- /* ctor vec4<T : abstract_or_scalar>(x: T, zyw: vec3<T>) -> vec4<T> */
+ /* ctor vec4<T : concrete_scalar>() -> vec4<T> */
+ /* ctor vec4<T : concrete_scalar>(vec4<T>) -> vec4<T> */
+ /* ctor vec4<T : scalar>(T) -> vec4<T> */
+ /* ctor vec4<T : scalar>(x: T, y: T, z: T, w: T) -> vec4<T> */
+ /* ctor vec4<T : scalar>(xy: vec2<T>, z: T, w: T) -> vec4<T> */
+ /* ctor vec4<T : scalar>(x: T, yz: vec2<T>, w: T) -> vec4<T> */
+ /* ctor vec4<T : scalar>(x: T, y: T, zw: vec2<T>) -> vec4<T> */
+ /* ctor vec4<T : scalar>(xy: vec2<T>, zw: vec2<T>) -> vec4<T> */
+ /* ctor vec4<T : scalar>(xyz: vec3<T>, w: T) -> vec4<T> */
+ /* ctor vec4<T : scalar>(x: T, zyw: vec3<T>) -> vec4<T> */
/* conv vec4<T : f32, U : scalar_no_f32>(vec4<U>) -> vec4<f32> */
/* conv vec4<T : f16, U : scalar_no_f16>(vec4<U>) -> vec4<f16> */
/* conv vec4<T : i32, U : scalar_no_i32>(vec4<U>) -> vec4<i32> */
diff --git a/src/tint/resolver/intrinsic_table_test.cc b/src/tint/resolver/intrinsic_table_test.cc
index 34cec07..b99fc34 100644
--- a/src/tint/resolver/intrinsic_table_test.cc
+++ b/src/tint/resolver/intrinsic_table_test.cc
@@ -793,11 +793,11 @@
vec3() -> vec3<T> where: T is f32, f16, i32, u32 or bool
5 candidate conversions:
- vec3(vec3<U>) -> vec3<f32> where: T is f32, U is i32, f16, u32 or bool
- vec3(vec3<U>) -> vec3<f16> where: T is f16, U is f32, i32, u32 or bool
- vec3(vec3<U>) -> vec3<i32> where: T is i32, U is f32, f16, u32 or bool
- vec3(vec3<U>) -> vec3<u32> where: T is u32, U is f32, f16, i32 or bool
- vec3(vec3<U>) -> vec3<bool> where: T is bool, U is f32, f16, i32 or u32
+ vec3(vec3<U>) -> vec3<f32> where: T is f32, U is abstract-int, abstract-float, i32, f16, u32 or bool
+ vec3(vec3<U>) -> vec3<f16> where: T is f16, U is abstract-int, abstract-float, f32, i32, u32 or bool
+ vec3(vec3<U>) -> vec3<i32> where: T is i32, U is abstract-int, abstract-float, f32, f16, u32 or bool
+ vec3(vec3<U>) -> vec3<u32> where: T is u32, U is abstract-int, abstract-float, f32, f16, i32 or bool
+ vec3(vec3<U>) -> vec3<bool> where: T is bool, U is abstract-int, abstract-float, f32, f16, i32 or u32
)");
}
@@ -819,11 +819,11 @@
vec3() -> vec3<T> where: T is f32, f16, i32, u32 or bool
5 candidate conversions:
- vec3(vec3<U>) -> vec3<f32> where: T is f32, U is i32, f16, u32 or bool
- vec3(vec3<U>) -> vec3<f16> where: T is f16, U is f32, i32, u32 or bool
- vec3(vec3<U>) -> vec3<i32> where: T is i32, U is f32, f16, u32 or bool
- vec3(vec3<U>) -> vec3<u32> where: T is u32, U is f32, f16, i32 or bool
- vec3(vec3<U>) -> vec3<bool> where: T is bool, U is f32, f16, i32 or u32
+ vec3(vec3<U>) -> vec3<f32> where: T is f32, U is abstract-int, abstract-float, i32, f16, u32 or bool
+ vec3(vec3<U>) -> vec3<f16> where: T is f16, U is abstract-int, abstract-float, f32, i32, u32 or bool
+ vec3(vec3<U>) -> vec3<i32> where: T is i32, U is abstract-int, abstract-float, f32, f16, u32 or bool
+ vec3(vec3<U>) -> vec3<u32> where: T is u32, U is abstract-int, abstract-float, f32, f16, i32 or bool
+ vec3(vec3<U>) -> vec3<bool> where: T is bool, U is abstract-int, abstract-float, f32, f16, i32 or u32
)");
}
@@ -875,11 +875,11 @@
vec3(x: T, y: T, z: T) -> vec3<T> where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
5 candidate conversions:
- vec3(vec3<U>) -> vec3<f32> where: T is f32, U is i32, f16, u32 or bool
- vec3(vec3<U>) -> vec3<f16> where: T is f16, U is f32, i32, u32 or bool
- vec3(vec3<U>) -> vec3<i32> where: T is i32, U is f32, f16, u32 or bool
- vec3(vec3<U>) -> vec3<u32> where: T is u32, U is f32, f16, i32 or bool
- vec3(vec3<U>) -> vec3<bool> where: T is bool, U is f32, f16, i32 or u32
+ vec3(vec3<U>) -> vec3<f32> where: T is f32, U is abstract-int, abstract-float, i32, f16, u32 or bool
+ vec3(vec3<U>) -> vec3<f16> where: T is f16, U is abstract-int, abstract-float, f32, i32, u32 or bool
+ vec3(vec3<U>) -> vec3<i32> where: T is i32, U is abstract-int, abstract-float, f32, f16, u32 or bool
+ vec3(vec3<U>) -> vec3<u32> where: T is u32, U is abstract-int, abstract-float, f32, f16, i32 or bool
+ vec3(vec3<U>) -> vec3<bool> where: T is bool, U is abstract-int, abstract-float, f32, f16, i32 or u32
)");
}
@@ -904,7 +904,7 @@
ASSERT_NE(result.target, nullptr);
EXPECT_EQ(result.target->ReturnType(), i32);
EXPECT_EQ(result.target->Parameters().Length(), 1u);
- EXPECT_EQ(result.target->Parameters()[0]->Type(), i32);
+ EXPECT_EQ(result.target->Parameters()[0]->Type(), ai);
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/src/tint/resolver/resolver_test_helper.h b/src/tint/resolver/resolver_test_helper.h
index 501cd0f..923f3ff 100644
--- a/src/tint/resolver/resolver_test_helper.h
+++ b/src/tint/resolver/resolver_test_helper.h
@@ -17,6 +17,7 @@
#include <functional>
#include <memory>
+#include <ostream>
#include <string>
#include <tuple>
#include <utility>
@@ -174,13 +175,15 @@
struct ptr {};
/// Type used to accept scalars as arguments. Can be either a single value that gets splatted for
-/// composite types, or all values requried by the composite type.
+/// composite types, or all values required by the composite type.
struct ScalarArgs {
/// Constructor
+ ScalarArgs() = default;
+
+ /// Constructor
/// @param single_value single value to initialize with
template <typename T>
- ScalarArgs(T single_value) // NOLINT: implicit on purpose
- : values(utils::Vector<Storage, 1>{single_value}) {}
+ explicit ScalarArgs(T single_value) : values(utils::Vector<Storage, 1>{single_value}) {}
/// Constructor
/// @param all_values all values to initialize the composite type with
@@ -192,6 +195,10 @@
}
}
+ /// @param other the other ScalarArgs to compare against
+ /// @returns true if all values are equal to the values in @p other
+ bool operator==(const ScalarArgs& other) const { return values == other.values; }
+
/// Valid scalar types for args
using Storage = std::variant<i32, u32, f32, f16, AInt, AFloat, bool>;
@@ -199,10 +206,28 @@
utils::Vector<Storage, 16> values;
};
+/// @param o the std::ostream to write to
+/// @param args the ScalarArgs
+/// @return the std::ostream so calls can be chained
+inline std::ostream& operator<<(std::ostream& o, const ScalarArgs& args) {
+ o << "[";
+ bool first = true;
+ for (auto& val : args.values) {
+ if (!first) {
+ o << ", ";
+ }
+ first = false;
+ std::visit([&](auto&& v) { o << v; }, val);
+ }
+ o << "]";
+ return o;
+}
+
using ast_type_func_ptr = const ast::Type* (*)(ProgramBuilder& b);
using ast_expr_func_ptr = const ast::Expression* (*)(ProgramBuilder& b, ScalarArgs args);
using ast_expr_from_double_func_ptr = const ast::Expression* (*)(ProgramBuilder& b, double v);
using sem_type_func_ptr = const sem::Type* (*)(ProgramBuilder& b);
+using type_name_func_ptr = std::string (*)();
template <typename T>
struct DataType {};
@@ -241,7 +266,7 @@
/// @param v arg of type double that will be cast to bool.
/// @return a new AST expression of the bool type
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<ElementType>(v));
+ return Expr(b, ScalarArgs{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() { return "bool"; }
@@ -272,7 +297,7 @@
/// @param v arg of type double that will be cast to i32.
/// @return a new AST i32 literal value expression
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<ElementType>(v));
+ return Expr(b, ScalarArgs{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() { return "i32"; }
@@ -303,7 +328,7 @@
/// @param v arg of type double that will be cast to u32.
/// @return a new AST u32 literal value expression
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<ElementType>(v));
+ return Expr(b, ScalarArgs{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() { return "u32"; }
@@ -334,7 +359,7 @@
/// @param v arg of type double that will be cast to f32.
/// @return a new AST f32 literal value expression
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<f32>(v));
+ return Expr(b, ScalarArgs{static_cast<f32>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() { return "f32"; }
@@ -365,7 +390,7 @@
/// @param v arg of type double that will be cast to f16.
/// @return a new AST f16 literal value expression
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<ElementType>(v));
+ return Expr(b, ScalarArgs{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() { return "f16"; }
@@ -395,7 +420,7 @@
/// @param v arg of type double that will be cast to AFloat.
/// @return a new AST abstract-float literal value expression
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<ElementType>(v));
+ return Expr(b, ScalarArgs{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() { return "abstract-float"; }
@@ -425,7 +450,7 @@
/// @param v arg of type double that will be cast to AInt.
/// @return a new AST abstract-int literal value expression
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<ElementType>(v));
+ return Expr(b, ScalarArgs{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() { return "abstract-int"; }
@@ -463,7 +488,7 @@
const bool one_value = args.values.Length() == 1;
utils::Vector<const ast::Expression*, N> r;
for (size_t i = 0; i < N; ++i) {
- r.Push(DataType<T>::Expr(b, one_value ? args.values[0] : args.values[i]));
+ r.Push(DataType<T>::Expr(b, ScalarArgs{one_value ? args.values[0] : args.values[i]}));
}
return r;
}
@@ -471,7 +496,7 @@
/// @param v arg of type double that will be cast to ElementType
/// @return a new AST vector value expression
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<ElementType>(v));
+ return Expr(b, ScalarArgs{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() {
@@ -514,7 +539,7 @@
utils::Vector<const ast::Expression*, N> r;
for (uint32_t i = 0; i < N; ++i) {
if (one_value) {
- r.Push(DataType<vec<M, T>>::Expr(b, args.values[0]));
+ r.Push(DataType<vec<M, T>>::Expr(b, ScalarArgs{args.values[0]}));
} else {
utils::Vector<T, M> v;
for (size_t j = 0; j < M; ++j) {
@@ -529,7 +554,7 @@
/// @param v arg of type double that will be cast to ElementType
/// @return a new AST matrix value expression
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<ElementType>(v));
+ return Expr(b, ScalarArgs{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() {
@@ -585,7 +610,7 @@
/// @param v arg of type double that will be cast to ElementType
/// @return a new AST expression of the alias type
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<ElementType>(v));
+ return Expr(b, ScalarArgs{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
@@ -626,7 +651,7 @@
/// @param v arg of type double that will be cast to ElementType
/// @return a new AST expression of the pointer type
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<ElementType>(v));
+ return Expr(b, ScalarArgs{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
@@ -680,7 +705,7 @@
const bool one_value = args.values.Length() == 1;
utils::Vector<const ast::Expression*, N> r;
for (uint32_t i = 0; i < N; i++) {
- r.Push(DataType<T>::Expr(b, one_value ? args.values[0] : args.values[i]));
+ r.Push(DataType<T>::Expr(b, ScalarArgs{one_value ? args.values[0] : args.values[i]}));
}
return r;
}
@@ -688,7 +713,7 @@
/// @param v arg of type double that will be cast to ElementType
/// @return a new AST array value expression
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<ElementType>(v));
+ return Expr(b, ScalarArgs{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() {
@@ -706,13 +731,23 @@
ast_expr_from_double_func_ptr expr_from_double;
/// sem type create function
sem_type_func_ptr sem;
+ /// type name function
+ type_name_func_ptr name;
};
+/// @param o the std::ostream to write to
+/// @param ptrs the CreatePtrs
+/// @return the std::ostream so calls can be chained
+inline std::ostream& operator<<(std::ostream& o, const CreatePtrs& ptrs) {
+ return o << (ptrs.name ? ptrs.name() : "<unknown>");
+}
+
/// Returns a CreatePtrs struct instance with all creation pointer types for
/// type `T`
template <typename T>
constexpr CreatePtrs CreatePtrsFor() {
- return {DataType<T>::AST, DataType<T>::Expr, DataType<T>::ExprFromDouble, DataType<T>::Sem};
+ return {DataType<T>::AST, DataType<T>::Expr, DataType<T>::ExprFromDouble, DataType<T>::Sem,
+ DataType<T>::Name};
}
/// Value<T> is an instance of a value of type DataType<T>. Useful for storing values to create
@@ -729,15 +764,15 @@
/// Creates a Value<T> with `args`
/// @param args the args that will be passed to the expression
/// @returns a Value<T>
- static Value Create(ScalarArgs args) { return Value{DataType::Expr, std::move(args)}; }
+ static Value Create(ScalarArgs args) { return Value{CreatePtrsFor<T>(), std::move(args)}; }
/// Creates an `ast::Expression` for the type T passing in previously stored args
/// @param b the ProgramBuilder
/// @returns an expression node
- const ast::Expression* Expr(ProgramBuilder& b) const { return (*expr)(b, args); }
+ const ast::Expression* Expr(ProgramBuilder& b) const { return (*create.expr)(b, args); }
- /// ast expression type create function
- ast_expr_func_ptr expr;
+ /// functions to create values / types of the value
+ CreatePtrs create;
/// args to create expression with
ScalarArgs args;
};
@@ -764,7 +799,7 @@
/// Creates a `Value<T>` from a scalar `v`
template <typename T>
auto Val(T v) {
- return Value<T>::Create(v);
+ return Value<T>::Create(ScalarArgs{v});
}
/// Creates a `Value<vec<N, T>>` from N scalar `args`
diff --git a/src/tint/writer/msl/generator_impl_cast_test.cc b/src/tint/writer/msl/generator_impl_cast_test.cc
index 4b9e3f2..74b3be6 100644
--- a/src/tint/writer/msl/generator_impl_cast_test.cc
+++ b/src/tint/writer/msl/generator_impl_cast_test.cc
@@ -51,7 +51,7 @@
std::stringstream out;
ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.error();
- EXPECT_EQ(out.str(), "0u");
+ EXPECT_EQ(out.str(), "2147483648u");
}
} // namespace