diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 324c2243c7..9614ed4ab8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/program_builder.cc b/src/program_builder.cc index c65a04470e..ac5d0d1af4 100644 --- a/src/program_builder.cc +++ b/src/program_builder.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 diff --git a/src/program_builder.h b/src/program_builder.h index a8779a8c26..ffe747623a 100644 --- a/src/program_builder.h +++ b/src/program_builder.h @@ -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 + ast::ReturnStatement* Return(Args&&... args) { + return create(std::forward(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 - void WrapInFunction(ARGS&&... args) { + ast::Function* WrapInFunction(ARGS&&... args) { ast::StatementList stmts{WrapInStatement(std::forward(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}; diff --git a/src/resolver/decoration_validation_test.cc b/src/resolver/decoration_validation_test.cc index b1a45c18e8..39f8f8d891 100644 --- a/src/resolver/decoration_validation_test.cc +++ b/src/resolver/decoration_validation_test.cc @@ -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(Expr(1.f))}, + ast::DecorationList{ + create(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) { diff --git a/src/resolver/function_validation_test.cc b/src/resolver/function_validation_test.cc new file mode 100644 index 0000000000..0da990a4b8 --- /dev/null +++ b/src/resolver/function_validation_test.cc @@ -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(Expr(2)), + }, + ast::DecorationList{}); + + Func(Source{Source::Location{12, 34}}, "func", ast::VariableList{}, ty.i32(), + ast::StatementList{ + create(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(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 diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc index d4eb5b1ce7..b02f6af111 100644 --- a/src/resolver/resolver.cc +++ b/src/resolver/resolver.cc @@ -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()) { + 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()) { + if (!func->get_last_statement() || + !func->get_last_statement()->Is()) { + 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() || + deco->Is())) { + 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(func); + if (!ValidateFunction(func)) { + return false; + } + ScopedAssignment sa(current_function_, func_info); variable_stack_.push_scope(); diff --git a/src/resolver/resolver.h b/src/resolver/resolver.h index 00102eee03..d6db052baf 100644 --- a/src/resolver/resolver.h +++ b/src/resolver/resolver.h @@ -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 variable_stack_; std::unordered_map symbol_to_function_; - std::unordered_map function_to_info_; + std::unordered_map function_to_info_; std::unordered_map variable_to_info_; std::unordered_map function_calls_; std::unordered_map expr_info_; diff --git a/src/resolver/resolver_test.cc b/src/resolver/resolver_test.cc index 664a465c84..171ef6d9f1 100644 --- a/src/resolver/resolver_test.cc +++ b/src/resolver/resolver_test.cc @@ -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(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()); 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(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(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()); 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(my_var_a, my_var_b); - Func("my_func", ast::VariableList{}, ty.f32(), + Func("my_func", ast::VariableList{}, ty.void_(), ast::StatementList{ create(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(var), assign, @@ -721,7 +722,7 @@ TEST_F(ResolverTest, Expr_Identifier_Function_Ptr) { auto* my_var_b = Expr("my_var"); auto* assign = create(my_var_a, my_var_b); - Func("my_func", ast::VariableList{}, ty.f32(), + Func("my_func", ast::VariableList{}, ty.void_(), ast::StatementList{ create( Var("my_var", ty.pointer(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(Expr("out_var"), Expr("in_var")), create(Expr("wg_var"), Expr("wg_var")), @@ -806,11 +807,11 @@ TEST_F(ResolverTest, Function_RegisterInputOutputVariables_SubFunction) { create(Expr("wg_var"), Expr("wg_var")), create(Expr("sb_var"), Expr("sb_var")), create(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(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(var), create(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(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(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(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( + Expr("second"), Call("b")), + Return(Expr(0.0f))}, + ast::DecorationList{}); - auto* func_a = - Func("a", params, ty.f32(), - ast::StatementList{ - create(Expr("first"), Call("c")), - }, - ast::DecorationList{}); + auto* func_a = Func("a", params, ty.f32(), + ast::StatementList{create( + 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(Expr("call_a"), Call("a")), create(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(Expr("call_c"), Call("c")), }, diff --git a/src/resolver/type_validation_test.cc b/src/resolver/type_validation_test.cc index 23caf52496..dc43bcf1d6 100644 --- a/src/resolver/type_validation_test.cc +++ b/src/resolver/type_validation_test.cc @@ -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) {} + // [[stage(vertex)]] fn main() {} + + auto* param = Var(Source{Source::Location{12, 34}}, "a", ty.array(), + ast::StorageClass::kNone); + + Func("func", ast::VariableList{param}, ty.void_(), + ast::StatementList{ + create(), + }, + ast::DecorationList{}); + + Func("main", ast::VariableList{}, ty.void_(), + ast::StatementList{ + create(), + }, + ast::DecorationList{ + create(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; diff --git a/src/resolver/validation_test.cc b/src/resolver/validation_test.cc index 659eb0f448..f9366e3dbc 100644 --- a/src/resolver/validation_test.cc +++ b/src/resolver/validation_test.cc @@ -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(call_expr), }, @@ -245,7 +245,7 @@ TEST_F(ResolverValidationTest, StorageClass_NonFunctionClassError) { auto* var = Var("var", ty.i32(), ast::StorageClass::kWorkgroup); auto* stmt = create(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()); diff --git a/src/validator/validator_decoration_test.cc b/src/validator/validator_decoration_test.cc index 52343f83f2..a48ff9f107 100644 --- a/src/validator/validator_decoration_test.cc +++ b/src/validator/validator_decoration_test.cc @@ -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(Expr(1.f))}, - ast::DecorationList{ - create(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(); diff --git a/src/validator/validator_function_test.cc b/src/validator/validator_function_test.cc index aa4706036e..5c217eb640 100644 --- a/src/validator/validator_function_test.cc +++ b/src/validator/validator_function_test.cc @@ -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(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(Expr(2)), - }, - ast::DecorationList{}); - - Func(Source{Source::Location{12, 34}}, "func", ast::VariableList{}, ty.i32(), - ast::StatementList{ - create(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)]] diff --git a/src/validator/validator_impl.cc b/src/validator/validator_impl.cc index 25d4da0f57..7f46edb1ab 100644 --- a/src/validator/validator_impl.cc +++ b/src/validator/validator_impl.cc @@ -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()) { - if (!func->get_last_statement() || - !func->get_last_statement()->Is()) { - 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() || - deco->Is())) { - 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()) { - 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; } diff --git a/src/validator/validator_impl.h b/src/validator/validator_impl.h index 1d2338ed88..93c1d8db20 100644 --- a/src/validator/validator_impl.h +++ b/src/validator/validator_impl.h @@ -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 variable_stack_; - ScopeStack function_stack_; ast::Function* current_function_ = nullptr; }; diff --git a/src/validator/validator_type_test.cc b/src/validator/validator_type_test.cc index dd991ae924..de8b6f570d 100644 --- a/src/validator/validator_type_test.cc +++ b/src/validator/validator_type_test.cc @@ -44,33 +44,5 @@ TEST_F(ValidatorTypeTest, RuntimeArrayInFunction_Fail) { "of a struct"); } -TEST_F(ValidatorTypeTest, RuntimeArrayAsParameter_Fail) { - // fn func(a : array) {} - // [[stage(vertex)]] fn main() {} - - auto* param = Var(Source{Source::Location{12, 34}}, "a", ty.array(), - ast::StorageClass::kNone); - - Func("func", ast::VariableList{param}, ty.void_(), - ast::StatementList{ - create(), - }, - ast::DecorationList{}); - - Func("main", ast::VariableList{}, ty.void_(), - ast::StatementList{ - create(), - }, - ast::DecorationList{ - create(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 diff --git a/src/writer/hlsl/generator_impl_function_entry_point_data_test.cc b/src/writer/hlsl/generator_impl_function_entry_point_data_test.cc index f1c3bb3c1e..2c65dc4ec1 100644 --- a/src/writer/hlsl/generator_impl_function_entry_point_data_test.cc +++ b/src/writer/hlsl/generator_impl_function_entry_point_data_test.cc @@ -42,7 +42,7 @@ TEST_F(HlslGeneratorImplTest_EntryPoint, create(1), }); - Func("vtx_main", ast::VariableList{}, ty.f32(), + Func("vtx_main", ast::VariableList{}, ty.void_(), ast::StatementList{ create(Expr("foo"), Expr("foo")), create(Expr("bar"), Expr("bar")), @@ -85,7 +85,7 @@ TEST_F(HlslGeneratorImplTest_EntryPoint, create(1), }); - Func("vtx_main", ast::VariableList{}, ty.f32(), + Func("vtx_main", ast::VariableList{}, ty.void_(), ast::StatementList{ create(Expr("foo"), Expr("foo")), create(Expr("bar"), Expr("bar")), @@ -128,7 +128,7 @@ TEST_F(HlslGeneratorImplTest_EntryPoint, create(1), }); - Func("main", ast::VariableList{}, ty.f32(), + Func("main", ast::VariableList{}, ty.void_(), ast::StatementList{ create(Expr("foo"), Expr("foo")), create(Expr("bar"), Expr("bar")), @@ -171,7 +171,7 @@ TEST_F(HlslGeneratorImplTest_EntryPoint, create(1), }); - Func("main", ast::VariableList{}, ty.f32(), + Func("main", ast::VariableList{}, ty.void_(), ast::StatementList{ create(Expr("foo"), Expr("foo")), create(Expr("bar"), Expr("bar")), @@ -211,7 +211,7 @@ TEST_F(HlslGeneratorImplTest_EntryPoint, create(1), }); - Func("main", ast::VariableList{}, ty.f32(), + Func("main", ast::VariableList{}, ty.void_(), ast::StatementList{ create(Expr("foo"), Expr("foo")), create(Expr("bar"), Expr("bar")), @@ -247,7 +247,7 @@ TEST_F(HlslGeneratorImplTest_EntryPoint, create(1), }); - Func("main", ast::VariableList{}, ty.f32(), + Func("main", ast::VariableList{}, ty.void_(), ast::StatementList{ create(Expr("foo"), Expr("foo")), create(Expr("bar"), Expr("bar")), diff --git a/src/writer/msl/generator_impl_function_entry_point_data_test.cc b/src/writer/msl/generator_impl_function_entry_point_data_test.cc index c31b79b782..fa22462190 100644 --- a/src/writer/msl/generator_impl_function_entry_point_data_test.cc +++ b/src/writer/msl/generator_impl_function_entry_point_data_test.cc @@ -42,7 +42,7 @@ TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Vertex_Input) { create(Expr("bar"), Expr("bar")), }; - Func("vtx_main", ast::VariableList{}, ty.f32(), body, + Func("vtx_main", ast::VariableList{}, ty.void_(), body, ast::DecorationList{ create(ast::PipelineStage::kVertex), }); @@ -79,7 +79,7 @@ TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Vertex_Output) { create(Expr("bar"), Expr("bar")), }; - Func("vtx_main", ast::VariableList{}, ty.f32(), body, + Func("vtx_main", ast::VariableList{}, ty.void_(), body, ast::DecorationList{ create(ast::PipelineStage::kVertex), }); @@ -116,7 +116,7 @@ TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Fragment_Input) { create(Expr("bar"), Expr("bar")), }; - Func("main", ast::VariableList{}, ty.f32(), body, + Func("main", ast::VariableList{}, ty.void_(), body, ast::DecorationList{ create(ast::PipelineStage::kFragment), }); @@ -153,7 +153,7 @@ TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Fragment_Output) { create(Expr("bar"), Expr("bar")), }; - Func("main", ast::VariableList{}, ty.f32(), body, + Func("main", ast::VariableList{}, ty.void_(), body, ast::DecorationList{ create(ast::PipelineStage::kFragment), }); @@ -187,7 +187,7 @@ TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Compute_Input) { create(Expr("bar"), Expr("bar")), }; - Func("main", ast::VariableList{}, ty.f32(), body, + Func("main", ast::VariableList{}, ty.void_(), body, ast::DecorationList{ create(ast::PipelineStage::kCompute), }); @@ -217,7 +217,7 @@ TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Compute_Output) { create(Expr("bar"), Expr("bar")), }; - Func("main", ast::VariableList{}, ty.f32(), body, + Func("main", ast::VariableList{}, ty.void_(), body, ast::DecorationList{ create(ast::PipelineStage::kCompute), }); diff --git a/src/writer/spirv/builder_intrinsic_test.cc b/src/writer/spirv/builder_intrinsic_test.cc index 4fe131e438..4e2bb84f7c 100644 --- a/src/writer/spirv/builder_intrinsic_test.cc +++ b/src/writer/spirv/builder_intrinsic_test.cc @@ -382,8 +382,8 @@ TEST_F(IntrinsicBuilderTest, Call_TextureSampleCompare_Twice) { auto* expr2 = Call("textureSampleCompare", "texture", "sampler", vec2(1.0f, 2.0f), 2.0f); - WrapInFunction(expr1); - WrapInFunction(expr2); + Func("f1", {}, ty.void_(), {create(expr1)}, {}); + Func("f2", {}, ty.void_(), {create(expr2)}, {}); spirv::Builder& b = Build(); diff --git a/src/writer/spirv/builder_switch_test.cc b/src/writer/spirv/builder_switch_test.cc index cff64ccf89..f9d2d977e5 100644 --- a/src/writer/spirv/builder_switch_test.cc +++ b/src/writer/spirv/builder_switch_test.cc @@ -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 )"); diff --git a/test/BUILD.gn b/test/BUILD.gn index 15508c4478..b49cf592e3 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -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",