diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 509bc5767e..324c2243c7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -467,6 +467,7 @@ if(${TINT_BUILD_TESTS}) inspector/inspector_test.cc intrinsic_table_test.cc program_test.cc + resolver/assignment_validation_test.cc resolver/decoration_validation_test.cc resolver/host_shareable_validation_test.cc resolver/intrinsic_test.cc diff --git a/src/inspector/inspector_test.cc b/src/inspector/inspector_test.cc index 436381522e..97f9f551d3 100644 --- a/src/inspector/inspector_test.cc +++ b/src/inspector/inspector_test.cc @@ -653,7 +653,7 @@ class InspectorHelper : public ProgramBuilder { if (vector_type_memo_.find(std::tie(type, count)) == vector_type_memo_.end()) { vector_type_memo_[std::tie(type, count)] = - create(ty.u32(), count); + create(type, count); } return vector_type_memo_[std::tie(type, count)]; } diff --git a/src/resolver/assignment_validation_test.cc b/src/resolver/assignment_validation_test.cc new file mode 100644 index 0000000000..ce975ee1b0 --- /dev/null +++ b/src/resolver/assignment_validation_test.cc @@ -0,0 +1,144 @@ +// Copyright 2021 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/resolver/resolver.h" + +#include "gmock/gmock.h" +#include "src/resolver/resolver_test_helper.h" + +namespace tint { +namespace resolver { +namespace { + +using ResolverAssignmentValidationTest = ResolverTest; + +TEST_F(ResolverAssignmentValidationTest, AssignIncompatibleTypes) { + // { + // var a : i32 = 2; + // a = 2.3; + // } + + auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2)); + auto* lhs = Expr("a"); + auto* rhs = Expr(2.3f); + + auto* assign = create(Source{{12, 34}}, lhs, rhs); + WrapInFunction(var, assign); + + ASSERT_FALSE(r()->Resolve()); + + EXPECT_EQ( + r()->error(), + R"(12:34 error: invalid assignment: cannot assign value of type 'f32' to a variable of type 'i32')"); +} + +TEST_F(ResolverAssignmentValidationTest, + AssignThroughPointerWrongeStoreType_Fail) { + // var a : f32; + // const b : ptr = a; + // b = 2; + const auto priv = ast::StorageClass::kFunction; + auto* var_a = Var("a", ty.f32(), priv); + auto* var_b = Const("b", ty.pointer(priv), Expr("a"), {}); + + auto* lhs = Expr("a"); + auto* rhs = Expr(2); + + auto* assign = create(Source{{12, 34}}, lhs, rhs); + WrapInFunction(var_a, var_b, assign); + + ASSERT_FALSE(r()->Resolve()); + + EXPECT_EQ( + r()->error(), + R"(12:34 error: invalid assignment: cannot assign value of type 'i32' to a variable of type 'f32')"); +} + +TEST_F(ResolverAssignmentValidationTest, + AssignCompatibleTypesInBlockStatement_Pass) { + // { + // var a : i32 = 2; + // a = 2 + // } + auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2)); + auto* lhs = Expr("a"); + auto* rhs = Expr(2); + + auto* body = create(ast::StatementList{ + create(var), + create(Source{{12, 34}}, lhs, rhs), + }); + WrapInFunction(var, body); + + ASSERT_TRUE(r()->Resolve()); +} + +TEST_F(ResolverAssignmentValidationTest, + AssignIncompatibleTypesInBlockStatement_Fail) { + // { + // var a : i32 = 2; + // a = 2.3; + // } + + auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2)); + auto* lhs = Expr("a"); + auto* rhs = Expr(2.3f); + + auto* block = create(ast::StatementList{ + create(var), + create(Source{{12, 34}}, lhs, rhs), + }); + WrapInFunction(var, block); + + ASSERT_FALSE(r()->Resolve()); + + EXPECT_EQ( + r()->error(), + R"(12:34 error: invalid assignment: cannot assign value of type 'f32' to a variable of type 'i32')"); +} + +TEST_F(ResolverAssignmentValidationTest, + AssignIncompatibleTypesInNestedBlockStatement_Fail) { + // { + // { + // var a : i32 = 2; + // a = 2.3; + // } + // } + + auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2)); + auto* lhs = Expr("a"); + auto* rhs = Expr(2.3f); + + auto* inner_block = create(ast::StatementList{ + create(var), + create(Source{{12, 34}}, lhs, rhs), + }); + + auto* outer_block = create(ast::StatementList{ + inner_block, + }); + + WrapInFunction(var, outer_block); + + ASSERT_FALSE(r()->Resolve()); + + EXPECT_EQ( + r()->error(), + R"(12:34 error: invalid assignment: cannot assign value of type 'f32' to a variable of type 'i32')"); +} + +} // namespace +} // namespace resolver +} // namespace tint diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc index 9443613b57..d4eb5b1ce7 100644 --- a/src/resolver/resolver.cc +++ b/src/resolver/resolver.cc @@ -157,6 +157,21 @@ bool Resolver::IsHostShareable(type::Type* type) { return false; } +bool Resolver::IsValidAssignment(type::Type* lhs, type::Type* rhs) { + // TODO(crbug.com/tint/659): This is a rough approximation, and is missing + // checks for writability of pointer storage class, access control, etc. + // This will need to be fixed after WGSL agrees the behavior of pointers / + // references. + // Check: + if (lhs->UnwrapIfNeeded() != rhs->UnwrapIfNeeded()) { + // Try RHS dereference + if (lhs->UnwrapIfNeeded() != rhs->UnwrapAll()) { + return false; + } + } + return true; +} + bool Resolver::ResolveInternal() { for (auto* ty : builder_->Types()) { if (auto* str = ty->As()) { @@ -251,7 +266,23 @@ bool Resolver::Statement(ast::Statement* stmt) { ScopedAssignment sa(current_statement_, sem_statement); if (auto* a = stmt->As()) { - return Expression(a->lhs()) && Expression(a->rhs()); + if (!Expression(a->lhs()) || !Expression(a->rhs())) { + return false; + } + // TODO(crbug.com/tint/659): This logic needs updating once pointers are + // pinned down in the WGSL spec. + auto* lhs_type = TypeOf(a->lhs())->UnwrapAll(); + auto* rhs_type = TypeOf(a->rhs()); + if (!IsValidAssignment(lhs_type, rhs_type)) { + diagnostics_.add_error( + "invalid assignment: cannot assign value of type '" + + rhs_type->FriendlyName(builder_->Symbols()) + + "' to a variable of type '" + + lhs_type->FriendlyName(builder_->Symbols()) + "'", + stmt->source()); + return false; + } + return true; } if (auto* b = stmt->As()) { return BlockStatement(b); @@ -1156,16 +1187,15 @@ bool Resolver::VariableDeclStatement(const ast::VariableDeclStatement* stmt) { if (!Expression(ctor)) { return false; } - if (auto* sce = ctor->As()) { - auto* lhs_type = stmt->variable()->type()->UnwrapAliasIfNeeded(); - auto* rhs_type = sce->literal()->type()->UnwrapAliasIfNeeded(); - - if (lhs_type != rhs_type) { - diagnostics_.add_error( - "constructor expression type does not match variable type", - stmt->source()); - return false; - } + auto* lhs_type = stmt->variable()->type(); + auto* rhs_type = TypeOf(ctor); + if (!IsValidAssignment(lhs_type, rhs_type)) { + diagnostics_.add_error( + "variable of type '" + lhs_type->FriendlyName(builder_->Symbols()) + + "' cannot be initialized with a value of type '" + + rhs_type->FriendlyName(builder_->Symbols()) + "'", + stmt->source()); + return false; } } diff --git a/src/resolver/resolver.h b/src/resolver/resolver.h index ea29d2e9a7..00102eee03 100644 --- a/src/resolver/resolver.h +++ b/src/resolver/resolver.h @@ -77,6 +77,13 @@ class Resolver { /// @returns true if the given type is host-shareable static bool IsHostShareable(type::Type* type); + /// @param lhs the assignment store type (non-pointer) + /// @param rhs the assignment source type (non-pointer or pointer with + /// auto-deref) + /// @returns true an expression of type `rhs` can be assigned to a variable, + /// structure member or array element of type `lhs` + static bool IsValidAssignment(type::Type* lhs, type::Type* rhs); + private: /// Structure holding semantic information about a variable. /// Used to build the semantic::Variable nodes at the end of resolving. @@ -191,7 +198,6 @@ class Resolver { // AST and Type traversal methods // Each return true on success, false on failure. bool ArrayAccessor(ast::ArrayAccessorExpression*); - bool ValidateBinary(ast::BinaryExpression* expr); bool Binary(ast::BinaryExpression*); bool Bitcast(ast::BitcastExpression*); bool BlockStatement(const ast::BlockStatement*); @@ -215,6 +221,10 @@ class Resolver { bool UnaryOp(ast::UnaryOpExpression*); bool VariableDeclStatement(const ast::VariableDeclStatement*); + // AST and Type validation methods + // Each return true on success, false on failure. + bool ValidateBinary(ast::BinaryExpression* expr); + /// @returns the semantic information for the array `arr`, building it if it /// hasn't been constructed already. If an error is raised, nullptr is /// returned. diff --git a/src/resolver/resolver_test.cc b/src/resolver/resolver_test.cc index bcaa8bc9f3..664a465c84 100644 --- a/src/resolver/resolver_test.cc +++ b/src/resolver/resolver_test.cc @@ -76,77 +76,75 @@ type::Type* ty_mat3x3(const ProgramBuilder::TypesBuilder& ty) { } TEST_F(ResolverTest, Stmt_Assign) { - auto* lhs = Expr(2); + auto* v = Var("v", ty.f32(), ast::StorageClass::kFunction); + auto* lhs = Expr("v"); auto* rhs = Expr(2.3f); auto* assign = create(lhs, rhs); - WrapInFunction(assign); + WrapInFunction(v, assign); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(lhs), nullptr); ASSERT_NE(TypeOf(rhs), nullptr); - EXPECT_TRUE(TypeOf(lhs)->Is()); + EXPECT_TRUE(TypeOf(lhs)->UnwrapAll()->Is()); EXPECT_TRUE(TypeOf(rhs)->Is()); EXPECT_EQ(StmtOf(lhs), assign); EXPECT_EQ(StmtOf(rhs), assign); } TEST_F(ResolverTest, Stmt_Case) { - auto* lhs = Expr(2); + auto* v = Var("v", ty.f32(), ast::StorageClass::kFunction); + auto* lhs = Expr("v"); auto* rhs = Expr(2.3f); auto* assign = create(lhs, rhs); - auto* body = create(ast::StatementList{ - assign, - }); + auto* body = Block(assign); ast::CaseSelectorList lit; lit.push_back(create(ty.i32(), 3)); auto* cse = create(lit, body); - WrapInFunction(cse); + WrapInFunction(v, cse); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(lhs), nullptr); ASSERT_NE(TypeOf(rhs), nullptr); - EXPECT_TRUE(TypeOf(lhs)->Is()); + EXPECT_TRUE(TypeOf(lhs)->UnwrapAll()->Is()); EXPECT_TRUE(TypeOf(rhs)->Is()); EXPECT_EQ(StmtOf(lhs), assign); EXPECT_EQ(StmtOf(rhs), assign); } TEST_F(ResolverTest, Stmt_Block) { - auto* lhs = Expr(2); + auto* v = Var("v", ty.f32(), ast::StorageClass::kFunction); + auto* lhs = Expr("v"); auto* rhs = Expr(2.3f); auto* assign = create(lhs, rhs); - auto* block = create(ast::StatementList{ - assign, - }); - WrapInFunction(block); + auto* block = Block(assign); + WrapInFunction(v, block); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(lhs), nullptr); ASSERT_NE(TypeOf(rhs), nullptr); - EXPECT_TRUE(TypeOf(lhs)->Is()); + EXPECT_TRUE(TypeOf(lhs)->UnwrapAll()->Is()); EXPECT_TRUE(TypeOf(rhs)->Is()); EXPECT_EQ(StmtOf(lhs), assign); EXPECT_EQ(StmtOf(rhs), assign); } TEST_F(ResolverTest, Stmt_Else) { - auto* lhs = Expr(2); + auto* v = Var("v", ty.f32(), ast::StorageClass::kFunction); + auto* lhs = Expr("v"); auto* rhs = Expr(2.3f); auto* assign = create(lhs, rhs); - auto* body = create(ast::StatementList{ - assign, - }); + auto* body = Block(assign); auto* cond = Expr(3); auto* stmt = create(cond, body); - WrapInFunction(stmt); + WrapInFunction(v, stmt); EXPECT_TRUE(r()->Resolve()) << r()->error(); @@ -154,7 +152,7 @@ TEST_F(ResolverTest, Stmt_Else) { ASSERT_NE(TypeOf(lhs), nullptr); ASSERT_NE(TypeOf(rhs), nullptr); EXPECT_TRUE(TypeOf(stmt->condition())->Is()); - EXPECT_TRUE(TypeOf(lhs)->Is()); + EXPECT_TRUE(TypeOf(lhs)->UnwrapAll()->Is()); EXPECT_TRUE(TypeOf(rhs)->Is()); EXPECT_EQ(StmtOf(lhs), assign); EXPECT_EQ(StmtOf(rhs), assign); @@ -162,25 +160,24 @@ TEST_F(ResolverTest, Stmt_Else) { } TEST_F(ResolverTest, Stmt_If) { - auto* else_lhs = Expr(2); + auto* v = Var("v", ty.f32(), ast::StorageClass::kFunction); + auto* else_lhs = Expr("v"); auto* else_rhs = Expr(2.3f); - auto* else_body = create(ast::StatementList{ - create(else_lhs, else_rhs), - }); + auto* else_body = Block(create(else_lhs, else_rhs)); auto* else_cond = Expr(3); auto* else_stmt = create(else_cond, else_body); - auto* lhs = Expr(2); + auto* lhs = Expr("v"); auto* rhs = Expr(2.3f); auto* assign = create(lhs, rhs); - auto* body = create(ast::StatementList{assign}); + auto* body = Block(assign); auto* cond = Expr(true); auto* stmt = create(cond, body, ast::ElseStatementList{else_stmt}); - WrapInFunction(stmt); + WrapInFunction(v, stmt); EXPECT_TRUE(r()->Resolve()) << r()->error(); @@ -190,9 +187,9 @@ TEST_F(ResolverTest, Stmt_If) { ASSERT_NE(TypeOf(lhs), nullptr); ASSERT_NE(TypeOf(rhs), nullptr); EXPECT_TRUE(TypeOf(stmt->condition())->Is()); - EXPECT_TRUE(TypeOf(else_lhs)->Is()); + EXPECT_TRUE(TypeOf(else_lhs)->UnwrapAll()->Is()); EXPECT_TRUE(TypeOf(else_rhs)->Is()); - EXPECT_TRUE(TypeOf(lhs)->Is()); + EXPECT_TRUE(TypeOf(lhs)->UnwrapAll()->Is()); EXPECT_TRUE(TypeOf(rhs)->Is()); EXPECT_EQ(StmtOf(lhs), assign); EXPECT_EQ(StmtOf(rhs), assign); @@ -201,22 +198,19 @@ TEST_F(ResolverTest, Stmt_If) { } TEST_F(ResolverTest, Stmt_Loop) { - auto* body_lhs = Expr(2); + auto* v = Var("v", ty.f32(), ast::StorageClass::kFunction); + auto* body_lhs = Expr("v"); auto* body_rhs = Expr(2.3f); - auto* body = create(ast::StatementList{ - create(body_lhs, body_rhs), - }); - auto* continuing_lhs = Expr(2); + auto* body = Block(create(body_lhs, body_rhs)); + auto* continuing_lhs = Expr("v"); auto* continuing_rhs = Expr(2.3f); - auto* continuing = create( - - ast::StatementList{ - create(continuing_lhs, continuing_rhs), - }); + auto* continuing = create(ast::StatementList{ + create(continuing_lhs, continuing_rhs), + }); auto* stmt = create(body, continuing); - WrapInFunction(stmt); + WrapInFunction(v, stmt); EXPECT_TRUE(r()->Resolve()) << r()->error(); @@ -224,9 +218,9 @@ TEST_F(ResolverTest, Stmt_Loop) { ASSERT_NE(TypeOf(body_rhs), nullptr); ASSERT_NE(TypeOf(continuing_lhs), nullptr); ASSERT_NE(TypeOf(continuing_rhs), nullptr); - EXPECT_TRUE(TypeOf(body_lhs)->Is()); + EXPECT_TRUE(TypeOf(body_lhs)->UnwrapAll()->Is()); EXPECT_TRUE(TypeOf(body_rhs)->Is()); - EXPECT_TRUE(TypeOf(continuing_lhs)->Is()); + EXPECT_TRUE(TypeOf(continuing_lhs)->UnwrapAll()->Is()); EXPECT_TRUE(TypeOf(continuing_rhs)->Is()); } @@ -250,12 +244,11 @@ TEST_F(ResolverTest, Stmt_Return_WithoutValue) { } TEST_F(ResolverTest, Stmt_Switch) { - auto* lhs = Expr(2); + auto* v = Var("v", ty.f32(), ast::StorageClass::kFunction); + auto* lhs = Expr("v"); auto* rhs = Expr(2.3f); - auto* body = create(ast::StatementList{ - create(lhs, rhs), - }); + auto* body = Block(create(lhs, rhs)); ast::CaseSelectorList lit; lit.push_back(create(ty.i32(), 3)); @@ -263,7 +256,7 @@ TEST_F(ResolverTest, Stmt_Switch) { cases.push_back(create(lit, body)); auto* stmt = create(Expr(2), cases); - WrapInFunction(stmt); + WrapInFunction(v, stmt); EXPECT_TRUE(r()->Resolve()) << r()->error(); @@ -272,7 +265,7 @@ TEST_F(ResolverTest, Stmt_Switch) { ASSERT_NE(TypeOf(rhs), nullptr); EXPECT_TRUE(TypeOf(stmt->condition())->Is()); - EXPECT_TRUE(TypeOf(lhs)->Is()); + EXPECT_TRUE(TypeOf(lhs)->UnwrapAll()->Is()); EXPECT_TRUE(TypeOf(rhs)->Is()); } diff --git a/src/resolver/validation_test.cc b/src/resolver/validation_test.cc index 8cc40933c9..659eb0f448 100644 --- a/src/resolver/validation_test.cc +++ b/src/resolver/validation_test.cc @@ -157,7 +157,7 @@ TEST_F(ResolverValidationTest, EXPECT_FALSE(r()->Resolve()); EXPECT_EQ( r()->error(), - R"(3:3 error: constructor expression type does not match variable type)"); + R"(3:3 error: variable of type 'i32' cannot be initialized with a value of type 'u32')"); } TEST_F(ResolverValidationTest, @@ -174,7 +174,7 @@ TEST_F(ResolverValidationTest, EXPECT_FALSE(r()->Resolve()); EXPECT_EQ( r()->error(), - R"(3:3 error: constructor expression type does not match variable type)"); + R"(3:3 error: variable of type 'MyInt' cannot be initialized with a value of type 'u32')"); } TEST_F(ResolverValidationTest, Expr_Error_Unknown) { diff --git a/src/transform/bound_array_accessors_test.cc b/src/transform/bound_array_accessors_test.cc index 344a75e9ea..4f82af464b 100644 --- a/src/transform/bound_array_accessors_test.cc +++ b/src/transform/bound_array_accessors_test.cc @@ -24,22 +24,22 @@ using BoundArrayAccessorsTest = TransformTest; TEST_F(BoundArrayAccessorsTest, Ptrs_Clamp) { auto* src = R"( -var a : array; +var a : array; const c : u32 = 1u; fn f() -> void { - const b : ptr = a[c]; + const b : ptr = a[c]; } )"; auto* expect = R"( -var a : array; +var a : array; const c : u32 = 1u; fn f() -> void { - const b : ptr = a[min(u32(c), 2u)]; + const b : ptr = a[min(u32(c), 2u)]; } )"; diff --git a/src/transform/hlsl_test.cc b/src/transform/hlsl_test.cc index 5f6d7d6295..fb59a5db47 100644 --- a/src/transform/hlsl_test.cc +++ b/src/transform/hlsl_test.cc @@ -30,7 +30,7 @@ fn main() -> void { var f1 : f32 = 2.0; var f2 : f32 = 3.0; var f3 : f32 = 4.0; - var i : i32 = array(f0, f1, f2, f3)[2]; + var i : f32 = array(f0, f1, f2, f3)[2]; } )"; @@ -42,7 +42,7 @@ fn main() -> void { var f2 : f32 = 3.0; var f3 : f32 = 4.0; const tint_symbol_1 : array = array(f0, f1, f2, f3); - var i : i32 = tint_symbol_1[2]; + var i : f32 = tint_symbol_1[2]; } )"; @@ -55,7 +55,7 @@ TEST_F(HlslTest, PromoteArrayInitializerToConstVar_ArrayInArray) { auto* src = R"( [[stage(vertex)]] fn main() -> void { - var i : i32 = array, 2>(array(1.0, 2.0), array(3.0, 4.0))[0][1]; + var i : f32 = array, 2>(array(1.0, 2.0), array(3.0, 4.0))[0][1]; } )"; @@ -65,7 +65,7 @@ fn main() -> void { const tint_symbol_1 : array = array(1.0, 2.0); const tint_symbol_2 : array = array(3.0, 4.0); const tint_symbol_3 : array, 2> = array, 2>(tint_symbol_1, tint_symbol_2); - var i : i32 = tint_symbol_3[0][1]; + var i : f32 = tint_symbol_3[0][1]; } )"; diff --git a/src/transform/vertex_pulling_test.cc b/src/transform/vertex_pulling_test.cc index c0b6e19f16..a6a16997e0 100644 --- a/src/transform/vertex_pulling_test.cc +++ b/src/transform/vertex_pulling_test.cc @@ -298,7 +298,7 @@ fn main() -> void { TEST_F(VertexPullingTest, TwoAttributesSameBuffer) { auto* src = R"( [[location(0)]] var var_a : f32; -[[location(1)]] var var_b : array; +[[location(1)]] var var_b : vec4; [[stage(vertex)]] fn main() -> void {} @@ -316,7 +316,7 @@ struct TintVertexData { var var_a : f32; -var var_b : array; +var var_b : vec4; [[stage(vertex)]] fn main() -> void { @@ -346,9 +346,9 @@ fn main() -> void { TEST_F(VertexPullingTest, FloatVectorAttributes) { auto* src = R"( -[[location(0)]] var var_a : array; -[[location(1)]] var var_b : array; -[[location(2)]] var var_c : array; +[[location(0)]] var var_a : vec2; +[[location(1)]] var var_b : vec3; +[[location(2)]] var var_c : vec4; [[stage(vertex)]] fn main() -> void {} @@ -368,11 +368,11 @@ struct TintVertexData { _tint_vertex_data : [[stride(4)]] array; }; -var var_a : array; +var var_a : vec2; -var var_b : array; +var var_b : vec3; -var var_c : array; +var var_c : vec4; [[stage(vertex)]] fn main() -> void { diff --git a/src/validator/validator_impl.cc b/src/validator/validator_impl.cc index eaf131a339..25d4da0f57 100644 --- a/src/validator/validator_impl.cc +++ b/src/validator/validator_impl.cc @@ -484,16 +484,7 @@ bool ValidatorImpl::ValidateAssign(const ast::AssignmentStatement* assign) { return false; } auto* lhs_result_type = program_->Sem().Get(lhs)->Type()->UnwrapIfNeeded(); - if (auto* lhs_reference_type = As(lhs_result_type)) { - auto* lhs_store_type = lhs_reference_type->type()->UnwrapIfNeeded(); - if (lhs_store_type != rhs_result_type) { - add_error(assign->source(), "v-000x", - "invalid assignment: can't assign value of type '" + - rhs_result_type->type_name() + "' to '" + - lhs_store_type->type_name() + "'"); - return false; - } - } else { + if (!Is(lhs_result_type)) { if (!ValidateBadAssignmentToIdentifier(assign)) { return false; } diff --git a/src/validator/validator_test.cc b/src/validator/validator_test.cc index cc39b22219..245a2610ec 100644 --- a/src/validator/validator_test.cc +++ b/src/validator/validator_test.cc @@ -139,157 +139,6 @@ TEST_F(ValidatorTest, AssignThroughPointer_Pass) { EXPECT_TRUE(v.ValidateAssign(assign)) << v.error(); } -TEST_F(ValidatorTest, AssignIncompatibleTypes_Fail) { - // { - // var a :i32 = 2; - // a = 2.3; - // } - - auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2)); - RegisterVariable(var); - - auto* lhs = Expr("a"); - auto* rhs = Expr(2.3f); - - auto* assign = create( - Source{Source::Location{12, 34}}, lhs, rhs); - WrapInFunction(assign); - - ValidatorImpl& v = Build(); - - ASSERT_NE(TypeOf(lhs), nullptr); - ASSERT_NE(TypeOf(rhs), nullptr); - - EXPECT_FALSE(v.ValidateAssign(assign)); - ASSERT_TRUE(v.has_error()); - // TODO(sarahM0): figure out what should be the error number. - EXPECT_EQ(v.error(), - "12:34 v-000x: invalid assignment: can't assign value of type " - "'__f32' to '__i32'"); -} - -TEST_F(ValidatorTest, AssignThroughPointerWrongeStoreType_Fail) { - // var a :f32; - // const b : ptr = a; - // b = 2; - const auto priv = ast::StorageClass::kFunction; - auto* var_a = Var("a", ty.f32(), priv, Expr(2), {}); - auto* var_b = Const("b", ty.pointer(priv), Expr("a"), {}); - RegisterVariable(var_a); - RegisterVariable(var_b); - - auto* lhs = Expr("a"); - auto* rhs = Expr(2); - - auto* assign = create( - Source{Source::Location{12, 34}}, lhs, rhs); - WrapInFunction(assign); - - ValidatorImpl& v = Build(); - - ASSERT_NE(TypeOf(lhs), nullptr); - ASSERT_NE(TypeOf(rhs), nullptr); - - EXPECT_FALSE(v.ValidateAssign(assign)); - EXPECT_EQ(v.error(), - "12:34 v-000x: invalid assignment: can't assign value of type " - "'__i32' to '__f32'"); -} - -TEST_F(ValidatorTest, AssignCompatibleTypesInBlockStatement_Pass) { - // { - // var a :i32 = 2; - // a = 2 - // } - auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2)); - - auto* lhs = Expr("a"); - auto* rhs = Expr(2); - - auto* body = create(ast::StatementList{ - create(var), - create(Source{Source::Location{12, 34}}, lhs, - rhs), - }); - WrapInFunction(body); - - ValidatorImpl& v = Build(); - - ASSERT_NE(TypeOf(lhs), nullptr); - ASSERT_NE(TypeOf(rhs), nullptr); - - EXPECT_TRUE(v.ValidateStatements(body)) << v.error(); -} - -TEST_F(ValidatorTest, AssignIncompatibleTypesInBlockStatement_Fail) { - // { - // var a :i32 = 2; - // a = 2.3; - // } - - auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2)); - - auto* lhs = Expr("a"); - auto* rhs = Expr(2.3f); - - auto* block = create(ast::StatementList{ - create(var), - create(Source{Source::Location{12, 34}}, lhs, - rhs), - }); - WrapInFunction(block); - - ValidatorImpl& v = Build(); - - ASSERT_NE(TypeOf(lhs), nullptr); - ASSERT_NE(TypeOf(rhs), nullptr); - - EXPECT_FALSE(v.ValidateStatements(block)); - ASSERT_TRUE(v.has_error()); - // TODO(sarahM0): figure out what should be the error number. - EXPECT_EQ(v.error(), - "12:34 v-000x: invalid assignment: can't assign value of type " - "'__f32' to '__i32'"); -} - -TEST_F(ValidatorTest, AssignIncompatibleTypesInNestedBlockStatement_Fail) { - // { - // { - // var a :i32 = 2; - // a = 2.3; - // } - // } - - auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2)); - - auto* lhs = Expr("a"); - auto* rhs = Expr(2.3f); - - auto* inner_block = create(ast::StatementList{ - create(var), - create(Source{Source::Location{12, 34}}, lhs, - rhs), - }); - - auto* outer_block = create(ast::StatementList{ - inner_block, - }); - - WrapInFunction(outer_block); - - ValidatorImpl& v = Build(); - - ASSERT_NE(TypeOf(lhs), nullptr); - ASSERT_NE(TypeOf(rhs), nullptr); - - EXPECT_FALSE(v.ValidateStatements(outer_block)); - ASSERT_TRUE(v.has_error()); - // TODO(sarahM0): figure out what should be the error number. - EXPECT_EQ(v.error(), - "12:34 v-000x: invalid assignment: can't assign value of type " - "'__f32' to '__i32'"); -} - TEST_F(ValidatorTest, GlobalVariableWithStorageClass_Pass) { // var global_var: f32; auto* var = Global(Source{Source::Location{12, 34}}, "global_var", ty.f32(), diff --git a/src/writer/spirv/builder_function_variable_test.cc b/src/writer/spirv/builder_function_variable_test.cc index fc9cd26291..18c12272d5 100644 --- a/src/writer/spirv/builder_function_variable_test.cc +++ b/src/writer/spirv/builder_function_variable_test.cc @@ -172,7 +172,7 @@ OpStore %7 %6 TEST_F(BuilderTest, FunctionVar_Const) { auto* init = vec3(1.f, 1.f, 3.f); - auto* v = Const("var", ty.f32(), init); + auto* v = Const("var", ty.vec3(), init); WrapInFunction(v); diff --git a/src/writer/spirv/builder_ident_expression_test.cc b/src/writer/spirv/builder_ident_expression_test.cc index bacfd582a8..55f2a128da 100644 --- a/src/writer/spirv/builder_ident_expression_test.cc +++ b/src/writer/spirv/builder_ident_expression_test.cc @@ -69,7 +69,7 @@ TEST_F(BuilderTest, IdentifierExpression_GlobalVar) { TEST_F(BuilderTest, IdentifierExpression_FunctionConst) { auto* init = vec3(1.f, 1.f, 3.f); - auto* v = Const("var", ty.f32(), init); + auto* v = Const("var", ty.vec3(), init); auto* expr = Expr("var"); WrapInFunction(v, expr); diff --git a/test/BUILD.gn b/test/BUILD.gn index f60bdc04f5..15508c4478 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -169,6 +169,7 @@ source_set("tint_unittests_core_src") { "../src/intrinsic_table_test.cc", "../src/program_builder_test.cc", "../src/program_test.cc", + "../src/resolver/assignment_validation_test.cc", "../src/resolver/decoration_validation_test.cc", "../src/resolver/host_shareable_validation_test.cc", "../src/resolver/intrinsic_test.cc",