tint/resolver: Prevent ICE with invalid input
Attempting to return an abstract-numeric when the function had no return type would trigger an ICE, as an abstract numeric cannot materialize to a void type. Bug: chromium:1332613 Change-Id: I635ebb8dddb2e7628939607a4f964be62b616745 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/92720 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Dan Sinclair <dsinclair@chromium.org> Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
4ef134c19d
commit
33fe68ee0f
|
@ -73,8 +73,8 @@ TEST_F(ResolverFunctionValidationTest, VoidFunctionEndWithoutReturnStatement_Pas
|
||||||
// fn func { var a:i32 = 2i; }
|
// fn func { var a:i32 = 2i; }
|
||||||
auto* var = Var("a", ty.i32(), Expr(2_i));
|
auto* var = Var("a", ty.i32(), Expr(2_i));
|
||||||
|
|
||||||
Func(Source{{12, 34}}, "func", ast::VariableList{}, ty.void_(),
|
Func(Source{{12, 34}}, "func", {}, ty.void_(),
|
||||||
ast::StatementList{
|
{
|
||||||
Decl(var),
|
Decl(var),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -88,12 +88,11 @@ TEST_F(ResolverFunctionValidationTest, FunctionUsingSameVariableName_Pass) {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
auto* var = Var("func", ty.i32(), Expr(0_i));
|
auto* var = Var("func", ty.i32(), Expr(0_i));
|
||||||
Func("func", ast::VariableList{}, ty.i32(),
|
Func("func", {}, ty.i32(),
|
||||||
ast::StatementList{
|
{
|
||||||
Decl(var),
|
Decl(var),
|
||||||
Return(Source{{12, 34}}, Expr("func")),
|
Return(Source{{12, 34}}, Expr("func")),
|
||||||
},
|
});
|
||||||
ast::AttributeList{});
|
|
||||||
|
|
||||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
}
|
}
|
||||||
|
@ -103,17 +102,15 @@ TEST_F(ResolverFunctionValidationTest, FunctionNameSameAsFunctionScopeVariableNa
|
||||||
// fn b() -> i32 { return 2; }
|
// fn b() -> i32 { return 2; }
|
||||||
|
|
||||||
auto* var = Var("b", ty.i32(), Expr(0_i));
|
auto* var = Var("b", ty.i32(), Expr(0_i));
|
||||||
Func("a", ast::VariableList{}, ty.void_(),
|
Func("a", {}, ty.void_(),
|
||||||
ast::StatementList{
|
{
|
||||||
Decl(var),
|
Decl(var),
|
||||||
},
|
});
|
||||||
ast::AttributeList{});
|
|
||||||
|
|
||||||
Func(Source{{12, 34}}, "b", ast::VariableList{}, ty.i32(),
|
Func(Source{{12, 34}}, "b", {}, ty.i32(),
|
||||||
ast::StatementList{
|
{
|
||||||
Return(2_i),
|
Return(2_i),
|
||||||
},
|
});
|
||||||
ast::AttributeList{});
|
|
||||||
|
|
||||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
}
|
}
|
||||||
|
@ -129,7 +126,7 @@ TEST_F(ResolverFunctionValidationTest, UnreachableCode_return) {
|
||||||
auto* ret = Return();
|
auto* ret = Return();
|
||||||
auto* assign_a = Assign(Source{{12, 34}}, "a", 2_i);
|
auto* assign_a = Assign(Source{{12, 34}}, "a", 2_i);
|
||||||
|
|
||||||
Func("func", ast::VariableList{}, ty.void_(), {decl_a, ret, assign_a});
|
Func("func", {}, ty.void_(), {decl_a, ret, assign_a});
|
||||||
|
|
||||||
ASSERT_TRUE(r()->Resolve());
|
ASSERT_TRUE(r()->Resolve());
|
||||||
|
|
||||||
|
@ -150,7 +147,7 @@ TEST_F(ResolverFunctionValidationTest, UnreachableCode_return_InBlocks) {
|
||||||
auto* ret = Return();
|
auto* ret = Return();
|
||||||
auto* assign_a = Assign(Source{{12, 34}}, "a", 2_i);
|
auto* assign_a = Assign(Source{{12, 34}}, "a", 2_i);
|
||||||
|
|
||||||
Func("func", ast::VariableList{}, ty.void_(), {decl_a, Block(Block(Block(ret))), assign_a});
|
Func("func", {}, ty.void_(), {decl_a, Block(Block(Block(ret))), assign_a});
|
||||||
|
|
||||||
ASSERT_TRUE(r()->Resolve());
|
ASSERT_TRUE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
|
EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
|
||||||
|
@ -170,7 +167,7 @@ TEST_F(ResolverFunctionValidationTest, UnreachableCode_discard) {
|
||||||
auto* discard = Discard();
|
auto* discard = Discard();
|
||||||
auto* assign_a = Assign(Source{{12, 34}}, "a", 2_i);
|
auto* assign_a = Assign(Source{{12, 34}}, "a", 2_i);
|
||||||
|
|
||||||
Func("func", ast::VariableList{}, ty.void_(), {decl_a, discard, assign_a});
|
Func("func", {}, ty.void_(), {decl_a, discard, assign_a});
|
||||||
|
|
||||||
ASSERT_TRUE(r()->Resolve());
|
ASSERT_TRUE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
|
EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
|
||||||
|
@ -190,7 +187,7 @@ TEST_F(ResolverFunctionValidationTest, UnreachableCode_discard_InBlocks) {
|
||||||
auto* discard = Discard();
|
auto* discard = Discard();
|
||||||
auto* assign_a = Assign(Source{{12, 34}}, "a", 2_i);
|
auto* assign_a = Assign(Source{{12, 34}}, "a", 2_i);
|
||||||
|
|
||||||
Func("func", ast::VariableList{}, ty.void_(), {decl_a, Block(Block(Block(discard))), assign_a});
|
Func("func", {}, ty.void_(), {decl_a, Block(Block(Block(discard))), assign_a});
|
||||||
|
|
||||||
ASSERT_TRUE(r()->Resolve());
|
ASSERT_TRUE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
|
EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
|
||||||
|
@ -204,11 +201,10 @@ TEST_F(ResolverFunctionValidationTest, FunctionEndWithoutReturnStatement_Fail) {
|
||||||
|
|
||||||
auto* var = Var("a", ty.i32(), Expr(2_i));
|
auto* var = Var("a", ty.i32(), Expr(2_i));
|
||||||
|
|
||||||
Func(Source{{12, 34}}, "func", ast::VariableList{}, ty.i32(),
|
Func(Source{{12, 34}}, "func", {}, ty.i32(),
|
||||||
ast::StatementList{
|
{
|
||||||
Decl(var),
|
Decl(var),
|
||||||
},
|
});
|
||||||
ast::AttributeList{});
|
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: missing return at end of function");
|
EXPECT_EQ(r()->error(), "12:34 error: missing return at end of function");
|
||||||
|
@ -217,7 +213,7 @@ TEST_F(ResolverFunctionValidationTest, FunctionEndWithoutReturnStatement_Fail) {
|
||||||
TEST_F(ResolverFunctionValidationTest, VoidFunctionEndWithoutReturnStatementEmptyBody_Pass) {
|
TEST_F(ResolverFunctionValidationTest, VoidFunctionEndWithoutReturnStatementEmptyBody_Pass) {
|
||||||
// fn func {}
|
// fn func {}
|
||||||
|
|
||||||
Func(Source{{12, 34}}, "func", ast::VariableList{}, ty.void_(), ast::StatementList{});
|
Func(Source{{12, 34}}, "func", {}, ty.void_(), {});
|
||||||
|
|
||||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
}
|
}
|
||||||
|
@ -225,8 +221,7 @@ TEST_F(ResolverFunctionValidationTest, VoidFunctionEndWithoutReturnStatementEmpt
|
||||||
TEST_F(ResolverFunctionValidationTest, FunctionEndWithoutReturnStatementEmptyBody_Fail) {
|
TEST_F(ResolverFunctionValidationTest, FunctionEndWithoutReturnStatementEmptyBody_Fail) {
|
||||||
// fn func() -> int {}
|
// fn func() -> int {}
|
||||||
|
|
||||||
Func(Source{{12, 34}}, "func", ast::VariableList{}, ty.i32(), ast::StatementList{},
|
Func(Source{{12, 34}}, "func", {}, ty.i32(), {});
|
||||||
ast::AttributeList{});
|
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: missing return at end of function");
|
EXPECT_EQ(r()->error(), "12:34 error: missing return at end of function");
|
||||||
|
@ -235,21 +230,34 @@ TEST_F(ResolverFunctionValidationTest, FunctionEndWithoutReturnStatementEmptyBod
|
||||||
TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementType_Pass) {
|
TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementType_Pass) {
|
||||||
// fn func { return; }
|
// fn func { return; }
|
||||||
|
|
||||||
Func("func", ast::VariableList{}, ty.void_(),
|
Func("func", {}, ty.void_(), {Return()});
|
||||||
ast::StatementList{
|
|
||||||
Return(),
|
|
||||||
});
|
|
||||||
|
|
||||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementType_fail) {
|
TEST_F(ResolverFunctionValidationTest, VoidFunctionReturnsAInt) {
|
||||||
|
// fn func { return 2; }
|
||||||
|
Func("func", {}, ty.void_(), {Return(Source{{12, 34}}, Expr(2_a))});
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(r()->error(),
|
||||||
|
"12:34 error: return statement type must match its function return "
|
||||||
|
"type, returned 'abstract-int', expected 'void'");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverFunctionValidationTest, VoidFunctionReturnsAFloat) {
|
||||||
|
// fn func { return 2.0; }
|
||||||
|
Func("func", {}, ty.void_(), {Return(Source{{12, 34}}, Expr(2.0_a))});
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(r()->error(),
|
||||||
|
"12:34 error: return statement type must match its function return "
|
||||||
|
"type, returned 'abstract-float', expected 'void'");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverFunctionValidationTest, VoidFunctionReturnsI32) {
|
||||||
// fn func { return 2i; }
|
// fn func { return 2i; }
|
||||||
Func("func", ast::VariableList{}, ty.void_(),
|
Func("func", {}, ty.void_(), {Return(Source{{12, 34}}, Expr(2_i))});
|
||||||
ast::StatementList{
|
|
||||||
Return(Source{{12, 34}}, Expr(2_i)),
|
|
||||||
},
|
|
||||||
ast::AttributeList{});
|
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
|
@ -272,11 +280,10 @@ TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementType_
|
||||||
|
|
||||||
TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementTypeMissing_fail) {
|
TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementTypeMissing_fail) {
|
||||||
// fn func() -> f32 { return; }
|
// fn func() -> f32 { return; }
|
||||||
Func("func", ast::VariableList{}, ty.f32(),
|
Func("func", {}, ty.f32(),
|
||||||
ast::StatementList{
|
{
|
||||||
Return(Source{{12, 34}}, nullptr),
|
Return(Source{{12, 34}}, nullptr),
|
||||||
},
|
});
|
||||||
ast::AttributeList{});
|
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
|
@ -286,22 +293,20 @@ TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementTypeM
|
||||||
|
|
||||||
TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementTypeF32_pass) {
|
TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementTypeF32_pass) {
|
||||||
// fn func() -> f32 { return 2.0; }
|
// fn func() -> f32 { return 2.0; }
|
||||||
Func("func", ast::VariableList{}, ty.f32(),
|
Func("func", {}, ty.f32(),
|
||||||
ast::StatementList{
|
{
|
||||||
Return(Source{{12, 34}}, Expr(2_f)),
|
Return(Source{{12, 34}}, Expr(2_f)),
|
||||||
},
|
});
|
||||||
ast::AttributeList{});
|
|
||||||
|
|
||||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementTypeF32_fail) {
|
TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementTypeF32_fail) {
|
||||||
// fn func() -> f32 { return 2i; }
|
// fn func() -> f32 { return 2i; }
|
||||||
Func("func", ast::VariableList{}, ty.f32(),
|
Func("func", {}, ty.f32(),
|
||||||
ast::StatementList{
|
{
|
||||||
Return(Source{{12, 34}}, Expr(2_i)),
|
Return(Source{{12, 34}}, Expr(2_i)),
|
||||||
},
|
});
|
||||||
ast::AttributeList{});
|
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
|
@ -313,11 +318,10 @@ TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementTypeF
|
||||||
// type myf32 = f32;
|
// type myf32 = f32;
|
||||||
// fn func() -> myf32 { return 2.0; }
|
// fn func() -> myf32 { return 2.0; }
|
||||||
auto* myf32 = Alias("myf32", ty.f32());
|
auto* myf32 = Alias("myf32", ty.f32());
|
||||||
Func("func", ast::VariableList{}, ty.Of(myf32),
|
Func("func", {}, ty.Of(myf32),
|
||||||
ast::StatementList{
|
{
|
||||||
Return(Source{{12, 34}}, Expr(2_f)),
|
Return(Source{{12, 34}}, Expr(2_f)),
|
||||||
},
|
});
|
||||||
ast::AttributeList{});
|
|
||||||
|
|
||||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
}
|
}
|
||||||
|
@ -326,11 +330,10 @@ TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementTypeF
|
||||||
// type myf32 = f32;
|
// type myf32 = f32;
|
||||||
// fn func() -> myf32 { return 2u; }
|
// fn func() -> myf32 { return 2u; }
|
||||||
auto* myf32 = Alias("myf32", ty.f32());
|
auto* myf32 = Alias("myf32", ty.f32());
|
||||||
Func("func", ast::VariableList{}, ty.Of(myf32),
|
Func("func", {}, ty.Of(myf32),
|
||||||
ast::StatementList{
|
{
|
||||||
Return(Source{{12, 34}}, Expr(2_u)),
|
Return(Source{{12, 34}}, Expr(2_u)),
|
||||||
},
|
});
|
||||||
ast::AttributeList{});
|
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
|
@ -341,10 +344,10 @@ TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementTypeF
|
||||||
TEST_F(ResolverFunctionValidationTest, CannotCallEntryPoint) {
|
TEST_F(ResolverFunctionValidationTest, CannotCallEntryPoint) {
|
||||||
// @stage(compute) @workgroup_size(1) fn entrypoint() {}
|
// @stage(compute) @workgroup_size(1) fn entrypoint() {}
|
||||||
// fn func() { return entrypoint(); }
|
// fn func() { return entrypoint(); }
|
||||||
Func("entrypoint", ast::VariableList{}, ty.void_(), {},
|
Func("entrypoint", {}, ty.void_(), {},
|
||||||
{Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
|
{Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
|
||||||
|
|
||||||
Func("func", ast::VariableList{}, ty.void_(),
|
Func("func", {}, ty.void_(),
|
||||||
{
|
{
|
||||||
CallStmt(Call(Source{{12, 34}}, "entrypoint")),
|
CallStmt(Call(Source{{12, 34}}, "entrypoint")),
|
||||||
});
|
});
|
||||||
|
@ -359,8 +362,8 @@ TEST_F(ResolverFunctionValidationTest, PipelineStage_MustBeUnique_Fail) {
|
||||||
// @stage(fragment)
|
// @stage(fragment)
|
||||||
// @stage(vertex)
|
// @stage(vertex)
|
||||||
// fn main() { return; }
|
// fn main() { return; }
|
||||||
Func(Source{{12, 34}}, "main", ast::VariableList{}, ty.void_(),
|
Func(Source{{12, 34}}, "main", {}, ty.void_(),
|
||||||
ast::StatementList{
|
{
|
||||||
Return(),
|
Return(),
|
||||||
},
|
},
|
||||||
ast::AttributeList{
|
ast::AttributeList{
|
||||||
|
@ -375,11 +378,10 @@ TEST_F(ResolverFunctionValidationTest, PipelineStage_MustBeUnique_Fail) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverFunctionValidationTest, NoPipelineEntryPoints) {
|
TEST_F(ResolverFunctionValidationTest, NoPipelineEntryPoints) {
|
||||||
Func("vtx_func", ast::VariableList{}, ty.void_(),
|
Func("vtx_func", {}, ty.void_(),
|
||||||
ast::StatementList{
|
{
|
||||||
Return(),
|
Return(),
|
||||||
},
|
});
|
||||||
ast::AttributeList{});
|
|
||||||
|
|
||||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
}
|
}
|
||||||
|
@ -392,8 +394,7 @@ TEST_F(ResolverFunctionValidationTest, FunctionVarInitWithParam) {
|
||||||
auto* bar = Param("bar", ty.f32());
|
auto* bar = Param("bar", ty.f32());
|
||||||
auto* baz = Var("baz", ty.f32(), Expr("bar"));
|
auto* baz = Var("baz", ty.f32(), Expr("bar"));
|
||||||
|
|
||||||
Func("foo", ast::VariableList{bar}, ty.void_(), ast::StatementList{Decl(baz)},
|
Func("foo", ast::VariableList{bar}, ty.void_(), {Decl(baz)});
|
||||||
ast::AttributeList{});
|
|
||||||
|
|
||||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
}
|
}
|
||||||
|
@ -406,8 +407,7 @@ TEST_F(ResolverFunctionValidationTest, FunctionConstInitWithParam) {
|
||||||
auto* bar = Param("bar", ty.f32());
|
auto* bar = Param("bar", ty.f32());
|
||||||
auto* baz = Let("baz", ty.f32(), Expr("bar"));
|
auto* baz = Let("baz", ty.f32(), Expr("bar"));
|
||||||
|
|
||||||
Func("foo", ast::VariableList{bar}, ty.void_(), ast::StatementList{Decl(baz)},
|
Func("foo", ast::VariableList{bar}, ty.void_(), {Decl(baz)});
|
||||||
ast::AttributeList{});
|
|
||||||
|
|
||||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2302,10 +2302,16 @@ sem::Statement* Resolver::ReturnStatement(const ast::ReturnStatement* stmt) {
|
||||||
|
|
||||||
const sem::Type* value_ty = nullptr;
|
const sem::Type* value_ty = nullptr;
|
||||||
if (auto* value = stmt->value) {
|
if (auto* value = stmt->value) {
|
||||||
const auto* expr = Materialize(Expression(value), current_function_->ReturnType());
|
const auto* expr = Expression(value);
|
||||||
if (!expr) {
|
if (!expr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (auto* ret_ty = current_function_->ReturnType(); !ret_ty->Is<sem::Void>()) {
|
||||||
|
expr = Materialize(expr, ret_ty);
|
||||||
|
if (!expr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
behaviors.Add(expr->Behaviors() - sem::Behavior::kNext);
|
behaviors.Add(expr->Behaviors() - sem::Behavior::kNext);
|
||||||
value_ty = expr->Type()->UnwrapRef();
|
value_ty = expr->Type()->UnwrapRef();
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue