diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5a9494edbc..fde494d9ef 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -590,6 +590,7 @@ if(${TINT_BUILD_TESTS}) resolver/block_test.cc resolver/builtins_validation_test.cc resolver/call_test.cc + resolver/call_validation_test.cc resolver/control_block_validation_test.cc resolver/decoration_validation_test.cc resolver/entry_point_validation_test.cc diff --git a/src/ast/module_clone_test.cc b/src/ast/module_clone_test.cc index e635d56fb0..0dc6eb9e11 100644 --- a/src/ast/module_clone_test.cc +++ b/src/ast/module_clone_test.cc @@ -97,7 +97,7 @@ fn f1(p0 : f32, p1 : i32) -> f32 { [[stage(fragment)]] fn main() { - f1(1.0, 2); + ignore(f1(1.0, 2)); } let declaration_order_check_0 : i32 = 1; diff --git a/src/inspector/inspector_test.cc b/src/inspector/inspector_test.cc index 0e050f6c9b..f7c9b94154 100644 --- a/src/inspector/inspector_test.cc +++ b/src/inspector/inspector_test.cc @@ -2755,8 +2755,8 @@ TEST_P(InspectorGetMultisampledTextureResourceBindingsTestWithParam, Func("ep", ast::VariableList(), ty.void_(), ast::StatementList{ - create(Call("textureLoad", "foo_texture", - "foo_coords", "foo_sample_index")), + Ignore(Call("textureLoad", "foo_texture", "foo_coords", + "foo_sample_index")), }, ast::DecorationList{ Stage(ast::PipelineStage::kFragment), @@ -3013,8 +3013,7 @@ TEST_P(InspectorGetDepthTextureResourceBindingsTestWithParam, Func("ep", ast::VariableList(), ty.void_(), ast::StatementList{ - create( - Call("textureDimensions", "dt", "dt_level")), + Ignore(Call("textureDimensions", "dt", "dt_level")), }, ast::DecorationList{ Stage(ast::PipelineStage::kFragment), @@ -3056,7 +3055,7 @@ TEST_F(InspectorGetExternalTextureResourceBindingsTest, Simple) { Func("ep", ast::VariableList(), ty.void_(), ast::StatementList{ - create(Call("textureDimensions", "et")), + Ignore(Call("textureDimensions", "et")), }, ast::DecorationList{ Stage(ast::PipelineStage::kFragment), diff --git a/src/program_builder.cc b/src/program_builder.cc index bdb57263fa..632d48016d 100644 --- a/src/program_builder.cc +++ b/src/program_builder.cc @@ -123,7 +123,7 @@ ast::Statement* ProgramBuilder::WrapInStatement(ast::Literal* lit) { ast::Statement* ProgramBuilder::WrapInStatement(ast::Expression* expr) { if (auto* ce = expr->As()) { - return create(ce); + return Ignore(ce); } // Create a temporary variable of inferred type from expr. return Decl(Const(symbols_.New(), nullptr, expr)); diff --git a/src/program_builder.h b/src/program_builder.h index b04a5adde5..ace90f08a7 100644 --- a/src/program_builder.h +++ b/src/program_builder.h @@ -26,6 +26,7 @@ #include "src/ast/bool.h" #include "src/ast/bool_literal.h" #include "src/ast/call_expression.h" +#include "src/ast/call_statement.h" #include "src/ast/case_statement.h" #include "src/ast/depth_texture.h" #include "src/ast/external_texture.h" @@ -1426,6 +1427,14 @@ class ProgramBuilder { ExprList(std::forward(args)...)); } + /// @param expr the expression to ignore + /// @returns a `ast::CallStatement` that calls the `ignore` intrinsic which is + /// passed the single `expr` argument + template + ast::CallStatement* Ignore(EXPR&& expr) { + return create(Call("ignore", Expr(expr))); + } + /// @param lhs the left hand argument to the addition operation /// @param rhs the right hand argument to the addition operation /// @returns a `ast::BinaryExpression` summing the arguments `lhs` and `rhs` diff --git a/src/resolver/builtins_validation_test.cc b/src/resolver/builtins_validation_test.cc index de9603e90a..b035cf0998 100644 --- a/src/resolver/builtins_validation_test.cc +++ b/src/resolver/builtins_validation_test.cc @@ -351,7 +351,7 @@ TEST_P(FloatAllMatching, Scalar) { params.push_back(Expr(1.0f)); } auto* builtin = Call(name, params); - Func("func", {}, ty.void_(), {create(builtin)}, + Func("func", {}, ty.void_(), {Ignore(builtin)}, {create(ast::PipelineStage::kFragment)}); EXPECT_TRUE(r()->Resolve()) << r()->error(); @@ -367,7 +367,7 @@ TEST_P(FloatAllMatching, Vec2) { params.push_back(vec2(1.0f, 1.0f)); } auto* builtin = Call(name, params); - Func("func", {}, ty.void_(), {create(builtin)}, + Func("func", {}, ty.void_(), {Ignore(builtin)}, {create(ast::PipelineStage::kFragment)}); EXPECT_TRUE(r()->Resolve()) << r()->error(); @@ -383,7 +383,7 @@ TEST_P(FloatAllMatching, Vec3) { params.push_back(vec3(1.0f, 1.0f, 1.0f)); } auto* builtin = Call(name, params); - Func("func", {}, ty.void_(), {create(builtin)}, + Func("func", {}, ty.void_(), {Ignore(builtin)}, {create(ast::PipelineStage::kFragment)}); EXPECT_TRUE(r()->Resolve()) << r()->error(); @@ -399,7 +399,7 @@ TEST_P(FloatAllMatching, Vec4) { params.push_back(vec4(1.0f, 1.0f, 1.0f, 1.0f)); } auto* builtin = Call(name, params); - Func("func", {}, ty.void_(), {create(builtin)}, + Func("func", {}, ty.void_(), {Ignore(builtin)}, {create(ast::PipelineStage::kFragment)}); EXPECT_TRUE(r()->Resolve()) << r()->error(); diff --git a/src/resolver/call_test.cc b/src/resolver/call_test.cc index f9e2af5823..e7e4d245a0 100644 --- a/src/resolver/call_test.cc +++ b/src/resolver/call_test.cc @@ -58,56 +58,6 @@ using u32 = builder::u32; using ResolverCallTest = ResolverTest; -TEST_F(ResolverCallTest, Recursive_Invalid) { - // fn main() {main(); } - - SetSource(Source::Location{12, 34}); - auto* call_expr = Call("main"); - ast::VariableList params0; - - Func("main", params0, ty.void_(), - ast::StatementList{ - create(call_expr), - }, - ast::DecorationList{ - Stage(ast::PipelineStage::kVertex), - }); - - EXPECT_FALSE(r()->Resolve()); - - EXPECT_EQ(r()->error(), - "12:34 error v-0004: recursion is not permitted. 'main' attempted " - "to call " - "itself."); -} - -TEST_F(ResolverCallTest, Undeclared_Invalid) { - // fn main() {func(); return; } - // fn func() { return; } - - SetSource(Source::Location{12, 34}); - auto* call_expr = Call("func"); - ast::VariableList params0; - - Func("main", params0, ty.f32(), - ast::StatementList{ - create(call_expr), - Return(), - }, - ast::DecorationList{}); - - Func("func", params0, ty.f32(), - ast::StatementList{ - Return(), - }, - ast::DecorationList{}); - - EXPECT_FALSE(r()->Resolve()); - - EXPECT_EQ(r()->error(), - "12:34 error: v-0006: unable to find called function: func"); -} - struct Params { builder::ast_expr_func_ptr create_value; builder::ast_type_func_ptr create_type; @@ -153,42 +103,6 @@ TEST_F(ResolverCallTest, Valid) { EXPECT_TRUE(r()->Resolve()) << r()->error(); } -TEST_F(ResolverCallTest, TooFewArgs) { - Func("foo", {Param(Sym(), ty.i32()), Param(Sym(), ty.f32())}, ty.void_(), - {Return()}); - auto* call = Call(Source{{12, 34}}, "foo", 1); - WrapInFunction(call); - - EXPECT_FALSE(r()->Resolve()); - EXPECT_EQ( - r()->error(), - "12:34 error: too few arguments in call to 'foo', expected 2, got 1"); -} - -TEST_F(ResolverCallTest, TooManyArgs) { - Func("foo", {Param(Sym(), ty.i32()), Param(Sym(), ty.f32())}, ty.void_(), - {Return()}); - auto* call = Call(Source{{12, 34}}, "foo", 1, 1.0f, 1.0f); - WrapInFunction(call); - - EXPECT_FALSE(r()->Resolve()); - EXPECT_EQ( - r()->error(), - "12:34 error: too many arguments in call to 'foo', expected 2, got 3"); -} - -TEST_F(ResolverCallTest, MismatchedArgs) { - Func("foo", {Param(Sym(), ty.i32()), Param(Sym(), ty.f32())}, ty.void_(), - {Return()}); - auto* call = Call("foo", Expr(Source{{12, 34}}, true), 1.0f); - WrapInFunction(call); - - EXPECT_FALSE(r()->Resolve()); - EXPECT_EQ(r()->error(), - "12:34 error: type mismatch for argument 1 in call to 'foo', " - "expected 'i32', got 'bool'"); -} - } // namespace } // namespace resolver } // namespace tint diff --git a/src/resolver/call_validation_test.cc b/src/resolver/call_validation_test.cc new file mode 100644 index 0000000000..d2f31961c6 --- /dev/null +++ b/src/resolver/call_validation_test.cc @@ -0,0 +1,135 @@ +// Copyright 2021 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/resolver/resolver.h" + +#include "gmock/gmock.h" +#include "src/ast/call_statement.h" +#include "src/resolver/resolver_test_helper.h" + +namespace tint { +namespace resolver { +namespace { + +using ResolverCallValidationTest = ResolverTest; + +TEST_F(ResolverCallValidationTest, Recursive_Invalid) { + // fn main() {main(); } + + SetSource(Source::Location{12, 34}); + auto* call_expr = Call("main"); + ast::VariableList params0; + + Func("main", params0, ty.void_(), + ast::StatementList{ + create(call_expr), + }, + ast::DecorationList{ + Stage(ast::PipelineStage::kVertex), + }); + + EXPECT_FALSE(r()->Resolve()); + + EXPECT_EQ(r()->error(), + "12:34 error v-0004: recursion is not permitted. 'main' attempted " + "to call " + "itself."); +} + +TEST_F(ResolverCallValidationTest, Undeclared_Invalid) { + // fn main() {func(); return; } + // fn func() { return; } + + SetSource(Source::Location{12, 34}); + auto* call_expr = Call("func"); + ast::VariableList params0; + + Func("main", params0, ty.f32(), + ast::StatementList{ + create(call_expr), + Return(), + }, + ast::DecorationList{}); + + Func("func", params0, ty.f32(), + ast::StatementList{ + Return(), + }, + ast::DecorationList{}); + + EXPECT_FALSE(r()->Resolve()); + + EXPECT_EQ(r()->error(), + "12:34 error: v-0006: unable to find called function: func"); +} + +TEST_F(ResolverCallValidationTest, TooFewArgs) { + Func("foo", {Param(Sym(), ty.i32()), Param(Sym(), ty.f32())}, ty.void_(), + {Return()}); + auto* call = Call(Source{{12, 34}}, "foo", 1); + WrapInFunction(call); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: too few arguments in call to 'foo', expected 2, got 1"); +} + +TEST_F(ResolverCallValidationTest, TooManyArgs) { + Func("foo", {Param(Sym(), ty.i32()), Param(Sym(), ty.f32())}, ty.void_(), + {Return()}); + auto* call = Call(Source{{12, 34}}, "foo", 1, 1.0f, 1.0f); + WrapInFunction(call); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: too many arguments in call to 'foo', expected 2, got 3"); +} + +TEST_F(ResolverCallValidationTest, MismatchedArgs) { + Func("foo", {Param(Sym(), ty.i32()), Param(Sym(), ty.f32())}, ty.void_(), + {Return()}); + auto* call = Call("foo", Expr(Source{{12, 34}}, true), 1.0f); + WrapInFunction(call); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: type mismatch for argument 1 in call to 'foo', " + "expected 'i32', got 'bool'"); +} + +TEST_F(ResolverCallValidationTest, UnusedRetval) { + // fn main() {func(); return; } + // fn func() { return; } + + Func("func", {}, ty.f32(), {Return(Expr(1.0f))}, {}); + + Func("main", {}, ty.f32(), + ast::StatementList{ + create(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()"); +} + +} // namespace +} // namespace resolver +} // namespace tint diff --git a/src/resolver/intrinsic_test.cc b/src/resolver/intrinsic_test.cc index 763cf244a9..92a7e49584 100644 --- a/src/resolver/intrinsic_test.cc +++ b/src/resolver/intrinsic_test.cc @@ -55,7 +55,7 @@ TEST_P(ResolverIntrinsicDerivativeTest, Scalar) { Global("ident", ty.f32(), ast::StorageClass::kInput); auto* expr = Call(name, "ident"); - Func("func", {}, ty.void_(), {create(expr)}, + Func("func", {}, ty.void_(), {Ignore(expr)}, {create(ast::PipelineStage::kFragment)}); EXPECT_TRUE(r()->Resolve()) << r()->error(); @@ -69,7 +69,7 @@ TEST_P(ResolverIntrinsicDerivativeTest, Vector) { Global("ident", ty.vec4(), ast::StorageClass::kInput); auto* expr = Call(name, "ident"); - Func("func", {}, ty.void_(), {create(expr)}, + Func("func", {}, ty.void_(), {Ignore(expr)}, {create(ast::PipelineStage::kFragment)}); EXPECT_TRUE(r()->Resolve()) << r()->error(); @@ -1938,7 +1938,7 @@ TEST_P(ResolverIntrinsicTest_Texture, Call) { param.buildSamplerVariable(this); auto* call = Call(param.function, param.args(this)); - Func("func", {}, ty.void_(), {create(call)}, + Func("func", {}, ty.void_(), {Ignore(call)}, {create(ast::PipelineStage::kFragment)}); ASSERT_TRUE(r()->Resolve()) << r()->error(); diff --git a/src/resolver/intrinsic_validation_test.cc b/src/resolver/intrinsic_validation_test.cc index 31b5d3d6b3..7de43a9f82 100644 --- a/src/resolver/intrinsic_validation_test.cc +++ b/src/resolver/intrinsic_validation_test.cc @@ -53,8 +53,7 @@ TEST_F(ResolverIntrinsicValidationTest, InvalidPipelineStageDirect) { auto* dpdx = create(Source{{3, 4}}, Expr("dpdx"), ast::ExpressionList{Expr(1.0f)}); - Func(Source{{1, 2}}, "func", ast::VariableList{}, ty.void_(), - {create(dpdx)}, + Func(Source{{1, 2}}, "func", ast::VariableList{}, ty.void_(), {Ignore(dpdx)}, {Stage(ast::PipelineStage::kCompute)}); EXPECT_FALSE(r()->Resolve()); @@ -70,18 +69,16 @@ TEST_F(ResolverIntrinsicValidationTest, InvalidPipelineStageIndirect) { auto* dpdx = create(Source{{3, 4}}, Expr("dpdx"), ast::ExpressionList{Expr(1.0f)}); - Func(Source{{1, 2}}, "f0", ast::VariableList{}, ty.void_(), - {create(dpdx)}); + Func(Source{{1, 2}}, "f0", ast::VariableList{}, ty.void_(), {Ignore(dpdx)}); Func(Source{{3, 4}}, "f1", ast::VariableList{}, ty.void_(), - {create(Call("f0"))}); + {Ignore(Call("f0"))}); Func(Source{{5, 6}}, "f2", ast::VariableList{}, ty.void_(), - {create(Call("f1"))}); + {Ignore(Call("f1"))}); Func(Source{{7, 8}}, "main", ast::VariableList{}, ty.void_(), - {create(Call("f2"))}, - {Stage(ast::PipelineStage::kCompute)}); + {Ignore(Call("f2"))}, {Stage(ast::PipelineStage::kCompute)}); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc index 3dcc3106e7..322f524bb1 100644 --- a/src/resolver/resolver.cc +++ b/src/resolver/resolver.cc @@ -1436,7 +1436,13 @@ bool Resolver::Statement(ast::Statement* stmt) { } if (auto* c = stmt->As()) { Mark(c->expr()); - return Expression(c->expr()); + if (!Expression(c->expr())) { + return false; + } + if (!ValidateCallStatement(c)) { + return false; + } + return true; } if (auto* c = stmt->As()) { return CaseStatement(c); @@ -1710,6 +1716,40 @@ bool Resolver::Call(ast::CallExpression* call) { return true; } +bool Resolver::ValidateCallStatement(ast::CallStatement* stmt) { + const sem::Type* return_type = nullptr; + // A function call is made to either a user declared function or an intrinsic. + // function_calls_ only maps CallExpression to user declared functions + auto it = function_calls_.find(stmt->expr()); + if (it != function_calls_.end()) { + return_type = it->second.function->return_type; + } else { + // Must be an intrinsic call + auto* target = builder_->Sem().Get(stmt->expr())->Target(); + if (auto* intrinsic = target->As()) { + return_type = intrinsic->ReturnType(); + } else { + TINT_ICE(diagnostics_) << "call target was not an intrinsic, but a " + << intrinsic->TypeInfo().name; + } + } + + if (!return_type->Is()) { + // 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). + diagnostics_.add_error( + "result of called function was not used. If this was intentional wrap " + "the function call in ignore()", + stmt->source()); + return false; + } + return true; +} + bool Resolver::IntrinsicCall(ast::CallExpression* call, sem::IntrinsicType intrinsic_type) { std::vector arg_tys; diff --git a/src/resolver/resolver.h b/src/resolver/resolver.h index b58d2b504d..6b08496bad 100644 --- a/src/resolver/resolver.h +++ b/src/resolver/resolver.h @@ -39,6 +39,7 @@ class BinaryExpression; class BitcastExpression; class CallExpression; class CaseStatement; +class CallStatement; class ConstructorExpression; class Function; class IdentifierExpression; @@ -255,6 +256,7 @@ class Resolver { uint32_t el_align, const Source& source); bool ValidateAssignment(const ast::AssignmentStatement* a); + bool ValidateCallStatement(ast::CallStatement* stmt); bool ValidateEntryPoint(const ast::Function* func, const FunctionInfo* info); bool ValidateFunction(const ast::Function* func, const FunctionInfo* info); bool ValidateGlobalVariable(const VariableInfo* var); diff --git a/src/resolver/resolver_test.cc b/src/resolver/resolver_test.cc index 1250deba81..96a72b2ff3 100644 --- a/src/resolver/resolver_test.cc +++ b/src/resolver/resolver_test.cc @@ -266,7 +266,7 @@ TEST_F(ResolverTest, Stmt_Switch) { TEST_F(ResolverTest, Stmt_Call) { ast::VariableList params; - Func("my_func", params, ty.f32(), {Return(0.0f)}, ast::DecorationList{}); + Func("my_func", params, ty.void_(), {Return()}, ast::DecorationList{}); auto* expr = Call("my_func"); @@ -276,7 +276,7 @@ TEST_F(ResolverTest, Stmt_Call) { EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(expr), nullptr); - EXPECT_TRUE(TypeOf(expr)->Is()); + EXPECT_TRUE(TypeOf(expr)->Is()); EXPECT_EQ(StmtOf(expr), call); } diff --git a/src/transform/first_index_offset_test.cc b/src/transform/first_index_offset_test.cc index b7ebab178c..65bd1315f6 100644 --- a/src/transform/first_index_offset_test.cc +++ b/src/transform/first_index_offset_test.cc @@ -53,7 +53,7 @@ fn test(vert_idx : u32) -> u32 { [[stage(vertex)]] fn entry([[builtin(vertex_index)]] vert_idx : u32) -> [[builtin(position)]] vec4 { - test(vert_idx); + ignore(test(vert_idx)); return vec4(); } )"; @@ -72,7 +72,7 @@ fn test(vert_idx : u32) -> u32 { [[stage(vertex)]] fn entry([[builtin(vertex_index)]] vert_idx : u32) -> [[builtin(position)]] vec4 { - test((vert_idx + tint_symbol_1.first_vertex_index)); + ignore(test((vert_idx + tint_symbol_1.first_vertex_index))); return vec4(); } )"; @@ -100,7 +100,7 @@ fn test(inst_idx : u32) -> u32 { [[stage(vertex)]] fn entry([[builtin(instance_index)]] inst_idx : u32) -> [[builtin(position)]] vec4 { - test(inst_idx); + ignore(test(inst_idx)); return vec4(); } )"; @@ -119,7 +119,7 @@ fn test(inst_idx : u32) -> u32 { [[stage(vertex)]] fn entry([[builtin(instance_index)]] inst_idx : u32) -> [[builtin(position)]] vec4 { - test((inst_idx + tint_symbol_1.first_instance_index)); + ignore(test((inst_idx + tint_symbol_1.first_instance_index))); return vec4(); } )"; @@ -152,7 +152,7 @@ struct Inputs { [[stage(vertex)]] fn entry(inputs : Inputs) -> [[builtin(position)]] vec4 { - test(inputs.instance_idx, inputs.vert_idx); + ignore(test(inputs.instance_idx, inputs.vert_idx)); return vec4(); } )"; @@ -179,7 +179,7 @@ struct Inputs { [[stage(vertex)]] fn entry(inputs : Inputs) -> [[builtin(position)]] vec4 { - test((inputs.instance_idx + tint_symbol_1.first_instance_index), (inputs.vert_idx + tint_symbol_1.first_vertex_index)); + ignore(test((inputs.instance_idx + tint_symbol_1.first_instance_index), (inputs.vert_idx + tint_symbol_1.first_vertex_index))); return vec4(); } )"; @@ -211,7 +211,7 @@ fn func2(vert_idx : u32) -> u32 { [[stage(vertex)]] fn entry([[builtin(vertex_index)]] vert_idx : u32) -> [[builtin(position)]] vec4 { - func2(vert_idx); + ignore(func2(vert_idx)); return vec4(); } )"; @@ -234,7 +234,7 @@ fn func2(vert_idx : u32) -> u32 { [[stage(vertex)]] fn entry([[builtin(vertex_index)]] vert_idx : u32) -> [[builtin(position)]] vec4 { - func2((vert_idx + tint_symbol_1.first_vertex_index)); + ignore(func2((vert_idx + tint_symbol_1.first_vertex_index))); return vec4(); } )"; @@ -262,19 +262,19 @@ fn func(i : u32) -> u32 { [[stage(vertex)]] fn entry_a([[builtin(vertex_index)]] vert_idx : u32) -> [[builtin(position)]] vec4 { - func(vert_idx); + ignore(func(vert_idx)); return vec4(); } [[stage(vertex)]] fn entry_b([[builtin(vertex_index)]] vert_idx : u32, [[builtin(instance_index)]] inst_idx : u32) -> [[builtin(position)]] vec4 { - func(vert_idx + inst_idx); + ignore(func(vert_idx + inst_idx)); return vec4(); } [[stage(vertex)]] fn entry_c([[builtin(instance_index)]] inst_idx : u32) -> [[builtin(position)]] vec4 { - func(inst_idx); + ignore(func(inst_idx)); return vec4(); } )"; @@ -294,19 +294,19 @@ fn func(i : u32) -> u32 { [[stage(vertex)]] fn entry_a([[builtin(vertex_index)]] vert_idx : u32) -> [[builtin(position)]] vec4 { - func((vert_idx + tint_symbol_1.first_vertex_index)); + ignore(func((vert_idx + tint_symbol_1.first_vertex_index))); return vec4(); } [[stage(vertex)]] fn entry_b([[builtin(vertex_index)]] vert_idx : u32, [[builtin(instance_index)]] inst_idx : u32) -> [[builtin(position)]] vec4 { - func(((vert_idx + tint_symbol_1.first_vertex_index) + (inst_idx + tint_symbol_1.first_instance_index))); + ignore(func(((vert_idx + tint_symbol_1.first_vertex_index) + (inst_idx + tint_symbol_1.first_instance_index)))); return vec4(); } [[stage(vertex)]] fn entry_c([[builtin(instance_index)]] inst_idx : u32) -> [[builtin(position)]] vec4 { - func((inst_idx + tint_symbol_1.first_instance_index)); + ignore(func((inst_idx + tint_symbol_1.first_instance_index))); return vec4(); } )"; @@ -338,7 +338,7 @@ fn test() -> u32 { [[stage(vertex)]] fn entry() { - test(); + ignore(test()); pos = vec4(); } )"; @@ -361,7 +361,7 @@ fn test() -> u32 { [[stage(vertex)]] fn entry() { - test(); + ignore(test()); pos = vec4(); } )"; @@ -393,7 +393,7 @@ fn test() -> u32 { [[stage(vertex)]] fn entry() { - test(); + ignore(test()); pos = vec4(); } )"; @@ -416,7 +416,7 @@ fn test() -> u32 { [[stage(vertex)]] fn entry() { - test(); + ignore(test()); pos = vec4(); } )"; @@ -448,7 +448,7 @@ fn test() -> u32 { [[stage(vertex)]] fn entry() { - test(); + ignore(test()); pos = vec4(); } )"; @@ -474,7 +474,7 @@ fn test() -> u32 { [[stage(vertex)]] fn entry() { - test(); + ignore(test()); pos = vec4(); } )"; @@ -509,7 +509,7 @@ fn func2() -> u32 { [[stage(vertex)]] fn entry() { - func2(); + ignore(func2()); pos = vec4(); } )"; @@ -536,7 +536,7 @@ fn func2() -> u32 { [[stage(vertex)]] fn entry() { - func2(); + ignore(func2()); pos = vec4(); } )"; diff --git a/src/transform/renamer_test.cc b/src/transform/renamer_test.cc index 5df98f91e4..f73708f58f 100644 --- a/src/transform/renamer_test.cc +++ b/src/transform/renamer_test.cc @@ -50,7 +50,7 @@ fn test() -> u32 { [[stage(vertex)]] fn entry() -> [[builtin(position)]] vec4 { - test(); + ignore(test()); return vec4(); } )"; @@ -64,7 +64,7 @@ fn tint_symbol_1() -> u32 { [[stage(vertex)]] fn tint_symbol_2() -> [[builtin(position)]] vec4 { - tint_symbol_1(); + ignore(tint_symbol_1()); return vec4(); } )"; diff --git a/src/writer/hlsl/generator_impl_intrinsic_test.cc b/src/writer/hlsl/generator_impl_intrinsic_test.cc index c3eb6d1b94..6c5290c7d7 100644 --- a/src/writer/hlsl/generator_impl_intrinsic_test.cc +++ b/src/writer/hlsl/generator_impl_intrinsic_test.cc @@ -166,7 +166,7 @@ TEST_P(HlslIntrinsicTest, Emit) { auto* call = GenerateCall(param.intrinsic, param.type, this); ASSERT_NE(nullptr, call) << "Unhandled intrinsic"; - Func("func", {}, ty.void_(), {create(call)}, + Func("func", {}, ty.void_(), {Ignore(call)}, {create(ast::PipelineStage::kFragment)}); GeneratorImpl& gen = Build(); @@ -327,7 +327,7 @@ TEST_F(HlslGeneratorImplTest_Intrinsic, Frexp_Scalar_i32) { float tint_tmp; float tint_tmp_1 = frexp(1.0f, tint_tmp); exp = int(tint_tmp); - (void) tint_tmp_1; + tint_tmp_1; )")); } @@ -343,7 +343,7 @@ TEST_F(HlslGeneratorImplTest_Intrinsic, Frexp_Vector_i32) { float3 tint_tmp; float3 tint_tmp_1 = frexp(float3(0.0f, 0.0f, 0.0f), tint_tmp); res = int3(tint_tmp); - (void) tint_tmp_1; + tint_tmp_1; )")); } diff --git a/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc b/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc index c705ccd395..7e50eacf0f 100644 --- a/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc +++ b/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc @@ -361,7 +361,7 @@ TEST_P(HlslGeneratorIntrinsicTextureTest, Call) { Func("main", ast::VariableList{}, ty.void_(), ast::StatementList{ - create(call), + Ignore(call), }, ast::DecorationList{ Stage(ast::PipelineStage::kFragment), diff --git a/src/writer/hlsl/generator_impl_type_test.cc b/src/writer/hlsl/generator_impl_type_test.cc index 24e004d5fe..3add6d98f3 100644 --- a/src/writer/hlsl/generator_impl_type_test.cc +++ b/src/writer/hlsl/generator_impl_type_test.cc @@ -360,8 +360,7 @@ TEST_P(HlslDepthTexturesTest, Emit) { create(2), }); - Func("main", {}, ty.void_(), - {create(Call("textureDimensions", "tex"))}, + Func("main", {}, ty.void_(), {Ignore(Call("textureDimensions", "tex"))}, {Stage(ast::PipelineStage::kFragment)}); GeneratorImpl& gen = Build(); @@ -419,8 +418,7 @@ TEST_P(HlslSampledTexturesTest, Emit) { create(2), }); - Func("main", {}, ty.void_(), - {create(Call("textureDimensions", "tex"))}, + Func("main", {}, ty.void_(), {Ignore(Call("textureDimensions", "tex"))}, {Stage(ast::PipelineStage::kFragment)}); GeneratorImpl& gen = Build(); @@ -562,8 +560,7 @@ TEST_P(HlslStorageTexturesTest, Emit) { create(2), }); - Func("main", {}, ty.void_(), - {create(Call("textureDimensions", "tex"))}, + Func("main", {}, ty.void_(), {Ignore(Call("textureDimensions", "tex"))}, {Stage(ast::PipelineStage::kFragment)}); GeneratorImpl& gen = Build(); diff --git a/src/writer/msl/generator_impl_intrinsic_test.cc b/src/writer/msl/generator_impl_intrinsic_test.cc index 0bcbebbc14..0e6c09fc96 100644 --- a/src/writer/msl/generator_impl_intrinsic_test.cc +++ b/src/writer/msl/generator_impl_intrinsic_test.cc @@ -180,7 +180,7 @@ TEST_P(MslIntrinsicTest, Emit) { auto* call = GenerateCall(param.intrinsic, param.type, this); ASSERT_NE(nullptr, call) << "Unhandled intrinsic"; - Func("func", {}, ty.void_(), {create(call)}, + Func("func", {}, ty.void_(), {Ignore(call)}, {create(ast::PipelineStage::kFragment)}); GeneratorImpl& gen = Build(); @@ -346,8 +346,7 @@ TEST_F(MslGeneratorImplTest, Ignore) { Func("f", {Param("a", ty.i32()), Param("b", ty.i32()), Param("c", ty.i32())}, ty.i32(), {Return(Mul(Add("a", "b"), "c"))}); - Func("func", {}, ty.void_(), - {create(Call("ignore", Call("f", 1, 2, 3)))}, + Func("func", {}, ty.void_(), {Ignore(Call("f", 1, 2, 3))}, {Stage(ast::PipelineStage::kCompute)}); GeneratorImpl& gen = Build(); diff --git a/src/writer/msl/generator_impl_intrinsic_texture_test.cc b/src/writer/msl/generator_impl_intrinsic_texture_test.cc index 8ffe66e3aa..ea047280a4 100644 --- a/src/writer/msl/generator_impl_intrinsic_texture_test.cc +++ b/src/writer/msl/generator_impl_intrinsic_texture_test.cc @@ -263,7 +263,7 @@ TEST_P(MslGeneratorIntrinsicTextureTest, Call) { Func("main", ast::VariableList{}, ty.void_(), ast::StatementList{ - create(call), + Ignore(call), }, ast::DecorationList{ Stage(ast::PipelineStage::kFragment), diff --git a/src/writer/spirv/builder_call_test.cc b/src/writer/spirv/builder_call_test.cc index 0bc6941fb0..7070d49cb7 100644 --- a/src/writer/spirv/builder_call_test.cc +++ b/src/writer/spirv/builder_call_test.cc @@ -82,7 +82,7 @@ TEST_F(BuilderTest, Statement_Call) { auto* func = Func("main", {}, ty.void_(), ast::StatementList{}, ast::DecorationList{}); - auto* expr = create(Call("a_func", 1.f, 1.f)); + auto* expr = Ignore(Call("a_func", 1.f, 1.f)); WrapInFunction(expr); @@ -100,7 +100,7 @@ OpName %10 "main" %1 = OpTypeFunction %2 %2 %2 %9 = OpTypeVoid %8 = OpTypeFunction %9 -%13 = OpConstant %2 1 +%14 = OpConstant %2 1 %3 = OpFunction %2 None %1 %4 = OpFunctionParameter %2 %5 = OpFunctionParameter %2 @@ -110,7 +110,7 @@ OpReturnValue %7 OpFunctionEnd %10 = OpFunction %9 None %8 %11 = OpLabel -%12 = OpFunctionCall %2 %3 %13 %13 +%13 = OpFunctionCall %2 %3 %14 %14 OpReturn OpFunctionEnd )"); diff --git a/src/writer/spirv/builder_intrinsic_test.cc b/src/writer/spirv/builder_intrinsic_test.cc index e78436dbe5..c68d77621e 100644 --- a/src/writer/spirv/builder_intrinsic_test.cc +++ b/src/writer/spirv/builder_intrinsic_test.cc @@ -414,7 +414,7 @@ TEST_P(IntrinsicDeriveTest, Call_Derivative_Scalar) { auto* var = Global("v", ty.f32(), ast::StorageClass::kPrivate); auto* expr = Call(param.name, "v"); - Func("func", {}, ty.void_(), {create(expr)}, + Func("func", {}, ty.void_(), {Ignore(expr)}, {create(ast::PipelineStage::kFragment)}); spirv::Builder& b = Build(); @@ -440,7 +440,7 @@ TEST_P(IntrinsicDeriveTest, Call_Derivative_Vector) { auto* var = Global("v", ty.vec3(), ast::StorageClass::kPrivate); auto* expr = Call(param.name, "v"); - Func("func", {}, ty.void_(), {create(expr)}, + Func("func", {}, ty.void_(), {Ignore(expr)}, {create(ast::PipelineStage::kFragment)}); spirv::Builder& b = Build(); @@ -538,8 +538,8 @@ TEST_F(IntrinsicBuilderTest, Call_TextureSampleCompare_Twice) { auto* expr2 = Call("textureSampleCompare", "texture", "sampler", vec2(1.0f, 2.0f), 2.0f); - Func("f1", {}, ty.void_(), {create(expr1)}, {}); - Func("f2", {}, ty.void_(), {create(expr2)}, {}); + Func("f1", {}, ty.void_(), {Ignore(expr1)}, {}); + Func("f2", {}, ty.void_(), {Ignore(expr2)}, {}); spirv::Builder& b = Build(); @@ -1414,7 +1414,7 @@ TEST_F(IntrinsicBuilderTest, Call_Modf) { Func("a_func", ast::VariableList{}, ty.void_(), ast::StatementList{ Decl(out), - create(expr), + Ignore(expr), }, ast::DecorationList{ Stage(ast::PipelineStage::kFragment), @@ -1425,7 +1425,7 @@ TEST_F(IntrinsicBuilderTest, Call_Modf) { ASSERT_TRUE(b.Build()) << b.error(); auto got = DumpBuilder(b); auto* expect = R"(OpCapability Shader -%11 = OpExtInstImport "GLSL.std.450" +%12 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %3 "a_func" OpExecutionMode %3 OriginUpperLeft @@ -1437,13 +1437,13 @@ OpName %5 "out" %7 = OpTypeVector %8 2 %6 = OpTypePointer Function %7 %9 = OpConstantNull %7 -%12 = OpConstant %8 1 -%13 = OpConstant %8 2 -%14 = OpConstantComposite %7 %12 %13 +%13 = OpConstant %8 1 +%14 = OpConstant %8 2 +%15 = OpConstantComposite %7 %13 %14 %3 = OpFunction %2 None %1 %4 = OpLabel %5 = OpVariable %6 Function %9 -%10 = OpExtInst %7 %11 Modf %14 %5 +%11 = OpExtInst %7 %12 Modf %15 %5 OpReturn OpFunctionEnd )"; @@ -1458,7 +1458,7 @@ TEST_F(IntrinsicBuilderTest, Call_Frexp) { Func("a_func", ast::VariableList{}, ty.void_(), ast::StatementList{ Decl(out), - create(expr), + Ignore(expr), }, ast::DecorationList{ Stage(ast::PipelineStage::kFragment), @@ -1469,7 +1469,7 @@ TEST_F(IntrinsicBuilderTest, Call_Frexp) { ASSERT_TRUE(b.Build()) << b.error(); auto got = DumpBuilder(b); auto* expect = R"(OpCapability Shader -%13 = OpExtInstImport "GLSL.std.450" +%14 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %3 "a_func" OpExecutionMode %3 OriginUpperLeft @@ -1481,15 +1481,15 @@ OpName %5 "out" %7 = OpTypeVector %8 2 %6 = OpTypePointer Function %7 %9 = OpConstantNull %7 -%12 = OpTypeFloat 32 -%11 = OpTypeVector %12 2 -%14 = OpConstant %12 1 -%15 = OpConstant %12 2 -%16 = OpConstantComposite %11 %14 %15 +%13 = OpTypeFloat 32 +%12 = OpTypeVector %13 2 +%15 = OpConstant %13 1 +%16 = OpConstant %13 2 +%17 = OpConstantComposite %12 %15 %16 %3 = OpFunction %2 None %1 %4 = OpLabel %5 = OpVariable %6 Function %9 -%10 = OpExtInst %11 %13 Frexp %16 %5 +%11 = OpExtInst %12 %14 Frexp %17 %5 OpReturn OpFunctionEnd )"; @@ -1585,7 +1585,7 @@ TEST_F(IntrinsicBuilderTest, Call_ArrayLength_DEPRECATED) { Func("a_func", ast::VariableList{}, ty.void_(), ast::StatementList{ - create(expr), + Ignore(expr), }, ast::DecorationList{ Stage(ast::PipelineStage::kFragment), @@ -1604,12 +1604,12 @@ TEST_F(IntrinsicBuilderTest, Call_ArrayLength_DEPRECATED) { %1 = OpVariable %2 StorageBuffer %7 = OpTypeVoid %6 = OpTypeFunction %7 -%11 = OpTypeInt 32 0 +%12 = OpTypeInt 32 0 )"; auto got_types = DumpInstructions(b.types()); EXPECT_EQ(expected_types, got_types); - auto* expected_instructions = R"(%10 = OpArrayLength %11 %1 0 + auto* expected_instructions = R"(%11 = OpArrayLength %12 %1 0 )"; auto got_instructions = DumpInstructions(b.functions()[0].instructions()); EXPECT_EQ(expected_instructions, got_instructions); @@ -1635,7 +1635,7 @@ TEST_F(IntrinsicBuilderTest, Call_ArrayLength_OtherMembersInStruct_DEPRECATED) { Func("a_func", ast::VariableList{}, ty.void_(), ast::StatementList{ - create(expr), + Ignore(expr), }, ast::DecorationList{ Stage(ast::PipelineStage::kFragment), @@ -1654,12 +1654,12 @@ TEST_F(IntrinsicBuilderTest, Call_ArrayLength_OtherMembersInStruct_DEPRECATED) { %1 = OpVariable %2 StorageBuffer %7 = OpTypeVoid %6 = OpTypeFunction %7 -%11 = OpTypeInt 32 0 +%12 = OpTypeInt 32 0 )"; auto got_types = DumpInstructions(b.types()); EXPECT_EQ(expected_types, got_types); - auto* expected_instructions = R"(%10 = OpArrayLength %11 %1 1 + auto* expected_instructions = R"(%11 = OpArrayLength %12 %1 1 )"; auto got_instructions = DumpInstructions(b.functions()[0].instructions()); EXPECT_EQ(expected_instructions, got_instructions); @@ -1680,7 +1680,7 @@ TEST_F(IntrinsicBuilderTest, Call_ArrayLength) { Func("a_func", ast::VariableList{}, ty.void_(), ast::StatementList{ - create(expr), + Ignore(expr), }, ast::DecorationList{ Stage(ast::PipelineStage::kFragment), @@ -1699,12 +1699,12 @@ TEST_F(IntrinsicBuilderTest, Call_ArrayLength) { %1 = OpVariable %2 StorageBuffer %7 = OpTypeVoid %6 = OpTypeFunction %7 -%11 = OpTypeInt 32 0 +%12 = OpTypeInt 32 0 )"; auto got_types = DumpInstructions(b.types()); EXPECT_EQ(expected_types, got_types); - auto* expected_instructions = R"(%10 = OpArrayLength %11 %1 0 + auto* expected_instructions = R"(%11 = OpArrayLength %12 %1 0 )"; auto got_instructions = DumpInstructions(b.functions()[0].instructions()); EXPECT_EQ(expected_instructions, got_instructions); @@ -1729,7 +1729,7 @@ TEST_F(IntrinsicBuilderTest, Call_ArrayLength_OtherMembersInStruct) { Func("a_func", ast::VariableList{}, ty.void_(), ast::StatementList{ - create(expr), + Ignore(expr), }, ast::DecorationList{ Stage(ast::PipelineStage::kFragment), @@ -1748,12 +1748,12 @@ TEST_F(IntrinsicBuilderTest, Call_ArrayLength_OtherMembersInStruct) { %1 = OpVariable %2 StorageBuffer %7 = OpTypeVoid %6 = OpTypeFunction %7 -%11 = OpTypeInt 32 0 +%12 = OpTypeInt 32 0 )"; auto got_types = DumpInstructions(b.types()); EXPECT_EQ(expected_types, got_types); - auto* expected_instructions = R"(%10 = OpArrayLength %11 %1 1 + auto* expected_instructions = R"(%11 = OpArrayLength %12 %1 1 )"; auto got_instructions = DumpInstructions(b.functions()[0].instructions()); EXPECT_EQ(expected_instructions, got_instructions); @@ -1778,7 +1778,7 @@ TEST_F(IntrinsicBuilderTest, Call_ArrayLength_ViaLets) { ast::StatementList{ Decl(p), Decl(p2), - create(expr), + Ignore(expr), }, ast::DecorationList{ Stage(ast::PipelineStage::kFragment), @@ -1797,12 +1797,12 @@ TEST_F(IntrinsicBuilderTest, Call_ArrayLength_ViaLets) { %1 = OpVariable %2 StorageBuffer %7 = OpTypeVoid %6 = OpTypeFunction %7 -%11 = OpTypeInt 32 0 +%12 = OpTypeInt 32 0 )"; auto got_types = DumpInstructions(b.types()); EXPECT_EQ(expected_types, got_types); - auto* expected_instructions = R"(%10 = OpArrayLength %11 %1 0 + auto* expected_instructions = R"(%11 = OpArrayLength %12 %1 0 )"; auto got_instructions = DumpInstructions(b.functions()[0].instructions()); EXPECT_EQ(expected_instructions, got_instructions); @@ -1839,7 +1839,7 @@ TEST_F(IntrinsicBuilderTest, Call_ArrayLength_ViaLets_WithPtrNoise) { Decl(p), Decl(p2), Decl(p3), - create(expr), + Ignore(expr), }, ast::DecorationList{ Stage(ast::PipelineStage::kFragment), @@ -1858,12 +1858,12 @@ TEST_F(IntrinsicBuilderTest, Call_ArrayLength_ViaLets_WithPtrNoise) { %1 = OpVariable %2 StorageBuffer %7 = OpTypeVoid %6 = OpTypeFunction %7 -%11 = OpTypeInt 32 0 +%12 = OpTypeInt 32 0 )"; auto got_types = DumpInstructions(b.types()); EXPECT_EQ(expected_types, got_types); - auto* expected_instructions = R"(%10 = OpArrayLength %11 %1 0 + auto* expected_instructions = R"(%11 = OpArrayLength %12 %1 0 )"; auto got_instructions = DumpInstructions(b.functions()[0].instructions()); EXPECT_EQ(expected_instructions, got_instructions); diff --git a/src/writer/spirv/builder_intrinsic_texture_test.cc b/src/writer/spirv/builder_intrinsic_texture_test.cc index 78795a27ef..a22a0d5f05 100644 --- a/src/writer/spirv/builder_intrinsic_texture_test.cc +++ b/src/writer/spirv/builder_intrinsic_texture_test.cc @@ -3460,10 +3460,9 @@ TEST_P(IntrinsicTextureTest, Call) { auto* texture = param.buildTextureVariable(this); auto* sampler = param.buildSamplerVariable(this); - auto* call = - create(Expr(param.function), param.args(this)); + auto* call = Call(param.function, param.args(this)); - Func("func", {}, ty.void_(), {create(call)}, + Func("func", {}, ty.void_(), {Ignore(call)}, {create(ast::PipelineStage::kFragment)}); spirv::Builder& b = Build(); @@ -3488,12 +3487,11 @@ TEST_P(IntrinsicTextureTest, ValidateSPIRV) { param.buildTextureVariable(this); param.buildSamplerVariable(this); - auto* call = - create(Expr(param.function), param.args(this)); + auto* call = Call(param.function, param.args(this)); Func("main", ast::VariableList{}, ty.void_(), ast::StatementList{ - create(call), + Ignore(call), }, ast::DecorationList{ Stage(ast::PipelineStage::kFragment), @@ -3515,9 +3513,8 @@ TEST_P(IntrinsicTextureTest, OutsideFunction_IsError) { auto* texture = param.buildTextureVariable(this); auto* sampler = param.buildSamplerVariable(this); - auto* call = - create(Expr(param.function), param.args(this)); - Func("func", {}, ty.void_(), {create(call)}, + auto* call = Call(param.function, param.args(this)); + Func("func", {}, ty.void_(), {Ignore(call)}, {create(ast::PipelineStage::kFragment)}); spirv::Builder& b = Build(); diff --git a/test/BUILD.gn b/test/BUILD.gn index 9c503cd4a8..32c390ce2f 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -226,6 +226,7 @@ tint_unittests_source_set("tint_unittests_core_src") { "../src/resolver/block_test.cc", "../src/resolver/builtins_validation_test.cc", "../src/resolver/call_test.cc", + "../src/resolver/call_validation_test.cc", "../src/resolver/control_block_validation_test.cc", "../src/resolver/decoration_validation_test.cc", "../src/resolver/entry_point_validation_test.cc",