Move function validation from Validator to Resolver
* Fixed many tests that now failed validation. Most of the time, functions declared that they returned a type, but with no return statement. * ProgramBuilder::WrapInFunction now returns the function is creates, and std::moves its StatementList. * ProgramBuilder: Added Return function to create ast::ReturnStatements more easily. Bug: tint:642 Change-Id: I3011314e66e264ebd7b89bf9271392391be6a0e5 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/45382 Reviewed-by: Antonio Maiorano <amaiorano@google.com> Reviewed-by: Ben Clayton <bclayton@google.com> Commit-Queue: Antonio Maiorano <amaiorano@google.com>
This commit is contained in:
parent
b4f11f3ff3
commit
03c01b5266
|
@ -469,6 +469,7 @@ if(${TINT_BUILD_TESTS})
|
|||
program_test.cc
|
||||
resolver/assignment_validation_test.cc
|
||||
resolver/decoration_validation_test.cc
|
||||
resolver/function_validation_test.cc
|
||||
resolver/host_shareable_validation_test.cc
|
||||
resolver/intrinsic_test.cc
|
||||
resolver/is_host_shareable_test.cc
|
||||
|
|
|
@ -97,8 +97,8 @@ ast::Statement* ProgramBuilder::WrapInStatement(ast::Statement* stmt) {
|
|||
return stmt;
|
||||
}
|
||||
|
||||
void ProgramBuilder::WrapInFunction(ast::StatementList stmts) {
|
||||
Func("test_function", {}, ty.void_(), stmts, {});
|
||||
ast::Function* ProgramBuilder::WrapInFunction(ast::StatementList stmts) {
|
||||
return Func("test_function", {}, ty.void_(), std::move(stmts), {});
|
||||
}
|
||||
|
||||
} // namespace tint
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "src/ast/loop_statement.h"
|
||||
#include "src/ast/member_accessor_expression.h"
|
||||
#include "src/ast/module.h"
|
||||
#include "src/ast/return_statement.h"
|
||||
#include "src/ast/scalar_constructor_expression.h"
|
||||
#include "src/ast/sint_literal.h"
|
||||
#include "src/ast/stride_decoration.h"
|
||||
|
@ -1031,6 +1032,14 @@ class ProgramBuilder {
|
|||
return func;
|
||||
}
|
||||
|
||||
/// Creates an ast::ReturnStatement with the input args
|
||||
/// @param args arguments to construct a return statement with
|
||||
/// @returns the return statement pointer
|
||||
template <typename... Args>
|
||||
ast::ReturnStatement* Return(Args&&... args) {
|
||||
return create<ast::ReturnStatement>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/// Creates a ast::Struct and type::Struct, registering the type::Struct with
|
||||
/// the AST().ConstructedTypes().
|
||||
/// @param source the source information
|
||||
|
@ -1206,14 +1215,16 @@ class ProgramBuilder {
|
|||
/// Wraps the list of arguments in a simple function so that each is reachable
|
||||
/// by the Resolver.
|
||||
/// @param args a mix of ast::Expression, ast::Statement, ast::Variables.
|
||||
/// @returns the function
|
||||
template <typename... ARGS>
|
||||
void WrapInFunction(ARGS&&... args) {
|
||||
ast::Function* WrapInFunction(ARGS&&... args) {
|
||||
ast::StatementList stmts{WrapInStatement(std::forward<ARGS>(args))...};
|
||||
WrapInFunction(stmts);
|
||||
return WrapInFunction(std::move(stmts));
|
||||
}
|
||||
/// @param stmts a list of ast::Statement that will be wrapped by a function,
|
||||
/// so that each statement is reachable by the Resolver.
|
||||
void WrapInFunction(ast::StatementList stmts);
|
||||
/// @returns the function
|
||||
ast::Function* WrapInFunction(ast::StatementList stmts);
|
||||
|
||||
/// The builder types
|
||||
TypesBuilder const ty{this};
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "src/ast/access_decoration.h"
|
||||
#include "src/ast/constant_id_decoration.h"
|
||||
#include "src/ast/return_statement.h"
|
||||
#include "src/ast/stage_decoration.h"
|
||||
#include "src/ast/struct_block_decoration.h"
|
||||
#include "src/ast/workgroup_decoration.h"
|
||||
|
@ -84,6 +85,41 @@ ast::Decoration* createDecoration(const Source& source,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
using FunctionReturnTypeDecorationTest = TestWithParams;
|
||||
TEST_P(FunctionReturnTypeDecorationTest, IsValid) {
|
||||
auto params = GetParam();
|
||||
|
||||
Func("main", ast::VariableList{}, ty.f32(),
|
||||
ast::StatementList{create<ast::ReturnStatement>(Expr(1.f))},
|
||||
ast::DecorationList{
|
||||
create<ast::StageDecoration>(ast::PipelineStage::kVertex)},
|
||||
ast::DecorationList{createDecoration({}, *this, params.kind)});
|
||||
|
||||
if (params.should_pass) {
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
} else {
|
||||
EXPECT_FALSE(r()->Resolve()) << r()->error();
|
||||
EXPECT_EQ(r()->error(),
|
||||
"error: decoration is not valid for function return types");
|
||||
}
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ResolverDecorationValidationTest,
|
||||
FunctionReturnTypeDecorationTest,
|
||||
testing::Values(TestParams{DecorationKind::kAccess, false},
|
||||
TestParams{DecorationKind::kAlign, false},
|
||||
TestParams{DecorationKind::kBinding, false},
|
||||
TestParams{DecorationKind::kBuiltin, true},
|
||||
TestParams{DecorationKind::kConstantId, false},
|
||||
TestParams{DecorationKind::kGroup, false},
|
||||
TestParams{DecorationKind::kLocation, true},
|
||||
TestParams{DecorationKind::kOffset, false},
|
||||
TestParams{DecorationKind::kSize, false},
|
||||
TestParams{DecorationKind::kStage, false},
|
||||
TestParams{DecorationKind::kStride, false},
|
||||
TestParams{DecorationKind::kStructBlock, false},
|
||||
TestParams{DecorationKind::kWorkgroup, false}));
|
||||
|
||||
using ArrayDecorationTest = TestWithParams;
|
||||
|
||||
TEST_P(ArrayDecorationTest, IsValid) {
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/ast/return_statement.h"
|
||||
#include "src/resolver/resolver.h"
|
||||
#include "src/resolver/resolver_test_helper.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
namespace tint {
|
||||
namespace {
|
||||
|
||||
class ResolverFunctionValidationTest : public resolver::TestHelper,
|
||||
public testing::Test {};
|
||||
|
||||
TEST_F(ResolverFunctionValidationTest, FunctionNamesMustBeUnique_fail) {
|
||||
// fn func -> i32 { return 2; }
|
||||
// fn func -> i32 { return 2; }
|
||||
Func("func", ast::VariableList{}, ty.i32(),
|
||||
ast::StatementList{
|
||||
create<ast::ReturnStatement>(Expr(2)),
|
||||
},
|
||||
ast::DecorationList{});
|
||||
|
||||
Func(Source{Source::Location{12, 34}}, "func", ast::VariableList{}, ty.i32(),
|
||||
ast::StatementList{
|
||||
create<ast::ReturnStatement>(Expr(2)),
|
||||
},
|
||||
ast::DecorationList{});
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error v-0016: function names must be unique 'func'");
|
||||
}
|
||||
|
||||
TEST_F(ResolverFunctionValidationTest, FunctionEndWithoutReturnStatement_Fail) {
|
||||
// fn func -> int { var a:i32 = 2; }
|
||||
|
||||
auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
|
||||
|
||||
Func(Source{Source::Location{12, 34}}, "func", ast::VariableList{}, ty.i32(),
|
||||
ast::StatementList{
|
||||
create<ast::VariableDeclStatement>(var),
|
||||
},
|
||||
ast::DecorationList{});
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(
|
||||
r()->error(),
|
||||
"12:34 error v-0002: non-void function must end with a return statement");
|
||||
}
|
||||
|
||||
TEST_F(ResolverFunctionValidationTest,
|
||||
FunctionEndWithoutReturnStatementEmptyBody_Fail) {
|
||||
// fn func -> int {}
|
||||
|
||||
Func(Source{Source::Location{12, 34}}, "func", ast::VariableList{}, ty.i32(),
|
||||
ast::StatementList{}, ast::DecorationList{});
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(
|
||||
r()->error(),
|
||||
"12:34 error v-0002: non-void function must end with a return statement");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace tint
|
|
@ -222,9 +222,65 @@ bool Resolver::Functions(const ast::FunctionList& funcs) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Resolver::ValidateParameter(const ast::Variable* param) {
|
||||
if (auto* r = param->type()->UnwrapAll()->As<type::Array>()) {
|
||||
if (r->IsRuntimeArray()) {
|
||||
diagnostics_.add_error(
|
||||
"v-0015",
|
||||
"runtime arrays may only appear as the last member of a struct",
|
||||
param->source());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Resolver::ValidateFunction(const ast::Function* func) {
|
||||
if (symbol_to_function_.find(func->symbol()) != symbol_to_function_.end()) {
|
||||
diagnostics_.add_error("v-0016",
|
||||
"function names must be unique '" +
|
||||
builder_->Symbols().NameFor(func->symbol()) +
|
||||
"'",
|
||||
func->source());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto* param : func->params()) {
|
||||
if (!ValidateParameter(param)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!func->return_type()->Is<type::Void>()) {
|
||||
if (!func->get_last_statement() ||
|
||||
!func->get_last_statement()->Is<ast::ReturnStatement>()) {
|
||||
diagnostics_.add_error(
|
||||
"v-0002", "non-void function must end with a return statement",
|
||||
func->source());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto* deco : func->return_type_decorations()) {
|
||||
if (!(deco->Is<ast::BuiltinDecoration>() ||
|
||||
deco->Is<ast::LocationDecoration>())) {
|
||||
diagnostics_.add_error(
|
||||
"decoration is not valid for function return types",
|
||||
deco->source());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Resolver::Function(ast::Function* func) {
|
||||
auto* func_info = function_infos_.Create<FunctionInfo>(func);
|
||||
|
||||
if (!ValidateFunction(func)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ScopedAssignment<FunctionInfo*> sa(current_function_, func_info);
|
||||
|
||||
variable_stack_.push_scope();
|
||||
|
|
|
@ -224,16 +224,15 @@ class Resolver {
|
|||
// AST and Type validation methods
|
||||
// Each return true on success, false on failure.
|
||||
bool ValidateBinary(ast::BinaryExpression* expr);
|
||||
bool ValidateParameter(const ast::Variable* param);
|
||||
bool ValidateFunction(const ast::Function* func);
|
||||
bool ValidateStructure(const type::Struct* st);
|
||||
|
||||
/// @returns the semantic information for the array `arr`, building it if it
|
||||
/// hasn't been constructed already. If an error is raised, nullptr is
|
||||
/// returned.
|
||||
const semantic::Array* Array(type::Array*);
|
||||
|
||||
/// @returns returns true if input struct is valid
|
||||
/// @param st the struct to validate
|
||||
bool ValidateStructure(const type::Struct* st);
|
||||
|
||||
/// @returns the StructInfo for the structure `str`, building it if it hasn't
|
||||
/// been constructed already. If an error is raised, nullptr is returned.
|
||||
StructInfo* Structure(type::Struct* str);
|
||||
|
@ -286,7 +285,7 @@ class Resolver {
|
|||
BlockInfo* current_block_ = nullptr;
|
||||
ScopeStack<VariableInfo*> variable_stack_;
|
||||
std::unordered_map<Symbol, FunctionInfo*> symbol_to_function_;
|
||||
std::unordered_map<ast::Function*, FunctionInfo*> function_to_info_;
|
||||
std::unordered_map<const ast::Function*, FunctionInfo*> function_to_info_;
|
||||
std::unordered_map<ast::Variable*, VariableInfo*> variable_to_info_;
|
||||
std::unordered_map<ast::CallExpression*, FunctionCallInfo> function_calls_;
|
||||
std::unordered_map<ast::Expression*, ExpressionInfo> expr_info_;
|
||||
|
|
|
@ -271,7 +271,7 @@ TEST_F(ResolverTest, Stmt_Switch) {
|
|||
|
||||
TEST_F(ResolverTest, Stmt_Call) {
|
||||
ast::VariableList params;
|
||||
Func("my_func", params, ty.f32(), ast::StatementList{},
|
||||
Func("my_func", params, ty.f32(), ast::StatementList{Return(Expr(0.0f))},
|
||||
ast::DecorationList{});
|
||||
|
||||
auto* expr = Call("my_func");
|
||||
|
@ -325,7 +325,7 @@ TEST_F(ResolverTest, Stmt_VariableDecl_ModuleScope) {
|
|||
}
|
||||
|
||||
TEST_F(ResolverTest, Stmt_VariableDecl_OuterScopeAfterInnerScope) {
|
||||
// fn func_i32() -> i32 {
|
||||
// fn func_i32() -> void {
|
||||
// {
|
||||
// var foo : i32 = 2;
|
||||
// var bar : i32 = foo;
|
||||
|
@ -359,11 +359,11 @@ TEST_F(ResolverTest, Stmt_VariableDecl_OuterScopeAfterInnerScope) {
|
|||
auto* bar_f32_init = bar_f32->constructor();
|
||||
auto* bar_f32_decl = create<ast::VariableDeclStatement>(bar_f32);
|
||||
|
||||
Func("func", params, ty.f32(),
|
||||
Func("func", params, ty.void_(),
|
||||
ast::StatementList{inner, foo_f32_decl, bar_f32_decl},
|
||||
ast::DecorationList{});
|
||||
|
||||
EXPECT_TRUE(r()->Resolve());
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
ASSERT_NE(TypeOf(foo_i32_init), nullptr);
|
||||
EXPECT_TRUE(TypeOf(foo_i32_init)->Is<type::I32>());
|
||||
ASSERT_NE(TypeOf(foo_f32_init), nullptr);
|
||||
|
@ -381,11 +381,11 @@ TEST_F(ResolverTest, Stmt_VariableDecl_OuterScopeAfterInnerScope) {
|
|||
}
|
||||
|
||||
TEST_F(ResolverTest, Stmt_VariableDecl_ModuleScopeAfterFunctionScope) {
|
||||
// fn func_i32() -> i32 {
|
||||
// fn func_i32() -> void {
|
||||
// var foo : i32 = 2;
|
||||
// }
|
||||
// var foo : f32 = 2.0;
|
||||
// fn func_f32() -> f32 {
|
||||
// fn func_f32() -> void {
|
||||
// var bar : f32 = foo;
|
||||
// }
|
||||
|
||||
|
@ -395,7 +395,7 @@ TEST_F(ResolverTest, Stmt_VariableDecl_ModuleScopeAfterFunctionScope) {
|
|||
auto* fn_i32 = Var("foo", ty.i32(), ast::StorageClass::kNone, Expr(2));
|
||||
auto* fn_i32_init = fn_i32->constructor();
|
||||
auto* fn_i32_decl = create<ast::VariableDeclStatement>(fn_i32);
|
||||
Func("func_i32", params, ty.i32(), ast::StatementList{fn_i32_decl},
|
||||
Func("func_i32", params, ty.void_(), ast::StatementList{fn_i32_decl},
|
||||
ast::DecorationList{});
|
||||
|
||||
// Declare f32 "foo" at module scope
|
||||
|
@ -407,10 +407,10 @@ TEST_F(ResolverTest, Stmt_VariableDecl_ModuleScopeAfterFunctionScope) {
|
|||
auto* fn_f32 = Var("bar", ty.f32(), ast::StorageClass::kNone, Expr("foo"));
|
||||
auto* fn_f32_init = fn_f32->constructor();
|
||||
auto* fn_f32_decl = create<ast::VariableDeclStatement>(fn_f32);
|
||||
Func("func_f32", params, ty.f32(), ast::StatementList{fn_f32_decl},
|
||||
Func("func_f32", params, ty.void_(), ast::StatementList{fn_f32_decl},
|
||||
ast::DecorationList{});
|
||||
|
||||
EXPECT_TRUE(r()->Resolve());
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
ASSERT_NE(TypeOf(mod_init), nullptr);
|
||||
EXPECT_TRUE(TypeOf(mod_init)->Is<type::F32>());
|
||||
ASSERT_NE(TypeOf(fn_i32_init), nullptr);
|
||||
|
@ -529,7 +529,7 @@ TEST_F(ResolverTest, Expr_Bitcast) {
|
|||
|
||||
TEST_F(ResolverTest, Expr_Call) {
|
||||
ast::VariableList params;
|
||||
Func("my_func", params, ty.f32(), ast::StatementList{},
|
||||
Func("my_func", params, ty.f32(), ast::StatementList{Return(Expr(0.0f))},
|
||||
ast::DecorationList{});
|
||||
|
||||
auto* call = Call("my_func");
|
||||
|
@ -543,7 +543,8 @@ TEST_F(ResolverTest, Expr_Call) {
|
|||
|
||||
TEST_F(ResolverTest, Expr_Call_InBinaryOp) {
|
||||
ast::VariableList params;
|
||||
Func("func", params, ty.f32(), ast::StatementList{}, ast::DecorationList{});
|
||||
Func("func", params, ty.f32(), ast::StatementList{Return(Expr(0.0f))},
|
||||
ast::DecorationList{});
|
||||
|
||||
auto* expr = Add(Call("func"), Call("func"));
|
||||
WrapInFunction(expr);
|
||||
|
@ -556,7 +557,7 @@ TEST_F(ResolverTest, Expr_Call_InBinaryOp) {
|
|||
|
||||
TEST_F(ResolverTest, Expr_Call_WithParams) {
|
||||
ast::VariableList params;
|
||||
Func("my_func", params, ty.f32(), ast::StatementList{},
|
||||
Func("my_func", params, ty.void_(), ast::StatementList{},
|
||||
ast::DecorationList{});
|
||||
|
||||
auto* param = Expr(2.4f);
|
||||
|
@ -671,7 +672,7 @@ TEST_F(ResolverTest, Expr_Identifier_FunctionVariable_Const) {
|
|||
auto* var = Const("my_var", ty.f32());
|
||||
auto* assign = create<ast::AssignmentStatement>(my_var_a, my_var_b);
|
||||
|
||||
Func("my_func", ast::VariableList{}, ty.f32(),
|
||||
Func("my_func", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{
|
||||
create<ast::VariableDeclStatement>(var),
|
||||
assign,
|
||||
|
@ -696,7 +697,7 @@ TEST_F(ResolverTest, Expr_Identifier_FunctionVariable) {
|
|||
|
||||
auto* var = Var("my_var", ty.f32(), ast::StorageClass::kNone);
|
||||
|
||||
Func("my_func", ast::VariableList{}, ty.f32(),
|
||||
Func("my_func", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{
|
||||
create<ast::VariableDeclStatement>(var),
|
||||
assign,
|
||||
|
@ -721,7 +722,7 @@ TEST_F(ResolverTest, Expr_Identifier_Function_Ptr) {
|
|||
auto* my_var_b = Expr("my_var");
|
||||
auto* assign = create<ast::AssignmentStatement>(my_var_a, my_var_b);
|
||||
|
||||
Func("my_func", ast::VariableList{}, ty.f32(),
|
||||
Func("my_func", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{
|
||||
create<ast::VariableDeclStatement>(
|
||||
Var("my_var", ty.pointer<f32>(ast::StorageClass::kFunction),
|
||||
|
@ -743,8 +744,8 @@ TEST_F(ResolverTest, Expr_Identifier_Function_Ptr) {
|
|||
}
|
||||
|
||||
TEST_F(ResolverTest, Expr_Call_Function) {
|
||||
Func("my_func", ast::VariableList{}, ty.f32(), ast::StatementList{},
|
||||
ast::DecorationList{});
|
||||
Func("my_func", ast::VariableList{}, ty.f32(),
|
||||
ast::StatementList{Return(Expr(0.0f))}, ast::DecorationList{});
|
||||
|
||||
auto* call = Call("my_func");
|
||||
WrapInFunction(call);
|
||||
|
@ -770,7 +771,7 @@ TEST_F(ResolverTest, Function_RegisterInputOutputVariables) {
|
|||
auto* priv_var = Global("priv_var", ty.f32(), ast::StorageClass::kPrivate);
|
||||
|
||||
auto* func = Func(
|
||||
"my_func", ast::VariableList{}, ty.f32(),
|
||||
"my_func", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{
|
||||
create<ast::AssignmentStatement>(Expr("out_var"), Expr("in_var")),
|
||||
create<ast::AssignmentStatement>(Expr("wg_var"), Expr("wg_var")),
|
||||
|
@ -806,11 +807,11 @@ TEST_F(ResolverTest, Function_RegisterInputOutputVariables_SubFunction) {
|
|||
create<ast::AssignmentStatement>(Expr("wg_var"), Expr("wg_var")),
|
||||
create<ast::AssignmentStatement>(Expr("sb_var"), Expr("sb_var")),
|
||||
create<ast::AssignmentStatement>(Expr("priv_var"), Expr("priv_var")),
|
||||
},
|
||||
Return(Expr(0.0f))},
|
||||
ast::DecorationList{});
|
||||
|
||||
auto* func2 = Func(
|
||||
"func", ast::VariableList{}, ty.f32(),
|
||||
"func", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{
|
||||
create<ast::AssignmentStatement>(Expr("out_var"), Call("my_func")),
|
||||
},
|
||||
|
@ -834,7 +835,7 @@ TEST_F(ResolverTest, Function_NotRegisterFunctionVariable) {
|
|||
auto* var = Var("in_var", ty.f32(), ast::StorageClass::kFunction);
|
||||
|
||||
auto* func =
|
||||
Func("my_func", ast::VariableList{}, ty.f32(),
|
||||
Func("my_func", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{
|
||||
create<ast::VariableDeclStatement>(var),
|
||||
create<ast::AssignmentStatement>(Expr("var"), Expr(1.f)),
|
||||
|
@ -1276,7 +1277,7 @@ TEST_F(ResolverTest, StorageClass_SetsIfMissing) {
|
|||
auto* var = Var("var", ty.i32(), ast::StorageClass::kNone);
|
||||
|
||||
auto* stmt = create<ast::VariableDeclStatement>(var);
|
||||
Func("func", ast::VariableList{}, ty.i32(), ast::StatementList{stmt},
|
||||
Func("func", ast::VariableList{}, ty.void_(), ast::StatementList{stmt},
|
||||
ast::DecorationList{});
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
@ -1287,7 +1288,7 @@ TEST_F(ResolverTest, StorageClass_SetsIfMissing) {
|
|||
TEST_F(ResolverTest, StorageClass_DoesNotSetOnConst) {
|
||||
auto* var = Const("var", ty.i32());
|
||||
auto* stmt = create<ast::VariableDeclStatement>(var);
|
||||
Func("func", ast::VariableList{}, ty.i32(), ast::StatementList{stmt},
|
||||
Func("func", ast::VariableList{}, ty.void_(), ast::StatementList{stmt},
|
||||
ast::DecorationList{});
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
@ -1310,23 +1311,22 @@ TEST_F(ResolverTest, Function_EntryPoints_StageDecoration) {
|
|||
|
||||
ast::VariableList params;
|
||||
auto* func_b =
|
||||
Func("b", params, ty.f32(), ast::StatementList{}, ast::DecorationList{});
|
||||
auto* func_c =
|
||||
Func("c", params, ty.f32(),
|
||||
ast::StatementList{
|
||||
create<ast::AssignmentStatement>(Expr("second"), Call("b")),
|
||||
},
|
||||
Func("b", params, ty.f32(), ast::StatementList{Return(Expr(0.0f))},
|
||||
ast::DecorationList{});
|
||||
auto* func_c = Func("c", params, ty.f32(),
|
||||
ast::StatementList{create<ast::AssignmentStatement>(
|
||||
Expr("second"), Call("b")),
|
||||
Return(Expr(0.0f))},
|
||||
ast::DecorationList{});
|
||||
|
||||
auto* func_a =
|
||||
Func("a", params, ty.f32(),
|
||||
ast::StatementList{
|
||||
create<ast::AssignmentStatement>(Expr("first"), Call("c")),
|
||||
},
|
||||
ast::DecorationList{});
|
||||
auto* func_a = Func("a", params, ty.f32(),
|
||||
ast::StatementList{create<ast::AssignmentStatement>(
|
||||
Expr("first"), Call("c")),
|
||||
Return(Expr(0.0f))},
|
||||
ast::DecorationList{});
|
||||
|
||||
auto* ep_1 =
|
||||
Func("ep_1", params, ty.f32(),
|
||||
Func("ep_1", params, ty.void_(),
|
||||
ast::StatementList{
|
||||
create<ast::AssignmentStatement>(Expr("call_a"), Call("a")),
|
||||
create<ast::AssignmentStatement>(Expr("call_b"), Call("b")),
|
||||
|
@ -1336,7 +1336,7 @@ TEST_F(ResolverTest, Function_EntryPoints_StageDecoration) {
|
|||
});
|
||||
|
||||
auto* ep_2 =
|
||||
Func("ep_2", params, ty.f32(),
|
||||
Func("ep_2", params, ty.void_(),
|
||||
ast::StatementList{
|
||||
create<ast::AssignmentStatement>(Expr("call_c"), Call("c")),
|
||||
},
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/ast/return_statement.h"
|
||||
#include "src/ast/stage_decoration.h"
|
||||
#include "src/ast/struct_block_decoration.h"
|
||||
#include "src/resolver/resolver.h"
|
||||
|
@ -96,6 +97,34 @@ TEST_F(ResolverTypeValidationTest, RuntimeArrayIsNotLast_Fail) {
|
|||
"member of a struct");
|
||||
}
|
||||
|
||||
TEST_F(ResolverTypeValidationTest, RuntimeArrayAsParameter_Fail) {
|
||||
// fn func(a : array<u32>) {}
|
||||
// [[stage(vertex)]] fn main() {}
|
||||
|
||||
auto* param = Var(Source{Source::Location{12, 34}}, "a", ty.array<i32>(),
|
||||
ast::StorageClass::kNone);
|
||||
|
||||
Func("func", ast::VariableList{param}, ty.void_(),
|
||||
ast::StatementList{
|
||||
create<ast::ReturnStatement>(),
|
||||
},
|
||||
ast::DecorationList{});
|
||||
|
||||
Func("main", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{
|
||||
create<ast::ReturnStatement>(),
|
||||
},
|
||||
ast::DecorationList{
|
||||
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
|
||||
});
|
||||
|
||||
EXPECT_FALSE(r()->Resolve()) << r()->error();
|
||||
EXPECT_EQ(
|
||||
r()->error(),
|
||||
"12:34 error v-0015: runtime arrays may only appear as the last member "
|
||||
"of a struct");
|
||||
}
|
||||
|
||||
TEST_F(ResolverTypeValidationTest, AliasRuntimeArrayIsNotLast_Fail) {
|
||||
// [[Block]]
|
||||
// type RTArr = array<u32>;
|
||||
|
|
|
@ -116,7 +116,7 @@ TEST_F(ResolverValidationTest, Stmt_Call_recursive) {
|
|||
auto* call_expr = Call("main");
|
||||
ast::VariableList params0;
|
||||
|
||||
Func("main", params0, ty.f32(),
|
||||
Func("main", params0, ty.void_(),
|
||||
ast::StatementList{
|
||||
create<ast::CallStatement>(call_expr),
|
||||
},
|
||||
|
@ -245,7 +245,7 @@ TEST_F(ResolverValidationTest, StorageClass_NonFunctionClassError) {
|
|||
auto* var = Var("var", ty.i32(), ast::StorageClass::kWorkgroup);
|
||||
|
||||
auto* stmt = create<ast::VariableDeclStatement>(var);
|
||||
Func("func", ast::VariableList{}, ty.i32(), ast::StatementList{stmt},
|
||||
Func("func", ast::VariableList{}, ty.void_(), ast::StatementList{stmt},
|
||||
ast::DecorationList{});
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
|
|
|
@ -121,42 +121,6 @@ INSTANTIATE_TEST_SUITE_P(
|
|||
DecorationTestParams{DecorationKind::kStructBlock, false},
|
||||
DecorationTestParams{DecorationKind::kWorkgroup, true}));
|
||||
|
||||
using FunctionReturnTypeDecorationTest = ValidatorDecorationsTestWithParams;
|
||||
TEST_P(FunctionReturnTypeDecorationTest, Decoration_IsValid) {
|
||||
auto params = GetParam();
|
||||
|
||||
Func("main", ast::VariableList{}, ty.f32(),
|
||||
ast::StatementList{create<ast::ReturnStatement>(Expr(1.f))},
|
||||
ast::DecorationList{
|
||||
create<ast::StageDecoration>(ast::PipelineStage::kVertex)},
|
||||
ast::DecorationList{createDecoration(*this, params.kind)});
|
||||
|
||||
ValidatorImpl& v = Build();
|
||||
|
||||
if (params.should_pass) {
|
||||
EXPECT_TRUE(v.Validate());
|
||||
} else {
|
||||
EXPECT_FALSE(v.Validate());
|
||||
EXPECT_EQ(v.error(), "decoration is not valid for function return types");
|
||||
}
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ValidatorTest,
|
||||
FunctionReturnTypeDecorationTest,
|
||||
testing::Values(DecorationTestParams{DecorationKind::kAccess, false},
|
||||
DecorationTestParams{DecorationKind::kAlign, false},
|
||||
DecorationTestParams{DecorationKind::kBinding, false},
|
||||
DecorationTestParams{DecorationKind::kBuiltin, true},
|
||||
DecorationTestParams{DecorationKind::kConstantId, false},
|
||||
DecorationTestParams{DecorationKind::kGroup, false},
|
||||
DecorationTestParams{DecorationKind::kLocation, true},
|
||||
DecorationTestParams{DecorationKind::kOffset, false},
|
||||
DecorationTestParams{DecorationKind::kSize, false},
|
||||
DecorationTestParams{DecorationKind::kStage, false},
|
||||
DecorationTestParams{DecorationKind::kStride, false},
|
||||
DecorationTestParams{DecorationKind::kStructBlock, false},
|
||||
DecorationTestParams{DecorationKind::kWorkgroup, false}));
|
||||
|
||||
using VariableDecorationTest = ValidatorDecorationsTestWithParams;
|
||||
TEST_P(VariableDecorationTest, Decoration_IsValid) {
|
||||
auto params = GetParam();
|
||||
|
|
|
@ -56,37 +56,6 @@ TEST_F(ValidateFunctionTest,
|
|||
EXPECT_TRUE(v.Validate());
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionTest, FunctionEndWithoutReturnStatement_Fail) {
|
||||
// fn func -> int { var a:i32 = 2; }
|
||||
|
||||
auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
|
||||
|
||||
Func(Source{Source::Location{12, 34}}, "func", ast::VariableList{}, ty.i32(),
|
||||
ast::StatementList{
|
||||
create<ast::VariableDeclStatement>(var),
|
||||
},
|
||||
ast::DecorationList{});
|
||||
|
||||
ValidatorImpl& v = Build();
|
||||
|
||||
EXPECT_FALSE(v.Validate());
|
||||
EXPECT_EQ(v.error(),
|
||||
"12:34 v-0002: non-void function must end with a return statement");
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionTest, FunctionEndWithoutReturnStatementEmptyBody_Fail) {
|
||||
// fn func -> int {}
|
||||
|
||||
Func(Source{Source::Location{12, 34}}, "func", ast::VariableList{}, ty.i32(),
|
||||
ast::StatementList{}, ast::DecorationList{});
|
||||
|
||||
ValidatorImpl& v = Build();
|
||||
|
||||
EXPECT_FALSE(v.Validate());
|
||||
EXPECT_EQ(v.error(),
|
||||
"12:34 v-0002: non-void function must end with a return statement");
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionTest, FunctionTypeMustMatchReturnStatementType_Pass) {
|
||||
// [[stage(vertex)]]
|
||||
// fn func -> void { return; }
|
||||
|
@ -204,26 +173,6 @@ TEST_F(ValidateFunctionTest,
|
|||
"return type, returned '__u32', expected '__alias_tint_symbol_1__f32'");
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionTest, FunctionNamesMustBeUnique_fail) {
|
||||
// fn func -> i32 { return 2; }
|
||||
// fn func -> i32 { return 2; }
|
||||
Func("func", ast::VariableList{}, ty.i32(),
|
||||
ast::StatementList{
|
||||
create<ast::ReturnStatement>(Expr(2)),
|
||||
},
|
||||
ast::DecorationList{});
|
||||
|
||||
Func(Source{Source::Location{12, 34}}, "func", ast::VariableList{}, ty.i32(),
|
||||
ast::StatementList{
|
||||
create<ast::ReturnStatement>(Expr(2)),
|
||||
},
|
||||
ast::DecorationList{});
|
||||
|
||||
ValidatorImpl& v = Build();
|
||||
|
||||
EXPECT_FALSE(v.Validate());
|
||||
EXPECT_EQ(v.error(), "12:34 v-0016: function names must be unique 'func'");
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionTest, PipelineStage_MustBeUnique_Fail) {
|
||||
// [[stage(fragment)]]
|
||||
|
|
|
@ -168,57 +168,11 @@ bool ValidatorImpl::ValidateEntryPoint(const ast::FunctionList& funcs) {
|
|||
}
|
||||
|
||||
bool ValidatorImpl::ValidateFunction(const ast::Function* func) {
|
||||
if (function_stack_.has(func->symbol())) {
|
||||
add_error(func->source(), "v-0016",
|
||||
"function names must be unique '" +
|
||||
program_->Symbols().NameFor(func->symbol()) + "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
function_stack_.set(func->symbol(), func);
|
||||
|
||||
variable_stack_.push_scope();
|
||||
|
||||
for (auto* param : func->params()) {
|
||||
variable_stack_.set(param->symbol(), param);
|
||||
if (!ValidateParameter(param)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// TODO(amaiorano): Remove ValidateFunction once we've moved all the statement
|
||||
// validation to Resovler
|
||||
if (!ValidateStatements(func->body())) {
|
||||
return false;
|
||||
}
|
||||
variable_stack_.pop_scope();
|
||||
|
||||
if (!current_function_->return_type()->Is<type::Void>()) {
|
||||
if (!func->get_last_statement() ||
|
||||
!func->get_last_statement()->Is<ast::ReturnStatement>()) {
|
||||
add_error(func->source(), "v-0002",
|
||||
"non-void function must end with a return statement");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto* deco : current_function_->return_type_decorations()) {
|
||||
if (!(deco->Is<ast::BuiltinDecoration>() ||
|
||||
deco->Is<ast::LocationDecoration>())) {
|
||||
add_error(deco->source(),
|
||||
"decoration is not valid for function return types");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidatorImpl::ValidateParameter(const ast::Variable* param) {
|
||||
if (auto* r = param->type()->UnwrapAll()->As<type::Array>()) {
|
||||
if (r->IsRuntimeArray()) {
|
||||
add_error(
|
||||
param->source(), "v-0015",
|
||||
"runtime arrays may only appear as the last member of a struct");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -76,10 +76,6 @@ class ValidatorImpl {
|
|||
/// @param func the function to check
|
||||
/// @returns true if the validation was successful
|
||||
bool ValidateFunction(const ast::Function* func);
|
||||
/// Validates a function parameter
|
||||
/// @param param the function parameter to check
|
||||
/// @returns true if the validation was successful
|
||||
bool ValidateParameter(const ast::Variable* param);
|
||||
/// Validates a block of statements
|
||||
/// @param block the statements to check
|
||||
/// @returns true if the validation was successful
|
||||
|
@ -147,7 +143,6 @@ class ValidatorImpl {
|
|||
const Program* program_;
|
||||
diag::List diags_;
|
||||
ScopeStack<const ast::Variable*> variable_stack_;
|
||||
ScopeStack<const ast::Function*> function_stack_;
|
||||
ast::Function* current_function_ = nullptr;
|
||||
};
|
||||
|
||||
|
|
|
@ -44,33 +44,5 @@ TEST_F(ValidatorTypeTest, RuntimeArrayInFunction_Fail) {
|
|||
"of a struct");
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTypeTest, RuntimeArrayAsParameter_Fail) {
|
||||
// fn func(a : array<u32>) {}
|
||||
// [[stage(vertex)]] fn main() {}
|
||||
|
||||
auto* param = Var(Source{Source::Location{12, 34}}, "a", ty.array<i32>(),
|
||||
ast::StorageClass::kNone);
|
||||
|
||||
Func("func", ast::VariableList{param}, ty.void_(),
|
||||
ast::StatementList{
|
||||
create<ast::ReturnStatement>(),
|
||||
},
|
||||
ast::DecorationList{});
|
||||
|
||||
Func("main", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{
|
||||
create<ast::ReturnStatement>(),
|
||||
},
|
||||
ast::DecorationList{
|
||||
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
|
||||
});
|
||||
|
||||
ValidatorImpl& v = Build();
|
||||
|
||||
EXPECT_FALSE(v.Validate());
|
||||
EXPECT_EQ(v.error(),
|
||||
"12:34 v-0015: runtime arrays may only appear as the last member "
|
||||
"of a struct");
|
||||
}
|
||||
} // namespace
|
||||
} // namespace tint
|
||||
|
|
|
@ -42,7 +42,7 @@ TEST_F(HlslGeneratorImplTest_EntryPoint,
|
|||
create<ast::LocationDecoration>(1),
|
||||
});
|
||||
|
||||
Func("vtx_main", ast::VariableList{}, ty.f32(),
|
||||
Func("vtx_main", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{
|
||||
create<ast::AssignmentStatement>(Expr("foo"), Expr("foo")),
|
||||
create<ast::AssignmentStatement>(Expr("bar"), Expr("bar")),
|
||||
|
@ -85,7 +85,7 @@ TEST_F(HlslGeneratorImplTest_EntryPoint,
|
|||
create<ast::LocationDecoration>(1),
|
||||
});
|
||||
|
||||
Func("vtx_main", ast::VariableList{}, ty.f32(),
|
||||
Func("vtx_main", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{
|
||||
create<ast::AssignmentStatement>(Expr("foo"), Expr("foo")),
|
||||
create<ast::AssignmentStatement>(Expr("bar"), Expr("bar")),
|
||||
|
@ -128,7 +128,7 @@ TEST_F(HlslGeneratorImplTest_EntryPoint,
|
|||
create<ast::LocationDecoration>(1),
|
||||
});
|
||||
|
||||
Func("main", ast::VariableList{}, ty.f32(),
|
||||
Func("main", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{
|
||||
create<ast::AssignmentStatement>(Expr("foo"), Expr("foo")),
|
||||
create<ast::AssignmentStatement>(Expr("bar"), Expr("bar")),
|
||||
|
@ -171,7 +171,7 @@ TEST_F(HlslGeneratorImplTest_EntryPoint,
|
|||
create<ast::LocationDecoration>(1),
|
||||
});
|
||||
|
||||
Func("main", ast::VariableList{}, ty.f32(),
|
||||
Func("main", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{
|
||||
create<ast::AssignmentStatement>(Expr("foo"), Expr("foo")),
|
||||
create<ast::AssignmentStatement>(Expr("bar"), Expr("bar")),
|
||||
|
@ -211,7 +211,7 @@ TEST_F(HlslGeneratorImplTest_EntryPoint,
|
|||
create<ast::LocationDecoration>(1),
|
||||
});
|
||||
|
||||
Func("main", ast::VariableList{}, ty.f32(),
|
||||
Func("main", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{
|
||||
create<ast::AssignmentStatement>(Expr("foo"), Expr("foo")),
|
||||
create<ast::AssignmentStatement>(Expr("bar"), Expr("bar")),
|
||||
|
@ -247,7 +247,7 @@ TEST_F(HlslGeneratorImplTest_EntryPoint,
|
|||
create<ast::LocationDecoration>(1),
|
||||
});
|
||||
|
||||
Func("main", ast::VariableList{}, ty.f32(),
|
||||
Func("main", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{
|
||||
create<ast::AssignmentStatement>(Expr("foo"), Expr("foo")),
|
||||
create<ast::AssignmentStatement>(Expr("bar"), Expr("bar")),
|
||||
|
|
|
@ -42,7 +42,7 @@ TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Vertex_Input) {
|
|||
create<ast::AssignmentStatement>(Expr("bar"), Expr("bar")),
|
||||
};
|
||||
|
||||
Func("vtx_main", ast::VariableList{}, ty.f32(), body,
|
||||
Func("vtx_main", ast::VariableList{}, ty.void_(), body,
|
||||
ast::DecorationList{
|
||||
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
|
||||
});
|
||||
|
@ -79,7 +79,7 @@ TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Vertex_Output) {
|
|||
create<ast::AssignmentStatement>(Expr("bar"), Expr("bar")),
|
||||
};
|
||||
|
||||
Func("vtx_main", ast::VariableList{}, ty.f32(), body,
|
||||
Func("vtx_main", ast::VariableList{}, ty.void_(), body,
|
||||
ast::DecorationList{
|
||||
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
|
||||
});
|
||||
|
@ -116,7 +116,7 @@ TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Fragment_Input) {
|
|||
create<ast::AssignmentStatement>(Expr("bar"), Expr("bar")),
|
||||
};
|
||||
|
||||
Func("main", ast::VariableList{}, ty.f32(), body,
|
||||
Func("main", ast::VariableList{}, ty.void_(), body,
|
||||
ast::DecorationList{
|
||||
create<ast::StageDecoration>(ast::PipelineStage::kFragment),
|
||||
});
|
||||
|
@ -153,7 +153,7 @@ TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Fragment_Output) {
|
|||
create<ast::AssignmentStatement>(Expr("bar"), Expr("bar")),
|
||||
};
|
||||
|
||||
Func("main", ast::VariableList{}, ty.f32(), body,
|
||||
Func("main", ast::VariableList{}, ty.void_(), body,
|
||||
ast::DecorationList{
|
||||
create<ast::StageDecoration>(ast::PipelineStage::kFragment),
|
||||
});
|
||||
|
@ -187,7 +187,7 @@ TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Compute_Input) {
|
|||
create<ast::AssignmentStatement>(Expr("bar"), Expr("bar")),
|
||||
};
|
||||
|
||||
Func("main", ast::VariableList{}, ty.f32(), body,
|
||||
Func("main", ast::VariableList{}, ty.void_(), body,
|
||||
ast::DecorationList{
|
||||
create<ast::StageDecoration>(ast::PipelineStage::kCompute),
|
||||
});
|
||||
|
@ -217,7 +217,7 @@ TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Compute_Output) {
|
|||
create<ast::AssignmentStatement>(Expr("bar"), Expr("bar")),
|
||||
};
|
||||
|
||||
Func("main", ast::VariableList{}, ty.f32(), body,
|
||||
Func("main", ast::VariableList{}, ty.void_(), body,
|
||||
ast::DecorationList{
|
||||
create<ast::StageDecoration>(ast::PipelineStage::kCompute),
|
||||
});
|
||||
|
|
|
@ -382,8 +382,8 @@ TEST_F(IntrinsicBuilderTest, Call_TextureSampleCompare_Twice) {
|
|||
auto* expr2 = Call("textureSampleCompare", "texture", "sampler",
|
||||
vec2<f32>(1.0f, 2.0f), 2.0f);
|
||||
|
||||
WrapInFunction(expr1);
|
||||
WrapInFunction(expr2);
|
||||
Func("f1", {}, ty.void_(), {create<ast::CallStatement>(expr1)}, {});
|
||||
Func("f2", {}, ty.void_(), {create<ast::CallStatement>(expr2)}, {});
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
||||
|
|
|
@ -79,8 +79,8 @@ TEST_F(BuilderTest, Switch_WithCase) {
|
|||
|
||||
WrapInFunction(expr);
|
||||
|
||||
auto* func =
|
||||
Func("a_func", {}, ty.i32(), ast::StatementList{}, ast::DecorationList{});
|
||||
auto* func = Func("a_func", {}, ty.void_(), ast::StatementList{},
|
||||
ast::DecorationList{});
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
||||
|
@ -92,29 +92,30 @@ TEST_F(BuilderTest, Switch_WithCase) {
|
|||
|
||||
EXPECT_EQ(DumpBuilder(b), R"(OpName %1 "v"
|
||||
OpName %5 "a"
|
||||
OpName %7 "a_func"
|
||||
OpName %8 "a_func"
|
||||
%3 = OpTypeInt 32 1
|
||||
%2 = OpTypePointer Private %3
|
||||
%4 = OpConstantNull %3
|
||||
%1 = OpVariable %2 Private %4
|
||||
%5 = OpVariable %2 Private %4
|
||||
%6 = OpTypeFunction %3
|
||||
%14 = OpConstant %3 1
|
||||
%15 = OpConstant %3 2
|
||||
%7 = OpFunction %3 None %6
|
||||
%8 = OpLabel
|
||||
%10 = OpLoad %3 %5
|
||||
OpSelectionMerge %9 None
|
||||
OpSwitch %10 %11 1 %12 2 %13
|
||||
%12 = OpLabel
|
||||
OpStore %1 %14
|
||||
OpBranch %9
|
||||
%7 = OpTypeVoid
|
||||
%6 = OpTypeFunction %7
|
||||
%15 = OpConstant %3 1
|
||||
%16 = OpConstant %3 2
|
||||
%8 = OpFunction %7 None %6
|
||||
%9 = OpLabel
|
||||
%11 = OpLoad %3 %5
|
||||
OpSelectionMerge %10 None
|
||||
OpSwitch %11 %12 1 %13 2 %14
|
||||
%13 = OpLabel
|
||||
OpStore %1 %15
|
||||
OpBranch %9
|
||||
%11 = OpLabel
|
||||
OpBranch %9
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%14 = OpLabel
|
||||
OpStore %1 %16
|
||||
OpBranch %10
|
||||
%12 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)");
|
||||
|
@ -151,8 +152,8 @@ TEST_F(BuilderTest, Switch_WithCase_Unsigned) {
|
|||
|
||||
WrapInFunction(expr);
|
||||
|
||||
auto* func =
|
||||
Func("a_func", {}, ty.i32(), ast::StatementList{}, ast::DecorationList{});
|
||||
auto* func = Func("a_func", {}, ty.void_(), ast::StatementList{},
|
||||
ast::DecorationList{});
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
||||
|
@ -164,7 +165,7 @@ TEST_F(BuilderTest, Switch_WithCase_Unsigned) {
|
|||
|
||||
EXPECT_EQ(DumpBuilder(b), R"(OpName %1 "v"
|
||||
OpName %5 "a"
|
||||
OpName %10 "a_func"
|
||||
OpName %11 "a_func"
|
||||
%3 = OpTypeInt 32 1
|
||||
%2 = OpTypePointer Private %3
|
||||
%4 = OpConstantNull %3
|
||||
|
@ -173,23 +174,24 @@ OpName %10 "a_func"
|
|||
%6 = OpTypePointer Private %7
|
||||
%8 = OpConstantNull %7
|
||||
%5 = OpVariable %6 Private %8
|
||||
%9 = OpTypeFunction %3
|
||||
%17 = OpConstant %3 1
|
||||
%18 = OpConstant %3 2
|
||||
%10 = OpFunction %3 None %9
|
||||
%11 = OpLabel
|
||||
%13 = OpLoad %7 %5
|
||||
OpSelectionMerge %12 None
|
||||
OpSwitch %13 %14 1 %15 2 %16
|
||||
%15 = OpLabel
|
||||
OpStore %1 %17
|
||||
OpBranch %12
|
||||
%10 = OpTypeVoid
|
||||
%9 = OpTypeFunction %10
|
||||
%18 = OpConstant %3 1
|
||||
%19 = OpConstant %3 2
|
||||
%11 = OpFunction %10 None %9
|
||||
%12 = OpLabel
|
||||
%14 = OpLoad %7 %5
|
||||
OpSelectionMerge %13 None
|
||||
OpSwitch %14 %15 1 %16 2 %17
|
||||
%16 = OpLabel
|
||||
OpStore %1 %18
|
||||
OpBranch %12
|
||||
%14 = OpLabel
|
||||
OpBranch %12
|
||||
%12 = OpLabel
|
||||
OpBranch %13
|
||||
%17 = OpLabel
|
||||
OpStore %1 %19
|
||||
OpBranch %13
|
||||
%15 = OpLabel
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)");
|
||||
|
@ -215,8 +217,8 @@ TEST_F(BuilderTest, Switch_WithDefault) {
|
|||
|
||||
WrapInFunction(expr);
|
||||
|
||||
auto* func =
|
||||
Func("a_func", {}, ty.i32(), ast::StatementList{}, ast::DecorationList{});
|
||||
auto* func = Func("a_func", {}, ty.void_(), ast::StatementList{},
|
||||
ast::DecorationList{});
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
||||
|
@ -228,23 +230,24 @@ TEST_F(BuilderTest, Switch_WithDefault) {
|
|||
|
||||
EXPECT_EQ(DumpBuilder(b), R"(OpName %1 "v"
|
||||
OpName %5 "a"
|
||||
OpName %7 "a_func"
|
||||
OpName %8 "a_func"
|
||||
%3 = OpTypeInt 32 1
|
||||
%2 = OpTypePointer Private %3
|
||||
%4 = OpConstantNull %3
|
||||
%1 = OpVariable %2 Private %4
|
||||
%5 = OpVariable %2 Private %4
|
||||
%6 = OpTypeFunction %3
|
||||
%12 = OpConstant %3 1
|
||||
%7 = OpFunction %3 None %6
|
||||
%8 = OpLabel
|
||||
%10 = OpLoad %3 %5
|
||||
OpSelectionMerge %9 None
|
||||
OpSwitch %10 %11
|
||||
%11 = OpLabel
|
||||
OpStore %1 %12
|
||||
OpBranch %9
|
||||
%7 = OpTypeVoid
|
||||
%6 = OpTypeFunction %7
|
||||
%13 = OpConstant %3 1
|
||||
%8 = OpFunction %7 None %6
|
||||
%9 = OpLabel
|
||||
%11 = OpLoad %3 %5
|
||||
OpSelectionMerge %10 None
|
||||
OpSwitch %11 %12
|
||||
%12 = OpLabel
|
||||
OpStore %1 %13
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)");
|
||||
|
@ -289,8 +292,8 @@ TEST_F(BuilderTest, Switch_WithCaseAndDefault) {
|
|||
|
||||
WrapInFunction(expr);
|
||||
|
||||
auto* func =
|
||||
Func("a_func", {}, ty.i32(), ast::StatementList{}, ast::DecorationList{});
|
||||
auto* func = Func("a_func", {}, ty.void_(), ast::StatementList{},
|
||||
ast::DecorationList{});
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
||||
|
@ -302,31 +305,32 @@ TEST_F(BuilderTest, Switch_WithCaseAndDefault) {
|
|||
|
||||
EXPECT_EQ(DumpBuilder(b), R"(OpName %1 "v"
|
||||
OpName %5 "a"
|
||||
OpName %7 "a_func"
|
||||
OpName %8 "a_func"
|
||||
%3 = OpTypeInt 32 1
|
||||
%2 = OpTypePointer Private %3
|
||||
%4 = OpConstantNull %3
|
||||
%1 = OpVariable %2 Private %4
|
||||
%5 = OpVariable %2 Private %4
|
||||
%6 = OpTypeFunction %3
|
||||
%14 = OpConstant %3 1
|
||||
%15 = OpConstant %3 2
|
||||
%16 = OpConstant %3 3
|
||||
%7 = OpFunction %3 None %6
|
||||
%8 = OpLabel
|
||||
%10 = OpLoad %3 %5
|
||||
OpSelectionMerge %9 None
|
||||
OpSwitch %10 %11 1 %12 2 %13 3 %13
|
||||
%12 = OpLabel
|
||||
OpStore %1 %14
|
||||
OpBranch %9
|
||||
%7 = OpTypeVoid
|
||||
%6 = OpTypeFunction %7
|
||||
%15 = OpConstant %3 1
|
||||
%16 = OpConstant %3 2
|
||||
%17 = OpConstant %3 3
|
||||
%8 = OpFunction %7 None %6
|
||||
%9 = OpLabel
|
||||
%11 = OpLoad %3 %5
|
||||
OpSelectionMerge %10 None
|
||||
OpSwitch %11 %12 1 %13 2 %14 3 %14
|
||||
%13 = OpLabel
|
||||
OpStore %1 %15
|
||||
OpBranch %9
|
||||
%11 = OpLabel
|
||||
OpBranch %10
|
||||
%14 = OpLabel
|
||||
OpStore %1 %16
|
||||
OpBranch %9
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%12 = OpLabel
|
||||
OpStore %1 %17
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)");
|
||||
|
@ -372,8 +376,8 @@ TEST_F(BuilderTest, Switch_CaseWithFallthrough) {
|
|||
|
||||
WrapInFunction(expr);
|
||||
|
||||
auto* func =
|
||||
Func("a_func", {}, ty.i32(), ast::StatementList{}, ast::DecorationList{});
|
||||
auto* func = Func("a_func", {}, ty.void_(), ast::StatementList{},
|
||||
ast::DecorationList{});
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
||||
|
@ -385,31 +389,32 @@ TEST_F(BuilderTest, Switch_CaseWithFallthrough) {
|
|||
|
||||
EXPECT_EQ(DumpBuilder(b), R"(OpName %1 "v"
|
||||
OpName %5 "a"
|
||||
OpName %7 "a_func"
|
||||
OpName %8 "a_func"
|
||||
%3 = OpTypeInt 32 1
|
||||
%2 = OpTypePointer Private %3
|
||||
%4 = OpConstantNull %3
|
||||
%1 = OpVariable %2 Private %4
|
||||
%5 = OpVariable %2 Private %4
|
||||
%6 = OpTypeFunction %3
|
||||
%14 = OpConstant %3 1
|
||||
%15 = OpConstant %3 2
|
||||
%16 = OpConstant %3 3
|
||||
%7 = OpFunction %3 None %6
|
||||
%8 = OpLabel
|
||||
%10 = OpLoad %3 %5
|
||||
OpSelectionMerge %9 None
|
||||
OpSwitch %10 %11 1 %12 2 %13
|
||||
%12 = OpLabel
|
||||
OpStore %1 %14
|
||||
OpBranch %13
|
||||
%7 = OpTypeVoid
|
||||
%6 = OpTypeFunction %7
|
||||
%15 = OpConstant %3 1
|
||||
%16 = OpConstant %3 2
|
||||
%17 = OpConstant %3 3
|
||||
%8 = OpFunction %7 None %6
|
||||
%9 = OpLabel
|
||||
%11 = OpLoad %3 %5
|
||||
OpSelectionMerge %10 None
|
||||
OpSwitch %11 %12 1 %13 2 %14
|
||||
%13 = OpLabel
|
||||
OpStore %1 %15
|
||||
OpBranch %9
|
||||
%11 = OpLabel
|
||||
OpBranch %14
|
||||
%14 = OpLabel
|
||||
OpStore %1 %16
|
||||
OpBranch %9
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%12 = OpLabel
|
||||
OpStore %1 %17
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)");
|
||||
|
@ -439,8 +444,8 @@ TEST_F(BuilderTest, Switch_CaseFallthroughLastStatement) {
|
|||
|
||||
WrapInFunction(expr);
|
||||
|
||||
auto* func =
|
||||
Func("a_func", {}, ty.i32(), ast::StatementList{}, ast::DecorationList{});
|
||||
auto* func = Func("a_func", {}, ty.void_(), ast::StatementList{},
|
||||
ast::DecorationList{});
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
||||
|
@ -482,8 +487,8 @@ TEST_F(BuilderTest, Switch_WithNestedBreak) {
|
|||
|
||||
WrapInFunction(expr);
|
||||
|
||||
auto* func =
|
||||
Func("a_func", {}, ty.i32(), ast::StatementList{}, ast::DecorationList{});
|
||||
auto* func = Func("a_func", {}, ty.void_(), ast::StatementList{},
|
||||
ast::DecorationList{});
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
||||
|
@ -495,32 +500,33 @@ TEST_F(BuilderTest, Switch_WithNestedBreak) {
|
|||
|
||||
EXPECT_EQ(DumpBuilder(b), R"(OpName %1 "v"
|
||||
OpName %5 "a"
|
||||
OpName %7 "a_func"
|
||||
OpName %8 "a_func"
|
||||
%3 = OpTypeInt 32 1
|
||||
%2 = OpTypePointer Private %3
|
||||
%4 = OpConstantNull %3
|
||||
%1 = OpVariable %2 Private %4
|
||||
%5 = OpVariable %2 Private %4
|
||||
%6 = OpTypeFunction %3
|
||||
%13 = OpTypeBool
|
||||
%14 = OpConstantTrue %13
|
||||
%17 = OpConstant %3 1
|
||||
%7 = OpFunction %3 None %6
|
||||
%8 = OpLabel
|
||||
%10 = OpLoad %3 %5
|
||||
OpSelectionMerge %9 None
|
||||
OpSwitch %10 %11 1 %12
|
||||
%12 = OpLabel
|
||||
OpSelectionMerge %15 None
|
||||
OpBranchConditional %14 %16 %15
|
||||
%16 = OpLabel
|
||||
OpBranch %9
|
||||
%15 = OpLabel
|
||||
OpStore %1 %17
|
||||
OpBranch %9
|
||||
%11 = OpLabel
|
||||
OpBranch %9
|
||||
%7 = OpTypeVoid
|
||||
%6 = OpTypeFunction %7
|
||||
%14 = OpTypeBool
|
||||
%15 = OpConstantTrue %14
|
||||
%18 = OpConstant %3 1
|
||||
%8 = OpFunction %7 None %6
|
||||
%9 = OpLabel
|
||||
%11 = OpLoad %3 %5
|
||||
OpSelectionMerge %10 None
|
||||
OpSwitch %11 %12 1 %13
|
||||
%13 = OpLabel
|
||||
OpSelectionMerge %16 None
|
||||
OpBranchConditional %15 %17 %16
|
||||
%17 = OpLabel
|
||||
OpBranch %10
|
||||
%16 = OpLabel
|
||||
OpStore %1 %18
|
||||
OpBranch %10
|
||||
%12 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)");
|
||||
|
|
|
@ -171,6 +171,7 @@ source_set("tint_unittests_core_src") {
|
|||
"../src/program_test.cc",
|
||||
"../src/resolver/assignment_validation_test.cc",
|
||||
"../src/resolver/decoration_validation_test.cc",
|
||||
"../src/resolver/function_validation_test.cc",
|
||||
"../src/resolver/host_shareable_validation_test.cc",
|
||||
"../src/resolver/intrinsic_test.cc",
|
||||
"../src/resolver/is_host_shareable_test.cc",
|
||||
|
|
Loading…
Reference in New Issue