Add break-if support.

This CL adds support for `break-if` to Tint.

Bug: tint:1633, tint:1451
Change-Id: I30dfd62a3e09255624ff76ebe0cdd3a3c7cf9c5f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/106420
Auto-Submit: Dan Sinclair <dsinclair@chromium.org>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: dan sinclair <dsinclair@google.com>
This commit is contained in:
dan sinclair
2022-10-20 22:45:50 +00:00
committed by Dawn LUCI CQ
parent f8c07d4753
commit b8b0c21918
49 changed files with 983 additions and 74 deletions

View File

@@ -19,6 +19,7 @@
#include "src/tint/ast/array.h"
#include "src/tint/ast/assignment_statement.h"
#include "src/tint/ast/bitcast_expression.h"
#include "src/tint/ast/break_if_statement.h"
#include "src/tint/ast/break_statement.h"
#include "src/tint/ast/call_statement.h"
#include "src/tint/ast/continue_statement.h"
@@ -2265,8 +2266,7 @@ ForHeader::ForHeader(const ast::Statement* init,
ForHeader::~ForHeader() = default;
// (variable_statement | variable_updating_statement |
// func_call_statement)?
// (variable_statement | variable_updating_statement | func_call_statement)?
Maybe<const ast::Statement*> ParserImpl::for_header_initializer() {
auto call = func_call_statement();
if (call.errored) {
@@ -2317,10 +2317,7 @@ Maybe<const ast::Statement*> ParserImpl::for_header_continuing() {
}
// for_header
// : (variable_statement | variable_updating_statement | func_call_statement)?
// SEMICOLON
// expression? SEMICOLON
// (variable_updating_statement | func_call_statement)?
// : for_header_initializer? SEMICOLON expression? SEMICOLON for_header_continuing?
Expect<std::unique_ptr<ForHeader>> ParserImpl::expect_for_header() {
auto initializer = for_header_initializer();
if (initializer.errored) {
@@ -2444,28 +2441,58 @@ Maybe<const ast::ContinueStatement*> ParserImpl::continue_statement() {
// break_if_statement:
// 'break' 'if' expression semicolon
Maybe<const ast::Statement*> ParserImpl::break_if_statement() {
// TODO(crbug.com/tint/1451): Add support for break-if
return Failure::kNoMatch;
auto& t1 = peek();
auto& t2 = peek(1);
// Match both the `break` and `if` at the same time.
if (!t1.Is(Token::Type::kBreak) || !t2.Is(Token::Type::kIf)) {
return Failure::kNoMatch;
}
next(); // Consume the peek
next(); // Consume the peek
auto expr = expression();
if (expr.errored) {
return Failure::kErrored;
}
if (!expr.matched) {
return add_error(t1, "expected expression for `break if`");
}
if (!match(Token::Type::kSemicolon)) {
return add_error(peek(), "expected ';' for `break if` statement");
}
return create<ast::BreakIfStatement>(t1.source(), expr.value);
}
// continuing_compound_statement:
// brace_left statement* break_if_statement? brace_right
Maybe<const ast::BlockStatement*> ParserImpl::continuing_compound_statement() {
return expect_brace_block("", [&]() -> Expect<ast::BlockStatement*> {
auto stmts = expect_statements();
if (stmts.errored) {
return Failure::kErrored;
StatementList stmts;
while (continue_parsing()) {
// Note, break-if has to parse before statements because statements includes `break`
auto break_if = break_if_statement();
if (break_if.errored) {
return Failure::kErrored;
}
if (break_if.matched) {
stmts.Push(break_if.value);
continue;
}
auto stmt = statement();
if (stmt.errored) {
return Failure::kErrored;
}
if (!stmt.matched) {
break;
}
stmts.Push(stmt.value);
}
auto break_if = break_if_statement();
if (break_if.errored) {
return Failure::kErrored;
}
if (break_if.matched) {
stmts.value.Push(break_if.value);
}
return create<ast::BlockStatement>(Source{}, stmts.value);
return create<ast::BlockStatement>(Source{}, stmts);
});
}

View File

@@ -110,5 +110,57 @@ TEST_F(ParserImplTest, LoopStmt_InvalidContinuing) {
EXPECT_EQ(p->error(), "1:29: expected ';' for discard statement");
}
TEST_F(ParserImplTest, LoopStmt_Continuing_BreakIf) {
auto p = parser("loop { continuing { break if 1 + 2 < 5; }}");
auto e = p->loop_statement();
EXPECT_TRUE(e.matched);
EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_NE(e.value, nullptr);
ASSERT_EQ(e->body->statements.Length(), 0u);
ASSERT_EQ(e->continuing->statements.Length(), 1u);
EXPECT_TRUE(e->continuing->statements[0]->Is<ast::BreakIfStatement>());
}
TEST_F(ParserImplTest, LoopStmt_Continuing_BreakIf_MissingExpr) {
auto p = parser("loop { continuing { break if; }}");
auto e = p->loop_statement();
EXPECT_FALSE(e.matched);
EXPECT_TRUE(e.errored);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(e.value, nullptr);
EXPECT_EQ(p->error(), "1:21: expected expression for `break if`");
}
TEST_F(ParserImplTest, LoopStmt_Continuing_BreakIf_InvalidExpr) {
auto p = parser("loop { continuing { break if switch; }}");
auto e = p->loop_statement();
EXPECT_FALSE(e.matched);
EXPECT_TRUE(e.errored);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(e.value, nullptr);
EXPECT_EQ(p->error(), "1:21: expected expression for `break if`");
}
TEST_F(ParserImplTest, LoopStmt_NoContinuing_BreakIf) {
auto p = parser("loop { break if true; }");
auto e = p->loop_statement();
EXPECT_FALSE(e.matched);
EXPECT_TRUE(e.errored);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(e.value, nullptr);
EXPECT_EQ(p->error(), "1:14: expected ';' for break statement");
}
TEST_F(ParserImplTest, LoopStmt_Continuing_BreakIf_MissingSemicolon) {
auto p = parser("loop { continuing { break if 1 + 2 < 5 }}");
auto e = p->loop_statement();
EXPECT_FALSE(e.matched);
EXPECT_TRUE(e.errored);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(e.value, nullptr);
EXPECT_EQ(p->error(), "1:40: expected ';' for `break if` statement");
}
} // namespace
} // namespace tint::reader::wgsl