Move return validation from Validator to Resolver

Improved error message to use friendly names. Fixed tests that broke as
a result of this change.

Bug: tint:642
Change-Id: I9a1e819e1a6110a89c826936b96ab84f7f79a084
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/45582
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
This commit is contained in:
Antonio Maiorano
2021-03-22 23:20:17 +00:00
committed by Commit Bot service account
parent 8ac7f8ae9e
commit 2e97435ba6
12 changed files with 177 additions and 190 deletions

View File

@@ -13,6 +13,7 @@
// limitations under the License.
#include "src/ast/return_statement.h"
#include "src/ast/stage_decoration.h"
#include "src/resolver/resolver.h"
#include "src/resolver/resolver_test_helper.h"
@@ -74,5 +75,111 @@ TEST_F(ResolverFunctionValidationTest,
"12:34 error v-0002: non-void function must end with a return statement");
}
TEST_F(ResolverFunctionValidationTest,
FunctionTypeMustMatchReturnStatementType_Pass) {
// [[stage(vertex)]]
// fn func -> void { return; }
Func("func", ast::VariableList{}, ty.void_(),
ast::StatementList{
create<ast::ReturnStatement>(),
},
ast::DecorationList{
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
});
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverFunctionValidationTest,
FunctionTypeMustMatchReturnStatementType_fail) {
// fn func -> void { return 2; }
Func("func", ast::VariableList{}, ty.void_(),
ast::StatementList{
create<ast::ReturnStatement>(Source{Source::Location{12, 34}},
Expr(2)),
},
ast::DecorationList{});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error v-000y: return statement type must match its function "
"return type, returned 'i32', expected 'void'");
}
TEST_F(ResolverFunctionValidationTest,
FunctionTypeMustMatchReturnStatementTypeF32_pass) {
// fn func -> f32 { return 2.0; }
Func("func", ast::VariableList{}, ty.f32(),
ast::StatementList{
create<ast::ReturnStatement>(Source{Source::Location{12, 34}},
Expr(2.f)),
},
ast::DecorationList{});
Func("main", ast::VariableList{}, ty.void_(), ast::StatementList{},
ast::DecorationList{
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
});
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverFunctionValidationTest,
FunctionTypeMustMatchReturnStatementTypeF32_fail) {
// fn func -> f32 { return 2; }
Func("func", ast::VariableList{}, ty.f32(),
ast::StatementList{
create<ast::ReturnStatement>(Source{Source::Location{12, 34}},
Expr(2)),
},
ast::DecorationList{});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error v-000y: return statement type must match its function "
"return type, returned 'i32', expected 'f32'");
}
TEST_F(ResolverFunctionValidationTest,
FunctionTypeMustMatchReturnStatementTypeF32Alias_pass) {
// type myf32 = f32;
// fn func -> myf32 { return 2.0; }
auto* myf32 = ty.alias("myf32", ty.f32());
Func("func", ast::VariableList{}, myf32,
ast::StatementList{
create<ast::ReturnStatement>(Source{Source::Location{12, 34}},
Expr(2.f)),
},
ast::DecorationList{});
Func("main", ast::VariableList{}, ty.void_(), ast::StatementList{},
ast::DecorationList{
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
});
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverFunctionValidationTest,
FunctionTypeMustMatchReturnStatementTypeF32Alias_fail) {
// type myf32 = f32;
// fn func -> myf32 { return 2; }
auto* myf32 = ty.alias("myf32", ty.f32());
Func("func", ast::VariableList{}, myf32,
ast::StatementList{
create<ast::ReturnStatement>(Source{Source::Location{12, 34}},
Expr(2u)),
},
ast::DecorationList{});
Func("main", ast::VariableList{}, ty.void_(), ast::StatementList{},
ast::DecorationList{
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error v-000y: return statement type must match its function "
"return type, returned 'u32', expected 'myf32'");
}
} // namespace
} // namespace tint

View File

@@ -406,8 +406,7 @@ bool Resolver::Statement(ast::Statement* stmt) {
});
}
if (auto* r = stmt->As<ast::ReturnStatement>()) {
current_function_->return_statements.push_back(r);
return Expression(r->value());
return Return(r);
}
if (auto* s = stmt->As<ast::SwitchStatement>()) {
if (!Expression(s->condition())) {
@@ -1647,6 +1646,36 @@ Resolver::StructInfo* Resolver::Structure(type::Struct* str) {
return info;
}
bool Resolver::ValidateReturn(const ast::ReturnStatement* ret) {
type::Type* func_type = current_function_->declaration->return_type();
auto* ret_type = ret->has_value() ? TypeOf(ret->value())->UnwrapAll()
: builder_->ty.void_();
if (func_type->UnwrapAll() != ret_type) {
diagnostics_.add_error(
"v-000y",
"return statement type must match its function "
"return type, returned '" +
ret_type->FriendlyName(builder_->Symbols()) + "', expected '" +
func_type->FriendlyName(builder_->Symbols()) + "'",
ret->source());
return false;
}
return true;
}
bool Resolver::Return(ast::ReturnStatement* ret) {
current_function_->return_statements.push_back(ret);
auto result = Expression(ret->value());
// Validate after processing the return value expression so that its type is
// available for validation
return result && ValidateReturn(ret);
}
bool Resolver::ApplyStorageClassUsageToType(ast::StorageClass sc,
type::Type* ty,
Source usage) {

View File

@@ -219,6 +219,7 @@ class Resolver {
bool Statements(const ast::StatementList&);
bool UnaryOp(ast::UnaryOpExpression*);
bool VariableDeclStatement(const ast::VariableDeclStatement*);
bool Return(ast::ReturnStatement* ret);
// AST and Type validation methods
// Each return true on success, false on failure.
@@ -226,6 +227,7 @@ class Resolver {
bool ValidateParameter(const ast::Variable* param);
bool ValidateFunction(const ast::Function* func);
bool ValidateStructure(const type::Struct* st);
bool ValidateReturn(const ast::ReturnStatement* ret);
/// @returns the semantic information for the array `arr`, building it if it
/// hasn't been constructed already. If an error is raised, nullptr is

View File

@@ -228,7 +228,7 @@ TEST_F(ResolverTest, Stmt_Return) {
auto* cond = Expr(2);
auto* ret = create<ast::ReturnStatement>(cond);
WrapInFunction(ret);
Func("test", {}, ty.i32(), {ret}, {});
EXPECT_TRUE(r()->Resolve()) << r()->error();