diff --git a/src/validator.h b/src/validator.h index 90400e1d63..b17f2d4189 100644 --- a/src/validator.h +++ b/src/validator.h @@ -18,7 +18,10 @@ #include #include +#include "src/ast/assignment_statement.h" +#include "src/ast/expression.h" #include "src/ast/module.h" +#include "src/ast/statement.h" #include "src/validator_impl.h" namespace tint { diff --git a/src/validator_impl.cc b/src/validator_impl.cc index 57fadba631..9195baf8c1 100644 --- a/src/validator_impl.cc +++ b/src/validator_impl.cc @@ -21,13 +21,59 @@ ValidatorImpl::ValidatorImpl() = default; ValidatorImpl::~ValidatorImpl() = default; void ValidatorImpl::set_error(const Source& src, const std::string& msg) { - error_ = + error_ += std::to_string(src.line) + ":" + std::to_string(src.column) + ": " + msg; } bool ValidatorImpl::Validate(const ast::Module& module) { if (!CheckImports(module)) return false; + if (!ValidateFunctions(module.functions())) + return false; + return true; +} + +bool ValidatorImpl::ValidateFunctions(const ast::FunctionList& funcs) { + for (const auto& func : funcs) { + if (!ValidateFunction(*(func.get()))) { + return false; + } + } + return true; +} + +bool ValidatorImpl::ValidateFunction(const ast::Function& func) { + if (!ValidateStatements(func.body())) + return false; + return true; +} + +bool ValidatorImpl::ValidateStatements(const ast::StatementList& stmts) { + for (const auto& stmt : stmts) { + if (!ValidateStatement(*(stmt.get()))) { + return false; + } + } + return true; +} + +bool ValidatorImpl::ValidateStatement(const ast::Statement& stmt) { + if (stmt.IsAssign() && !ValidateAssign(*(stmt.AsAssign()))) + return false; + + return true; +} + +bool ValidatorImpl::ValidateAssign(const ast::AssignmentStatement& a) { + auto lhs_result_type = a.lhs()->result_type()->UnwrapAliasPtrAlias(); + auto rhs_result_type = a.rhs()->result_type()->UnwrapAliasPtrAlias(); + if (lhs_result_type != rhs_result_type) { + // TODO(sarahM0): figur out what should be the error number. + set_error(a.source(), "v-000x: invalid assignment of '" + + lhs_result_type->type_name() + "' to '" + + rhs_result_type->type_name() + "'"); + return false; + } return true; } diff --git a/src/validator_impl.h b/src/validator_impl.h index 414bf48ff4..27ffb8dda3 100644 --- a/src/validator_impl.h +++ b/src/validator_impl.h @@ -17,7 +17,10 @@ #include +#include "src/ast/assignment_statement.h" +#include "src/ast/expression.h" #include "src/ast/module.h" +#include "src/ast/statement.h" namespace tint { @@ -43,7 +46,26 @@ class ValidatorImpl { /// @param src the source causing the error /// @param msg the error message void set_error(const Source& src, const std::string& msg); - + /// Validates Functions + /// @param funcs the functions to check + /// @returns true if the validation was successful + bool ValidateFunctions(const ast::FunctionList& funcs); + /// Validates a function + /// @param func the function to check + /// @returns true if the validation was successful + bool ValidateFunction(const ast::Function& func); + /// Validates a set of statements + /// @param stmts the statements to check + /// @returns true if the validation was successful + bool ValidateStatements(const ast::StatementList& stmts); + /// Validates a statement + /// @param stmt the statement to check + /// @returns true if the validation was successful + bool ValidateStatement(const ast::Statement& stmt); + /// Validates an assignment + /// @param a the assignment to check + /// @returns true if the validation was successful + bool ValidateAssign(const ast::AssignmentStatement& a); /// Validates v-0001: Only allowed import is "GLSL.std.450" /// @param module the modele to check imports /// @returns ture if input complies with v-0001 rule diff --git a/src/validator_test.cc b/src/validator_test.cc index d5fbf42bcf..00ac35eeb5 100644 --- a/src/validator_test.cc +++ b/src/validator_test.cc @@ -48,11 +48,26 @@ #include "src/ast/type/struct_type.h" #include "src/ast/type/vector_type.h" #include "src/ast/type_constructor_expression.h" +#include "src/type_determiner.h" namespace tint { namespace { -using ValidatorTest = testing::Test; +class TypeDeterminerHelper { + public: + TypeDeterminerHelper() + : td_(std::make_unique(&ctx_, &mod_)) {} + + TypeDeterminer* td() const { return td_.get(); } + ast::Module* mod() { return &mod_; } + + private: + Context ctx_; + ast::Module mod_; + std::unique_ptr td_; +}; + +class ValidatorTest : public TypeDeterminerHelper, public testing::Test {}; TEST_F(ValidatorTest, Import) { ast::Module m; @@ -64,58 +79,88 @@ TEST_F(ValidatorTest, Import) { TEST_F(ValidatorTest, Import_Fail_NotGLSL) { ast::Module m; - m.AddImport(std::make_unique(Source{1, 1}, "not.GLSL", "glsl")); + m.AddImport( + std::make_unique(Source{12, 34}, "not.GLSL", "glsl")); tint::ValidatorImpl v; EXPECT_FALSE(v.CheckImports(m)); ASSERT_TRUE(v.has_error()); - EXPECT_EQ(v.error(), "1:1: v-0001: unknown import: not.GLSL"); + EXPECT_EQ(v.error(), "12:34: v-0001: unknown import: not.GLSL"); } TEST_F(ValidatorTest, Import_Fail_Typo) { ast::Module m; m.AddImport( - std::make_unique(Source{1, 1}, "GLSL.std.4501", "glsl")); + std::make_unique(Source{12, 34}, "GLSL.std.4501", "glsl")); tint::ValidatorImpl v; EXPECT_FALSE(v.CheckImports(m)); ASSERT_TRUE(v.has_error()); - EXPECT_EQ(v.error(), "1:1: v-0001: unknown import: GLSL.std.4501"); + EXPECT_EQ(v.error(), "12:34: v-0001: unknown import: GLSL.std.4501"); } TEST_F(ValidatorTest, DISABLED_AssignToScalar_Fail) { - // 1 = my_var + // 1 = my_var; ast::type::I32Type i32; auto lhs = std::make_unique( std::make_unique(&i32, 1)); auto rhs = std::make_unique("my_var"); - ast::AssignmentStatement assign(std::move(lhs), std::move(rhs)); + ast::AssignmentStatement assign(Source{12, 32}, std::move(lhs), + std::move(rhs)); tint::ValidatorImpl v; // TODO(sarahM0): Invalidate assignment to scalar. ASSERT_TRUE(v.has_error()); - EXPECT_EQ(v.error(), "1:1: v-000x: invalid assignment"); + // TODO(sarahM0): figure out what should be the error number. + EXPECT_EQ(v.error(), "12:34: v-000x: invalid assignment"); } -TEST_F(ValidatorTest, DISABLED_AssignUncompatibleTypes_Fail) { +TEST_F(ValidatorTest, AssignIncompatibleTypes_Fail) { // var a :i32; - // a = 2.3 + // a = 2.3; ast::type::F32Type f32; ast::type::I32Type i32; ast::Variable var("a", ast::StorageClass::kPrivate, &i32); auto lhs = std::make_unique("a"); - + auto lhs_ptr = lhs.get(); auto rhs = std::make_unique( std::make_unique(&f32, 2.3f)); + auto rhs_ptr = rhs.get(); - ast::AssignmentStatement assign(std::move(lhs), std::move(rhs)); + ast::AssignmentStatement assign(Source{12, 34}, std::move(lhs), + std::move(rhs)); + td()->RegisterVariableForTesting(&var); + EXPECT_TRUE(td()->DetermineResultType(&assign)) << td()->error(); + ASSERT_NE(lhs_ptr->result_type(), nullptr); + ASSERT_NE(rhs_ptr->result_type(), nullptr); tint::ValidatorImpl v; - // TODO(SarahM0): Invalidate assignments of different types. + EXPECT_FALSE(v.ValidateAssign(assign)); ASSERT_TRUE(v.has_error()); - EXPECT_EQ(v.error(), "1:1: v-000x: invalid assignment"); + // TODO(sarahM0): figure out what should be the error number. + EXPECT_EQ(v.error(), + "12:34: v-000x: invalid assignment of '__i32' to '__f32'"); +} + +TEST_F(ValidatorTest, AssignCompatibleTypes_Pass) { + // var a :i32; + // a = 2; + ast::type::I32Type i32; + + ast::Variable var("a", ast::StorageClass::kPrivate, &i32); + auto lhs = std::make_unique("a"); + auto rhs = std::make_unique( + std::make_unique(&i32, 2)); + + ast::AssignmentStatement assign(Source{12, 34}, std::move(lhs), + std::move(rhs)); + + td()->RegisterVariableForTesting(&var); + EXPECT_TRUE(td()->DetermineResultType(&assign)) << td()->error(); + tint::ValidatorImpl v; + EXPECT_TRUE(v.ValidateAssign(assign)); } } // namespace