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<array<N, T>> - 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 <bclayton@chromium.org>
Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
Ben Clayton 2022-08-01 17:36:54 +00:00 committed by Dawn LUCI CQ
parent 70a43fa5b9
commit 4a65620716
8 changed files with 440 additions and 27 deletions

View File

@ -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<double>(3.5e+38);
// constexpr double kTooBigF16 = static_cast<double>(6.6e+4);
@ -111,6 +115,14 @@ class MaterializeTest : public resolver::ResolverTestWithParam<CASE> {
}
}
},
[&](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<T>(el->Value()), expected_value);
}
},
[&](Default) { EXPECT_EQ(std::get<T>(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<u32, AInt>(AInt(u32::Lowest()), u32::Lowest()), //
})));
INSTANTIATE_TEST_SUITE_P(
MaterializeArray,
MaterializeAbstractNumericToConcreteType,
testing::Combine(
testing::Values(Expectation::kMaterialize),
testing::ValuesIn(kArrayMethods),
testing::ValuesIn(std::vector<Data>{
Types<i32A, AIntA>(0_a, 0.0), //
Types<i32A, AIntA>(1_a, 1.0), //
Types<i32A, AIntA>(-1_a, -1.0), //
Types<i32A, AIntA>(AInt(i32::Highest()), i32::Highest()), //
Types<i32A, AIntA>(AInt(i32::Lowest()), i32::Lowest()), //
Types<u32A, AIntA>(0_a, 0.0), //
Types<u32A, AIntA>(1_a, 1.0), //
Types<u32A, AIntA>(AInt(u32::Highest()), u32::Highest()), //
Types<u32A, AIntA>(AInt(u32::Lowest()), u32::Lowest()), //
Types<f32A, AFloatA>(0.0_a, 0.0), //
Types<f32A, AFloatA>(1.0_a, 1.0), //
Types<f32A, AFloatA>(-1.0_a, -1.0), //
Types<f32A, AFloatA>(AFloat(f32::Highest()), static_cast<double>(f32::Highest())), //
Types<f32A, AFloatA>(AFloat(f32::Lowest()), static_cast<double>(f32::Lowest())), //
Types<f32A, AFloatA>(AFloat(kPiF32), kPiF64), //
Types<f32A, AFloatA>(AFloat(kSubnormalF32), kSubnormalF32), //
Types<f32A, AFloatA>(AFloat(-kSubnormalF32), -kSubnormalF32), //
/* Types<f16A, AFloatA>(0.0_a, 0.0), */ //
/* Types<f16A, AFloatA>(1.0_a, 1.0), */ //
/* Types<f16A, AFloatA>(-1.0_a, -1.0), */ //
/* Types<f16A, AFloatA>(AFloat(kHighestF16), kHighestF16), */ //
/* Types<f16A, AFloatA>(AFloat(kLowestF16), kLowestF16), */ //
/* Types<f16A, AFloatA>(AFloat(kPiF16), kPiF64), */ //
/* Types<f16A, AFloatA>(AFloat(kSubnormalF16), kSubnormalF16), */ //
/* Types<f16A, AFloatA>(AFloat(-kSubnormalF16), -kSubnormalF16), */ //
})));
INSTANTIATE_TEST_SUITE_P(
MaterializeArrayRuntimeIndex,
MaterializeAbstractNumericToConcreteType,
testing::Combine(
testing::Values(Expectation::kMaterialize),
testing::Values(Method::kRuntimeIndex),
testing::ValuesIn(std::vector<Data>{
Types<f32A, AFloatA>(0.0_a, 0.0), //
Types<f32A, AFloatA>(1.0_a, 1.0), //
Types<f32A, AFloatA>(-1.0_a, -1.0), //
Types<f32A, AFloatA>(AFloat(f32::Highest()), static_cast<double>(f32::Highest())), //
Types<f32A, AFloatA>(AFloat(f32::Lowest()), static_cast<double>(f32::Lowest())), //
Types<f32A, AFloatA>(AFloat(kPiF32), kPiF64), //
Types<f32A, AFloatA>(AFloat(kSubnormalF32), kSubnormalF32), //
Types<f32A, AFloatA>(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<Data>{
Types<i32, AFloat>(), //
Types<u32, AFloat>(), //
Types<i32V, AFloatV>(), //
Types<u32V, AFloatV>(), //
Types<i32Varr, AInt>(), //
Types<i32Varr, AIntV>(), //
Types<i32Varr, AFloat>(), //
Types<i32Varr, AFloatV>(), //
Types<i32, AFloat>(), //
Types<u32, AFloat>(), //
Types<i32V, AFloatV>(), //
Types<u32V, AFloatV>(), //
Types<i32A, AInt>(), //
Types<i32A, AIntV>(), //
Types<i32A, AFloat>(), //
Types<i32A, AFloatV>(), //
})));
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<i32, AInt>(AInt(i32::Lowest()), i32::Lowest()), //
})));
INSTANTIATE_TEST_SUITE_P(
MaterializeArray,
MaterializeAbstractNumericToDefaultType,
testing::Combine(
testing::Values(Expectation::kMaterialize),
testing::ValuesIn(kArrayMethods),
testing::ValuesIn(std::vector<Data>{
Types<i32A, AIntA>(0_a, 0.0), //
Types<i32A, AIntA>(1_a, 1.0), //
Types<i32A, AIntA>(-1_a, -1.0), //
Types<i32A, AIntA>(AInt(i32::Highest()), i32::Highest()), //
Types<i32A, AIntA>(AInt(i32::Lowest()), i32::Lowest()), //
Types<f32A, AFloatA>(0.0_a, 0.0), //
Types<f32A, AFloatA>(1.0_a, 1.0), //
Types<f32A, AFloatA>(-1.0_a, -1.0), //
Types<f32A, AFloatA>(AFloat(f32::Highest()), static_cast<double>(f32::Highest())), //
Types<f32A, AFloatA>(AFloat(f32::Lowest()), static_cast<double>(f32::Lowest())), //
Types<f32A, AFloatA>(AFloat(kPiF32), kPiF64), //
Types<f32A, AFloatA>(AFloat(kSubnormalF32), kSubnormalF32), //
Types<f32A, AFloatA>(AFloat(-kSubnormalF32), -kSubnormalF32), //
})));
INSTANTIATE_TEST_SUITE_P(
MaterializeArrayLength,
MaterializeAbstractNumericToDefaultType,

View File

@ -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<sem::I32>(); };
auto f32 = [&] { return builder_->create<sem::F32>(); };
auto i32v = [&](uint32_t width) { return builder_->create<sem::Vector>(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<sem::Array>(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<const sem::Parameter*, 8> params;
params.Reserve(args.Length());
for (size_t i = 0; i < args.Length(); i++) {
params.Push(builder_->create<sem::Parameter>(
nullptr, // declaration
static_cast<uint32_t>(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<sem::Parameter>(
nullptr, // declaration
static_cast<uint32_t>(i), // index
arr->ElemType(), // type
ast::StorageClass::kNone, // storage_class
ast::Access::kUndefined);
});
return builder_->create<sem::TypeConstructor>(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<uint32_t>(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<const sem::Type*> 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)) {

View File

@ -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.

View File

@ -538,7 +538,10 @@ struct DataType<array<N, T>> {
/// @param b the ProgramBuilder
/// @return a new AST array type
static inline const ast::Type* AST(ProgramBuilder& b) {
return b.ty.array(DataType<T>::AST(b), u32(N));
if (auto* ast = DataType<T>::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<array<N, T>> {
/* element */ el,
/* count */ N,
/* align */ el->Align(),
/* size */ el->Size(),
/* size */ N * el->Size(),
/* stride */ el->Align(),
/* implicit_stride */ el->Align());
}

View File

@ -521,6 +521,145 @@ TEST_F(ResolverTypeConstructorValidationTest, Array_U32U32U32) {
EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::U32>());
}
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<sem::Call>(tc);
ASSERT_NE(call, nullptr);
EXPECT_TRUE(call->Type()->Is<sem::Array>());
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
ASSERT_EQ(ctor->Parameters().Length(), 3u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::U32>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::U32>());
EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::U32>());
}
TEST_F(ResolverTypeConstructorValidationTest, Array_U32AIU32) {
// array<u32, 3u>(0u, 10, 20u);
auto* tc = array<u32, 3>(0_u, 10_a, 20_u);
WrapInFunction(tc);
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* call = Sem().Get<sem::Call>(tc);
ASSERT_NE(call, nullptr);
EXPECT_TRUE(call->Type()->Is<sem::Array>());
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
ASSERT_EQ(ctor->Parameters().Length(), 3u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::U32>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::U32>());
EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::U32>());
}
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<sem::Call>(tc);
ASSERT_NE(call, nullptr);
EXPECT_TRUE(call->Type()->Is<sem::Array>());
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
ASSERT_EQ(ctor->Parameters().Length(), 3u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::U32>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::U32>());
EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::U32>());
}
TEST_F(ResolverTypeConstructorValidationTest, ArrayU32_AIAIAI) {
// array<u32, 3u>(0, 10, 20);
auto* tc = array<u32, 3>(0_a, 10_a, 20_a);
WrapInFunction(tc);
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* call = Sem().Get<sem::Call>(tc);
ASSERT_NE(call, nullptr);
EXPECT_TRUE(call->Type()->Is<sem::Array>());
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
ASSERT_EQ(ctor->Parameters().Length(), 3u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::U32>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::U32>());
EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::U32>());
}
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<sem::Call>(tc);
ASSERT_NE(call, nullptr);
EXPECT_TRUE(call->Type()->Is<sem::Array>());
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
ASSERT_EQ(ctor->Parameters().Length(), 3u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::AbstractInt>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::AbstractInt>());
EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::AbstractInt>());
}
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<sem::Call>(tc);
ASSERT_NE(call, nullptr);
EXPECT_TRUE(call->Type()->Is<sem::Array>());
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
ASSERT_EQ(ctor->Parameters().Length(), 2u);
ASSERT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Vector>());
EXPECT_TRUE(ctor->Parameters()[0]->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
ASSERT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::Vector>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
}
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<sem::Call>(tc);
ASSERT_NE(call, nullptr);
EXPECT_TRUE(call->Type()->Is<sem::Array>());
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
ASSERT_EQ(ctor->Parameters().Length(), 2u);
ASSERT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Vector>());
EXPECT_TRUE(ctor->Parameters()[0]->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
ASSERT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::Vector>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
}
TEST_F(ResolverTypeConstructorValidationTest, ArrayArgumentTypeMismatch_U32F32) {
// array<u32, 3u>(0u, 1.0f, 20u);
auto* tc = array<u32, 3>(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<f32, 1u>(1i);
auto* tc = array<f32, 1>(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<u32, 1u>(1i, 0u, 0u, 0u, 0u, 0u);
auto* tc = array<u32, 1>(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<i32, 3u>(1i, vec2<i32>());
auto* tc = array<i32, 3>(1_i, vec2<i32>(Source{{12, 34}}));
@ -557,6 +732,17 @@ TEST_F(ResolverTypeConstructorValidationTest, ArrayArgumentTypeMismatch_I32Vec2)
R"(12:34 error: 'vec2<i32>' cannot be used to construct an array of 'i32')");
}
TEST_F(ResolverTypeConstructorValidationTest, InferredArrayArgumentTypeMismatch_I32Vec2) {
// array(1i, vec2<i32>());
auto* tc = array(Source{{12, 34}}, nullptr, nullptr, 1_i, vec2<i32>());
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<i32>')");
}
TEST_F(ResolverTypeConstructorValidationTest, ArrayArgumentTypeMismatch_Vec3i32_Vec3u32) {
// array<vec3<i32>, 2u>(vec3<u32>(), vec3<u32>());
auto* t = array(ty.vec3<i32>(), 2_u, vec3<u32>(Source{{12, 34}}), vec3<u32>());
@ -567,6 +753,31 @@ TEST_F(ResolverTypeConstructorValidationTest, ArrayArgumentTypeMismatch_Vec3i32_
R"(12:34 error: 'vec3<u32>' cannot be used to construct an array of 'vec3<i32>')");
}
TEST_F(ResolverTypeConstructorValidationTest, InferredArrayArgumentTypeMismatch_Vec3i32_Vec3u32) {
// array(vec3<i32>(), vec3<u32>());
auto* t = array(Source{{12, 34}}, nullptr, nullptr, vec3<i32>(), vec3<u32>());
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<i32>'
note: argument 1 is of type 'vec3<u32>')");
}
TEST_F(ResolverTypeConstructorValidationTest, InferredArrayArgumentTypeMismatch_Vec3i32_Vec3AF) {
// array(vec3<i32>(), vec3(1.0));
auto* t =
array(Source{{12, 34}}, nullptr, nullptr, vec3<i32>(), 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<i32>'
note: argument 1 is of type 'vec3<abstract-float>')");
}
TEST_F(ResolverTypeConstructorValidationTest, ArrayArgumentTypeMismatch_Vec3i32_Vec3bool) {
// array<vec3<i32>, 2u>(vec3<i32>(), vec3<bool>());
auto* t = array(ty.vec3<i32>(), 2_u, vec3<i32>(), vec3<bool>());
@ -577,6 +788,18 @@ TEST_F(ResolverTypeConstructorValidationTest, ArrayArgumentTypeMismatch_Vec3i32_
R"(error: 'vec3<bool>' cannot be used to construct an array of 'vec3<i32>')");
}
TEST_F(ResolverTypeConstructorValidationTest, InferredArrayArgumentTypeMismatch_Vec3i32_Vec3bool) {
// array(vec3<i32>(), vec3<bool>());
auto* t = array(Source{{12, 34}}, nullptr, nullptr, vec3<i32>(), vec3<bool>());
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<i32>'
note: argument 1 is of type 'vec3<bool>')");
}
TEST_F(ResolverTypeConstructorValidationTest, ArrayOfArray_SubElemSizeMismatch) {
// array<array<i32, 2u>, 2u>(array<i32, 3u>(), array<i32, 2u>());
auto* t = array(Source{{12, 34}}, ty.array<i32, 2>(), 2_i, array<i32, 3>(), array<i32, 2>());
@ -587,6 +810,18 @@ TEST_F(ResolverTypeConstructorValidationTest, ArrayOfArray_SubElemSizeMismatch)
R"(error: 'array<i32, 3>' cannot be used to construct an array of 'array<i32, 2>')");
}
TEST_F(ResolverTypeConstructorValidationTest, InferredArrayOfArray_SubElemSizeMismatch) {
// array<array<i32, 2u>, 2u>(array<i32, 3u>(), array<i32, 2u>());
auto* t = array(Source{{12, 34}}, nullptr, nullptr, array<i32, 3>(), array<i32, 2>());
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<i32, 3>'
note: argument 1 is of type 'array<i32, 2>')");
}
TEST_F(ResolverTypeConstructorValidationTest, ArrayOfArray_SubElemTypeMismatch) {
// array<array<i32, 2u>, 2u>(array<i32, 2u>(), array<u32, 2u>());
auto* t = array(Source{{12, 34}}, ty.array<i32, 2>(), 2_i, array<i32, 2>(), array<u32, 2>());
@ -597,6 +832,18 @@ TEST_F(ResolverTypeConstructorValidationTest, ArrayOfArray_SubElemTypeMismatch)
R"(error: 'array<u32, 2>' cannot be used to construct an array of 'array<i32, 2>')");
}
TEST_F(ResolverTypeConstructorValidationTest, InferredArrayOfArray_SubElemTypeMismatch) {
// array<array<i32, 2u>, 2u>(array<i32, 2u>(), array<u32, 2u>());
auto* t = array(Source{{12, 34}}, nullptr, nullptr, array<i32, 2>(), array<u32, 2>());
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<i32, 2>'
note: argument 1 is of type 'array<u32, 2>')");
}
TEST_F(ResolverTypeConstructorValidationTest, Array_TooFewElements) {
// array<i32, 4u>(1i, 2i, 3i);
SetSource(Source::Location({12, 34}));

View File

@ -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) +
"'",

View File

@ -31,7 +31,7 @@ uint32_t AbstractNumeric::Align() const {
}
bool AbstractNumeric::IsConstructible() const {
return false;
return true;
}
} // namespace tint::sem

View File

@ -38,7 +38,7 @@ class AbstractNumeric : public Castable<AbstractNumeric, Type> {
/// @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;
};