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>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 324c224..9614ed4 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -469,6 +469,7 @@
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 c65a044..ac5d0d1 100644
--- a/src/program_builder.cc
+++ b/src/program_builder.cc
@@ -97,8 +97,8 @@
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 a8779a8..ffe7476 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 @@
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 @@
/// 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};
diff --git a/src/resolver/decoration_validation_test.cc b/src/resolver/decoration_validation_test.cc
index b1a45c1..39f8f8d 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 @@
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) {
diff --git a/src/resolver/function_validation_test.cc b/src/resolver/function_validation_test.cc
new file mode 100644
index 0000000..0da990a
--- /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<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
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc
index d4eb5b1..b02f6af 100644
--- a/src/resolver/resolver.cc
+++ b/src/resolver/resolver.cc
@@ -222,9 +222,65 @@
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();
diff --git a/src/resolver/resolver.h b/src/resolver/resolver.h
index 00102ee..d6db052 100644
--- a/src/resolver/resolver.h
+++ b/src/resolver/resolver.h
@@ -224,16 +224,15 @@
// 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 @@
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_;
diff --git a/src/resolver/resolver_test.cc b/src/resolver/resolver_test.cc
index 664a465..171ef6d 100644
--- a/src/resolver/resolver_test.cc
+++ b/src/resolver/resolver_test.cc
@@ -271,7 +271,7 @@
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_OuterScopeAfterInnerScope) {
- // fn func_i32() -> i32 {
+ // fn func_i32() -> void {
// {
// var foo : i32 = 2;
// var bar : i32 = foo;
@@ -359,11 +359,11 @@
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_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 @@
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 @@
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_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_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_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 @@
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 @@
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 @@
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_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 @@
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 @@
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 @@
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 @@
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_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 @@
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 @@
});
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")),
},
diff --git a/src/resolver/type_validation_test.cc b/src/resolver/type_validation_test.cc
index 23caf52..dc43bcf 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 @@
"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>;
diff --git a/src/resolver/validation_test.cc b/src/resolver/validation_test.cc
index 659eb0f..f9366e3 100644
--- a/src/resolver/validation_test.cc
+++ b/src/resolver/validation_test.cc
@@ -116,7 +116,7 @@
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 @@
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());
diff --git a/src/validator/validator_decoration_test.cc b/src/validator/validator_decoration_test.cc
index 52343f8..a48ff9f 100644
--- a/src/validator/validator_decoration_test.cc
+++ b/src/validator/validator_decoration_test.cc
@@ -121,42 +121,6 @@
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();
diff --git a/src/validator/validator_function_test.cc b/src/validator/validator_function_test.cc
index aa47060..5c217eb 100644
--- a/src/validator/validator_function_test.cc
+++ b/src/validator/validator_function_test.cc
@@ -56,37 +56,6 @@
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 @@
"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)]]
diff --git a/src/validator/validator_impl.cc b/src/validator/validator_impl.cc
index 25d4da0..7f46edb 100644
--- a/src/validator/validator_impl.cc
+++ b/src/validator/validator_impl.cc
@@ -168,57 +168,11 @@
}
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;
}
diff --git a/src/validator/validator_impl.h b/src/validator/validator_impl.h
index 1d2338e..93c1d8d 100644
--- a/src/validator/validator_impl.h
+++ b/src/validator/validator_impl.h
@@ -76,10 +76,6 @@
/// @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 @@
const Program* program_;
diag::List diags_;
ScopeStack<const ast::Variable*> variable_stack_;
- ScopeStack<const ast::Function*> function_stack_;
ast::Function* current_function_ = nullptr;
};
diff --git a/src/validator/validator_type_test.cc b/src/validator/validator_type_test.cc
index dd991ae..de8b6f5 100644
--- a/src/validator/validator_type_test.cc
+++ b/src/validator/validator_type_test.cc
@@ -44,33 +44,5 @@
"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
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 f1c3bb3..2c65dc4 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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")),
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 c31b79b..fa22462 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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),
});
diff --git a/src/writer/spirv/builder_intrinsic_test.cc b/src/writer/spirv/builder_intrinsic_test.cc
index 4fe131e..4e2bb84 100644
--- a/src/writer/spirv/builder_intrinsic_test.cc
+++ b/src/writer/spirv/builder_intrinsic_test.cc
@@ -382,8 +382,8 @@
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();
diff --git a/src/writer/spirv/builder_switch_test.cc b/src/writer/spirv/builder_switch_test.cc
index cff64cc..f9d2d97 100644
--- a/src/writer/spirv/builder_switch_test.cc
+++ b/src/writer/spirv/builder_switch_test.cc
@@ -79,8 +79,8 @@
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 @@
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 @@
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 @@
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 @@
%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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 15508c4..b49cf59 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -171,6 +171,7 @@
"../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",