diff --git a/src/tint/resolver/resolver_constants.cc b/src/tint/resolver/resolver_constants.cc index dd846f1cf0..3bf8df0853 100644 --- a/src/tint/resolver/resolver_constants.cc +++ b/src/tint/resolver/resolver_constants.cc @@ -89,14 +89,34 @@ sem::Constant::Elements Transform(const sem::Constant::Elements& in, in); } -/// Converts and returns all the elements in `in` to the type `el_ty`, by performing a `static_cast` -/// on each element value. No checks will be performed that the value fits in the target type. +/// Converts and returns all the elements in `in` to the type `el_ty`. +/// If the value does not fit in the target type, and: +/// * the target type is an integer type, then the resulting value will be clamped to the integer's +/// highest or lowest value. +/// * the target type is an float type, then the resulting value will be either positive or +/// negative infinity, based on the sign of the input value. /// @param in the input elements /// @param el_ty the target element type /// @returns the elements converted to `el_ty` sem::Constant::Elements ConvertElements(const sem::Constant::Elements& in, const sem::Type* el_ty) { return Transform(in, el_ty, [](auto& el_out, auto el_in) { - el_out = std::decay_t(el_in); + using OUT = std::decay_t; + if (auto conv = CheckedConvert(el_in)) { + el_out = conv.Get(); + } else { + constexpr auto kInf = std::numeric_limits::infinity(); + switch (conv.Failure()) { + case ConversionFailure::kExceedsNegativeLimit: + el_out = IsFloatingPoint> ? OUT(-kInf) : OUT::kLowest; + break; + case ConversionFailure::kExceedsPositiveLimit: + el_out = IsFloatingPoint> ? OUT(kInf) : OUT::kHighest; + break; + case ConversionFailure::kTooSmall: + el_out = OUT(el_in < 0 ? -0.0 : 0.0); + break; + } + } }); } diff --git a/src/tint/resolver/resolver_constants_test.cc b/src/tint/resolver/resolver_constants_test.cc index 7be067a918..bbdbfeafd4 100644 --- a/src/tint/resolver/resolver_constants_test.cc +++ b/src/tint/resolver/resolver_constants_test.cc @@ -389,7 +389,7 @@ TEST_F(ResolverConstantsTest, Vec3_MixConstruct_bool) { EXPECT_EQ(sem->ConstantValue().Element(2), true); } -TEST_F(ResolverConstantsTest, Vec3_Cast_f32_to_i32) { +TEST_F(ResolverConstantsTest, Vec3_Convert_f32_to_i32) { auto* expr = vec3(vec3(1.1_f, 2.2_f, 3.3_f)); WrapInFunction(expr); @@ -408,7 +408,7 @@ TEST_F(ResolverConstantsTest, Vec3_Cast_f32_to_i32) { EXPECT_EQ(sem->ConstantValue().Element(2).value, 3); } -TEST_F(ResolverConstantsTest, Vec3_Cast_u32_to_f32) { +TEST_F(ResolverConstantsTest, Vec3_Convert_u32_to_f32) { auto* expr = vec3(vec3(10_u, 20_u, 30_u)); WrapInFunction(expr); @@ -427,5 +427,89 @@ TEST_F(ResolverConstantsTest, Vec3_Cast_u32_to_f32) { EXPECT_EQ(sem->ConstantValue().Element(2).value, 30.f); } +TEST_F(ResolverConstantsTest, Vec3_Convert_Large_f32_to_i32) { + auto* expr = vec3(vec3(1e10_f, -1e20_f, 1e30_f)); + WrapInFunction(expr); + + EXPECT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(expr); + EXPECT_NE(sem, nullptr); + ASSERT_TRUE(sem->Type()->Is()); + EXPECT_TRUE(sem->Type()->As()->type()->Is()); + EXPECT_EQ(sem->Type()->As()->Width(), 3u); + EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); + ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); + EXPECT_EQ(sem->ConstantValue().Element(0).value, i32::kHighest); + EXPECT_EQ(sem->ConstantValue().Element(1).value, i32::kLowest); + EXPECT_EQ(sem->ConstantValue().Element(2).value, i32::kHighest); +} + +TEST_F(ResolverConstantsTest, Vec3_Convert_Large_f32_to_u32) { + auto* expr = vec3(vec3(1e10_f, -1e20_f, 1e30_f)); + WrapInFunction(expr); + + EXPECT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(expr); + EXPECT_NE(sem, nullptr); + ASSERT_TRUE(sem->Type()->Is()); + EXPECT_TRUE(sem->Type()->As()->type()->Is()); + EXPECT_EQ(sem->Type()->As()->Width(), 3u); + EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); + ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); + EXPECT_EQ(sem->ConstantValue().Element(0).value, u32::kHighest); + EXPECT_EQ(sem->ConstantValue().Element(1).value, u32::kLowest); + EXPECT_EQ(sem->ConstantValue().Element(2).value, u32::kHighest); +} + +// TODO(crbug.com/tint/1502): Enable when f16 overloads are implemented +TEST_F(ResolverConstantsTest, DISABLED_Vec3_Convert_Large_f32_to_f16) { + Enable(ast::Extension::kF16); + + auto* expr = vec3(vec3(0.00001_f, -0.00002_f, 0.00003_f)); + WrapInFunction(expr); + + EXPECT_TRUE(r()->Resolve()) << r()->error(); + + constexpr auto kInf = std::numeric_limits::infinity(); + + auto* sem = Sem().Get(expr); + EXPECT_NE(sem, nullptr); + ASSERT_TRUE(sem->Type()->Is()); + EXPECT_TRUE(sem->Type()->As()->type()->Is()); + EXPECT_EQ(sem->Type()->As()->Width(), 3u); + EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); + ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); + EXPECT_EQ(sem->ConstantValue().Element(0).value, kInf); + EXPECT_EQ(sem->ConstantValue().Element(1).value, -kInf); + EXPECT_EQ(sem->ConstantValue().Element(2).value, kInf); +} + +// TODO(crbug.com/tint/1502): Enable when f16 overloads are implemented +TEST_F(ResolverConstantsTest, DISABLED_Vec3_Convert_Small_f32_to_f16) { + Enable(ast::Extension::kF16); + + auto* expr = vec3(vec3(1e-10_f, -1e20_f, 1e30_f)); + WrapInFunction(expr); + + EXPECT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(expr); + EXPECT_NE(sem, nullptr); + ASSERT_TRUE(sem->Type()->Is()); + EXPECT_TRUE(sem->Type()->As()->type()->Is()); + EXPECT_EQ(sem->Type()->As()->Width(), 3u); + EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); + ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); + EXPECT_EQ(sem->ConstantValue().Element(0).value, 0.0); + EXPECT_EQ(sem->ConstantValue().Element(1).value, -0.0); + EXPECT_EQ(sem->ConstantValue().Element(2).value, 0.0); +} + } // namespace } // namespace tint::resolver