resolver: Migrate validation to behavior analysis
Migrate some of the validation logic over to use the results of behavior analysis. The most significant changes are: * Unreachable-statements now consider merge-points of control flow. For example, if all branches of a if-statement or switch-statement either return or discard, the next statement will be considered unreachable. * Unreachable statements are no longer an error, but a warning. See https://github.com/gpuweb/gpuweb/issues/2378. * Statements that follow a loops that does not break, or have a conditional will now be considered unreachable. * Unreachable statements produced by the SPIR-V reader are now removed using the new RemoveUnreachableStatements transform. Some other new changes include additional validation for the continuing block for for-loops, to match the rules of a loop continuing block. The new cases this validation is testing for are not expressible in WGSL, but some transforms may produce complex continuing statements that might violate these rules. All the writers are able to decay these complex for-loop continuing statements to regular loops. Bug: tint:1302 Change-Id: I0d8a48c73d5d5c30a1cddf92cc3383a692a58e61 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/71500 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
parent
c270322884
commit
ea3eee9885
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
* Taking the address of a vector component is no longer allowed.
|
* Taking the address of a vector component is no longer allowed.
|
||||||
* Module-scope declarations can no longer alias a builtin name. [tint:1318](https://crbug.com/tint/1318)
|
* Module-scope declarations can no longer alias a builtin name. [tint:1318](https://crbug.com/tint/1318)
|
||||||
|
* It is now an error to call a function either directly or transitively, from a loop continuing block, that uses `discard`. [tint:1302](https://crbug.com/tint/1302)
|
||||||
|
|
||||||
### Deprecated Features
|
### Deprecated Features
|
||||||
|
|
||||||
|
@ -16,6 +17,8 @@
|
||||||
* Shadowing is now fully supported. [tint:819](https://crbug.com/tint/819)
|
* Shadowing is now fully supported. [tint:819](https://crbug.com/tint/819)
|
||||||
* The `dot()` builtin now supports integer vector types.
|
* The `dot()` builtin now supports integer vector types.
|
||||||
* Identifiers can now start with a single leading underscore. [tint:1292](https://crbug.com/tint/1292)
|
* Identifiers can now start with a single leading underscore. [tint:1292](https://crbug.com/tint/1292)
|
||||||
|
* Control flow analysis has been improved, and functions no longer need to `return` if the statement is unreachable. [tint:1302](https://crbug.com/tint/1302)
|
||||||
|
* Unreachable statements now produce a warning instead of an error, to allow WGSL code to be updated to the new analysis behavior. These warnings may become errors in the future [gpuweb#2378](https://github.com/gpuweb/gpuweb/issues/2378)
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
|
|
|
@ -465,6 +465,8 @@ libtint_source_set("libtint_core_all_src") {
|
||||||
"transform/promote_initializers_to_const_var.h",
|
"transform/promote_initializers_to_const_var.h",
|
||||||
"transform/remove_phonies.cc",
|
"transform/remove_phonies.cc",
|
||||||
"transform/remove_phonies.h",
|
"transform/remove_phonies.h",
|
||||||
|
"transform/remove_unreachable_statements.cc",
|
||||||
|
"transform/remove_unreachable_statements.h",
|
||||||
"transform/renamer.cc",
|
"transform/renamer.cc",
|
||||||
"transform/renamer.h",
|
"transform/renamer.h",
|
||||||
"transform/robustness.cc",
|
"transform/robustness.cc",
|
||||||
|
|
|
@ -330,6 +330,8 @@ set(TINT_LIB_SRCS
|
||||||
transform/promote_initializers_to_const_var.h
|
transform/promote_initializers_to_const_var.h
|
||||||
transform/remove_phonies.cc
|
transform/remove_phonies.cc
|
||||||
transform/remove_phonies.h
|
transform/remove_phonies.h
|
||||||
|
transform/remove_unreachable_statements.cc
|
||||||
|
transform/remove_unreachable_statements.h
|
||||||
transform/renamer.cc
|
transform/renamer.cc
|
||||||
transform/renamer.h
|
transform/renamer.h
|
||||||
transform/robustness.cc
|
transform/robustness.cc
|
||||||
|
@ -969,6 +971,7 @@ if(${TINT_BUILD_TESTS})
|
||||||
transform/pad_array_elements_test.cc
|
transform/pad_array_elements_test.cc
|
||||||
transform/promote_initializers_to_const_var_test.cc
|
transform/promote_initializers_to_const_var_test.cc
|
||||||
transform/remove_phonies_test.cc
|
transform/remove_phonies_test.cc
|
||||||
|
transform/remove_unreachable_statements_test.cc
|
||||||
transform/renamer_test.cc
|
transform/renamer_test.cc
|
||||||
transform/robustness_test.cc
|
transform/robustness_test.cc
|
||||||
transform/simplify_pointers_test.cc
|
transform/simplify_pointers_test.cc
|
||||||
|
|
|
@ -76,7 +76,7 @@ fn f1(p0 : f32, p1 : i32) -> f32 {
|
||||||
loop {
|
loop {
|
||||||
l0 = (p1 + 2);
|
l0 = (p1 + 2);
|
||||||
if (((l0 % 4) == 0)) {
|
if (((l0 % 4) == 0)) {
|
||||||
continue;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
continuing {
|
continuing {
|
||||||
|
|
|
@ -2104,6 +2104,18 @@ class ProgramBuilder {
|
||||||
Expr(std::forward<RhsExpressionInit>(rhs)));
|
Expr(std::forward<RhsExpressionInit>(rhs)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a ast::LoopStatement with input body and optional continuing
|
||||||
|
/// @param source the source information
|
||||||
|
/// @param body the loop body
|
||||||
|
/// @param continuing the optional continuing block
|
||||||
|
/// @returns the loop statement pointer
|
||||||
|
const ast::LoopStatement* Loop(
|
||||||
|
const Source& source,
|
||||||
|
const ast::BlockStatement* body,
|
||||||
|
const ast::BlockStatement* continuing = nullptr) {
|
||||||
|
return create<ast::LoopStatement>(source, body, continuing);
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a ast::LoopStatement with input body and optional continuing
|
/// Creates a ast::LoopStatement with input body and optional continuing
|
||||||
/// @param body the loop body
|
/// @param body the loop body
|
||||||
/// @param continuing the optional continuing block
|
/// @param continuing the optional continuing block
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "src/reader/spirv/parser_impl.h"
|
#include "src/reader/spirv/parser_impl.h"
|
||||||
#include "src/transform/decompose_strided_matrix.h"
|
#include "src/transform/decompose_strided_matrix.h"
|
||||||
#include "src/transform/manager.h"
|
#include "src/transform/manager.h"
|
||||||
|
#include "src/transform/remove_unreachable_statements.h"
|
||||||
#include "src/transform/simplify_pointers.h"
|
#include "src/transform/simplify_pointers.h"
|
||||||
#include "src/transform/unshadow.h"
|
#include "src/transform/unshadow.h"
|
||||||
|
|
||||||
|
@ -56,6 +57,7 @@ Program Parse(const std::vector<uint32_t>& input) {
|
||||||
manager.Add<transform::Unshadow>();
|
manager.Add<transform::Unshadow>();
|
||||||
manager.Add<transform::SimplifyPointers>();
|
manager.Add<transform::SimplifyPointers>();
|
||||||
manager.Add<transform::DecomposeStridedMatrix>();
|
manager.Add<transform::DecomposeStridedMatrix>();
|
||||||
|
manager.Add<transform::RemoveUnreachableStatements>();
|
||||||
return manager.Run(&program).program;
|
return manager.Run(&program).program;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ TEST_F(ResolverControlBlockValidationTest,
|
||||||
// switch (a) {
|
// switch (a) {
|
||||||
// default: {}
|
// default: {}
|
||||||
// }
|
// }
|
||||||
auto* var = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(3.14f));
|
auto* var = Var("a", ty.f32(), Expr(3.14f));
|
||||||
|
|
||||||
auto* block = Block(Decl(var), Switch(Expr(Source{{12, 34}}, "a"), //
|
auto* block = Block(Decl(var), Switch(Expr(Source{{12, 34}}, "a"), //
|
||||||
DefaultCase()));
|
DefaultCase()));
|
||||||
|
@ -48,7 +48,7 @@ TEST_F(ResolverControlBlockValidationTest, SwitchWithoutDefault_Fail) {
|
||||||
// switch (a) {
|
// switch (a) {
|
||||||
// case 1: {}
|
// case 1: {}
|
||||||
// }
|
// }
|
||||||
auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
|
auto* var = Var("a", ty.i32(), Expr(2));
|
||||||
|
|
||||||
auto* block = Block(Decl(var), //
|
auto* block = Block(Decl(var), //
|
||||||
Switch(Source{{12, 34}}, "a", //
|
Switch(Source{{12, 34}}, "a", //
|
||||||
|
@ -68,7 +68,7 @@ TEST_F(ResolverControlBlockValidationTest, SwitchWithTwoDefault_Fail) {
|
||||||
// case 1: {}
|
// case 1: {}
|
||||||
// default: {}
|
// default: {}
|
||||||
// }
|
// }
|
||||||
auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
|
auto* var = Var("a", ty.i32(), Expr(2));
|
||||||
|
|
||||||
auto* block = Block(Decl(var), //
|
auto* block = Block(Decl(var), //
|
||||||
Switch("a", //
|
Switch("a", //
|
||||||
|
@ -90,12 +90,16 @@ TEST_F(ResolverControlBlockValidationTest, UnreachableCode_Loop_continue) {
|
||||||
// continue;
|
// continue;
|
||||||
// z = 1;
|
// z = 1;
|
||||||
// }
|
// }
|
||||||
WrapInFunction(Loop(Block(Decl(Var("z", ty.i32(), ast::StorageClass::kNone)),
|
auto* decl_z = Decl(Var("z", ty.i32()));
|
||||||
create<ast::ContinueStatement>(),
|
auto* cont = Continue();
|
||||||
Assign(Source{{12, 34}}, "z", 1))));
|
auto* assign_z = Assign(Source{{12, 34}}, "z", 1);
|
||||||
|
WrapInFunction(Loop(Block(decl_z, cont, assign_z)));
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve()) << r()->error();
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: code is unreachable");
|
EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
|
||||||
|
EXPECT_TRUE(Sem().Get(decl_z)->IsReachable());
|
||||||
|
EXPECT_TRUE(Sem().Get(cont)->IsReachable());
|
||||||
|
EXPECT_FALSE(Sem().Get(assign_z)->IsReachable());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverControlBlockValidationTest,
|
TEST_F(ResolverControlBlockValidationTest,
|
||||||
|
@ -105,13 +109,16 @@ TEST_F(ResolverControlBlockValidationTest,
|
||||||
// {{{continue;}}}
|
// {{{continue;}}}
|
||||||
// z = 1;
|
// z = 1;
|
||||||
// }
|
// }
|
||||||
WrapInFunction(
|
auto* decl_z = Decl(Var("z", ty.i32()));
|
||||||
Loop(Block(Decl(Var("z", ty.i32(), ast::StorageClass::kNone)),
|
auto* cont = Continue();
|
||||||
Block(Block(Block(create<ast::ContinueStatement>()))),
|
auto* assign_z = Assign(Source{{12, 34}}, "z", 1);
|
||||||
Assign(Source{{12, 34}}, "z", 1))));
|
WrapInFunction(Loop(Block(decl_z, Block(Block(Block(cont))), assign_z)));
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve()) << r()->error();
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: code is unreachable");
|
EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
|
||||||
|
EXPECT_TRUE(Sem().Get(decl_z)->IsReachable());
|
||||||
|
EXPECT_TRUE(Sem().Get(cont)->IsReachable());
|
||||||
|
EXPECT_FALSE(Sem().Get(assign_z)->IsReachable());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverControlBlockValidationTest, UnreachableCode_ForLoop_continue) {
|
TEST_F(ResolverControlBlockValidationTest, UnreachableCode_ForLoop_continue) {
|
||||||
|
@ -120,14 +127,17 @@ TEST_F(ResolverControlBlockValidationTest, UnreachableCode_ForLoop_continue) {
|
||||||
// continue;
|
// continue;
|
||||||
// z = 1;
|
// z = 1;
|
||||||
// }
|
// }
|
||||||
WrapInFunction(
|
auto* decl_z = Decl(Var("z", ty.i32()));
|
||||||
For(nullptr, nullptr, nullptr,
|
auto* cont = Continue();
|
||||||
Block(create<ast::ContinueStatement>(),
|
auto* assign_z = Assign(Source{{12, 34}}, "z", 1);
|
||||||
Decl(Source{{12, 34}},
|
WrapInFunction(For(nullptr, nullptr, nullptr, //
|
||||||
Var("z", ty.i32(), ast::StorageClass::kNone)))));
|
Block(decl_z, cont, assign_z)));
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve()) << r()->error();
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: code is unreachable");
|
EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
|
||||||
|
EXPECT_TRUE(Sem().Get(decl_z)->IsReachable());
|
||||||
|
EXPECT_TRUE(Sem().Get(cont)->IsReachable());
|
||||||
|
EXPECT_FALSE(Sem().Get(assign_z)->IsReachable());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverControlBlockValidationTest,
|
TEST_F(ResolverControlBlockValidationTest,
|
||||||
|
@ -137,31 +147,40 @@ TEST_F(ResolverControlBlockValidationTest,
|
||||||
// {{{continue;}}}
|
// {{{continue;}}}
|
||||||
// z = 1;
|
// z = 1;
|
||||||
// }
|
// }
|
||||||
WrapInFunction(
|
auto* decl_z = Decl(Var("z", ty.i32()));
|
||||||
For(nullptr, nullptr, nullptr,
|
auto* cont = Continue();
|
||||||
Block(Decl(Var("z", ty.i32(), ast::StorageClass::kNone)),
|
auto* assign_z = Assign(Source{{12, 34}}, "z", 1);
|
||||||
Block(Block(Block(create<ast::ContinueStatement>()))),
|
WrapInFunction(For(nullptr, nullptr, nullptr,
|
||||||
Assign(Source{{12, 34}}, "z", 1))));
|
Block(decl_z, Block(Block(Block(cont))), assign_z)));
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve()) << r()->error();
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: code is unreachable");
|
EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
|
||||||
|
EXPECT_TRUE(Sem().Get(decl_z)->IsReachable());
|
||||||
|
EXPECT_TRUE(Sem().Get(cont)->IsReachable());
|
||||||
|
EXPECT_FALSE(Sem().Get(assign_z)->IsReachable());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverControlBlockValidationTest, UnreachableCode_break) {
|
TEST_F(ResolverControlBlockValidationTest, UnreachableCode_break) {
|
||||||
// switch (1) {
|
// switch (1) {
|
||||||
// case 1: { break; var a : u32 = 2;}
|
// case 1: {
|
||||||
|
// var z: i32;
|
||||||
|
// break;
|
||||||
|
// z = 1;
|
||||||
// default: {}
|
// default: {}
|
||||||
// }
|
// }
|
||||||
auto* decl = Decl(Source{{12, 34}},
|
auto* decl_z = Decl(Var("z", ty.i32()));
|
||||||
Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2)));
|
auto* brk = Break();
|
||||||
|
auto* assign_z = Assign(Source{{12, 34}}, "z", 1);
|
||||||
WrapInFunction( //
|
WrapInFunction( //
|
||||||
Loop(Block(Switch(1, //
|
Loop(Block(Switch(1, //
|
||||||
Case(Expr(1), Block(Break(), decl)), //
|
Case(Expr(1), Block(decl_z, brk, assign_z)), //
|
||||||
DefaultCase()))));
|
DefaultCase()))));
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: code is unreachable");
|
EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
|
||||||
|
EXPECT_TRUE(Sem().Get(decl_z)->IsReachable());
|
||||||
|
EXPECT_TRUE(Sem().Get(brk)->IsReachable());
|
||||||
|
EXPECT_FALSE(Sem().Get(assign_z)->IsReachable());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverControlBlockValidationTest, UnreachableCode_break_InBlocks) {
|
TEST_F(ResolverControlBlockValidationTest, UnreachableCode_break_InBlocks) {
|
||||||
|
@ -171,16 +190,19 @@ TEST_F(ResolverControlBlockValidationTest, UnreachableCode_break_InBlocks) {
|
||||||
// default: {}
|
// default: {}
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
auto* decl = Decl(Source{{12, 34}},
|
auto* decl_z = Decl(Var("z", ty.i32()));
|
||||||
Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2)));
|
auto* brk = Break();
|
||||||
|
auto* assign_z = Assign(Source{{12, 34}}, "z", 1);
|
||||||
|
WrapInFunction(Loop(Block(
|
||||||
|
Switch(1, //
|
||||||
|
Case(Expr(1), Block(decl_z, Block(Block(Block(brk))), assign_z)),
|
||||||
|
DefaultCase()))));
|
||||||
|
|
||||||
WrapInFunction(Loop(
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
Block(Switch(1, //
|
EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
|
||||||
Case(Expr(1), Block(Block(Block(Block(Break()))), decl)),
|
EXPECT_TRUE(Sem().Get(decl_z)->IsReachable());
|
||||||
DefaultCase()))));
|
EXPECT_TRUE(Sem().Get(brk)->IsReachable());
|
||||||
|
EXPECT_FALSE(Sem().Get(assign_z)->IsReachable());
|
||||||
EXPECT_FALSE(r()->Resolve());
|
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: code is unreachable");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverControlBlockValidationTest,
|
TEST_F(ResolverControlBlockValidationTest,
|
||||||
|
@ -190,7 +212,7 @@ TEST_F(ResolverControlBlockValidationTest,
|
||||||
// case 1: {}
|
// case 1: {}
|
||||||
// default: {}
|
// default: {}
|
||||||
// }
|
// }
|
||||||
auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
|
auto* var = Var("a", ty.i32(), Expr(2));
|
||||||
|
|
||||||
auto* block = Block(Decl(var), Switch("a", //
|
auto* block = Block(Decl(var), Switch("a", //
|
||||||
Case(Source{{12, 34}}, {Expr(1u)}), //
|
Case(Source{{12, 34}}, {Expr(1u)}), //
|
||||||
|
@ -210,7 +232,7 @@ TEST_F(ResolverControlBlockValidationTest,
|
||||||
// case -1: {}
|
// case -1: {}
|
||||||
// default: {}
|
// default: {}
|
||||||
// }
|
// }
|
||||||
auto* var = Var("a", ty.u32(), ast::StorageClass::kNone, Expr(2u));
|
auto* var = Var("a", ty.u32(), Expr(2u));
|
||||||
|
|
||||||
auto* block = Block(Decl(var), //
|
auto* block = Block(Decl(var), //
|
||||||
Switch("a", //
|
Switch("a", //
|
||||||
|
@ -232,7 +254,7 @@ TEST_F(ResolverControlBlockValidationTest,
|
||||||
// case 2u, 3u, 2u: {}
|
// case 2u, 3u, 2u: {}
|
||||||
// default: {}
|
// default: {}
|
||||||
// }
|
// }
|
||||||
auto* var = Var("a", ty.u32(), ast::StorageClass::kNone, Expr(3u));
|
auto* var = Var("a", ty.u32(), Expr(3u));
|
||||||
|
|
||||||
auto* block = Block(Decl(var), //
|
auto* block = Block(Decl(var), //
|
||||||
Switch("a", //
|
Switch("a", //
|
||||||
|
@ -259,7 +281,7 @@ TEST_F(ResolverControlBlockValidationTest,
|
||||||
// case 0,1,2,-10: {}
|
// case 0,1,2,-10: {}
|
||||||
// default: {}
|
// default: {}
|
||||||
// }
|
// }
|
||||||
auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
|
auto* var = Var("a", ty.i32(), Expr(2));
|
||||||
|
|
||||||
auto* block = Block(Decl(var), //
|
auto* block = Block(Decl(var), //
|
||||||
Switch("a", //
|
Switch("a", //
|
||||||
|
@ -285,7 +307,7 @@ TEST_F(ResolverControlBlockValidationTest,
|
||||||
// switch (a) {
|
// switch (a) {
|
||||||
// default: { fallthrough; }
|
// default: { fallthrough; }
|
||||||
// }
|
// }
|
||||||
auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
|
auto* var = Var("a", ty.i32(), Expr(2));
|
||||||
auto* fallthrough = create<ast::FallthroughStatement>(Source{{12, 34}});
|
auto* fallthrough = create<ast::FallthroughStatement>(Source{{12, 34}});
|
||||||
auto* block = Block(Decl(var), //
|
auto* block = Block(Decl(var), //
|
||||||
Switch("a", //
|
Switch("a", //
|
||||||
|
@ -304,7 +326,7 @@ TEST_F(ResolverControlBlockValidationTest, SwitchCase_Pass) {
|
||||||
// default: {}
|
// default: {}
|
||||||
// case 5: {}
|
// case 5: {}
|
||||||
// }
|
// }
|
||||||
auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
|
auto* var = Var("a", ty.i32(), Expr(2));
|
||||||
|
|
||||||
auto* block = Block(Decl(var), //
|
auto* block = Block(Decl(var), //
|
||||||
Switch("a", //
|
Switch("a", //
|
||||||
|
@ -323,7 +345,7 @@ TEST_F(ResolverControlBlockValidationTest, SwitchCaseAlias_Pass) {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
auto* my_int = Alias("MyInt", ty.u32());
|
auto* my_int = Alias("MyInt", ty.u32());
|
||||||
auto* var = Var("a", ty.Of(my_int), ast::StorageClass::kNone, Expr(2u));
|
auto* var = Var("a", ty.Of(my_int), Expr(2u));
|
||||||
auto* block = Block(Decl(var), //
|
auto* block = Block(Decl(var), //
|
||||||
Switch("a", DefaultCase(Source{{12, 34}})));
|
Switch("a", DefaultCase(Source{{12, 34}})));
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ TEST_F(ResolverFunctionValidationTest, NestedLocalMayShadowParameter) {
|
||||||
TEST_F(ResolverFunctionValidationTest,
|
TEST_F(ResolverFunctionValidationTest,
|
||||||
VoidFunctionEndWithoutReturnStatement_Pass) {
|
VoidFunctionEndWithoutReturnStatement_Pass) {
|
||||||
// fn func { var a:i32 = 2; }
|
// fn func { var a:i32 = 2; }
|
||||||
auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
|
auto* var = Var("a", ty.i32(), Expr(2));
|
||||||
|
|
||||||
Func(Source{{12, 34}}, "func", ast::VariableList{}, ty.void_(),
|
Func(Source{{12, 34}}, "func", ast::VariableList{}, ty.void_(),
|
||||||
ast::StatementList{
|
ast::StatementList{
|
||||||
|
@ -87,7 +87,7 @@ TEST_F(ResolverFunctionValidationTest, FunctionUsingSameVariableName_Pass) {
|
||||||
// return func;
|
// return func;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
auto* var = Var("func", ty.i32(), ast::StorageClass::kNone, Expr(0));
|
auto* var = Var("func", ty.i32(), Expr(0));
|
||||||
Func("func", ast::VariableList{}, ty.i32(),
|
Func("func", ast::VariableList{}, ty.i32(),
|
||||||
ast::StatementList{
|
ast::StatementList{
|
||||||
Decl(var),
|
Decl(var),
|
||||||
|
@ -103,7 +103,7 @@ TEST_F(ResolverFunctionValidationTest,
|
||||||
// fn a() -> void { var b:i32 = 0; }
|
// fn a() -> void { var b:i32 = 0; }
|
||||||
// fn b() -> i32 { return 2; }
|
// fn b() -> i32 { return 2; }
|
||||||
|
|
||||||
auto* var = Var("b", ty.i32(), ast::StorageClass::kNone, Expr(0));
|
auto* var = Var("b", ty.i32(), Expr(0));
|
||||||
Func("a", ast::VariableList{}, ty.void_(),
|
Func("a", ast::VariableList{}, ty.void_(),
|
||||||
ast::StatementList{
|
ast::StatementList{
|
||||||
Decl(var),
|
Decl(var),
|
||||||
|
@ -126,14 +126,18 @@ TEST_F(ResolverFunctionValidationTest, UnreachableCode_return) {
|
||||||
// a = 2;
|
// a = 2;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
Func("func", ast::VariableList{}, ty.void_(),
|
auto* decl_a = Decl(Var("a", ty.i32()));
|
||||||
{
|
auto* ret = Return();
|
||||||
Decl(Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2))),
|
auto* assign_a = Assign(Source{{12, 34}}, "a", 2);
|
||||||
Return(),
|
|
||||||
Assign(Source{{12, 34}}, "a", 2),
|
Func("func", ast::VariableList{}, ty.void_(), {decl_a, ret, assign_a});
|
||||||
});
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
ASSERT_TRUE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: code is unreachable");
|
|
||||||
|
EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
|
||||||
|
EXPECT_TRUE(Sem().Get(decl_a)->IsReachable());
|
||||||
|
EXPECT_TRUE(Sem().Get(ret)->IsReachable());
|
||||||
|
EXPECT_FALSE(Sem().Get(assign_a)->IsReachable());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverFunctionValidationTest, UnreachableCode_return_InBlocks) {
|
TEST_F(ResolverFunctionValidationTest, UnreachableCode_return_InBlocks) {
|
||||||
|
@ -143,14 +147,18 @@ TEST_F(ResolverFunctionValidationTest, UnreachableCode_return_InBlocks) {
|
||||||
// a = 2;
|
// a = 2;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
auto* decl_a = Decl(Var("a", ty.i32()));
|
||||||
|
auto* ret = Return();
|
||||||
|
auto* assign_a = Assign(Source{{12, 34}}, "a", 2);
|
||||||
|
|
||||||
Func("func", ast::VariableList{}, ty.void_(),
|
Func("func", ast::VariableList{}, ty.void_(),
|
||||||
{
|
{decl_a, Block(Block(Block(ret))), assign_a});
|
||||||
Decl(Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2))),
|
|
||||||
Block(Block(Block(Return()))),
|
ASSERT_TRUE(r()->Resolve());
|
||||||
Assign(Source{{12, 34}}, "a", 2),
|
EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
|
||||||
});
|
EXPECT_TRUE(Sem().Get(decl_a)->IsReachable());
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_TRUE(Sem().Get(ret)->IsReachable());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: code is unreachable");
|
EXPECT_FALSE(Sem().Get(assign_a)->IsReachable());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverFunctionValidationTest, UnreachableCode_discard) {
|
TEST_F(ResolverFunctionValidationTest, UnreachableCode_discard) {
|
||||||
|
@ -160,14 +168,17 @@ TEST_F(ResolverFunctionValidationTest, UnreachableCode_discard) {
|
||||||
// a = 2;
|
// a = 2;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
Func("func", ast::VariableList{}, ty.void_(),
|
auto* decl_a = Decl(Var("a", ty.i32()));
|
||||||
{
|
auto* discard = Discard();
|
||||||
Decl(Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2))),
|
auto* assign_a = Assign(Source{{12, 34}}, "a", 2);
|
||||||
create<ast::DiscardStatement>(),
|
|
||||||
Assign(Source{{12, 34}}, "a", 2),
|
Func("func", ast::VariableList{}, ty.void_(), {decl_a, discard, assign_a});
|
||||||
});
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
ASSERT_TRUE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: code is unreachable");
|
EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
|
||||||
|
EXPECT_TRUE(Sem().Get(decl_a)->IsReachable());
|
||||||
|
EXPECT_TRUE(Sem().Get(discard)->IsReachable());
|
||||||
|
EXPECT_FALSE(Sem().Get(assign_a)->IsReachable());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverFunctionValidationTest, UnreachableCode_discard_InBlocks) {
|
TEST_F(ResolverFunctionValidationTest, UnreachableCode_discard_InBlocks) {
|
||||||
|
@ -177,20 +188,24 @@ TEST_F(ResolverFunctionValidationTest, UnreachableCode_discard_InBlocks) {
|
||||||
// a = 2;
|
// a = 2;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
auto* decl_a = Decl(Var("a", ty.i32()));
|
||||||
|
auto* discard = Discard();
|
||||||
|
auto* assign_a = Assign(Source{{12, 34}}, "a", 2);
|
||||||
|
|
||||||
Func("func", ast::VariableList{}, ty.void_(),
|
Func("func", ast::VariableList{}, ty.void_(),
|
||||||
{
|
{decl_a, Block(Block(Block(discard))), assign_a});
|
||||||
Decl(Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2))),
|
|
||||||
Block(Block(Block(create<ast::DiscardStatement>()))),
|
ASSERT_TRUE(r()->Resolve());
|
||||||
Assign(Source{{12, 34}}, "a", 2),
|
EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
|
||||||
});
|
EXPECT_TRUE(Sem().Get(decl_a)->IsReachable());
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_TRUE(Sem().Get(discard)->IsReachable());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: code is unreachable");
|
EXPECT_FALSE(Sem().Get(assign_a)->IsReachable());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverFunctionValidationTest, FunctionEndWithoutReturnStatement_Fail) {
|
TEST_F(ResolverFunctionValidationTest, FunctionEndWithoutReturnStatement_Fail) {
|
||||||
// fn func() -> int { var a:i32 = 2; }
|
// fn func() -> int { var a:i32 = 2; }
|
||||||
|
|
||||||
auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
|
auto* var = Var("a", ty.i32(), Expr(2));
|
||||||
|
|
||||||
Func(Source{{12, 34}}, "func", ast::VariableList{}, ty.i32(),
|
Func(Source{{12, 34}}, "func", ast::VariableList{}, ty.i32(),
|
||||||
ast::StatementList{
|
ast::StatementList{
|
||||||
|
@ -199,8 +214,7 @@ TEST_F(ResolverFunctionValidationTest, FunctionEndWithoutReturnStatement_Fail) {
|
||||||
ast::DecorationList{});
|
ast::DecorationList{});
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(), "12:34 error: missing return at end of function");
|
||||||
"12:34 error: non-void function must end with a return statement");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverFunctionValidationTest,
|
TEST_F(ResolverFunctionValidationTest,
|
||||||
|
@ -221,8 +235,7 @@ TEST_F(ResolverFunctionValidationTest,
|
||||||
ast::StatementList{}, ast::DecorationList{});
|
ast::StatementList{}, ast::DecorationList{});
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(), "12:34 error: missing return at end of function");
|
||||||
"12:34 error: non-void function must end with a return statement");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverFunctionValidationTest,
|
TEST_F(ResolverFunctionValidationTest,
|
||||||
|
@ -392,7 +405,7 @@ TEST_F(ResolverFunctionValidationTest, FunctionVarInitWithParam) {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
auto* bar = Param("bar", ty.f32());
|
auto* bar = Param("bar", ty.f32());
|
||||||
auto* baz = Var("baz", ty.f32(), ast::StorageClass::kNone, 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_(), ast::StatementList{Decl(baz)},
|
||||||
ast::DecorationList{});
|
ast::DecorationList{});
|
||||||
|
|
|
@ -809,6 +809,7 @@ bool Resolver::WorkgroupSize(const ast::Function* func) {
|
||||||
bool Resolver::Statements(const ast::StatementList& stmts) {
|
bool Resolver::Statements(const ast::StatementList& stmts) {
|
||||||
sem::Behaviors behaviors{sem::Behavior::kNext};
|
sem::Behaviors behaviors{sem::Behavior::kNext};
|
||||||
|
|
||||||
|
bool reachable = true;
|
||||||
for (auto* stmt : stmts) {
|
for (auto* stmt : stmts) {
|
||||||
Mark(stmt);
|
Mark(stmt);
|
||||||
auto* sem = Statement(stmt);
|
auto* sem = Statement(stmt);
|
||||||
|
@ -816,9 +817,11 @@ bool Resolver::Statements(const ast::StatementList& stmts) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// s1 s2:(B1∖{Next}) ∪ B2
|
// s1 s2:(B1∖{Next}) ∪ B2
|
||||||
// ValidateStatements will ensure that statements can only follow a Next.
|
sem->SetIsReachable(reachable);
|
||||||
behaviors.Remove(sem::Behavior::kNext);
|
if (reachable) {
|
||||||
behaviors.Add(sem->Behaviors());
|
behaviors = (behaviors - sem::Behavior::kNext) + sem->Behaviors();
|
||||||
|
}
|
||||||
|
reachable = reachable && sem->Behaviors().Contains(sem::Behavior::kNext);
|
||||||
}
|
}
|
||||||
|
|
||||||
current_statement_->Behaviors() = behaviors;
|
current_statement_->Behaviors() = behaviors;
|
||||||
|
@ -2660,6 +2663,26 @@ bool Resolver::IsCallStatement(const ast::Expression* expr) const {
|
||||||
[&](auto* stmt) { return stmt->expr == expr; });
|
[&](auto* stmt) { return stmt->expr == expr; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ast::Statement* Resolver::ClosestContinuing(bool stop_at_loop) const {
|
||||||
|
for (const auto* s = current_statement_; s != nullptr; s = s->Parent()) {
|
||||||
|
if (stop_at_loop && s->Is<sem::LoopStatement>()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (s->Is<sem::LoopContinuingBlockStatement>()) {
|
||||||
|
return s->Declaration();
|
||||||
|
}
|
||||||
|
if (auto* f = As<sem::ForLoopStatement>(s->Parent())) {
|
||||||
|
if (f->Declaration()->continuing == s->Declaration()) {
|
||||||
|
return s->Declaration();
|
||||||
|
}
|
||||||
|
if (stop_at_loop) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Resolver::TypeConversionSig
|
// Resolver::TypeConversionSig
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -464,6 +464,14 @@ class Resolver {
|
||||||
/// @returns true if `expr` is the current CallStatement's CallExpression
|
/// @returns true if `expr` is the current CallStatement's CallExpression
|
||||||
bool IsCallStatement(const ast::Expression* expr) const;
|
bool IsCallStatement(const ast::Expression* expr) const;
|
||||||
|
|
||||||
|
/// Searches the current statement and up through parents of the current
|
||||||
|
/// statement looking for a loop or for-loop continuing statement.
|
||||||
|
/// @returns the closest continuing statement to the current statement that
|
||||||
|
/// (transitively) owns the current statement.
|
||||||
|
/// @param stop_at_loop if true then the function will return nullptr if a
|
||||||
|
/// loop or for-loop was found before the continuing.
|
||||||
|
const ast::Statement* ClosestContinuing(bool stop_at_loop) const;
|
||||||
|
|
||||||
/// @returns the resolved symbol (function, type or variable) for the given
|
/// @returns the resolved symbol (function, type or variable) for the given
|
||||||
/// ast::Identifier or ast::TypeName cast to the given semantic type.
|
/// ast::Identifier or ast::TypeName cast to the given semantic type.
|
||||||
template <typename SEM = sem::Node>
|
template <typename SEM = sem::Node>
|
||||||
|
|
|
@ -327,28 +327,6 @@ TEST_F(ResolverBehaviorTest, StmtForLoopEmpty_CondCallFuncMayDiscard) {
|
||||||
sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
|
sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBehaviorTest, StmtForLoopBreak_ContCallFuncMayDiscard) {
|
|
||||||
auto* stmt =
|
|
||||||
For(nullptr, nullptr, CallStmt(Call("DiscardOrNext")), Block(Break()));
|
|
||||||
WrapInFunction(stmt);
|
|
||||||
|
|
||||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
|
||||||
|
|
||||||
auto* sem = Sem().Get(stmt);
|
|
||||||
EXPECT_EQ(sem->Behaviors(),
|
|
||||||
sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ResolverBehaviorTest, StmtForLoopEmpty_ContCallFuncMayDiscard) {
|
|
||||||
auto* stmt = For(nullptr, nullptr, CallStmt(Call("DiscardOrNext")), Block());
|
|
||||||
WrapInFunction(stmt);
|
|
||||||
|
|
||||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
|
||||||
|
|
||||||
auto* sem = Sem().Get(stmt);
|
|
||||||
EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ResolverBehaviorTest, StmtIfTrue_ThenEmptyBlock) {
|
TEST_F(ResolverBehaviorTest, StmtIfTrue_ThenEmptyBlock) {
|
||||||
auto* stmt = If(true, Block());
|
auto* stmt = If(true, Block());
|
||||||
WrapInFunction(stmt);
|
WrapInFunction(stmt);
|
||||||
|
|
|
@ -1000,10 +1000,12 @@ bool Resolver::ValidateFunction(const sem::Function* func) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decl->body) {
|
if (decl->body) {
|
||||||
if (!decl->body->Last() ||
|
sem::Behaviors behaviors{sem::Behavior::kNext};
|
||||||
!decl->body->Last()->Is<ast::ReturnStatement>()) {
|
if (auto* last = decl->body->Last()) {
|
||||||
AddError("non-void function must end with a return statement",
|
behaviors = Sem(last)->Behaviors();
|
||||||
decl->source);
|
}
|
||||||
|
if (behaviors.Contains(sem::Behavior::kNext)) {
|
||||||
|
AddError("missing return at end of function", decl->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (IsValidationEnabled(
|
} else if (IsValidationEnabled(
|
||||||
|
@ -1334,31 +1336,19 @@ bool Resolver::ValidateEntryPoint(const sem::Function* func) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resolver::ValidateStatements(const ast::StatementList& stmts) {
|
bool Resolver::ValidateStatements(const ast::StatementList& stmts) {
|
||||||
bool unreachable = false;
|
|
||||||
for (auto* stmt : stmts) {
|
for (auto* stmt : stmts) {
|
||||||
if (unreachable) {
|
if (!Sem(stmt)->IsReachable()) {
|
||||||
AddError("code is unreachable", stmt->source);
|
/// TODO(https://github.com/gpuweb/gpuweb/issues/2378): This may need to
|
||||||
return false;
|
/// become an error.
|
||||||
}
|
AddWarning("code is unreachable", stmt->source);
|
||||||
|
break;
|
||||||
auto* nested_stmt = stmt;
|
|
||||||
while (auto* block = nested_stmt->As<ast::BlockStatement>()) {
|
|
||||||
if (block->Empty()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
nested_stmt = block->statements.back();
|
|
||||||
}
|
|
||||||
if (nested_stmt->IsAnyOf<ast::ReturnStatement, ast::BreakStatement,
|
|
||||||
ast::ContinueStatement, ast::DiscardStatement>()) {
|
|
||||||
unreachable = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resolver::ValidateBreakStatement(const sem::Statement* stmt) {
|
bool Resolver::ValidateBreakStatement(const sem::Statement* stmt) {
|
||||||
if (!stmt->FindFirstParent<sem::LoopBlockStatement>() &&
|
if (!stmt->FindFirstParent<sem::LoopBlockStatement, sem::CaseStatement>()) {
|
||||||
!stmt->FindFirstParent<sem::CaseStatement>()) {
|
|
||||||
AddError("break statement must be in a loop or switch case",
|
AddError("break statement must be in a loop or switch case",
|
||||||
stmt->Declaration()->source);
|
stmt->Declaration()->source);
|
||||||
return false;
|
return false;
|
||||||
|
@ -1367,15 +1357,17 @@ bool Resolver::ValidateBreakStatement(const sem::Statement* stmt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resolver::ValidateContinueStatement(const sem::Statement* stmt) {
|
bool Resolver::ValidateContinueStatement(const sem::Statement* stmt) {
|
||||||
if (auto* block =
|
if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ true)) {
|
||||||
stmt->FindFirstParent<sem::LoopBlockStatement,
|
AddError("continuing blocks must not contain a continue statement",
|
||||||
sem::LoopContinuingBlockStatement>()) {
|
stmt->Declaration()->source);
|
||||||
if (block->Is<sem::LoopContinuingBlockStatement>()) {
|
if (continuing != stmt->Declaration() &&
|
||||||
AddError("continuing blocks must not contain a continue statement",
|
continuing != stmt->Parent()->Declaration()) {
|
||||||
stmt->Declaration()->source);
|
AddNote("see continuing block here", continuing->source);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
} else {
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stmt->FindFirstParent<sem::LoopBlockStatement>()) {
|
||||||
AddError("continue statement must be in a loop",
|
AddError("continue statement must be in a loop",
|
||||||
stmt->Declaration()->source);
|
stmt->Declaration()->source);
|
||||||
return false;
|
return false;
|
||||||
|
@ -1385,12 +1377,12 @@ bool Resolver::ValidateContinueStatement(const sem::Statement* stmt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resolver::ValidateDiscardStatement(const sem::Statement* stmt) {
|
bool Resolver::ValidateDiscardStatement(const sem::Statement* stmt) {
|
||||||
if (auto* continuing =
|
if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ false)) {
|
||||||
stmt->FindFirstParent<sem::LoopContinuingBlockStatement>()) {
|
|
||||||
AddError("continuing blocks must not contain a discard statement",
|
AddError("continuing blocks must not contain a discard statement",
|
||||||
stmt->Declaration()->source);
|
stmt->Declaration()->source);
|
||||||
if (continuing != stmt->Parent()) {
|
if (continuing != stmt->Declaration() &&
|
||||||
AddNote("see continuing block here", continuing->Declaration()->source);
|
continuing != stmt->Parent()->Declaration()) {
|
||||||
|
AddNote("see continuing block here", continuing->source);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1629,6 +1621,20 @@ bool Resolver::ValidateFunctionCall(const sem::Call* call) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (call->Behaviors().Contains(sem::Behavior::kDiscard)) {
|
||||||
|
if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ false)) {
|
||||||
|
AddError(
|
||||||
|
"cannot call a function that may discard inside a continuing block",
|
||||||
|
call->Declaration()->source);
|
||||||
|
if (continuing != call->Stmt()->Declaration() &&
|
||||||
|
continuing != call->Stmt()->Parent()->Declaration()) {
|
||||||
|
AddNote("see continuing block here", continuing->source);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2200,12 +2206,12 @@ bool Resolver::ValidateReturn(const ast::ReturnStatement* ret) {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* sem = Sem(ret);
|
auto* sem = Sem(ret);
|
||||||
if (auto* continuing =
|
if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ false)) {
|
||||||
sem->FindFirstParent<sem::LoopContinuingBlockStatement>()) {
|
|
||||||
AddError("continuing blocks must not contain a return statement",
|
AddError("continuing blocks must not contain a return statement",
|
||||||
ret->source);
|
ret->source);
|
||||||
if (continuing != sem->Parent()) {
|
if (continuing != sem->Declaration() &&
|
||||||
AddNote("see continuing block here", continuing->Declaration()->source);
|
continuing != sem->Parent()->Declaration()) {
|
||||||
|
AddNote("see continuing block here", continuing->source);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -455,8 +455,7 @@ TEST_F(ResolverValidationTest,
|
||||||
Stmt_Loop_ContinueInLoopBodyBeforeDeclAndAfterDecl_UsageInContinuing) {
|
Stmt_Loop_ContinueInLoopBodyBeforeDeclAndAfterDecl_UsageInContinuing) {
|
||||||
// loop {
|
// loop {
|
||||||
// continue; // Bypasses z decl
|
// continue; // Bypasses z decl
|
||||||
// var z : i32;
|
// var z : i32; // unreachable
|
||||||
// continue; // Ok
|
|
||||||
//
|
//
|
||||||
// continuing {
|
// continuing {
|
||||||
// z = 2;
|
// z = 2;
|
||||||
|
@ -465,24 +464,25 @@ TEST_F(ResolverValidationTest,
|
||||||
|
|
||||||
auto error_loc = Source{{12, 34}};
|
auto error_loc = Source{{12, 34}};
|
||||||
auto* body =
|
auto* body =
|
||||||
Block(create<ast::ContinueStatement>(),
|
Block(Continue(),
|
||||||
Decl(error_loc, Var("z", ty.i32(), ast::StorageClass::kNone)),
|
Decl(error_loc, Var("z", ty.i32(), ast::StorageClass::kNone)));
|
||||||
create<ast::ContinueStatement>());
|
|
||||||
auto* continuing = Block(Assign(Expr("z"), 2));
|
auto* continuing = Block(Assign(Expr("z"), 2));
|
||||||
auto* loop_stmt = Loop(body, continuing);
|
auto* loop_stmt = Loop(body, continuing);
|
||||||
WrapInFunction(loop_stmt);
|
WrapInFunction(loop_stmt);
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve()) << r()->error();
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: code is unreachable");
|
EXPECT_EQ(r()->error(),
|
||||||
|
R"(12:34 warning: code is unreachable
|
||||||
|
error: continue statement bypasses declaration of 'z'
|
||||||
|
note: identifier 'z' declared here
|
||||||
|
note: identifier 'z' referenced in continuing block here)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(
|
TEST_F(ResolverValidationTest,
|
||||||
ResolverValidationTest,
|
Stmt_Loop_ContinueInLoopBodyAfterDecl_UsageInContinuing_InBlocks) {
|
||||||
Stmt_Loop_ContinueInLoopBodyBeforeDeclAndAfterDecl_UsageInContinuing_InBlocks) { // NOLINT - line length
|
|
||||||
// loop {
|
// loop {
|
||||||
// var z : i32;
|
// var z : i32;
|
||||||
// {{{continue;}}} // Bypasses z decl
|
// {{{continue;}}}
|
||||||
// z = 1;
|
|
||||||
// continue; // Ok
|
// continue; // Ok
|
||||||
//
|
//
|
||||||
// continuing {
|
// continuing {
|
||||||
|
@ -490,16 +490,13 @@ TEST_F(
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
auto* body =
|
auto* body = Block(Decl(Var("z", ty.i32(), ast::StorageClass::kNone)),
|
||||||
Block(Decl(Var("z", ty.i32(), ast::StorageClass::kNone)),
|
Block(Block(Block(Continue()))));
|
||||||
Block(Block(Block(create<ast::ContinueStatement>()))),
|
|
||||||
Assign(Source{{12, 34}}, "z", 2), create<ast::ContinueStatement>());
|
|
||||||
auto* continuing = Block(Assign(Expr("z"), 2));
|
auto* continuing = Block(Assign(Expr("z"), 2));
|
||||||
auto* loop_stmt = Loop(body, continuing);
|
auto* loop_stmt = Loop(body, continuing);
|
||||||
WrapInFunction(loop_stmt);
|
WrapInFunction(loop_stmt);
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve()) << r()->error();
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: code is unreachable");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverValidationTest,
|
TEST_F(ResolverValidationTest,
|
||||||
|
@ -518,7 +515,7 @@ TEST_F(ResolverValidationTest,
|
||||||
auto decl_loc = Source{{56, 78}};
|
auto decl_loc = Source{{56, 78}};
|
||||||
auto ref_loc = Source{{90, 12}};
|
auto ref_loc = Source{{90, 12}};
|
||||||
auto* body =
|
auto* body =
|
||||||
Block(If(Expr(true), Block(create<ast::ContinueStatement>(cont_loc))),
|
Block(If(Expr(true), Block(Continue(cont_loc))),
|
||||||
Decl(Var(decl_loc, "z", ty.i32(), ast::StorageClass::kNone)));
|
Decl(Var(decl_loc, "z", ty.i32(), ast::StorageClass::kNone)));
|
||||||
auto* continuing = Block(Assign(Expr(ref_loc, "z"), 2));
|
auto* continuing = Block(Assign(Expr(ref_loc, "z"), 2));
|
||||||
auto* loop_stmt = Loop(body, continuing);
|
auto* loop_stmt = Loop(body, continuing);
|
||||||
|
@ -550,7 +547,7 @@ TEST_F(
|
||||||
auto decl_loc = Source{{56, 78}};
|
auto decl_loc = Source{{56, 78}};
|
||||||
auto ref_loc = Source{{90, 12}};
|
auto ref_loc = Source{{90, 12}};
|
||||||
auto* body =
|
auto* body =
|
||||||
Block(If(Expr(true), Block(create<ast::ContinueStatement>(cont_loc))),
|
Block(If(Expr(true), Block(Continue(cont_loc))),
|
||||||
Decl(Var(decl_loc, "z", ty.i32(), ast::StorageClass::kNone)));
|
Decl(Var(decl_loc, "z", ty.i32(), ast::StorageClass::kNone)));
|
||||||
|
|
||||||
auto* continuing =
|
auto* continuing =
|
||||||
|
@ -584,7 +581,7 @@ TEST_F(ResolverValidationTest,
|
||||||
auto decl_loc = Source{{56, 78}};
|
auto decl_loc = Source{{56, 78}};
|
||||||
auto ref_loc = Source{{90, 12}};
|
auto ref_loc = Source{{90, 12}};
|
||||||
auto* body =
|
auto* body =
|
||||||
Block(If(Expr(true), Block(create<ast::ContinueStatement>(cont_loc))),
|
Block(If(Expr(true), Block(Continue(cont_loc))),
|
||||||
Decl(Var(decl_loc, "z", ty.i32(), ast::StorageClass::kNone)));
|
Decl(Var(decl_loc, "z", ty.i32(), ast::StorageClass::kNone)));
|
||||||
auto* compare = create<ast::BinaryExpression>(ast::BinaryOp::kLessThan,
|
auto* compare = create<ast::BinaryExpression>(ast::BinaryOp::kLessThan,
|
||||||
Expr(ref_loc, "z"), Expr(2));
|
Expr(ref_loc, "z"), Expr(2));
|
||||||
|
@ -617,7 +614,7 @@ TEST_F(ResolverValidationTest,
|
||||||
auto decl_loc = Source{{56, 78}};
|
auto decl_loc = Source{{56, 78}};
|
||||||
auto ref_loc = Source{{90, 12}};
|
auto ref_loc = Source{{90, 12}};
|
||||||
auto* body =
|
auto* body =
|
||||||
Block(If(Expr(true), Block(create<ast::ContinueStatement>(cont_loc))),
|
Block(If(Expr(true), Block(Continue(cont_loc))),
|
||||||
Decl(Var(decl_loc, "z", ty.i32(), ast::StorageClass::kNone)));
|
Decl(Var(decl_loc, "z", ty.i32(), ast::StorageClass::kNone)));
|
||||||
|
|
||||||
auto* continuing = Block(Loop(Block(Assign(Expr(ref_loc, "z"), 2))));
|
auto* continuing = Block(Loop(Block(Assign(Expr(ref_loc, "z"), 2))));
|
||||||
|
@ -635,7 +632,8 @@ TEST_F(ResolverValidationTest,
|
||||||
Stmt_Loop_ContinueInNestedLoopBodyBeforeDecl_UsageInContinuing) {
|
Stmt_Loop_ContinueInNestedLoopBodyBeforeDecl_UsageInContinuing) {
|
||||||
// loop {
|
// loop {
|
||||||
// loop {
|
// loop {
|
||||||
// continue; // OK: not part of the outer loop
|
// if (true) { continue; } // OK: not part of the outer loop
|
||||||
|
// break;
|
||||||
// }
|
// }
|
||||||
// var z : i32;
|
// var z : i32;
|
||||||
//
|
//
|
||||||
|
@ -644,7 +642,9 @@ TEST_F(ResolverValidationTest,
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
auto* inner_loop = Loop(Block(create<ast::ContinueStatement>()));
|
auto* inner_loop = Loop(Block( //
|
||||||
|
If(true, Block(Continue())), //
|
||||||
|
Break()));
|
||||||
auto* body =
|
auto* body =
|
||||||
Block(inner_loop, Decl(Var("z", ty.i32(), ast::StorageClass::kNone)));
|
Block(inner_loop, Decl(Var("z", ty.i32(), ast::StorageClass::kNone)));
|
||||||
auto* continuing = Block(Assign("z", 2));
|
auto* continuing = Block(Assign("z", 2));
|
||||||
|
@ -658,7 +658,8 @@ TEST_F(ResolverValidationTest,
|
||||||
Stmt_Loop_ContinueInNestedLoopBodyBeforeDecl_UsageInContinuingSubscope) {
|
Stmt_Loop_ContinueInNestedLoopBodyBeforeDecl_UsageInContinuingSubscope) {
|
||||||
// loop {
|
// loop {
|
||||||
// loop {
|
// loop {
|
||||||
// continue; // OK: not part of the outer loop
|
// if (true) { continue; } // OK: not part of the outer loop
|
||||||
|
// break;
|
||||||
// }
|
// }
|
||||||
// var z : i32;
|
// var z : i32;
|
||||||
//
|
//
|
||||||
|
@ -669,7 +670,8 @@ TEST_F(ResolverValidationTest,
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
auto* inner_loop = Loop(Block(create<ast::ContinueStatement>()));
|
auto* inner_loop = Loop(Block(If(true, Block(Continue())), //
|
||||||
|
Break()));
|
||||||
auto* body =
|
auto* body =
|
||||||
Block(inner_loop, Decl(Var("z", ty.i32(), ast::StorageClass::kNone)));
|
Block(inner_loop, Decl(Var("z", ty.i32(), ast::StorageClass::kNone)));
|
||||||
auto* continuing = Block(If(Expr(true), Block(Assign("z", 2))));
|
auto* continuing = Block(If(Expr(true), Block(Assign("z", 2))));
|
||||||
|
@ -683,7 +685,8 @@ TEST_F(ResolverValidationTest,
|
||||||
Stmt_Loop_ContinueInNestedLoopBodyBeforeDecl_UsageInContinuingLoop) {
|
Stmt_Loop_ContinueInNestedLoopBodyBeforeDecl_UsageInContinuingLoop) {
|
||||||
// loop {
|
// loop {
|
||||||
// loop {
|
// loop {
|
||||||
// continue; // OK: not part of the outer loop
|
// if (true) { continue; } // OK: not part of the outer loop
|
||||||
|
// break;
|
||||||
// }
|
// }
|
||||||
// var z : i32;
|
// var z : i32;
|
||||||
//
|
//
|
||||||
|
@ -694,7 +697,8 @@ TEST_F(ResolverValidationTest,
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
auto* inner_loop = Loop(Block(create<ast::ContinueStatement>()));
|
auto* inner_loop = Loop(Block(If(true, Block(Continue())), //
|
||||||
|
Break()));
|
||||||
auto* body =
|
auto* body =
|
||||||
Block(inner_loop, Decl(Var("z", ty.i32(), ast::StorageClass::kNone)));
|
Block(inner_loop, Decl(Var("z", ty.i32(), ast::StorageClass::kNone)));
|
||||||
auto* continuing = Block(Loop(Block(Assign("z", 2))));
|
auto* continuing = Block(Loop(Block(Assign("z", 2))));
|
||||||
|
@ -707,7 +711,7 @@ TEST_F(ResolverValidationTest,
|
||||||
TEST_F(ResolverTest, Stmt_Loop_ContinueInLoopBodyAfterDecl_UsageInContinuing) {
|
TEST_F(ResolverTest, Stmt_Loop_ContinueInLoopBodyAfterDecl_UsageInContinuing) {
|
||||||
// loop {
|
// loop {
|
||||||
// var z : i32;
|
// var z : i32;
|
||||||
// continue;
|
// if (true) { continue; }
|
||||||
//
|
//
|
||||||
// continuing {
|
// continuing {
|
||||||
// z = 2;
|
// z = 2;
|
||||||
|
@ -716,7 +720,7 @@ TEST_F(ResolverTest, Stmt_Loop_ContinueInLoopBodyAfterDecl_UsageInContinuing) {
|
||||||
|
|
||||||
auto error_loc = Source{{12, 34}};
|
auto error_loc = Source{{12, 34}};
|
||||||
auto* body = Block(Decl(Var("z", ty.i32(), ast::StorageClass::kNone)),
|
auto* body = Block(Decl(Var("z", ty.i32(), ast::StorageClass::kNone)),
|
||||||
create<ast::ContinueStatement>());
|
If(true, Block(Continue())));
|
||||||
auto* continuing = Block(Assign(Expr(error_loc, "z"), 2));
|
auto* continuing = Block(Assign(Expr(error_loc, "z"), 2));
|
||||||
auto* loop_stmt = Loop(body, continuing);
|
auto* loop_stmt = Loop(body, continuing);
|
||||||
WrapInFunction(loop_stmt);
|
WrapInFunction(loop_stmt);
|
||||||
|
@ -775,7 +779,7 @@ TEST_F(ResolverTest, Stmt_Loop_DiscardInContinuing_Direct) {
|
||||||
WrapInFunction(Loop( // loop
|
WrapInFunction(Loop( // loop
|
||||||
Block(), // loop block
|
Block(), // loop block
|
||||||
Block( // loop continuing block
|
Block( // loop continuing block
|
||||||
create<ast::DiscardStatement>(Source{{12, 34}}))));
|
Discard(Source{{12, 34}}))));
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
|
@ -795,7 +799,7 @@ TEST_F(ResolverTest, Stmt_Loop_DiscardInContinuing_Indirect) {
|
||||||
Block(Source{{56, 78}}, // outer loop continuing block
|
Block(Source{{56, 78}}, // outer loop continuing block
|
||||||
Loop( // inner loop
|
Loop( // inner loop
|
||||||
Block( // inner loop block
|
Block( // inner loop block
|
||||||
create<ast::DiscardStatement>(Source{{12, 34}}))))));
|
Discard(Source{{12, 34}}))))));
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
|
@ -804,6 +808,32 @@ TEST_F(ResolverTest, Stmt_Loop_DiscardInContinuing_Indirect) {
|
||||||
56:78 note: see continuing block here)");
|
56:78 note: see continuing block here)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverTest, Stmt_Loop_DiscardInContinuing_Indirect_ViaCall) {
|
||||||
|
// fn MayDiscard() { if (true) { discard; } }
|
||||||
|
// fn F() { MayDiscard(); }
|
||||||
|
// loop {
|
||||||
|
// continuing {
|
||||||
|
// loop { F(); }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
Func("MayDiscard", {}, ty.void_(), {If(true, Block(Discard()))});
|
||||||
|
Func("SomeFunc", {}, ty.void_(), {CallStmt(Call("MayDiscard"))});
|
||||||
|
|
||||||
|
WrapInFunction(Loop( // outer loop
|
||||||
|
Block(), // outer loop block
|
||||||
|
Block(Source{{56, 78}}, // outer loop continuing block
|
||||||
|
Loop( // inner loop
|
||||||
|
Block( // inner loop block
|
||||||
|
CallStmt(Call(Source{{12, 34}}, "SomeFunc")))))));
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(
|
||||||
|
r()->error(),
|
||||||
|
R"(12:34 error: cannot call a function that may discard inside a continuing block
|
||||||
|
56:78 note: see continuing block here)");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ResolverTest, Stmt_Loop_ContinueInContinuing_Direct) {
|
TEST_F(ResolverTest, Stmt_Loop_ContinueInContinuing_Direct) {
|
||||||
// loop {
|
// loop {
|
||||||
// continuing {
|
// continuing {
|
||||||
|
@ -814,7 +844,7 @@ TEST_F(ResolverTest, Stmt_Loop_ContinueInContinuing_Direct) {
|
||||||
WrapInFunction(Loop( // loop
|
WrapInFunction(Loop( // loop
|
||||||
Block(), // loop block
|
Block(), // loop block
|
||||||
Block(Source{{56, 78}}, // loop continuing block
|
Block(Source{{56, 78}}, // loop continuing block
|
||||||
create<ast::ContinueStatement>(Source{{12, 34}}))));
|
Continue(Source{{12, 34}}))));
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
|
@ -836,7 +866,118 @@ TEST_F(ResolverTest, Stmt_Loop_ContinueInContinuing_Indirect) {
|
||||||
Block( // outer loop continuing block
|
Block( // outer loop continuing block
|
||||||
Loop( // inner loop
|
Loop( // inner loop
|
||||||
Block( // inner loop block
|
Block( // inner loop block
|
||||||
create<ast::ContinueStatement>(Source{{12, 34}}))))));
|
Continue(Source{{12, 34}}))))));
|
||||||
|
|
||||||
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverTest, Stmt_ForLoop_ReturnInContinuing_Direct) {
|
||||||
|
// for(;; return) {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
|
||||||
|
WrapInFunction(For(nullptr, nullptr, Return(Source{{12, 34}}), //
|
||||||
|
Block(Break())));
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(
|
||||||
|
r()->error(),
|
||||||
|
R"(12:34 error: continuing blocks must not contain a return statement)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverTest, Stmt_ForLoop_ReturnInContinuing_Indirect) {
|
||||||
|
// for(;; loop { return }) {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
|
||||||
|
WrapInFunction(For(nullptr, nullptr,
|
||||||
|
Loop(Source{{56, 78}}, //
|
||||||
|
Block(Return(Source{{12, 34}}))), //
|
||||||
|
Block(Break())));
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(
|
||||||
|
r()->error(),
|
||||||
|
R"(12:34 error: continuing blocks must not contain a return statement
|
||||||
|
56:78 note: see continuing block here)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverTest, Stmt_ForLoop_DiscardInContinuing_Direct) {
|
||||||
|
// for(;; discard) {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
|
||||||
|
WrapInFunction(For(nullptr, nullptr, Discard(Source{{12, 34}}), //
|
||||||
|
Block(Break())));
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(
|
||||||
|
r()->error(),
|
||||||
|
R"(12:34 error: continuing blocks must not contain a discard statement)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverTest, Stmt_ForLoop_DiscardInContinuing_Indirect) {
|
||||||
|
// for(;; loop { discard }) {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
|
||||||
|
WrapInFunction(For(nullptr, nullptr,
|
||||||
|
Loop(Source{{56, 78}}, //
|
||||||
|
Block(Discard(Source{{12, 34}}))), //
|
||||||
|
Block(Break())));
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(
|
||||||
|
r()->error(),
|
||||||
|
R"(12:34 error: continuing blocks must not contain a discard statement
|
||||||
|
56:78 note: see continuing block here)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverTest, Stmt_ForLoop_DiscardInContinuing_Indirect_ViaCall) {
|
||||||
|
// fn MayDiscard() { if (true) { discard; } }
|
||||||
|
// fn F() { MayDiscard(); }
|
||||||
|
// for(;; loop { F() }) {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
|
||||||
|
Func("MayDiscard", {}, ty.void_(), {If(true, Block(Discard()))});
|
||||||
|
Func("F", {}, ty.void_(), {CallStmt(Call("MayDiscard"))});
|
||||||
|
|
||||||
|
WrapInFunction(For(nullptr, nullptr,
|
||||||
|
Loop(Source{{56, 78}}, //
|
||||||
|
Block(CallStmt(Call(Source{{12, 34}}, "F")))), //
|
||||||
|
Block(Break())));
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(
|
||||||
|
r()->error(),
|
||||||
|
R"(12:34 error: cannot call a function that may discard inside a continuing block
|
||||||
|
56:78 note: see continuing block here)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverTest, Stmt_ForLoop_ContinueInContinuing_Direct) {
|
||||||
|
// for(;; continue) {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
|
||||||
|
WrapInFunction(For(nullptr, nullptr, Continue(Source{{12, 34}}), //
|
||||||
|
Block(Break())));
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(
|
||||||
|
r()->error(),
|
||||||
|
"12:34 error: continuing blocks must not contain a continue statement");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverTest, Stmt_ForLoop_ContinueInContinuing_Indirect) {
|
||||||
|
// for(;; loop { continue }) {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
|
||||||
|
WrapInFunction(For(nullptr, nullptr,
|
||||||
|
Loop( //
|
||||||
|
Block(Continue(Source{{12, 34}}))), //
|
||||||
|
Block(Break())));
|
||||||
|
|
||||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
}
|
}
|
||||||
|
@ -863,12 +1004,12 @@ TEST_F(ResolverTest, Stmt_ForLoop_CondIsNotBool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverValidationTest, Stmt_ContinueInLoop) {
|
TEST_F(ResolverValidationTest, Stmt_ContinueInLoop) {
|
||||||
WrapInFunction(Loop(Block(create<ast::ContinueStatement>(Source{{12, 34}}))));
|
WrapInFunction(Loop(Block(Continue(Source{{12, 34}}))));
|
||||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverValidationTest, Stmt_ContinueNotInLoop) {
|
TEST_F(ResolverValidationTest, Stmt_ContinueNotInLoop) {
|
||||||
WrapInFunction(create<ast::ContinueStatement>(Source{{12, 34}}));
|
WrapInFunction(Continue(Source{{12, 34}}));
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: continue statement must be in a loop");
|
EXPECT_EQ(r()->error(), "12:34 error: continue statement must be in a loop");
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,5 +28,9 @@ ForLoopStatement::ForLoopStatement(const ast::ForLoopStatement* declaration,
|
||||||
|
|
||||||
ForLoopStatement::~ForLoopStatement() = default;
|
ForLoopStatement::~ForLoopStatement() = default;
|
||||||
|
|
||||||
|
const ast::ForLoopStatement* ForLoopStatement::Declaration() const {
|
||||||
|
return static_cast<const ast::ForLoopStatement*>(Base::Declaration());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace sem
|
} // namespace sem
|
||||||
} // namespace tint
|
} // namespace tint
|
||||||
|
|
|
@ -43,6 +43,9 @@ class ForLoopStatement : public Castable<ForLoopStatement, CompoundStatement> {
|
||||||
/// Destructor
|
/// Destructor
|
||||||
~ForLoopStatement() override;
|
~ForLoopStatement() override;
|
||||||
|
|
||||||
|
/// @returns the AST node
|
||||||
|
const ast::ForLoopStatement* Declaration() const;
|
||||||
|
|
||||||
/// @returns the for-loop condition expression
|
/// @returns the for-loop condition expression
|
||||||
const Expression* Condition() const { return condition_; }
|
const Expression* Condition() const { return condition_; }
|
||||||
|
|
||||||
|
|
|
@ -106,11 +106,20 @@ class Statement : public Castable<Statement, Node> {
|
||||||
/// @return the behaviors of this statement
|
/// @return the behaviors of this statement
|
||||||
sem::Behaviors& Behaviors() { return behaviors_; }
|
sem::Behaviors& Behaviors() { return behaviors_; }
|
||||||
|
|
||||||
|
/// @returns true if this statement is reachable by control flow according to
|
||||||
|
/// the behavior analysis
|
||||||
|
bool IsReachable() const { return is_reachable_; }
|
||||||
|
|
||||||
|
/// @param is_reachable whether this statement is reachable by control flow
|
||||||
|
/// according to the behavior analysis
|
||||||
|
void SetIsReachable(bool is_reachable) { is_reachable_ = is_reachable; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const ast::Statement* const declaration_;
|
const ast::Statement* const declaration_;
|
||||||
const CompoundStatement* const parent_;
|
const CompoundStatement* const parent_;
|
||||||
const sem::Function* const function_;
|
const sem::Function* const function_;
|
||||||
sem::Behaviors behaviors_{sem::Behavior::kNext};
|
sem::Behaviors behaviors_{sem::Behavior::kNext};
|
||||||
|
bool is_reachable_ = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// CompoundStatement is the base class of statements that can hold other
|
/// CompoundStatement is the base class of statements that can hold other
|
||||||
|
|
|
@ -194,8 +194,10 @@ fn z() -> i32 {
|
||||||
|
|
||||||
fn f() {
|
fn f() {
|
||||||
for (_ = &s.arr; ;_ = &s.arr) {
|
for (_ = &s.arr; ;_ = &s.arr) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
for (_ = x(); ;_ = y() + z()) {
|
for (_ = x(); ;_ = y() + z()) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
@ -225,8 +227,10 @@ fn phony_sink(p0 : i32, p1 : i32) {
|
||||||
|
|
||||||
fn f() {
|
fn f() {
|
||||||
for(; ; ) {
|
for(; ; ) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
for(x(); ; phony_sink(y(), z())) {
|
for(x(); ; phony_sink(y(), z())) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
// 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/transform/remove_unreachable_statements.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "src/ast/traverse_expressions.h"
|
||||||
|
#include "src/program_builder.h"
|
||||||
|
#include "src/sem/block_statement.h"
|
||||||
|
#include "src/sem/function.h"
|
||||||
|
#include "src/sem/statement.h"
|
||||||
|
#include "src/sem/variable.h"
|
||||||
|
#include "src/utils/map.h"
|
||||||
|
#include "src/utils/scoped_assignment.h"
|
||||||
|
|
||||||
|
TINT_INSTANTIATE_TYPEINFO(tint::transform::RemoveUnreachableStatements);
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
namespace transform {
|
||||||
|
|
||||||
|
RemoveUnreachableStatements::RemoveUnreachableStatements() = default;
|
||||||
|
|
||||||
|
RemoveUnreachableStatements::~RemoveUnreachableStatements() = default;
|
||||||
|
|
||||||
|
void RemoveUnreachableStatements::Run(CloneContext& ctx,
|
||||||
|
const DataMap&,
|
||||||
|
DataMap&) {
|
||||||
|
for (auto* node : ctx.src->ASTNodes().Objects()) {
|
||||||
|
if (auto* stmt = ctx.src->Sem().Get<sem::Statement>(node)) {
|
||||||
|
if (!stmt->IsReachable()) {
|
||||||
|
RemoveStatement(ctx, stmt->Declaration());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace transform
|
||||||
|
} // namespace tint
|
|
@ -0,0 +1,50 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#ifndef SRC_TRANSFORM_REMOVE_UNREACHABLE_STATEMENTS_H_
|
||||||
|
#define SRC_TRANSFORM_REMOVE_UNREACHABLE_STATEMENTS_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "src/transform/transform.h"
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
namespace transform {
|
||||||
|
|
||||||
|
/// RemoveUnreachableStatements is a Transform that removes all statements
|
||||||
|
/// marked as unreachable.
|
||||||
|
class RemoveUnreachableStatements
|
||||||
|
: public Castable<RemoveUnreachableStatements, Transform> {
|
||||||
|
public:
|
||||||
|
/// Constructor
|
||||||
|
RemoveUnreachableStatements();
|
||||||
|
|
||||||
|
/// Destructor
|
||||||
|
~RemoveUnreachableStatements() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// Runs the transform using the CloneContext built for transforming a
|
||||||
|
/// program. Run() is responsible for calling Clone() on the CloneContext.
|
||||||
|
/// @param ctx the CloneContext primed with the input program and
|
||||||
|
/// ProgramBuilder
|
||||||
|
/// @param inputs optional extra transform-specific input data
|
||||||
|
/// @param outputs optional extra transform-specific output data
|
||||||
|
void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace transform
|
||||||
|
} // namespace tint
|
||||||
|
|
||||||
|
#endif // SRC_TRANSFORM_REMOVE_UNREACHABLE_STATEMENTS_H_
|
|
@ -0,0 +1,537 @@
|
||||||
|
// 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/transform/remove_unreachable_statements.h"
|
||||||
|
|
||||||
|
#include "src/transform/test_helper.h"
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
namespace transform {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using RemoveUnreachableStatementsTest = TransformTest;
|
||||||
|
|
||||||
|
TEST_F(RemoveUnreachableStatementsTest, EmptyModule) {
|
||||||
|
auto* src = "";
|
||||||
|
auto* expect = "";
|
||||||
|
|
||||||
|
auto got = Run<RemoveUnreachableStatements>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RemoveUnreachableStatementsTest, Return) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
return;
|
||||||
|
var remove_me = 1;
|
||||||
|
if (true) {
|
||||||
|
var remove_me_too = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn f() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<RemoveUnreachableStatements>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RemoveUnreachableStatementsTest, NestedReturn) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
{
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var remove_me = 1;
|
||||||
|
if (true) {
|
||||||
|
var remove_me_too = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn f() {
|
||||||
|
{
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<RemoveUnreachableStatements>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RemoveUnreachableStatementsTest, Discard) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
discard;
|
||||||
|
var remove_me = 1;
|
||||||
|
if (true) {
|
||||||
|
var remove_me_too = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn f() {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<RemoveUnreachableStatements>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RemoveUnreachableStatementsTest, NestedDiscard) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
{
|
||||||
|
{
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var remove_me = 1;
|
||||||
|
if (true) {
|
||||||
|
var remove_me_too = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn f() {
|
||||||
|
{
|
||||||
|
{
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<RemoveUnreachableStatements>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RemoveUnreachableStatementsTest, CallToFuncWithDiscard) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn DISCARD() {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
DISCARD();
|
||||||
|
var remove_me = 1;
|
||||||
|
if (true) {
|
||||||
|
var remove_me_too = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn DISCARD() {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
DISCARD();
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<RemoveUnreachableStatements>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RemoveUnreachableStatementsTest, CallToFuncWithIfDiscard) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn DISCARD() {
|
||||||
|
if (true) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
DISCARD();
|
||||||
|
var preserve_me = 1;
|
||||||
|
if (true) {
|
||||||
|
var preserve_me_too = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = src;
|
||||||
|
|
||||||
|
auto got = Run<RemoveUnreachableStatements>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RemoveUnreachableStatementsTest, IfDiscardElseDiscard) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
if (true) {
|
||||||
|
discard;
|
||||||
|
} else {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
var remove_me = 1;
|
||||||
|
if (true) {
|
||||||
|
var remove_me_too = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn f() {
|
||||||
|
if (true) {
|
||||||
|
discard;
|
||||||
|
} else {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<RemoveUnreachableStatements>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RemoveUnreachableStatementsTest, IfDiscardElseReturn) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
if (true) {
|
||||||
|
discard;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var remove_me = 1;
|
||||||
|
if (true) {
|
||||||
|
var remove_me_too = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn f() {
|
||||||
|
if (true) {
|
||||||
|
discard;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<RemoveUnreachableStatements>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RemoveUnreachableStatementsTest, IfDiscard) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
if (true) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
var preserve_me = 1;
|
||||||
|
if (true) {
|
||||||
|
var preserve_me_too = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = src;
|
||||||
|
|
||||||
|
auto got = Run<RemoveUnreachableStatements>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RemoveUnreachableStatementsTest, IfReturn) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
if (true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var preserve_me = 1;
|
||||||
|
if (true) {
|
||||||
|
var preserve_me_too = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = src;
|
||||||
|
|
||||||
|
auto got = Run<RemoveUnreachableStatements>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RemoveUnreachableStatementsTest, IfElseDiscard) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
if (true) {
|
||||||
|
} else {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
var preserve_me = 1;
|
||||||
|
if (true) {
|
||||||
|
var preserve_me_too = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = src;
|
||||||
|
|
||||||
|
auto got = Run<RemoveUnreachableStatements>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RemoveUnreachableStatementsTest, IfElseReturn) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
if (true) {
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var preserve_me = 1;
|
||||||
|
if (true) {
|
||||||
|
var preserve_me_too = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = src;
|
||||||
|
|
||||||
|
auto got = Run<RemoveUnreachableStatements>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RemoveUnreachableStatementsTest, LoopWithNoBreak) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
loop {
|
||||||
|
var a = 1;
|
||||||
|
continuing {
|
||||||
|
var b = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var remove_me = 1;
|
||||||
|
if (true) {
|
||||||
|
var remove_me_too = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn f() {
|
||||||
|
loop {
|
||||||
|
var a = 1;
|
||||||
|
|
||||||
|
continuing {
|
||||||
|
var b = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<RemoveUnreachableStatements>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RemoveUnreachableStatementsTest, LoopWithConditionalBreak) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
loop {
|
||||||
|
var a = 1;
|
||||||
|
if (true) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
continuing {
|
||||||
|
var b = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var preserve_me = 1;
|
||||||
|
if (true) {
|
||||||
|
var preserve_me_too = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = src;
|
||||||
|
|
||||||
|
auto got = Run<RemoveUnreachableStatements>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RemoveUnreachableStatementsTest, LoopWithConditionalBreakInContinuing) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
loop {
|
||||||
|
|
||||||
|
continuing {
|
||||||
|
if (true) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var preserve_me = 1;
|
||||||
|
if (true) {
|
||||||
|
var preserve_me_too = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = src;
|
||||||
|
|
||||||
|
auto got = Run<RemoveUnreachableStatements>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RemoveUnreachableStatementsTest, SwitchDefaultDiscard) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
switch(1) {
|
||||||
|
default: {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var remove_me = 1;
|
||||||
|
if (true) {
|
||||||
|
var remove_me_too = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn f() {
|
||||||
|
switch(1) {
|
||||||
|
default: {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<RemoveUnreachableStatements>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RemoveUnreachableStatementsTest, SwitchCaseReturnDefaultDiscard) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
switch(1) {
|
||||||
|
case 0: {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var remove_me = 1;
|
||||||
|
if (true) {
|
||||||
|
var remove_me_too = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn f() {
|
||||||
|
switch(1) {
|
||||||
|
case 0: {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<RemoveUnreachableStatements>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RemoveUnreachableStatementsTest, SwitchCaseBreakDefaultDiscard) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
switch(1) {
|
||||||
|
case 0: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var preserve_me = 1;
|
||||||
|
if (true) {
|
||||||
|
var preserve_me_too = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = src;
|
||||||
|
|
||||||
|
auto got = Run<RemoveUnreachableStatements>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RemoveUnreachableStatementsTest, SwitchCaseReturnDefaultBreak) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
switch(1) {
|
||||||
|
case 0: {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var preserve_me = 1;
|
||||||
|
if (true) {
|
||||||
|
var preserve_me_too = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = src;
|
||||||
|
|
||||||
|
auto got = Run<RemoveUnreachableStatements>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace transform
|
||||||
|
} // namespace tint
|
|
@ -328,26 +328,14 @@ TEST_F(GlslGeneratorImplTest_Binary, If_WithLogical) {
|
||||||
Global("b", ty.bool_(), ast::StorageClass::kPrivate);
|
Global("b", ty.bool_(), ast::StorageClass::kPrivate);
|
||||||
Global("c", ty.bool_(), ast::StorageClass::kPrivate);
|
Global("c", ty.bool_(), ast::StorageClass::kPrivate);
|
||||||
|
|
||||||
auto* body = Block(Return(3));
|
auto* expr = If(create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd,
|
||||||
auto* else_stmt = create<ast::ElseStatement>(nullptr, body);
|
Expr("a"), Expr("b")),
|
||||||
|
Block(Return(1)),
|
||||||
body = Block(Return(2));
|
Else(create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr,
|
||||||
auto* else_if_stmt = create<ast::ElseStatement>(
|
Expr("b"), Expr("c")),
|
||||||
create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("b"),
|
Block(Return(2))),
|
||||||
Expr("c")),
|
Else(Block(Return(3))));
|
||||||
body);
|
Func("func", {}, ty.i32(), {WrapInStatement(expr)});
|
||||||
|
|
||||||
body = Block(Return(1));
|
|
||||||
|
|
||||||
auto* expr = create<ast::IfStatement>(
|
|
||||||
create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"),
|
|
||||||
Expr("b")),
|
|
||||||
body,
|
|
||||||
ast::ElseStatementList{
|
|
||||||
else_if_stmt,
|
|
||||||
else_stmt,
|
|
||||||
});
|
|
||||||
Func("func", {}, ty.i32(), {WrapInStatement(expr), Return(0)});
|
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
GeneratorImpl& gen = Build();
|
||||||
|
|
||||||
|
|
|
@ -328,26 +328,14 @@ TEST_F(HlslGeneratorImplTest_Binary, If_WithLogical) {
|
||||||
Global("b", ty.bool_(), ast::StorageClass::kPrivate);
|
Global("b", ty.bool_(), ast::StorageClass::kPrivate);
|
||||||
Global("c", ty.bool_(), ast::StorageClass::kPrivate);
|
Global("c", ty.bool_(), ast::StorageClass::kPrivate);
|
||||||
|
|
||||||
auto* body = Block(Return(3));
|
auto* expr = If(create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd,
|
||||||
auto* else_stmt = create<ast::ElseStatement>(nullptr, body);
|
Expr("a"), Expr("b")),
|
||||||
|
Block(Return(1)),
|
||||||
body = Block(Return(2));
|
Else(create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr,
|
||||||
auto* else_if_stmt = create<ast::ElseStatement>(
|
Expr("b"), Expr("c")),
|
||||||
create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("b"),
|
Block(Return(2))),
|
||||||
Expr("c")),
|
Else(Block(Return(3))));
|
||||||
body);
|
Func("func", {}, ty.i32(), {WrapInStatement(expr)});
|
||||||
|
|
||||||
body = Block(Return(1));
|
|
||||||
|
|
||||||
auto* expr = create<ast::IfStatement>(
|
|
||||||
create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"),
|
|
||||||
Expr("b")),
|
|
||||||
body,
|
|
||||||
ast::ElseStatementList{
|
|
||||||
else_if_stmt,
|
|
||||||
else_stmt,
|
|
||||||
});
|
|
||||||
Func("func", {}, ty.i32(), {WrapInStatement(expr), Return(0)});
|
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
GeneratorImpl& gen = Build();
|
||||||
|
|
||||||
|
|
|
@ -319,6 +319,7 @@ tint_unittests_source_set("tint_unittests_transform_src") {
|
||||||
"../src/transform/pad_array_elements_test.cc",
|
"../src/transform/pad_array_elements_test.cc",
|
||||||
"../src/transform/promote_initializers_to_const_var_test.cc",
|
"../src/transform/promote_initializers_to_const_var_test.cc",
|
||||||
"../src/transform/remove_phonies_test.cc",
|
"../src/transform/remove_phonies_test.cc",
|
||||||
|
"../src/transform/remove_unreachable_statements_test.cc",
|
||||||
"../src/transform/renamer_test.cc",
|
"../src/transform/renamer_test.cc",
|
||||||
"../src/transform/robustness_test.cc",
|
"../src/transform/robustness_test.cc",
|
||||||
"../src/transform/simplify_pointers_test.cc",
|
"../src/transform/simplify_pointers_test.cc",
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
SKIP: FAILED
|
|
||||||
|
|
||||||
[numthreads(1, 1, 1)]
|
[numthreads(1, 1, 1)]
|
||||||
void f() {
|
void f() {
|
||||||
{
|
{
|
||||||
for(int i = 0; (i < 4); i = (i + 1)) {
|
[loop] for(int i = 0; (i < 4); i = (i + 1)) {
|
||||||
switch(i) {
|
switch(i) {
|
||||||
case 0: {
|
case 0: {
|
||||||
continue;
|
continue;
|
||||||
|
@ -17,5 +15,3 @@ void f() {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
C:\src\tint\test\Shader@0x000001C513B5E750(7,11-19): error X3708: continue cannot be used in a switch
|
|
||||||
|
|
||||||
|
|
|
@ -6,5 +6,4 @@ fn f() -> i32 {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,5 +11,4 @@ int f() {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,5 @@ int f() {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
; SPIR-V
|
; SPIR-V
|
||||||
; Version: 1.3
|
; Version: 1.3
|
||||||
; Generator: Google Tint Compiler; 0
|
; Generator: Google Tint Compiler; 0
|
||||||
; Bound: 27
|
; Bound: 26
|
||||||
; Schema: 0
|
; Schema: 0
|
||||||
OpCapability Shader
|
OpCapability Shader
|
||||||
OpMemoryModel Logical GLSL450
|
OpMemoryModel Logical GLSL450
|
||||||
|
@ -19,7 +19,6 @@
|
||||||
%int_1 = OpConstant %int 1
|
%int_1 = OpConstant %int 1
|
||||||
%int_4 = OpConstant %int 4
|
%int_4 = OpConstant %int 4
|
||||||
%bool = OpTypeBool
|
%bool = OpTypeBool
|
||||||
%int_0 = OpConstant %int 0
|
|
||||||
%unused_entry_point = OpFunction %void None %1
|
%unused_entry_point = OpFunction %void None %1
|
||||||
%4 = OpLabel
|
%4 = OpLabel
|
||||||
OpReturn
|
OpReturn
|
||||||
|
@ -47,5 +46,5 @@
|
||||||
%14 = OpLabel
|
%14 = OpLabel
|
||||||
OpBranch %12
|
OpBranch %12
|
||||||
%13 = OpLabel
|
%13 = OpLabel
|
||||||
OpReturnValue %int_0
|
OpReturnValue %11
|
||||||
OpFunctionEnd
|
OpFunctionEnd
|
||||||
|
|
|
@ -6,5 +6,4 @@ fn f() -> i32 {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,5 +8,4 @@ fn f() -> i32 {
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,5 +13,4 @@ int f() {
|
||||||
i = (i + 1);
|
i = (i + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,5 @@ int f() {
|
||||||
i = as_type<int>((as_type<uint>(i) + as_type<uint>(1)));
|
i = as_type<int>((as_type<uint>(i) + as_type<uint>(1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
; SPIR-V
|
; SPIR-V
|
||||||
; Version: 1.3
|
; Version: 1.3
|
||||||
; Generator: Google Tint Compiler; 0
|
; Generator: Google Tint Compiler; 0
|
||||||
; Bound: 27
|
; Bound: 26
|
||||||
; Schema: 0
|
; Schema: 0
|
||||||
OpCapability Shader
|
OpCapability Shader
|
||||||
OpMemoryModel Logical GLSL450
|
OpMemoryModel Logical GLSL450
|
||||||
|
@ -19,7 +19,6 @@
|
||||||
%int_4 = OpConstant %int 4
|
%int_4 = OpConstant %int 4
|
||||||
%bool = OpTypeBool
|
%bool = OpTypeBool
|
||||||
%int_1 = OpConstant %int 1
|
%int_1 = OpConstant %int 1
|
||||||
%int_0 = OpConstant %int 0
|
|
||||||
%unused_entry_point = OpFunction %void None %1
|
%unused_entry_point = OpFunction %void None %1
|
||||||
%4 = OpLabel
|
%4 = OpLabel
|
||||||
OpReturn
|
OpReturn
|
||||||
|
@ -47,5 +46,5 @@
|
||||||
OpStore %i %25
|
OpStore %i %25
|
||||||
OpBranch %12
|
OpBranch %12
|
||||||
%13 = OpLabel
|
%13 = OpLabel
|
||||||
OpReturnValue %int_0
|
OpReturnValue %11
|
||||||
OpFunctionEnd
|
OpFunctionEnd
|
||||||
|
|
|
@ -9,5 +9,4 @@ fn f() -> i32 {
|
||||||
i = (i + 1);
|
i = (i + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,5 +13,4 @@ fn f() -> i32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,5 +18,4 @@ int f() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,5 @@ int f() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
; SPIR-V
|
; SPIR-V
|
||||||
; Version: 1.3
|
; Version: 1.3
|
||||||
; Generator: Google Tint Compiler; 0
|
; Generator: Google Tint Compiler; 0
|
||||||
; Bound: 38
|
; Bound: 37
|
||||||
; Schema: 0
|
; Schema: 0
|
||||||
OpCapability Shader
|
OpCapability Shader
|
||||||
OpMemoryModel Logical GLSL450
|
OpMemoryModel Logical GLSL450
|
||||||
|
@ -21,7 +21,6 @@
|
||||||
%int_4 = OpConstant %int 4
|
%int_4 = OpConstant %int 4
|
||||||
%bool = OpTypeBool
|
%bool = OpTypeBool
|
||||||
%int_2 = OpConstant %int 2
|
%int_2 = OpConstant %int 2
|
||||||
%int_0 = OpConstant %int 0
|
|
||||||
%unused_entry_point = OpFunction %void None %1
|
%unused_entry_point = OpFunction %void None %1
|
||||||
%4 = OpLabel
|
%4 = OpLabel
|
||||||
OpReturn
|
OpReturn
|
||||||
|
@ -68,5 +67,5 @@
|
||||||
%15 = OpLabel
|
%15 = OpLabel
|
||||||
OpBranch %13
|
OpBranch %13
|
||||||
%14 = OpLabel
|
%14 = OpLabel
|
||||||
OpReturnValue %int_0
|
OpReturnValue %11
|
||||||
OpFunctionEnd
|
OpFunctionEnd
|
||||||
|
|
|
@ -13,5 +13,4 @@ fn f() -> i32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,5 +17,4 @@ fn f() -> i32 {
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,5 +22,4 @@ int f() {
|
||||||
i = (i + 1);
|
i = (i + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,5 @@ int f() {
|
||||||
i = as_type<int>((as_type<uint>(i) + as_type<uint>(1)));
|
i = as_type<int>((as_type<uint>(i) + as_type<uint>(1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
; SPIR-V
|
; SPIR-V
|
||||||
; Version: 1.3
|
; Version: 1.3
|
||||||
; Generator: Google Tint Compiler; 0
|
; Generator: Google Tint Compiler; 0
|
||||||
; Bound: 38
|
; Bound: 37
|
||||||
; Schema: 0
|
; Schema: 0
|
||||||
OpCapability Shader
|
OpCapability Shader
|
||||||
OpMemoryModel Logical GLSL450
|
OpMemoryModel Logical GLSL450
|
||||||
|
@ -21,7 +21,6 @@
|
||||||
%bool = OpTypeBool
|
%bool = OpTypeBool
|
||||||
%int_1 = OpConstant %int 1
|
%int_1 = OpConstant %int 1
|
||||||
%int_2 = OpConstant %int 2
|
%int_2 = OpConstant %int 2
|
||||||
%int_0 = OpConstant %int 0
|
|
||||||
%unused_entry_point = OpFunction %void None %1
|
%unused_entry_point = OpFunction %void None %1
|
||||||
%4 = OpLabel
|
%4 = OpLabel
|
||||||
OpReturn
|
OpReturn
|
||||||
|
@ -68,5 +67,5 @@
|
||||||
OpStore %i %36
|
OpStore %i %36
|
||||||
OpBranch %13
|
OpBranch %13
|
||||||
%14 = OpLabel
|
%14 = OpLabel
|
||||||
OpReturnValue %int_0
|
OpReturnValue %11
|
||||||
OpFunctionEnd
|
OpFunctionEnd
|
||||||
|
|
|
@ -19,5 +19,4 @@ fn f() -> i32 {
|
||||||
i = (i + 1);
|
i = (i + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
fn f() {
|
fn f() {
|
||||||
for (var must_not_collide : i32 = 0;;) {}
|
for (var must_not_collide : i32 = 0;;) { break; }
|
||||||
var must_not_collide : i32;
|
var must_not_collide : i32;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ void unused_entry_point() {
|
||||||
void f() {
|
void f() {
|
||||||
{
|
{
|
||||||
[loop] for(int must_not_collide = 0; ; ) {
|
[loop] for(int must_not_collide = 0; ; ) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int must_not_collide = 0;
|
int must_not_collide = 0;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
using namespace metal;
|
using namespace metal;
|
||||||
void f() {
|
void f() {
|
||||||
for(int must_not_collide = 0; ; ) {
|
for(int must_not_collide = 0; ; ) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
int must_not_collide = 0;
|
int must_not_collide = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
OpLoopMerge %13 %14 None
|
OpLoopMerge %13 %14 None
|
||||||
OpBranch %15
|
OpBranch %15
|
||||||
%15 = OpLabel
|
%15 = OpLabel
|
||||||
OpBranch %14
|
OpBranch %13
|
||||||
%14 = OpLabel
|
%14 = OpLabel
|
||||||
OpBranch %12
|
OpBranch %12
|
||||||
%13 = OpLabel
|
%13 = OpLabel
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
fn f() {
|
fn f() {
|
||||||
for(var must_not_collide : i32 = 0; ; ) {
|
for(var must_not_collide : i32 = 0; ; ) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
var must_not_collide : i32;
|
var must_not_collide : i32;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
OpCapability Shader
|
|
||||||
%1 = OpExtInstImport "GLSL.std.450"
|
|
||||||
OpMemoryModel Logical GLSL450
|
|
||||||
OpEntryPoint Fragment %main "main" %_GLF_color
|
|
||||||
OpExecutionMode %main OriginUpperLeft
|
|
||||||
OpSource ESSL 310
|
|
||||||
OpName %main "main"
|
|
||||||
OpName %func_i1_ "func(i1;"
|
|
||||||
OpName %x "x"
|
|
||||||
OpName %buf0 "buf0"
|
|
||||||
OpMemberName %buf0 0 "zero"
|
|
||||||
OpName %_ ""
|
|
||||||
OpName %_GLF_color "_GLF_color"
|
|
||||||
OpName %i "i"
|
|
||||||
OpName %param "param"
|
|
||||||
OpMemberDecorate %buf0 0 Offset 0
|
|
||||||
OpDecorate %buf0 Block
|
|
||||||
OpDecorate %_ DescriptorSet 0
|
|
||||||
OpDecorate %_ Binding 0
|
|
||||||
OpDecorate %_GLF_color Location 0
|
|
||||||
%void = OpTypeVoid
|
|
||||||
%11 = OpTypeFunction %void
|
|
||||||
%int = OpTypeInt 32 1
|
|
||||||
%_ptr_Function_int = OpTypePointer Function %int
|
|
||||||
%14 = OpTypeFunction %void %_ptr_Function_int
|
|
||||||
%buf0 = OpTypeStruct %int
|
|
||||||
%_ptr_Uniform_buf0 = OpTypePointer Uniform %buf0
|
|
||||||
%_ = OpVariable %_ptr_Uniform_buf0 Uniform
|
|
||||||
%int_0 = OpConstant %int 0
|
|
||||||
%_ptr_Uniform_int = OpTypePointer Uniform %int
|
|
||||||
%bool = OpTypeBool
|
|
||||||
%int_8 = OpConstant %int 8
|
|
||||||
%float = OpTypeFloat 32
|
|
||||||
%v4float = OpTypeVector %float 4
|
|
||||||
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
||||||
%_GLF_color = OpVariable %_ptr_Output_v4float Output
|
|
||||||
%float_1 = OpConstant %float 1
|
|
||||||
%float_0 = OpConstant %float 0
|
|
||||||
%25 = OpConstantComposite %v4float %float_1 %float_0 %float_0 %float_1
|
|
||||||
%26 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
|
|
||||||
%int_10 = OpConstant %int 10
|
|
||||||
%int_1 = OpConstant %int 1
|
|
||||||
%main = OpFunction %void None %11
|
|
||||||
%29 = OpLabel
|
|
||||||
%i = OpVariable %_ptr_Function_int Function
|
|
||||||
%param = OpVariable %_ptr_Function_int Function
|
|
||||||
OpStore %_GLF_color %26
|
|
||||||
OpStore %i %int_0
|
|
||||||
OpBranch %30
|
|
||||||
%30 = OpLabel
|
|
||||||
%31 = OpPhi %int %int_0 %29 %32 %33
|
|
||||||
%34 = OpAccessChain %_ptr_Uniform_int %_ %int_0
|
|
||||||
%35 = OpLoad %int %34
|
|
||||||
%36 = OpIAdd %int %int_10 %35
|
|
||||||
%37 = OpSLessThan %bool %31 %36
|
|
||||||
OpLoopMerge %38 %33 None
|
|
||||||
OpBranchConditional %37 %33 %38
|
|
||||||
%33 = OpLabel
|
|
||||||
OpStore %param %31
|
|
||||||
%39 = OpFunctionCall %void %func_i1_ %param
|
|
||||||
%32 = OpIAdd %int %31 %int_1
|
|
||||||
OpStore %i %32
|
|
||||||
OpBranch %30
|
|
||||||
%38 = OpLabel
|
|
||||||
OpReturn
|
|
||||||
OpFunctionEnd
|
|
||||||
%func_i1_ = OpFunction %void None %14
|
|
||||||
%x = OpFunctionParameter %_ptr_Function_int
|
|
||||||
%40 = OpLabel
|
|
||||||
%41 = OpLoad %int %x
|
|
||||||
%42 = OpAccessChain %_ptr_Uniform_int %_ %int_0
|
|
||||||
%43 = OpLoad %int %42
|
|
||||||
%44 = OpSLessThan %bool %41 %43
|
|
||||||
OpSelectionMerge %45 None
|
|
||||||
OpBranchConditional %44 %46 %45
|
|
||||||
%46 = OpLabel
|
|
||||||
OpKill
|
|
||||||
%45 = OpLabel
|
|
||||||
%47 = OpLoad %int %x
|
|
||||||
%48 = OpSGreaterThan %bool %47 %int_8
|
|
||||||
OpSelectionMerge %49 None
|
|
||||||
OpBranchConditional %48 %50 %51
|
|
||||||
%50 = OpLabel
|
|
||||||
OpStore %_GLF_color %25
|
|
||||||
OpBranch %49
|
|
||||||
%51 = OpLabel
|
|
||||||
OpStore %_GLF_color %26
|
|
||||||
OpBranch %49
|
|
||||||
%49 = OpLabel
|
|
||||||
OpReturn
|
|
||||||
OpFunctionEnd
|
|
|
@ -1,60 +0,0 @@
|
||||||
[[block]]
|
|
||||||
struct buf0 {
|
|
||||||
zero : i32;
|
|
||||||
};
|
|
||||||
|
|
||||||
[[group(0), binding(0)]] var<uniform> x_7 : buf0;
|
|
||||||
|
|
||||||
var<private> x_GLF_color : vec4<f32>;
|
|
||||||
|
|
||||||
fn func_i1_(x : ptr<function, i32>) {
|
|
||||||
let x_41 : i32 = *(x);
|
|
||||||
let x_43 : i32 = x_7.zero;
|
|
||||||
if ((x_41 < x_43)) {
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
let x_47 : i32 = *(x);
|
|
||||||
if ((x_47 > 8)) {
|
|
||||||
x_GLF_color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
|
||||||
} else {
|
|
||||||
x_GLF_color = vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main_1() {
|
|
||||||
var i : i32;
|
|
||||||
var param : i32;
|
|
||||||
var x_31_phi : i32;
|
|
||||||
x_GLF_color = vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
|
||||||
i = 0;
|
|
||||||
x_31_phi = 0;
|
|
||||||
loop {
|
|
||||||
let x_31 : i32 = x_31_phi;
|
|
||||||
let x_35 : i32 = x_7.zero;
|
|
||||||
if ((x_31 < (10 + x_35))) {
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
continuing {
|
|
||||||
param = x_31;
|
|
||||||
func_i1_(&(param));
|
|
||||||
let x_32 : i32 = (x_31 + 1);
|
|
||||||
i = x_32;
|
|
||||||
x_31_phi = x_32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct main_out {
|
|
||||||
[[location(0)]]
|
|
||||||
x_GLF_color_1 : vec4<f32>;
|
|
||||||
};
|
|
||||||
|
|
||||||
[[stage(fragment)]]
|
|
||||||
fn main() -> main_out {
|
|
||||||
main_1();
|
|
||||||
return main_out(x_GLF_color);
|
|
||||||
}
|
|
|
@ -1,123 +0,0 @@
|
||||||
OpCapability Shader
|
|
||||||
%1 = OpExtInstImport "GLSL.std.450"
|
|
||||||
OpMemoryModel Logical GLSL450
|
|
||||||
OpEntryPoint Fragment %main "main" %gl_FragCoord %_GLF_color
|
|
||||||
OpExecutionMode %main OriginUpperLeft
|
|
||||||
OpSource ESSL 310
|
|
||||||
OpName %main "main"
|
|
||||||
OpName %func_f1_ "func(f1;"
|
|
||||||
OpName %x "x"
|
|
||||||
OpName %gl_FragCoord "gl_FragCoord"
|
|
||||||
OpName %f "f"
|
|
||||||
OpName %i "i"
|
|
||||||
OpName %buf0 "buf0"
|
|
||||||
OpMemberName %buf0 0 "five"
|
|
||||||
OpName %_ ""
|
|
||||||
OpName %param "param"
|
|
||||||
OpName %_GLF_color "_GLF_color"
|
|
||||||
OpDecorate %gl_FragCoord BuiltIn FragCoord
|
|
||||||
OpMemberDecorate %buf0 0 Offset 0
|
|
||||||
OpDecorate %buf0 Block
|
|
||||||
OpDecorate %_ DescriptorSet 0
|
|
||||||
OpDecorate %_ Binding 0
|
|
||||||
OpDecorate %_GLF_color Location 0
|
|
||||||
%void = OpTypeVoid
|
|
||||||
%13 = OpTypeFunction %void
|
|
||||||
%float = OpTypeFloat 32
|
|
||||||
%_ptr_Function_float = OpTypePointer Function %float
|
|
||||||
%16 = OpTypeFunction %float %_ptr_Function_float
|
|
||||||
%float_5 = OpConstant %float 5
|
|
||||||
%bool = OpTypeBool
|
|
||||||
%v4float = OpTypeVector %float 4
|
|
||||||
%_ptr_Input_v4float = OpTypePointer Input %v4float
|
|
||||||
%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
|
|
||||||
%uint = OpTypeInt 32 0
|
|
||||||
%uint_0 = OpConstant %uint 0
|
|
||||||
%_ptr_Input_float = OpTypePointer Input %float
|
|
||||||
%float_0_5 = OpConstant %float 0.5
|
|
||||||
%uint_1 = OpConstant %uint 1
|
|
||||||
%float_1 = OpConstant %float 1
|
|
||||||
%float_0 = OpConstant %float 0
|
|
||||||
%int = OpTypeInt 32 1
|
|
||||||
%_ptr_Function_int = OpTypePointer Function %int
|
|
||||||
%int_0 = OpConstant %int 0
|
|
||||||
%buf0 = OpTypeStruct %int
|
|
||||||
%_ptr_Uniform_buf0 = OpTypePointer Uniform %buf0
|
|
||||||
%_ = OpVariable %_ptr_Uniform_buf0 Uniform
|
|
||||||
%_ptr_Uniform_int = OpTypePointer Uniform %int
|
|
||||||
%int_1 = OpConstant %int 1
|
|
||||||
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
||||||
%_GLF_color = OpVariable %_ptr_Output_v4float Output
|
|
||||||
%35 = OpConstantComposite %v4float %float_1 %float_0 %float_0 %float_1
|
|
||||||
%36 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
|
|
||||||
%main = OpFunction %void None %13
|
|
||||||
%37 = OpLabel
|
|
||||||
%f = OpVariable %_ptr_Function_float Function
|
|
||||||
%i = OpVariable %_ptr_Function_int Function
|
|
||||||
%param = OpVariable %_ptr_Function_float Function
|
|
||||||
OpStore %f %float_0
|
|
||||||
OpStore %i %int_0
|
|
||||||
OpBranch %38
|
|
||||||
%38 = OpLabel
|
|
||||||
%39 = OpLoad %int %i
|
|
||||||
%40 = OpAccessChain %_ptr_Uniform_int %_ %int_0
|
|
||||||
%41 = OpLoad %int %40
|
|
||||||
%42 = OpSLessThan %bool %39 %41
|
|
||||||
OpLoopMerge %43 %44 None
|
|
||||||
OpBranchConditional %42 %44 %43
|
|
||||||
%44 = OpLabel
|
|
||||||
%45 = OpLoad %int %i
|
|
||||||
%46 = OpConvertSToF %float %45
|
|
||||||
OpStore %param %46
|
|
||||||
%47 = OpFunctionCall %float %func_f1_ %param
|
|
||||||
OpStore %f %47
|
|
||||||
%48 = OpLoad %int %i
|
|
||||||
%49 = OpIAdd %int %48 %int_1
|
|
||||||
OpStore %i %49
|
|
||||||
OpBranch %38
|
|
||||||
%43 = OpLabel
|
|
||||||
%50 = OpLoad %float %f
|
|
||||||
%51 = OpFOrdEqual %bool %50 %float_5
|
|
||||||
OpSelectionMerge %52 None
|
|
||||||
OpBranchConditional %51 %53 %54
|
|
||||||
%53 = OpLabel
|
|
||||||
OpStore %_GLF_color %35
|
|
||||||
OpBranch %52
|
|
||||||
%54 = OpLabel
|
|
||||||
OpStore %_GLF_color %36
|
|
||||||
OpBranch %52
|
|
||||||
%52 = OpLabel
|
|
||||||
OpReturn
|
|
||||||
OpFunctionEnd
|
|
||||||
%func_f1_ = OpFunction %float None %16
|
|
||||||
%x = OpFunctionParameter %_ptr_Function_float
|
|
||||||
%55 = OpLabel
|
|
||||||
%56 = OpLoad %float %x
|
|
||||||
%57 = OpFOrdGreaterThan %bool %56 %float_5
|
|
||||||
OpSelectionMerge %58 None
|
|
||||||
OpBranchConditional %57 %59 %58
|
|
||||||
%59 = OpLabel
|
|
||||||
%60 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0
|
|
||||||
%61 = OpLoad %float %60
|
|
||||||
%62 = OpFOrdLessThan %bool %61 %float_0_5
|
|
||||||
OpSelectionMerge %63 None
|
|
||||||
OpBranchConditional %62 %64 %65
|
|
||||||
%64 = OpLabel
|
|
||||||
OpKill
|
|
||||||
%65 = OpLabel
|
|
||||||
%66 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_1
|
|
||||||
%67 = OpLoad %float %66
|
|
||||||
%68 = OpFOrdLessThan %bool %67 %float_0_5
|
|
||||||
OpSelectionMerge %69 None
|
|
||||||
OpBranchConditional %68 %70 %69
|
|
||||||
%70 = OpLabel
|
|
||||||
OpKill
|
|
||||||
%69 = OpLabel
|
|
||||||
OpBranch %63
|
|
||||||
%63 = OpLabel
|
|
||||||
OpBranch %58
|
|
||||||
%58 = OpLabel
|
|
||||||
%71 = OpLoad %float %x
|
|
||||||
%72 = OpFAdd %float %71 %float_1
|
|
||||||
OpReturnValue %72
|
|
||||||
OpFunctionEnd
|
|
|
@ -1,71 +0,0 @@
|
||||||
[[block]]
|
|
||||||
struct buf0 {
|
|
||||||
five : i32;
|
|
||||||
};
|
|
||||||
|
|
||||||
var<private> gl_FragCoord : vec4<f32>;
|
|
||||||
|
|
||||||
[[group(0), binding(0)]] var<uniform> x_10 : buf0;
|
|
||||||
|
|
||||||
var<private> x_GLF_color : vec4<f32>;
|
|
||||||
|
|
||||||
fn func_f1_(x : ptr<function, f32>) -> f32 {
|
|
||||||
let x_56 : f32 = *(x);
|
|
||||||
if ((x_56 > 5.0)) {
|
|
||||||
let x_61 : f32 = gl_FragCoord.x;
|
|
||||||
if ((x_61 < 0.5)) {
|
|
||||||
discard;
|
|
||||||
} else {
|
|
||||||
let x_67 : f32 = gl_FragCoord.y;
|
|
||||||
if ((x_67 < 0.5)) {
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let x_71 : f32 = *(x);
|
|
||||||
return (x_71 + 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main_1() {
|
|
||||||
var f : f32;
|
|
||||||
var i : i32;
|
|
||||||
var param : f32;
|
|
||||||
f = 0.0;
|
|
||||||
i = 0;
|
|
||||||
loop {
|
|
||||||
let x_39 : i32 = i;
|
|
||||||
let x_41 : i32 = x_10.five;
|
|
||||||
if ((x_39 < x_41)) {
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
continuing {
|
|
||||||
let x_45 : i32 = i;
|
|
||||||
param = f32(x_45);
|
|
||||||
let x_47 : f32 = func_f1_(&(param));
|
|
||||||
f = x_47;
|
|
||||||
let x_48 : i32 = i;
|
|
||||||
i = (x_48 + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let x_50 : f32 = f;
|
|
||||||
if ((x_50 == 5.0)) {
|
|
||||||
x_GLF_color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
|
||||||
} else {
|
|
||||||
x_GLF_color = vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct main_out {
|
|
||||||
[[location(0)]]
|
|
||||||
x_GLF_color_1 : vec4<f32>;
|
|
||||||
};
|
|
||||||
|
|
||||||
[[stage(fragment)]]
|
|
||||||
fn main([[builtin(position)]] gl_FragCoord_param : vec4<f32>) -> main_out {
|
|
||||||
gl_FragCoord = gl_FragCoord_param;
|
|
||||||
main_1();
|
|
||||||
return main_out(x_GLF_color);
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
OpCapability Shader
|
|
||||||
%1 = OpExtInstImport "GLSL.std.450"
|
|
||||||
OpMemoryModel Logical GLSL450
|
|
||||||
OpEntryPoint Fragment %main "main" %_GLF_color
|
|
||||||
OpExecutionMode %main OriginUpperLeft
|
|
||||||
OpSource ESSL 310
|
|
||||||
OpName %main "main"
|
|
||||||
OpName %mand_ "mand("
|
|
||||||
OpName %k "k"
|
|
||||||
OpName %buf0 "buf0"
|
|
||||||
OpMemberName %buf0 0 "injectionSwitch"
|
|
||||||
OpName %_ ""
|
|
||||||
OpName %j "j"
|
|
||||||
OpName %_GLF_color "_GLF_color"
|
|
||||||
OpDecorate %k RelaxedPrecision
|
|
||||||
OpMemberDecorate %buf0 0 Offset 0
|
|
||||||
OpDecorate %buf0 Block
|
|
||||||
OpDecorate %_ DescriptorSet 0
|
|
||||||
OpDecorate %_ Binding 0
|
|
||||||
OpDecorate %j RelaxedPrecision
|
|
||||||
OpDecorate %_GLF_color Location 0
|
|
||||||
OpDecorate %int_0 RelaxedPrecision
|
|
||||||
%void = OpTypeVoid
|
|
||||||
%11 = OpTypeFunction %void
|
|
||||||
%float = OpTypeFloat 32
|
|
||||||
%v3float = OpTypeVector %float 3
|
|
||||||
%14 = OpTypeFunction %v3float
|
|
||||||
%int = OpTypeInt 32 1
|
|
||||||
%_ptr_Function_int = OpTypePointer Function %int
|
|
||||||
%int_0 = OpConstant %int 0
|
|
||||||
%int_1000 = OpConstant %int 1000
|
|
||||||
%bool = OpTypeBool
|
|
||||||
%int_1 = OpConstant %int 1
|
|
||||||
%float_1 = OpConstant %float 1
|
|
||||||
%21 = OpConstantComposite %v3float %float_1 %float_1 %float_1
|
|
||||||
%v2float = OpTypeVector %float 2
|
|
||||||
%buf0 = OpTypeStruct %v2float
|
|
||||||
%_ptr_Uniform_buf0 = OpTypePointer Uniform %buf0
|
|
||||||
%_ = OpVariable %_ptr_Uniform_buf0 Uniform
|
|
||||||
%uint = OpTypeInt 32 0
|
|
||||||
%uint_0 = OpConstant %uint 0
|
|
||||||
%_ptr_Uniform_float = OpTypePointer Uniform %float
|
|
||||||
%uint_1 = OpConstant %uint 1
|
|
||||||
%int_4 = OpConstant %int 4
|
|
||||||
%v4float = OpTypeVector %float 4
|
|
||||||
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
||||||
%_GLF_color = OpVariable %_ptr_Output_v4float Output
|
|
||||||
%float_0 = OpConstant %float 0
|
|
||||||
%32 = OpConstantComposite %v4float %float_1 %float_0 %float_0 %float_1
|
|
||||||
%33 = OpUndef %int
|
|
||||||
%true = OpConstantTrue %bool
|
|
||||||
%main = OpFunction %void None %11
|
|
||||||
%35 = OpLabel
|
|
||||||
%j = OpVariable %_ptr_Function_int Function
|
|
||||||
%36 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %uint_0
|
|
||||||
%37 = OpLoad %float %36
|
|
||||||
%38 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %uint_1
|
|
||||||
%39 = OpLoad %float %38
|
|
||||||
%40 = OpFOrdGreaterThan %bool %37 %39
|
|
||||||
OpSelectionMerge %41 None
|
|
||||||
OpBranchConditional %40 %42 %41
|
|
||||||
%42 = OpLabel
|
|
||||||
OpStore %j %int_0
|
|
||||||
OpBranch %43
|
|
||||||
%43 = OpLabel
|
|
||||||
OpLoopMerge %44 %45 None
|
|
||||||
OpBranchConditional %true %45 %44
|
|
||||||
%45 = OpLabel
|
|
||||||
%46 = OpFunctionCall %v3float %mand_
|
|
||||||
OpBranch %43
|
|
||||||
%44 = OpLabel
|
|
||||||
OpBranch %41
|
|
||||||
%41 = OpLabel
|
|
||||||
OpStore %_GLF_color %32
|
|
||||||
OpReturn
|
|
||||||
OpFunctionEnd
|
|
||||||
%mand_ = OpFunction %v3float None %14
|
|
||||||
%47 = OpLabel
|
|
||||||
%k = OpVariable %_ptr_Function_int Function
|
|
||||||
OpStore %k %int_0
|
|
||||||
OpBranch %48
|
|
||||||
%48 = OpLabel
|
|
||||||
OpLoopMerge %49 %50 None
|
|
||||||
OpBranchConditional %true %51 %49
|
|
||||||
%51 = OpLabel
|
|
||||||
OpKill
|
|
||||||
%50 = OpLabel
|
|
||||||
OpBranch %48
|
|
||||||
%49 = OpLabel
|
|
||||||
OpReturnValue %21
|
|
||||||
OpFunctionEnd
|
|
|
@ -1,52 +0,0 @@
|
||||||
SKIP: FAILED
|
|
||||||
|
|
||||||
cbuffer cbuffer_x_7 : register(b0, space0) {
|
|
||||||
uint4 x_7[1];
|
|
||||||
};
|
|
||||||
static float4 x_GLF_color = float4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
||||||
|
|
||||||
float3 mand_() {
|
|
||||||
int k = 0;
|
|
||||||
k = 0;
|
|
||||||
while (true) {
|
|
||||||
if (true) {
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
return float3(1.0f, 1.0f, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void main_1() {
|
|
||||||
int j = 0;
|
|
||||||
const float x_37 = asfloat(x_7[0].x);
|
|
||||||
const float x_39 = asfloat(x_7[0].y);
|
|
||||||
if ((x_37 > x_39)) {
|
|
||||||
j = 0;
|
|
||||||
{
|
|
||||||
for(; true; const float3 x_46 = mand_()) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
x_GLF_color = float4(1.0f, 0.0f, 0.0f, 1.0f);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct main_out {
|
|
||||||
float4 x_GLF_color_1;
|
|
||||||
};
|
|
||||||
struct tint_symbol {
|
|
||||||
float4 x_GLF_color_1 : SV_Target0;
|
|
||||||
};
|
|
||||||
|
|
||||||
tint_symbol main() {
|
|
||||||
main_1();
|
|
||||||
const main_out tint_symbol_1 = {x_GLF_color};
|
|
||||||
const tint_symbol tint_symbol_3 = {tint_symbol_1.x_GLF_color_1};
|
|
||||||
return tint_symbol_3;
|
|
||||||
}
|
|
||||||
C:\src\tint\test\Shader@0x00000294138DDB00(26,19-23): error X3000: syntax error: unexpected token 'const'
|
|
||||||
C:\src\tint\test\Shader@0x00000294138DDB00(41,17): error X3000: syntax error: unexpected token '('
|
|
||||||
C:\src\tint\test\Shader@0x00000294138DDB00(45,3-23): error X3079: 'main_1': void functions cannot return a value
|
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
[[block]]
|
|
||||||
struct buf0 {
|
|
||||||
injectionSwitch : vec2<f32>;
|
|
||||||
};
|
|
||||||
|
|
||||||
[[group(0), binding(0)]] var<uniform> x_7 : buf0;
|
|
||||||
|
|
||||||
var<private> x_GLF_color : vec4<f32>;
|
|
||||||
|
|
||||||
fn mand_() -> vec3<f32> {
|
|
||||||
var k : i32;
|
|
||||||
k = 0;
|
|
||||||
loop {
|
|
||||||
if (true) {
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
return vec3<f32>(1.0, 1.0, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main_1() {
|
|
||||||
var j : i32;
|
|
||||||
let x_37 : f32 = x_7.injectionSwitch.x;
|
|
||||||
let x_39 : f32 = x_7.injectionSwitch.y;
|
|
||||||
if ((x_37 > x_39)) {
|
|
||||||
j = 0;
|
|
||||||
loop {
|
|
||||||
if (true) {
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
continuing {
|
|
||||||
let x_46 : vec3<f32> = mand_();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
x_GLF_color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct main_out {
|
|
||||||
[[location(0)]]
|
|
||||||
x_GLF_color_1 : vec4<f32>;
|
|
||||||
};
|
|
||||||
|
|
||||||
[[stage(fragment)]]
|
|
||||||
fn main() -> main_out {
|
|
||||||
main_1();
|
|
||||||
return main_out(x_GLF_color);
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
SKIP: FAILED
|
|
||||||
|
|
||||||
cbuffer cbuffer_x_7 : register(b0, space0) {
|
|
||||||
uint4 x_7[1];
|
|
||||||
};
|
|
||||||
static float4 x_GLF_color = float4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
||||||
|
|
||||||
float3 mand_() {
|
|
||||||
int k = 0;
|
|
||||||
k = 0;
|
|
||||||
while (true) {
|
|
||||||
if (true) {
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
return float3(1.0f, 1.0f, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void main_1() {
|
|
||||||
int j = 0;
|
|
||||||
const float x_37 = asfloat(x_7[0].x);
|
|
||||||
const float x_39 = asfloat(x_7[0].y);
|
|
||||||
if ((x_37 > x_39)) {
|
|
||||||
j = 0;
|
|
||||||
{
|
|
||||||
for(; true; const float3 x_46 = mand_()) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
x_GLF_color = float4(1.0f, 0.0f, 0.0f, 1.0f);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct main_out {
|
|
||||||
float4 x_GLF_color_1;
|
|
||||||
};
|
|
||||||
struct tint_symbol {
|
|
||||||
float4 x_GLF_color_1 : SV_Target0;
|
|
||||||
};
|
|
||||||
|
|
||||||
tint_symbol main() {
|
|
||||||
main_1();
|
|
||||||
const main_out tint_symbol_1 = {x_GLF_color};
|
|
||||||
const tint_symbol tint_symbol_3 = {tint_symbol_1.x_GLF_color_1};
|
|
||||||
return tint_symbol_3;
|
|
||||||
}
|
|
||||||
C:\src\tint\test\Shader@0x000001DB32C08F00(26,19-23): error X3000: syntax error: unexpected token 'const'
|
|
||||||
C:\src\tint\test\Shader@0x000001DB32C08F00(41,17): error X3000: syntax error: unexpected token '('
|
|
||||||
C:\src\tint\test\Shader@0x000001DB32C08F00(45,3-23): error X3079: 'main_1': void functions cannot return a value
|
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
OpCapability Shader
|
|
||||||
%1 = OpExtInstImport "GLSL.std.450"
|
|
||||||
OpMemoryModel Logical GLSL450
|
|
||||||
OpEntryPoint Fragment %main "main" %_GLF_color
|
|
||||||
OpExecutionMode %main OriginUpperLeft
|
|
||||||
OpSource ESSL 310
|
|
||||||
OpName %main "main"
|
|
||||||
OpName %mand_ "mand("
|
|
||||||
OpName %k "k"
|
|
||||||
OpName %_GLF_color "_GLF_color"
|
|
||||||
OpName %i "i"
|
|
||||||
OpDecorate %k RelaxedPrecision
|
|
||||||
OpDecorate %7 RelaxedPrecision
|
|
||||||
OpDecorate %_GLF_color Location 0
|
|
||||||
OpDecorate %i RelaxedPrecision
|
|
||||||
OpDecorate %8 RelaxedPrecision
|
|
||||||
OpDecorate %9 RelaxedPrecision
|
|
||||||
OpDecorate %10 RelaxedPrecision
|
|
||||||
%void = OpTypeVoid
|
|
||||||
%12 = OpTypeFunction %void
|
|
||||||
%float = OpTypeFloat 32
|
|
||||||
%v3float = OpTypeVector %float 3
|
|
||||||
%15 = OpTypeFunction %v3float
|
|
||||||
%int = OpTypeInt 32 1
|
|
||||||
%_ptr_Function_int = OpTypePointer Function %int
|
|
||||||
%int_0 = OpConstant %int 0
|
|
||||||
%int_1000 = OpConstant %int 1000
|
|
||||||
%bool = OpTypeBool
|
|
||||||
%float_1 = OpConstant %float 1
|
|
||||||
%22 = OpConstantComposite %v3float %float_1 %float_1 %float_1
|
|
||||||
%int_1 = OpConstant %int 1
|
|
||||||
%v4float = OpTypeVector %float 4
|
|
||||||
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
||||||
%_GLF_color = OpVariable %_ptr_Output_v4float Output
|
|
||||||
%float_0 = OpConstant %float 0
|
|
||||||
%27 = OpConstantComposite %v4float %float_1 %float_0 %float_0 %float_1
|
|
||||||
%int_4 = OpConstant %int 4
|
|
||||||
%_ptr_Function_v3float = OpTypePointer Function %v3float
|
|
||||||
%false = OpConstantFalse %bool
|
|
||||||
%_ptr_Function_bool = OpTypePointer Function %bool
|
|
||||||
%true = OpConstantTrue %bool
|
|
||||||
%main = OpFunction %void None %12
|
|
||||||
%33 = OpLabel
|
|
||||||
%i = OpVariable %_ptr_Function_int Function
|
|
||||||
OpStore %_GLF_color %27
|
|
||||||
OpStore %i %int_0
|
|
||||||
OpBranch %34
|
|
||||||
%34 = OpLabel
|
|
||||||
%8 = OpLoad %int %i
|
|
||||||
%35 = OpSLessThan %bool %8 %int_4
|
|
||||||
OpLoopMerge %36 %37 None
|
|
||||||
OpBranchConditional %35 %37 %36
|
|
||||||
%37 = OpLabel
|
|
||||||
%38 = OpFunctionCall %v3float %mand_
|
|
||||||
%9 = OpLoad %int %i
|
|
||||||
%10 = OpIAdd %int %9 %int_1
|
|
||||||
OpStore %i %10
|
|
||||||
OpBranch %34
|
|
||||||
%36 = OpLabel
|
|
||||||
OpReturn
|
|
||||||
OpFunctionEnd
|
|
||||||
%mand_ = OpFunction %v3float None %15
|
|
||||||
%39 = OpLabel
|
|
||||||
%40 = OpVariable %_ptr_Function_bool Function %false
|
|
||||||
%41 = OpVariable %_ptr_Function_v3float Function
|
|
||||||
%k = OpVariable %_ptr_Function_int Function
|
|
||||||
OpBranch %42
|
|
||||||
%42 = OpLabel
|
|
||||||
OpStore %k %int_0
|
|
||||||
OpLoopMerge %43 %44 None
|
|
||||||
OpBranch %45
|
|
||||||
%45 = OpLabel
|
|
||||||
%7 = OpLoad %int %k
|
|
||||||
%46 = OpSLessThan %bool %7 %int_1000
|
|
||||||
OpLoopMerge %47 %48 None
|
|
||||||
OpBranchConditional %46 %49 %47
|
|
||||||
%49 = OpLabel
|
|
||||||
OpStore %40 %true
|
|
||||||
OpStore %41 %22
|
|
||||||
OpBranch %47
|
|
||||||
%48 = OpLabel
|
|
||||||
OpBranch %45
|
|
||||||
%47 = OpLabel
|
|
||||||
%50 = OpLoad %bool %40
|
|
||||||
OpSelectionMerge %51 None
|
|
||||||
OpBranchConditional %50 %43 %51
|
|
||||||
%51 = OpLabel
|
|
||||||
OpKill
|
|
||||||
%44 = OpLabel
|
|
||||||
OpBranch %42
|
|
||||||
%43 = OpLabel
|
|
||||||
%52 = OpLoad %v3float %41
|
|
||||||
OpReturnValue %52
|
|
||||||
OpFunctionEnd
|
|
|
@ -1,58 +0,0 @@
|
||||||
var<private> x_GLF_color : vec4<f32>;
|
|
||||||
|
|
||||||
fn mand_() -> vec3<f32> {
|
|
||||||
var x_40 : bool = false;
|
|
||||||
var x_41 : vec3<f32>;
|
|
||||||
var k : i32;
|
|
||||||
loop {
|
|
||||||
k = 0;
|
|
||||||
loop {
|
|
||||||
let x_7 : i32 = k;
|
|
||||||
if ((x_7 < 1000)) {
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
x_40 = true;
|
|
||||||
x_41 = vec3<f32>(1.0, 1.0, 1.0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let x_50 : bool = x_40;
|
|
||||||
if (x_50) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
let x_52 : vec3<f32> = x_41;
|
|
||||||
return x_52;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main_1() {
|
|
||||||
var i : i32;
|
|
||||||
x_GLF_color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
|
||||||
i = 0;
|
|
||||||
loop {
|
|
||||||
let x_8 : i32 = i;
|
|
||||||
if ((x_8 < 4)) {
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
continuing {
|
|
||||||
let x_38 : vec3<f32> = mand_();
|
|
||||||
let x_9 : i32 = i;
|
|
||||||
i = (x_9 + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct main_out {
|
|
||||||
[[location(0)]]
|
|
||||||
x_GLF_color_1 : vec4<f32>;
|
|
||||||
};
|
|
||||||
|
|
||||||
[[stage(fragment)]]
|
|
||||||
fn main() -> main_out {
|
|
||||||
main_1();
|
|
||||||
return main_out(x_GLF_color);
|
|
||||||
}
|
|
Loading…
Reference in New Issue