resolver: Remove rule that call statements to functions must return void

Fixed: tint:1256
Change-Id: Ibff78a1009d57afd7af8867952a9444daaf13a9c
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/67201
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
Ben Clayton
2021-10-21 20:36:04 +00:00
committed by Tint LUCI CQ
parent 38ed53ce8f
commit 72789de9f5
42 changed files with 318 additions and 353 deletions

View File

@@ -986,7 +986,7 @@ TEST_P(FloatAllMatching, Scalar) {
params.push_back(Expr(1.0f));
}
auto* builtin = Call(name, params);
Func("func", {}, ty.void_(), {Ignore(builtin)},
Func("func", {}, ty.void_(), {CallStmt(builtin)},
{create<ast::StageDecoration>(ast::PipelineStage::kFragment)});
EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -1002,7 +1002,7 @@ TEST_P(FloatAllMatching, Vec2) {
params.push_back(vec2<f32>(1.0f, 1.0f));
}
auto* builtin = Call(name, params);
Func("func", {}, ty.void_(), {Ignore(builtin)},
Func("func", {}, ty.void_(), {CallStmt(builtin)},
{create<ast::StageDecoration>(ast::PipelineStage::kFragment)});
EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -1018,7 +1018,7 @@ TEST_P(FloatAllMatching, Vec3) {
params.push_back(vec3<f32>(1.0f, 1.0f, 1.0f));
}
auto* builtin = Call(name, params);
Func("func", {}, ty.void_(), {Ignore(builtin)},
Func("func", {}, ty.void_(), {CallStmt(builtin)},
{create<ast::StageDecoration>(ast::PipelineStage::kFragment)});
EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -1034,7 +1034,7 @@ TEST_P(FloatAllMatching, Vec4) {
params.push_back(vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f));
}
auto* builtin = Call(name, params);
Func("func", {}, ty.void_(), {Ignore(builtin)},
Func("func", {}, ty.void_(), {CallStmt(builtin)},
{create<ast::StageDecoration>(ast::PipelineStage::kFragment)});
EXPECT_TRUE(r()->Resolve()) << r()->error();

View File

@@ -33,7 +33,7 @@ TEST_F(ResolverCallValidationTest, Recursive_Invalid) {
Func("main", params0, ty.void_(),
ast::StatementList{
create<ast::CallStatement>(call_expr),
CallStmt(call_expr),
},
ast::DecorationList{
Stage(ast::PipelineStage::kVertex),
@@ -56,7 +56,7 @@ TEST_F(ResolverCallValidationTest, Undeclared_Invalid) {
Func("main", params0, ty.f32(),
ast::StatementList{
create<ast::CallStatement>(call_expr),
CallStmt(call_expr),
Return(),
},
ast::DecorationList{});
@@ -109,23 +109,19 @@ TEST_F(ResolverCallValidationTest, MismatchedArgs) {
}
TEST_F(ResolverCallValidationTest, UnusedRetval) {
// fn func() -> f32 { return 1.0; }
// fn main() {func(); return; }
// fn func() { return; }
Func("func", {}, ty.f32(), {Return(Expr(1.0f))}, {});
Func("main", {}, ty.f32(),
Func("main", {}, ty.void_(),
ast::StatementList{
create<ast::CallStatement>(Source{{12, 34}}, Call("func")),
CallStmt(Source{{12, 34}}, Call("func")),
Return(),
},
{});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: result of called function was not used. If this was "
"intentional wrap the function call in ignore()");
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverCallValidationTest, PointerArgument_VariableIdentExpr) {
@@ -139,8 +135,7 @@ TEST_F(ResolverCallValidationTest, PointerArgument_VariableIdentExpr) {
Func("main", {}, ty.void_(),
ast::StatementList{
Decl(Var("z", ty.i32(), Expr(1))),
create<ast::CallStatement>(
Call("foo", AddressOf(Source{{12, 34}}, Expr("z")))),
CallStmt(Call("foo", AddressOf(Source{{12, 34}}, Expr("z")))),
});
EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -157,8 +152,7 @@ TEST_F(ResolverCallValidationTest, PointerArgument_ConstIdentExpr) {
Func("main", {}, ty.void_(),
ast::StatementList{
Decl(Const("z", ty.i32(), Expr(1))),
create<ast::CallStatement>(
Call("foo", AddressOf(Expr(Source{{12, 34}}, "z")))),
CallStmt(Call("foo", AddressOf(Expr(Source{{12, 34}}, "z")))),
});
EXPECT_FALSE(r()->Resolve());
@@ -178,7 +172,7 @@ TEST_F(ResolverCallValidationTest, PointerArgument_NotIdentExprVar) {
Func("main", {}, ty.void_(),
ast::StatementList{
Decl(Var("v", ty.Of(S))),
create<ast::CallStatement>(Call(
CallStmt(Call(
"foo", AddressOf(Source{{12, 34}}, MemberAccessor("v", "m")))),
});
@@ -201,9 +195,8 @@ TEST_F(ResolverCallValidationTest, PointerArgument_AddressOfMemberAccessor) {
Func("main", {}, ty.void_(),
ast::StatementList{
Decl(Const("v", ty.Of(S), Construct(ty.Of(S)))),
create<ast::CallStatement>(Call(
"foo",
AddressOf(Expr(Source{{12, 34}}, MemberAccessor("v", "m"))))),
CallStmt(Call("foo", AddressOf(Expr(Source{{12, 34}},
MemberAccessor("v", "m"))))),
});
EXPECT_FALSE(r()->Resolve());
@@ -218,8 +211,7 @@ TEST_F(ResolverCallValidationTest, PointerArgument_FunctionParam) {
Func("foo", {Param("p", ty.pointer<i32>(ast::StorageClass::kFunction))},
ty.void_(), {});
Func("bar", {Param("p", ty.pointer<i32>(ast::StorageClass::kFunction))},
ty.void_(),
ast::StatementList{create<ast::CallStatement>(Call("foo", Expr("p")))});
ty.void_(), ast::StatementList{CallStmt(Call("foo", Expr("p")))});
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
@@ -237,12 +229,11 @@ TEST_F(ResolverCallValidationTest, PointerArgument_FunctionParamWithMain) {
Func("foo", {Param("p", ty.pointer<i32>(ast::StorageClass::kFunction))},
ty.void_(), {});
Func("bar", {Param("p", ty.pointer<i32>(ast::StorageClass::kFunction))},
ty.void_(),
ast::StatementList{create<ast::CallStatement>(Call("foo", Expr("p")))});
ty.void_(), ast::StatementList{CallStmt(Call("foo", Expr("p")))});
Func("main", ast::VariableList{}, ty.void_(),
{
Decl(Var("v", ty.i32(), Expr(1))),
create<ast::CallStatement>(Call("foo", AddressOf(Expr("v")))),
CallStmt(Call("foo", AddressOf(Expr("v")))),
},
{
Stage(ast::PipelineStage::kFragment),

View File

@@ -399,7 +399,7 @@ TEST_F(ResolverFunctionValidationTest, CannotCallEntryPoint) {
Func("func", ast::VariableList{}, ty.void_(),
{
create<ast::CallStatement>(Call(Source{{12, 34}}, "entrypoint")),
CallStmt(Call(Source{{12, 34}}, "entrypoint")),
});
EXPECT_FALSE(r()->Resolve());

View File

@@ -506,7 +506,7 @@ TEST_P(ResolverIntrinsicTest_Barrier, InferType) {
auto param = GetParam();
auto* call = Call(param.name);
WrapInFunction(create<ast::CallStatement>(call));
WrapInFunction(CallStmt(call));
EXPECT_TRUE(r()->Resolve()) << r()->error();
ASSERT_NE(TypeOf(call), nullptr);
@@ -517,7 +517,7 @@ TEST_P(ResolverIntrinsicTest_Barrier, Error_TooManyParams) {
auto param = GetParam();
auto* call = Call(param.name, vec4<f32>(1.f, 2.f, 3.f, 4.f), 1.0f);
WrapInFunction(create<ast::CallStatement>(call));
WrapInFunction(CallStmt(call));
EXPECT_FALSE(r()->Resolve());
@@ -1943,9 +1943,7 @@ TEST_P(ResolverIntrinsicTest_Texture, Call) {
param.BuildSamplerVariable(this);
auto* call = Call(param.function, param.args(this));
auto* stmt = ast::intrinsic::test::ReturnsVoid(param.overload)
? create<ast::CallStatement>(call)
: Ignore(call);
auto* stmt = CallStmt(call);
Func("func", {}, ty.void_(), {stmt}, {Stage(ast::PipelineStage::kFragment)});
ASSERT_TRUE(r()->Resolve()) << r()->error();

View File

@@ -40,7 +40,8 @@ TEST_F(ResolverIntrinsicValidationTest, InvalidPipelineStageDirect) {
auto* dpdx = create<ast::CallExpression>(Source{{3, 4}}, Expr("dpdx"),
ast::ExpressionList{Expr(1.0f)});
Func(Source{{1, 2}}, "func", ast::VariableList{}, ty.void_(), {Ignore(dpdx)},
Func(Source{{1, 2}}, "func", ast::VariableList{}, ty.void_(),
{CallStmt(dpdx)},
{Stage(ast::PipelineStage::kCompute), WorkgroupSize(1)});
EXPECT_FALSE(r()->Resolve());
@@ -56,16 +57,13 @@ TEST_F(ResolverIntrinsicValidationTest, InvalidPipelineStageIndirect) {
auto* dpdx = create<ast::CallExpression>(Source{{3, 4}}, Expr("dpdx"),
ast::ExpressionList{Expr(1.0f)});
Func(Source{{1, 2}}, "f0", {}, ty.void_(), {Ignore(dpdx)});
Func(Source{{1, 2}}, "f0", {}, ty.void_(), {CallStmt(dpdx)});
Func(Source{{3, 4}}, "f1", {}, ty.void_(),
{create<ast::CallStatement>(Call("f0"))});
Func(Source{{3, 4}}, "f1", {}, ty.void_(), {CallStmt(Call("f0"))});
Func(Source{{5, 6}}, "f2", {}, ty.void_(),
{create<ast::CallStatement>(Call("f1"))});
Func(Source{{5, 6}}, "f2", {}, ty.void_(), {CallStmt(Call("f1"))});
Func(Source{{7, 8}}, "main", {}, ty.void_(),
{create<ast::CallStatement>(Call("f2"))},
Func(Source{{7, 8}}, "main", {}, ty.void_(), {CallStmt(Call("f2"))},
{Stage(ast::PipelineStage::kCompute), WorkgroupSize(1)});
EXPECT_FALSE(r()->Resolve());
@@ -141,7 +139,7 @@ TEST_P(IntrinsicTextureSamplerValidationTest, ConstExpr) {
}
auto* call = Call(param.function, args);
Func("func", {}, ty.void_(), {Ignore(call)},
Func("func", {}, ty.void_(), {CallStmt(call)},
{create<ast::StageDecoration>(ast::PipelineStage::kFragment)});
if (offset.is_valid) {
@@ -175,7 +173,7 @@ TEST_P(IntrinsicTextureSamplerValidationTest, ConstExprOfConstExpr) {
Construct(ty.vec2<i32>(), offset.y, offset.z)));
}
auto* call = Call(param.function, args);
Func("func", {}, ty.void_(), {Ignore(call)},
Func("func", {}, ty.void_(), {CallStmt(call)},
{create<ast::StageDecoration>(ast::PipelineStage::kFragment)});
if (offset.is_valid) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -206,7 +204,7 @@ TEST_P(IntrinsicTextureSamplerValidationTest, EmptyVectorConstructor) {
}
auto* call = Call(param.function, args);
Func("func", {}, ty.void_(), {Ignore(call)},
Func("func", {}, ty.void_(), {CallStmt(call)},
{create<ast::StageDecoration>(ast::PipelineStage::kFragment)});
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
@@ -232,7 +230,7 @@ TEST_P(IntrinsicTextureSamplerValidationTest, GlobalConst) {
}
auto* call = Call(param.function, args);
Func("func", {}, ty.void_(), {Ignore(call)},
Func("func", {}, ty.void_(), {CallStmt(call)},
{create<ast::StageDecoration>(ast::PipelineStage::kFragment)});
EXPECT_FALSE(r()->Resolve());
std::stringstream err;
@@ -261,7 +259,7 @@ TEST_P(IntrinsicTextureSamplerValidationTest, ScalarConst) {
}
auto* call = Call(param.function, args);
Func("func", {}, ty.void_(), {Decl(x), Ignore(call)},
Func("func", {}, ty.void_(), {Decl(x), CallStmt(call)},
{create<ast::StageDecoration>(ast::PipelineStage::kFragment)});
EXPECT_FALSE(r()->Resolve());
std::stringstream err;

View File

@@ -2475,21 +2475,7 @@ bool Resolver::ValidateCall(const ast::CallExpression* call) {
return true;
}
bool Resolver::ValidateCallStatement(const ast::CallStatement* stmt) {
const sem::Type* return_type = TypeOf(stmt->expr);
if (!return_type->Is<sem::Void>()) {
// https://gpuweb.github.io/gpuweb/wgsl/#function-call-statement
// A function call statement executes a function call where the called
// function does not return a value. If the called function returns a value,
// that value must be consumed either through assignment, evaluation in
// another expression or through use of the ignore built-in function (see
// §16.13 Value-steering functions).
AddError(
"result of called function was not used. If this was intentional wrap "
"the function call in ignore()",
stmt->source);
return false;
}
bool Resolver::ValidateCallStatement(const ast::CallStatement*) {
return true;
}

View File

@@ -270,7 +270,7 @@ TEST_F(ResolverTest, Stmt_Call) {
auto* expr = Call("my_func");
auto* call = create<ast::CallStatement>(expr);
auto* call = CallStmt(expr);
WrapInFunction(call);
EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -1947,22 +1947,22 @@ TEST_F(ResolverTest, Function_EntryPoints_LinearTime) {
for (int i = levels - 1; i >= 0; i--) {
Func(fn_a(i), {}, ty.void_(),
{
create<ast::CallStatement>(Call(fn_a(i + 1))),
create<ast::CallStatement>(Call(fn_b(i + 1))),
CallStmt(Call(fn_a(i + 1))),
CallStmt(Call(fn_b(i + 1))),
},
{});
Func(fn_b(i), {}, ty.void_(),
{
create<ast::CallStatement>(Call(fn_a(i + 1))),
create<ast::CallStatement>(Call(fn_b(i + 1))),
CallStmt(Call(fn_a(i + 1))),
CallStmt(Call(fn_b(i + 1))),
},
{});
}
Func("main", {}, ty.void_(),
{
create<ast::CallStatement>(Call(fn_a(0))),
create<ast::CallStatement>(Call(fn_b(0))),
CallStmt(Call(fn_a(0))),
CallStmt(Call(fn_b(0))),
},
{Stage(ast::PipelineStage::kCompute), WorkgroupSize(1)});

View File

@@ -90,10 +90,8 @@ TEST_F(ResolverValidationTest, WorkgroupMemoryUsedInFragmentStage) {
auto* stmt = Assign(Expr("dst"), Expr(Source{{3, 4}}, "wg"));
Func(Source{{5, 6}}, "f2", {}, ty.void_(), {stmt});
Func(Source{{7, 8}}, "f1", {}, ty.void_(),
{create<ast::CallStatement>(Call("f2"))});
Func(Source{{9, 10}}, "f0", {}, ty.void_(),
{create<ast::CallStatement>(Call("f1"))},
Func(Source{{7, 8}}, "f1", {}, ty.void_(), {CallStmt(Call("f2"))});
Func(Source{{9, 10}}, "f0", {}, ty.void_(), {CallStmt(Call("f1"))},
ast::DecorationList{Stage(ast::PipelineStage::kFragment)});
EXPECT_FALSE(r()->Resolve());