[validation] relax rule v-0002: return is mandatory for non-void functions

Bug: tint:302
Change-Id: Ia8a5cf1d36d5da6e2defdfff6c87f0d8a1a39b4a
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/32040
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
diff --git a/src/validator/validator_function_test.cc b/src/validator/validator_function_test.cc
index 8614744..15ec76a 100644
--- a/src/validator/validator_function_test.cc
+++ b/src/validator/validator_function_test.cc
@@ -35,9 +35,9 @@
 class ValidateFunctionTest : public ValidatorTestHelper,
                              public testing::Test {};
 
-TEST_F(ValidateFunctionTest, FunctionEndWithoutReturnStatement_Fail) {
+TEST_F(ValidateFunctionTest, VoidFunctionEndWithoutReturnStatement_Pass) {
+  // [[stage(vertex)]]
   // fn func -> void { var a:i32 = 2; }
-
   ast::type::I32Type i32;
   auto var =
       std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &i32);
@@ -51,26 +51,69 @@
   auto body = std::make_unique<ast::BlockStatement>();
   body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
   func->set_body(std::move(body));
+  func->add_decoration(std::make_unique<ast::StageDecoration>(
+      ast::PipelineStage::kVertex, Source{}));
   mod()->AddFunction(std::move(func));
 
   EXPECT_TRUE(td()->Determine()) << td()->error();
-  EXPECT_FALSE(v()->Validate(mod()));
-  EXPECT_EQ(v()->error(),
-            "12:34: v-0002: function must end with a return statement");
+  EXPECT_TRUE(v()->Validate(mod()));
 }
 
-TEST_F(ValidateFunctionTest, FunctionEndWithoutReturnStatementEmptyBody_Fail) {
+TEST_F(ValidateFunctionTest,
+       VoidFunctionEndWithoutReturnStatementEmptyBody_Pass) {
+  // [[stage(vertex)]]
   // fn func -> void {}
   ast::type::VoidType void_type;
   ast::VariableList params;
   auto func = std::make_unique<ast::Function>(
       Source{Source::Location{12, 34}}, "func", std::move(params), &void_type);
+  func->add_decoration(std::make_unique<ast::StageDecoration>(
+      ast::PipelineStage::kVertex, Source{}));
+  mod()->AddFunction(std::move(func));
+
+  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(v()->Validate(mod()));
+}
+
+TEST_F(ValidateFunctionTest, FunctionEndWithoutReturnStatement_Fail) {
+  // fn func -> int { var a:i32 = 2; }
+
+  ast::type::I32Type i32;
+  auto var =
+      std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &i32);
+  var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 2)));
+
+  ast::VariableList params;
+  ast::type::VoidType void_type;
+  auto func = std::make_unique<ast::Function>(Source{Source::Location{12, 34}},
+                                              "func", std::move(params), &i32);
+  auto body = std::make_unique<ast::BlockStatement>();
+  body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
+  func->set_body(std::move(body));
   mod()->AddFunction(std::move(func));
 
   EXPECT_TRUE(td()->Determine()) << td()->error();
   EXPECT_FALSE(v()->Validate(mod()));
-  EXPECT_EQ(v()->error(),
-            "12:34: v-0002: function must end with a return statement");
+  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 {}
+  ast::type::VoidType void_type;
+  ast::type::I32Type i32;
+  ast::VariableList params;
+  auto func = std::make_unique<ast::Function>(Source{Source::Location{12, 34}},
+                                              "func", std::move(params), &i32);
+  mod()->AddFunction(std::move(func));
+
+  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_FALSE(v()->Validate(mod()));
+  EXPECT_EQ(
+      v()->error(),
+      "12:34: v-0002: non-void function must end with a return statement");
 }
 
 TEST_F(ValidateFunctionTest, FunctionTypeMustMatchReturnStatementType_Pass) {
diff --git a/src/validator/validator_impl.cc b/src/validator/validator_impl.cc
index 37ef83c..1f6c01e 100644
--- a/src/validator/validator_impl.cc
+++ b/src/validator/validator_impl.cc
@@ -154,10 +154,13 @@
   }
   variable_stack_.pop_scope();
 
-  if (!func->get_last_statement() || !func->get_last_statement()->IsReturn()) {
-    set_error(func->source(),
-              "v-0002: function must end with a return statement");
-    return false;
+  if (!current_function_->return_type()->IsVoid()) {
+    if (!func->get_last_statement() ||
+        !func->get_last_statement()->IsReturn()) {
+      set_error(func->source(),
+                "v-0002: non-void function must end with a return statement");
+      return false;
+    }
   }
   return true;
 }