From 4a6562071605a1a73c14e1b705a8e4f002d03c39 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Mon, 1 Aug 2022 17:36:54 +0000 Subject: [PATCH] tint/resolver: Handle array() constructors If the array constructor has no explicit type and count, infer from the arguments. Implement and test array materialization. Also: * Change sem::AbstractNumeric::IsConstructable() to return true. This has changed in the WGSL spec. * Fix the test-helper builder for DataType> - it was incorrectly calculating the array size. Bug: tint:1628 Change-Id: I0ec4e55b469ca6423b4d1848f27bb11df4fa683b Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/97663 Commit-Queue: Ben Clayton Reviewed-by: David Neto --- src/tint/resolver/materialize_test.cc | 115 +++++++- src/tint/resolver/resolver.cc | 87 +++++- src/tint/resolver/resolver.h | 5 +- src/tint/resolver/resolver_test_helper.h | 7 +- .../type_constructor_validation_test.cc | 247 ++++++++++++++++++ src/tint/resolver/validator.cc | 2 +- src/tint/sem/abstract_numeric.cc | 2 +- src/tint/sem/abstract_numeric.h | 2 +- 8 files changed, 440 insertions(+), 27 deletions(-) diff --git a/src/tint/resolver/materialize_test.cc b/src/tint/resolver/materialize_test.cc index bd363c1904..b1ec91359a 100644 --- a/src/tint/resolver/materialize_test.cc +++ b/src/tint/resolver/materialize_test.cc @@ -27,13 +27,17 @@ namespace { using AFloatV = builder::vec<3, AFloat>; using AFloatM = builder::mat<3, 2, AFloat>; +using AFloatA = builder::array<3, AFloat>; using AIntV = builder::vec<3, AInt>; +using AIntA = builder::array<3, AInt>; using f32V = builder::vec<3, f32>; using f16V = builder::vec<3, f16>; using i32V = builder::vec<3, i32>; using u32V = builder::vec<3, u32>; using f32M = builder::mat<3, 2, f32>; -using i32Varr = builder::array<3, i32>; +using f32A = builder::array<3, f32>; +using i32A = builder::array<3, i32>; +using u32A = builder::array<3, u32>; constexpr double kTooBigF32 = static_cast(3.5e+38); // constexpr double kTooBigF16 = static_cast(6.6e+4); @@ -111,6 +115,14 @@ class MaterializeTest : public resolver::ResolverTestWithParam { } } }, + [&](const sem::Array* a) { + for (uint32_t i = 0; i < a->Count(); i++) { + auto* el = value->Index(i); + ASSERT_NE(el, nullptr); + EXPECT_TYPE(el->Type(), a->ElemType()); + EXPECT_EQ(std::get(el->Value()), expected_value); + } + }, [&](Default) { EXPECT_EQ(std::get(value->Value()), expected_value); }); } }; @@ -435,6 +447,12 @@ constexpr Method kMatrixMethods[] = { Method::kReturn, Method::kArray, Method::kStruct, Method::kBinaryOp, }; +/// Methods that support array materialization +constexpr Method kArrayMethods[] = { + Method::kLet, Method::kVar, Method::kAssign, Method::kFnArg, + Method::kReturn, Method::kArray, Method::kStruct, +}; + /// Methods that support materialization for switch cases constexpr Method kSwitchMethods[] = { Method::kSwitchCond, @@ -595,6 +613,57 @@ INSTANTIATE_TEST_SUITE_P( Types(AInt(u32::Lowest()), u32::Lowest()), // }))); +INSTANTIATE_TEST_SUITE_P( + MaterializeArray, + MaterializeAbstractNumericToConcreteType, + testing::Combine( + testing::Values(Expectation::kMaterialize), + testing::ValuesIn(kArrayMethods), + testing::ValuesIn(std::vector{ + Types(0_a, 0.0), // + Types(1_a, 1.0), // + Types(-1_a, -1.0), // + Types(AInt(i32::Highest()), i32::Highest()), // + Types(AInt(i32::Lowest()), i32::Lowest()), // + Types(0_a, 0.0), // + Types(1_a, 1.0), // + Types(AInt(u32::Highest()), u32::Highest()), // + Types(AInt(u32::Lowest()), u32::Lowest()), // + Types(0.0_a, 0.0), // + Types(1.0_a, 1.0), // + Types(-1.0_a, -1.0), // + Types(AFloat(f32::Highest()), static_cast(f32::Highest())), // + Types(AFloat(f32::Lowest()), static_cast(f32::Lowest())), // + Types(AFloat(kPiF32), kPiF64), // + Types(AFloat(kSubnormalF32), kSubnormalF32), // + Types(AFloat(-kSubnormalF32), -kSubnormalF32), // + /* Types(0.0_a, 0.0), */ // + /* Types(1.0_a, 1.0), */ // + /* Types(-1.0_a, -1.0), */ // + /* Types(AFloat(kHighestF16), kHighestF16), */ // + /* Types(AFloat(kLowestF16), kLowestF16), */ // + /* Types(AFloat(kPiF16), kPiF64), */ // + /* Types(AFloat(kSubnormalF16), kSubnormalF16), */ // + /* Types(AFloat(-kSubnormalF16), -kSubnormalF16), */ // + }))); + +INSTANTIATE_TEST_SUITE_P( + MaterializeArrayRuntimeIndex, + MaterializeAbstractNumericToConcreteType, + testing::Combine( + testing::Values(Expectation::kMaterialize), + testing::Values(Method::kRuntimeIndex), + testing::ValuesIn(std::vector{ + Types(0.0_a, 0.0), // + Types(1.0_a, 1.0), // + Types(-1.0_a, -1.0), // + Types(AFloat(f32::Highest()), static_cast(f32::Highest())), // + Types(AFloat(f32::Lowest()), static_cast(f32::Lowest())), // + Types(AFloat(kPiF32), kPiF64), // + Types(AFloat(kSubnormalF32), kSubnormalF32), // + Types(AFloat(-kSubnormalF32), -kSubnormalF32), // + }))); + INSTANTIATE_TEST_SUITE_P(MaterializeWorkgroupSize, MaterializeAbstractNumericToConcreteType, testing::Combine(testing::Values(Expectation::kMaterialize), @@ -625,14 +694,14 @@ INSTANTIATE_TEST_SUITE_P(InvalidConversion, testing::Combine(testing::Values(Expectation::kInvalidConversion), testing::ValuesIn(kScalarMethods), testing::ValuesIn(std::vector{ - Types(), // - Types(), // - Types(), // - Types(), // - Types(), // - Types(), // - Types(), // - Types(), // + Types(), // + Types(), // + Types(), // + Types(), // + Types(), // + Types(), // + Types(), // + Types(), // }))); INSTANTIATE_TEST_SUITE_P( @@ -895,6 +964,12 @@ constexpr Method kMatrixMethods[] = { Method::kVar, }; +/// Methods that support array materialization +constexpr Method kArrayMethods[] = { + Method::kLet, + Method::kVar, +}; + INSTANTIATE_TEST_SUITE_P( MaterializeScalar, MaterializeAbstractNumericToDefaultType, @@ -966,6 +1041,28 @@ INSTANTIATE_TEST_SUITE_P( Types(AInt(i32::Lowest()), i32::Lowest()), // }))); +INSTANTIATE_TEST_SUITE_P( + MaterializeArray, + MaterializeAbstractNumericToDefaultType, + testing::Combine( + testing::Values(Expectation::kMaterialize), + testing::ValuesIn(kArrayMethods), + testing::ValuesIn(std::vector{ + Types(0_a, 0.0), // + Types(1_a, 1.0), // + Types(-1_a, -1.0), // + Types(AInt(i32::Highest()), i32::Highest()), // + Types(AInt(i32::Lowest()), i32::Lowest()), // + Types(0.0_a, 0.0), // + Types(1.0_a, 1.0), // + Types(-1.0_a, -1.0), // + Types(AFloat(f32::Highest()), static_cast(f32::Highest())), // + Types(AFloat(f32::Lowest()), static_cast(f32::Lowest())), // + Types(AFloat(kPiF32), kPiF64), // + Types(AFloat(kSubnormalF32), kSubnormalF32), // + Types(AFloat(-kSubnormalF32), -kSubnormalF32), // + }))); + INSTANTIATE_TEST_SUITE_P( MaterializeArrayLength, MaterializeAbstractNumericToDefaultType, diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc index da6cfe29fa..3af15ab371 100644 --- a/src/tint/resolver/resolver.cc +++ b/src/tint/resolver/resolver.cc @@ -1316,7 +1316,9 @@ sem::Expression* Resolver::Expression(const ast::Expression* root) { return nullptr; } -const sem::Type* Resolver::ConcreteType(const sem::Type* ty, const sem::Type* target_ty) { +const sem::Type* Resolver::ConcreteType(const sem::Type* ty, + const sem::Type* target_ty, + const Source& source) { auto i32 = [&] { return builder_->create(); }; auto f32 = [&] { return builder_->create(); }; auto i32v = [&](uint32_t width) { return builder_->create(i32(), width); }; @@ -1342,6 +1344,16 @@ const sem::Type* Resolver::ConcreteType(const sem::Type* ty, const sem::Type* ta [&](const sem::AbstractFloat*) { return target_ty ? target_ty : f32m(m->columns(), m->rows()); }); + }, + [&](const sem::Array* a) -> const sem::Type* { + const sem::Type* target_el_ty = nullptr; + if (auto* target_arr_ty = As(target_ty)) { + target_el_ty = target_arr_ty->ElemType(); + } + if (auto* el_ty = ConcreteType(a->ElemType(), target_el_ty, source)) { + return Array(source, el_ty, a->Count(), /* explicit_stride */ 0); + } + return nullptr; }); } @@ -1354,7 +1366,7 @@ const sem::Expression* Resolver::Materialize(const sem::Expression* expr, auto* decl = expr->Declaration(); - auto* concrete_ty = ConcreteType(expr->Type(), target_type); + auto* concrete_ty = ConcreteType(expr->Type(), target_type, decl->source); if (!concrete_ty) { return expr; // Does not require materialization } @@ -1579,16 +1591,14 @@ sem::Call* Resolver::Call(const ast::CallExpression* expr) { auto* call_target = utils::GetOrCreate( array_ctors_, ArrayConstructorSig{{arr, args.Length(), args_stage}}, [&]() -> sem::TypeConstructor* { - utils::Vector params; - params.Reserve(args.Length()); - for (size_t i = 0; i < args.Length(); i++) { - params.Push(builder_->create( - nullptr, // declaration - static_cast(i), // index - arr->ElemType(), // type - ast::StorageClass::kNone, // storage_class - ast::Access::kUndefined)); // access - } + auto params = utils::Transform(args, [&](auto, size_t i) { + return builder_->create( + nullptr, // declaration + static_cast(i), // index + arr->ElemType(), // type + ast::StorageClass::kNone, // storage_class + ast::Access::kUndefined); + }); return builder_->create(arr, std::move(params), args_stage); }); @@ -1679,6 +1689,59 @@ sem::Call* Resolver::Call(const ast::CallExpression* expr) { } return nullptr; }, + [&](const ast::Array* a) -> sem::Call* { + Mark(a); + // array element type must be inferred if it was not specified. + auto el_count = static_cast(args.Length()); + const sem::Type* el_ty = nullptr; + if (a->type) { + el_ty = Type(a->type); + if (!el_ty) { + return nullptr; + } + if (!a->count) { + AddError("cannot construct a runtime-sized array", expr->source); + return nullptr; + } + if (auto count = ArrayCount(a->count)) { + el_count = count.Get(); + } else { + return nullptr; + } + // Note: validation later will detect any mismatches between explicit array + // size and number of constructor expressions. + } else { + auto arg_tys = + utils::Transform(args, [](auto* arg) { return arg->Type()->UnwrapRef(); }); + el_ty = sem::Type::Common(arg_tys); + if (!el_ty) { + AddError( + "cannot infer common array element type from constructor arguments", + expr->source); + std::unordered_set types; + for (size_t i = 0; i < args.Length(); i++) { + if (types.emplace(args[i]->Type()).second) { + AddNote("argument " + std::to_string(i) + " is of type '" + + sem_.TypeNameOf(args[i]->Type()) + "'", + args[i]->Declaration()->source); + } + } + return nullptr; + } + } + uint32_t explicit_stride = 0; + if (!ArrayAttributes(a->attributes, el_ty, explicit_stride)) { + return nullptr; + } + + auto* arr = Array(a->source, el_ty, el_count, explicit_stride); + if (!arr) { + return nullptr; + } + builder_->Sem().Add(a, arr); + + return ty_ctor_or_conv(arr); + }, [&](const ast::Type* ast) -> sem::Call* { // Handler for AST types that do not have an optional element type. if (auto* ty = Type(ast)) { diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h index 5d38fef74b..fa5a569cfc 100644 --- a/src/tint/resolver/resolver.h +++ b/src/tint/resolver/resolver.h @@ -233,9 +233,12 @@ class Resolver { /// @param ty the type that may hold abstract numeric types /// @param target_ty the target type for the expression (variable type, parameter type, etc). /// May be nullptr. + /// @param source the source of the expression requiring materialization /// @returns the concrete (materialized) type for the given type, or nullptr if the type is /// already concrete. - const sem::Type* ConcreteType(const sem::Type* ty, const sem::Type* target_ty); + const sem::Type* ConcreteType(const sem::Type* ty, + const sem::Type* target_ty, + const Source& source); // Statement resolving methods // Each return true on success, false on failure. diff --git a/src/tint/resolver/resolver_test_helper.h b/src/tint/resolver/resolver_test_helper.h index f56d82254e..efb215b05c 100644 --- a/src/tint/resolver/resolver_test_helper.h +++ b/src/tint/resolver/resolver_test_helper.h @@ -538,7 +538,10 @@ struct DataType> { /// @param b the ProgramBuilder /// @return a new AST array type static inline const ast::Type* AST(ProgramBuilder& b) { - return b.ty.array(DataType::AST(b), u32(N)); + if (auto* ast = DataType::AST(b)) { + return b.ty.array(ast, u32(N)); + } + return b.ty.array(nullptr, nullptr); } /// @param b the ProgramBuilder /// @return the semantic array type @@ -548,7 +551,7 @@ struct DataType> { /* element */ el, /* count */ N, /* align */ el->Align(), - /* size */ el->Size(), + /* size */ N * el->Size(), /* stride */ el->Align(), /* implicit_stride */ el->Align()); } diff --git a/src/tint/resolver/type_constructor_validation_test.cc b/src/tint/resolver/type_constructor_validation_test.cc index fcede05022..fe1973bc45 100644 --- a/src/tint/resolver/type_constructor_validation_test.cc +++ b/src/tint/resolver/type_constructor_validation_test.cc @@ -521,6 +521,145 @@ TEST_F(ResolverTypeConstructorValidationTest, Array_U32U32U32) { EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is()); } +TEST_F(ResolverTypeConstructorValidationTest, InferredArray_U32U32U32) { + // array(0u, 10u, 20u); + auto* tc = array(Source{{12, 34}}, nullptr, nullptr, 0_u, 10_u, 20_u); + WrapInFunction(tc); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* call = Sem().Get(tc); + ASSERT_NE(call, nullptr); + EXPECT_TRUE(call->Type()->Is()); + auto* ctor = call->Target()->As(); + ASSERT_NE(ctor, nullptr); + EXPECT_EQ(call->Type(), ctor->ReturnType()); + ASSERT_EQ(ctor->Parameters().Length(), 3u); + EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is()); + EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is()); + EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is()); +} + +TEST_F(ResolverTypeConstructorValidationTest, Array_U32AIU32) { + // array(0u, 10, 20u); + auto* tc = array(0_u, 10_a, 20_u); + WrapInFunction(tc); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* call = Sem().Get(tc); + ASSERT_NE(call, nullptr); + EXPECT_TRUE(call->Type()->Is()); + auto* ctor = call->Target()->As(); + ASSERT_NE(ctor, nullptr); + EXPECT_EQ(call->Type(), ctor->ReturnType()); + ASSERT_EQ(ctor->Parameters().Length(), 3u); + EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is()); + EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is()); + EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is()); +} + +TEST_F(ResolverTypeConstructorValidationTest, InferredArray_U32AIU32) { + // array(0u, 10u, 20u); + auto* tc = array(Source{{12, 34}}, nullptr, nullptr, 0_u, 10_a, 20_u); + WrapInFunction(tc); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* call = Sem().Get(tc); + ASSERT_NE(call, nullptr); + EXPECT_TRUE(call->Type()->Is()); + auto* ctor = call->Target()->As(); + ASSERT_NE(ctor, nullptr); + EXPECT_EQ(call->Type(), ctor->ReturnType()); + ASSERT_EQ(ctor->Parameters().Length(), 3u); + EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is()); + EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is()); + EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is()); +} + +TEST_F(ResolverTypeConstructorValidationTest, ArrayU32_AIAIAI) { + // array(0, 10, 20); + auto* tc = array(0_a, 10_a, 20_a); + WrapInFunction(tc); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* call = Sem().Get(tc); + ASSERT_NE(call, nullptr); + EXPECT_TRUE(call->Type()->Is()); + auto* ctor = call->Target()->As(); + ASSERT_NE(ctor, nullptr); + EXPECT_EQ(call->Type(), ctor->ReturnType()); + ASSERT_EQ(ctor->Parameters().Length(), 3u); + EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is()); + EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is()); + EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is()); +} + +TEST_F(ResolverTypeConstructorValidationTest, InferredArray_AIAIAI) { + // const c = array(0, 10, 20); + auto* tc = array(Source{{12, 34}}, nullptr, nullptr, 0_a, 10_a, 20_a); + WrapInFunction(Decl(Const("C", nullptr, tc))); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* call = Sem().Get(tc); + ASSERT_NE(call, nullptr); + EXPECT_TRUE(call->Type()->Is()); + auto* ctor = call->Target()->As(); + ASSERT_NE(ctor, nullptr); + EXPECT_EQ(call->Type(), ctor->ReturnType()); + ASSERT_EQ(ctor->Parameters().Length(), 3u); + EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is()); + EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is()); + EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is()); +} + +TEST_F(ResolverTypeConstructorValidationTest, InferredArrayU32_VecI32_VecAI) { + // array(vec2(10i), vec2(20)); + auto* tc = array(Source{{12, 34}}, nullptr, nullptr, // + Construct(ty.vec(nullptr, 2), 20_i), // + Construct(ty.vec(nullptr, 2), 20_a)); + WrapInFunction(tc); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* call = Sem().Get(tc); + ASSERT_NE(call, nullptr); + EXPECT_TRUE(call->Type()->Is()); + auto* ctor = call->Target()->As(); + ASSERT_NE(ctor, nullptr); + EXPECT_EQ(call->Type(), ctor->ReturnType()); + ASSERT_EQ(ctor->Parameters().Length(), 2u); + ASSERT_TRUE(ctor->Parameters()[0]->Type()->Is()); + EXPECT_TRUE(ctor->Parameters()[0]->Type()->As()->type()->Is()); + ASSERT_TRUE(ctor->Parameters()[1]->Type()->Is()); + EXPECT_TRUE(ctor->Parameters()[1]->Type()->As()->type()->Is()); +} + +TEST_F(ResolverTypeConstructorValidationTest, InferredArrayU32_VecAI_VecF32) { + // array(vec2(20), vec2(10f)); + auto* tc = array(Source{{12, 34}}, nullptr, nullptr, // + Construct(ty.vec(nullptr, 2), 20_a), // + Construct(ty.vec(nullptr, 2), 20_f)); + WrapInFunction(tc); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* call = Sem().Get(tc); + ASSERT_NE(call, nullptr); + EXPECT_TRUE(call->Type()->Is()); + auto* ctor = call->Target()->As(); + ASSERT_NE(ctor, nullptr); + EXPECT_EQ(call->Type(), ctor->ReturnType()); + ASSERT_EQ(ctor->Parameters().Length(), 2u); + ASSERT_TRUE(ctor->Parameters()[0]->Type()->Is()); + EXPECT_TRUE(ctor->Parameters()[0]->Type()->As()->type()->Is()); + ASSERT_TRUE(ctor->Parameters()[1]->Type()->Is()); + EXPECT_TRUE(ctor->Parameters()[1]->Type()->As()->type()->Is()); +} + TEST_F(ResolverTypeConstructorValidationTest, ArrayArgumentTypeMismatch_U32F32) { // array(0u, 1.0f, 20u); auto* tc = array(0_u, Expr(Source{{12, 34}}, 1_f), 20_u); @@ -530,6 +669,18 @@ TEST_F(ResolverTypeConstructorValidationTest, ArrayArgumentTypeMismatch_U32F32) EXPECT_EQ(r()->error(), R"(12:34 error: 'f32' cannot be used to construct an array of 'u32')"); } +TEST_F(ResolverTypeConstructorValidationTest, InferredArrayArgumentTypeMismatch_U32F32) { + // array(0u, 1.0f, 20u); + auto* tc = array(Source{{12, 34}}, nullptr, nullptr, 0_u, 1_f, 20_u); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + R"(12:34 error: cannot infer common array element type from constructor arguments +note: argument 0 is of type 'u32' +note: argument 1 is of type 'f32')"); +} + TEST_F(ResolverTypeConstructorValidationTest, ArrayArgumentTypeMismatch_F32I32) { // array(1i); auto* tc = array(Expr(Source{{12, 34}}, 1_i)); @@ -539,6 +690,18 @@ TEST_F(ResolverTypeConstructorValidationTest, ArrayArgumentTypeMismatch_F32I32) EXPECT_EQ(r()->error(), R"(12:34 error: 'i32' cannot be used to construct an array of 'f32')"); } +TEST_F(ResolverTypeConstructorValidationTest, InferredArrayArgumentTypeMismatch_F32I32) { + // array(1f, 1i); + auto* tc = array(Source{{12, 34}}, nullptr, nullptr, 1_f, 1_i); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + R"(12:34 error: cannot infer common array element type from constructor arguments +note: argument 0 is of type 'f32' +note: argument 1 is of type 'i32')"); +} + TEST_F(ResolverTypeConstructorValidationTest, ArrayArgumentTypeMismatch_U32I32) { // array(1i, 0u, 0u, 0u, 0u, 0u); auto* tc = array(Expr(Source{{12, 34}}, 1_i), 0_u, 0_u, 0_u, 0_u); @@ -548,6 +711,18 @@ TEST_F(ResolverTypeConstructorValidationTest, ArrayArgumentTypeMismatch_U32I32) EXPECT_EQ(r()->error(), R"(12:34 error: 'i32' cannot be used to construct an array of 'u32')"); } +TEST_F(ResolverTypeConstructorValidationTest, InferredArrayArgumentTypeMismatch_U32I32) { + // array(1i, 0u, 0u, 0u, 0u, 0u); + auto* tc = array(Source{{12, 34}}, nullptr, nullptr, 1_i, 0_u, 0_u, 0_u, 0_u); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + R"(12:34 error: cannot infer common array element type from constructor arguments +note: argument 0 is of type 'i32' +note: argument 1 is of type 'u32')"); +} + TEST_F(ResolverTypeConstructorValidationTest, ArrayArgumentTypeMismatch_I32Vec2) { // array(1i, vec2()); auto* tc = array(1_i, vec2(Source{{12, 34}})); @@ -557,6 +732,17 @@ TEST_F(ResolverTypeConstructorValidationTest, ArrayArgumentTypeMismatch_I32Vec2) R"(12:34 error: 'vec2' cannot be used to construct an array of 'i32')"); } +TEST_F(ResolverTypeConstructorValidationTest, InferredArrayArgumentTypeMismatch_I32Vec2) { + // array(1i, vec2()); + auto* tc = array(Source{{12, 34}}, nullptr, nullptr, 1_i, vec2()); + WrapInFunction(tc); + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + R"(12:34 error: cannot infer common array element type from constructor arguments +note: argument 0 is of type 'i32' +note: argument 1 is of type 'vec2')"); +} + TEST_F(ResolverTypeConstructorValidationTest, ArrayArgumentTypeMismatch_Vec3i32_Vec3u32) { // array, 2u>(vec3(), vec3()); auto* t = array(ty.vec3(), 2_u, vec3(Source{{12, 34}}), vec3()); @@ -567,6 +753,31 @@ TEST_F(ResolverTypeConstructorValidationTest, ArrayArgumentTypeMismatch_Vec3i32_ R"(12:34 error: 'vec3' cannot be used to construct an array of 'vec3')"); } +TEST_F(ResolverTypeConstructorValidationTest, InferredArrayArgumentTypeMismatch_Vec3i32_Vec3u32) { + // array(vec3(), vec3()); + auto* t = array(Source{{12, 34}}, nullptr, nullptr, vec3(), vec3()); + WrapInFunction(t); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + R"(12:34 error: cannot infer common array element type from constructor arguments +note: argument 0 is of type 'vec3' +note: argument 1 is of type 'vec3')"); +} + +TEST_F(ResolverTypeConstructorValidationTest, InferredArrayArgumentTypeMismatch_Vec3i32_Vec3AF) { + // array(vec3(), vec3(1.0)); + auto* t = + array(Source{{12, 34}}, nullptr, nullptr, vec3(), Construct(ty.vec3(nullptr), 1._a)); + WrapInFunction(t); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + R"(12:34 error: cannot infer common array element type from constructor arguments +note: argument 0 is of type 'vec3' +note: argument 1 is of type 'vec3')"); +} + TEST_F(ResolverTypeConstructorValidationTest, ArrayArgumentTypeMismatch_Vec3i32_Vec3bool) { // array, 2u>(vec3(), vec3()); auto* t = array(ty.vec3(), 2_u, vec3(), vec3()); @@ -577,6 +788,18 @@ TEST_F(ResolverTypeConstructorValidationTest, ArrayArgumentTypeMismatch_Vec3i32_ R"(error: 'vec3' cannot be used to construct an array of 'vec3')"); } +TEST_F(ResolverTypeConstructorValidationTest, InferredArrayArgumentTypeMismatch_Vec3i32_Vec3bool) { + // array(vec3(), vec3()); + auto* t = array(Source{{12, 34}}, nullptr, nullptr, vec3(), vec3()); + WrapInFunction(t); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + R"(12:34 error: cannot infer common array element type from constructor arguments +note: argument 0 is of type 'vec3' +note: argument 1 is of type 'vec3')"); +} + TEST_F(ResolverTypeConstructorValidationTest, ArrayOfArray_SubElemSizeMismatch) { // array, 2u>(array(), array()); auto* t = array(Source{{12, 34}}, ty.array(), 2_i, array(), array()); @@ -587,6 +810,18 @@ TEST_F(ResolverTypeConstructorValidationTest, ArrayOfArray_SubElemSizeMismatch) R"(error: 'array' cannot be used to construct an array of 'array')"); } +TEST_F(ResolverTypeConstructorValidationTest, InferredArrayOfArray_SubElemSizeMismatch) { + // array, 2u>(array(), array()); + auto* t = array(Source{{12, 34}}, nullptr, nullptr, array(), array()); + WrapInFunction(t); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + R"(12:34 error: cannot infer common array element type from constructor arguments +note: argument 0 is of type 'array' +note: argument 1 is of type 'array')"); +} + TEST_F(ResolverTypeConstructorValidationTest, ArrayOfArray_SubElemTypeMismatch) { // array, 2u>(array(), array()); auto* t = array(Source{{12, 34}}, ty.array(), 2_i, array(), array()); @@ -597,6 +832,18 @@ TEST_F(ResolverTypeConstructorValidationTest, ArrayOfArray_SubElemTypeMismatch) R"(error: 'array' cannot be used to construct an array of 'array')"); } +TEST_F(ResolverTypeConstructorValidationTest, InferredArrayOfArray_SubElemTypeMismatch) { + // array, 2u>(array(), array()); + auto* t = array(Source{{12, 34}}, nullptr, nullptr, array(), array()); + WrapInFunction(t); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + R"(12:34 error: cannot infer common array element type from constructor arguments +note: argument 0 is of type 'array' +note: argument 1 is of type 'array')"); +} + TEST_F(ResolverTypeConstructorValidationTest, Array_TooFewElements) { // array(1i, 2i, 3i); SetSource(Source::Location({12, 34})); diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc index c7df529ad1..ec858c4f48 100644 --- a/src/tint/resolver/validator.cc +++ b/src/tint/resolver/validator.cc @@ -1867,7 +1867,7 @@ bool Validator::ArrayConstructor(const ast::CallExpression* ctor, auto* elem_ty = array_type->ElemType(); for (auto* value : values) { auto* value_ty = sem_.TypeOf(value)->UnwrapRef(); - if (value_ty != elem_ty) { + if (sem::Type::ConversionRank(value_ty, elem_ty) == sem::Type::kNoConversion) { AddError("'" + sem_.TypeNameOf(value_ty) + "' cannot be used to construct an array of '" + sem_.TypeNameOf(elem_ty) + "'", diff --git a/src/tint/sem/abstract_numeric.cc b/src/tint/sem/abstract_numeric.cc index 6481a433ae..e7d184652d 100644 --- a/src/tint/sem/abstract_numeric.cc +++ b/src/tint/sem/abstract_numeric.cc @@ -31,7 +31,7 @@ uint32_t AbstractNumeric::Align() const { } bool AbstractNumeric::IsConstructible() const { - return false; + return true; } } // namespace tint::sem diff --git a/src/tint/sem/abstract_numeric.h b/src/tint/sem/abstract_numeric.h index 0b38448e05..620c5281e2 100644 --- a/src/tint/sem/abstract_numeric.h +++ b/src/tint/sem/abstract_numeric.h @@ -38,7 +38,7 @@ class AbstractNumeric : public Castable { /// @returns 0, as the type is abstract. uint32_t Align() const override; - /// @returns 0, as the type is abstract. + /// @returns true. bool IsConstructible() const override; };