[validate] Update to validate pipeline stages.

This CL converts the validator to work against the pipeline stages
attached to functions instead of entry point nodes.

Change-Id: Ia6274423e49426766c527d1123880ef6e0140959
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/28700
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: David Neto <dneto@google.com>
Reviewed-by: Sarah Mashayekhi <sarahmashay@google.com>
This commit is contained in:
dan sinclair 2020-09-21 17:56:41 +00:00 committed by Commit Bot service account
parent 0592643782
commit 51ac24ab10
7 changed files with 156 additions and 271 deletions

View File

@ -379,14 +379,12 @@ TEST_F(ValidateControlBlockTest, SwitchCase_Pass) {
} }
TEST_F(ValidateControlBlockTest, SwitchCaseAlias_Pass) { TEST_F(ValidateControlBlockTest, SwitchCaseAlias_Pass) {
// entry_point vertex = main
// type MyInt = u32; // type MyInt = u32;
// fn main()->void { // var v: MyInt;
// var v: MyInt; // switch(v){
// switch(v){ // default: {}
// default: {}
// }
// } // }
ast::type::U32Type u32; ast::type::U32Type u32;
ast::type::AliasType my_int{"MyInt", &u32}; ast::type::AliasType my_int{"MyInt", &u32};
@ -406,21 +404,11 @@ TEST_F(ValidateControlBlockTest, SwitchCaseAlias_Pass) {
block->append(std::make_unique<ast::VariableDeclStatement>(std::move(var))); block->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
block->append( block->append(
std::make_unique<ast::SwitchStatement>(std::move(cond), std::move(body))); std::make_unique<ast::SwitchStatement>(std::move(cond), std::move(body)));
block->append(std::make_unique<ast::ReturnStatement>());
ast::type::VoidType void_type;
ast::VariableList params;
auto func =
std::make_unique<ast::Function>("main", std::move(params), &void_type);
func->set_body(std::move(block));
auto entry_point = std::make_unique<ast::EntryPoint>(
ast::PipelineStage::kVertex, "", "main");
mod()->AddFunction(std::move(func));
mod()->AddAliasType(&my_int); mod()->AddAliasType(&my_int);
mod()->AddEntryPoint(std::move(entry_point));
EXPECT_TRUE(td()->Determine()) << td()->error(); EXPECT_TRUE(td()->DetermineStatements(block.get())) << td()->error();
EXPECT_TRUE(v()->Validate(mod())) << v()->error(); EXPECT_TRUE(v()->ValidateStatements(block.get())) << v()->error();
} }
} // namespace } // namespace

View File

