mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-11 06:27:54 +00:00
[validation] make validator directory
Change-Id: Ia5caea84b48b37f0bee9582ee4c8514da4ee3978 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/31981 Commit-Queue: David Neto <dneto@google.com> Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
committed by
Commit Bot service account
parent
cc2305dd95
commit
34f90a0097
34
src/validator/validator.cc
Normal file
34
src/validator/validator.cc
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright 2020 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/validator/validator.h"
|
||||
|
||||
#include "src/validator/validator_impl.h"
|
||||
|
||||
namespace tint {
|
||||
|
||||
Validator::Validator() : impl_(std::make_unique<tint::ValidatorImpl>()) {}
|
||||
|
||||
Validator::~Validator() = default;
|
||||
|
||||
bool Validator::Validate(const ast::Module* module) {
|
||||
bool ret = impl_->Validate(module);
|
||||
|
||||
if (impl_->has_error())
|
||||
set_error(impl_->error());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace tint
|
||||
58
src/validator/validator.h
Normal file
58
src/validator/validator.h
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright 2020 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.
|
||||
|
||||
#ifndef SRC_VALIDATOR_VALIDATOR_H_
|
||||
#define SRC_VALIDATOR_VALIDATOR_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "src/ast/assignment_statement.h"
|
||||
#include "src/ast/expression.h"
|
||||
#include "src/ast/module.h"
|
||||
#include "src/ast/statement.h"
|
||||
#include "src/validator/validator_impl.h"
|
||||
|
||||
namespace tint {
|
||||
|
||||
class ValidatorImpl;
|
||||
|
||||
/// Determines if the module is complete and valid
|
||||
class Validator {
|
||||
public:
|
||||
/// Constructor
|
||||
Validator();
|
||||
~Validator();
|
||||
|
||||
/// Runs the validator
|
||||
/// @param module the module to validate
|
||||
/// @returns true if the validation was successful
|
||||
bool Validate(const ast::Module* module);
|
||||
|
||||
/// @returns error messages from the validator
|
||||
const std::string& error() { return error_; }
|
||||
/// @returns true if an error was encountered
|
||||
bool has_error() const { return error_.size() > 0; }
|
||||
/// Sets the error string
|
||||
/// @param msg the error message
|
||||
void set_error(const std::string& msg) { error_ = msg; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<ValidatorImpl> impl_;
|
||||
std::string error_;
|
||||
};
|
||||
|
||||
} // namespace tint
|
||||
|
||||
#endif // SRC_VALIDATOR_VALIDATOR_H_
|
||||
419
src/validator/validator_control_block_test.cc
Normal file
419
src/validator/validator_control_block_test.cc
Normal file
@@ -0,0 +1,419 @@
|
||||
// Copyright 2020 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 "gtest/gtest.h"
|
||||
#include "spirv/unified1/GLSL.std.450.h"
|
||||
#include "src/ast/case_statement.h"
|
||||
#include "src/ast/fallthrough_statement.h"
|
||||
#include "src/ast/return_statement.h"
|
||||
#include "src/ast/scalar_constructor_expression.h"
|
||||
#include "src/ast/sint_literal.h"
|
||||
#include "src/ast/switch_statement.h"
|
||||
#include "src/ast/type/alias_type.h"
|
||||
#include "src/ast/type/f32_type.h"
|
||||
#include "src/ast/type/i32_type.h"
|
||||
#include "src/ast/type/u32_type.h"
|
||||
#include "src/ast/uint_literal.h"
|
||||
#include "src/ast/variable.h"
|
||||
#include "src/ast/variable_decl_statement.h"
|
||||
#include "src/type_determiner.h"
|
||||
#include "src/validator/validator_impl.h"
|
||||
#include "src/validator/validator_test_helper.h"
|
||||
|
||||
namespace tint {
|
||||
namespace {
|
||||
|
||||
class ValidateControlBlockTest : public ValidatorTestHelper,
|
||||
public testing::Test {};
|
||||
|
||||
TEST_F(ValidateControlBlockTest, SwitchSelectorExpressionNoneIntegerType_Fail) {
|
||||
// var a : f32 = 3.14;
|
||||
// switch (a) {
|
||||
// default: {}
|
||||
// }
|
||||
ast::type::F32Type f32;
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &f32);
|
||||
var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&f32, 3.14f)));
|
||||
|
||||
auto cond = std::make_unique<ast::IdentifierExpression>(
|
||||
Source{Source::Location{12, 34}}, "a");
|
||||
ast::CaseSelectorList default_csl;
|
||||
auto block_default = std::make_unique<ast::BlockStatement>();
|
||||
ast::CaseStatementList body;
|
||||
body.push_back(std::make_unique<ast::CaseStatement>(
|
||||
std::move(default_csl), std::move(block_default)));
|
||||
|
||||
auto block = std::make_unique<ast::BlockStatement>();
|
||||
block->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
|
||||
block->append(
|
||||
std::make_unique<ast::SwitchStatement>(std::move(cond), std::move(body)));
|
||||
|
||||
EXPECT_TRUE(td()->DetermineStatements(block.get())) << td()->error();
|
||||
EXPECT_FALSE(v()->ValidateStatements(block.get()));
|
||||
EXPECT_EQ(v()->error(),
|
||||
"12:34: v-0025: switch statement selector expression must be "
|
||||
"of a scalar integer type");
|
||||
}
|
||||
|
||||
TEST_F(ValidateControlBlockTest, SwitchWithoutDefault_Fail) {
|
||||
// var a : i32 = 2;
|
||||
// switch (a) {
|
||||
// case 1: {}
|
||||
// }
|
||||
ast::type::I32Type i32;
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &i32);
|
||||
var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2)));
|
||||
|
||||
auto cond = std::make_unique<ast::IdentifierExpression>("a");
|
||||
ast::CaseSelectorList csl;
|
||||
csl.push_back(std::make_unique<ast::SintLiteral>(&i32, 1));
|
||||
ast::CaseStatementList body;
|
||||
body.push_back(std::make_unique<ast::CaseStatement>(
|
||||
std::move(csl), std::make_unique<ast::BlockStatement>()));
|
||||
|
||||
auto block = std::make_unique<ast::BlockStatement>();
|
||||
block->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
|
||||
block->append(std::make_unique<ast::SwitchStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(cond), std::move(body)));
|
||||
|
||||
EXPECT_TRUE(td()->DetermineStatements(block.get())) << td()->error();
|
||||
EXPECT_FALSE(v()->ValidateStatements(block.get()));
|
||||
EXPECT_EQ(v()->error(),
|
||||
"12:34: v-0008: switch statement must have exactly one default "
|
||||
"clause");
|
||||
}
|
||||
|
||||
TEST_F(ValidateControlBlockTest, SwitchWithTwoDefault_Fail) {
|
||||
// var a : i32 = 2;
|
||||
// switch (a) {
|
||||
// default: {}
|
||||
// case 1: {}
|
||||
// default: {}
|
||||
// }
|
||||
ast::type::I32Type i32;
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &i32);
|
||||
var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2)));
|
||||
|
||||
ast::CaseStatementList switch_body;
|
||||
auto cond = std::make_unique<ast::IdentifierExpression>("a");
|
||||
|
||||
ast::CaseSelectorList default_csl_1;
|
||||
auto block_default_1 = std::make_unique<ast::BlockStatement>();
|
||||
switch_body.push_back(std::make_unique<ast::CaseStatement>(
|
||||
std::move(default_csl_1), std::move(block_default_1)));
|
||||
|
||||
ast::CaseSelectorList csl_case_1;
|
||||
csl_case_1.push_back(std::make_unique<ast::SintLiteral>(&i32, 1));
|
||||
auto block_case_1 = std::make_unique<ast::BlockStatement>();
|
||||
switch_body.push_back(std::make_unique<ast::CaseStatement>(
|
||||
std::move(csl_case_1), std::move(block_case_1)));
|
||||
|
||||
ast::CaseSelectorList default_csl_2;
|
||||
auto block_default_2 = std::make_unique<ast::BlockStatement>();
|
||||
switch_body.push_back(std::make_unique<ast::CaseStatement>(
|
||||
std::move(default_csl_2), std::move(block_default_2)));
|
||||
|
||||
auto block = std::make_unique<ast::BlockStatement>();
|
||||
block->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
|
||||
block->append(std::make_unique<ast::SwitchStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(cond),
|
||||
std::move(switch_body)));
|
||||
|
||||
EXPECT_TRUE(td()->DetermineStatements(block.get())) << td()->error();
|
||||
EXPECT_FALSE(v()->ValidateStatements(block.get()));
|
||||
EXPECT_EQ(v()->error(),
|
||||
"12:34: v-0008: switch statement must have exactly one default "
|
||||
"clause");
|
||||
}
|
||||
|
||||
TEST_F(ValidateControlBlockTest,
|
||||
SwitchConditionTypeMustMatchSelectorType2_Fail) {
|
||||
// var a : i32 = 2;
|
||||
// switch (a) {
|
||||
// case 1: {}
|
||||
// default: {}
|
||||
// }
|
||||
ast::type::U32Type u32;
|
||||
ast::type::I32Type i32;
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &i32);
|
||||
var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2)));
|
||||
|
||||
ast::CaseStatementList switch_body;
|
||||
auto cond = std::make_unique<ast::IdentifierExpression>("a");
|
||||
|
||||
ast::CaseSelectorList csl;
|
||||
csl.push_back(std::make_unique<ast::UintLiteral>(&u32, 1));
|
||||
switch_body.push_back(std::make_unique<ast::CaseStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(csl),
|
||||
std::make_unique<ast::BlockStatement>()));
|
||||
|
||||
ast::CaseSelectorList default_csl;
|
||||
auto block_default = std::make_unique<ast::BlockStatement>();
|
||||
switch_body.push_back(std::make_unique<ast::CaseStatement>(
|
||||
std::move(default_csl), std::move(block_default)));
|
||||
|
||||
auto block = std::make_unique<ast::BlockStatement>();
|
||||
block->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
|
||||
block->append(std::make_unique<ast::SwitchStatement>(std::move(cond),
|
||||
std::move(switch_body)));
|
||||
|
||||
EXPECT_TRUE(td()->DetermineStatements(block.get())) << td()->error();
|
||||
EXPECT_FALSE(v()->ValidateStatements(block.get()));
|
||||
EXPECT_EQ(v()->error(),
|
||||
"12:34: v-0026: the case selector values must have the same "
|
||||
"type as the selector expression.");
|
||||
}
|
||||
|
||||
TEST_F(ValidateControlBlockTest,
|
||||
SwitchConditionTypeMustMatchSelectorType_Fail) {
|
||||
// var a : u32 = 2;
|
||||
// switch (a) {
|
||||
// case -1: {}
|
||||
// default: {}
|
||||
// }
|
||||
ast::type::U32Type u32;
|
||||
ast::type::I32Type i32;
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &u32);
|
||||
var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::UintLiteral>(&u32, 2)));
|
||||
|
||||
ast::CaseStatementList switch_body;
|
||||
auto cond = std::make_unique<ast::IdentifierExpression>("a");
|
||||
|
||||
ast::CaseSelectorList csl;
|
||||
csl.push_back(std::make_unique<ast::SintLiteral>(&i32, -1));
|
||||
switch_body.push_back(std::make_unique<ast::CaseStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(csl),
|
||||
std::make_unique<ast::BlockStatement>()));
|
||||
|
||||
ast::CaseSelectorList default_csl;
|
||||
auto block_default = std::make_unique<ast::BlockStatement>();
|
||||
switch_body.push_back(std::make_unique<ast::CaseStatement>(
|
||||
std::move(default_csl), std::move(block_default)));
|
||||
|
||||
auto block = std::make_unique<ast::BlockStatement>();
|
||||
block->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
|
||||
block->append(std::make_unique<ast::SwitchStatement>(std::move(cond),
|
||||
std::move(switch_body)));
|
||||
|
||||
EXPECT_TRUE(td()->DetermineStatements(block.get())) << td()->error();
|
||||
EXPECT_FALSE(v()->ValidateStatements(block.get()));
|
||||
EXPECT_EQ(v()->error(),
|
||||
"12:34: v-0026: the case selector values must have the same "
|
||||
"type as the selector expression.");
|
||||
}
|
||||
|
||||
TEST_F(ValidateControlBlockTest, NonUniqueCaseSelectorValueUint_Fail) {
|
||||
// var a : u32 = 3;
|
||||
// switch (a) {
|
||||
// case 0: {}
|
||||
// case 2, 2: {}
|
||||
// default: {}
|
||||
// }
|
||||
ast::type::U32Type u32;
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &u32);
|
||||
var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::UintLiteral>(&u32, 3)));
|
||||
|
||||
ast::CaseStatementList switch_body;
|
||||
auto cond = std::make_unique<ast::IdentifierExpression>("a");
|
||||
|
||||
ast::CaseSelectorList csl_1;
|
||||
csl_1.push_back(std::make_unique<ast::UintLiteral>(&u32, 0));
|
||||
switch_body.push_back(std::make_unique<ast::CaseStatement>(
|
||||
std::move(csl_1), std::make_unique<ast::BlockStatement>()));
|
||||
|
||||
ast::CaseSelectorList csl_2;
|
||||
csl_2.push_back(std::make_unique<ast::UintLiteral>(&u32, 2));
|
||||
csl_2.push_back(std::make_unique<ast::UintLiteral>(&u32, 2));
|
||||
switch_body.push_back(std::make_unique<ast::CaseStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(csl_2),
|
||||
std::make_unique<ast::BlockStatement>()));
|
||||
|
||||
ast::CaseSelectorList default_csl;
|
||||
auto block_default = std::make_unique<ast::BlockStatement>();
|
||||
switch_body.push_back(std::make_unique<ast::CaseStatement>(
|
||||
std::move(default_csl), std::move(block_default)));
|
||||
|
||||
auto block = std::make_unique<ast::BlockStatement>();
|
||||
block->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
|
||||
block->append(std::make_unique<ast::SwitchStatement>(std::move(cond),
|
||||
std::move(switch_body)));
|
||||
|
||||
EXPECT_TRUE(td()->DetermineStatements(block.get())) << td()->error();
|
||||
EXPECT_FALSE(v()->ValidateStatements(block.get()));
|
||||
EXPECT_EQ(v()->error(),
|
||||
"12:34: v-0027: a literal value must not appear more than once "
|
||||
"in the case selectors for a switch statement: '2'");
|
||||
}
|
||||
|
||||
TEST_F(ValidateControlBlockTest, NonUniqueCaseSelectorValueSint_Fail) {
|
||||
// var a : i32 = 2;
|
||||
// switch (a) {
|
||||
// case 10: {}
|
||||
// case 0,1,2,10: {}
|
||||
// default: {}
|
||||
// }
|
||||
ast::type::I32Type i32;
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &i32);
|
||||
var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2)));
|
||||
|
||||
ast::CaseStatementList switch_body;
|
||||
auto cond = std::make_unique<ast::IdentifierExpression>("a");
|
||||
|
||||
ast::CaseSelectorList csl_1;
|
||||
csl_1.push_back(std::make_unique<ast::SintLiteral>(&i32, 10));
|
||||
switch_body.push_back(std::make_unique<ast::CaseStatement>(
|
||||
std::move(csl_1), std::make_unique<ast::BlockStatement>()));
|
||||
|
||||
ast::CaseSelectorList csl_2;
|
||||
csl_2.push_back(std::make_unique<ast::SintLiteral>(&i32, 0));
|
||||
csl_2.push_back(std::make_unique<ast::SintLiteral>(&i32, 1));
|
||||
csl_2.push_back(std::make_unique<ast::SintLiteral>(&i32, 2));
|
||||
csl_2.push_back(std::make_unique<ast::SintLiteral>(&i32, 10));
|
||||
switch_body.push_back(std::make_unique<ast::CaseStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(csl_2),
|
||||
std::make_unique<ast::BlockStatement>()));
|
||||
|
||||
ast::CaseSelectorList default_csl;
|
||||
auto block_default = std::make_unique<ast::BlockStatement>();
|
||||
switch_body.push_back(std::make_unique<ast::CaseStatement>(
|
||||
std::move(default_csl), std::move(block_default)));
|
||||
|
||||
auto block = std::make_unique<ast::BlockStatement>();
|
||||
block->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
|
||||
block->append(std::make_unique<ast::SwitchStatement>(std::move(cond),
|
||||
std::move(switch_body)));
|
||||
|
||||
EXPECT_TRUE(td()->DetermineStatements(block.get())) << td()->error();
|
||||
EXPECT_FALSE(v()->ValidateStatements(block.get()));
|
||||
EXPECT_EQ(v()->error(),
|
||||
"12:34: v-0027: a literal value must not appear more than once in "
|
||||
"the case selectors for a switch statement: '10'");
|
||||
}
|
||||
|
||||
TEST_F(ValidateControlBlockTest, LastClauseLastStatementIsFallthrough_Fail) {
|
||||
// var a : i32 = 2;
|
||||
// switch (a) {
|
||||
// default: { fallthrough; }
|
||||
// }
|
||||
ast::type::I32Type i32;
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &i32);
|
||||
var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2)));
|
||||
|
||||
auto cond = std::make_unique<ast::IdentifierExpression>("a");
|
||||
ast::CaseSelectorList default_csl;
|
||||
auto block_default = std::make_unique<ast::BlockStatement>();
|
||||
block_default->append(std::make_unique<ast::FallthroughStatement>(
|
||||
Source{Source::Location{12, 34}}));
|
||||
ast::CaseStatementList body;
|
||||
body.push_back(std::make_unique<ast::CaseStatement>(
|
||||
std::move(default_csl), std::move(block_default)));
|
||||
|
||||
auto block = std::make_unique<ast::BlockStatement>();
|
||||
block->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
|
||||
block->append(
|
||||
std::make_unique<ast::SwitchStatement>(std::move(cond), std::move(body)));
|
||||
|
||||
EXPECT_TRUE(td()->DetermineStatements(block.get())) << td()->error();
|
||||
EXPECT_FALSE(v()->ValidateStatements(block.get()));
|
||||
EXPECT_EQ(v()->error(),
|
||||
"12:34: v-0028: a fallthrough statement must not appear as the "
|
||||
"last statement in last clause of a switch");
|
||||
}
|
||||
|
||||
TEST_F(ValidateControlBlockTest, SwitchCase_Pass) {
|
||||
// var a : i32 = 2;
|
||||
// switch (a) {
|
||||
// default: {}
|
||||
// case 5: {}
|
||||
// }
|
||||
ast::type::I32Type i32;
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &i32);
|
||||
var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2)));
|
||||
|
||||
auto cond = std::make_unique<ast::IdentifierExpression>("a");
|
||||
ast::CaseSelectorList default_csl;
|
||||
auto block_default = std::make_unique<ast::BlockStatement>();
|
||||
ast::CaseStatementList body;
|
||||
body.push_back(std::make_unique<ast::CaseStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(default_csl),
|
||||
std::move(block_default)));
|
||||
ast::CaseSelectorList case_csl;
|
||||
case_csl.push_back(std::make_unique<ast::SintLiteral>(&i32, 5));
|
||||
auto block_case = std::make_unique<ast::BlockStatement>();
|
||||
body.push_back(std::make_unique<ast::CaseStatement>(std::move(case_csl),
|
||||
std::move(block_case)));
|
||||
|
||||
auto block = std::make_unique<ast::BlockStatement>();
|
||||
block->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
|
||||
block->append(
|
||||
std::make_unique<ast::SwitchStatement>(std::move(cond), std::move(body)));
|
||||
|
||||
EXPECT_TRUE(td()->DetermineStatements(block.get())) << td()->error();
|
||||
EXPECT_TRUE(v()->ValidateStatements(block.get())) << v()->error();
|
||||
}
|
||||
|
||||
TEST_F(ValidateControlBlockTest, SwitchCaseAlias_Pass) {
|
||||
// type MyInt = u32;
|
||||
// var v: MyInt;
|
||||
// switch(v){
|
||||
// default: {}
|
||||
// }
|
||||
|
||||
ast::type::U32Type u32;
|
||||
ast::type::AliasType my_int{"MyInt", &u32};
|
||||
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &my_int);
|
||||
var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&u32, 2)));
|
||||
|
||||
auto cond = std::make_unique<ast::IdentifierExpression>("a");
|
||||
ast::CaseSelectorList default_csl;
|
||||
auto block_default = std::make_unique<ast::BlockStatement>();
|
||||
ast::CaseStatementList body;
|
||||
body.push_back(std::make_unique<ast::CaseStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(default_csl),
|
||||
std::move(block_default)));
|
||||
|
||||
auto block = std::make_unique<ast::BlockStatement>();
|
||||
block->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
|
||||
block->append(
|
||||
std::make_unique<ast::SwitchStatement>(std::move(cond), std::move(body)));
|
||||
|
||||
mod()->AddConstructedType(&my_int);
|
||||
|
||||
EXPECT_TRUE(td()->DetermineStatements(block.get())) << td()->error();
|
||||
EXPECT_TRUE(v()->ValidateStatements(block.get())) << v()->error();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace tint
|
||||
374
src/validator/validator_function_test.cc
Normal file
374
src/validator/validator_function_test.cc
Normal file
@@ -0,0 +1,374 @@
|
||||
// Copyright 2020 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 "gtest/gtest.h"
|
||||
#include "spirv/unified1/GLSL.std.450.h"
|
||||
#include "src/ast/call_statement.h"
|
||||
#include "src/ast/pipeline_stage.h"
|
||||
#include "src/ast/return_statement.h"
|
||||
#include "src/ast/scalar_constructor_expression.h"
|
||||
#include "src/ast/sint_literal.h"
|
||||
#include "src/ast/stage_decoration.h"
|
||||
#include "src/ast/type/f32_type.h"
|
||||
#include "src/ast/type/i32_type.h"
|
||||
#include "src/ast/type/void_type.h"
|
||||
#include "src/ast/variable.h"
|
||||
#include "src/ast/variable_decl_statement.h"
|
||||
#include "src/type_determiner.h"
|
||||
#include "src/validator/validator_impl.h"
|
||||
#include "src/validator/validator_test_helper.h"
|
||||
|
||||
namespace tint {
|
||||
namespace {
|
||||
|
||||
class ValidateFunctionTest : public ValidatorTestHelper,
|
||||
public testing::Test {};
|
||||
|
||||
TEST_F(ValidateFunctionTest, FunctionEndWithoutReturnStatement_Fail) {
|
||||
// fn func -> void { var a:i32 = 2; }
|
||||
|
||||
ast::type::I32Type i32;
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &i32);
|
||||
var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2)));
|
||||
|
||||
ast::VariableList params;
|
||||
ast::type::VoidType void_type;
|
||||
auto func = std::make_unique<ast::Function>(
|
||||
Source{Source::Location{12, 34}}, "func", std::move(params), &void_type);
|
||||
auto body = std::make_unique<ast::BlockStatement>();
|
||||
body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
|
||||
func->set_body(std::move(body));
|
||||
mod()->AddFunction(std::move(func));
|
||||
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
EXPECT_FALSE(v()->Validate(mod()));
|
||||
EXPECT_EQ(v()->error(),
|
||||
"12:34: v-0002: function must end with a return statement");
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionTest, FunctionEndWithoutReturnStatementEmptyBody_Fail) {
|
||||
// fn func -> void {}
|
||||
ast::type::VoidType void_type;
|
||||
ast::VariableList params;
|
||||
auto func = std::make_unique<ast::Function>(
|
||||
Source{Source::Location{12, 34}}, "func", std::move(params), &void_type);
|
||||
mod()->AddFunction(std::move(func));
|
||||
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
EXPECT_FALSE(v()->Validate(mod()));
|
||||
EXPECT_EQ(v()->error(),
|
||||
"12:34: v-0002: function must end with a return statement");
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionTest, FunctionTypeMustMatchReturnStatementType_Pass) {
|
||||
// [[stage(vertex)]]
|
||||
// fn func -> void { return; }
|
||||
ast::type::VoidType void_type;
|
||||
ast::VariableList params;
|
||||
|
||||
auto func =
|
||||
std::make_unique<ast::Function>("func", std::move(params), &void_type);
|
||||
auto body = std::make_unique<ast::BlockStatement>();
|
||||
body->append(std::make_unique<ast::ReturnStatement>());
|
||||
func->set_body(std::move(body));
|
||||
func->add_decoration(std::make_unique<ast::StageDecoration>(
|
||||
ast::PipelineStage::kVertex, Source{}));
|
||||
mod()->AddFunction(std::move(func));
|
||||
|
||||
EXPECT_TRUE(td()->DetermineFunctions(mod()->functions())) << td()->error();
|
||||
EXPECT_TRUE(v()->ValidateFunctions(mod()->functions())) << v()->error();
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionTest, FunctionTypeMustMatchReturnStatementType_fail) {
|
||||
// fn func -> void { return 2; }
|
||||
ast::type::VoidType void_type;
|
||||
ast::type::I32Type i32;
|
||||
ast::VariableList params;
|
||||
auto func =
|
||||
std::make_unique<ast::Function>("func", std::move(params), &void_type);
|
||||
auto body = std::make_unique<ast::BlockStatement>();
|
||||
auto return_expr = std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2));
|
||||
|
||||
body->append(std::make_unique<ast::ReturnStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(return_expr)));
|
||||
func->set_body(std::move(body));
|
||||
mod()->AddFunction(std::move(func));
|
||||
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
EXPECT_FALSE(v()->Validate(mod()));
|
||||
// TODO(sarahM0): replace 000y with a rule number
|
||||
EXPECT_EQ(v()->error(),
|
||||
"12:34: v-000y: return statement type must match its function "
|
||||
"return type, returned '__i32', expected '__void'");
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionTest, FunctionTypeMustMatchReturnStatementTypeF32_fail) {
|
||||
// fn func -> f32 { return 2; }
|
||||
ast::type::I32Type i32;
|
||||
ast::type::F32Type f32;
|
||||
ast::VariableList params;
|
||||
auto func = std::make_unique<ast::Function>("func", std::move(params), &f32);
|
||||
auto body = std::make_unique<ast::BlockStatement>();
|
||||
auto return_expr = std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2));
|
||||
|
||||
body->append(std::make_unique<ast::ReturnStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(return_expr)));
|
||||
func->set_body(std::move(body));
|
||||
mod()->AddFunction(std::move(func));
|
||||
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
EXPECT_FALSE(v()->Validate(mod()));
|
||||
// TODO(sarahM0): replace 000y with a rule number
|
||||
EXPECT_EQ(v()->error(),
|
||||
"12:34: v-000y: return statement type must match its function "
|
||||
"return type, returned '__i32', expected '__f32'");
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionTest, FunctionNamesMustBeUnique_fail) {
|
||||
// fn func -> i32 { return 2; }
|
||||
// fn func -> i32 { return 2; }
|
||||
ast::type::VoidType void_type;
|
||||
ast::type::I32Type i32;
|
||||
|
||||
ast::VariableList params;
|
||||
auto func = std::make_unique<ast::Function>("func", std::move(params), &i32);
|
||||
auto body = std::make_unique<ast::BlockStatement>();
|
||||
auto return_expr = std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2));
|
||||
|
||||
body->append(std::make_unique<ast::ReturnStatement>(std::move(return_expr)));
|
||||
func->set_body(std::move(body));
|
||||
|
||||
ast::VariableList params_copy;
|
||||
auto func_copy = std::make_unique<ast::Function>(
|
||||
Source{Source::Location{12, 34}}, "func", std::move(params_copy), &i32);
|
||||
auto body_copy = std::make_unique<ast::BlockStatement>();
|
||||
auto return_expr_copy = std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2));
|
||||
|
||||
body_copy->append(
|
||||
std::make_unique<ast::ReturnStatement>(std::move(return_expr_copy)));
|
||||
func_copy->set_body(std::move(body_copy));
|
||||
|
||||
mod()->AddFunction(std::move(func));
|
||||
mod()->AddFunction(std::move(func_copy));
|
||||
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
EXPECT_FALSE(v()->Validate(mod()));
|
||||
EXPECT_EQ(v()->error(),
|
||||
"12:34: v-0016: function names must be unique 'func'");
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionTest, RecursionIsNotAllowed_Fail) {
|
||||
// fn func() -> void {func(); return; }
|
||||
ast::type::F32Type f32;
|
||||
ast::type::VoidType void_type;
|
||||
ast::ExpressionList call_params;
|
||||
auto call_expr = std::make_unique<ast::CallExpression>(
|
||||
Source{Source::Location{12, 34}},
|
||||
std::make_unique<ast::IdentifierExpression>("func"),
|
||||
std::move(call_params));
|
||||
ast::VariableList params0;
|
||||
auto func0 =
|
||||
std::make_unique<ast::Function>("func", std::move(params0), &f32);
|
||||
auto body0 = std::make_unique<ast::BlockStatement>();
|
||||
body0->append(std::make_unique<ast::CallStatement>(std::move(call_expr)));
|
||||
body0->append(std::make_unique<ast::ReturnStatement>());
|
||||
func0->set_body(std::move(body0));
|
||||
mod()->AddFunction(std::move(func0));
|
||||
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
EXPECT_FALSE(v()->Validate(mod())) << v()->error();
|
||||
EXPECT_EQ(v()->error(), "12:34: v-0004: recursion is not allowed: 'func'");
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionTest, RecursionIsNotAllowedExpr_Fail) {
|
||||
// fn func() -> i32 {var a: i32 = func(); return 2; }
|
||||
ast::type::I32Type i32;
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &i32);
|
||||
ast::ExpressionList call_params;
|
||||
auto call_expr = std::make_unique<ast::CallExpression>(
|
||||
Source{Source::Location{12, 34}},
|
||||
std::make_unique<ast::IdentifierExpression>("func"),
|
||||
std::move(call_params));
|
||||
var->set_constructor(std::move(call_expr));
|
||||
ast::VariableList params0;
|
||||
auto func0 =
|
||||
std::make_unique<ast::Function>("func", std::move(params0), &i32);
|
||||
auto body0 = std::make_unique<ast::BlockStatement>();
|
||||
body0->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
|
||||
auto return_expr = std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2));
|
||||
|
||||
body0->append(std::make_unique<ast::ReturnStatement>(std::move(return_expr)));
|
||||
func0->set_body(std::move(body0));
|
||||
mod()->AddFunction(std::move(func0));
|
||||
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
EXPECT_FALSE(v()->Validate(mod())) << v()->error();
|
||||
EXPECT_EQ(v()->error(), "12:34: v-0004: recursion is not allowed: 'func'");
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionTest, Function_WithPipelineStage_NotVoid_Fail) {
|
||||
// [[stage(vertex)]]
|
||||
// fn vtx_main() -> i32 { return 0; }
|
||||
ast::type::I32Type i32;
|
||||
ast::VariableList params;
|
||||
auto func = std::make_unique<ast::Function>(
|
||||
Source{Source::Location{12, 34}}, "vtx_main", std::move(params), &i32);
|
||||
auto return_expr = std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 0));
|
||||
|
||||
auto body = std::make_unique<ast::BlockStatement>();
|
||||
body->append(std::make_unique<ast::ReturnStatement>(std::move(return_expr)));
|
||||
func->set_body(std::move(body));
|
||||
func->add_decoration(std::make_unique<ast::StageDecoration>(
|
||||
ast::PipelineStage::kVertex, Source{}));
|
||||
|
||||
mod()->AddFunction(std::move(func));
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
EXPECT_FALSE(v()->Validate(mod()));
|
||||
EXPECT_EQ(v()->error(),
|
||||
"12:34: v-0024: Entry point function must return void: 'vtx_main'");
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionTest, Function_WithPipelineStage_WithParams_Fail) {
|
||||
// [[stage(vertex)]]
|
||||
// fn vtx_func(a : i32) -> void { return; }
|
||||
ast::type::I32Type i32;
|
||||
ast::type::VoidType void_type;
|
||||
ast::VariableList params;
|
||||
params.push_back(
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &i32));
|
||||
auto func = std::make_unique<ast::Function>(Source{Source::Location{12, 34}},
|
||||
"vtx_func", std::move(params),
|
||||
&void_type);
|
||||
auto body = std::make_unique<ast::BlockStatement>();
|
||||
body->append(std::make_unique<ast::ReturnStatement>());
|
||||
func->set_body(std::move(body));
|
||||
func->add_decoration(std::make_unique<ast::StageDecoration>(
|
||||
ast::PipelineStage::kVertex, Source{}));
|
||||
|
||||
mod()->AddFunction(std::move(func));
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
EXPECT_FALSE(v()->Validate(mod()));
|
||||
EXPECT_EQ(v()->error(),
|
||||
"12:34: v-0023: Entry point function must accept no parameters: "
|
||||
"'vtx_func'");
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionTest, PipelineStageNamePair_MustBeUnique_Fail) {
|
||||
// [[stage(vertex)]]
|
||||
// fn main() -> void { return ;}
|
||||
// [[stage(vertex)]]
|
||||
// fn main() -> void { return; }
|
||||
ast::type::VoidType void_type;
|
||||
ast::VariableList params;
|
||||
auto func = std::make_unique<ast::Function>(
|
||||
Source{Source::Location{5, 6}}, "main", std::move(params), &void_type);
|
||||
auto body = std::make_unique<ast::BlockStatement>();
|
||||
body->append(std::make_unique<ast::ReturnStatement>());
|
||||
func->set_body(std::move(body));
|
||||
func->add_decoration(std::make_unique<ast::StageDecoration>(
|
||||
ast::PipelineStage::kVertex, Source{}));
|
||||
mod()->AddFunction(std::move(func));
|
||||
|
||||
func = std::make_unique<ast::Function>(Source{Source::Location{12, 34}},
|
||||
"main", std::move(params), &void_type);
|
||||
body = std::make_unique<ast::BlockStatement>();
|
||||
body->append(std::make_unique<ast::ReturnStatement>());
|
||||
func->set_body(std::move(body));
|
||||
func->add_decoration(std::make_unique<ast::StageDecoration>(
|
||||
ast::PipelineStage::kVertex, Source{}));
|
||||
mod()->AddFunction(std::move(func));
|
||||
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
EXPECT_FALSE(v()->Validate(mod()));
|
||||
EXPECT_EQ(v()->error(),
|
||||
"12:34: v-0020: The pair of <entry point name, pipeline stage> "
|
||||
"must be unique");
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionTest, PipelineStageNamePair_MustBeUnique_Pass) {
|
||||
// [[stage(vertex)]]
|
||||
// fn main() -> void { return; }
|
||||
// [[stage(fragment)]]
|
||||
// fn main() -> void { return; }
|
||||
ast::type::VoidType void_type;
|
||||
ast::VariableList params;
|
||||
auto func = std::make_unique<ast::Function>(
|
||||
Source{Source::Location{5, 6}}, "main", std::move(params), &void_type);
|
||||
auto body = std::make_unique<ast::BlockStatement>();
|
||||
body->append(std::make_unique<ast::ReturnStatement>());
|
||||
func->set_body(std::move(body));
|
||||
func->add_decoration(std::make_unique<ast::StageDecoration>(
|
||||
ast::PipelineStage::kVertex, Source{}));
|
||||
mod()->AddFunction(std::move(func));
|
||||
|
||||
func = std::make_unique<ast::Function>(Source{Source::Location{12, 34}},
|
||||
"main", std::move(params), &void_type);
|
||||
body = std::make_unique<ast::BlockStatement>();
|
||||
body->append(std::make_unique<ast::ReturnStatement>());
|
||||
func->set_body(std::move(body));
|
||||
func->add_decoration(std::make_unique<ast::StageDecoration>(
|
||||
ast::PipelineStage::kFragment, Source{}));
|
||||
mod()->AddFunction(std::move(func));
|
||||
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
EXPECT_TRUE(v()->Validate(mod())) << v()->error();
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionTest, OnePipelineStageFunctionMustBePresent_Pass) {
|
||||
// [[stage(vertex)]]
|
||||
// fn vtx_func() -> void { return; }
|
||||
ast::type::VoidType void_type;
|
||||
ast::VariableList params;
|
||||
auto func = std::make_unique<ast::Function>("vtx_func", std::move(params),
|
||||
&void_type);
|
||||
auto body = std::make_unique<ast::BlockStatement>();
|
||||
body->append(std::make_unique<ast::ReturnStatement>());
|
||||
func->set_body(std::move(body));
|
||||
func->add_decoration(std::make_unique<ast::StageDecoration>(
|
||||
ast::PipelineStage::kVertex, Source{}));
|
||||
mod()->AddFunction(std::move(func));
|
||||
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
EXPECT_TRUE(v()->Validate(mod())) << v()->error();
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionTest, OnePipelineStageFunctionMustBePresent_Fail) {
|
||||
// fn vtx_func() -> void { return; }
|
||||
ast::type::VoidType void_type;
|
||||
ast::VariableList params;
|
||||
auto func = std::make_unique<ast::Function>("vtx_func", std::move(params),
|
||||
&void_type);
|
||||
auto body = std::make_unique<ast::BlockStatement>();
|
||||
body->append(std::make_unique<ast::ReturnStatement>());
|
||||
func->set_body(std::move(body));
|
||||
mod()->AddFunction(std::move(func));
|
||||
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
EXPECT_FALSE(v()->Validate(mod()));
|
||||
EXPECT_EQ(v()->error(),
|
||||
"0:0: v-0003: At least one of vertex, fragment or compute shader "
|
||||
"must be present");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace tint
|
||||
431
src/validator/validator_impl.cc
Normal file
431
src/validator/validator_impl.cc
Normal file
@@ -0,0 +1,431 @@
|
||||
// Copyright 2020 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/validator/validator_impl.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "src/ast/call_statement.h"
|
||||
#include "src/ast/function.h"
|
||||
#include "src/ast/int_literal.h"
|
||||
#include "src/ast/intrinsic.h"
|
||||
#include "src/ast/sint_literal.h"
|
||||
#include "src/ast/switch_statement.h"
|
||||
#include "src/ast/type/i32_type.h"
|
||||
#include "src/ast/type/u32_type.h"
|
||||
#include "src/ast/type/void_type.h"
|
||||
#include "src/ast/uint_literal.h"
|
||||
#include "src/ast/variable_decl_statement.h"
|
||||
|
||||
namespace tint {
|
||||
|
||||
ValidatorImpl::ValidatorImpl() = default;
|
||||
|
||||
ValidatorImpl::~ValidatorImpl() = default;
|
||||
|
||||
void ValidatorImpl::set_error(const Source& src, const std::string& msg) {
|
||||
error_ += std::to_string(src.range.begin.line) + ":" +
|
||||
std::to_string(src.range.begin.column) + ": " + msg;
|
||||
}
|
||||
|
||||
bool ValidatorImpl::Validate(const ast::Module* module) {
|
||||
if (!module) {
|
||||
return false;
|
||||
}
|
||||
function_stack_.push_scope();
|
||||
if (!ValidateGlobalVariables(module->global_variables())) {
|
||||
return false;
|
||||
}
|
||||
if (!ValidateFunctions(module->functions())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function_stack_.pop_scope();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidatorImpl::ValidateGlobalVariables(
|
||||
const ast::VariableList& global_vars) {
|
||||
for (const auto& var : global_vars) {
|
||||
if (variable_stack_.has(var->name())) {
|
||||
set_error(var->source(),
|
||||
"v-0011: redeclared global identifier '" + var->name() + "'");
|
||||
return false;
|
||||
}
|
||||
if (!var->is_const() && var->storage_class() == ast::StorageClass::kNone) {
|
||||
set_error(var->source(),
|
||||
"v-0022: global variables must have a storage class");
|
||||
return false;
|
||||
}
|
||||
if (var->is_const() &&
|
||||
!(var->storage_class() == ast::StorageClass::kNone)) {
|
||||
set_error(var->source(),
|
||||
"v-global01: global constants shouldn't have a storage class");
|
||||
return false;
|
||||
}
|
||||
variable_stack_.set_global(var->name(), var.get());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidatorImpl::ValidateFunctions(const ast::FunctionList& funcs) {
|
||||
ScopeStack<ast::PipelineStage> entry_point_map;
|
||||
entry_point_map.push_scope();
|
||||
|
||||
size_t pipeline_count = 0;
|
||||
for (const auto& func : funcs) {
|
||||
// The entry points will be checked later to see if their duplicated
|
||||
if (function_stack_.has(func->name()) &&
|
||||
!entry_point_map.has(func->name())) {
|
||||
set_error(func->source(),
|
||||
"v-0016: function names must be unique '" + func->name() + "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (func->IsEntryPoint()) {
|
||||
pipeline_count++;
|
||||
|
||||
if (!func->return_type()->IsVoid()) {
|
||||
set_error(func->source(),
|
||||
"v-0024: Entry point function must return void: '" +
|
||||
func->name() + "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (func->params().size() != 0) {
|
||||
set_error(func->source(),
|
||||
"v-0023: Entry point function must accept no parameters: '" +
|
||||
func->name() + "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
ast::PipelineStage pipeline_stage;
|
||||
if (entry_point_map.get(func->name(), &pipeline_stage)) {
|
||||
if (pipeline_stage == func->pipeline_stage()) {
|
||||
set_error(
|
||||
func->source(),
|
||||
"v-0020: The pair of <entry point name, pipeline stage> must "
|
||||
"be unique");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
entry_point_map.set(func->name(), func->pipeline_stage());
|
||||
}
|
||||
|
||||
function_stack_.set(func->name(), func.get());
|
||||
current_function_ = func.get();
|
||||
if (!ValidateFunction(func.get())) {
|
||||
return false;
|
||||
}
|
||||
current_function_ = nullptr;
|
||||
}
|
||||
|
||||
if (pipeline_count == 0) {
|
||||
set_error(Source{},
|
||||
"v-0003: At least one of vertex, fragment or compute shader must "
|
||||
"be present");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidatorImpl::ValidateFunction(const ast::Function* func) {
|
||||
variable_stack_.push_scope();
|
||||
|
||||
for (const auto& param : func->params()) {
|
||||
variable_stack_.set(param->name(), param.get());
|
||||
}
|
||||
if (!ValidateStatements(func->body())) {
|
||||
return false;
|
||||
}
|
||||
variable_stack_.pop_scope();
|
||||
|
||||
if (!func->get_last_statement() || !func->get_last_statement()->IsReturn()) {
|
||||
set_error(func->source(),
|
||||
"v-0002: function must end with a return statement");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidatorImpl::ValidateReturnStatement(const ast::ReturnStatement* ret) {
|
||||
// TODO(sarahM0): update this when this issue resolves:
|
||||
// https://github.com/gpuweb/gpuweb/issues/996
|
||||
ast::type::Type* func_type = current_function_->return_type();
|
||||
|
||||
ast::type::VoidType void_type;
|
||||
auto* ret_type =
|
||||
ret->has_value() ? ret->value()->result_type()->UnwrapAll() : &void_type;
|
||||
|
||||
if (func_type->type_name() != ret_type->type_name()) {
|
||||
set_error(ret->source(),
|
||||
"v-000y: return statement type must match its function return "
|
||||
"type, returned '" +
|
||||
ret_type->type_name() + "', expected '" +
|
||||
func_type->type_name() + "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidatorImpl::ValidateStatements(const ast::BlockStatement* block) {
|
||||
if (!block) {
|
||||
return false;
|
||||
}
|
||||
for (const auto& stmt : *block) {
|
||||
if (!ValidateStatement(stmt.get())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidatorImpl::ValidateDeclStatement(
|
||||
const ast::VariableDeclStatement* decl) {
|
||||
auto name = decl->variable()->name();
|
||||
bool is_global = false;
|
||||
if (variable_stack_.get(name, nullptr, &is_global)) {
|
||||
std::string error_number = "v-0014: ";
|
||||
if (is_global) {
|
||||
error_number = "v-0013: ";
|
||||
}
|
||||
set_error(decl->source(),
|
||||
error_number + "redeclared identifier '" + name + "'");
|
||||
return false;
|
||||
}
|
||||
variable_stack_.set(name, decl->variable());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidatorImpl::ValidateStatement(const ast::Statement* stmt) {
|
||||
if (!stmt) {
|
||||
return false;
|
||||
}
|
||||
if (stmt->IsVariableDecl()) {
|
||||
auto* v = stmt->AsVariableDecl();
|
||||
bool constructor_valid =
|
||||
v->variable()->has_constructor()
|
||||
? ValidateExpression(v->variable()->constructor())
|
||||
: true;
|
||||
|
||||
return constructor_valid && ValidateDeclStatement(stmt->AsVariableDecl());
|
||||
}
|
||||
if (stmt->IsAssign()) {
|
||||
return ValidateAssign(stmt->AsAssign());
|
||||
}
|
||||
if (stmt->IsReturn()) {
|
||||
return ValidateReturnStatement(stmt->AsReturn());
|
||||
}
|
||||
if (stmt->IsCall()) {
|
||||
return ValidateCallExpr(stmt->AsCall()->expr());
|
||||
}
|
||||
if (stmt->IsSwitch()) {
|
||||
return ValidateSwitch(stmt->AsSwitch());
|
||||
}
|
||||
if (stmt->IsCase()) {
|
||||
return ValidateCase(stmt->AsCase());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidatorImpl::ValidateSwitch(const ast::SwitchStatement* s) {
|
||||
if (!ValidateExpression(s->condition())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* cond_type = s->condition()->result_type()->UnwrapAll();
|
||||
if (!(cond_type->IsI32() || cond_type->IsU32())) {
|
||||
set_error(s->condition()->source(),
|
||||
"v-0025: switch statement selector expression must be of a "
|
||||
"scalar integer type");
|
||||
return false;
|
||||
}
|
||||
|
||||
int default_counter = 0;
|
||||
std::unordered_set<int32_t> selector_set;
|
||||
for (const auto& case_stmt : s->body()) {
|
||||
if (!ValidateStatement(case_stmt.get())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (case_stmt.get()->IsDefault()) {
|
||||
default_counter++;
|
||||
}
|
||||
|
||||
for (const auto& selector : case_stmt.get()->selectors()) {
|
||||
auto* selector_ptr = selector.get();
|
||||
if (cond_type != selector_ptr->type()) {
|
||||
set_error(case_stmt.get()->source(),
|
||||
"v-0026: the case selector values must have the same "
|
||||
"type as the selector expression.");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto v = static_cast<int32_t>(selector_ptr->type()->IsU32()
|
||||
? selector_ptr->AsUint()->value()
|
||||
: selector_ptr->AsSint()->value());
|
||||
if (selector_set.count(v)) {
|
||||
auto v_str = selector_ptr->type()->IsU32()
|
||||
? selector_ptr->AsUint()->to_str()
|
||||
: selector_ptr->AsSint()->to_str();
|
||||
set_error(case_stmt.get()->source(),
|
||||
"v-0027: a literal value must not appear more than once in "
|
||||
"the case selectors for a switch statement: '" +
|
||||
v_str + "'");
|
||||
return false;
|
||||
}
|
||||
selector_set.emplace(v);
|
||||
}
|
||||
}
|
||||
|
||||
if (default_counter != 1) {
|
||||
set_error(s->source(),
|
||||
"v-0008: switch statement must have exactly one default clause");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* last_clause = s->body().back().get();
|
||||
auto* last_stmt_of_last_clause = last_clause->AsCase()->body()->last();
|
||||
if (last_stmt_of_last_clause && last_stmt_of_last_clause->IsFallthrough()) {
|
||||
set_error(last_stmt_of_last_clause->source(),
|
||||
"v-0028: a fallthrough statement must not appear as "
|
||||
"the last statement in last clause of a switch");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidatorImpl::ValidateCase(const ast::CaseStatement* c) {
|
||||
if (!ValidateStatement(c->body())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidatorImpl::ValidateCallExpr(const ast::CallExpression* expr) {
|
||||
if (!expr) {
|
||||
// TODO(sarahM0): Here and other Validate.*: figure out whether return
|
||||
// false or true
|
||||
return false;
|
||||
}
|
||||
|
||||
if (expr->func()->IsIdentifier()) {
|
||||
auto* ident = expr->func()->AsIdentifier();
|
||||
auto func_name = ident->name();
|
||||
if (ident->IsIntrinsic()) {
|
||||
// TODO(sarahM0): validate intrinsics - tied with type-determiner
|
||||
} else {
|
||||
if (!function_stack_.has(func_name)) {
|
||||
set_error(expr->source(),
|
||||
"v-0005: function must be declared before use: '" +
|
||||
func_name + "'");
|
||||
return false;
|
||||
}
|
||||
if (func_name == current_function_->name()) {
|
||||
set_error(expr->source(),
|
||||
"v-0004: recursion is not allowed: '" + func_name + "'");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
set_error(expr->source(), "Invalid function call expression");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidatorImpl::ValidateAssign(const ast::AssignmentStatement* a) {
|
||||
if (!a) {
|
||||
return false;
|
||||
}
|
||||
if (!(ValidateConstant(a))) {
|
||||
return false;
|
||||
}
|
||||
if (!(ValidateExpression(a->lhs()) && ValidateExpression(a->rhs()))) {
|
||||
return false;
|
||||
}
|
||||
if (!ValidateResultTypes(a)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidatorImpl::ValidateConstant(const ast::AssignmentStatement* assign) {
|
||||
if (!assign) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (assign->lhs()->IsIdentifier()) {
|
||||
ast::Variable* var;
|
||||
auto* ident = assign->lhs()->AsIdentifier();
|
||||
if (variable_stack_.get(ident->name(), &var)) {
|
||||
if (var->is_const()) {
|
||||
set_error(assign->source(), "v-0021: cannot re-assign a constant: '" +
|
||||
ident->name() + "'");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidatorImpl::ValidateResultTypes(const ast::AssignmentStatement* a) {
|
||||
if (!a->lhs()->result_type() || !a->rhs()->result_type()) {
|
||||
set_error(a->source(), "result_type() is nullptr");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* lhs_result_type = a->lhs()->result_type()->UnwrapAll();
|
||||
auto* rhs_result_type = a->rhs()->result_type()->UnwrapAll();
|
||||
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;
|
||||
}
|
||||
|
||||
bool ValidatorImpl::ValidateExpression(const ast::Expression* expr) {
|
||||
if (!expr) {
|
||||
return false;
|
||||
}
|
||||
if (expr->IsIdentifier()) {
|
||||
return ValidateIdentifier(expr->AsIdentifier());
|
||||
}
|
||||
|
||||
if (expr->IsCall()) {
|
||||
return ValidateCallExpr(expr->AsCall());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidatorImpl::ValidateIdentifier(const ast::IdentifierExpression* ident) {
|
||||
ast::Variable* var;
|
||||
if (!variable_stack_.get(ident->name(), &var)) {
|
||||
set_error(ident->source(),
|
||||
"v-0006: '" + ident->name() + "' is not declared");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace tint
|
||||
126
src/validator/validator_impl.h
Normal file
126
src/validator/validator_impl.h
Normal file
@@ -0,0 +1,126 @@
|
||||
// Copyright 2020 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.
|
||||
|
||||
#ifndef SRC_VALIDATOR_VALIDATOR_IMPL_H_
|
||||
#define SRC_VALIDATOR_VALIDATOR_IMPL_H_
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "src/ast/assignment_statement.h"
|
||||
#include "src/ast/call_expression.h"
|
||||
#include "src/ast/expression.h"
|
||||
#include "src/ast/identifier_expression.h"
|
||||
#include "src/ast/module.h"
|
||||
#include "src/ast/return_statement.h"
|
||||
#include "src/ast/statement.h"
|
||||
#include "src/ast/variable.h"
|
||||
#include "src/scope_stack.h"
|
||||
|
||||
namespace tint {
|
||||
|
||||
/// Determines if the module is complete and valid
|
||||
class ValidatorImpl {
|
||||
public:
|
||||
/// Constructor
|
||||
ValidatorImpl();
|
||||
~ValidatorImpl();
|
||||
|
||||
/// Runs the validator
|
||||
/// @param module the module to validate
|
||||
/// @returns true if the validation was successful
|
||||
bool Validate(const ast::Module* module);
|
||||
|
||||
/// @returns error messages from the validator
|
||||
const std::string& error() { return error_; }
|
||||
|
||||
/// @returns true if an error was encountered
|
||||
bool has_error() const { return error_.size() > 0; }
|
||||
|
||||
/// Sets the error string
|
||||
/// @param src the source causing the error
|
||||
/// @param msg the error message
|
||||
void set_error(const Source& src, const std::string& msg);
|
||||
/// Validate global variables
|
||||
/// @param global_vars list of global variables to check
|
||||
/// @returns true if the validation was successful
|
||||
bool ValidateGlobalVariables(const ast::VariableList& global_vars);
|
||||
/// 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 block of statements
|
||||
/// @param block the statements to check
|
||||
/// @returns true if the validation was successful
|
||||
bool ValidateStatements(const ast::BlockStatement* block);
|
||||
/// 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 assign the assignment to check
|
||||
/// @returns true if the validation was successful
|
||||
bool ValidateAssign(const ast::AssignmentStatement* assign);
|
||||
/// Validates an expression
|
||||
/// @param expr the expression to check
|
||||
/// @return true if the expression is valid
|
||||
bool ValidateExpression(const ast::Expression* expr);
|
||||
/// Validates v-0006:Variables must be defined before use
|
||||
/// @param ident the identifer to check if its in the scope
|
||||
/// @return true if idnet was defined
|
||||
bool ValidateIdentifier(const ast::IdentifierExpression* ident);
|
||||
/// Validates if the input follows type checking rules
|
||||
/// @param assign the assignment to check
|
||||
/// @returns ture if successful
|
||||
bool ValidateResultTypes(const ast::AssignmentStatement* assign);
|
||||
/// Validate v-0021: Cannot re-assign a constant
|
||||
/// @param assign is the assigment to check if its lhs is a const
|
||||
/// @returns false if lhs of assign is a constant identifier
|
||||
bool ValidateConstant(const ast::AssignmentStatement* assign);
|
||||
/// Validates declaration name uniquness
|
||||
/// @param decl is the new declartion to be added
|
||||
/// @returns true if no previous decleration with the |decl|'s name
|
||||
/// exist in the variable stack
|
||||
bool ValidateDeclStatement(const ast::VariableDeclStatement* decl);
|
||||
/// Validates return statement
|
||||
/// @param ret the return statement to check
|
||||
/// @returns true if function return type matches the return statement type
|
||||
bool ValidateReturnStatement(const ast::ReturnStatement* ret);
|
||||
/// Validates function calls
|
||||
/// @param expr the call to validate
|
||||
/// @returns true if successful
|
||||
bool ValidateCallExpr(const ast::CallExpression* expr);
|
||||
/// Validates switch statements
|
||||
/// @param s the switch statement to check
|
||||
/// @returns true if the valdiation was successful
|
||||
bool ValidateSwitch(const ast::SwitchStatement* s);
|
||||
/// Validates case statements
|
||||
/// @param c the case statement to check
|
||||
/// @returns true if the valdiation was successful
|
||||
bool ValidateCase(const ast::CaseStatement* c);
|
||||
|
||||
private:
|
||||
std::string error_;
|
||||
ScopeStack<ast::Variable*> variable_stack_;
|
||||
ScopeStack<ast::Function*> function_stack_;
|
||||
ast::Function* current_function_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace tint
|
||||
|
||||
#endif // SRC_VALIDATOR_VALIDATOR_IMPL_H_
|
||||
715
src/validator/validator_test.cc
Normal file
715
src/validator/validator_test.cc
Normal file
@@ -0,0 +1,715 @@
|
||||
// Copyright 2020 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 "gtest/gtest.h"
|
||||
#include "spirv/unified1/GLSL.std.450.h"
|
||||
#include "src/ast/array_accessor_expression.h"
|
||||
#include "src/ast/assignment_statement.h"
|
||||
#include "src/ast/binary_expression.h"
|
||||
#include "src/ast/bool_literal.h"
|
||||
#include "src/ast/break_statement.h"
|
||||
#include "src/ast/call_expression.h"
|
||||
#include "src/ast/call_statement.h"
|
||||
#include "src/ast/case_statement.h"
|
||||
#include "src/ast/continue_statement.h"
|
||||
#include "src/ast/else_statement.h"
|
||||
#include "src/ast/float_literal.h"
|
||||
#include "src/ast/identifier_expression.h"
|
||||
#include "src/ast/if_statement.h"
|
||||
#include "src/ast/int_literal.h"
|
||||
#include "src/ast/loop_statement.h"
|
||||
#include "src/ast/member_accessor_expression.h"
|
||||
#include "src/ast/pipeline_stage.h"
|
||||
#include "src/ast/return_statement.h"
|
||||
#include "src/ast/scalar_constructor_expression.h"
|
||||
#include "src/ast/sint_literal.h"
|
||||
#include "src/ast/stage_decoration.h"
|
||||
#include "src/ast/struct.h"
|
||||
#include "src/ast/struct_member.h"
|
||||
#include "src/ast/switch_statement.h"
|
||||
#include "src/ast/type/alias_type.h"
|
||||
#include "src/ast/type/array_type.h"
|
||||
#include "src/ast/type/bool_type.h"
|
||||
#include "src/ast/type/f32_type.h"
|
||||
#include "src/ast/type/i32_type.h"
|
||||
#include "src/ast/type/matrix_type.h"
|
||||
#include "src/ast/type/pointer_type.h"
|
||||
#include "src/ast/type/struct_type.h"
|
||||
#include "src/ast/type/vector_type.h"
|
||||
#include "src/ast/type/void_type.h"
|
||||
#include "src/ast/type_constructor_expression.h"
|
||||
#include "src/ast/variable.h"
|
||||
#include "src/ast/variable_decl_statement.h"
|
||||
#include "src/type_determiner.h"
|
||||
#include "src/validator/validator_impl.h"
|
||||
#include "src/validator/validator_test_helper.h"
|
||||
|
||||
namespace tint {
|
||||
namespace {
|
||||
|
||||
class ValidatorTest : public ValidatorTestHelper, public testing::Test {};
|
||||
|
||||
TEST_F(ValidatorTest, DISABLED_AssignToScalar_Fail) {
|
||||
// 1 = my_var;
|
||||
ast::type::I32Type i32;
|
||||
|
||||
auto lhs = std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 1));
|
||||
auto rhs = std::make_unique<ast::IdentifierExpression>("my_var");
|
||||
ast::AssignmentStatement assign(Source{Source::Location{12, 34}},
|
||||
std::move(lhs), std::move(rhs));
|
||||
|
||||
// TODO(sarahM0): Invalidate assignment to scalar.
|
||||
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");
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, UsingUndefinedVariable_Fail) {
|
||||
// b = 2;
|
||||
ast::type::I32Type i32;
|
||||
|
||||
auto lhs = std::make_unique<ast::IdentifierExpression>(
|
||||
Source{Source::Location{12, 34}}, "b");
|
||||
auto rhs = std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2));
|
||||
auto assign = std::make_unique<ast::AssignmentStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(lhs), std::move(rhs));
|
||||
|
||||
EXPECT_FALSE(td()->DetermineResultType(assign.get()));
|
||||
EXPECT_EQ(td()->error(),
|
||||
"12:34: v-0006: identifier must be declared before use: b");
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, UsingUndefinedVariableInBlockStatement_Fail) {
|
||||
// {
|
||||
// b = 2;
|
||||
// }
|
||||
ast::type::I32Type i32;
|
||||
|
||||
auto lhs = std::make_unique<ast::IdentifierExpression>(
|
||||
Source{Source::Location{12, 34}}, "b");
|
||||
auto rhs = std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2));
|
||||
|
||||
auto body = std::make_unique<ast::BlockStatement>();
|
||||
body->append(std::make_unique<ast::AssignmentStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(lhs), std::move(rhs)));
|
||||
|
||||
EXPECT_FALSE(td()->DetermineStatements(body.get()));
|
||||
EXPECT_EQ(td()->error(),
|
||||
"12:34: v-0006: identifier must be declared before use: b");
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, AssignCompatibleTypes_Pass) {
|
||||
// var a :i32 = 2;
|
||||
// a = 2
|
||||
ast::type::I32Type i32;
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &i32);
|
||||
var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2)));
|
||||
|
||||
auto lhs = std::make_unique<ast::IdentifierExpression>("a");
|
||||
auto* lhs_ptr = lhs.get();
|
||||
auto rhs = std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2));
|
||||
auto* rhs_ptr = rhs.get();
|
||||
|
||||
ast::AssignmentStatement assign(Source{Source::Location{12, 34}},
|
||||
std::move(lhs), std::move(rhs));
|
||||
td()->RegisterVariableForTesting(var.get());
|
||||
EXPECT_TRUE(td()->DetermineResultType(&assign)) << td()->error();
|
||||
ASSERT_NE(lhs_ptr->result_type(), nullptr);
|
||||
ASSERT_NE(rhs_ptr->result_type(), nullptr);
|
||||
EXPECT_TRUE(v()->ValidateResultTypes(&assign));
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, AssignIncompatibleTypes_Fail) {
|
||||
// {
|
||||
// var a :i32 = 2;
|
||||
// a = 2.3;
|
||||
// }
|
||||
ast::type::F32Type f32;
|
||||
ast::type::I32Type i32;
|
||||
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &i32);
|
||||
var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2)));
|
||||
auto lhs = std::make_unique<ast::IdentifierExpression>("a");
|
||||
auto* lhs_ptr = lhs.get();
|
||||
auto rhs = std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::FloatLiteral>(&f32, 2.3f));
|
||||
auto* rhs_ptr = rhs.get();
|
||||
|
||||
ast::AssignmentStatement assign(Source{Source::Location{12, 34}},
|
||||
std::move(lhs), std::move(rhs));
|
||||
td()->RegisterVariableForTesting(var.get());
|
||||
EXPECT_TRUE(td()->DetermineResultType(&assign)) << td()->error();
|
||||
ASSERT_NE(lhs_ptr->result_type(), nullptr);
|
||||
ASSERT_NE(rhs_ptr->result_type(), nullptr);
|
||||
|
||||
EXPECT_FALSE(v()->ValidateResultTypes(&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 of '__i32' to '__f32'");
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, AssignCompatibleTypesInBlockStatement_Pass) {
|
||||
// {
|
||||
// var a :i32 = 2;
|
||||
// a = 2
|
||||
// }
|
||||
ast::type::I32Type i32;
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &i32);
|
||||
var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2)));
|
||||
|
||||
auto lhs = std::make_unique<ast::IdentifierExpression>("a");
|
||||
auto* lhs_ptr = lhs.get();
|
||||
auto rhs = std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2));
|
||||
auto* rhs_ptr = rhs.get();
|
||||
|
||||
auto body = std::make_unique<ast::BlockStatement>();
|
||||
body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
|
||||
body->append(std::make_unique<ast::AssignmentStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(lhs), std::move(rhs)));
|
||||
|
||||
EXPECT_TRUE(td()->DetermineStatements(body.get())) << td()->error();
|
||||
ASSERT_NE(lhs_ptr->result_type(), nullptr);
|
||||
ASSERT_NE(rhs_ptr->result_type(), nullptr);
|
||||
|
||||
EXPECT_TRUE(v()->ValidateStatements(body.get())) << v()->error();
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, AssignIncompatibleTypesInBlockStatement_Fail) {
|
||||
// {
|
||||
// var a :i32 = 2;
|
||||
// a = 2.3;
|
||||
// }
|
||||
ast::type::F32Type f32;
|
||||
ast::type::I32Type i32;
|
||||
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &i32);
|
||||
var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2)));
|
||||
auto lhs = std::make_unique<ast::IdentifierExpression>("a");
|
||||
auto* lhs_ptr = lhs.get();
|
||||
auto rhs = std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::FloatLiteral>(&f32, 2.3f));
|
||||
auto* rhs_ptr = rhs.get();
|
||||
|
||||
ast::BlockStatement block;
|
||||
block.append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
|
||||
block.append(std::make_unique<ast::AssignmentStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(lhs), std::move(rhs)));
|
||||
|
||||
EXPECT_TRUE(td()->DetermineStatements(&block)) << td()->error();
|
||||
ASSERT_NE(lhs_ptr->result_type(), nullptr);
|
||||
ASSERT_NE(rhs_ptr->result_type(), 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 of '__i32' to '__f32'");
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, GlobalVariableWithStorageClass_Pass) {
|
||||
// var<in> gloabl_var: f32;
|
||||
ast::type::F32Type f32;
|
||||
auto global_var = std::make_unique<ast::Variable>(
|
||||
Source{Source::Location{12, 34}}, "global_var", ast::StorageClass::kInput,
|
||||
&f32);
|
||||
mod()->AddGlobalVariable(std::move(global_var));
|
||||
EXPECT_TRUE(v()->ValidateGlobalVariables(mod()->global_variables()))
|
||||
<< v()->error();
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, GlobalVariableNoStorageClass_Fail) {
|
||||
// var gloabl_var: f32;
|
||||
ast::type::F32Type f32;
|
||||
auto global_var = std::make_unique<ast::Variable>(
|
||||
Source{Source::Location{12, 34}}, "global_var", ast::StorageClass::kNone,
|
||||
&f32);
|
||||
mod()->AddGlobalVariable(std::move(global_var));
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
EXPECT_FALSE(v()->Validate(mod()));
|
||||
EXPECT_EQ(v()->error(),
|
||||
"12:34: v-0022: global variables must have a storage class");
|
||||
}
|
||||
TEST_F(ValidatorTest, GlobalConstantWithStorageClass_Fail) {
|
||||
// const<in> gloabl_var: f32;
|
||||
ast::type::F32Type f32;
|
||||
auto global_var = std::make_unique<ast::Variable>(
|
||||
Source{Source::Location{12, 34}}, "global_var", ast::StorageClass::kInput,
|
||||
&f32);
|
||||
global_var->set_is_const(true);
|
||||
|
||||
mod()->AddGlobalVariable(std::move(global_var));
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
EXPECT_FALSE(v()->Validate(mod()));
|
||||
EXPECT_EQ(
|
||||
v()->error(),
|
||||
"12:34: v-global01: global constants shouldn't have a storage class");
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, GlobalConstNoStorageClass_Pass) {
|
||||
// const gloabl_var: f32;
|
||||
ast::type::F32Type f32;
|
||||
auto global_var = std::make_unique<ast::Variable>(
|
||||
Source{Source::Location{12, 34}}, "global_var", ast::StorageClass::kNone,
|
||||
&f32);
|
||||
global_var->set_is_const(true);
|
||||
|
||||
mod()->AddGlobalVariable(std::move(global_var));
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
EXPECT_FALSE(v()->Validate(mod())) << v()->error();
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, UsingUndefinedVariableGlobalVariable_Fail) {
|
||||
// var global_var: f32 = 2.1;
|
||||
// fn my_func() -> f32 {
|
||||
// not_global_var = 3.14f;
|
||||
// }
|
||||
ast::type::F32Type f32;
|
||||
auto global_var = std::make_unique<ast::Variable>(
|
||||
"global_var", ast::StorageClass::kPrivate, &f32);
|
||||
global_var->set_constructor(
|
||||
std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::FloatLiteral>(&f32, 2.1)));
|
||||
mod()->AddGlobalVariable(std::move(global_var));
|
||||
|
||||
auto lhs = std::make_unique<ast::IdentifierExpression>(
|
||||
Source{Source::Location{12, 34}}, "not_global_var");
|
||||
auto rhs = std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::FloatLiteral>(&f32, 3.14f));
|
||||
|
||||
ast::VariableList params;
|
||||
auto func =
|
||||
std::make_unique<ast::Function>("my_func", std::move(params), &f32);
|
||||
|
||||
auto body = std::make_unique<ast::BlockStatement>();
|
||||
body->append(std::make_unique<ast::AssignmentStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(lhs), std::move(rhs)));
|
||||
func->set_body(std::move(body));
|
||||
mod()->AddFunction(std::move(func));
|
||||
|
||||
EXPECT_FALSE(v()->Validate(mod()));
|
||||
EXPECT_EQ(v()->error(), "12:34: v-0006: 'not_global_var' is not declared");
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, UsingUndefinedVariableGlobalVariable_Pass) {
|
||||
// var global_var: f32 = 2.1;
|
||||
// fn my_func() -> void {
|
||||
// global_var = 3.14;
|
||||
// return;
|
||||
// }
|
||||
ast::type::F32Type f32;
|
||||
ast::type::VoidType void_type;
|
||||
|
||||
auto global_var = std::make_unique<ast::Variable>(
|
||||
"global_var", ast::StorageClass::kPrivate, &f32);
|
||||
global_var->set_constructor(
|
||||
std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::FloatLiteral>(&f32, 2.1)));
|
||||
mod()->AddGlobalVariable(std::move(global_var));
|
||||
|
||||
auto lhs = std::make_unique<ast::IdentifierExpression>("global_var");
|
||||
auto rhs = std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::FloatLiteral>(&f32, 3.14f));
|
||||
|
||||
ast::VariableList params;
|
||||
auto func =
|
||||
std::make_unique<ast::Function>("my_func", std::move(params), &void_type);
|
||||
|
||||
auto body = std::make_unique<ast::BlockStatement>();
|
||||
body->append(std::make_unique<ast::AssignmentStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(lhs), std::move(rhs)));
|
||||
body->append(std::make_unique<ast::ReturnStatement>());
|
||||
func->set_body(std::move(body));
|
||||
func->add_decoration(std::make_unique<ast::StageDecoration>(
|
||||
ast::PipelineStage::kVertex, Source{}));
|
||||
mod()->AddFunction(std::move(func));
|
||||
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
EXPECT_TRUE(v()->Validate(mod())) << v()->error();
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, UsingUndefinedVariableInnerScope_Fail) {
|
||||
// {
|
||||
// if (true) { var a : f32 = 2.0; }
|
||||
// a = 3.14;
|
||||
// }
|
||||
ast::type::F32Type f32;
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &f32);
|
||||
var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
|
||||
|
||||
ast::type::BoolType bool_type;
|
||||
auto cond = std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::BoolLiteral>(&bool_type, true));
|
||||
auto body = std::make_unique<ast::BlockStatement>();
|
||||
body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
|
||||
|
||||
auto lhs = std::make_unique<ast::IdentifierExpression>(
|
||||
Source{Source::Location{12, 34}}, "a");
|
||||
auto* lhs_ptr = lhs.get();
|
||||
auto rhs = std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::FloatLiteral>(&f32, 3.14f));
|
||||
auto* rhs_ptr = rhs.get();
|
||||
|
||||
auto outer_body = std::make_unique<ast::BlockStatement>();
|
||||
outer_body->append(
|
||||
std::make_unique<ast::IfStatement>(std::move(cond), std::move(body)));
|
||||
outer_body->append(std::make_unique<ast::AssignmentStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(lhs), std::move(rhs)));
|
||||
|
||||
EXPECT_TRUE(td()->DetermineStatements(outer_body.get())) << td()->error();
|
||||
ASSERT_NE(lhs_ptr->result_type(), nullptr);
|
||||
ASSERT_NE(rhs_ptr->result_type(), nullptr);
|
||||
EXPECT_FALSE(v()->ValidateStatements(outer_body.get()));
|
||||
EXPECT_EQ(v()->error(), "12:34: v-0006: 'a' is not declared");
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, UsingUndefinedVariableOuterScope_Pass) {
|
||||
// {
|
||||
// var a : f32 = 2.0;
|
||||
// if (true) { a = 3.14; }
|
||||
// }
|
||||
ast::type::F32Type f32;
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &f32);
|
||||
var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
|
||||
|
||||
auto lhs = std::make_unique<ast::IdentifierExpression>(
|
||||
Source{Source::Location{12, 34}}, "a");
|
||||
auto* lhs_ptr = lhs.get();
|
||||
auto rhs = std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::FloatLiteral>(&f32, 3.14f));
|
||||
auto* rhs_ptr = rhs.get();
|
||||
ast::type::BoolType bool_type;
|
||||
auto cond = std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::BoolLiteral>(&bool_type, true));
|
||||
auto body = std::make_unique<ast::BlockStatement>();
|
||||
body->append(std::make_unique<ast::AssignmentStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(lhs), std::move(rhs)));
|
||||
|
||||
auto outer_body = std::make_unique<ast::BlockStatement>();
|
||||
outer_body->append(
|
||||
std::make_unique<ast::VariableDeclStatement>(std::move(var)));
|
||||
outer_body->append(
|
||||
std::make_unique<ast::IfStatement>(std::move(cond), std::move(body)));
|
||||
EXPECT_TRUE(td()->DetermineStatements(outer_body.get())) << td()->error();
|
||||
ASSERT_NE(lhs_ptr->result_type(), nullptr);
|
||||
ASSERT_NE(rhs_ptr->result_type(), nullptr);
|
||||
EXPECT_TRUE(v()->ValidateStatements(outer_body.get())) << v()->error();
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, GlobalVariableUnique_Pass) {
|
||||
// var global_var0 : f32 = 0.1;
|
||||
// var global_var1 : i32 = 0;
|
||||
ast::type::F32Type f32;
|
||||
ast::type::I32Type i32;
|
||||
auto var0 = std::make_unique<ast::Variable>(
|
||||
"global_var0", ast::StorageClass::kPrivate, &f32);
|
||||
var0->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::FloatLiteral>(&f32, 0.1)));
|
||||
mod()->AddGlobalVariable(std::move(var0));
|
||||
|
||||
auto var1 = std::make_unique<ast::Variable>(
|
||||
Source{Source::Location{12, 34}}, "global_var1",
|
||||
ast::StorageClass::kPrivate, &f32);
|
||||
var1->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 0)));
|
||||
mod()->AddGlobalVariable(std::move(var1));
|
||||
|
||||
EXPECT_TRUE(v()->ValidateGlobalVariables(mod()->global_variables()))
|
||||
<< v()->error();
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, GlobalVariableNotUnique_Fail) {
|
||||
// var global_var : f32 = 0.1;
|
||||
// var global_var : i32 = 0;
|
||||
ast::type::F32Type f32;
|
||||
ast::type::I32Type i32;
|
||||
auto var0 = std::make_unique<ast::Variable>(
|
||||
"global_var", ast::StorageClass::kPrivate, &f32);
|
||||
var0->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::FloatLiteral>(&f32, 0.1)));
|
||||
mod()->AddGlobalVariable(std::move(var0));
|
||||
|
||||
auto var1 = std::make_unique<ast::Variable>(
|
||||
Source{Source::Location{12, 34}}, "global_var",
|
||||
ast::StorageClass::kPrivate, &f32);
|
||||
var1->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 0)));
|
||||
mod()->AddGlobalVariable(std::move(var1));
|
||||
|
||||
EXPECT_FALSE(v()->ValidateGlobalVariables(mod()->global_variables()));
|
||||
EXPECT_EQ(v()->error(),
|
||||
"12:34: v-0011: redeclared global identifier 'global_var'");
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, AssignToConstant_Fail) {
|
||||
// {
|
||||
// const a :i32 = 2;
|
||||
// a = 2
|
||||
// }
|
||||
ast::type::I32Type i32;
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &i32);
|
||||
var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2)));
|
||||
var->set_is_const(true);
|
||||
|
||||
auto lhs = std::make_unique<ast::IdentifierExpression>("a");
|
||||
auto* lhs_ptr = lhs.get();
|
||||
auto rhs = std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2));
|
||||
auto* rhs_ptr = rhs.get();
|
||||
|
||||
auto body = std::make_unique<ast::BlockStatement>();
|
||||
body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
|
||||
body->append(std::make_unique<ast::AssignmentStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(lhs), std::move(rhs)));
|
||||
|
||||
EXPECT_TRUE(td()->DetermineStatements(body.get())) << td()->error();
|
||||
ASSERT_NE(lhs_ptr->result_type(), nullptr);
|
||||
ASSERT_NE(rhs_ptr->result_type(), nullptr);
|
||||
|
||||
EXPECT_FALSE(v()->ValidateStatements(body.get()));
|
||||
EXPECT_EQ(v()->error(), "12:34: v-0021: cannot re-assign a constant: 'a'");
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, GlobalVariableFunctionVariableNotUnique_Fail) {
|
||||
// var a: f32 = 2.1;
|
||||
// fn my_func -> void {
|
||||
// var a: f32 = 2.0;
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
ast::type::VoidType void_type;
|
||||
ast::type::F32Type f32;
|
||||
auto global_var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kPrivate, &f32);
|
||||
global_var->set_constructor(
|
||||
std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::FloatLiteral>(&f32, 2.1)));
|
||||
mod()->AddGlobalVariable(std::move(global_var));
|
||||
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &f32);
|
||||
var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
|
||||
ast::VariableList params;
|
||||
auto func =
|
||||
std::make_unique<ast::Function>("my_func", std::move(params), &void_type);
|
||||
auto body = std::make_unique<ast::BlockStatement>();
|
||||
body->append(std::make_unique<ast::VariableDeclStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(var)));
|
||||
func->set_body(std::move(body));
|
||||
auto* func_ptr = func.get();
|
||||
mod()->AddFunction(std::move(func));
|
||||
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
EXPECT_TRUE(td()->DetermineFunction(func_ptr)) << td()->error();
|
||||
EXPECT_FALSE(v()->Validate(mod())) << v()->error();
|
||||
EXPECT_EQ(v()->error(), "12:34: v-0013: redeclared identifier 'a'");
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, RedeclaredIndentifier_Fail) {
|
||||
// fn my_func() -> void {
|
||||
// var a :i32 = 2;
|
||||
// var a :f21 = 2.0;
|
||||
// }
|
||||
ast::type::VoidType void_type;
|
||||
ast::type::I32Type i32;
|
||||
ast::type::F32Type f32;
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &i32);
|
||||
var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2)));
|
||||
|
||||
auto var_a_float =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &f32);
|
||||
var_a_float->set_constructor(
|
||||
std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::FloatLiteral>(&f32, 0.1)));
|
||||
|
||||
ast::VariableList params;
|
||||
auto func =
|
||||
std::make_unique<ast::Function>("my_func", std::move(params), &void_type);
|
||||
auto body = std::make_unique<ast::BlockStatement>();
|
||||
body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
|
||||
body->append(std::make_unique<ast::VariableDeclStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(var_a_float)));
|
||||
func->set_body(std::move(body));
|
||||
auto* func_ptr = func.get();
|
||||
mod()->AddFunction(std::move(func));
|
||||
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
EXPECT_TRUE(td()->DetermineFunction(func_ptr)) << td()->error();
|
||||
EXPECT_FALSE(v()->Validate(mod()));
|
||||
EXPECT_EQ(v()->error(), "12:34: v-0014: redeclared identifier 'a'");
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, RedeclaredIdentifierInnerScope_Pass) {
|
||||
// {
|
||||
// if (true) { var a : f32 = 2.0; }
|
||||
// var a : f32 = 3.14;
|
||||
// }
|
||||
ast::type::F32Type f32;
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &f32);
|
||||
var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
|
||||
|
||||
ast::type::BoolType bool_type;
|
||||
auto cond = std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::BoolLiteral>(&bool_type, true));
|
||||
auto body = std::make_unique<ast::BlockStatement>();
|
||||
body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
|
||||
|
||||
auto var_a_float =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &f32);
|
||||
var_a_float->set_constructor(
|
||||
std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::FloatLiteral>(&f32, 3.14)));
|
||||
|
||||
auto outer_body = std::make_unique<ast::BlockStatement>();
|
||||
outer_body->append(
|
||||
std::make_unique<ast::IfStatement>(std::move(cond), std::move(body)));
|
||||
outer_body->append(std::make_unique<ast::VariableDeclStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(var_a_float)));
|
||||
|
||||
EXPECT_TRUE(td()->DetermineStatements(outer_body.get())) << td()->error();
|
||||
EXPECT_TRUE(v()->ValidateStatements(outer_body.get())) << v()->error();
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, DISABLED_RedeclaredIdentifierInnerScope_False) {
|
||||
// TODO(sarahM0): remove DISABLED after implementing ValidateIfStatement
|
||||
// and it should just work
|
||||
// {
|
||||
// var a : f32 = 3.14;
|
||||
// if (true) { var a : f32 = 2.0; }
|
||||
// }
|
||||
ast::type::F32Type f32;
|
||||
auto var_a_float =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &f32);
|
||||
var_a_float->set_constructor(
|
||||
std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::FloatLiteral>(&f32, 3.14)));
|
||||
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &f32);
|
||||
var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
|
||||
|
||||
ast::type::BoolType bool_type;
|
||||
auto cond = std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::BoolLiteral>(&bool_type, true));
|
||||
auto body = std::make_unique<ast::BlockStatement>();
|
||||
body->append(std::make_unique<ast::VariableDeclStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(var)));
|
||||
|
||||
auto outer_body = std::make_unique<ast::BlockStatement>();
|
||||
outer_body->append(
|
||||
std::make_unique<ast::VariableDeclStatement>(std::move(var_a_float)));
|
||||
outer_body->append(
|
||||
std::make_unique<ast::IfStatement>(std::move(cond), std::move(body)));
|
||||
|
||||
EXPECT_TRUE(td()->DetermineStatements(outer_body.get())) << td()->error();
|
||||
EXPECT_FALSE(v()->ValidateStatements(outer_body.get()));
|
||||
EXPECT_EQ(v()->error(), "12:34: v-0014: redeclared identifier 'a'");
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, RedeclaredIdentifierDifferentFunctions_Pass) {
|
||||
// func0 { var a : f32 = 2.0; return; }
|
||||
// func1 { var a : f32 = 3.0; return; }
|
||||
ast::type::F32Type f32;
|
||||
ast::type::VoidType void_type;
|
||||
auto var0 =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &f32);
|
||||
var0->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
|
||||
|
||||
auto var1 = std::make_unique<ast::Variable>("a", ast::StorageClass::kNone,
|
||||
&void_type);
|
||||
var1->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::FloatLiteral>(&f32, 1.0)));
|
||||
|
||||
ast::VariableList params0;
|
||||
auto func0 =
|
||||
std::make_unique<ast::Function>("func0", std::move(params0), &void_type);
|
||||
auto body0 = std::make_unique<ast::BlockStatement>();
|
||||
body0->append(std::make_unique<ast::VariableDeclStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(var0)));
|
||||
body0->append(std::make_unique<ast::ReturnStatement>());
|
||||
func0->set_body(std::move(body0));
|
||||
|
||||
ast::VariableList params1;
|
||||
auto func1 =
|
||||
std::make_unique<ast::Function>("func1", std::move(params1), &void_type);
|
||||
auto body1 = std::make_unique<ast::BlockStatement>();
|
||||
body1->append(std::make_unique<ast::VariableDeclStatement>(
|
||||
Source{Source::Location{13, 34}}, std::move(var1)));
|
||||
body1->append(std::make_unique<ast::ReturnStatement>());
|
||||
func1->set_body(std::move(body1));
|
||||
func1->add_decoration(std::make_unique<ast::StageDecoration>(
|
||||
ast::PipelineStage::kVertex, Source{}));
|
||||
|
||||
mod()->AddFunction(std::move(func0));
|
||||
mod()->AddFunction(std::move(func1));
|
||||
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
EXPECT_TRUE(v()->Validate(mod())) << v()->error();
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, VariableDeclNoConstructor_Pass) {
|
||||
// {
|
||||
// var a :i32;
|
||||
// a = 2;
|
||||
// }
|
||||
ast::type::I32Type i32;
|
||||
auto var =
|
||||
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &i32);
|
||||
|
||||
td()->RegisterVariableForTesting(var.get());
|
||||
auto lhs = std::make_unique<ast::IdentifierExpression>("a");
|
||||
auto* lhs_ptr = lhs.get();
|
||||
auto rhs = std::make_unique<ast::ScalarConstructorExpression>(
|
||||
std::make_unique<ast::SintLiteral>(&i32, 2));
|
||||
auto* rhs_ptr = rhs.get();
|
||||
|
||||
auto body = std::make_unique<ast::BlockStatement>();
|
||||
body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
|
||||
body->append(std::make_unique<ast::AssignmentStatement>(
|
||||
Source{Source::Location{12, 34}}, std::move(lhs), std::move(rhs)));
|
||||
|
||||
EXPECT_TRUE(td()->DetermineStatements(body.get())) << td()->error();
|
||||
ASSERT_NE(lhs_ptr->result_type(), nullptr);
|
||||
ASSERT_NE(rhs_ptr->result_type(), nullptr);
|
||||
EXPECT_TRUE(v()->ValidateStatements(body.get())) << v()->error();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace tint
|
||||
30
src/validator/validator_test_helper.cc
Normal file
30
src/validator/validator_test_helper.cc
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2020 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/validator/validator_test_helper.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "src/type_manager.h"
|
||||
|
||||
namespace tint {
|
||||
|
||||
ValidatorTestHelper::ValidatorTestHelper() {
|
||||
td_ = std::make_unique<TypeDeterminer>(&ctx_, &mod_);
|
||||
v_ = std::make_unique<ValidatorImpl>();
|
||||
}
|
||||
|
||||
ValidatorTestHelper::~ValidatorTestHelper() = default;
|
||||
|
||||
} // namespace tint
|
||||
53
src/validator/validator_test_helper.h
Normal file
53
src/validator/validator_test_helper.h
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright 2020 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.
|
||||
|
||||
#ifndef SRC_VALIDATOR_VALIDATOR_TEST_HELPER_H_
|
||||
#define SRC_VALIDATOR_VALIDATOR_TEST_HELPER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "src/ast/type/void_type.h"
|
||||
#include "src/type_determiner.h"
|
||||
#include "src/validator/validator_impl.h"
|
||||
|
||||
namespace tint {
|
||||
|
||||
/// A helper for testing validation
|
||||
class ValidatorTestHelper {
|
||||
public:
|
||||
/// Constructor
|
||||
ValidatorTestHelper();
|
||||
~ValidatorTestHelper();
|
||||
|
||||
/// A handle to validator
|
||||
/// @returns a pointer to the validator
|
||||
ValidatorImpl* v() const { return v_.get(); }
|
||||
/// A handle to type_determiner
|
||||
/// @returns a pointer to the type_determiner object
|
||||
TypeDeterminer* td() const { return td_.get(); }
|
||||
/// A handle to the created module
|
||||
/// @return a pointer to the test module
|
||||
ast::Module* mod() { return &mod_; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<ValidatorImpl> v_;
|
||||
Context ctx_;
|
||||
ast::Module mod_;
|
||||
std::unique_ptr<TypeDeterminer> td_;
|
||||
ast::type::VoidType void_type_;
|
||||
};
|
||||
|
||||
} // namespace tint
|
||||
|
||||
#endif // SRC_VALIDATOR_VALIDATOR_TEST_HELPER_H_
|
||||
Reference in New Issue
Block a user