[validation] validate array constructors

Bug: tint:634
Change-Id: I873e510d3645886c878b686c1272ec2205e2bd70
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/54260
Reviewed-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Sarah Mashayekhi <sarahmashay@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Sarah Mashayekhi <sarahmashay@google.com>
This commit is contained in:
Sarah 2021-06-11 15:51:26 +00:00 committed by Tint LUCI CQ
parent aff8f8f26a
commit 5a4dc9a02f
4 changed files with 265 additions and 19 deletions

View File

@ -1876,7 +1876,9 @@ bool Resolver::Constructor(ast::ConstructorExpression* expr) {
if (auto* mat_type = type->As<sem::Matrix>()) {
return ValidateMatrixConstructor(type_ctor, mat_type);
}
// TODO(crbug.com/tint/634): Validate array constructor
if (auto* arr_type = type->As<sem::Array>()) {
return ValidateArrayConstructor(type_ctor, arr_type);
}
} else if (auto* scalar_ctor = expr->As<ast::ScalarConstructorExpression>()) {
Mark(scalar_ctor->literal());
auto* type = TypeOf(scalar_ctor->literal());
@ -1890,6 +1892,46 @@ bool Resolver::Constructor(ast::ConstructorExpression* expr) {
return true;
}
bool Resolver::ValidateArrayConstructor(
const ast::TypeConstructorExpression* ctor,
const sem::Array* array_type) {
auto& values = ctor->values();
auto* elem_type = array_type->ElemType();
for (auto* value : values) {
auto* value_type = TypeOf(value)->UnwrapRef();
if (value_type != elem_type) {
diagnostics_.add_error(
"type in array constructor does not match array type: "
"expected '" +
elem_type->FriendlyName(builder_->Symbols()) + "', found '" +
TypeNameOf(value) + "'",
value->source());
return false;
}
}
if (array_type->IsRuntimeSized()) {
diagnostics_.add_error("cannot init a runtime-sized array", ctor->source());
return false;
} else if (!values.empty() && (values.size() != array_type->Count())) {
std::string fm = values.size() < array_type->Count() ? "few" : "many";
diagnostics_.add_error("array constructor has too " + fm +
" elements: expected " +
std::to_string(array_type->Count()) +
", found " + std::to_string(values.size()),
ctor->source());
return false;
} else if (values.size() > array_type->Count()) {
diagnostics_.add_error(
"array constructor has too many elements: expected " +
std::to_string(array_type->Count()) + ", found " +
std::to_string(values.size()),
ctor->source());
return false;
}
return true;
}
bool Resolver::ValidateVectorConstructor(
const ast::TypeConstructorExpression* ctor,
const sem::Vector* vec_type) {

View File

@ -275,6 +275,8 @@ class Resolver {
const std::string& rhs_type_name);
bool ValidateVectorConstructor(const ast::TypeConstructorExpression* ctor,
const sem::Vector* vec_type);
bool ValidateArrayConstructor(const ast::TypeConstructorExpression* ctor,
const sem::Array* arr_type);
bool ValidateTypeDecl(const ast::TypeDecl* named_type) const;
bool ValidateNoDuplicateDecorations(const ast::DecorationList& decorations);

View File

@ -728,6 +728,208 @@ TEST_F(ResolverValidationTest, OffsetAndAlignAndSizeDecoration) {
"decorations");
}
TEST_F(ResolverValidationTest, Expr_Constructor_Array_ZeroValue_Pass) {
// array<u32, 10>();
auto* tc = array<u32, 10>();
WrapInFunction(tc);
EXPECT_TRUE(r()->Resolve());
}
TEST_F(ResolverValidationTest, Expr_Constructor_Array_type_match) {
// array<u32, 3>(0u, 10u. 20u);
auto* tc =
array<u32, 3>(create<ast::ScalarConstructorExpression>(Literal(0u)),
create<ast::ScalarConstructorExpression>(Literal(10u)),
create<ast::ScalarConstructorExpression>(Literal(20u)));
WrapInFunction(tc);
EXPECT_TRUE(r()->Resolve());
}
TEST_F(ResolverValidationTest, Expr_Constructor_Array_type_Mismatch_U32F32) {
// array<u32, 3>(0u, 1.0f, 20u);
auto* tc = array<u32, 3>(
create<ast::ScalarConstructorExpression>(Literal(0u)),
create<ast::ScalarConstructorExpression>(Source{{12, 34}}, Literal(1.0f)),
create<ast::ScalarConstructorExpression>(Literal(20u)));
WrapInFunction(tc);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: type in array constructor does not match array type: "
"expected 'u32', found 'f32'");
}
TEST_F(ResolverValidationTest,
Expr_Constructor_Array_ScalarArgumentTypeMismatch_F32I32) {
// array<f32, 1>(1);
auto* tc = array<f32, 1>(
create<ast::ScalarConstructorExpression>(Source{{12, 34}}, Literal(1)));
WrapInFunction(tc);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: type in array constructor does not match array type: "
"expected 'f32', found 'i32'");
}
TEST_F(ResolverValidationTest,
Expr_Constructor_Array_ScalarArgumentTypeMismatch_U32I32) {
// array<u32, 6>(1, 0u, 0u, 0u, 0u, 0u);
auto* tc = array<u32, 1>(
create<ast::ScalarConstructorExpression>(Source{{12, 34}}, Literal(1)),
create<ast::ScalarConstructorExpression>(Literal(0u)),
create<ast::ScalarConstructorExpression>(Literal(0u)),
create<ast::ScalarConstructorExpression>(Literal(0u)),
create<ast::ScalarConstructorExpression>(Literal(0u)));
WrapInFunction(tc);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: type in array constructor does not match array type: "
"expected 'u32', found 'i32'");
}
TEST_F(ResolverValidationTest,
Expr_Constructor_Array_ScalarArgumentTypeMismatch_Vec2) {
// array<i32, 3>(1, vec2<i32>());
auto* tc = array<i32, 3>(create<ast::ScalarConstructorExpression>(Literal(1)),
create<ast::TypeConstructorExpression>(
Source{{12, 34}}, ty.vec2<i32>(), ExprList()));
WrapInFunction(tc);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: type in array constructor does not match array type: "
"expected 'i32', found 'vec2<i32>'");
}
TEST_F(ResolverValidationTest,
Expr_Constructor_ArrayOfVector_SubElemTypeMismatch_I32U32) {
// array<vec3<i32>, 2>(vec3<i32>(), vec3<u32>());
auto* e0 = vec3<i32>();
SetSource(Source::Location({12, 34}));
auto* e1 = vec3<u32>();
auto* t = Construct(ty.array(ty.vec3<i32>(), 2), e0, e1);
WrapInFunction(t);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: type in array constructor does not match array type: "
"expected 'vec3<i32>', found 'vec3<u32>'");
}
TEST_F(ResolverValidationTest,
Expr_Constructor_ArrayOfVector_SubElemTypeMismatch_I32Bool) {
// array<vec3<i32>, 2>(vec3<i32>(), vec3<bool>(true, true, false));
SetSource(Source::Location({12, 34}));
auto* e0 = vec3<bool>(true, true, false);
auto* e1 = vec3<i32>();
auto* t = Construct(ty.array(ty.vec3<i32>(), 2), e0, e1);
WrapInFunction(t);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: type in array constructor does not match array type: "
"expected 'vec3<i32>', found 'vec3<bool>'");
}
TEST_F(ResolverValidationTest,
Expr_Constructor_ArrayOfArray_SubElemSizeMismatch) {
// array<array<i32, 2>, 2>(array<i32, 3>(), array<i32, 2>());
SetSource(Source::Location({12, 34}));
auto* e0 = array<i32, 3>();
auto* e1 = array<i32, 2>();
auto* t = Construct(ty.array(ty.array<i32, 2>(), 2), e0, e1);
WrapInFunction(t);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: type in array constructor does not match array type: "
"expected 'array<i32, 2>', found 'array<i32, 3>'");
}
TEST_F(ResolverValidationTest,
Expr_Constructor_ArrayOfArray_SubElemTypeMismatch) {
// array<array<i32, 2>, 2>(array<i32, 2>(), array<u32, 2>());
auto* e0 = array<i32, 2>();
SetSource(Source::Location({12, 34}));
auto* e1 = array<u32, 2>();
auto* t = Construct(ty.array(ty.array<i32, 2>(), 2), e0, e1);
WrapInFunction(t);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: type in array constructor does not match array type: "
"expected 'array<i32, 2>', found 'array<u32, 2>'");
}
TEST_F(ResolverValidationTest,
Expr_Constructor_ArrayOfMatrix_SubElemTypeMismatch) {
// array<mat2x2<f32>, 2>(mat2x2<f32>(), mat2x2<i32>());
auto* e0 = mat2x2<f32>();
SetSource(Source::Location({12, 34}));
auto* e1 = mat2x2<i32>();
auto* t = Construct(ty.array(ty.mat2x2<f32>(), 2), e0, e1);
WrapInFunction(t);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: type in array constructor does not match array type: "
"expected 'mat2x2<f32>', found 'mat2x2<i32>'");
}
TEST_F(ResolverValidationTest, Expr_Constructor_Array_TooFewElements) {
// array<i32, 4>(1, 2, 3);
SetSource(Source::Location({12, 34}));
auto* tc =
array<i32, 4>(create<ast::ScalarConstructorExpression>(Literal(1)),
create<ast::ScalarConstructorExpression>(Literal(2)),
create<ast::ScalarConstructorExpression>(Literal(3)));
WrapInFunction(tc);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: array constructor has too few elements: expected 4, "
"found 3");
}
TEST_F(ResolverValidationTest, Expr_Constructor_Array_TooManyElements) {
// array<i32, 4>(1, 2, 3, 4, 5);
SetSource(Source::Location({12, 34}));
auto* tc =
array<i32, 4>(create<ast::ScalarConstructorExpression>(Literal(1)),
create<ast::ScalarConstructorExpression>(Literal(2)),
create<ast::ScalarConstructorExpression>(Literal(3)),
create<ast::ScalarConstructorExpression>(Literal(4)),
create<ast::ScalarConstructorExpression>(Literal(5)));
WrapInFunction(tc);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: array constructor has too many "
"elements: expected 4, "
"found 5");
}
TEST_F(ResolverValidationTest, Expr_Constructor_Array_Runtime) {
// array<f32>(1);
auto* tc = array<i32>(
create<ast::ScalarConstructorExpression>(Source{{12, 34}}, Literal(1)));
WrapInFunction(tc);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "error: cannot init a runtime-sized array");
}
TEST_F(ResolverValidationTest, Expr_Constructor_Array_RuntimeZeroValue) {
// array<f32>();
auto* tc = array<i32>();
WrapInFunction(tc);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "error: cannot init a runtime-sized array");
}
TEST_F(ResolverValidationTest,
Expr_Constructor_Vec2F32_Error_ScalarArgumentTypeMismatch) {
auto* tc = vec2<f32>(

View File

@ -1098,23 +1098,24 @@ TEST_F(SpvBuilderConstructorTest, Type_Array_5_F32) {
}
TEST_F(SpvBuilderConstructorTest, Type_Array_2_Vec3) {
auto* cast =
array<f32, 2>(vec3<f32>(2.0f, 2.0f, 2.0f), vec3<f32>(2.0f, 2.0f, 2.0f));
WrapInFunction(cast);
auto* first = vec3<f32>(1.f, 2.f, 3.f);
auto* second = vec3<f32>(1.f, 2.f, 3.f);
auto* t = Construct(ty.array(ty.vec3<f32>(), 2), first, second);
WrapInFunction(t);
spirv::Builder& b = Build();
b.push_function(Function{});
EXPECT_EQ(b.GenerateExpression(cast), 8u);
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
%3 = OpTypeInt 32 0
%4 = OpConstant %3 2
%1 = OpTypeArray %2 %4
%5 = OpTypeVector %2 3
%6 = OpConstant %2 2
%7 = OpConstantComposite %5 %6 %6 %6
%8 = OpConstantComposite %1 %7 %7
EXPECT_EQ(b.GenerateExpression(t), 10u);
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
%2 = OpTypeVector %3 3
%4 = OpTypeInt 32 0
%5 = OpConstant %4 2
%1 = OpTypeArray %2 %5
%6 = OpConstant %3 1
%7 = OpConstant %3 2
%8 = OpConstant %3 3
%9 = OpConstantComposite %2 %6 %7 %8
%10 = OpConstantComposite %1 %9 %9
)");
}
@ -1169,7 +1170,7 @@ TEST_F(SpvBuilderConstructorTest, CommonInitializer_Array_VecArray) {
// different OpConstantComposite instructions.
// crbug.com/tint/777
auto* a1 = array<f32, 2>(1.0f, 2.0f);
auto* a2 = array<f32, 2>(vec2<f32>(1.0f, 2.0f), vec2<f32>(1.0f, 2.0f));
auto* a2 = vec2<f32>(1.0f, 2.0f);
ast::StatementList stmts = {
WrapInStatement(a1),
WrapInStatement(a2),
@ -1179,7 +1180,7 @@ TEST_F(SpvBuilderConstructorTest, CommonInitializer_Array_VecArray) {
b.push_function(Function{});
EXPECT_EQ(b.GenerateExpression(a1), 7u);
EXPECT_EQ(b.GenerateExpression(a2), 10u);
EXPECT_EQ(b.GenerateExpression(a2), 9u);
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
%3 = OpTypeInt 32 0
@ -1190,7 +1191,6 @@ TEST_F(SpvBuilderConstructorTest, CommonInitializer_Array_VecArray) {
%7 = OpConstantComposite %1 %5 %6
%8 = OpTypeVector %2 2
%9 = OpConstantComposite %8 %5 %6
%10 = OpConstantComposite %1 %9 %9
)");
}
@ -1659,7 +1659,7 @@ TEST_F(SpvBuilderConstructorTest,
IsConstructorConst_GlobalArrayWithAllConstConstructors) {
// array<vec3<f32>, 2>(vec3<f32>(1.0, 2.0, 3.0), vec3<f32>(1.0, 2.0, 3.0))
// -> true
auto* t = Construct(ty.array(ty.vec2<f32>(), 2), vec3<f32>(1.f, 2.f, 3.f),
auto* t = Construct(ty.array(ty.vec3<f32>(), 2), vec3<f32>(1.f, 2.f, 3.f),
vec3<f32>(1.f, 2.f, 3.f));
WrapInFunction(t);