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:
parent
70a43fa5b9
commit
4a65620716
|
@ -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,
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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}));
|
||||
|
|
|
@ -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) +
|
||||
"'",
|
||||
|
|
|
@ -31,7 +31,7 @@ uint32_t AbstractNumeric::Align() const {
|
|||
}
|
||||
|
||||
bool AbstractNumeric::IsConstructible() const {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace tint::sem
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue