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:
parent
f8c07d4753
commit
b8b0c21918
|
@ -205,6 +205,8 @@ libtint_source_set("libtint_core_all_src") {
|
||||||
"ast/bool.h",
|
"ast/bool.h",
|
||||||
"ast/bool_literal_expression.cc",
|
"ast/bool_literal_expression.cc",
|
||||||
"ast/bool_literal_expression.h",
|
"ast/bool_literal_expression.h",
|
||||||
|
"ast/break_if_statement.cc",
|
||||||
|
"ast/break_if_statement.h",
|
||||||
"ast/break_statement.cc",
|
"ast/break_statement.cc",
|
||||||
"ast/break_statement.h",
|
"ast/break_statement.h",
|
||||||
"ast/builtin_attribute.cc",
|
"ast/builtin_attribute.cc",
|
||||||
|
@ -421,6 +423,7 @@ libtint_source_set("libtint_core_all_src") {
|
||||||
"sem/behavior.h",
|
"sem/behavior.h",
|
||||||
"sem/binding_point.h",
|
"sem/binding_point.h",
|
||||||
"sem/bool.h",
|
"sem/bool.h",
|
||||||
|
"sem/break_if_statement.h",
|
||||||
"sem/builtin.h",
|
"sem/builtin.h",
|
||||||
"sem/builtin_type.h",
|
"sem/builtin_type.h",
|
||||||
"sem/call.h",
|
"sem/call.h",
|
||||||
|
@ -630,6 +633,8 @@ libtint_source_set("libtint_sem_src") {
|
||||||
"sem/block_statement.cc",
|
"sem/block_statement.cc",
|
||||||
"sem/bool.cc",
|
"sem/bool.cc",
|
||||||
"sem/bool.h",
|
"sem/bool.h",
|
||||||
|
"sem/break_if_statement.cc",
|
||||||
|
"sem/break_if_statement.h",
|
||||||
"sem/builtin.cc",
|
"sem/builtin.cc",
|
||||||
"sem/builtin.h",
|
"sem/builtin.h",
|
||||||
"sem/builtin_type.cc",
|
"sem/builtin_type.cc",
|
||||||
|
@ -1016,6 +1021,7 @@ if (tint_build_unittests) {
|
||||||
"ast/block_statement_test.cc",
|
"ast/block_statement_test.cc",
|
||||||
"ast/bool_literal_expression_test.cc",
|
"ast/bool_literal_expression_test.cc",
|
||||||
"ast/bool_test.cc",
|
"ast/bool_test.cc",
|
||||||
|
"ast/break_if_statement_test.cc",
|
||||||
"ast/break_statement_test.cc",
|
"ast/break_statement_test.cc",
|
||||||
"ast/builtin_attribute_test.cc",
|
"ast/builtin_attribute_test.cc",
|
||||||
"ast/builtin_texture_helper_test.cc",
|
"ast/builtin_texture_helper_test.cc",
|
||||||
|
|
|
@ -73,6 +73,8 @@ set(TINT_LIB_SRCS
|
||||||
ast/bool_literal_expression.h
|
ast/bool_literal_expression.h
|
||||||
ast/bool.cc
|
ast/bool.cc
|
||||||
ast/bool.h
|
ast/bool.h
|
||||||
|
ast/break_if_statement.cc
|
||||||
|
ast/break_if_statement.h
|
||||||
ast/break_statement.cc
|
ast/break_statement.cc
|
||||||
ast/break_statement.h
|
ast/break_statement.h
|
||||||
ast/builtin_attribute.cc
|
ast/builtin_attribute.cc
|
||||||
|
@ -292,6 +294,8 @@ set(TINT_LIB_SRCS
|
||||||
sem/block_statement.h
|
sem/block_statement.h
|
||||||
sem/bool.cc
|
sem/bool.cc
|
||||||
sem/bool.h
|
sem/bool.h
|
||||||
|
sem/break_if_statement.cc
|
||||||
|
sem/break_if_statement.h
|
||||||
sem/builtin_type.cc
|
sem/builtin_type.cc
|
||||||
sem/builtin_type.h
|
sem/builtin_type.h
|
||||||
sem/builtin.cc
|
sem/builtin.cc
|
||||||
|
@ -708,6 +712,7 @@ if(TINT_BUILD_TESTS)
|
||||||
ast/block_statement_test.cc
|
ast/block_statement_test.cc
|
||||||
ast/bool_literal_expression_test.cc
|
ast/bool_literal_expression_test.cc
|
||||||
ast/bool_test.cc
|
ast/bool_test.cc
|
||||||
|
ast/break_if_statement_test.cc
|
||||||
ast/break_statement_test.cc
|
ast/break_statement_test.cc
|
||||||
ast/builtin_attribute_test.cc
|
ast/builtin_attribute_test.cc
|
||||||
ast/builtin_texture_helper_test.cc
|
ast/builtin_texture_helper_test.cc
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright 2022 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/tint/ast/break_if_statement.h"
|
||||||
|
|
||||||
|
#include "src/tint/program_builder.h"
|
||||||
|
|
||||||
|
TINT_INSTANTIATE_TYPEINFO(tint::ast::BreakIfStatement);
|
||||||
|
|
||||||
|
namespace tint::ast {
|
||||||
|
|
||||||
|
BreakIfStatement::BreakIfStatement(ProgramID pid,
|
||||||
|
NodeID nid,
|
||||||
|
const Source& src,
|
||||||
|
const Expression* cond)
|
||||||
|
: Base(pid, nid, src), condition(cond) {
|
||||||
|
TINT_ASSERT(AST, condition);
|
||||||
|
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, condition, program_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
BreakIfStatement::BreakIfStatement(BreakIfStatement&&) = default;
|
||||||
|
|
||||||
|
BreakIfStatement::~BreakIfStatement() = default;
|
||||||
|
|
||||||
|
const BreakIfStatement* BreakIfStatement::Clone(CloneContext* ctx) const {
|
||||||
|
// Clone arguments outside of create() call to have deterministic ordering
|
||||||
|
auto src = ctx->Clone(source);
|
||||||
|
auto* cond = ctx->Clone(condition);
|
||||||
|
return ctx->dst->create<BreakIfStatement>(src, cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace tint::ast
|
|
@ -0,0 +1,50 @@
|
||||||
|
// Copyright 2022 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_TINT_AST_BREAK_IF_STATEMENT_H_
|
||||||
|
#define SRC_TINT_AST_BREAK_IF_STATEMENT_H_
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "src/tint/ast/block_statement.h"
|
||||||
|
#include "src/tint/ast/expression.h"
|
||||||
|
|
||||||
|
namespace tint::ast {
|
||||||
|
|
||||||
|
/// A break if statement
|
||||||
|
class BreakIfStatement final : public Castable<BreakIfStatement, Statement> {
|
||||||
|
public:
|
||||||
|
/// Constructor
|
||||||
|
/// @param pid the identifier of the program that owns this node
|
||||||
|
/// @param nid the unique node identifier
|
||||||
|
/// @param src the source of this node
|
||||||
|
/// @param condition the if condition
|
||||||
|
BreakIfStatement(ProgramID pid, NodeID nid, const Source& src, const Expression* condition);
|
||||||
|
|
||||||
|
/// Move constructor
|
||||||
|
BreakIfStatement(BreakIfStatement&&);
|
||||||
|
~BreakIfStatement() override;
|
||||||
|
|
||||||
|
/// Clones this node and all transitive child nodes using the `CloneContext` `ctx`.
|
||||||
|
/// @param ctx the clone context
|
||||||
|
/// @return the newly cloned node
|
||||||
|
const BreakIfStatement* Clone(CloneContext* ctx) const override;
|
||||||
|
|
||||||
|
/// The if condition or nullptr if none set
|
||||||
|
const Expression* const condition;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace tint::ast
|
||||||
|
|
||||||
|
#endif // SRC_TINT_AST_BREAK_IF_STATEMENT_H_
|
|
@ -0,0 +1,58 @@
|
||||||
|
// Copyright 2022 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/tint/ast/break_if_statement.h"
|
||||||
|
|
||||||
|
#include "gtest/gtest-spi.h"
|
||||||
|
#include "src/tint/ast/test_helper.h"
|
||||||
|
|
||||||
|
namespace tint::ast {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using BreakIfStatementTest = TestHelper;
|
||||||
|
|
||||||
|
TEST_F(BreakIfStatementTest, Creation) {
|
||||||
|
auto* cond = Expr("cond");
|
||||||
|
auto* stmt = BreakIf(Source{Source::Location{20, 2}}, cond);
|
||||||
|
auto src = stmt->source;
|
||||||
|
EXPECT_EQ(src.range.begin.line, 20u);
|
||||||
|
EXPECT_EQ(src.range.begin.column, 2u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(BreakIfStatementTest, IsBreakIf) {
|
||||||
|
auto* stmt = BreakIf(Expr(true));
|
||||||
|
EXPECT_TRUE(stmt->Is<BreakIfStatement>());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(BreakIfStatementTest, Assert_Null_Condition) {
|
||||||
|
EXPECT_FATAL_FAILURE(
|
||||||
|
{
|
||||||
|
ProgramBuilder b;
|
||||||
|
b.BreakIf(nullptr);
|
||||||
|
},
|
||||||
|
"internal compiler error");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(BreakIfStatementTest, Assert_DifferentProgramID_Cond) {
|
||||||
|
EXPECT_FATAL_FAILURE(
|
||||||
|
{
|
||||||
|
ProgramBuilder b1;
|
||||||
|
ProgramBuilder b2;
|
||||||
|
b1.BreakIf(b2.Expr(true));
|
||||||
|
},
|
||||||
|
"internal compiler error");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace tint::ast
|
|
@ -30,6 +30,7 @@
|
||||||
#include "src/tint/ast/bitcast_expression.h"
|
#include "src/tint/ast/bitcast_expression.h"
|
||||||
#include "src/tint/ast/bool.h"
|
#include "src/tint/ast/bool.h"
|
||||||
#include "src/tint/ast/bool_literal_expression.h"
|
#include "src/tint/ast/bool_literal_expression.h"
|
||||||
|
#include "src/tint/ast/break_if_statement.h"
|
||||||
#include "src/tint/ast/break_statement.h"
|
#include "src/tint/ast/break_statement.h"
|
||||||
#include "src/tint/ast/call_expression.h"
|
#include "src/tint/ast/call_expression.h"
|
||||||
#include "src/tint/ast/call_statement.h"
|
#include "src/tint/ast/call_statement.h"
|
||||||
|
@ -2416,6 +2417,23 @@ class ProgramBuilder {
|
||||||
/// @returns the break statement pointer
|
/// @returns the break statement pointer
|
||||||
const ast::BreakStatement* Break() { return create<ast::BreakStatement>(); }
|
const ast::BreakStatement* Break() { return create<ast::BreakStatement>(); }
|
||||||
|
|
||||||
|
/// Creates a ast::BreakIfStatement with input condition
|
||||||
|
/// @param source the source information for the if statement
|
||||||
|
/// @param condition the if statement condition expression
|
||||||
|
/// @returns the break-if statement pointer
|
||||||
|
template <typename CONDITION>
|
||||||
|
const ast::BreakIfStatement* BreakIf(const Source& source, CONDITION&& condition) {
|
||||||
|
return create<ast::BreakIfStatement>(source, Expr(std::forward<CONDITION>(condition)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a ast::BreakIfStatement with input condition
|
||||||
|
/// @param condition the if statement condition expression
|
||||||
|
/// @returns the break-if statement pointer
|
||||||
|
template <typename CONDITION>
|
||||||
|
const ast::BreakIfStatement* BreakIf(CONDITION&& condition) {
|
||||||
|
return create<ast::BreakIfStatement>(Expr(std::forward<CONDITION>(condition)));
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates an ast::ContinueStatement
|
/// Creates an ast::ContinueStatement
|
||||||
/// @param source the source information
|
/// @param source the source information
|
||||||
/// @returns the continue statement pointer
|
/// @returns the continue statement pointer
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "src/tint/ast/array.h"
|
#include "src/tint/ast/array.h"
|
||||||
#include "src/tint/ast/assignment_statement.h"
|
#include "src/tint/ast/assignment_statement.h"
|
||||||
#include "src/tint/ast/bitcast_expression.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/break_statement.h"
|
||||||
#include "src/tint/ast/call_statement.h"
|
#include "src/tint/ast/call_statement.h"
|
||||||
#include "src/tint/ast/continue_statement.h"
|
#include "src/tint/ast/continue_statement.h"
|
||||||
|
@ -2265,8 +2266,7 @@ ForHeader::ForHeader(const ast::Statement* init,
|
||||||
|
|
||||||
ForHeader::~ForHeader() = default;
|
ForHeader::~ForHeader() = default;
|
||||||
|
|
||||||
// (variable_statement | variable_updating_statement |
|
// (variable_statement | variable_updating_statement | func_call_statement)?
|
||||||
// func_call_statement)?
|
|
||||||
Maybe<const ast::Statement*> ParserImpl::for_header_initializer() {
|
Maybe<const ast::Statement*> ParserImpl::for_header_initializer() {
|
||||||
auto call = func_call_statement();
|
auto call = func_call_statement();
|
||||||
if (call.errored) {
|
if (call.errored) {
|
||||||
|
@ -2317,10 +2317,7 @@ Maybe<const ast::Statement*> ParserImpl::for_header_continuing() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// for_header
|
// for_header
|
||||||
// : (variable_statement | variable_updating_statement | func_call_statement)?
|
// : for_header_initializer? SEMICOLON expression? SEMICOLON for_header_continuing?
|
||||||
// SEMICOLON
|
|
||||||
// expression? SEMICOLON
|
|
||||||
// (variable_updating_statement | func_call_statement)?
|
|
||||||
Expect<std::unique_ptr<ForHeader>> ParserImpl::expect_for_header() {
|
Expect<std::unique_ptr<ForHeader>> ParserImpl::expect_for_header() {
|
||||||
auto initializer = for_header_initializer();
|
auto initializer = for_header_initializer();
|
||||||
if (initializer.errored) {
|
if (initializer.errored) {
|
||||||
|
@ -2444,28 +2441,58 @@ Maybe<const ast::ContinueStatement*> ParserImpl::continue_statement() {
|
||||||
// break_if_statement:
|
// break_if_statement:
|
||||||
// 'break' 'if' expression semicolon
|
// 'break' 'if' expression semicolon
|
||||||
Maybe<const ast::Statement*> ParserImpl::break_if_statement() {
|
Maybe<const ast::Statement*> ParserImpl::break_if_statement() {
|
||||||
// TODO(crbug.com/tint/1451): Add support for break-if
|
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;
|
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:
|
// continuing_compound_statement:
|
||||||
// brace_left statement* break_if_statement? brace_right
|
// brace_left statement* break_if_statement? brace_right
|
||||||
Maybe<const ast::BlockStatement*> ParserImpl::continuing_compound_statement() {
|
Maybe<const ast::BlockStatement*> ParserImpl::continuing_compound_statement() {
|
||||||
return expect_brace_block("", [&]() -> Expect<ast::BlockStatement*> {
|
return expect_brace_block("", [&]() -> Expect<ast::BlockStatement*> {
|
||||||
auto stmts = expect_statements();
|
StatementList stmts;
|
||||||
if (stmts.errored) {
|
|
||||||
return Failure::kErrored;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
while (continue_parsing()) {
|
||||||
|
// Note, break-if has to parse before statements because statements includes `break`
|
||||||
auto break_if = break_if_statement();
|
auto break_if = break_if_statement();
|
||||||
if (break_if.errored) {
|
if (break_if.errored) {
|
||||||
return Failure::kErrored;
|
return Failure::kErrored;
|
||||||
}
|
}
|
||||||
if (break_if.matched) {
|
if (break_if.matched) {
|
||||||
stmts.value.Push(break_if.value);
|
stmts.Push(break_if.value);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return create<ast::BlockStatement>(Source{}, stmts.value);
|
auto stmt = statement();
|
||||||
|
if (stmt.errored) {
|
||||||
|
return Failure::kErrored;
|
||||||
|
}
|
||||||
|
if (!stmt.matched) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
stmts.Push(stmt.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return create<ast::BlockStatement>(Source{}, stmts);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -110,5 +110,57 @@ TEST_F(ParserImplTest, LoopStmt_InvalidContinuing) {
|
||||||
EXPECT_EQ(p->error(), "1:29: expected ';' for discard statement");
|
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
|
||||||
} // namespace tint::reader::wgsl
|
} // namespace tint::reader::wgsl
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "src/tint/ast/atomic.h"
|
#include "src/tint/ast/atomic.h"
|
||||||
#include "src/tint/ast/block_statement.h"
|
#include "src/tint/ast/block_statement.h"
|
||||||
#include "src/tint/ast/bool.h"
|
#include "src/tint/ast/bool.h"
|
||||||
|
#include "src/tint/ast/break_if_statement.h"
|
||||||
#include "src/tint/ast/break_statement.h"
|
#include "src/tint/ast/break_statement.h"
|
||||||
#include "src/tint/ast/call_statement.h"
|
#include "src/tint/ast/call_statement.h"
|
||||||
#include "src/tint/ast/compound_assignment_statement.h"
|
#include "src/tint/ast/compound_assignment_statement.h"
|
||||||
|
@ -263,9 +264,8 @@ class DependencyScanner {
|
||||||
TINT_DEFER(scope_stack_.Pop());
|
TINT_DEFER(scope_stack_.Pop());
|
||||||
TraverseStatements(b->statements);
|
TraverseStatements(b->statements);
|
||||||
},
|
},
|
||||||
[&](const ast::CallStatement* r) { //
|
[&](const ast::BreakIfStatement* b) { TraverseExpression(b->condition); },
|
||||||
TraverseExpression(r->expr);
|
[&](const ast::CallStatement* r) { TraverseExpression(r->expr); },
|
||||||
},
|
|
||||||
[&](const ast::CompoundAssignmentStatement* a) {
|
[&](const ast::CompoundAssignmentStatement* a) {
|
||||||
TraverseExpression(a->lhs);
|
TraverseExpression(a->lhs);
|
||||||
TraverseExpression(a->rhs);
|
TraverseExpression(a->rhs);
|
||||||
|
@ -292,9 +292,7 @@ class DependencyScanner {
|
||||||
TraverseStatement(i->else_statement);
|
TraverseStatement(i->else_statement);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[&](const ast::ReturnStatement* r) { //
|
[&](const ast::ReturnStatement* r) { TraverseExpression(r->value); },
|
||||||
TraverseExpression(r->value);
|
|
||||||
},
|
|
||||||
[&](const ast::SwitchStatement* s) {
|
[&](const ast::SwitchStatement* s) {
|
||||||
TraverseExpression(s->condition);
|
TraverseExpression(s->condition);
|
||||||
for (auto* c : s->body) {
|
for (auto* c : s->body) {
|
||||||
|
|
|
@ -1260,7 +1260,7 @@ TEST_F(ResolverDependencyGraphTraversalTest, SymbolsReached) {
|
||||||
Block( //
|
Block( //
|
||||||
Assign(V, V))), //
|
Assign(V, V))), //
|
||||||
Loop(Block(Assign(V, V)), //
|
Loop(Block(Assign(V, V)), //
|
||||||
Block(Assign(V, V))), //
|
Block(Assign(V, V), BreakIf(V))), //
|
||||||
Switch(V, //
|
Switch(V, //
|
||||||
Case(CaseSelector(1_i), //
|
Case(CaseSelector(1_i), //
|
||||||
Block(Assign(V, V))), //
|
Block(Assign(V, V))), //
|
||||||
|
|
|
@ -57,6 +57,7 @@
|
||||||
#include "src/tint/sem/abstract_int.h"
|
#include "src/tint/sem/abstract_int.h"
|
||||||
#include "src/tint/sem/array.h"
|
#include "src/tint/sem/array.h"
|
||||||
#include "src/tint/sem/atomic.h"
|
#include "src/tint/sem/atomic.h"
|
||||||
|
#include "src/tint/sem/break_if_statement.h"
|
||||||
#include "src/tint/sem/call.h"
|
#include "src/tint/sem/call.h"
|
||||||
#include "src/tint/sem/depth_multisampled_texture.h"
|
#include "src/tint/sem/depth_multisampled_texture.h"
|
||||||
#include "src/tint/sem/depth_texture.h"
|
#include "src/tint/sem/depth_texture.h"
|
||||||
|
@ -1213,6 +1214,7 @@ sem::Statement* Resolver::Statement(const ast::Statement* stmt) {
|
||||||
// Non-Compound statements
|
// Non-Compound statements
|
||||||
[&](const ast::AssignmentStatement* a) { return AssignmentStatement(a); },
|
[&](const ast::AssignmentStatement* a) { return AssignmentStatement(a); },
|
||||||
[&](const ast::BreakStatement* b) { return BreakStatement(b); },
|
[&](const ast::BreakStatement* b) { return BreakStatement(b); },
|
||||||
|
[&](const ast::BreakIfStatement* b) { return BreakIfStatement(b); },
|
||||||
[&](const ast::CallStatement* c) { return CallStatement(c); },
|
[&](const ast::CallStatement* c) { return CallStatement(c); },
|
||||||
[&](const ast::CompoundAssignmentStatement* c) { return CompoundAssignmentStatement(c); },
|
[&](const ast::CompoundAssignmentStatement* c) { return CompoundAssignmentStatement(c); },
|
||||||
[&](const ast::ContinueStatement* c) { return ContinueStatement(c); },
|
[&](const ast::ContinueStatement* c) { return ContinueStatement(c); },
|
||||||
|
@ -3224,6 +3226,22 @@ sem::Statement* Resolver::BreakStatement(const ast::BreakStatement* stmt) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sem::Statement* Resolver::BreakIfStatement(const ast::BreakIfStatement* stmt) {
|
||||||
|
auto* sem = builder_->create<sem::BreakIfStatement>(stmt, current_compound_statement_,
|
||||||
|
current_function_);
|
||||||
|
return StatementScope(stmt, sem, [&] {
|
||||||
|
auto* cond = Expression(stmt->condition);
|
||||||
|
if (!cond) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sem->SetCondition(cond);
|
||||||
|
sem->Behaviors() = cond->Behaviors();
|
||||||
|
sem->Behaviors().Add(sem::Behavior::kBreak);
|
||||||
|
|
||||||
|
return validator_.BreakIfStatement(sem, current_statement_);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
sem::Statement* Resolver::CallStatement(const ast::CallStatement* stmt) {
|
sem::Statement* Resolver::CallStatement(const ast::CallStatement* stmt) {
|
||||||
auto* sem =
|
auto* sem =
|
||||||
builder_->create<sem::Statement>(stmt, current_compound_statement_, current_function_);
|
builder_->create<sem::Statement>(stmt, current_compound_statement_, current_function_);
|
||||||
|
|
|
@ -208,6 +208,7 @@ class Resolver {
|
||||||
sem::Statement* AssignmentStatement(const ast::AssignmentStatement*);
|
sem::Statement* AssignmentStatement(const ast::AssignmentStatement*);
|
||||||
sem::BlockStatement* BlockStatement(const ast::BlockStatement*);
|
sem::BlockStatement* BlockStatement(const ast::BlockStatement*);
|
||||||
sem::Statement* BreakStatement(const ast::BreakStatement*);
|
sem::Statement* BreakStatement(const ast::BreakStatement*);
|
||||||
|
sem::Statement* BreakIfStatement(const ast::BreakIfStatement*);
|
||||||
sem::Statement* CallStatement(const ast::CallStatement*);
|
sem::Statement* CallStatement(const ast::CallStatement*);
|
||||||
sem::CaseStatement* CaseStatement(const ast::CaseStatement*, const sem::Type*);
|
sem::CaseStatement* CaseStatement(const ast::CaseStatement*, const sem::Type*);
|
||||||
sem::Statement* CompoundAssignmentStatement(const ast::CompoundAssignmentStatement*);
|
sem::Statement* CompoundAssignmentStatement(const ast::CompoundAssignmentStatement*);
|
||||||
|
|
|
@ -569,6 +569,16 @@ TEST_F(ResolverBehaviorTest, StmtLoopEmpty_ContIfTrueBreak) {
|
||||||
EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
|
EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverBehaviorTest, StmtLoopEmpty_BreakIf) {
|
||||||
|
auto* stmt = Loop(Block(), Block(BreakIf(true)));
|
||||||
|
WrapInFunction(stmt);
|
||||||
|
|
||||||
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
|
||||||
|
auto* sem = Sem().Get(stmt);
|
||||||
|
EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBehaviorTest, StmtReturn) {
|
TEST_F(ResolverBehaviorTest, StmtReturn) {
|
||||||
auto* stmt = Return();
|
auto* stmt = Return();
|
||||||
WrapInFunction(stmt);
|
WrapInFunction(stmt);
|
||||||
|
|
|
@ -215,7 +215,8 @@ TEST_F(ResolverTest, Stmt_Loop) {
|
||||||
auto* continuing_lhs = Expr("v");
|
auto* continuing_lhs = Expr("v");
|
||||||
auto* continuing_rhs = Expr(2.3_f);
|
auto* continuing_rhs = Expr(2.3_f);
|
||||||
|
|
||||||
auto* continuing = Block(Assign(continuing_lhs, continuing_rhs));
|
auto* break_if = BreakIf(false);
|
||||||
|
auto* continuing = Block(Assign(continuing_lhs, continuing_rhs), break_if);
|
||||||
auto* stmt = Loop(body, continuing);
|
auto* stmt = Loop(body, continuing);
|
||||||
WrapInFunction(v, stmt);
|
WrapInFunction(v, stmt);
|
||||||
|
|
||||||
|
@ -233,6 +234,7 @@ TEST_F(ResolverTest, Stmt_Loop) {
|
||||||
EXPECT_EQ(BlockOf(body_rhs), body);
|
EXPECT_EQ(BlockOf(body_rhs), body);
|
||||||
EXPECT_EQ(BlockOf(continuing_lhs), continuing);
|
EXPECT_EQ(BlockOf(continuing_lhs), continuing);
|
||||||
EXPECT_EQ(BlockOf(continuing_rhs), continuing);
|
EXPECT_EQ(BlockOf(continuing_rhs), continuing);
|
||||||
|
EXPECT_EQ(BlockOf(break_if), continuing);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverTest, Stmt_Return) {
|
TEST_F(ResolverTest, Stmt_Return) {
|
||||||
|
|
|
@ -537,6 +537,51 @@ class UniformityGraph {
|
||||||
return cf;
|
return cf;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[&](const ast::BreakIfStatement* b) {
|
||||||
|
// This works very similar to the IfStatement uniformity below, execpt instead of
|
||||||
|
// processing the body, we directly inline the BreakStatement uniformity from
|
||||||
|
// above.
|
||||||
|
|
||||||
|
auto [_, v_cond] = ProcessExpression(cf, b->condition);
|
||||||
|
|
||||||
|
// Add a diagnostic node to capture the control flow change.
|
||||||
|
auto* v = current_function_->CreateNode("break_if_stmt", b);
|
||||||
|
v->affects_control_flow = true;
|
||||||
|
v->AddEdge(v_cond);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto* parent = sem_.Get(b)->FindFirstParent<sem::LoopStatement>();
|
||||||
|
TINT_ASSERT(Resolver, current_function_->loop_switch_infos.count(parent));
|
||||||
|
auto& info = current_function_->loop_switch_infos.at(parent);
|
||||||
|
|
||||||
|
// Propagate variable values to the loop exit nodes.
|
||||||
|
for (auto* var : current_function_->local_var_decls) {
|
||||||
|
// Skip variables that were declared inside this loop.
|
||||||
|
if (auto* lv = var->As<sem::LocalVariable>();
|
||||||
|
lv && lv->Statement()->FindFirstParent(
|
||||||
|
[&](auto* s) { return s == parent; })) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an edge from the variable exit node to its value at this point.
|
||||||
|
auto* exit_node = utils::GetOrCreate(info.var_exit_nodes, var, [&]() {
|
||||||
|
auto name = builder_->Symbols().NameFor(var->Declaration()->symbol);
|
||||||
|
return CreateNode(name + "_value_" + info.type + "_exit");
|
||||||
|
});
|
||||||
|
|
||||||
|
exit_node->AddEdge(current_function_->variables.Get(var));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* sem_break_if = sem_.Get(b);
|
||||||
|
if (sem_break_if->Behaviors() != sem::Behaviors{sem::Behavior::kNext}) {
|
||||||
|
auto* cf_end = CreateNode("break_if_CFend");
|
||||||
|
cf_end->AddEdge(v);
|
||||||
|
return cf_end;
|
||||||
|
}
|
||||||
|
return cf;
|
||||||
|
},
|
||||||
|
|
||||||
[&](const ast::CallStatement* c) {
|
[&](const ast::CallStatement* c) {
|
||||||
auto [cf1, _] = ProcessCall(cf, c->expr);
|
auto [cf1, _] = ProcessCall(cf, c->expr);
|
||||||
return cf1;
|
return cf1;
|
||||||
|
|
|
@ -1125,9 +1125,7 @@ fn foo() {
|
||||||
workgroupBarrier();
|
workgroupBarrier();
|
||||||
continuing {
|
continuing {
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
if (i == n) {
|
break if (i == n);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1146,9 +1144,7 @@ fn foo() {
|
||||||
workgroupBarrier();
|
workgroupBarrier();
|
||||||
continuing {
|
continuing {
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
if (i == n) {
|
break if (i == n);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1161,11 +1157,11 @@ fn foo() {
|
||||||
^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
test:10:7 note: control flow depends on non-uniform value
|
test:10:7 note: control flow depends on non-uniform value
|
||||||
if (i == n) {
|
break if (i == n);
|
||||||
^^
|
^^^^^
|
||||||
|
|
||||||
test:10:16 note: reading from read_write storage buffer 'n' may result in a non-uniform value
|
test:10:22 note: reading from read_write storage buffer 'n' may result in a non-uniform value
|
||||||
if (i == n) {
|
break if (i == n);
|
||||||
^
|
^
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
@ -1180,9 +1176,7 @@ fn foo() {
|
||||||
continuing {
|
continuing {
|
||||||
workgroupBarrier();
|
workgroupBarrier();
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
if (i == n) {
|
break if (i == n);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1201,9 +1195,7 @@ fn foo() {
|
||||||
continuing {
|
continuing {
|
||||||
workgroupBarrier();
|
workgroupBarrier();
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
if (i == n) {
|
break if (i == n);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1216,11 +1208,11 @@ fn foo() {
|
||||||
^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
test:10:7 note: control flow depends on non-uniform value
|
test:10:7 note: control flow depends on non-uniform value
|
||||||
if (i == n) {
|
break if (i == n);
|
||||||
^^
|
^^^^^
|
||||||
|
|
||||||
test:10:16 note: reading from read_write storage buffer 'n' may result in a non-uniform value
|
test:10:22 note: reading from read_write storage buffer 'n' may result in a non-uniform value
|
||||||
if (i == n) {
|
break if (i == n);
|
||||||
^
|
^
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
@ -1249,9 +1241,7 @@ fn foo() {
|
||||||
continuing {
|
continuing {
|
||||||
// Pretend that this isn't an infinite loop, in case the interrupt is a
|
// Pretend that this isn't an infinite loop, in case the interrupt is a
|
||||||
// continue statement.
|
// continue statement.
|
||||||
if (false) {
|
break if (false);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1597,9 +1587,7 @@ fn foo() {
|
||||||
if (v == 0) {
|
if (v == 0) {
|
||||||
workgroupBarrier();
|
workgroupBarrier();
|
||||||
}
|
}
|
||||||
if (true) {
|
break if (true);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -870,6 +870,75 @@ TEST_F(ResolverTest, Stmt_Loop_ContinueInContinuing_Indirect) {
|
||||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverValidationTest, Stmt_Loop_Continuing_BreakIf) {
|
||||||
|
// loop {
|
||||||
|
// continuing {
|
||||||
|
// break if true;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
auto* body = Block();
|
||||||
|
auto* continuing = Block(BreakIf(true));
|
||||||
|
auto* loop_stmt = Loop(body, continuing);
|
||||||
|
WrapInFunction(loop_stmt);
|
||||||
|
|
||||||
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverValidationTest, Stmt_Loop_Continuing_BreakIf_Not_Last) {
|
||||||
|
// loop {
|
||||||
|
// var z : i32;
|
||||||
|
// continuing {
|
||||||
|
// break if true;
|
||||||
|
// z = 2i;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
auto* body = Block(Decl(Var("z", ty.i32())));
|
||||||
|
auto* continuing =
|
||||||
|
Block(Source{{10, 9}}, BreakIf(Source{{12, 23}}, true), Assign(Expr("z"), 2_i));
|
||||||
|
auto* loop_stmt = Loop(body, continuing);
|
||||||
|
WrapInFunction(loop_stmt);
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(r()->error(), R"(12:23 error: break-if must be last statement in a continuing block
|
||||||
|
10:9 note: see continuing block here)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverValidationTest, Stmt_Loop_Continuing_BreakIf_Duplicate) {
|
||||||
|
// loop {
|
||||||
|
// continuing {
|
||||||
|
// break if true;
|
||||||
|
// break if false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
auto* body = Block();
|
||||||
|
auto* continuing = Block(Source{{10, 9}}, BreakIf(Source{{12, 23}}, true), BreakIf(false));
|
||||||
|
auto* loop_stmt = Loop(body, continuing);
|
||||||
|
WrapInFunction(loop_stmt);
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(r()->error(), R"(12:23 error: break-if must be last statement in a continuing block
|
||||||
|
10:9 note: see continuing block here)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverValidationTest, Stmt_Loop_Continuing_BreakIf_NonBool) {
|
||||||
|
// loop {
|
||||||
|
// continuing {
|
||||||
|
// break if 1i;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
auto* body = Block();
|
||||||
|
auto* continuing = Block(BreakIf(Expr(Source{{12, 23}}, 1_i)));
|
||||||
|
auto* loop_stmt = Loop(body, continuing);
|
||||||
|
WrapInFunction(loop_stmt);
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(r()->error(), R"(12:23 error: break-if statement condition must be bool, got i32)");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ResolverTest, Stmt_ForLoop_ReturnInContinuing_Direct) {
|
TEST_F(ResolverTest, Stmt_ForLoop_ReturnInContinuing_Direct) {
|
||||||
// for(;; return) {
|
// for(;; return) {
|
||||||
// break;
|
// break;
|
||||||
|
@ -1055,6 +1124,9 @@ TEST_F(ResolverValidationTest, Stmt_BreakInIfTrueInContinuing) {
|
||||||
// }
|
// }
|
||||||
WrapInFunction(Loop(Block(), cont));
|
WrapInFunction(Loop(Block(), cont));
|
||||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
EXPECT_EQ(r()->error(),
|
||||||
|
"12:34 warning: use of deprecated language feature: `break` must not be used to exit "
|
||||||
|
"from a continuing block. Use break-if instead.");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverValidationTest, Stmt_BreakInIfElseInContinuing) {
|
TEST_F(ResolverValidationTest, Stmt_BreakInIfElseInContinuing) {
|
||||||
|
@ -1066,6 +1138,9 @@ TEST_F(ResolverValidationTest, Stmt_BreakInIfElseInContinuing) {
|
||||||
// }
|
// }
|
||||||
WrapInFunction(Loop(Block(), cont));
|
WrapInFunction(Loop(Block(), cont));
|
||||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
EXPECT_EQ(r()->error(),
|
||||||
|
"12:34 warning: use of deprecated language feature: `break` must not be used to exit "
|
||||||
|
"from a continuing block. Use break-if instead.");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverValidationTest, Stmt_BreakInContinuing) {
|
TEST_F(ResolverValidationTest, Stmt_BreakInContinuing) {
|
||||||
|
@ -1075,6 +1150,8 @@ TEST_F(ResolverValidationTest, Stmt_BreakInContinuing) {
|
||||||
WrapInFunction(Loop(Block(), cont));
|
WrapInFunction(Loop(Block(), cont));
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
|
"12:34 warning: use of deprecated language feature: `break` must not be used to exit "
|
||||||
|
"from a continuing block. Use break-if instead.\n"
|
||||||
"12:34 error: break statement in a continuing block must be the single "
|
"12:34 error: break statement in a continuing block must be the single "
|
||||||
"statement of an if statement's true or false block, and that if "
|
"statement of an if statement's true or false block, and that if "
|
||||||
"statement must be the last statement of the continuing block\n"
|
"statement must be the last statement of the continuing block\n"
|
||||||
|
@ -1092,6 +1169,8 @@ TEST_F(ResolverValidationTest, Stmt_BreakInIfInIfInContinuing) {
|
||||||
WrapInFunction(Loop(Block(), cont));
|
WrapInFunction(Loop(Block(), cont));
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
|
"12:34 warning: use of deprecated language feature: `break` must not be used to exit "
|
||||||
|
"from a continuing block. Use break-if instead.\n"
|
||||||
"12:34 error: break statement in a continuing block must be the single "
|
"12:34 error: break statement in a continuing block must be the single "
|
||||||
"statement of an if statement's true or false block, and that if "
|
"statement of an if statement's true or false block, and that if "
|
||||||
"statement must be the last statement of the continuing block\n"
|
"statement must be the last statement of the continuing block\n"
|
||||||
|
@ -1109,6 +1188,8 @@ TEST_F(ResolverValidationTest, Stmt_BreakInIfTrueMultipleStmtsInContinuing) {
|
||||||
WrapInFunction(Loop(Block(), cont));
|
WrapInFunction(Loop(Block(), cont));
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
|
"12:34 warning: use of deprecated language feature: `break` must not be used to exit "
|
||||||
|
"from a continuing block. Use break-if instead.\n"
|
||||||
"12:34 error: break statement in a continuing block must be the single "
|
"12:34 error: break statement in a continuing block must be the single "
|
||||||
"statement of an if statement's true or false block, and that if "
|
"statement of an if statement's true or false block, and that if "
|
||||||
"statement must be the last statement of the continuing block\n"
|
"statement must be the last statement of the continuing block\n"
|
||||||
|
@ -1126,6 +1207,8 @@ TEST_F(ResolverValidationTest, Stmt_BreakInIfElseMultipleStmtsInContinuing) {
|
||||||
WrapInFunction(Loop(Block(), cont));
|
WrapInFunction(Loop(Block(), cont));
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
|
"12:34 warning: use of deprecated language feature: `break` must not be used to exit "
|
||||||
|
"from a continuing block. Use break-if instead.\n"
|
||||||
"12:34 error: break statement in a continuing block must be the single "
|
"12:34 error: break statement in a continuing block must be the single "
|
||||||
"statement of an if statement's true or false block, and that if "
|
"statement of an if statement's true or false block, and that if "
|
||||||
"statement must be the last statement of the continuing block\n"
|
"statement must be the last statement of the continuing block\n"
|
||||||
|
@ -1142,6 +1225,8 @@ TEST_F(ResolverValidationTest, Stmt_BreakInIfElseIfInContinuing) {
|
||||||
WrapInFunction(Loop(Block(), cont));
|
WrapInFunction(Loop(Block(), cont));
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
|
"12:34 warning: use of deprecated language feature: `break` must not be used to exit "
|
||||||
|
"from a continuing block. Use break-if instead.\n"
|
||||||
"12:34 error: break statement in a continuing block must be the single "
|
"12:34 error: break statement in a continuing block must be the single "
|
||||||
"statement of an if statement's true or false block, and that if "
|
"statement of an if statement's true or false block, and that if "
|
||||||
"statement must be the last statement of the continuing block\n"
|
"statement must be the last statement of the continuing block\n"
|
||||||
|
@ -1159,6 +1244,8 @@ TEST_F(ResolverValidationTest, Stmt_BreakInIfNonEmptyElseInContinuing) {
|
||||||
WrapInFunction(Loop(Block(), cont));
|
WrapInFunction(Loop(Block(), cont));
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
|
"12:34 warning: use of deprecated language feature: `break` must not be used to exit "
|
||||||
|
"from a continuing block. Use break-if instead.\n"
|
||||||
"12:34 error: break statement in a continuing block must be the single "
|
"12:34 error: break statement in a continuing block must be the single "
|
||||||
"statement of an if statement's true or false block, and that if "
|
"statement of an if statement's true or false block, and that if "
|
||||||
"statement must be the last statement of the continuing block\n"
|
"statement must be the last statement of the continuing block\n"
|
||||||
|
@ -1176,6 +1263,8 @@ TEST_F(ResolverValidationTest, Stmt_BreakInIfElseNonEmptyTrueInContinuing) {
|
||||||
WrapInFunction(Loop(Block(), cont));
|
WrapInFunction(Loop(Block(), cont));
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
|
"12:34 warning: use of deprecated language feature: `break` must not be used to exit "
|
||||||
|
"from a continuing block. Use break-if instead.\n"
|
||||||
"12:34 error: break statement in a continuing block must be the single "
|
"12:34 error: break statement in a continuing block must be the single "
|
||||||
"statement of an if statement's true or false block, and that if "
|
"statement of an if statement's true or false block, and that if "
|
||||||
"statement must be the last statement of the continuing block\n"
|
"statement must be the last statement of the continuing block\n"
|
||||||
|
@ -1192,6 +1281,8 @@ TEST_F(ResolverValidationTest, Stmt_BreakInIfInContinuingNotLast) {
|
||||||
WrapInFunction(Loop(Block(), cont));
|
WrapInFunction(Loop(Block(), cont));
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
|
"12:34 warning: use of deprecated language feature: `break` must not be used to exit "
|
||||||
|
"from a continuing block. Use break-if instead.\n"
|
||||||
"12:34 error: break statement in a continuing block must be the single "
|
"12:34 error: break statement in a continuing block must be the single "
|
||||||
"statement of an if statement's true or false block, and that if "
|
"statement of an if statement's true or false block, and that if "
|
||||||
"statement must be the last statement of the continuing block\n"
|
"statement must be the last statement of the continuing block\n"
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
#include "src/tint/sem/abstract_numeric.h"
|
#include "src/tint/sem/abstract_numeric.h"
|
||||||
#include "src/tint/sem/array.h"
|
#include "src/tint/sem/array.h"
|
||||||
#include "src/tint/sem/atomic.h"
|
#include "src/tint/sem/atomic.h"
|
||||||
|
#include "src/tint/sem/break_if_statement.h"
|
||||||
#include "src/tint/sem/call.h"
|
#include "src/tint/sem/call.h"
|
||||||
#include "src/tint/sem/depth_multisampled_texture.h"
|
#include "src/tint/sem/depth_multisampled_texture.h"
|
||||||
#include "src/tint/sem/depth_texture.h"
|
#include "src/tint/sem/depth_texture.h"
|
||||||
|
@ -1462,6 +1463,11 @@ bool Validator::BreakStatement(const sem::Statement* stmt,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ true, current_statement)) {
|
if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ true, current_statement)) {
|
||||||
|
AddWarning(
|
||||||
|
"use of deprecated language feature: `break` must not be used to exit from "
|
||||||
|
"a continuing block. Use break-if instead.",
|
||||||
|
stmt->Declaration()->source);
|
||||||
|
|
||||||
auto fail = [&](const char* note_msg, const Source& note_src) {
|
auto fail = [&](const char* note_msg, const Source& note_src) {
|
||||||
constexpr const char* kErrorMsg =
|
constexpr const char* kErrorMsg =
|
||||||
"break statement in a continuing block must be the single statement of an if "
|
"break statement in a continuing block must be the single statement of an if "
|
||||||
|
@ -1632,6 +1638,35 @@ bool Validator::WhileStatement(const sem::WhileStatement* stmt) const {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Validator::BreakIfStatement(const sem::BreakIfStatement* stmt,
|
||||||
|
sem::Statement* current_statement) const {
|
||||||
|
auto* cond_ty = stmt->Condition()->Type()->UnwrapRef();
|
||||||
|
if (!cond_ty->Is<sem::Bool>()) {
|
||||||
|
AddError("break-if statement condition must be bool, got " + sem_.TypeNameOf(cond_ty),
|
||||||
|
stmt->Condition()->Declaration()->source);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto* s = current_statement; s != nullptr; s = s->Parent()) {
|
||||||
|
if (s->Is<sem::LoopStatement>()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (s->Is<sem::LoopContinuingBlockStatement>()) {
|
||||||
|
if (s->Declaration()->As<ast::BlockStatement>()->statements.Back() !=
|
||||||
|
stmt->Declaration()) {
|
||||||
|
AddError("break-if must be last statement in a continuing block",
|
||||||
|
stmt->Declaration()->source);
|
||||||
|
AddNote("see continuing block here", s->Declaration()->source);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddError("break-if must in a continuing block", stmt->Declaration()->source);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool Validator::IfStatement(const sem::IfStatement* stmt) const {
|
bool Validator::IfStatement(const sem::IfStatement* stmt) const {
|
||||||
auto* cond_ty = stmt->Condition()->Type()->UnwrapRef();
|
auto* cond_ty = stmt->Condition()->Type()->UnwrapRef();
|
||||||
if (!cond_ty->Is<sem::Bool>()) {
|
if (!cond_ty->Is<sem::Bool>()) {
|
||||||
|
|
|
@ -51,6 +51,7 @@ namespace tint::sem {
|
||||||
class Array;
|
class Array;
|
||||||
class Atomic;
|
class Atomic;
|
||||||
class BlockStatement;
|
class BlockStatement;
|
||||||
|
class BreakIfStatement;
|
||||||
class Builtin;
|
class Builtin;
|
||||||
class Call;
|
class Call;
|
||||||
class CaseStatement;
|
class CaseStatement;
|
||||||
|
@ -256,6 +257,13 @@ class Validator {
|
||||||
const std::unordered_map<OverrideId, const sem::Variable*>& override_id,
|
const std::unordered_map<OverrideId, const sem::Variable*>& override_id,
|
||||||
const std::unordered_map<const sem::Type*, const Source&>& atomic_composite_info) const;
|
const std::unordered_map<const sem::Type*, const Source&>& atomic_composite_info) const;
|
||||||
|
|
||||||
|
/// Validates a break-if statement
|
||||||
|
/// @param stmt the statement to validate
|
||||||
|
/// @param current_statement the current statement being resolved
|
||||||
|
/// @returns true on success, false otherwise
|
||||||
|
bool BreakIfStatement(const sem::BreakIfStatement* stmt,
|
||||||
|
sem::Statement* current_statement) const;
|
||||||
|
|
||||||
/// Validates an if statement
|
/// Validates an if statement
|
||||||
/// @param stmt the statement to validate
|
/// @param stmt the statement to validate
|
||||||
/// @returns true on success, false otherwise
|
/// @returns true on success, false otherwise
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright 2022 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/tint/sem/break_if_statement.h"
|
||||||
|
|
||||||
|
#include "src/tint/program_builder.h"
|
||||||
|
|
||||||
|
TINT_INSTANTIATE_TYPEINFO(tint::sem::BreakIfStatement);
|
||||||
|
|
||||||
|
namespace tint::sem {
|
||||||
|
|
||||||
|
BreakIfStatement::BreakIfStatement(const ast::BreakIfStatement* declaration,
|
||||||
|
const CompoundStatement* parent,
|
||||||
|
const sem::Function* function)
|
||||||
|
: Base(declaration, parent, function) {}
|
||||||
|
|
||||||
|
BreakIfStatement::~BreakIfStatement() = default;
|
||||||
|
|
||||||
|
const ast::BreakIfStatement* BreakIfStatement::Declaration() const {
|
||||||
|
return static_cast<const ast::BreakIfStatement*>(Base::Declaration());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace tint::sem
|
|
@ -0,0 +1,60 @@
|
||||||
|
// Copyright 2022 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_TINT_SEM_BREAK_IF_STATEMENT_H_
|
||||||
|
#define SRC_TINT_SEM_BREAK_IF_STATEMENT_H_
|
||||||
|
|
||||||
|
#include "src/tint/sem/statement.h"
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
namespace tint::ast {
|
||||||
|
class BreakIfStatement;
|
||||||
|
} // namespace tint::ast
|
||||||
|
namespace tint::sem {
|
||||||
|
class Expression;
|
||||||
|
} // namespace tint::sem
|
||||||
|
|
||||||
|
namespace tint::sem {
|
||||||
|
|
||||||
|
/// Holds semantic information about a break-if statement
|
||||||
|
class BreakIfStatement final : public Castable<BreakIfStatement, CompoundStatement> {
|
||||||
|
public:
|
||||||
|
/// Constructor
|
||||||
|
/// @param declaration the AST node for this break-if statement
|
||||||
|
/// @param parent the owning statement
|
||||||
|
/// @param function the owning function
|
||||||
|
BreakIfStatement(const ast::BreakIfStatement* declaration,
|
||||||
|
const CompoundStatement* parent,
|
||||||
|
const sem::Function* function);
|
||||||
|
|
||||||
|
/// Destructor
|
||||||
|
~BreakIfStatement() override;
|
||||||
|
|
||||||
|
/// @returns the AST node
|
||||||
|
const ast::BreakIfStatement* Declaration() const;
|
||||||
|
|
||||||
|
/// @returns the break-if-statement condition expression
|
||||||
|
const Expression* Condition() const { return condition_; }
|
||||||
|
|
||||||
|
/// Sets the break-if-statement condition expression
|
||||||
|
/// @param condition the break-if condition expression
|
||||||
|
void SetCondition(const Expression* condition) { condition_ = condition; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Expression* condition_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace tint::sem
|
||||||
|
|
||||||
|
#endif // SRC_TINT_SEM_BREAK_IF_STATEMENT_H_
|
|
@ -712,6 +712,16 @@ bool GeneratorImpl::EmitBreak(const ast::BreakStatement*) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GeneratorImpl::EmitBreakIf(const ast::BreakIfStatement* b) {
|
||||||
|
auto out = line();
|
||||||
|
out << "if (";
|
||||||
|
if (!EmitExpression(out, b->condition)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out << ") { break; }";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool GeneratorImpl::EmitCall(std::ostream& out, const ast::CallExpression* expr) {
|
bool GeneratorImpl::EmitCall(std::ostream& out, const ast::CallExpression* expr) {
|
||||||
auto* call = builder_.Sem().Get<sem::Call>(expr);
|
auto* call = builder_.Sem().Get<sem::Call>(expr);
|
||||||
auto* target = call->Target();
|
auto* target = call->Target();
|
||||||
|
@ -2616,6 +2626,7 @@ bool GeneratorImpl::EmitStatement(const ast::Statement* stmt) {
|
||||||
[&](const ast::AssignmentStatement* a) { return EmitAssign(a); },
|
[&](const ast::AssignmentStatement* a) { return EmitAssign(a); },
|
||||||
[&](const ast::BlockStatement* b) { return EmitBlock(b); },
|
[&](const ast::BlockStatement* b) { return EmitBlock(b); },
|
||||||
[&](const ast::BreakStatement* b) { return EmitBreak(b); },
|
[&](const ast::BreakStatement* b) { return EmitBreak(b); },
|
||||||
|
[&](const ast::BreakIfStatement* b) { return EmitBreakIf(b); },
|
||||||
[&](const ast::CallStatement* c) {
|
[&](const ast::CallStatement* c) {
|
||||||
auto out = line();
|
auto out = line();
|
||||||
if (!EmitCall(out, c->expr)) {
|
if (!EmitCall(out, c->expr)) {
|
||||||
|
|
|
@ -139,6 +139,10 @@ class GeneratorImpl : public TextGenerator {
|
||||||
/// @param stmt the statement to emit
|
/// @param stmt the statement to emit
|
||||||
/// @returns true if the statement was emitted successfully
|
/// @returns true if the statement was emitted successfully
|
||||||
bool EmitBreak(const ast::BreakStatement* stmt);
|
bool EmitBreak(const ast::BreakStatement* stmt);
|
||||||
|
/// Handles a break-if statement
|
||||||
|
/// @param stmt the statement to emit
|
||||||
|
/// @returns true if the statement was emitted successfully
|
||||||
|
bool EmitBreakIf(const ast::BreakIfStatement* stmt);
|
||||||
/// Handles generating a call expression
|
/// Handles generating a call expression
|
||||||
/// @param out the output of the expression stream
|
/// @param out the output of the expression stream
|
||||||
/// @param expr the call expression
|
/// @param expr the call expression
|
||||||
|
|
|
@ -65,6 +65,31 @@ TEST_F(GlslGeneratorImplTest_Loop, Emit_LoopWithContinuing) {
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(GlslGeneratorImplTest_Loop, Emit_LoopWithContinuing_BreakIf) {
|
||||||
|
Func("a_statement", {}, ty.void_(), {});
|
||||||
|
|
||||||
|
auto* body = Block(create<ast::DiscardStatement>());
|
||||||
|
auto* continuing = Block(CallStmt(Call("a_statement")), BreakIf(true));
|
||||||
|
auto* l = Loop(body, continuing);
|
||||||
|
|
||||||
|
Func("F", utils::Empty, ty.void_(), utils::Vector{l},
|
||||||
|
utils::Vector{Stage(ast::PipelineStage::kFragment)});
|
||||||
|
|
||||||
|
GeneratorImpl& gen = Build();
|
||||||
|
|
||||||
|
gen.increment_indent();
|
||||||
|
|
||||||
|
ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
|
||||||
|
EXPECT_EQ(gen.result(), R"( while (true) {
|
||||||
|
discard;
|
||||||
|
{
|
||||||
|
a_statement();
|
||||||
|
if (true) { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(GlslGeneratorImplTest_Loop, Emit_LoopNestedWithContinuing) {
|
TEST_F(GlslGeneratorImplTest_Loop, Emit_LoopNestedWithContinuing) {
|
||||||
Func("a_statement", {}, ty.void_(), {});
|
Func("a_statement", {}, ty.void_(), {});
|
||||||
|
|
||||||
|
|
|
@ -942,6 +942,16 @@ bool GeneratorImpl::EmitBreak(const ast::BreakStatement*) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GeneratorImpl::EmitBreakIf(const ast::BreakIfStatement* b) {
|
||||||
|
auto out = line();
|
||||||
|
out << "if (";
|
||||||
|
if (!EmitExpression(out, b->condition)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out << ") { break; }";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool GeneratorImpl::EmitCall(std::ostream& out, const ast::CallExpression* expr) {
|
bool GeneratorImpl::EmitCall(std::ostream& out, const ast::CallExpression* expr) {
|
||||||
auto* call = builder_.Sem().Get<sem::Call>(expr);
|
auto* call = builder_.Sem().Get<sem::Call>(expr);
|
||||||
auto* target = call->Target();
|
auto* target = call->Target();
|
||||||
|
@ -3591,6 +3601,9 @@ bool GeneratorImpl::EmitStatement(const ast::Statement* stmt) {
|
||||||
[&](const ast::BreakStatement* b) { //
|
[&](const ast::BreakStatement* b) { //
|
||||||
return EmitBreak(b);
|
return EmitBreak(b);
|
||||||
},
|
},
|
||||||
|
[&](const ast::BreakIfStatement* b) { //
|
||||||
|
return EmitBreakIf(b);
|
||||||
|
},
|
||||||
[&](const ast::CallStatement* c) { //
|
[&](const ast::CallStatement* c) { //
|
||||||
auto out = line();
|
auto out = line();
|
||||||
if (!EmitCall(out, c->expr)) {
|
if (!EmitCall(out, c->expr)) {
|
||||||
|
|
|
@ -125,6 +125,10 @@ class GeneratorImpl : public TextGenerator {
|
||||||
/// @param stmt the statement to emit
|
/// @param stmt the statement to emit
|
||||||
/// @returns true if the statement was emitted successfully
|
/// @returns true if the statement was emitted successfully
|
||||||
bool EmitBreak(const ast::BreakStatement* stmt);
|
bool EmitBreak(const ast::BreakStatement* stmt);
|
||||||
|
/// Handles a break-if statement
|
||||||
|
/// @param stmt the statement to emit
|
||||||
|
/// @returns true if the statement was emitted successfully
|
||||||
|
bool EmitBreakIf(const ast::BreakIfStatement* stmt);
|
||||||
/// Handles generating a call expression
|
/// Handles generating a call expression
|
||||||
/// @param out the output of the expression stream
|
/// @param out the output of the expression stream
|
||||||
/// @param expr the call expression
|
/// @param expr the call expression
|
||||||
|
|
|
@ -65,6 +65,31 @@ TEST_F(HlslGeneratorImplTest_Loop, Emit_LoopWithContinuing) {
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(HlslGeneratorImplTest_Loop, Emit_LoopWithContinuing_BreakIf) {
|
||||||
|
Func("a_statement", {}, ty.void_(), {});
|
||||||
|
|
||||||
|
auto* body = Block(create<ast::DiscardStatement>());
|
||||||
|
auto* continuing = Block(CallStmt(Call("a_statement")), BreakIf(true));
|
||||||
|
auto* l = Loop(body, continuing);
|
||||||
|
|
||||||
|
Func("F", utils::Empty, ty.void_(), utils::Vector{l},
|
||||||
|
utils::Vector{Stage(ast::PipelineStage::kFragment)});
|
||||||
|
|
||||||
|
GeneratorImpl& gen = Build();
|
||||||
|
|
||||||
|
gen.increment_indent();
|
||||||
|
|
||||||
|
ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
|
||||||
|
EXPECT_EQ(gen.result(), R"( while (true) {
|
||||||
|
discard;
|
||||||
|
{
|
||||||
|
a_statement();
|
||||||
|
if (true) { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(HlslGeneratorImplTest_Loop, Emit_LoopNestedWithContinuing) {
|
TEST_F(HlslGeneratorImplTest_Loop, Emit_LoopNestedWithContinuing) {
|
||||||
Func("a_statement", {}, ty.void_(), {});
|
Func("a_statement", {}, ty.void_(), {});
|
||||||
|
|
||||||
|
|
|
@ -660,6 +660,16 @@ bool GeneratorImpl::EmitBreak(const ast::BreakStatement*) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GeneratorImpl::EmitBreakIf(const ast::BreakIfStatement* b) {
|
||||||
|
auto out = line();
|
||||||
|
out << "if (";
|
||||||
|
if (!EmitExpression(out, b->condition)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out << ") { break; }";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool GeneratorImpl::EmitCall(std::ostream& out, const ast::CallExpression* expr) {
|
bool GeneratorImpl::EmitCall(std::ostream& out, const ast::CallExpression* expr) {
|
||||||
auto* call = program_->Sem().Get<sem::Call>(expr);
|
auto* call = program_->Sem().Get<sem::Call>(expr);
|
||||||
auto* target = call->Target();
|
auto* target = call->Target();
|
||||||
|
@ -2433,6 +2443,9 @@ bool GeneratorImpl::EmitStatement(const ast::Statement* stmt) {
|
||||||
[&](const ast::BreakStatement* b) { //
|
[&](const ast::BreakStatement* b) { //
|
||||||
return EmitBreak(b);
|
return EmitBreak(b);
|
||||||
},
|
},
|
||||||
|
[&](const ast::BreakIfStatement* b) { //
|
||||||
|
return EmitBreakIf(b);
|
||||||
|
},
|
||||||
[&](const ast::CallStatement* c) { //
|
[&](const ast::CallStatement* c) { //
|
||||||
auto out = line();
|
auto out = line();
|
||||||
if (!EmitCall(out, c->expr)) { //
|
if (!EmitCall(out, c->expr)) { //
|
||||||
|
|
|
@ -129,6 +129,10 @@ class GeneratorImpl : public TextGenerator {
|
||||||
/// @param stmt the statement to emit
|
/// @param stmt the statement to emit
|
||||||
/// @returns true if the statement was emitted successfully
|
/// @returns true if the statement was emitted successfully
|
||||||
bool EmitBreak(const ast::BreakStatement* stmt);
|
bool EmitBreak(const ast::BreakStatement* stmt);
|
||||||
|
/// Handles a break-if statement
|
||||||
|
/// @param stmt the statement to emit
|
||||||
|
/// @returns true if the statement was emitted successfully
|
||||||
|
bool EmitBreakIf(const ast::BreakIfStatement* stmt);
|
||||||
/// Handles generating a call expression
|
/// Handles generating a call expression
|
||||||
/// @param out the output of the expression stream
|
/// @param out the output of the expression stream
|
||||||
/// @param expr the call expression
|
/// @param expr the call expression
|
||||||
|
|
|
@ -65,6 +65,31 @@ TEST_F(MslGeneratorImplTest, Emit_LoopWithContinuing) {
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(MslGeneratorImplTest, Emit_LoopWithContinuing_BreakIf) {
|
||||||
|
Func("a_statement", {}, ty.void_(), {});
|
||||||
|
|
||||||
|
auto* body = Block(create<ast::DiscardStatement>());
|
||||||
|
auto* continuing = Block(CallStmt(Call("a_statement")), BreakIf(true));
|
||||||
|
auto* l = Loop(body, continuing);
|
||||||
|
|
||||||
|
Func("F", utils::Empty, ty.void_(), utils::Vector{l},
|
||||||
|
utils::Vector{Stage(ast::PipelineStage::kFragment)});
|
||||||
|
|
||||||
|
GeneratorImpl& gen = Build();
|
||||||
|
|
||||||
|
gen.increment_indent();
|
||||||
|
|
||||||
|
ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
|
||||||
|
EXPECT_EQ(gen.result(), R"( while (true) {
|
||||||
|
discard_fragment();
|
||||||
|
{
|
||||||
|
a_statement();
|
||||||
|
if (true) { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(MslGeneratorImplTest, Emit_LoopNestedWithContinuing) {
|
TEST_F(MslGeneratorImplTest, Emit_LoopNestedWithContinuing) {
|
||||||
Func("a_statement", {}, ty.void_(), utils::Empty);
|
Func("a_statement", {}, ty.void_(), utils::Empty);
|
||||||
|
|
||||||
|
|
|
@ -455,6 +455,19 @@ bool Builder::GenerateBreakStatement(const ast::BreakStatement*) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Builder::GenerateBreakIfStatement(const ast::BreakIfStatement* stmt) {
|
||||||
|
TINT_ASSERT(Writer, !backedge_stack_.empty());
|
||||||
|
const auto cond_id = GenerateExpressionWithLoadIfNeeded(stmt->condition);
|
||||||
|
if (!cond_id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const ContinuingInfo& ci = continuing_stack_.back();
|
||||||
|
backedge_stack_.back() =
|
||||||
|
Backedge(spv::Op::OpBranchConditional,
|
||||||
|
{Operand(cond_id), Operand(ci.break_target_id), Operand(ci.loop_header_id)});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Builder::GenerateContinueStatement(const ast::ContinueStatement*) {
|
bool Builder::GenerateContinueStatement(const ast::ContinueStatement*) {
|
||||||
if (continue_stack_.empty()) {
|
if (continue_stack_.empty()) {
|
||||||
error_ = "Attempted to continue without a continue block";
|
error_ = "Attempted to continue without a continue block";
|
||||||
|
@ -3400,6 +3413,8 @@ bool Builder::GenerateIfStatement(const ast::IfStatement* stmt) {
|
||||||
// continuing { ...
|
// continuing { ...
|
||||||
// if (cond) {} else {break;}
|
// if (cond) {} else {break;}
|
||||||
// }
|
// }
|
||||||
|
//
|
||||||
|
// TODO(crbug.com/tint/1451): Remove this when the if break construct is made an error.
|
||||||
auto is_just_a_break = [](const ast::BlockStatement* block) {
|
auto is_just_a_break = [](const ast::BlockStatement* block) {
|
||||||
return block && (block->statements.Length() == 1) &&
|
return block && (block->statements.Length() == 1) &&
|
||||||
block->Last()->Is<ast::BreakStatement>();
|
block->Last()->Is<ast::BreakStatement>();
|
||||||
|
@ -3643,6 +3658,7 @@ bool Builder::GenerateStatement(const ast::Statement* stmt) {
|
||||||
stmt, [&](const ast::AssignmentStatement* a) { return GenerateAssignStatement(a); },
|
stmt, [&](const ast::AssignmentStatement* a) { return GenerateAssignStatement(a); },
|
||||||
[&](const ast::BlockStatement* b) { return GenerateBlockStatement(b); },
|
[&](const ast::BlockStatement* b) { return GenerateBlockStatement(b); },
|
||||||
[&](const ast::BreakStatement* b) { return GenerateBreakStatement(b); },
|
[&](const ast::BreakStatement* b) { return GenerateBreakStatement(b); },
|
||||||
|
[&](const ast::BreakIfStatement* b) { return GenerateBreakIfStatement(b); },
|
||||||
[&](const ast::CallStatement* c) { return GenerateCallExpression(c->expr) != 0; },
|
[&](const ast::CallStatement* c) { return GenerateCallExpression(c->expr) != 0; },
|
||||||
[&](const ast::ContinueStatement* c) { return GenerateContinueStatement(c); },
|
[&](const ast::ContinueStatement* c) { return GenerateContinueStatement(c); },
|
||||||
[&](const ast::DiscardStatement* d) { return GenerateDiscardStatement(d); },
|
[&](const ast::DiscardStatement* d) { return GenerateDiscardStatement(d); },
|
||||||
|
@ -3659,7 +3675,7 @@ bool Builder::GenerateStatement(const ast::Statement* stmt) {
|
||||||
return true; // Not emitted
|
return true; // Not emitted
|
||||||
},
|
},
|
||||||
[&](Default) {
|
[&](Default) {
|
||||||
error_ = "Unknown statement: " + std::string(stmt->TypeInfo().name);
|
error_ = "unknown statement type: " + std::string(stmt->TypeInfo().name);
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -248,6 +248,10 @@ class Builder {
|
||||||
/// @param stmt the statement to generate
|
/// @param stmt the statement to generate
|
||||||
/// @returns true if the statement was successfully generated
|
/// @returns true if the statement was successfully generated
|
||||||
bool GenerateBreakStatement(const ast::BreakStatement* stmt);
|
bool GenerateBreakStatement(const ast::BreakStatement* stmt);
|
||||||
|
/// Generates a break-if statement
|
||||||
|
/// @param stmt the statement to generate
|
||||||
|
/// @returns true if the statement was successfully generated
|
||||||
|
bool GenerateBreakIfStatement(const ast::BreakIfStatement* stmt);
|
||||||
/// Generates a continue statement
|
/// Generates a continue statement
|
||||||
/// @param stmt the statement to generate
|
/// @param stmt the statement to generate
|
||||||
/// @returns true if the statement was successfully generated
|
/// @returns true if the statement was successfully generated
|
||||||
|
|
|
@ -234,12 +234,11 @@ OpBranch %1
|
||||||
TEST_F(BuilderTest, Loop_WithContinuing_BreakIf) {
|
TEST_F(BuilderTest, Loop_WithContinuing_BreakIf) {
|
||||||
// loop {
|
// loop {
|
||||||
// continuing {
|
// continuing {
|
||||||
// if (true) { break; }
|
// break if (true);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
auto* if_stmt = If(Expr(true), Block(Break()));
|
auto* continuing = Block(BreakIf(true));
|
||||||
auto* continuing = Block(if_stmt);
|
|
||||||
auto* loop = Loop(Block(), continuing);
|
auto* loop = Loop(Block(), continuing);
|
||||||
WrapInFunction(loop);
|
WrapInFunction(loop);
|
||||||
|
|
||||||
|
@ -267,11 +266,10 @@ OpBranchConditional %6 %2 %1
|
||||||
TEST_F(BuilderTest, Loop_WithContinuing_BreakUnless) {
|
TEST_F(BuilderTest, Loop_WithContinuing_BreakUnless) {
|
||||||
// loop {
|
// loop {
|
||||||
// continuing {
|
// continuing {
|
||||||
// if (true) {} else { break; }
|
// break if (false);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
auto* if_stmt = If(Expr(true), Block(), Else(Block(Break())));
|
auto* continuing = Block(BreakIf(false));
|
||||||
auto* continuing = Block(if_stmt);
|
|
||||||
auto* loop = Loop(Block(), continuing);
|
auto* loop = Loop(Block(), continuing);
|
||||||
WrapInFunction(loop);
|
WrapInFunction(loop);
|
||||||
|
|
||||||
|
@ -281,7 +279,7 @@ TEST_F(BuilderTest, Loop_WithContinuing_BreakUnless) {
|
||||||
|
|
||||||
EXPECT_TRUE(b.GenerateLoopStatement(loop)) << b.error();
|
EXPECT_TRUE(b.GenerateLoopStatement(loop)) << b.error();
|
||||||
EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeBool
|
EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeBool
|
||||||
%6 = OpConstantTrue %5
|
%6 = OpConstantNull %5
|
||||||
)");
|
)");
|
||||||
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
|
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
|
||||||
R"(OpBranch %1
|
R"(OpBranch %1
|
||||||
|
@ -291,7 +289,7 @@ OpBranch %4
|
||||||
%4 = OpLabel
|
%4 = OpLabel
|
||||||
OpBranch %3
|
OpBranch %3
|
||||||
%3 = OpLabel
|
%3 = OpLabel
|
||||||
OpBranchConditional %6 %1 %2
|
OpBranchConditional %6 %2 %1
|
||||||
%2 = OpLabel
|
%2 = OpLabel
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
@ -300,13 +298,12 @@ TEST_F(BuilderTest, Loop_WithContinuing_BreakIf_ConditionIsVar) {
|
||||||
// loop {
|
// loop {
|
||||||
// continuing {
|
// continuing {
|
||||||
// var cond = true;
|
// var cond = true;
|
||||||
// if (cond) { break; }
|
// break if (cond);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
auto* cond_var = Decl(Var("cond", Expr(true)));
|
auto* cond_var = Decl(Var("cond", Expr(true)));
|
||||||
auto* if_stmt = If(Expr("cond"), Block(Break()));
|
auto* continuing = Block(cond_var, BreakIf("cond"));
|
||||||
auto* continuing = Block(cond_var, if_stmt);
|
|
||||||
auto* loop = Loop(Block(), continuing);
|
auto* loop = Loop(Block(), continuing);
|
||||||
WrapInFunction(loop);
|
WrapInFunction(loop);
|
||||||
|
|
||||||
|
@ -379,19 +376,17 @@ TEST_F(BuilderTest, Loop_WithContinuing_BreakIf_Nested) {
|
||||||
// continuing {
|
// continuing {
|
||||||
// loop {
|
// loop {
|
||||||
// continuing {
|
// continuing {
|
||||||
// if (true) { break; }
|
// break if (true);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// if (true) { break; }
|
// break if (true);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
auto* inner_if_stmt = If(Expr(true), Block(Break()));
|
auto* inner_continuing = Block(BreakIf(true));
|
||||||
auto* inner_continuing = Block(inner_if_stmt);
|
|
||||||
auto* inner_loop = Loop(Block(), inner_continuing);
|
auto* inner_loop = Loop(Block(), inner_continuing);
|
||||||
|
|
||||||
auto* outer_if_stmt = If(Expr(true), Block(Break()));
|
auto* outer_continuing = Block(inner_loop, BreakIf(true));
|
||||||
auto* outer_continuing = Block(inner_loop, outer_if_stmt);
|
|
||||||
auto* outer_loop = Loop(Block(), outer_continuing);
|
auto* outer_loop = Loop(Block(), outer_continuing);
|
||||||
|
|
||||||
WrapInFunction(outer_loop);
|
WrapInFunction(outer_loop);
|
||||||
|
|
|
@ -958,6 +958,7 @@ bool GeneratorImpl::EmitStatement(const ast::Statement* stmt) {
|
||||||
[&](const ast::AssignmentStatement* a) { return EmitAssign(a); },
|
[&](const ast::AssignmentStatement* a) { return EmitAssign(a); },
|
||||||
[&](const ast::BlockStatement* b) { return EmitBlock(b); },
|
[&](const ast::BlockStatement* b) { return EmitBlock(b); },
|
||||||
[&](const ast::BreakStatement* b) { return EmitBreak(b); },
|
[&](const ast::BreakStatement* b) { return EmitBreak(b); },
|
||||||
|
[&](const ast::BreakIfStatement* b) { return EmitBreakIf(b); },
|
||||||
[&](const ast::CallStatement* c) {
|
[&](const ast::CallStatement* c) {
|
||||||
auto out = line();
|
auto out = line();
|
||||||
if (!EmitCall(out, c->expr)) {
|
if (!EmitCall(out, c->expr)) {
|
||||||
|
@ -1023,6 +1024,17 @@ bool GeneratorImpl::EmitBreak(const ast::BreakStatement*) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GeneratorImpl::EmitBreakIf(const ast::BreakIfStatement* b) {
|
||||||
|
auto out = line();
|
||||||
|
|
||||||
|
out << "break if ";
|
||||||
|
if (!EmitExpression(out, b->condition)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out << ";";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool GeneratorImpl::EmitCase(const ast::CaseStatement* stmt) {
|
bool GeneratorImpl::EmitCase(const ast::CaseStatement* stmt) {
|
||||||
if (stmt->selectors.Length() == 1 && stmt->ContainsDefault()) {
|
if (stmt->selectors.Length() == 1 && stmt->ContainsDefault()) {
|
||||||
line() << "default: {";
|
line() << "default: {";
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "src/tint/ast/assignment_statement.h"
|
#include "src/tint/ast/assignment_statement.h"
|
||||||
#include "src/tint/ast/binary_expression.h"
|
#include "src/tint/ast/binary_expression.h"
|
||||||
#include "src/tint/ast/bitcast_expression.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/break_statement.h"
|
||||||
#include "src/tint/ast/compound_assignment_statement.h"
|
#include "src/tint/ast/compound_assignment_statement.h"
|
||||||
#include "src/tint/ast/continue_statement.h"
|
#include "src/tint/ast/continue_statement.h"
|
||||||
|
@ -92,6 +93,10 @@ class GeneratorImpl : public TextGenerator {
|
||||||
/// @param stmt the statement to emit
|
/// @param stmt the statement to emit
|
||||||
/// @returns true if the statement was emitted successfully
|
/// @returns true if the statement was emitted successfully
|
||||||
bool EmitBreak(const ast::BreakStatement* stmt);
|
bool EmitBreak(const ast::BreakStatement* stmt);
|
||||||
|
/// Handles a break-if statement
|
||||||
|
/// @param stmt the statement to emit
|
||||||
|
/// @returns true if the statement was emitted successfully
|
||||||
|
bool EmitBreakIf(const ast::BreakIfStatement* stmt);
|
||||||
/// Handles generating a call expression
|
/// Handles generating a call expression
|
||||||
/// @param out the output of the expression stream
|
/// @param out the output of the expression stream
|
||||||
/// @param expr the call expression
|
/// @param expr the call expression
|
||||||
|
|
|
@ -65,6 +65,32 @@ TEST_F(WgslGeneratorImplTest, Emit_LoopWithContinuing) {
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(WgslGeneratorImplTest, Emit_LoopWithContinuing_BreakIf) {
|
||||||
|
Func("a_statement", {}, ty.void_(), {});
|
||||||
|
|
||||||
|
auto* body = Block(create<ast::DiscardStatement>());
|
||||||
|
auto* continuing = Block(CallStmt(Call("a_statement")), BreakIf(true));
|
||||||
|
auto* l = Loop(body, continuing);
|
||||||
|
|
||||||
|
Func("F", utils::Empty, ty.void_(), utils::Vector{l},
|
||||||
|
utils::Vector{Stage(ast::PipelineStage::kFragment)});
|
||||||
|
|
||||||
|
GeneratorImpl& gen = Build();
|
||||||
|
|
||||||
|
gen.increment_indent();
|
||||||
|
|
||||||
|
ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
|
||||||
|
EXPECT_EQ(gen.result(), R"( loop {
|
||||||
|
discard;
|
||||||
|
|
||||||
|
continuing {
|
||||||
|
a_statement();
|
||||||
|
break if true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(WgslGeneratorImplTest, Emit_ForLoopWithMultiStmtInit) {
|
TEST_F(WgslGeneratorImplTest, Emit_ForLoopWithMultiStmtInit) {
|
||||||
// var<workgroup> a : atomic<i32>;
|
// var<workgroup> a : atomic<i32>;
|
||||||
// for({ignore(1i); ignore(2i);}; ; ) {
|
// for({ignore(1i); ignore(2i);}; ; ) {
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
bug/tint/1064.wgsl:12:9 warning: use of deprecated language feature: `break` must not be used to exit from a continuing block. Use break-if instead.
|
||||||
|
break;
|
||||||
|
^^^^^
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
while (true) {
|
while (true) {
|
||||||
if (false) {
|
if (false) {
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
bug/tint/1064.wgsl:12:9 warning: use of deprecated language feature: `break` must not be used to exit from a continuing block. Use break-if instead.
|
||||||
|
break;
|
||||||
|
^^^^^
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
while (true) {
|
while (true) {
|
||||||
if (false) {
|
if (false) {
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
bug/tint/1064.wgsl:12:9 warning: use of deprecated language feature: `break` must not be used to exit from a continuing block. Use break-if instead.
|
||||||
|
break;
|
||||||
|
^^^^^
|
||||||
|
|
||||||
#version 310 es
|
#version 310 es
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
bug/tint/1064.wgsl:12:9 warning: use of deprecated language feature: `break` must not be used to exit from a continuing block. Use break-if instead.
|
||||||
|
break;
|
||||||
|
^^^^^
|
||||||
|
|
||||||
#include <metal_stdlib>
|
#include <metal_stdlib>
|
||||||
|
|
||||||
using namespace metal;
|
using namespace metal;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
bug/tint/1064.wgsl:12:9 warning: use of deprecated language feature: `break` must not be used to exit from a continuing block. Use break-if instead.
|
||||||
|
break;
|
||||||
|
^^^^^
|
||||||
|
|
||||||
; SPIR-V
|
; SPIR-V
|
||||||
; Version: 1.3
|
; Version: 1.3
|
||||||
; Generator: Google Tint Compiler; 0
|
; Generator: Google Tint Compiler; 0
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
bug/tint/1064.wgsl:12:9 warning: use of deprecated language feature: `break` must not be used to exit from a continuing block. Use break-if instead.
|
||||||
|
break;
|
||||||
|
^^^^^
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn main() {
|
fn main() {
|
||||||
loop {
|
loop {
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
fn f() -> i32 {
|
||||||
|
var i : i32;
|
||||||
|
loop {
|
||||||
|
if (i > 4) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
continuing {
|
||||||
|
i = i + 1;
|
||||||
|
|
||||||
|
break if i == 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
[numthreads(1, 1, 1)]
|
||||||
|
void unused_entry_point() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int f() {
|
||||||
|
int i = 0;
|
||||||
|
while (true) {
|
||||||
|
if ((i > 4)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
i = (i + 1);
|
||||||
|
if ((i == 4)) { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
[numthreads(1, 1, 1)]
|
||||||
|
void unused_entry_point() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int f() {
|
||||||
|
int i = 0;
|
||||||
|
while (true) {
|
||||||
|
if ((i > 4)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
i = (i + 1);
|
||||||
|
if ((i == 4)) { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
#version 310 es
|
||||||
|
|
||||||
|
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
||||||
|
void unused_entry_point() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int f() {
|
||||||
|
int i = 0;
|
||||||
|
while (true) {
|
||||||
|
if ((i > 4)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
i = (i + 1);
|
||||||
|
if ((i == 4)) { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
#include <metal_stdlib>
|
||||||
|
|
||||||
|
using namespace metal;
|
||||||
|
int f() {
|
||||||
|
int i = 0;
|
||||||
|
while (true) {
|
||||||
|
if ((i > 4)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
i = as_type<int>((as_type<uint>(i) + as_type<uint>(1)));
|
||||||
|
if ((i == 4)) { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
; SPIR-V
|
||||||
|
; Version: 1.3
|
||||||
|
; Generator: Google Tint Compiler; 0
|
||||||
|
; Bound: 29
|
||||||
|
; Schema: 0
|
||||||
|
OpCapability Shader
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
|
||||||
|
OpExecutionMode %unused_entry_point LocalSize 1 1 1
|
||||||
|
OpName %unused_entry_point "unused_entry_point"
|
||||||
|
OpName %f "f"
|
||||||
|
OpName %i "i"
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%1 = OpTypeFunction %void
|
||||||
|
%int = OpTypeInt 32 1
|
||||||
|
%5 = OpTypeFunction %int
|
||||||
|
%_ptr_Function_int = OpTypePointer Function %int
|
||||||
|
%11 = OpConstantNull %int
|
||||||
|
%int_4 = OpConstant %int 4
|
||||||
|
%bool = OpTypeBool
|
||||||
|
%int_1 = OpConstant %int 1
|
||||||
|
%unused_entry_point = OpFunction %void None %1
|
||||||
|
%4 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%f = OpFunction %int None %5
|
||||||
|
%8 = OpLabel
|
||||||
|
%i = OpVariable %_ptr_Function_int Function %11
|
||||||
|
OpBranch %12
|
||||||
|
%12 = OpLabel
|
||||||
|
OpLoopMerge %13 %14 None
|
||||||
|
OpBranch %15
|
||||||
|
%15 = OpLabel
|
||||||
|
%16 = OpLoad %int %i
|
||||||
|
%18 = OpSGreaterThan %bool %16 %int_4
|
||||||
|
OpSelectionMerge %20 None
|
||||||
|
OpBranchConditional %18 %21 %20
|
||||||
|
%21 = OpLabel
|
||||||
|
%22 = OpLoad %int %i
|
||||||
|
OpReturnValue %22
|
||||||
|
%20 = OpLabel
|
||||||
|
OpBranch %14
|
||||||
|
%14 = OpLabel
|
||||||
|
%23 = OpLoad %int %i
|
||||||
|
%25 = OpIAdd %int %23 %int_1
|
||||||
|
OpStore %i %25
|
||||||
|
%26 = OpLoad %int %i
|
||||||
|
%27 = OpIEqual %bool %26 %int_4
|
||||||
|
OpBranchConditional %27 %13 %12
|
||||||
|
%13 = OpLabel
|
||||||
|
%28 = OpLoad %int %i
|
||||||
|
OpReturnValue %28
|
||||||
|
OpFunctionEnd
|
|
@ -0,0 +1,14 @@
|
||||||
|
fn f() -> i32 {
|
||||||
|
var i : i32;
|
||||||
|
loop {
|
||||||
|
if ((i > 4)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
continuing {
|
||||||
|
i = (i + 1);
|
||||||
|
break if (i == 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
Loading…
Reference in New Issue