@ -18,9 +18,11 @@
#include "spirv/unified1/GLSL.std.450.h" #include "spirv/unified1/GLSL.std.450.h"
#include "src/ast/call_statement.h" #include "src/ast/call_statement.h"
#include "src/ast/entry_point.h" #include "src/ast/entry_point.h"
#include "src/ast/pipeline_stage.h"
#include "src/ast/return_statement.h" #include "src/ast/return_statement.h"
#include "src/ast/scalar_constructor_expression.h" #include "src/ast/scalar_constructor_expression.h"
#include "src/ast/sint_literal.h" #include "src/ast/sint_literal.h"
#include "src/ast/stage_decoration.h"
#include "src/ast/type/f32_type.h" #include "src/ast/type/f32_type.h"
#include "src/ast/type/i32_type.h" #include "src/ast/type/i32_type.h"
#include "src/ast/type/void_type.h" #include "src/ast/type/void_type.h"
@ -74,19 +76,23 @@ TEST_F(ValidateFunctionTest, FunctionEndWithoutReturnStatementEmptyBody_Fail) {
} }
TEST_F(ValidateFunctionTest, FunctionTypeMustMatchReturnStatementType_Pass) { TEST_F(ValidateFunctionTest, FunctionTypeMustMatchReturnStatementType_Pass) {
// [[stage(vertex)]]
// fn func -> void { return; } // fn func -> void { return; }
ast::type::VoidType void_type; ast::type::VoidType void_type;
ast::VariableList params; ast::VariableList params;
auto func = auto func =
std::make_unique<ast::Function>("func", std::move(params), &void_type); std::make_unique<ast::Function>("func", std::move(params), &void_type);
auto body = std::make_unique<ast::BlockStatement>(); auto body = std::make_unique<ast::BlockStatement>();
body->append(std::make_unique<ast::ReturnStatement>()); body->append(std::make_unique<ast::ReturnStatement>());
func->set_body(std::move(body)); func->set_body(std::move(body));
func->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
mod()->AddFunction(std::move(func)); mod()->AddFunction(std::move(func));
AddFakeEntryPoint();
EXPECT_TRUE(td()->Determine()) << td()->error(); EXPECT_TRUE(td()->DetermineFunctions(mod()->functions())) << td()->error();
EXPECT_TRUE(v()->Validate(mod())) << v()->error(); EXPECT_TRUE(v()->ValidateFunctions(mod(), mod()->functions()))
<< v()->error();
} }
TEST_F(ValidateFunctionTest, FunctionTypeMustMatchReturnStatementType_fail) { TEST_F(ValidateFunctionTest, FunctionTypeMustMatchReturnStatementType_fail) {
@ -220,93 +226,46 @@ TEST_F(ValidateFunctionTest, RecursionIsNotAllowedExpr_Fail) {
EXPECT_EQ(v()->error(), "12:34: v-0004: recursion is not allowed: 'func'"); EXPECT_EQ(v()->error(), "12:34: v-0004: recursion is not allowed: 'func'");
} }
TEST_F(ValidateFunctionTest, EntryPointFunctionMissing_Fail) { TEST_F(ValidateFunctionTest, Function_WithPipelineStage_NotVoid_Fail) {
// entry_point vertex as "main" = vtx_main // [[stage(vertex)]]
// fn frag_main() -> void { return; }
ast::type::VoidType void_type;
ast::VariableList params;
auto func = std::make_unique<ast::Function>("vtx_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));
auto entry_point = std::make_unique<ast::EntryPoint>(
Source{12, 34}, ast::PipelineStage::kVertex, "main", "frag_main");
mod()->AddFunction(std::move(func));
mod()->AddEntryPoint(std::move(entry_point));
EXPECT_TRUE(td()->Determine()) << td()->error();
EXPECT_FALSE(v()->Validate(mod()));
EXPECT_EQ(v()->error(),
"12:34: v-0019: Function used in entry point does not exist: "
"'frag_main'");
}
TEST_F(ValidateFunctionTest, EntryPointFunctionExist_Pass) {
// entry_point vertex as "main" = vtx_main
// fn vtx_main() -> void { return; }
ast::type::VoidType void_type;
ast::VariableList params;
auto func = std::make_unique<ast::Function>("vtx_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));
auto entry_point = std::make_unique<ast::EntryPoint>(
ast::PipelineStage::kVertex, "main", "vtx_main");
mod()->AddFunction(std::move(func));
mod()->AddEntryPoint(std::move(entry_point));
EXPECT_TRUE(td()->Determine()) << td()->error();
EXPECT_TRUE(v()->Validate(mod())) << v()->error();
}
TEST_F(ValidateFunctionTest, EntryPointFunctionNotVoid_Fail) {
// entry_point vertex as "main" = vtx_main
// fn vtx_main() -> i32 { return 0; } // fn vtx_main() -> i32 { return 0; }
ast::type::I32Type i32; ast::type::I32Type i32;
ast::VariableList params; ast::VariableList params;
auto func = auto func = std::make_unique<ast::Function>(Source{12, 34}, "vtx_main",
std::make_unique<ast::Function>("vtx_main", std::move(params), &i32); std::move(params), &i32);
auto return_expr = std::make_unique<ast::ScalarConstructorExpression>( auto return_expr = std::make_unique<ast::ScalarConstructorExpression>(
std::make_unique<ast::SintLiteral>(&i32, 0)); std::make_unique<ast::SintLiteral>(&i32, 0));
auto body = std::make_unique<ast::BlockStatement>(); auto body = std::make_unique<ast::BlockStatement>();
body->append(std::make_unique<ast::ReturnStatement>(std::move(return_expr))); body->append(std::make_unique<ast::ReturnStatement>(std::move(return_expr)));
func->set_body(std::move(body)); func->set_body(std::move(body));
func->add_decoration(
auto entry_point = std::make_unique<ast::EntryPoint>( std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
Source{12, 34}, ast::PipelineStage::kVertex, "main", "vtx_main");
mod()->AddFunction(std::move(func)); mod()->AddFunction(std::move(func));
mod()->AddEntryPoint(std::move(entry_point));
EXPECT_TRUE(td()->Determine()) << td()->error(); EXPECT_TRUE(td()->Determine()) << td()->error();
EXPECT_FALSE(v()->Validate(mod())); EXPECT_FALSE(v()->Validate(mod()));
EXPECT_EQ(v()->error(), EXPECT_EQ(v()->error(),
"12:34: v-0024: Entry point function must return void: 'vtx_main'"); "12:34: v-0024: Entry point function must return void: 'vtx_main'");
} }
TEST_F(ValidateFunctionTest, EntryPointFunctionWithParams_Fail) { TEST_F(ValidateFunctionTest, Function_WithPipelineStage_WithParams_Fail) {
// entry_point vertex as "func" = vtx_func // [[stage(vertex)]]
// fn vtx_func(a : i32) -> void { return; } // fn vtx_func(a : i32) -> void { return; }
ast::type::I32Type i32; ast::type::I32Type i32;
ast::type::VoidType void_type; ast::type::VoidType void_type;
ast::VariableList params; ast::VariableList params;
params.push_back( params.push_back(
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &i32)); std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &i32));
auto func = std::make_unique<ast::Function>("vtx_func", std::move(params), auto func = std::make_unique<ast::Function>(Source{12, 34}, "vtx_func",
&void_type); std::move(params), &void_type);
auto body = std::make_unique<ast::BlockStatement>(); auto body = std::make_unique<ast::BlockStatement>();
body->append(std::make_unique<ast::ReturnStatement>()); body->append(std::make_unique<ast::ReturnStatement>());
func->set_body(std::move(body)); func->set_body(std::move(body));
func->add_decoration(
auto entry_point = std::make_unique<ast::EntryPoint>( std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
Source{12, 34}, ast::PipelineStage::kVertex, "func", "vtx_func");
mod()->AddFunction(std::move(func)); mod()->AddFunction(std::move(func));
mod()->AddEntryPoint(std::move(entry_point));
EXPECT_TRUE(td()->Determine()) << td()->error(); EXPECT_TRUE(td()->Determine()) << td()->error();
EXPECT_FALSE(v()->Validate(mod())); EXPECT_FALSE(v()->Validate(mod()));
EXPECT_EQ(v()->error(), EXPECT_EQ(v()->error(),
@ -314,26 +273,31 @@ TEST_F(ValidateFunctionTest, EntryPointFunctionWithParams_Fail) {
"'vtx_func'"); "'vtx_func'");
} }
TEST_F(ValidateFunctionTest, EntryPointFunctionPairMustBeUniqueDuplicate_Fail) { TEST_F(ValidateFunctionTest, PipelineStageNamePair_MustBeUnique_Fail) {
// entry_point vertex = vtx_main // [[stage(vertex)]]
// entry_point vertex = vtx_main // fn main() -> void { return ;}
// fn vtx_main() -> void { return; } // [[stage(vertex)]]
// fn main() -> void { return; }
ast::type::VoidType void_type; ast::type::VoidType void_type;
ast::VariableList params; ast::VariableList params;
auto func = std::make_unique<ast::Function>("vtx_main", std::move(params), auto func = std::make_unique<ast::Function>(Source{5, 6}, "main",
&void_type); std::move(params), &void_type);
auto body = std::make_unique<ast::BlockStatement>(); auto body = std::make_unique<ast::BlockStatement>();
body->append(std::make_unique<ast::ReturnStatement>()); body->append(std::make_unique<ast::ReturnStatement>());
func->set_body(std::move(body)); func->set_body(std::move(body));
func->add_decoration(
auto entry_point = std::make_unique<ast::EntryPoint>( std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
ast::PipelineStage::kVertex, "", "vtx_main");
auto entry_point_copy = std::make_unique<ast::EntryPoint>(
Source{12, 34}, ast::PipelineStage::kVertex, "", "vtx_main");
mod()->AddFunction(std::move(func)); mod()->AddFunction(std::move(func));
mod()->AddEntryPoint(std::move(entry_point));
mod()->AddEntryPoint(std::move(entry_point_copy)); func = std::make_unique<ast::Function>(Source{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));
mod()->AddFunction(std::move(func));
EXPECT_TRUE(td()->Determine()) << td()->error(); EXPECT_TRUE(td()->Determine()) << td()->error();
EXPECT_FALSE(v()->Validate(mod())); EXPECT_FALSE(v()->Validate(mod()));
EXPECT_EQ(v()->error(), EXPECT_EQ(v()->error(),
@ -341,46 +305,37 @@ TEST_F(ValidateFunctionTest, EntryPointFunctionPairMustBeUniqueDuplicate_Fail) {
"must be unique"); "must be unique");
} }
TEST_F(ValidateFunctionTest, EntryPointFunctionPairMustBeUniqueTowVertex_Fail) { TEST_F(ValidateFunctionTest, PipelineStageNamePair_MustBeUnique_Pass) {
// entry_point vertex as "main" = vtx_func1 // [[stage(vertex)]]
// entry_point vertex as "main" = vtx_func0 // fn main() -> void { return; }
// fn vtx_func1() -> void { return; } // [[stage(fragment)]]
// fn vtx_func0() -> void { return; } // fn main() -> void { return; }
ast::type::VoidType void_type; ast::type::VoidType void_type;
ast::VariableList params; ast::VariableList params;
auto func = std::make_unique<ast::Function>("vtx_func0", std::move(params), auto func = std::make_unique<ast::Function>(Source{5, 6}, "main",
&void_type); std::move(params), &void_type);
auto body = std::make_unique<ast::BlockStatement>(); auto body = std::make_unique<ast::BlockStatement>();
body->append(std::make_unique<ast::ReturnStatement>()); body->append(std::make_unique<ast::ReturnStatement>());
func->set_body(std::move(body)); func->set_body(std::move(body));
func->add_decoration(
ast::VariableList params1; std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
auto func1 = std::make_unique<ast::Function>("vtx_func1", std::move(params1),
&void_type);
auto body1 = std::make_unique<ast::BlockStatement>();
body1->append(std::make_unique<ast::ReturnStatement>());
func1->set_body(std::move(body1));
auto entry_point = std::make_unique<ast::EntryPoint>(
ast::PipelineStage::kVertex, "main", "vtx_func0");
auto entry_point1 = std::make_unique<ast::EntryPoint>(
Source{12, 34}, ast::PipelineStage::kVertex, "main", "vtx_func1");
mod()->AddFunction(std::move(func)); mod()->AddFunction(std::move(func));
mod()->AddFunction(std::move(func1));
mod()->AddEntryPoint(std::move(entry_point)); func = std::make_unique<ast::Function>(Source{12, 34}, "main",
mod()->AddEntryPoint(std::move(entry_point1)); 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));
mod()->AddFunction(std::move(func));
EXPECT_TRUE(td()->Determine()) << td()->error(); EXPECT_TRUE(td()->Determine()) << td()->error();
EXPECT_FALSE(v()->Validate(mod())); EXPECT_TRUE(v()->Validate(mod())) << v()->error();
EXPECT_EQ(v()->error(),
"12:34: v-0020: The pair of <entry point name, pipeline stage> "
"must be unique");
} }
TEST_F(ValidateFunctionTest, TEST_F(ValidateFunctionTest, OnePipelineStageFunctionMustBePresent_Pass) {
EntryPointFunctionPairMustBeUniqueSameFuncDiffStage_Pass) { // [[stage(vertex)]]
// entry_point vertex as "main" = vtx_func
// entry_point fragment as "main" = vtx_func
// fn vtx_func() -> void { return; } // fn vtx_func() -> void { return; }
ast::type::VoidType void_type; ast::type::VoidType void_type;
ast::VariableList params; ast::VariableList params;
@ -389,42 +344,15 @@ TEST_F(ValidateFunctionTest,
auto body = std::make_unique<ast::BlockStatement>(); auto body = std::make_unique<ast::BlockStatement>();
body->append(std::make_unique<ast::ReturnStatement>()); body->append(std::make_unique<ast::ReturnStatement>());
func->set_body(std::move(body)); func->set_body(std::move(body));
func->add_decoration(
auto entry_point = std::make_unique<ast::EntryPoint>( std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
ast::PipelineStage::kVertex, "main", "vtx_func");
auto entry_point1 = std::make_unique<ast::EntryPoint>(
Source{12, 34}, ast::PipelineStage::kFragment, "main", "vtx_func");
mod()->AddFunction(std::move(func)); mod()->AddFunction(std::move(func));
mod()->AddEntryPoint(std::move(entry_point));
mod()->AddEntryPoint(std::move(entry_point1));
EXPECT_TRUE(td()->Determine()) << td()->error();
EXPECT_TRUE(v()->Validate(mod())) << v()->error();
}
TEST_F(ValidateFunctionTest,
AtLeastOneOfVertexFragmentComputeShaderMustBePeresent_Pass) {
// entry_point vertex as "main" = vtx_func
// 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));
auto entry_point = std::make_unique<ast::EntryPoint>(
ast::PipelineStage::kVertex, "main", "vtx_func");
mod()->AddFunction(std::move(func));
mod()->AddEntryPoint(std::move(entry_point));
EXPECT_TRUE(td()->Determine()) << td()->error(); EXPECT_TRUE(td()->Determine()) << td()->error();
EXPECT_TRUE(v()->Validate(mod())) << v()->error(); EXPECT_TRUE(v()->Validate(mod())) << v()->error();
} }
TEST_F(ValidateFunctionTest, TEST_F(ValidateFunctionTest, OnePipelineStageFunctionMustBePresent_Fail) {
AtLeastOneOfVertexFragmentComputeShaderMustBePeresent_Fail) {
// fn vtx_func() -> void { return; } // fn vtx_func() -> void { return; }
ast::type::VoidType void_type; ast::type::VoidType void_type;
ast::VariableList params; ast::VariableList params;

View File

@ -49,11 +49,7 @@ bool ValidatorImpl::Validate(const ast::Module* module) {
if (!CheckImports(module)) { if (!CheckImports(module)) {
return false; return false;
} }
if (!ValidateFunctions(module->functions())) { if (!ValidateFunctions(module, module->functions())) {
return false;
}
// ValidateEntryPoints must be done after populating function_stack_
if (!ValidateEntryPoints(module->entry_points())) {
return false; return false;
} }
@ -86,71 +82,66 @@ bool ValidatorImpl::ValidateGlobalVariables(
return true; return true;
} }
bool ValidatorImpl::ValidateEntryPoints(const ast::EntryPointList& eps) { bool ValidatorImpl::ValidateFunctions(const ast::Module* mod,
const ast::FunctionList& funcs) {
ScopeStack<ast::PipelineStage> entry_point_map; ScopeStack<ast::PipelineStage> entry_point_map;
entry_point_map.push_scope(); entry_point_map.push_scope();
for (const auto& ep : eps) {
auto* ep_ptr = ep.get(); size_t pipeline_count = 0;
ast::Function* func = nullptr; for (const auto& func : funcs) {
if (!function_stack_.get(ep_ptr->function_name(), &func)) { // The entry points will be checked later to see if their duplicated
set_error(ep_ptr->source(), if (function_stack_.has(func->name()) &&
"v-0019: Function used in entry point does not exist: '" + !entry_point_map.has(func->name())) {
ep_ptr->function_name() + "'"); set_error(func->source(),
"v-0016: function names must be unique '" + func->name() + "'");
return false; return false;
} }
if (!func->return_type()->IsVoid()) { if (func->pipeline_stage() != ast::PipelineStage::kNone) {
set_error(ep_ptr->source(), pipeline_count++;
"v-0024: Entry point function must return void: '" +
ep_ptr->function_name() + "'");
return false;
}
if (func->params().size() != 0) { if (!func->return_type()->IsVoid()) {
set_error(ep_ptr->source(), set_error(func->source(),
"v-0023: Entry point function must accept no parameters: '" + "v-0024: Entry point function must return void: '" +
ep_ptr->function_name() + "'"); func->name() + "'");
return false;
}
ast::PipelineStage pipeline_stage;
if (entry_point_map.get(ep_ptr->name(), &pipeline_stage)) {
if (pipeline_stage == ep_ptr->stage()) {
set_error(ep_ptr->source(),
"v-0020: The pair of <entry point name, pipeline stage> must "
"be unique");
return false; 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());
} }
entry_point_map.set(ep_ptr->name(), ep_ptr->stage());
function_stack_.set(func->name(), func.get());
current_function_ = func.get();
if (!ValidateFunction(func.get())) {
return false;
}
current_function_ = nullptr;
} }
if (eps.empty()) { if (pipeline_count == 0 && mod->entry_points().empty()) {
set_error(Source{0, 0}, set_error(Source{0, 0},
"v-0003: At least one of vertex, fragment or compute shader must " "v-0003: At least one of vertex, fragment or compute shader must "
"be present"); "be present");
return false; return false;
} }
entry_point_map.pop_scope();
return true;
}
bool ValidatorImpl::ValidateFunctions(const ast::FunctionList& funcs) {
for (const auto& func : funcs) {
auto* func_ptr = func.get();
if (function_stack_.has(func_ptr->name())) {
set_error(func_ptr->source(), "v-0016: function names must be unique '" +
func_ptr->name() + "'");
return false;
}
function_stack_.set(func_ptr->name(), func_ptr);
current_function_ = func_ptr;
if (!ValidateFunction(func_ptr)) {
return false;
}
current_function_ = nullptr;
}
return true; return true;
} }

View File

@ -58,9 +58,11 @@ class ValidatorImpl {
/// @returns true if the validation was successful /// @returns true if the validation was successful
bool ValidateGlobalVariables(const ast::VariableList& global_vars); bool ValidateGlobalVariables(const ast::VariableList& global_vars);
/// Validates Functions /// Validates Functions
/// @param mod the module
/// @param funcs the functions to check /// @param funcs the functions to check
/// @returns true if the validation was successful /// @returns true if the validation was successful
bool ValidateFunctions(const ast::FunctionList& funcs); bool ValidateFunctions(const ast::Module* mod,
const ast::FunctionList& funcs);
/// Validates a function /// Validates a function
/// @param func the function to check /// @param func the function to check
/// @returns true if the validation was successful /// @returns true if the validation was successful

View File

@ -34,9 +34,11 @@
#include "src/ast/int_literal.h" #include "src/ast/int_literal.h"
#include "src/ast/loop_statement.h" #include "src/ast/loop_statement.h"
#include "src/ast/member_accessor_expression.h" #include "src/ast/member_accessor_expression.h"
#include "src/ast/pipeline_stage.h"
#include "src/ast/return_statement.h" #include "src/ast/return_statement.h"
#include "src/ast/scalar_constructor_expression.h" #include "src/ast/scalar_constructor_expression.h"
#include "src/ast/sint_literal.h" #include "src/ast/sint_literal.h"
#include "src/ast/stage_decoration.h"
#include "src/ast/struct.h" #include "src/ast/struct.h"
#include "src/ast/struct_member.h" #include "src/ast/struct_member.h"
#include "src/ast/switch_statement.h" #include "src/ast/switch_statement.h"
@ -263,10 +265,8 @@ TEST_F(ValidatorTest, GlobalVariableWithStorageClass_Pass) {
auto global_var = std::make_unique<ast::Variable>( auto global_var = std::make_unique<ast::Variable>(
Source{12, 34}, "global_var", ast::StorageClass::kInput, &f32); Source{12, 34}, "global_var", ast::StorageClass::kInput, &f32);
mod()->AddGlobalVariable(std::move(global_var)); mod()->AddGlobalVariable(std::move(global_var));
AddFakeEntryPoint(); EXPECT_TRUE(v()->ValidateGlobalVariables(mod()->global_variables()))
<< v()->error();
EXPECT_TRUE(td()->Determine()) << td()->error();
EXPECT_TRUE(v()->Validate(mod())) << v()->error();
} }
TEST_F(ValidatorTest, GlobalVariableNoStorageClass_Fail) { TEST_F(ValidatorTest, GlobalVariableNoStorageClass_Fail) {
@ -341,11 +341,13 @@ TEST_F(ValidatorTest, UsingUndefinedVariableGlobalVariable_Fail) {
TEST_F(ValidatorTest, UsingUndefinedVariableGlobalVariable_Pass) { TEST_F(ValidatorTest, UsingUndefinedVariableGlobalVariable_Pass) {
// var global_var: f32 = 2.1; // var global_var: f32 = 2.1;
// fn my_func() -> f32 { // fn my_func() -> void {
// global_var = 3.14; // global_var = 3.14;
// return 3.14; // return;
// } // }
ast::type::F32Type f32; ast::type::F32Type f32;
ast::type::VoidType void_type;
auto global_var = std::make_unique<ast::Variable>( auto global_var = std::make_unique<ast::Variable>(
"global_var", ast::StorageClass::kPrivate, &f32); "global_var", ast::StorageClass::kPrivate, &f32);
global_var->set_constructor( global_var->set_constructor(
@ -354,30 +356,23 @@ TEST_F(ValidatorTest, UsingUndefinedVariableGlobalVariable_Pass) {
mod()->AddGlobalVariable(std::move(global_var)); mod()->AddGlobalVariable(std::move(global_var));
auto lhs = std::make_unique<ast::IdentifierExpression>("global_var"); auto lhs = std::make_unique<ast::IdentifierExpression>("global_var");
auto* lhs_ptr = lhs.get();
auto rhs = std::make_unique<ast::ScalarConstructorExpression>( auto rhs = std::make_unique<ast::ScalarConstructorExpression>(
std::make_unique<ast::FloatLiteral>(&f32, 3.14f)); std::make_unique<ast::FloatLiteral>(&f32, 3.14f));
auto* rhs_ptr = rhs.get();
auto return_expr = std::make_unique<ast::ScalarConstructorExpression>(
std::make_unique<ast::FloatLiteral>(&f32, 3.14f));
ast::VariableList params; ast::VariableList params;
auto func = auto func =
std::make_unique<ast::Function>("my_func", std::move(params), &f32); std::make_unique<ast::Function>("my_func", std::move(params), &void_type);
auto body = std::make_unique<ast::BlockStatement>(); auto body = std::make_unique<ast::BlockStatement>();
body->append(std::make_unique<ast::AssignmentStatement>( body->append(std::make_unique<ast::AssignmentStatement>(
Source{12, 34}, std::move(lhs), std::move(rhs))); Source{12, 34}, std::move(lhs), std::move(rhs)));
body->append(std::make_unique<ast::ReturnStatement>(std::move(return_expr))); body->append(std::make_unique<ast::ReturnStatement>());
func->set_body(std::move(body)); func->set_body(std::move(body));
auto* func_ptr = func.get(); func->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
mod()->AddFunction(std::move(func)); mod()->AddFunction(std::move(func));
AddFakeEntryPoint();
EXPECT_TRUE(td()->Determine()) << td()->error(); EXPECT_TRUE(td()->Determine()) << td()->error();
EXPECT_TRUE(td()->DetermineFunction(func_ptr)) << td()->error();
ASSERT_NE(lhs_ptr->result_type(), nullptr);
ASSERT_NE(rhs_ptr->result_type(), nullptr);
EXPECT_TRUE(v()->Validate(mod())) << v()->error(); EXPECT_TRUE(v()->Validate(mod())) << v()->error();
} }
@ -467,9 +462,31 @@ TEST_F(ValidatorTest, GlobalVariableUnique_Pass) {
var1->set_constructor(std::make_unique<ast::ScalarConstructorExpression>( var1->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
std::make_unique<ast::SintLiteral>(&i32, 0))); std::make_unique<ast::SintLiteral>(&i32, 0)));
mod()->AddGlobalVariable(std::move(var1)); mod()->AddGlobalVariable(std::move(var1));
AddFakeEntryPoint();
EXPECT_TRUE(v()->Validate(mod())) << v()->error(); 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{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) { TEST_F(ValidatorTest, AssignToConstant_Fail) {
@ -503,28 +520,6 @@ TEST_F(ValidatorTest, AssignToConstant_Fail) {
EXPECT_EQ(v()->error(), "12:34: v-0021: cannot re-assign a constant: 'a'"); EXPECT_EQ(v()->error(), "12:34: v-0021: cannot re-assign a constant: 'a'");
} }
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{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()->Validate(mod()));
EXPECT_EQ(v()->error(),
"12:34: v-0011: redeclared global identifier 'global_var'");
}
TEST_F(ValidatorTest, GlobalVariableFunctionVariableNotUnique_Fail) { TEST_F(ValidatorTest, GlobalVariableFunctionVariableNotUnique_Fail) {
// var a: f32 = 2.1; // var a: f32 = 2.1;
// fn my_func -> void { // fn my_func -> void {
@ -699,10 +694,11 @@ TEST_F(ValidatorTest, RedeclaredIdentifierDifferentFunctions_Pass) {
std::move(var1))); std::move(var1)));
body1->append(std::make_unique<ast::ReturnStatement>()); body1->append(std::make_unique<ast::ReturnStatement>());
func1->set_body(std::move(body1)); func1->set_body(std::move(body1));
func1->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
mod()->AddFunction(std::move(func0)); mod()->AddFunction(std::move(func0));
mod()->AddFunction(std::move(func1)); mod()->AddFunction(std::move(func1));
AddFakeEntryPoint();
EXPECT_TRUE(td()->Determine()) << td()->error(); EXPECT_TRUE(td()->Determine()) << td()->error();
EXPECT_TRUE(v()->Validate(mod())) << v()->error(); EXPECT_TRUE(v()->Validate(mod())) << v()->error();

View File

@ -24,22 +24,4 @@ ValidatorTestHelper::ValidatorTestHelper() {
ValidatorTestHelper::~ValidatorTestHelper() = default; ValidatorTestHelper::~ValidatorTestHelper() = default;
void ValidatorTestHelper::AddFakeEntryPoint() {
// entry_point vertex as "fake_entry_point" = fake_func
// fn fake_func() -> void {}
auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kVertex,
"fake_entry_point", "fake_func");
ast::VariableList fake_params;
auto fake_func = std::make_unique<ast::Function>(
"fake_func", std::move(fake_params),
ctx_.type_mgr().Get(std::make_unique<ast::type::VoidType>()));
auto fake_body = std::make_unique<ast::BlockStatement>();
auto return_stmt = std::make_unique<ast::ReturnStatement>();
fake_body->append(std::move(return_stmt));
fake_func->set_body(std::move(fake_body));
mod()->AddFunction(std::move(fake_func));
mod()->AddEntryPoint(std::move(ep));
return;
}
} // namespace tint } // namespace tint

View File

@ -37,8 +37,6 @@ class ValidatorTestHelper {
/// A handle to the created module /// A handle to the created module
/// @return a pointer to the test module /// @return a pointer to the test module
ast::Module* mod() { return &mod_; } ast::Module* mod() { return &mod_; }
/// Creates a function and add an entry point to it
void AddFakeEntryPoint();
private: private:
std::unique_ptr<ValidatorImpl> v_; std::unique_ptr<ValidatorImpl> v_;