Fix issue with break inside switch inside continuing.
Tint is currently too restrictive in testing for a break statement
inside of a continuing block. In the case where the break is inside a
switch it is permissible, as it breaks the switch, not the loop.
This CL updates the validation routines to allow a switch to hold a
break statement inside a continuing block.
Bug: tint:2144
Change-Id: I82850cb736d1c2433fd98f2a2cd9bfd325d698d7
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/175080
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/lang/wgsl/resolver/validation_test.cc b/src/tint/lang/wgsl/resolver/validation_test.cc
index 6bc6fc2..ba5da4a 100644
--- a/src/tint/lang/wgsl/resolver/validation_test.cc
+++ b/src/tint/lang/wgsl/resolver/validation_test.cc
@@ -994,6 +994,22 @@
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
+TEST_F(ResolverValidationTest, Stmt_BreakInSwitchInContinuing) {
+ // loop {
+ // continuing {
+ // switch(1) {
+ // default:
+ // break;
+ // }
+ // }
+ // }
+
+ auto* cont = Block(Switch(1_i, DefaultCase(Block(Break()))));
+
+ WrapInFunction(Loop(Block(Break()), cont));
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
TEST_F(ResolverValidationTest, Stmt_BreakInIfTrueInContinuing) {
auto* cont = Block( // continuing {
If(true, Block( // if(true) {
diff --git a/src/tint/lang/wgsl/resolver/validator.cc b/src/tint/lang/wgsl/resolver/validator.cc
index 8e54a74..3cf40c0 100644
--- a/src/tint/lang/wgsl/resolver/validator.cc
+++ b/src/tint/lang/wgsl/resolver/validator.cc
@@ -266,11 +266,15 @@
}
const ast::Statement* Validator::ClosestContinuing(bool stop_at_loop,
+ bool stop_at_switch,
sem::Statement* current_statement) const {
for (const auto* s = current_statement; s != nullptr; s = s->Parent()) {
if (stop_at_loop && s->Is<sem::LoopStatement>()) {
break;
}
+ if (stop_at_switch && s->Is<sem::SwitchStatement>()) {
+ break;
+ }
if (s->Is<sem::LoopContinuingBlockStatement>()) {
return s->Declaration();
}
@@ -1583,7 +1587,8 @@
AddError("break statement must be in a loop or switch case", stmt->Declaration()->source);
return false;
}
- if (ClosestContinuing(/*stop_at_loop*/ true, current_statement) != nullptr) {
+ if (ClosestContinuing(/*stop_at_loop*/ true, /* stop_at_switch */ true, current_statement) !=
+ nullptr) {
AddError(
"`break` must not be used to exit from a continuing block. Use `break-if` instead.",
stmt->Declaration()->source);
@@ -1594,7 +1599,8 @@
bool Validator::ContinueStatement(const sem::Statement* stmt,
sem::Statement* current_statement) const {
- if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ true, current_statement)) {
+ if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ true, /* stop_at_switch */ false,
+ current_statement)) {
AddError("continuing blocks must not contain a continue statement",
stmt->Declaration()->source);
if (continuing != stmt->Declaration() && continuing != stmt->Parent()->Declaration()) {
@@ -2463,7 +2469,8 @@
}
auto* sem = sem_.Get(ret);
- if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ false, current_statement)) {
+ if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ false, /* stop_at_switch */ false,
+ current_statement)) {
AddError("continuing blocks must not contain a return statement", ret->source);
if (continuing != sem->Declaration() && continuing != sem->Parent()->Declaration()) {
AddNote("see continuing block here", continuing->source);
diff --git a/src/tint/lang/wgsl/resolver/validator.h b/src/tint/lang/wgsl/resolver/validator.h
index 92ccf7c..fc42824 100644
--- a/src/tint/lang/wgsl/resolver/validator.h
+++ b/src/tint/lang/wgsl/resolver/validator.h
@@ -579,8 +579,11 @@
/// (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.
+ /// @param stop_at_switch if true then the function will return nullptr if a switch was found
+ /// before continuing.
/// @param current_statement the current statement being resolved
const ast::Statement* ClosestContinuing(bool stop_at_loop,
+ bool stop_at_switch,
sem::Statement* current_statement) const;
/// Returns a human-readable string representation of the vector type name