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_literal_expression.cc",
|
||||
"ast/bool_literal_expression.h",
|
||||
"ast/break_if_statement.cc",
|
||||
"ast/break_if_statement.h",
|
||||
"ast/break_statement.cc",
|
||||
"ast/break_statement.h",
|
||||
"ast/builtin_attribute.cc",
|
||||
|
@ -421,6 +423,7 @@ libtint_source_set("libtint_core_all_src") {
|
|||
"sem/behavior.h",
|
||||
"sem/binding_point.h",
|
||||
"sem/bool.h",
|
||||
"sem/break_if_statement.h",
|
||||
"sem/builtin.h",
|
||||
"sem/builtin_type.h",
|
||||
"sem/call.h",
|
||||
|
@ -630,6 +633,8 @@ libtint_source_set("libtint_sem_src") {
|
|||
"sem/block_statement.cc",
|
||||
"sem/bool.cc",
|
||||
"sem/bool.h",
|
||||
"sem/break_if_statement.cc",
|
||||
"sem/break_if_statement.h",
|
||||
"sem/builtin.cc",
|
||||
"sem/builtin.h",
|
||||
"sem/builtin_type.cc",
|
||||
|
@ -1016,6 +1021,7 @@ if (tint_build_unittests) {
|
|||
"ast/block_statement_test.cc",
|
||||
"ast/bool_literal_expression_test.cc",
|
||||
"ast/bool_test.cc",
|
||||
"ast/break_if_statement_test.cc",
|
||||
"ast/break_statement_test.cc",
|
||||
"ast/builtin_attribute_test.cc",
|
||||
"ast/builtin_texture_helper_test.cc",
|
||||
|
|
|
@ -73,6 +73,8 @@ set(TINT_LIB_SRCS
|
|||
ast/bool_literal_expression.h
|
||||
ast/bool.cc
|
||||
ast/bool.h
|
||||
ast/break_if_statement.cc
|
||||
ast/break_if_statement.h
|
||||
ast/break_statement.cc
|
||||
ast/break_statement.h
|
||||
ast/builtin_attribute.cc
|
||||
|
@ -292,6 +294,8 @@ set(TINT_LIB_SRCS
|
|||
sem/block_statement.h
|
||||
sem/bool.cc
|
||||
sem/bool.h
|
||||
sem/break_if_statement.cc
|
||||
sem/break_if_statement.h
|
||||
sem/builtin_type.cc
|
||||
sem/builtin_type.h
|
||||
sem/builtin.cc
|
||||
|
@ -708,6 +712,7 @@ if(TINT_BUILD_TESTS)
|
|||
ast/block_statement_test.cc
|
||||
ast/bool_literal_expression_test.cc
|
||||
ast/bool_test.cc
|
||||
ast/break_if_statement_test.cc
|
||||
ast/break_statement_test.cc
|
||||
ast/builtin_attribute_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/bool.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/call_expression.h"
|
||||
#include "src/tint/ast/call_statement.h"
|
||||
|
@ -2416,6 +2417,23 @@ class ProgramBuilder {
|
|||
/// @returns the break statement pointer
|
||||
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
|
||||
/// @param source the source information
|
||||
/// @returns the continue statement pointer
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "src/tint/ast/atomic.h"
|
||||
#include "src/tint/ast/block_statement.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/call_statement.h"
|
||||
#include "src/tint/ast/compound_assignment_statement.h"
|
||||
|
@ -263,9 +264,8 @@ class DependencyScanner {
|
|||
TINT_DEFER(scope_stack_.Pop());
|
||||
TraverseStatements(b->statements);
|
||||
},
|
||||
[&](const ast::CallStatement* r) { //
|
||||
TraverseExpression(r->expr);
|
||||
},
|
||||
[&](const ast::BreakIfStatement* b) { TraverseExpression(b->condition); },
|
||||
[&](const ast::CallStatement* r) { TraverseExpression(r->expr); },
|
||||
[&](const ast::CompoundAssignmentStatement* a) {
|
||||
TraverseExpression(a->lhs);
|
||||
TraverseExpression(a->rhs);
|
||||
|
@ -292,9 +292,7 @@ class DependencyScanner {
|
|||
TraverseStatement(i->else_statement);
|
||||
}
|
||||
},
|
||||
[&](const ast::ReturnStatement* r) { //
|
||||
TraverseExpression(r->value);
|
||||
},
|
||||
[&](const ast::ReturnStatement* r) { TraverseExpression(r->value); },
|
||||
[&](const ast::SwitchStatement* s) {
|
||||
TraverseExpression(s->condition);
|
||||
for (auto* c : s->body) {
|
||||
|
|
|
@ -1260,7 +1260,7 @@ TEST_F(ResolverDependencyGraphTraversalTest, SymbolsReached) {
|
|||
Block( //
|
||||
Assign(V, V))), //
|
||||
Loop(Block(Assign(V, V)), //
|
||||
Block(Assign(V, V))), //
|
||||
Block(Assign(V, V), BreakIf(V))), //
|
||||
Switch(V, //
|
||||
Case(CaseSelector(1_i), //
|
||||
Block(Assign(V, V))), //
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
#include "src/tint/sem/abstract_int.h"
|
||||
#include "src/tint/sem/array.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/depth_multisampled_texture.h"
|
||||
#include "src/tint/sem/depth_texture.h"
|
||||
|
@ -1213,6 +1214,7 @@ sem::Statement* Resolver::Statement(const ast::Statement* stmt) {
|
|||
// Non-Compound statements
|
||||
[&](const ast::AssignmentStatement* a) { return AssignmentStatement(a); },
|
||||
[&](const ast::BreakStatement* b) { return BreakStatement(b); },
|
||||
[&](const ast::BreakIfStatement* b) { return BreakIfStatement(b); },
|
||||
[&](const ast::CallStatement* c) { return CallStatement(c); },
|
||||
[&](const ast::CompoundAssignmentStatement* c) { return CompoundAssignmentStatement(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) {
|
||||
auto* sem =
|
||||
builder_->create<sem::Statement>(stmt, current_compound_statement_, current_function_);
|
||||
|
|
|
@ -208,6 +208,7 @@ class Resolver {
|
|||
sem::Statement* AssignmentStatement(const ast::AssignmentStatement*);
|
||||
sem::BlockStatement* BlockStatement(const ast::BlockStatement*);
|
||||
sem::Statement* BreakStatement(const ast::BreakStatement*);
|
||||
sem::Statement* BreakIfStatement(const ast::BreakIfStatement*);
|
||||
sem::Statement* CallStatement(const ast::CallStatement*);
|
||||
sem::CaseStatement* CaseStatement(const ast::CaseStatement*, const sem::Type*);
|
||||
sem::Statement* CompoundAssignmentStatement(const ast::CompoundAssignmentStatement*);
|
||||
|
|
|
@ -569,6 +569,16 @@ TEST_F(ResolverBehaviorTest, StmtLoopEmpty_ContIfTrueBreak) {
|
|||
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) {
|
||||
auto* stmt = Return();
|
||||
WrapInFunction(stmt);
|
||||
|
|
|
@ -215,7 +215,8 @@ TEST_F(ResolverTest, Stmt_Loop) {
|
|||
auto* continuing_lhs = Expr("v");
|
||||
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);
|
||||
WrapInFunction(v, stmt);
|
||||
|
||||
|
@ -233,6 +234,7 @@ TEST_F(ResolverTest, Stmt_Loop) {
|
|||
EXPECT_EQ(BlockOf(body_rhs), body);
|
||||
EXPECT_EQ(BlockOf(continuing_lhs), continuing);
|
||||
EXPECT_EQ(BlockOf(continuing_rhs), continuing);
|
||||
EXPECT_EQ(BlockOf(break_if), continuing);
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, Stmt_Return) {
|
||||
|
|
|
@ -537,6 +537,51 @@ class UniformityGraph {
|
|||
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) {
|
||||
auto [cf1, _] = ProcessCall(cf, c->expr);
|
||||
return cf1;
|
||||
|
|
|
@ -1125,9 +1125,7 @@ fn foo() {
|
|||
workgroupBarrier();
|
||||
continuing {
|
||||
i = i + 1;
|
||||
if (i == n) {
|
||||
break;
|
||||
}
|
||||
break if (i == n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1146,9 +1144,7 @@ fn foo() {
|
|||
workgroupBarrier();
|
||||
continuing {
|
||||
i = i + 1;
|
||||
if (i == n) {
|
||||
break;
|
||||
}
|
||||
break if (i == n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1161,12 +1157,12 @@ fn foo() {
|
|||
^^^^^^^^^^^^^^^^
|
||||
|
||||
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
|
||||
if (i == n) {
|
||||
^
|
||||
test:10:22 note: reading from read_write storage buffer 'n' may result in a non-uniform value
|
||||
break if (i == n);
|
||||
^
|
||||
)");
|
||||
}
|
||||
|
||||
|
@ -1180,9 +1176,7 @@ fn foo() {
|
|||
continuing {
|
||||
workgroupBarrier();
|
||||
i = i + 1;
|
||||
if (i == n) {
|
||||
break;
|
||||
}
|
||||
break if (i == n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1201,9 +1195,7 @@ fn foo() {
|
|||
continuing {
|
||||
workgroupBarrier();
|
||||
i = i + 1;
|
||||
if (i == n) {
|
||||
break;
|
||||
}
|
||||
break if (i == n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1216,12 +1208,12 @@ fn foo() {
|
|||
^^^^^^^^^^^^^^^^
|
||||
|
||||
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
|
||||
if (i == n) {
|
||||
^
|
||||
test:10:22 note: reading from read_write storage buffer 'n' may result in a non-uniform value
|
||||
break if (i == n);
|
||||
^
|
||||
)");
|
||||
}
|
||||
|
||||
|
@ -1249,9 +1241,7 @@ fn foo() {
|
|||
continuing {
|
||||
// Pretend that this isn't an infinite loop, in case the interrupt is a
|
||||
// continue statement.
|
||||
if (false) {
|
||||
break;
|
||||
}
|
||||
break if (false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1597,9 +1587,7 @@ fn foo() {
|
|||
if (v == 0) {
|
||||
workgroupBarrier();
|
||||
}
|
||||
if (true) {
|
||||
break;
|
||||
}
|
||||
break if (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -870,6 +870,75 @@ TEST_F(ResolverTest, Stmt_Loop_ContinueInContinuing_Indirect) {
|
|||
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) {
|
||||
// for(;; return) {
|
||||
// break;
|
||||
|
@ -1055,6 +1124,9 @@ TEST_F(ResolverValidationTest, Stmt_BreakInIfTrueInContinuing) {
|
|||
// }
|
||||
WrapInFunction(Loop(Block(), cont));
|
||||
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) {
|
||||
|
@ -1066,6 +1138,9 @@ TEST_F(ResolverValidationTest, Stmt_BreakInIfElseInContinuing) {
|
|||
// }
|
||||
WrapInFunction(Loop(Block(), cont));
|
||||
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) {
|
||||
|
@ -1075,6 +1150,8 @@ TEST_F(ResolverValidationTest, Stmt_BreakInContinuing) {
|
|||
WrapInFunction(Loop(Block(), cont));
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
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 "
|
||||
"statement of an if statement's true or false block, and that if "
|
||||
"statement must be the last statement of the continuing block\n"
|
||||
|
@ -1092,6 +1169,8 @@ TEST_F(ResolverValidationTest, Stmt_BreakInIfInIfInContinuing) {
|
|||
WrapInFunction(Loop(Block(), cont));
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
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 "
|
||||
"statement of an if statement's true or false block, and that if "
|
||||
"statement must be the last statement of the continuing block\n"
|
||||
|
@ -1109,6 +1188,8 @@ TEST_F(ResolverValidationTest, Stmt_BreakInIfTrueMultipleStmtsInContinuing) {
|
|||
WrapInFunction(Loop(Block(), cont));
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
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 "
|
||||
"statement of an if statement's true or false block, and that if "
|
||||
"statement must be the last statement of the continuing block\n"
|
||||
|
@ -1126,6 +1207,8 @@ TEST_F(ResolverValidationTest, Stmt_BreakInIfElseMultipleStmtsInContinuing) {
|
|||
WrapInFunction(Loop(Block(), cont));
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
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 "
|
||||
"statement of an if statement's true or false block, and that if "
|
||||
"statement must be the last statement of the continuing block\n"
|
||||
|
@ -1142,6 +1225,8 @@ TEST_F(ResolverValidationTest, Stmt_BreakInIfElseIfInContinuing) {
|
|||
WrapInFunction(Loop(Block(), cont));
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
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 "
|
||||
"statement of an if statement's true or false block, and that if "
|
||||
"statement must be the last statement of the continuing block\n"
|
||||
|
@ -1159,6 +1244,8 @@ TEST_F(ResolverValidationTest, Stmt_BreakInIfNonEmptyElseInContinuing) {
|
|||
WrapInFunction(Loop(Block(), cont));
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
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 "
|
||||
"statement of an if statement's true or false block, and that if "
|
||||
"statement must be the last statement of the continuing block\n"
|
||||
|
@ -1176,6 +1263,8 @@ TEST_F(ResolverValidationTest, Stmt_BreakInIfElseNonEmptyTrueInContinuing) {
|
|||
WrapInFunction(Loop(Block(), cont));
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
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 "
|
||||
"statement of an if statement's true or false block, and that if "
|
||||
"statement must be the last statement of the continuing block\n"
|
||||
|
@ -1192,6 +1281,8 @@ TEST_F(ResolverValidationTest, Stmt_BreakInIfInContinuingNotLast) {
|
|||
WrapInFunction(Loop(Block(), cont));
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
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 "
|
||||
"statement of an if statement's true or false block, and that if "
|
||||
"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/array.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/depth_multisampled_texture.h"
|
||||
#include "src/tint/sem/depth_texture.h"
|
||||
|
@ -1462,6 +1463,11 @@ bool Validator::BreakStatement(const sem::Statement* stmt,
|
|||
return false;
|
||||
}
|
||||
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) {
|
||||
constexpr const char* kErrorMsg =
|
||||
"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;
|
||||
}
|
||||
|
||||
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 {
|
||||
auto* cond_ty = stmt->Condition()->Type()->UnwrapRef();
|
||||
if (!cond_ty->Is<sem::Bool>()) {
|
||||
|
|
|
@ -51,6 +51,7 @@ namespace tint::sem {
|
|||
class Array;
|
||||
class Atomic;
|
||||
class BlockStatement;
|
||||
class BreakIfStatement;
|
||||
class Builtin;
|
||||
class Call;
|
||||
class CaseStatement;
|
||||
|
@ -256,6 +257,13 @@ class Validator {
|
|||
const std::unordered_map<OverrideId, const sem::Variable*>& override_id,
|
||||
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
|
||||
/// @param stmt the statement to validate
|
||||
/// @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;
|
||||
}
|
||||
|
||||
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) {
|
||||
auto* call = builder_.Sem().Get<sem::Call>(expr);
|
||||
auto* target = call->Target();
|
||||
|
@ -2616,6 +2626,7 @@ bool GeneratorImpl::EmitStatement(const ast::Statement* stmt) {
|
|||
[&](const ast::AssignmentStatement* a) { return EmitAssign(a); },
|
||||
[&](const ast::BlockStatement* b) { return EmitBlock(b); },
|
||||
[&](const ast::BreakStatement* b) { return EmitBreak(b); },
|
||||
[&](const ast::BreakIfStatement* b) { return EmitBreakIf(b); },
|
||||
[&](const ast::CallStatement* c) {
|
||||
auto out = line();
|
||||
if (!EmitCall(out, c->expr)) {
|
||||
|
|
|
@ -139,6 +139,10 @@ class GeneratorImpl : public TextGenerator {
|
|||
/// @param stmt the statement to emit
|
||||
/// @returns true if the statement was emitted successfully
|
||||
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
|
||||
/// @param out the output of the expression stream
|
||||
/// @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) {
|
||||
Func("a_statement", {}, ty.void_(), {});
|
||||
|
||||
|
|
|
@ -942,6 +942,16 @@ bool GeneratorImpl::EmitBreak(const ast::BreakStatement*) {
|
|||
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) {
|
||||
auto* call = builder_.Sem().Get<sem::Call>(expr);
|
||||
auto* target = call->Target();
|
||||
|
@ -3591,6 +3601,9 @@ bool GeneratorImpl::EmitStatement(const ast::Statement* stmt) {
|
|||
[&](const ast::BreakStatement* b) { //
|
||||
return EmitBreak(b);
|
||||
},
|
||||
[&](const ast::BreakIfStatement* b) { //
|
||||
return EmitBreakIf(b);
|
||||
},
|
||||
[&](const ast::CallStatement* c) { //
|
||||
auto out = line();
|
||||
if (!EmitCall(out, c->expr)) {
|
||||
|
|
|
@ -125,6 +125,10 @@ class GeneratorImpl : public TextGenerator {
|
|||
/// @param stmt the statement to emit
|
||||
/// @returns true if the statement was emitted successfully
|
||||
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
|
||||
/// @param out the output of the expression stream
|
||||
/// @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) {
|
||||
Func("a_statement", {}, ty.void_(), {});
|
||||
|
||||
|
|
|
@ -660,6 +660,16 @@ bool GeneratorImpl::EmitBreak(const ast::BreakStatement*) {
|
|||
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) {
|
||||
auto* call = program_->Sem().Get<sem::Call>(expr);
|
||||
auto* target = call->Target();
|
||||
|
@ -2433,6 +2443,9 @@ bool GeneratorImpl::EmitStatement(const ast::Statement* stmt) {
|
|||
[&](const ast::BreakStatement* b) { //
|
||||
return EmitBreak(b);
|
||||
},
|
||||
[&](const ast::BreakIfStatement* b) { //
|
||||
return EmitBreakIf(b);
|
||||
},
|
||||
[&](const ast::CallStatement* c) { //
|
||||
auto out = line();
|
||||
if (!EmitCall(out, c->expr)) { //
|
||||
|
|
|
@ -129,6 +129,10 @@ class GeneratorImpl : public TextGenerator {
|
|||
/// @param stmt the statement to emit
|
||||
/// @returns true if the statement was emitted successfully
|
||||
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
|
||||
/// @param out the output of the expression stream
|
||||
/// @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) {
|
||||
Func("a_statement", {}, ty.void_(), utils::Empty);
|
||||
|
||||
|
|
|
@ -455,6 +455,19 @@ bool Builder::GenerateBreakStatement(const ast::BreakStatement*) {
|
|||
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*) {
|
||||
if (continue_stack_.empty()) {
|
||||
error_ = "Attempted to continue without a continue block";
|
||||
|
@ -3400,6 +3413,8 @@ bool Builder::GenerateIfStatement(const ast::IfStatement* stmt) {
|
|||
// continuing { ...
|
||||
// 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) {
|
||||
return block && (block->statements.Length() == 1) &&
|
||||
block->Last()->Is<ast::BreakStatement>();
|
||||
|
@ -3643,6 +3658,7 @@ bool Builder::GenerateStatement(const ast::Statement* stmt) {
|
|||
stmt, [&](const ast::AssignmentStatement* a) { return GenerateAssignStatement(a); },
|
||||
[&](const ast::BlockStatement* b) { return GenerateBlockStatement(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::ContinueStatement* c) { return GenerateContinueStatement(c); },
|
||||
[&](const ast::DiscardStatement* d) { return GenerateDiscardStatement(d); },
|
||||
|
@ -3659,7 +3675,7 @@ bool Builder::GenerateStatement(const ast::Statement* stmt) {
|
|||
return true; // Not emitted
|
||||
},
|
||||
[&](Default) {
|
||||
error_ = "Unknown statement: " + std::string(stmt->TypeInfo().name);
|
||||
error_ = "unknown statement type: " + std::string(stmt->TypeInfo().name);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -248,6 +248,10 @@ class Builder {
|
|||
/// @param stmt the statement to generate
|
||||
/// @returns true if the statement was successfully generated
|
||||
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
|
||||
/// @param stmt the statement to generate
|
||||
/// @returns true if the statement was successfully generated
|
||||
|
|
|
@ -234,12 +234,11 @@ OpBranch %1
|
|||
TEST_F(BuilderTest, Loop_WithContinuing_BreakIf) {
|
||||
// loop {
|
||||
// continuing {
|
||||
// if (true) { break; }
|
||||
// break if (true);
|
||||
// }
|
||||
// }
|
||||
|
||||
auto* if_stmt = If(Expr(true), Block(Break()));
|
||||
auto* continuing = Block(if_stmt);
|
||||
auto* continuing = Block(BreakIf(true));
|
||||
auto* loop = Loop(Block(), continuing);
|
||||
WrapInFunction(loop);
|
||||
|
||||
|
@ -267,11 +266,10 @@ OpBranchConditional %6 %2 %1
|
|||
TEST_F(BuilderTest, Loop_WithContinuing_BreakUnless) {
|
||||
// loop {
|
||||
// continuing {
|
||||
// if (true) {} else { break; }
|
||||
// break if (false);
|
||||
// }
|
||||
// }
|
||||
auto* if_stmt = If(Expr(true), Block(), Else(Block(Break())));
|
||||
auto* continuing = Block(if_stmt);
|
||||
auto* continuing = Block(BreakIf(false));
|
||||
auto* loop = Loop(Block(), continuing);
|
||||
WrapInFunction(loop);
|
||||
|
||||
|
@ -281,7 +279,7 @@ TEST_F(BuilderTest, Loop_WithContinuing_BreakUnless) {
|
|||
|
||||
EXPECT_TRUE(b.GenerateLoopStatement(loop)) << b.error();
|
||||
EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeBool
|
||||
%6 = OpConstantTrue %5
|
||||
%6 = OpConstantNull %5
|
||||
)");
|
||||
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
|
||||
R"(OpBranch %1
|
||||
|
@ -291,7 +289,7 @@ OpBranch %4
|
|||
%4 = OpLabel
|
||||
OpBranch %3
|
||||
%3 = OpLabel
|
||||
OpBranchConditional %6 %1 %2
|
||||
OpBranchConditional %6 %2 %1
|
||||
%2 = OpLabel
|
||||
)");
|
||||
}
|
||||
|
@ -300,13 +298,12 @@ TEST_F(BuilderTest, Loop_WithContinuing_BreakIf_ConditionIsVar) {
|
|||
// loop {
|
||||
// continuing {
|
||||
// var cond = true;
|
||||
// if (cond) { break; }
|
||||
// break if (cond);
|
||||
// }
|
||||
// }
|
||||
|
||||
auto* cond_var = Decl(Var("cond", Expr(true)));
|
||||
auto* if_stmt = If(Expr("cond"), Block(Break()));
|
||||
auto* continuing = Block(cond_var, if_stmt);
|
||||
auto* continuing = Block(cond_var, BreakIf("cond"));
|
||||
auto* loop = Loop(Block(), continuing);
|
||||
WrapInFunction(loop);
|
||||
|
||||
|
@ -379,19 +376,17 @@ TEST_F(BuilderTest, Loop_WithContinuing_BreakIf_Nested) {
|
|||
// continuing {
|
||||
// loop {
|
||||
// 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(inner_if_stmt);
|
||||
auto* inner_continuing = Block(BreakIf(true));
|
||||
auto* inner_loop = Loop(Block(), inner_continuing);
|
||||
|
||||
auto* outer_if_stmt = If(Expr(true), Block(Break()));
|
||||
auto* outer_continuing = Block(inner_loop, outer_if_stmt);
|
||||
auto* outer_continuing = Block(inner_loop, BreakIf(true));
|
||||
auto* outer_loop = Loop(Block(), outer_continuing);
|
||||
|
||||
WrapInFunction(outer_loop);
|
||||
|
|
|
@ -958,6 +958,7 @@ bool GeneratorImpl::EmitStatement(const ast::Statement* stmt) {
|
|||
[&](const ast::AssignmentStatement* a) { return EmitAssign(a); },
|
||||
[&](const ast::BlockStatement* b) { return EmitBlock(b); },
|
||||
[&](const ast::BreakStatement* b) { return EmitBreak(b); },
|
||||
[&](const ast::BreakIfStatement* b) { return EmitBreakIf(b); },
|
||||
[&](const ast::CallStatement* c) {
|
||||
auto out = line();
|
||||
if (!EmitCall(out, c->expr)) {
|
||||
|
@ -1023,6 +1024,17 @@ bool GeneratorImpl::EmitBreak(const ast::BreakStatement*) {
|
|||
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) {
|
||||
if (stmt->selectors.Length() == 1 && stmt->ContainsDefault()) {
|
||||
line() << "default: {";
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "src/tint/ast/assignment_statement.h"
|
||||
#include "src/tint/ast/binary_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/compound_assignment_statement.h"
|
||||
#include "src/tint/ast/continue_statement.h"
|
||||
|
@ -92,6 +93,10 @@ class GeneratorImpl : public TextGenerator {
|
|||
/// @param stmt the statement to emit
|
||||
/// @returns true if the statement was emitted successfully
|
||||
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
|
||||
/// @param out the output of the expression stream
|
||||
/// @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) {
|
||||
// var<workgroup> a : atomic<i32>;
|
||||
// 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() {
|
||||
while (true) {
|
||||
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() {
|
||||
while (true) {
|
||||
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
|
||||
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>
|
||||
|
||||
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
|
||||
; Version: 1.3
|
||||
; 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
|
||||
fn main() {
|
||||
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