mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-17 08:57:26 +00:00
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:
committed by
Commit Bot service account
parent
8ac7f8ae9e
commit
2e97435ba6
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user