[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:
parent
aff8f8f26a
commit
5a4dc9a02f
|
@ -1876,7 +1876,9 @@ bool Resolver::Constructor(ast::ConstructorExpression* expr) {
|
||||||
if (auto* mat_type = type->As<sem::Matrix>()) {
|
if (auto* mat_type = type->As<sem::Matrix>()) {
|
||||||
return ValidateMatrixConstructor(type_ctor, mat_type);
|
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>()) {
|
} else if (auto* scalar_ctor = expr->As<ast::ScalarConstructorExpression>()) {
|
||||||
Mark(scalar_ctor->literal());
|
Mark(scalar_ctor->literal());
|
||||||
auto* type = TypeOf(scalar_ctor->literal());
|
auto* type = TypeOf(scalar_ctor->literal());
|
||||||
|
@ -1890,6 +1892,46 @@ bool Resolver::Constructor(ast::ConstructorExpression* expr) {
|
||||||
return true;
|
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(
|
bool Resolver::ValidateVectorConstructor(
|
||||||
const ast::TypeConstructorExpression* ctor,
|
const ast::TypeConstructorExpression* ctor,
|
||||||
const sem::Vector* vec_type) {
|
const sem::Vector* vec_type) {
|
||||||
|
|
|
@ -275,6 +275,8 @@ class Resolver {
|
||||||
const std::string& rhs_type_name);
|
const std::string& rhs_type_name);
|
||||||
bool ValidateVectorConstructor(const ast::TypeConstructorExpression* ctor,
|
bool ValidateVectorConstructor(const ast::TypeConstructorExpression* ctor,
|
||||||
const sem::Vector* vec_type);
|
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 ValidateTypeDecl(const ast::TypeDecl* named_type) const;
|
||||||
bool ValidateNoDuplicateDecorations(const ast::DecorationList& decorations);
|
bool ValidateNoDuplicateDecorations(const ast::DecorationList& decorations);
|
||||||
|
|
||||||
|
|
|
@ -728,6 +728,208 @@ TEST_F(ResolverValidationTest, OffsetAndAlignAndSizeDecoration) {
|
||||||
"decorations");
|
"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,
|
TEST_F(ResolverValidationTest,
|
||||||
Expr_Constructor_Vec2F32_Error_ScalarArgumentTypeMismatch) {
|
Expr_Constructor_Vec2F32_Error_ScalarArgumentTypeMismatch) {
|
||||||
auto* tc = vec2<f32>(
|
auto* tc = vec2<f32>(
|
||||||
|
|
|
@ -1098,23 +1098,24 @@ TEST_F(SpvBuilderConstructorTest, Type_Array_5_F32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SpvBuilderConstructorTest, Type_Array_2_Vec3) {
|
TEST_F(SpvBuilderConstructorTest, Type_Array_2_Vec3) {
|
||||||
auto* cast =
|
auto* first = vec3<f32>(1.f, 2.f, 3.f);
|
||||||
array<f32, 2>(vec3<f32>(2.0f, 2.0f, 2.0f), vec3<f32>(2.0f, 2.0f, 2.0f));
|
auto* second = vec3<f32>(1.f, 2.f, 3.f);
|
||||||
WrapInFunction(cast);
|
auto* t = Construct(ty.array(ty.vec3<f32>(), 2), first, second);
|
||||||
|
WrapInFunction(t);
|
||||||
spirv::Builder& b = Build();
|
spirv::Builder& b = Build();
|
||||||
|
|
||||||
b.push_function(Function{});
|
b.push_function(Function{});
|
||||||
EXPECT_EQ(b.GenerateExpression(cast), 8u);
|
EXPECT_EQ(b.GenerateExpression(t), 10u);
|
||||||
|
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
|
||||||
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
|
%2 = OpTypeVector %3 3
|
||||||
%3 = OpTypeInt 32 0
|
%4 = OpTypeInt 32 0
|
||||||
%4 = OpConstant %3 2
|
%5 = OpConstant %4 2
|
||||||
%1 = OpTypeArray %2 %4
|
%1 = OpTypeArray %2 %5
|
||||||
%5 = OpTypeVector %2 3
|
%6 = OpConstant %3 1
|
||||||
%6 = OpConstant %2 2
|
%7 = OpConstant %3 2
|
||||||
%7 = OpConstantComposite %5 %6 %6 %6
|
%8 = OpConstant %3 3
|
||||||
%8 = OpConstantComposite %1 %7 %7
|
%9 = OpConstantComposite %2 %6 %7 %8
|
||||||
|
%10 = OpConstantComposite %1 %9 %9
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1169,7 +1170,7 @@ TEST_F(SpvBuilderConstructorTest, CommonInitializer_Array_VecArray) {
|
||||||
// different OpConstantComposite instructions.
|
// different OpConstantComposite instructions.
|
||||||
// crbug.com/tint/777
|
// crbug.com/tint/777
|
||||||
auto* a1 = array<f32, 2>(1.0f, 2.0f);
|
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 = {
|
ast::StatementList stmts = {
|
||||||
WrapInStatement(a1),
|
WrapInStatement(a1),
|
||||||
WrapInStatement(a2),
|
WrapInStatement(a2),
|
||||||
|
@ -1179,7 +1180,7 @@ TEST_F(SpvBuilderConstructorTest, CommonInitializer_Array_VecArray) {
|
||||||
|
|
||||||
b.push_function(Function{});
|
b.push_function(Function{});
|
||||||
EXPECT_EQ(b.GenerateExpression(a1), 7u);
|
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
|
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
|
||||||
%3 = OpTypeInt 32 0
|
%3 = OpTypeInt 32 0
|
||||||
|
@ -1190,7 +1191,6 @@ TEST_F(SpvBuilderConstructorTest, CommonInitializer_Array_VecArray) {
|
||||||
%7 = OpConstantComposite %1 %5 %6
|
%7 = OpConstantComposite %1 %5 %6
|
||||||
%8 = OpTypeVector %2 2
|
%8 = OpTypeVector %2 2
|
||||||
%9 = OpConstantComposite %8 %5 %6
|
%9 = OpConstantComposite %8 %5 %6
|
||||||
%10 = OpConstantComposite %1 %9 %9
|
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1659,7 +1659,7 @@ TEST_F(SpvBuilderConstructorTest,
|
||||||
IsConstructorConst_GlobalArrayWithAllConstConstructors) {
|
IsConstructorConst_GlobalArrayWithAllConstConstructors) {
|
||||||
// array<vec3<f32>, 2>(vec3<f32>(1.0, 2.0, 3.0), vec3<f32>(1.0, 2.0, 3.0))
|
// array<vec3<f32>, 2>(vec3<f32>(1.0, 2.0, 3.0), vec3<f32>(1.0, 2.0, 3.0))
|
||||||
// -> true
|
// -> 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));
|
vec3<f32>(1.f, 2.f, 3.f));
|
||||||
WrapInFunction(t);
|
WrapInFunction(t);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue