tint: Refactor if-else statement representation
Instead of using an `if` node that has a list of `else` statements, make each `if` statement have a single optional `else` statement, which may itself be an `if` statement (or just a block statement). This better matches the WGSL grammar (now that we have removed `elseif`), and simplifies various pieces of code that handle these statements. Change-Id: Ie4272f1422224490ac598a03aa8b4dd00ba03010 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/87940 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Antonio Maiorano <amaiorano@google.com> Commit-Queue: James Price <jrprice@google.com>
This commit is contained in:
parent
68e039c456
commit
26ebe5ec36
|
@ -24,13 +24,11 @@ sem::IfStatement {
|
|||
sem::BlockStatement {
|
||||
statement_a
|
||||
}
|
||||
sem::ElseStatement {
|
||||
sem::IfStatement {
|
||||
condition_b
|
||||
sem::BlockStatement {
|
||||
statement_b
|
||||
}
|
||||
}
|
||||
sem::ElseStatement {
|
||||
sem::BlockStatement {
|
||||
statement_c
|
||||
}
|
||||
|
|
|
@ -222,8 +222,6 @@ libtint_source_set("libtint_core_all_src") {
|
|||
"ast/disable_validation_attribute.h",
|
||||
"ast/discard_statement.cc",
|
||||
"ast/discard_statement.h",
|
||||
"ast/else_statement.cc",
|
||||
"ast/else_statement.h",
|
||||
"ast/enable.cc",
|
||||
"ast/enable.h",
|
||||
"ast/expression.cc",
|
||||
|
|
|
@ -110,8 +110,6 @@ set(TINT_LIB_SRCS
|
|||
ast/depth_texture.h
|
||||
ast/discard_statement.cc
|
||||
ast/discard_statement.h
|
||||
ast/else_statement.cc
|
||||
ast/else_statement.h
|
||||
ast/enable.cc
|
||||
ast/enable.h
|
||||
ast/expression.cc
|
||||
|
@ -676,7 +674,6 @@ if(TINT_BUILD_TESTS)
|
|||
ast/depth_multisampled_texture_test.cc
|
||||
ast/depth_texture_test.cc
|
||||
ast/discard_statement_test.cc
|
||||
ast/else_statement_test.cc
|
||||
ast/enable_test.cc
|
||||
ast/external_texture_test.cc
|
||||
ast/f32_test.cc
|
||||
|
@ -897,7 +894,6 @@ if(TINT_BUILD_TESTS)
|
|||
reader/wgsl/parser_impl_depth_texture_test.cc
|
||||
reader/wgsl/parser_impl_enable_directive_test.cc
|
||||
reader/wgsl/parser_impl_external_texture_test.cc
|
||||
reader/wgsl/parser_impl_elseif_stmt_test.cc
|
||||
reader/wgsl/parser_impl_equality_expression_test.cc
|
||||
reader/wgsl/parser_impl_error_msg_test.cc
|
||||
reader/wgsl/parser_impl_error_resync_test.cc
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
// Copyright 2020 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/else_statement.h"
|
||||
|
||||
#include "src/tint/program_builder.h"
|
||||
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::ast::ElseStatement);
|
||||
|
||||
namespace tint::ast {
|
||||
|
||||
ElseStatement::ElseStatement(ProgramID pid,
|
||||
const Source& src,
|
||||
const Expression* cond,
|
||||
const BlockStatement* b)
|
||||
: Base(pid, src), condition(cond), body(b) {
|
||||
TINT_ASSERT(AST, body);
|
||||
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, body, program_id);
|
||||
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, condition, program_id);
|
||||
}
|
||||
|
||||
ElseStatement::ElseStatement(ElseStatement&&) = default;
|
||||
|
||||
ElseStatement::~ElseStatement() = default;
|
||||
|
||||
const ElseStatement* ElseStatement::Clone(CloneContext* ctx) const {
|
||||
// Clone arguments outside of create() call to have deterministic ordering
|
||||
auto src = ctx->Clone(source);
|
||||
auto* cond = ctx->Clone(condition);
|
||||
auto* b = ctx->Clone(body);
|
||||
return ctx->dst->create<ElseStatement>(src, cond, b);
|
||||
}
|
||||
|
||||
} // namespace tint::ast
|
|
@ -1,59 +0,0 @@
|
|||
// Copyright 2020 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_ELSE_STATEMENT_H_
|
||||
#define SRC_TINT_AST_ELSE_STATEMENT_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "src/tint/ast/block_statement.h"
|
||||
#include "src/tint/ast/expression.h"
|
||||
|
||||
namespace tint::ast {
|
||||
|
||||
/// An else statement
|
||||
class ElseStatement final : public Castable<ElseStatement, Statement> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param pid the identifier of the program that owns this node
|
||||
/// @param src the source of this node
|
||||
/// @param condition the else condition
|
||||
/// @param body the else body
|
||||
ElseStatement(ProgramID pid,
|
||||
const Source& src,
|
||||
const Expression* condition,
|
||||
const BlockStatement* body);
|
||||
/// Move constructor
|
||||
ElseStatement(ElseStatement&&);
|
||||
~ElseStatement() override;
|
||||
|
||||
/// Clones this node and all transitive child nodes using the `CloneContext`
|
||||
/// `ctx`.
|
||||
/// @param ctx the clone context
|
||||
/// @return the newly cloned node
|
||||
const ElseStatement* Clone(CloneContext* ctx) const override;
|
||||
|
||||
/// The else condition or nullptr if none set
|
||||
const Expression* const condition;
|
||||
|
||||
/// The else body
|
||||
const BlockStatement* const body;
|
||||
};
|
||||
|
||||
/// A list of else statements
|
||||
using ElseStatementList = std::vector<const ElseStatement*>;
|
||||
|
||||
} // namespace tint::ast
|
||||
|
||||
#endif // SRC_TINT_AST_ELSE_STATEMENT_H_
|
|
@ -1,92 +0,0 @@
|
|||
// Copyright 2020 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 "gtest/gtest-spi.h"
|
||||
#include "src/tint/ast/discard_statement.h"
|
||||
#include "src/tint/ast/if_statement.h"
|
||||
#include "src/tint/ast/test_helper.h"
|
||||
|
||||
namespace tint::ast {
|
||||
namespace {
|
||||
|
||||
using ElseStatementTest = TestHelper;
|
||||
|
||||
TEST_F(ElseStatementTest, Creation) {
|
||||
auto* cond = Expr(true);
|
||||
auto* body = create<BlockStatement>(StatementList{
|
||||
create<DiscardStatement>(),
|
||||
});
|
||||
auto* discard = body->statements[0];
|
||||
|
||||
auto* e = create<ElseStatement>(cond, body);
|
||||
EXPECT_EQ(e->condition, cond);
|
||||
ASSERT_EQ(e->body->statements.size(), 1u);
|
||||
EXPECT_EQ(e->body->statements[0], discard);
|
||||
}
|
||||
|
||||
TEST_F(ElseStatementTest, Creation_WithSource) {
|
||||
auto* e = create<ElseStatement>(Source{Source::Location{20, 2}}, Expr(true),
|
||||
Block());
|
||||
auto src = e->source;
|
||||
EXPECT_EQ(src.range.begin.line, 20u);
|
||||
EXPECT_EQ(src.range.begin.column, 2u);
|
||||
}
|
||||
|
||||
TEST_F(ElseStatementTest, IsElse) {
|
||||
auto* e = create<ElseStatement>(nullptr, Block());
|
||||
EXPECT_TRUE(e->Is<ElseStatement>());
|
||||
}
|
||||
|
||||
TEST_F(ElseStatementTest, HasCondition) {
|
||||
auto* cond = Expr(true);
|
||||
auto* e = create<ElseStatement>(cond, Block());
|
||||
EXPECT_TRUE(e->condition);
|
||||
}
|
||||
|
||||
TEST_F(ElseStatementTest, HasContition_NullCondition) {
|
||||
auto* e = create<ElseStatement>(nullptr, Block());
|
||||
EXPECT_FALSE(e->condition);
|
||||
}
|
||||
|
||||
TEST_F(ElseStatementTest, Assert_Null_Body) {
|
||||
EXPECT_FATAL_FAILURE(
|
||||
{
|
||||
ProgramBuilder b;
|
||||
b.create<ElseStatement>(b.Expr(true), nullptr);
|
||||
},
|
||||
"internal compiler error");
|
||||
}
|
||||
|
||||
TEST_F(ElseStatementTest, Assert_DifferentProgramID_Condition) {
|
||||
EXPECT_FATAL_FAILURE(
|
||||
{
|
||||
ProgramBuilder b1;
|
||||
ProgramBuilder b2;
|
||||
b1.create<ElseStatement>(b2.Expr(true), b1.Block());
|
||||
},
|
||||
"internal compiler error");
|
||||
}
|
||||
|
||||
TEST_F(ElseStatementTest, Assert_DifferentProgramID_Body) {
|
||||
EXPECT_FATAL_FAILURE(
|
||||
{
|
||||
ProgramBuilder b1;
|
||||
ProgramBuilder b2;
|
||||
b1.create<ElseStatement>(b1.Expr(true), b2.Block());
|
||||
},
|
||||
"internal compiler error");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace tint::ast
|
|
@ -24,18 +24,17 @@ IfStatement::IfStatement(ProgramID pid,
|
|||
const Source& src,
|
||||
const Expression* cond,
|
||||
const BlockStatement* b,
|
||||
ElseStatementList else_stmts)
|
||||
: Base(pid, src),
|
||||
condition(cond),
|
||||
body(b),
|
||||
else_statements(std::move(else_stmts)) {
|
||||
const Statement* else_stmt)
|
||||
: Base(pid, src), condition(cond), body(b), else_statement(else_stmt) {
|
||||
TINT_ASSERT(AST, condition);
|
||||
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, condition, program_id);
|
||||
TINT_ASSERT(AST, body);
|
||||
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, body, program_id);
|
||||
for (auto* el : else_statements) {
|
||||
TINT_ASSERT(AST, el);
|
||||
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, el, program_id);
|
||||
if (else_statement) {
|
||||
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, else_statement, program_id);
|
||||
TINT_ASSERT(
|
||||
AST,
|
||||
(else_statement->IsAnyOf<ast::IfStatement, ast::BlockStatement>()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,7 +47,7 @@ const IfStatement* IfStatement::Clone(CloneContext* ctx) const {
|
|||
auto src = ctx->Clone(source);
|
||||
auto* cond = ctx->Clone(condition);
|
||||
auto* b = ctx->Clone(body);
|
||||
auto el = ctx->Clone(else_statements);
|
||||
auto el = ctx->Clone(else_statement);
|
||||
return ctx->dst->create<IfStatement>(src, cond, b, el);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
|
||||
#include <utility>
|
||||
|
||||
#include "src/tint/ast/else_statement.h"
|
||||
#include "src/tint/ast/block_statement.h"
|
||||
#include "src/tint/ast/expression.h"
|
||||
|
||||
namespace tint::ast {
|
||||
|
||||
|
@ -29,12 +30,12 @@ class IfStatement final : public Castable<IfStatement, Statement> {
|
|||
/// @param src the source of this node
|
||||
/// @param condition the if condition
|
||||
/// @param body the if body
|
||||
/// @param else_stmts the else statements
|
||||
/// @param else_stmt the else statement, or nullptr
|
||||
IfStatement(ProgramID pid,
|
||||
const Source& src,
|
||||
const Expression* condition,
|
||||
const BlockStatement* body,
|
||||
ElseStatementList else_stmts);
|
||||
const Statement* else_stmt);
|
||||
/// Move constructor
|
||||
IfStatement(IfStatement&&);
|
||||
~IfStatement() override;
|
||||
|
@ -51,8 +52,8 @@ class IfStatement final : public Castable<IfStatement, Statement> {
|
|||
/// The if body
|
||||
const BlockStatement* const body;
|
||||
|
||||
/// The else statements
|
||||
const ElseStatementList else_statements;
|
||||
/// The optional else statement, or nullptr
|
||||
const Statement* else_statement;
|
||||
};
|
||||
|
||||
} // namespace tint::ast
|
||||
|
|
|
@ -25,16 +25,15 @@ using IfStatementTest = TestHelper;
|
|||
|
||||
TEST_F(IfStatementTest, Creation) {
|
||||
auto* cond = Expr("cond");
|
||||
auto* stmt = create<IfStatement>(Source{Source::Location{20, 2}}, cond,
|
||||
Block(create<DiscardStatement>()),
|
||||
ElseStatementList{});
|
||||
auto* stmt = If(Source{Source::Location{20, 2}}, cond,
|
||||
Block(create<DiscardStatement>()));
|
||||
auto src = stmt->source;
|
||||
EXPECT_EQ(src.range.begin.line, 20u);
|
||||
EXPECT_EQ(src.range.begin.column, 2u);
|
||||
}
|
||||
|
||||
TEST_F(IfStatementTest, IsIf) {
|
||||
auto* stmt = create<IfStatement>(Expr(true), Block(), ElseStatementList{});
|
||||
auto* stmt = If(Expr(true), Block());
|
||||
EXPECT_TRUE(stmt->Is<IfStatement>());
|
||||
}
|
||||
|
||||
|
@ -42,7 +41,7 @@ TEST_F(IfStatementTest, Assert_Null_Condition) {
|
|||
EXPECT_FATAL_FAILURE(
|
||||
{
|
||||
ProgramBuilder b;
|
||||
b.create<IfStatement>(nullptr, b.Block(), ElseStatementList{});
|
||||
b.If(nullptr, b.Block());
|
||||
},
|
||||
"internal compiler error");
|
||||
}
|
||||
|
@ -51,17 +50,16 @@ TEST_F(IfStatementTest, Assert_Null_Body) {
|
|||
EXPECT_FATAL_FAILURE(
|
||||
{
|
||||
ProgramBuilder b;
|
||||
b.create<IfStatement>(b.Expr(true), nullptr, ElseStatementList{});
|
||||
b.If(b.Expr(true), nullptr);
|
||||
},
|
||||
"internal compiler error");
|
||||
}
|
||||
|
||||
TEST_F(IfStatementTest, Assert_Null_ElseStatement) {
|
||||
TEST_F(IfStatementTest, Assert_InvalidElse) {
|
||||
EXPECT_FATAL_FAILURE(
|
||||
{
|
||||
ProgramBuilder b;
|
||||
auto* body = b.create<BlockStatement>(StatementList{});
|
||||
b.create<IfStatement>(b.Expr(true), body, ElseStatementList{nullptr});
|
||||
b.If(b.Expr(true), b.Block(), b.CallStmt(b.Call("foo")));
|
||||
},
|
||||
"internal compiler error");
|
||||
}
|
||||
|
@ -71,7 +69,7 @@ TEST_F(IfStatementTest, Assert_DifferentProgramID_Cond) {
|
|||
{
|
||||
ProgramBuilder b1;
|
||||
ProgramBuilder b2;
|
||||
b1.create<IfStatement>(b2.Expr(true), b1.Block(), ElseStatementList{});
|
||||
b1.If(b2.Expr(true), b1.Block());
|
||||
},
|
||||
"internal compiler error");
|
||||
}
|
||||
|
@ -81,7 +79,7 @@ TEST_F(IfStatementTest, Assert_DifferentProgramID_Body) {
|
|||
{
|
||||
ProgramBuilder b1;
|
||||
ProgramBuilder b2;
|
||||
b1.create<IfStatement>(b1.Expr(true), b2.Block(), ElseStatementList{});
|
||||
b1.If(b1.Expr(true), b2.Block());
|
||||
},
|
||||
"internal compiler error");
|
||||
}
|
||||
|
@ -91,11 +89,7 @@ TEST_F(IfStatementTest, Assert_DifferentProgramID_ElseStatement) {
|
|||
{
|
||||
ProgramBuilder b1;
|
||||
ProgramBuilder b2;
|
||||
b1.create<IfStatement>(
|
||||
b1.Expr(true), b1.Block(),
|
||||
ElseStatementList{
|
||||
b2.create<ElseStatement>(b2.Expr("ident"), b2.Block()),
|
||||
});
|
||||
b1.If(b1.Expr(true), b1.Block(), b2.If(b2.Expr("ident"), b2.Block()));
|
||||
},
|
||||
"internal compiler error");
|
||||
}
|
||||
|
|
|
@ -58,9 +58,6 @@ const char* Statement::Name() const {
|
|||
if (Is<DiscardStatement>()) {
|
||||
return "discard statement";
|
||||
}
|
||||
if (Is<ElseStatement>()) {
|
||||
return "else statement";
|
||||
}
|
||||
if (Is<FallthroughStatement>()) {
|
||||
return "fallthrough statement";
|
||||
}
|
||||
|
|
|
@ -2190,56 +2190,34 @@ class ProgramBuilder {
|
|||
ast::StatementList{std::forward<STATEMENTS>(statements)...});
|
||||
}
|
||||
|
||||
/// Creates a ast::ElseStatement with input condition and body
|
||||
/// @param condition the else condition expression
|
||||
/// @param body the else body
|
||||
/// @returns the else statement pointer
|
||||
template <typename CONDITION>
|
||||
const ast::ElseStatement* Else(CONDITION&& condition,
|
||||
const ast::BlockStatement* body) {
|
||||
return create<ast::ElseStatement>(Expr(std::forward<CONDITION>(condition)),
|
||||
body);
|
||||
}
|
||||
|
||||
/// Creates a ast::ElseStatement with no condition and body
|
||||
/// @param body the else body
|
||||
/// @returns the else statement pointer
|
||||
const ast::ElseStatement* Else(const ast::BlockStatement* body) {
|
||||
return create<ast::ElseStatement>(nullptr, body);
|
||||
}
|
||||
|
||||
/// Creates a ast::IfStatement with input condition, body, and optional
|
||||
/// variadic else statements
|
||||
/// else statement
|
||||
/// @param source the source information for the if statement
|
||||
/// @param condition the if statement condition expression
|
||||
/// @param body the if statement body
|
||||
/// @param elseStatements optional variadic else statements
|
||||
/// @param else_stmt optional else statement
|
||||
/// @returns the if statement pointer
|
||||
template <typename CONDITION, typename... ELSE_STATEMENTS>
|
||||
template <typename CONDITION>
|
||||
const ast::IfStatement* If(const Source& source,
|
||||
CONDITION&& condition,
|
||||
const ast::BlockStatement* body,
|
||||
ELSE_STATEMENTS&&... elseStatements) {
|
||||
const ast::Statement* else_stmt = nullptr) {
|
||||
return create<ast::IfStatement>(
|
||||
source, Expr(std::forward<CONDITION>(condition)), body,
|
||||
ast::ElseStatementList{
|
||||
std::forward<ELSE_STATEMENTS>(elseStatements)...});
|
||||
source, Expr(std::forward<CONDITION>(condition)), body, else_stmt);
|
||||
}
|
||||
|
||||
/// Creates a ast::IfStatement with input condition, body, and optional
|
||||
/// variadic else statements
|
||||
/// else statement
|
||||
/// @param condition the if statement condition expression
|
||||
/// @param body the if statement body
|
||||
/// @param elseStatements optional variadic else statements
|
||||
/// @param else_stmt optional else statement
|
||||
/// @returns the if statement pointer
|
||||
template <typename CONDITION, typename... ELSE_STATEMENTS>
|
||||
template <typename CONDITION>
|
||||
const ast::IfStatement* If(CONDITION&& condition,
|
||||
const ast::BlockStatement* body,
|
||||
ELSE_STATEMENTS&&... elseStatements) {
|
||||
return create<ast::IfStatement>(
|
||||
Expr(std::forward<CONDITION>(condition)), body,
|
||||
ast::ElseStatementList{
|
||||
std::forward<ELSE_STATEMENTS>(elseStatements)...});
|
||||
const ast::Statement* else_stmt = nullptr) {
|
||||
return create<ast::IfStatement>(Expr(std::forward<CONDITION>(condition)),
|
||||
body, else_stmt);
|
||||
}
|
||||
|
||||
/// Creates a ast::AssignmentStatement with input lhs and rhs expressions
|
||||
|
|
|
@ -681,15 +681,15 @@ struct IfStatementBuilder final
|
|||
/// @param builder the program builder
|
||||
/// @returns the built ast::IfStatement
|
||||
const ast::IfStatement* Build(ProgramBuilder* builder) const override {
|
||||
return builder->create<ast::IfStatement>(Source{}, cond, body, else_stmts);
|
||||
return builder->create<ast::IfStatement>(Source{}, cond, body, else_stmt);
|
||||
}
|
||||
|
||||
/// If-statement condition
|
||||
const ast::Expression* const cond;
|
||||
/// If-statement block body
|
||||
const ast::BlockStatement* body = nullptr;
|
||||
/// Optional if-statement else statements
|
||||
ast::ElseStatementList else_stmts;
|
||||
/// Optional if-statement else statement
|
||||
const ast::Statement* else_stmt = nullptr;
|
||||
};
|
||||
|
||||
/// A StatementBuilder for ast::LoopStatement
|
||||
|
@ -2964,10 +2964,8 @@ bool FunctionEmitter::EmitIfStart(const BlockInfo& block_info) {
|
|||
// Only set the else-clause if there are statements to fill it.
|
||||
if (!stmts.empty()) {
|
||||
// The "else" consists of the statement list from the top of
|
||||
// statements stack, without an elseif condition.
|
||||
auto* else_body = create<ast::BlockStatement>(Source{}, stmts);
|
||||
builder->else_stmts.emplace_back(
|
||||
create<ast::ElseStatement>(Source{}, nullptr, else_body));
|
||||
// statements stack, without an "else if" condition.
|
||||
builder->else_stmt = create<ast::BlockStatement>(Source{}, stmts);
|
||||
}
|
||||
});
|
||||
if (false_is_break) {
|
||||
|
@ -3363,19 +3361,19 @@ const ast::Statement* FunctionEmitter::MakeSimpleIf(
|
|||
if ((then_stmt == nullptr) && (else_stmt == nullptr)) {
|
||||
return nullptr;
|
||||
}
|
||||
ast::ElseStatementList else_stmts;
|
||||
if (else_stmt != nullptr) {
|
||||
ast::StatementList stmts{else_stmt};
|
||||
else_stmts.emplace_back(create<ast::ElseStatement>(
|
||||
Source{}, nullptr, create<ast::BlockStatement>(Source{}, stmts)));
|
||||
}
|
||||
ast::StatementList if_stmts;
|
||||
if (then_stmt != nullptr) {
|
||||
if_stmts.emplace_back(then_stmt);
|
||||
}
|
||||
auto* if_block = create<ast::BlockStatement>(Source{}, if_stmts);
|
||||
|
||||
const ast::Statement* else_block = nullptr;
|
||||
if (else_stmt) {
|
||||
else_block = create<ast::BlockStatement>(ast::StatementList{else_stmt});
|
||||
}
|
||||
|
||||
auto* if_stmt =
|
||||
create<ast::IfStatement>(Source{}, condition, if_block, else_stmts);
|
||||
create<ast::IfStatement>(Source{}, condition, if_block, else_block);
|
||||
|
||||
return if_stmt;
|
||||
}
|
||||
|
|
|
@ -1729,58 +1729,33 @@ Maybe<const ast::VariableDeclStatement*> ParserImpl::variable_stmt() {
|
|||
}
|
||||
|
||||
// if_stmt
|
||||
// : IF expression compound_stmt ( ELSE else_stmts ) ?
|
||||
Maybe<const ast::IfStatement*> ParserImpl::if_stmt() {
|
||||
Source source;
|
||||
if (!match(Token::Type::kIf, &source))
|
||||
return Failure::kNoMatch;
|
||||
|
||||
auto condition = logical_or_expression();
|
||||
if (condition.errored)
|
||||
return Failure::kErrored;
|
||||
if (!condition.matched) {
|
||||
return add_error(peek(), "unable to parse condition expression");
|
||||
}
|
||||
|
||||
auto body = expect_body_stmt();
|
||||
if (body.errored)
|
||||
return Failure::kErrored;
|
||||
|
||||
auto el = else_stmts();
|
||||
if (el.errored) {
|
||||
return Failure::kErrored;
|
||||
}
|
||||
|
||||
return create<ast::IfStatement>(source, condition.value, body.value,
|
||||
std::move(el.value));
|
||||
}
|
||||
|
||||
// else_stmts
|
||||
// : IF expression compound_stmt ( ELSE else_stmt ) ?
|
||||
// else_stmt
|
||||
// : body_stmt
|
||||
// | if_stmt
|
||||
Expect<ast::ElseStatementList> ParserImpl::else_stmts() {
|
||||
ast::ElseStatementList stmts;
|
||||
while (continue_parsing()) {
|
||||
Source start;
|
||||
Maybe<const ast::IfStatement*> ParserImpl::if_stmt() {
|
||||
// Parse if-else chains iteratively instead of recursively, to avoid
|
||||
// stack-overflow for long chains of if-else statements.
|
||||
|
||||
bool else_if = false;
|
||||
if (match(Token::Type::kElse, &start)) {
|
||||
else_if = match(Token::Type::kIf);
|
||||
} else {
|
||||
break;
|
||||
struct IfInfo {
|
||||
Source source;
|
||||
const ast::Expression* condition;
|
||||
const ast::BlockStatement* body;
|
||||
};
|
||||
|
||||
// Parse an if statement, capturing the source, condition, and body statement.
|
||||
auto parse_if = [&]() -> Maybe<IfInfo> {
|
||||
Source source;
|
||||
if (!match(Token::Type::kIf, &source)) {
|
||||
return Failure::kNoMatch;
|
||||
}
|
||||
|
||||
const ast::Expression* cond = nullptr;
|
||||
if (else_if) {
|
||||
auto condition = logical_or_expression();
|
||||
if (condition.errored) {
|
||||
return Failure::kErrored;
|
||||
}
|
||||
if (!condition.matched) {
|
||||
return add_error(peek(), "unable to parse condition expression");
|
||||
}
|
||||
|
||||
cond = condition.value;
|
||||
auto condition = logical_or_expression();
|
||||
if (condition.errored) {
|
||||
return Failure::kErrored;
|
||||
}
|
||||
if (!condition.matched) {
|
||||
return add_error(peek(), "unable to parse condition expression");
|
||||
}
|
||||
|
||||
auto body = expect_body_stmt();
|
||||
|
@ -1788,11 +1763,52 @@ Expect<ast::ElseStatementList> ParserImpl::else_stmts() {
|
|||
return Failure::kErrored;
|
||||
}
|
||||
|
||||
Source source = make_source_range_from(start);
|
||||
stmts.emplace_back(create<ast::ElseStatement>(source, cond, body.value));
|
||||
return IfInfo{source, condition.value, body.value};
|
||||
};
|
||||
|
||||
std::vector<IfInfo> statements;
|
||||
|
||||
// Parse the first if statement.
|
||||
auto first_if = parse_if();
|
||||
if (first_if.errored) {
|
||||
return Failure::kErrored;
|
||||
} else if (!first_if.matched) {
|
||||
return Failure::kNoMatch;
|
||||
}
|
||||
statements.push_back(first_if.value);
|
||||
|
||||
// Parse the components of every "else {if}" in the chain.
|
||||
const ast::Statement* last_stmt = nullptr;
|
||||
while (continue_parsing()) {
|
||||
if (!match(Token::Type::kElse)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Try to parse an "else if".
|
||||
auto else_if = parse_if();
|
||||
if (else_if.errored) {
|
||||
return Failure::kErrored;
|
||||
} else if (else_if.matched) {
|
||||
statements.push_back(else_if.value);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If it wasn't an "else if", it must just be an "else".
|
||||
auto else_body = expect_body_stmt();
|
||||
if (else_body.errored) {
|
||||
return Failure::kErrored;
|
||||
}
|
||||
last_stmt = else_body.value;
|
||||
break;
|
||||
}
|
||||
|
||||
return stmts;
|
||||
// Now walk back through the statements to create their AST nodes.
|
||||
for (auto itr = statements.rbegin(); itr != statements.rend(); itr++) {
|
||||
last_stmt = create<ast::IfStatement>(itr->source, itr->condition, itr->body,
|
||||
last_stmt);
|
||||
}
|
||||
|
||||
return last_stmt->As<ast::IfStatement>();
|
||||
}
|
||||
|
||||
// switch_stmt
|
||||
|
|
|
@ -513,9 +513,6 @@ class ParserImpl {
|
|||
/// Parses a `if_stmt` grammar element
|
||||
/// @returns the parsed statement or nullptr
|
||||
Maybe<const ast::IfStatement*> if_stmt();
|
||||
/// Parses a list of `else_stmt` grammar elements
|
||||
/// @returns the parsed statement or nullptr
|
||||
Expect<ast::ElseStatementList> else_stmts();
|
||||
/// Parses a `switch_stmt` grammar element
|
||||
/// @returns the parsed statement or nullptr
|
||||
Maybe<const ast::SwitchStatement*> switch_stmt();
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
// Copyright 2020 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/reader/wgsl/parser_impl_test_helper.h"
|
||||
|
||||
namespace tint::reader::wgsl {
|
||||
namespace {
|
||||
|
||||
TEST_F(ParserImplTest, ElseStmts) {
|
||||
auto p = parser("else if (a == 4) { a = b; c = d; }");
|
||||
auto e = p->else_stmts();
|
||||
EXPECT_FALSE(p->has_error()) << p->error();
|
||||
ASSERT_EQ(e.value.size(), 1u);
|
||||
|
||||
ASSERT_TRUE(e.value[0]->Is<ast::ElseStatement>());
|
||||
ASSERT_NE(e.value[0]->condition, nullptr);
|
||||
ASSERT_TRUE(e.value[0]->condition->Is<ast::BinaryExpression>());
|
||||
EXPECT_EQ(e.value[0]->body->statements.size(), 2u);
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, ElseStmts_Multiple) {
|
||||
auto p = parser("else if (a == 4) { a = b; c = d; } else if(c) { d = 2; }");
|
||||
auto e = p->else_stmts();
|
||||
EXPECT_FALSE(p->has_error()) << p->error();
|
||||
ASSERT_EQ(e.value.size(), 2u);
|
||||
|
||||
ASSERT_TRUE(e.value[0]->Is<ast::ElseStatement>());
|
||||
ASSERT_NE(e.value[0]->condition, nullptr);
|
||||
ASSERT_TRUE(e.value[0]->condition->Is<ast::BinaryExpression>());
|
||||
EXPECT_EQ(e.value[0]->body->statements.size(), 2u);
|
||||
|
||||
ASSERT_TRUE(e.value[1]->Is<ast::ElseStatement>());
|
||||
ASSERT_NE(e.value[1]->condition, nullptr);
|
||||
ASSERT_TRUE(e.value[1]->condition->Is<ast::IdentifierExpression>());
|
||||
EXPECT_EQ(e.value[1]->body->statements.size(), 1u);
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, ElseStmts_InvalidBody) {
|
||||
auto p = parser("else if (true) { fn main() {}}");
|
||||
auto e = p->else_stmts();
|
||||
EXPECT_TRUE(e.errored);
|
||||
EXPECT_TRUE(p->has_error());
|
||||
EXPECT_EQ(p->error(), "1:18: expected '}'");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, ElseStmts_MissingBody) {
|
||||
auto p = parser("else if (true)");
|
||||
auto e = p->else_stmts();
|
||||
EXPECT_TRUE(e.errored);
|
||||
EXPECT_TRUE(p->has_error());
|
||||
EXPECT_EQ(p->error(), "1:15: expected '{'");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace tint::reader::wgsl
|
|
@ -29,7 +29,7 @@ TEST_F(ParserImplTest, IfStmt) {
|
|||
ASSERT_NE(e->condition, nullptr);
|
||||
ASSERT_TRUE(e->condition->Is<ast::BinaryExpression>());
|
||||
EXPECT_EQ(e->body->statements.size(), 2u);
|
||||
EXPECT_EQ(e->else_statements.size(), 0u);
|
||||
EXPECT_EQ(e->else_statement, nullptr);
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, IfStmt_WithElse) {
|
||||
|
@ -45,14 +45,14 @@ TEST_F(ParserImplTest, IfStmt_WithElse) {
|
|||
ASSERT_TRUE(e->condition->Is<ast::BinaryExpression>());
|
||||
EXPECT_EQ(e->body->statements.size(), 2u);
|
||||
|
||||
ASSERT_EQ(e->else_statements.size(), 2u);
|
||||
ASSERT_NE(e->else_statements[0]->condition, nullptr);
|
||||
ASSERT_TRUE(
|
||||
e->else_statements[0]->condition->Is<ast::IdentifierExpression>());
|
||||
EXPECT_EQ(e->else_statements[0]->body->statements.size(), 1u);
|
||||
auto* elseif = As<ast::IfStatement>(e->else_statement);
|
||||
ASSERT_NE(elseif, nullptr);
|
||||
ASSERT_TRUE(elseif->condition->Is<ast::IdentifierExpression>());
|
||||
EXPECT_EQ(elseif->body->statements.size(), 1u);
|
||||
|
||||
ASSERT_EQ(e->else_statements[1]->condition, nullptr);
|
||||
EXPECT_EQ(e->else_statements[1]->body->statements.size(), 0u);
|
||||
auto* el = As<ast::BlockStatement>(elseif->else_statement);
|
||||
ASSERT_NE(el, nullptr);
|
||||
EXPECT_EQ(el->statements.size(), 0u);
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, IfStmt_WithElse_WithParens) {
|
||||
|
@ -68,14 +68,14 @@ TEST_F(ParserImplTest, IfStmt_WithElse_WithParens) {
|
|||
ASSERT_TRUE(e->condition->Is<ast::BinaryExpression>());
|
||||
EXPECT_EQ(e->body->statements.size(), 2u);
|
||||
|
||||
ASSERT_EQ(e->else_statements.size(), 2u);
|
||||
ASSERT_NE(e->else_statements[0]->condition, nullptr);
|
||||
ASSERT_TRUE(
|
||||
e->else_statements[0]->condition->Is<ast::IdentifierExpression>());
|
||||
EXPECT_EQ(e->else_statements[0]->body->statements.size(), 1u);
|
||||
auto* elseif = As<ast::IfStatement>(e->else_statement);
|
||||
ASSERT_NE(elseif, nullptr);
|
||||
ASSERT_TRUE(elseif->condition->Is<ast::IdentifierExpression>());
|
||||
EXPECT_EQ(elseif->body->statements.size(), 1u);
|
||||
|
||||
ASSERT_EQ(e->else_statements[1]->condition, nullptr);
|
||||
EXPECT_EQ(e->else_statements[1]->body->statements.size(), 0u);
|
||||
auto* el = As<ast::BlockStatement>(elseif->else_statement);
|
||||
ASSERT_NE(el, nullptr);
|
||||
EXPECT_EQ(el->statements.size(), 0u);
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, IfStmt_InvalidCondition) {
|
||||
|
|
|
@ -232,8 +232,8 @@ TEST_F(ResolverCompoundStatementTest, If) {
|
|||
auto* cond_b = Expr(true);
|
||||
auto* stmt_b = Ignore(1);
|
||||
auto* stmt_c = Ignore(1);
|
||||
auto* if_stmt = If(cond_a, Block(stmt_a), Else(cond_b, Block(stmt_b)),
|
||||
Else(nullptr, Block(stmt_c)));
|
||||
auto* if_stmt =
|
||||
If(cond_a, Block(stmt_a), If(cond_b, Block(stmt_b), Block(stmt_c)));
|
||||
WrapInFunction(if_stmt);
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
@ -268,8 +268,8 @@ TEST_F(ResolverCompoundStatementTest, If) {
|
|||
ASSERT_NE(e, nullptr);
|
||||
auto* s = e->Stmt();
|
||||
ASSERT_NE(s, nullptr);
|
||||
EXPECT_TRUE(s->Is<sem::ElseStatement>());
|
||||
EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::IfStatement>());
|
||||
EXPECT_TRUE(s->Is<sem::IfStatement>());
|
||||
EXPECT_EQ(s->Parent(), s->Parent()->FindFirstParent<sem::IfStatement>());
|
||||
EXPECT_EQ(s->Parent()->Parent(),
|
||||
s->FindFirstParent<sem::FunctionBlockStatement>());
|
||||
EXPECT_EQ(s->Parent()->Parent(), s->Block());
|
||||
|
@ -279,9 +279,10 @@ TEST_F(ResolverCompoundStatementTest, If) {
|
|||
ASSERT_NE(s, nullptr);
|
||||
EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::BlockStatement>());
|
||||
EXPECT_EQ(s->Parent(), s->Block());
|
||||
EXPECT_EQ(s->Parent()->Parent(), s->FindFirstParent<sem::ElseStatement>());
|
||||
auto* elseif = s->FindFirstParent<sem::IfStatement>();
|
||||
EXPECT_EQ(s->Parent()->Parent(), elseif);
|
||||
EXPECT_EQ(s->Parent()->Parent()->Parent(),
|
||||
s->FindFirstParent<sem::IfStatement>());
|
||||
elseif->Parent()->FindFirstParent<sem::IfStatement>());
|
||||
EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent(),
|
||||
s->FindFirstParent<sem::FunctionBlockStatement>());
|
||||
}
|
||||
|
@ -290,9 +291,10 @@ TEST_F(ResolverCompoundStatementTest, If) {
|
|||
ASSERT_NE(s, nullptr);
|
||||
EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::BlockStatement>());
|
||||
EXPECT_EQ(s->Parent(), s->Block());
|
||||
EXPECT_EQ(s->Parent()->Parent(), s->FindFirstParent<sem::ElseStatement>());
|
||||
auto* elseif = s->FindFirstParent<sem::IfStatement>();
|
||||
EXPECT_EQ(s->Parent()->Parent(), elseif);
|
||||
EXPECT_EQ(s->Parent()->Parent()->Parent(),
|
||||
s->FindFirstParent<sem::IfStatement>());
|
||||
elseif->Parent()->FindFirstParent<sem::IfStatement>());
|
||||
EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent(),
|
||||
s->FindFirstParent<sem::FunctionBlockStatement>());
|
||||
}
|
||||
|
|
|
@ -248,9 +248,8 @@ class DependencyScanner {
|
|||
[&](const ast::IfStatement* i) {
|
||||
TraverseExpression(i->condition);
|
||||
TraverseStatement(i->body);
|
||||
for (auto* e : i->else_statements) {
|
||||
TraverseExpression(e->condition);
|
||||
TraverseStatement(e->body);
|
||||
if (i->else_statement) {
|
||||
TraverseStatement(i->else_statement);
|
||||
}
|
||||
},
|
||||
[&](const ast::ReturnStatement* r) { //
|
||||
|
|
|
@ -1259,8 +1259,8 @@ TEST_F(ResolverDependencyGraphTraversalTest, SymbolsReached) {
|
|||
Assign(V, V)), //
|
||||
If(V, //
|
||||
Block(Assign(V, V)), //
|
||||
Else(V, //
|
||||
Block(Assign(V, V)))), //
|
||||
If(V, //
|
||||
Block(Assign(V, V)))), //
|
||||
Ignore(Bitcast(T, V)), //
|
||||
For(Decl(Var(Sym(), T, V)), //
|
||||
Equal(V, V), //
|
||||
|
|
|
@ -892,13 +892,6 @@ sem::Statement* Resolver::Statement(const ast::Statement* stmt) {
|
|||
stmt->source);
|
||||
return nullptr;
|
||||
},
|
||||
[&](const ast::ElseStatement*) {
|
||||
TINT_ICE(Resolver, diagnostics_)
|
||||
<< "Resolver::Statement() encountered an Else statement. Else "
|
||||
"statements are embedded in If statements, so should never be "
|
||||
"encountered as top-level statements";
|
||||
return nullptr;
|
||||
},
|
||||
[&](Default) {
|
||||
AddError(
|
||||
"unknown statement type: " + std::string(stmt->TypeInfo().name),
|
||||
|
@ -946,17 +939,14 @@ sem::IfStatement* Resolver::IfStatement(const ast::IfStatement* stmt) {
|
|||
}
|
||||
sem->Behaviors().Add(body->Behaviors());
|
||||
|
||||
for (auto* else_stmt : stmt->else_statements) {
|
||||
Mark(else_stmt);
|
||||
auto* else_sem = ElseStatement(else_stmt);
|
||||
if (stmt->else_statement) {
|
||||
Mark(stmt->else_statement);
|
||||
auto* else_sem = Statement(stmt->else_statement);
|
||||
if (!else_sem) {
|
||||
return false;
|
||||
}
|
||||
sem->Behaviors().Add(else_sem->Behaviors());
|
||||
}
|
||||
|
||||
if (stmt->else_statements.empty() ||
|
||||
stmt->else_statements.back()->condition != nullptr) {
|
||||
} else {
|
||||
// https://www.w3.org/TR/WGSL/#behaviors-rules
|
||||
// if statements without an else branch are treated as if they had an
|
||||
// empty else branch (which adds Next to their behavior)
|
||||
|
@ -967,37 +957,6 @@ sem::IfStatement* Resolver::IfStatement(const ast::IfStatement* stmt) {
|
|||
});
|
||||
}
|
||||
|
||||
sem::ElseStatement* Resolver::ElseStatement(const ast::ElseStatement* stmt) {
|
||||
auto* sem = builder_->create<sem::ElseStatement>(
|
||||
stmt, current_compound_statement_->As<sem::IfStatement>(),
|
||||
current_function_);
|
||||
return StatementScope(stmt, sem, [&] {
|
||||
if (auto* cond_expr = stmt->condition) {
|
||||
auto* cond = Expression(cond_expr);
|
||||
if (!cond) {
|
||||
return false;
|
||||
}
|
||||
sem->SetCondition(cond);
|
||||
// https://www.w3.org/TR/WGSL/#behaviors-rules
|
||||
// if statements with else if branches are treated as if they were nested
|
||||
// simple if/else statements
|
||||
sem->Behaviors() = cond->Behaviors();
|
||||
}
|
||||
sem->Behaviors().Remove(sem::Behavior::kNext);
|
||||
|
||||
Mark(stmt->body);
|
||||
auto* body = builder_->create<sem::BlockStatement>(
|
||||
stmt->body, current_compound_statement_, current_function_);
|
||||
if (!StatementScope(stmt->body, body,
|
||||
[&] { return Statements(stmt->body->statements); })) {
|
||||
return false;
|
||||
}
|
||||
sem->Behaviors().Add(body->Behaviors());
|
||||
|
||||
return validator_.ElseStatement(sem);
|
||||
});
|
||||
}
|
||||
|
||||
sem::BlockStatement* Resolver::BlockStatement(const ast::BlockStatement* stmt) {
|
||||
auto* sem = builder_->create<sem::BlockStatement>(
|
||||
stmt->As<ast::BlockStatement>(), current_compound_statement_,
|
||||
|
|
|
@ -59,7 +59,6 @@ class Atomic;
|
|||
class BlockStatement;
|
||||
class Builtin;
|
||||
class CaseStatement;
|
||||
class ElseStatement;
|
||||
class ForLoopStatement;
|
||||
class IfStatement;
|
||||
class LoopStatement;
|
||||
|
@ -222,7 +221,6 @@ class Resolver {
|
|||
const ast::CompoundAssignmentStatement*);
|
||||
sem::Statement* ContinueStatement(const ast::ContinueStatement*);
|
||||
sem::Statement* DiscardStatement(const ast::DiscardStatement*);
|
||||
sem::ElseStatement* ElseStatement(const ast::ElseStatement*);
|
||||
sem::Statement* FallthroughStatement(const ast::FallthroughStatement*);
|
||||
sem::ForLoopStatement* ForLoopStatement(const ast::ForLoopStatement*);
|
||||
sem::GlobalVariable* GlobalVariable(const ast::Variable*);
|
||||
|
|
|
@ -348,7 +348,7 @@ TEST_F(ResolverBehaviorTest, StmtIfTrue_ThenDiscard) {
|
|||
}
|
||||
|
||||
TEST_F(ResolverBehaviorTest, StmtIfTrue_ThenEmptyBlock_ElseDiscard) {
|
||||
auto* stmt = If(true, Block(), Else(Block(Discard())));
|
||||
auto* stmt = If(true, Block(), Block(Discard()));
|
||||
WrapInFunction(stmt);
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
@ -359,7 +359,7 @@ TEST_F(ResolverBehaviorTest, StmtIfTrue_ThenEmptyBlock_ElseDiscard) {
|
|||
}
|
||||
|
||||
TEST_F(ResolverBehaviorTest, StmtIfTrue_ThenDiscard_ElseDiscard) {
|
||||
auto* stmt = If(true, Block(Discard()), Else(Block(Discard())));
|
||||
auto* stmt = If(true, Block(Discard()), Block(Discard()));
|
||||
WrapInFunction(stmt);
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
@ -381,7 +381,7 @@ TEST_F(ResolverBehaviorTest, StmtIfCallFuncMayDiscard_ThenEmptyBlock) {
|
|||
|
||||
TEST_F(ResolverBehaviorTest, StmtIfTrue_ThenEmptyBlock_ElseCallFuncMayDiscard) {
|
||||
auto* stmt = If(true, Block(), //
|
||||
Else(Equal(Call("DiscardOrNext"), 1), Block()));
|
||||
If(Equal(Call("DiscardOrNext"), 1), Block()));
|
||||
WrapInFunction(stmt);
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
|
|
@ -160,7 +160,7 @@ TEST_F(ResolverTest, Stmt_If) {
|
|||
auto* else_body = Block(Assign(else_lhs, else_rhs));
|
||||
|
||||
auto* else_cond = Expr(true);
|
||||
auto* else_stmt = create<ast::ElseStatement>(else_cond, else_body);
|
||||
auto* else_stmt = If(else_cond, else_body);
|
||||
|
||||
auto* lhs = Expr("v");
|
||||
auto* rhs = Expr(2.3f);
|
||||
|
@ -168,8 +168,7 @@ TEST_F(ResolverTest, Stmt_If) {
|
|||
auto* assign = Assign(lhs, rhs);
|
||||
auto* body = Block(assign);
|
||||
auto* cond = Expr(true);
|
||||
auto* stmt =
|
||||
create<ast::IfStatement>(cond, body, ast::ElseStatementList{else_stmt});
|
||||
auto* stmt = If(cond, body, else_stmt);
|
||||
WrapInFunction(v, stmt);
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
|
|
@ -149,9 +149,7 @@ TEST_F(ResolverTypeValidationTest, RedeclaredIdentifierInnerScope_Pass) {
|
|||
|
||||
auto* var_a_float = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(3.1f));
|
||||
|
||||
auto* outer_body =
|
||||
Block(create<ast::IfStatement>(cond, body, ast::ElseStatementList{}),
|
||||
Decl(Source{{12, 34}}, var_a_float));
|
||||
auto* outer_body = Block(If(cond, body), Decl(Source{{12, 34}}, var_a_float));
|
||||
|
||||
WrapInFunction(outer_body);
|
||||
|
||||
|
|
|
@ -124,16 +124,16 @@ TEST_F(ResolverValidationTest, Stmt_If_NonBool) {
|
|||
"12:34 error: if statement condition must be bool, got f32");
|
||||
}
|
||||
|
||||
TEST_F(ResolverValidationTest, Stmt_Else_NonBool) {
|
||||
// else (1.23f) {}
|
||||
TEST_F(ResolverValidationTest, Stmt_ElseIf_NonBool) {
|
||||
// else if (1.23f) {}
|
||||
|
||||
WrapInFunction(
|
||||
If(Expr(true), Block(), Else(Expr(Source{{12, 34}}, 1.23f), Block())));
|
||||
If(Expr(true), Block(), If(Expr(Source{{12, 34}}, 1.23f), Block())));
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: else statement condition must be bool, got f32");
|
||||
"12:34 error: if statement condition must be bool, got f32");
|
||||
}
|
||||
|
||||
TEST_F(ResolverValidationTest, Expr_ErrUnknownExprType) {
|
||||
|
@ -242,9 +242,7 @@ TEST_F(ResolverValidationTest, UsingUndefinedVariableInnerScope_Fail) {
|
|||
auto* lhs = Expr(Source{{12, 34}}, "a");
|
||||
auto* rhs = Expr(3.14f);
|
||||
|
||||
auto* outer_body =
|
||||
Block(create<ast::IfStatement>(cond, body, ast::ElseStatementList{}),
|
||||
Assign(lhs, rhs));
|
||||
auto* outer_body = Block(If(cond, body), Assign(lhs, rhs));
|
||||
|
||||
WrapInFunction(outer_body);
|
||||
|
||||
|
@ -265,9 +263,7 @@ TEST_F(ResolverValidationTest, UsingUndefinedVariableOuterScope_Pass) {
|
|||
auto* cond = Expr(true);
|
||||
auto* body = Block(Assign(lhs, rhs));
|
||||
|
||||
auto* outer_body =
|
||||
Block(Decl(var),
|
||||
create<ast::IfStatement>(cond, body, ast::ElseStatementList{}));
|
||||
auto* outer_body = Block(Decl(var), If(cond, body));
|
||||
|
||||
WrapInFunction(outer_body);
|
||||
|
||||
|
@ -1053,12 +1049,12 @@ TEST_F(ResolverValidationTest, Stmt_BreakInIfTrueInContinuing) {
|
|||
}
|
||||
|
||||
TEST_F(ResolverValidationTest, Stmt_BreakInIfElseInContinuing) {
|
||||
auto* cont = Block( // continuing {
|
||||
If(true, Block(), // if(true) {
|
||||
Else(Block( // } else {
|
||||
Break(Source{{12, 34}}))))); // break;
|
||||
// }
|
||||
// }
|
||||
auto* cont = Block( // continuing {
|
||||
If(true, Block(), // if(true) {
|
||||
Block( // } else {
|
||||
Break(Source{{12, 34}})))); // break;
|
||||
// }
|
||||
// }
|
||||
WrapInFunction(Loop(Block(), cont));
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
@ -1114,13 +1110,13 @@ TEST_F(ResolverValidationTest, Stmt_BreakInIfTrueMultipleStmtsInContinuing) {
|
|||
}
|
||||
|
||||
TEST_F(ResolverValidationTest, Stmt_BreakInIfElseMultipleStmtsInContinuing) {
|
||||
auto* cont = Block( // continuing {
|
||||
If(true, Block(), // if(true) {
|
||||
Else(Block(Source{{56, 78}}, // } else {
|
||||
Assign(Phony(), 1), // _ = 1;
|
||||
Break(Source{{12, 34}}))))); // break;
|
||||
// }
|
||||
// }
|
||||
auto* cont = Block( // continuing {
|
||||
If(true, Block(), // if(true) {
|
||||
Block(Source{{56, 78}}, // } else {
|
||||
Assign(Phony(), 1), // _ = 1;
|
||||
Break(Source{{12, 34}})))); // break;
|
||||
// }
|
||||
// }
|
||||
WrapInFunction(Loop(Block(), cont));
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(
|
||||
|
@ -1132,12 +1128,12 @@ TEST_F(ResolverValidationTest, Stmt_BreakInIfElseMultipleStmtsInContinuing) {
|
|||
}
|
||||
|
||||
TEST_F(ResolverValidationTest, Stmt_BreakInIfElseIfInContinuing) {
|
||||
auto* cont = Block( // continuing {
|
||||
If(true, Block(), // if(true) {
|
||||
Else(Expr(Source{{56, 78}}, true), // } else if (true) {
|
||||
Block(Break(Source{{12, 34}}))))); // break;
|
||||
// }
|
||||
// }
|
||||
auto* cont = Block( // continuing {
|
||||
If(true, Block(), // if(true) {
|
||||
If(Source{{56, 78}}, Expr(true), // } else if (true) {
|
||||
Block(Break(Source{{12, 34}}))))); // break;
|
||||
// }
|
||||
// }
|
||||
WrapInFunction(Loop(Block(), cont));
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(
|
||||
|
@ -1149,13 +1145,13 @@ TEST_F(ResolverValidationTest, Stmt_BreakInIfElseIfInContinuing) {
|
|||
}
|
||||
|
||||
TEST_F(ResolverValidationTest, Stmt_BreakInIfNonEmptyElseInContinuing) {
|
||||
auto* cont = Block( // continuing {
|
||||
If(true, // if(true) {
|
||||
Block(Break(Source{{12, 34}})), // break;
|
||||
Else(Block(Source{{56, 78}}, // } else {
|
||||
Assign(Phony(), 1))))); // _ = 1;
|
||||
// }
|
||||
// }
|
||||
auto* cont = Block( // continuing {
|
||||
If(true, // if(true) {
|
||||
Block(Break(Source{{12, 34}})), // break;
|
||||
Block(Source{{56, 78}}, // } else {
|
||||
Assign(Phony(), 1)))); // _ = 1;
|
||||
// }
|
||||
// }
|
||||
WrapInFunction(Loop(Block(), cont));
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(
|
||||
|
@ -1170,8 +1166,8 @@ TEST_F(ResolverValidationTest, Stmt_BreakInIfElseNonEmptyTrueInContinuing) {
|
|||
auto* cont = Block( // continuing {
|
||||
If(true, // if(true) {
|
||||
Block(Source{{56, 78}}, Assign(Phony(), 1)), // _ = 1;
|
||||
Else(Block( // } else {
|
||||
Break(Source{{12, 34}}))))); // break;
|
||||
Block( // } else {
|
||||
Break(Source{{12, 34}})))); // break;
|
||||
// }
|
||||
// }
|
||||
WrapInFunction(Loop(Block(), cont));
|
||||
|
|
|
@ -1383,10 +1383,6 @@ bool Validator::BreakStatement(const sem::Statement* stmt,
|
|||
if (auto* block = stmt->Parent()->As<sem::BlockStatement>()) {
|
||||
auto* block_parent = block->Parent();
|
||||
auto* if_stmt = block_parent->As<sem::IfStatement>();
|
||||
auto* el_stmt = block_parent->As<sem::ElseStatement>();
|
||||
if (el_stmt) {
|
||||
if_stmt = el_stmt->Parent();
|
||||
}
|
||||
if (!if_stmt) {
|
||||
return fail("break statement is not directly in if statement block",
|
||||
stmt->Declaration()->source);
|
||||
|
@ -1395,22 +1391,25 @@ bool Validator::BreakStatement(const sem::Statement* stmt,
|
|||
return fail("if statement block contains multiple statements",
|
||||
block->Declaration()->source);
|
||||
}
|
||||
for (auto* el : if_stmt->Declaration()->else_statements) {
|
||||
if (el->condition) {
|
||||
return fail("else has condition", el->condition->source);
|
||||
|
||||
if (if_stmt->Parent()->Is<sem::IfStatement>()) {
|
||||
return fail("else has condition", if_stmt->Declaration()->source);
|
||||
}
|
||||
|
||||
bool el_contains_break =
|
||||
block->Declaration() == if_stmt->Declaration()->else_statement;
|
||||
if (el_contains_break) {
|
||||
if (auto* true_block = if_stmt->Declaration()->body;
|
||||
!true_block->Empty()) {
|
||||
return fail("non-empty true block", true_block->source);
|
||||
}
|
||||
bool el_contains_break = el_stmt && el == el_stmt->Declaration();
|
||||
if (el_contains_break) {
|
||||
if (auto* true_block = if_stmt->Declaration()->body;
|
||||
!true_block->Empty()) {
|
||||
return fail("non-empty true block", true_block->source);
|
||||
}
|
||||
} else {
|
||||
if (!el->body->Empty()) {
|
||||
return fail("non-empty false block", el->body->source);
|
||||
}
|
||||
} else {
|
||||
auto* else_stmt = if_stmt->Declaration()->else_statement;
|
||||
if (else_stmt) {
|
||||
return fail("non-empty false block", else_stmt->source);
|
||||
}
|
||||
}
|
||||
|
||||
if (if_stmt->Parent()->Declaration() != continuing) {
|
||||
return fail(
|
||||
"if statement containing break statement is not directly in "
|
||||
|
@ -1490,19 +1489,6 @@ bool Validator::FallthroughStatement(const sem::Statement* stmt) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Validator::ElseStatement(const sem::ElseStatement* stmt) const {
|
||||
if (auto* cond = stmt->Condition()) {
|
||||
auto* cond_ty = cond->Type()->UnwrapRef();
|
||||
if (!cond_ty->Is<sem::Bool>()) {
|
||||
AddError("else statement condition must be bool, got " +
|
||||
sem_.TypeNameOf(cond_ty),
|
||||
stmt->Condition()->Declaration()->source);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Validator::LoopStatement(const sem::LoopStatement* stmt) const {
|
||||
if (stmt->Behaviors().Empty()) {
|
||||
AddError("loop does not exit", stmt->Declaration()->source.Begin());
|
||||
|
|
|
@ -51,7 +51,6 @@ class Atomic;
|
|||
class BlockStatement;
|
||||
class Builtin;
|
||||
class CaseStatement;
|
||||
class ElseStatement;
|
||||
class ForLoopStatement;
|
||||
class IfStatement;
|
||||
class LoopStatement;
|
||||
|
@ -194,11 +193,6 @@ class Validator {
|
|||
bool DiscardStatement(const sem::Statement* stmt,
|
||||
sem::Statement* current_statement) const;
|
||||
|
||||
/// Validates an else statement
|
||||
/// @param stmt the else statement to validate
|
||||
/// @returns true on success, false otherwise
|
||||
bool ElseStatement(const sem::ElseStatement* stmt) const;
|
||||
|
||||
/// Validates an entry point
|
||||
/// @param func the entry point function to validate
|
||||
/// @param stage the pipeline stage for the entry point
|
||||
|
|
|
@ -205,9 +205,7 @@ TEST_F(ResolverVarLetValidationTest, VarRedeclaredInIfBlock) {
|
|||
auto* cond = Expr(true);
|
||||
auto* body = Block(Decl(var));
|
||||
|
||||
auto* outer_body =
|
||||
Block(Decl(var_a_float),
|
||||
create<ast::IfStatement>(cond, body, ast::ElseStatementList{}));
|
||||
auto* outer_body = Block(Decl(var_a_float), If(cond, body));
|
||||
|
||||
WrapInFunction(outer_body);
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include "src/tint/program_builder.h"
|
||||
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::IfStatement);
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::ElseStatement);
|
||||
|
||||
namespace tint::sem {
|
||||
|
||||
|
@ -32,15 +31,4 @@ const ast::IfStatement* IfStatement::Declaration() const {
|
|||
return static_cast<const ast::IfStatement*>(Base::Declaration());
|
||||
}
|
||||
|
||||
ElseStatement::ElseStatement(const ast::ElseStatement* declaration,
|
||||
const IfStatement* parent,
|
||||
const sem::Function* function)
|
||||
: Base(declaration, parent, function) {}
|
||||
|
||||
ElseStatement::~ElseStatement() = default;
|
||||
|
||||
const ast::ElseStatement* ElseStatement::Declaration() const {
|
||||
return static_cast<const ast::ElseStatement*>(Base::Declaration());
|
||||
}
|
||||
|
||||
} // namespace tint::sem
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
// Forward declarations
|
||||
namespace tint::ast {
|
||||
class IfStatement;
|
||||
class ElseStatement;
|
||||
} // namespace tint::ast
|
||||
namespace tint::sem {
|
||||
class Expression;
|
||||
|
@ -56,39 +55,6 @@ class IfStatement final : public Castable<IfStatement, CompoundStatement> {
|
|||
const Expression* condition_ = nullptr;
|
||||
};
|
||||
|
||||
/// Holds semantic information about an else statement
|
||||
class ElseStatement final : public Castable<ElseStatement, CompoundStatement> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param declaration the AST node for this else statement
|
||||
/// @param parent the owning statement
|
||||
/// @param function the owning function
|
||||
ElseStatement(const ast::ElseStatement* declaration,
|
||||
const IfStatement* parent,
|
||||
const sem::Function* function);
|
||||
|
||||
/// Destructor
|
||||
~ElseStatement() override;
|
||||
|
||||
/// @returns the AST node
|
||||
const ast::ElseStatement* Declaration() const;
|
||||
|
||||
/// @returns the else-statement condition expression
|
||||
const Expression* Condition() const { return condition_; }
|
||||
|
||||
/// @return the statement that encloses this statement
|
||||
const IfStatement* Parent() const {
|
||||
return static_cast<const IfStatement*>(Statement::Parent());
|
||||
}
|
||||
|
||||
/// Sets the else-statement condition expression
|
||||
/// @param condition the else condition expression
|
||||
void SetCondition(const Expression* condition) { condition_ = condition; }
|
||||
|
||||
private:
|
||||
const Expression* condition_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace tint::sem
|
||||
|
||||
#endif // SRC_TINT_SEM_IF_STATEMENT_H_
|
||||
|
|
|
@ -22,7 +22,6 @@ namespace tint::ast {
|
|||
class Array;
|
||||
class CallExpression;
|
||||
class Expression;
|
||||
class ElseStatement;
|
||||
class ForLoopStatement;
|
||||
class Function;
|
||||
class IfStatement;
|
||||
|
@ -39,7 +38,6 @@ namespace tint::sem {
|
|||
class Array;
|
||||
class Call;
|
||||
class Expression;
|
||||
class ElseStatement;
|
||||
class ForLoopStatement;
|
||||
class Function;
|
||||
class IfStatement;
|
||||
|
@ -63,7 +61,6 @@ struct TypeMappings {
|
|||
Array* operator()(ast::Array*);
|
||||
Call* operator()(ast::CallExpression*);
|
||||
Expression* operator()(ast::Expression*);
|
||||
ElseStatement* operator()(ast::ElseStatement*);
|
||||
ForLoopStatement* operator()(ast::ForLoopStatement*);
|
||||
Function* operator()(ast::Function*);
|
||||
IfStatement* operator()(ast::IfStatement*);
|
||||
|
|
|
@ -83,14 +83,14 @@ void LoopToForLoop::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
|
|||
if (!if_stmt) {
|
||||
return nullptr;
|
||||
}
|
||||
auto* else_stmt = tint::As<ast::BlockStatement>(if_stmt->else_statement);
|
||||
|
||||
bool negate_condition = false;
|
||||
if (IsBlockWithSingleBreak(if_stmt->body) &&
|
||||
if_stmt->else_statements.empty()) {
|
||||
if_stmt->else_statement == nullptr) {
|
||||
negate_condition = true;
|
||||
} else if (if_stmt->body->Empty() && if_stmt->else_statements.size() == 1 &&
|
||||
if_stmt->else_statements[0]->condition == nullptr &&
|
||||
IsBlockWithSingleBreak(if_stmt->else_statements[0]->body)) {
|
||||
} else if (if_stmt->body->Empty() && else_stmt &&
|
||||
IsBlockWithSingleBreak(else_stmt)) {
|
||||
negate_condition = false;
|
||||
} else {
|
||||
return nullptr;
|
||||
|
|
|
@ -302,5 +302,57 @@ fn f() {
|
|||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(LoopToForLoopTest, NoTransform_IfBreakWithElse) {
|
||||
auto* src = R"(
|
||||
fn f() {
|
||||
var i : i32;
|
||||
i = 0;
|
||||
loop {
|
||||
if ((i > 15)) {
|
||||
break;
|
||||
} else {
|
||||
}
|
||||
_ = 123;
|
||||
|
||||
continuing {
|
||||
i = (i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = src;
|
||||
|
||||
auto got = Run<LoopToForLoop>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(LoopToForLoopTest, NoTransform_IfBreakWithElseIf) {
|
||||
auto* src = R"(
|
||||
fn f() {
|
||||
var i : i32;
|
||||
i = 0;
|
||||
loop {
|
||||
if ((i > 15)) {
|
||||
break;
|
||||
} else if (true) {
|
||||
}
|
||||
_ = 123;
|
||||
|
||||
continuing {
|
||||
i = (i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = src;
|
||||
|
||||
auto got = Run<LoopToForLoop>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace tint::transform
|
||||
|
|
|
@ -288,7 +288,7 @@ struct MultiplanarExternalTexture::State {
|
|||
b.Block(
|
||||
// color = textureLoad(plane0, coord, 0).rgb;
|
||||
b.Assign("color", b.MemberAccessor(single_plane_call, "rgb"))),
|
||||
b.Else(b.Block(
|
||||
b.Block(
|
||||
// color = vec4<f32>(plane_0_call.r, plane_1_call.rg, 1.0) *
|
||||
// params.yuvToRgbConversionMatrix;
|
||||
b.Assign("color",
|
||||
|
@ -296,7 +296,7 @@ struct MultiplanarExternalTexture::State {
|
|||
b.MemberAccessor(plane_0_call, "r"),
|
||||
b.MemberAccessor(plane_1_call, "rg"), 1.0f),
|
||||
b.MemberAccessor(
|
||||
"params", "yuvToRgbConversionMatrix")))))),
|
||||
"params", "yuvToRgbConversionMatrix"))))),
|
||||
// return vec4<f32>(color, 1.0f);
|
||||
b.Return(b.vec4<f32>("color", 1.0f))};
|
||||
}
|
||||
|
|
|
@ -393,9 +393,6 @@ class DecomposeSideEffects::CollectHoistsState : public StateBase {
|
|||
[&](const ast::CallStatement* s) { //
|
||||
ProcessStatement(s->expr);
|
||||
},
|
||||
[&](const ast::ElseStatement* s) { //
|
||||
ProcessStatement(s->condition);
|
||||
},
|
||||
[&](const ast::ForLoopStatement* s) {
|
||||
ProcessStatement(s->condition);
|
||||
},
|
||||
|
@ -594,18 +591,6 @@ class DecomposeSideEffects::DecomposeState : public StateBase {
|
|||
InsertBefore(stmts, s);
|
||||
return ctx.CloneWithoutTransform(s);
|
||||
},
|
||||
[&](const ast::ElseStatement* s) -> const ast::Statement* {
|
||||
if (!s->condition || !sem.Get(s->condition)->HasSideEffects()) {
|
||||
return nullptr;
|
||||
}
|
||||
// NOTE: We shouldn't reach here as else-if with side-effect
|
||||
// conditions are simplified to else { if } by
|
||||
// SimplifySideEffectStatements.
|
||||
ast::StatementList stmts;
|
||||
ctx.Replace(s->condition, Decompose(s->condition, &stmts));
|
||||
InsertBefore(stmts, s);
|
||||
return ctx.CloneWithoutTransform(s);
|
||||
},
|
||||
[&](const ast::ForLoopStatement* s) -> const ast::Statement* {
|
||||
if (!s->condition || !sem.Get(s->condition)->HasSideEffects()) {
|
||||
return nullptr;
|
||||
|
|
|
@ -43,15 +43,6 @@ class State {
|
|||
Symbol module_discard_var_name; // Use ModuleDiscardVarName() to read
|
||||
Symbol module_discard_func_name; // Use ModuleDiscardFuncName() to read
|
||||
|
||||
// If `block`'s parent is of type TO, returns pointer to it.
|
||||
template <typename TO>
|
||||
const TO* ParentAs(const ast::BlockStatement* block) {
|
||||
if (auto* sem_block = sem.Get(block)) {
|
||||
return As<TO>(sem_block->Parent());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Returns true if `sem_expr` contains a call expression that may
|
||||
// (transitively) execute a discard statement.
|
||||
bool MayDiscard(const sem::Expression* sem_expr) {
|
||||
|
@ -265,14 +256,6 @@ class State {
|
|||
}
|
||||
return TryInsertAfter(s, sem_expr);
|
||||
},
|
||||
[&](const ast::ElseStatement* s) -> const ast::Statement* {
|
||||
if (MayDiscard(sem.Get(s->condition))) {
|
||||
TINT_ICE(Transform, b.Diagnostics())
|
||||
<< "Unexpected ElseIf condition that may discard. Make sure "
|
||||
"transform::PromoteSideEffectsToDecl was run first.";
|
||||
}
|
||||
return nullptr;
|
||||
},
|
||||
[&](const ast::ForLoopStatement* s) -> const ast::Statement* {
|
||||
if (MayDiscard(sem.Get(s->condition))) {
|
||||
TINT_ICE(Transform, b.Diagnostics())
|
||||
|
@ -326,20 +309,6 @@ class State {
|
|||
void Run() {
|
||||
ctx.ReplaceAll(
|
||||
[&](const ast::BlockStatement* block) -> const ast::Statement* {
|
||||
// If this block is for an else-if statement, process the else-if now
|
||||
// before processing its block statements.
|
||||
// NOTE: we can't replace else statements at this point - this would
|
||||
// need to be done when replacing the parent if-statement. However, in
|
||||
// this transform, we don't ever expect to need to do this as else-ifs
|
||||
// are converted to else { if } by PromoteSideEffectsToDecl, so this
|
||||
// is only for validation.
|
||||
if (auto* sem_else = ParentAs<sem::ElseStatement>(block)) {
|
||||
if (auto* new_stmt = Statement(sem_else->Declaration())) {
|
||||
TINT_ASSERT(Transform, new_stmt == nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate block statements and replace them as needed.
|
||||
for (auto* stmt : block->statements) {
|
||||
if (auto* new_stmt = Statement(stmt)) {
|
||||
|
|
|
@ -39,25 +39,17 @@ class HoistToDeclBefore::State {
|
|||
ast::StatementList cont_decls;
|
||||
};
|
||||
|
||||
/// Holds information about 'if's with 'else-if' statements that need to be
|
||||
/// decomposed into 'if {else}' so that declaration statements can be
|
||||
/// inserted before the condition expression.
|
||||
struct IfInfo {
|
||||
/// Info for each else-if that needs decomposing
|
||||
struct ElseIfInfo {
|
||||
/// Decls to insert before condition
|
||||
ast::StatementList cond_decls;
|
||||
};
|
||||
|
||||
/// 'else if's that need to be decomposed to 'else { if }'
|
||||
std::unordered_map<const sem::ElseStatement*, ElseIfInfo> else_ifs;
|
||||
/// Info for each else-if that needs decomposing
|
||||
struct ElseIfInfo {
|
||||
/// Decls to insert before condition
|
||||
ast::StatementList cond_decls;
|
||||
};
|
||||
|
||||
/// For-loops that need to be decomposed to loops.
|
||||
std::unordered_map<const sem::ForLoopStatement*, LoopInfo> loops;
|
||||
|
||||
/// If statements with 'else if's that need to be decomposed to 'else {if}'
|
||||
std::unordered_map<const sem::IfStatement*, IfInfo> ifs;
|
||||
/// 'else if' statements that need to be decomposed to 'else {if}'
|
||||
std::unordered_map<const ast::IfStatement*, ElseIfInfo> else_ifs;
|
||||
|
||||
// Converts any for-loops marked for conversion to loops, inserting
|
||||
// registered declaration statements before the condition or continuing
|
||||
|
@ -118,78 +110,27 @@ class HoistToDeclBefore::State {
|
|||
}
|
||||
|
||||
void ElseIfsToElseWithNestedIfs() {
|
||||
if (ifs.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.ReplaceAll([&](const ast::IfStatement* if_stmt) //
|
||||
-> const ast::IfStatement* {
|
||||
auto& sem = ctx.src->Sem();
|
||||
auto* sem_if = sem.Get(if_stmt);
|
||||
if (!sem_if) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto it = ifs.find(sem_if);
|
||||
if (it == ifs.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto& if_info = it->second;
|
||||
|
||||
// This if statement has "else if"s that need to be converted to "else
|
||||
// { if }"s
|
||||
|
||||
ast::ElseStatementList next_else_stmts;
|
||||
next_else_stmts.reserve(if_stmt->else_statements.size());
|
||||
|
||||
for (auto* else_stmt : utils::Reverse(if_stmt->else_statements)) {
|
||||
if (else_stmt->condition == nullptr) {
|
||||
// The last 'else', keep as is
|
||||
next_else_stmts.insert(next_else_stmts.begin(), ctx.Clone(else_stmt));
|
||||
|
||||
} else {
|
||||
auto* sem_else_if = sem.Get(else_stmt);
|
||||
|
||||
auto it2 = if_info.else_ifs.find(sem_else_if);
|
||||
if (it2 == if_info.else_ifs.end()) {
|
||||
// 'else if' we don't need to modify (no decls to insert), so
|
||||
// keep as is
|
||||
next_else_stmts.insert(next_else_stmts.begin(),
|
||||
ctx.Clone(else_stmt));
|
||||
|
||||
} else {
|
||||
// 'else if' we need to replace with 'else <decls> { if }'
|
||||
auto& else_if_info = it2->second;
|
||||
|
||||
// Build the else body's statements, starting with let decls for
|
||||
// the conditional expression
|
||||
auto& body_stmts = else_if_info.cond_decls;
|
||||
|
||||
// Build nested if
|
||||
auto* cond = ctx.Clone(else_stmt->condition);
|
||||
auto* body = ctx.Clone(else_stmt->body);
|
||||
body_stmts.emplace_back(b.If(cond, body, next_else_stmts));
|
||||
|
||||
// Build else
|
||||
auto* else_with_nested_if = b.Else(b.Block(body_stmts));
|
||||
|
||||
// This will be used in parent if (either another nested if, or
|
||||
// top-level if)
|
||||
next_else_stmts = {else_with_nested_if};
|
||||
// Decompose 'else-if' statements into 'else { if }' blocks.
|
||||
ctx.ReplaceAll(
|
||||
[&](const ast::IfStatement* else_if) -> const ast::Statement* {
|
||||
if (!else_ifs.count(else_if)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto& else_if_info = else_ifs[else_if];
|
||||
|
||||
// Build a new top-level if with new else statements
|
||||
if (next_else_stmts.empty()) {
|
||||
TINT_ICE(Transform, b.Diagnostics())
|
||||
<< "Expected else statements to insert into new if";
|
||||
}
|
||||
auto* cond = ctx.Clone(if_stmt->condition);
|
||||
auto* body = ctx.Clone(if_stmt->body);
|
||||
auto* new_if = b.If(cond, body, next_else_stmts);
|
||||
return new_if;
|
||||
});
|
||||
// Build the else block's body statements, starting with let decls for
|
||||
// the conditional expression.
|
||||
auto& body_stmts = else_if_info.cond_decls;
|
||||
|
||||
// Move the 'else-if' into the new `else` block as a plain 'if'.
|
||||
auto* cond = ctx.Clone(else_if->condition);
|
||||
auto* body = ctx.Clone(else_if->body);
|
||||
auto* new_if = b.If(cond, body, ctx.Clone(else_if->else_statement));
|
||||
body_stmts.emplace_back(new_if);
|
||||
|
||||
// Replace the 'else-if' with the new 'else' block.
|
||||
return b.Block(body_stmts);
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -235,13 +176,14 @@ class HoistToDeclBefore::State {
|
|||
const ast::Statement* stmt) {
|
||||
auto* ip = before_stmt->Declaration();
|
||||
|
||||
if (auto* else_if = before_stmt->As<sem::ElseStatement>()) {
|
||||
auto* else_if = before_stmt->As<sem::IfStatement>();
|
||||
if (else_if && else_if->Parent()->Is<sem::IfStatement>()) {
|
||||
// Insertion point is an 'else if' condition.
|
||||
// Need to convert 'else if' to 'else { if }'.
|
||||
auto& if_info = ifs[else_if->Parent()->As<sem::IfStatement>()];
|
||||
auto& else_if_info = else_ifs[else_if->Declaration()];
|
||||
|
||||
// Index the map to convert this else if, even if `stmt` is nullptr.
|
||||
auto& decls = if_info.else_ifs[else_if].cond_decls;
|
||||
auto& decls = else_if_info.cond_decls;
|
||||
if (stmt) {
|
||||
decls.emplace_back(stmt);
|
||||
}
|
||||
|
|
|
@ -187,8 +187,8 @@ TEST_F(HoistToDeclBeforeTest, ElseIf) {
|
|||
auto* var = b.Decl(b.Var("a", b.ty.bool_()));
|
||||
auto* expr = b.Expr("a");
|
||||
auto* s = b.If(b.Expr(true), b.Block(), //
|
||||
b.Else(expr, b.Block()), //
|
||||
b.Else(b.Block()));
|
||||
b.If(expr, b.Block(), //
|
||||
b.Block()));
|
||||
b.Func("f", {}, b.ty.void_(), {var, s});
|
||||
|
||||
Program original(std::move(b));
|
||||
|
@ -383,8 +383,8 @@ TEST_F(HoistToDeclBeforeTest, Prepare_ElseIf) {
|
|||
auto* var = b.Decl(b.Var("a", b.ty.bool_()));
|
||||
auto* expr = b.Expr("a");
|
||||
auto* s = b.If(b.Expr(true), b.Block(), //
|
||||
b.Else(expr, b.Block()), //
|
||||
b.Else(b.Block()));
|
||||
b.If(expr, b.Block(), //
|
||||
b.Block()));
|
||||
b.Func("f", {}, b.ty.void_(), {var, s});
|
||||
|
||||
Program original(std::move(b));
|
||||
|
@ -556,10 +556,9 @@ TEST_F(HoistToDeclBeforeTest, InsertBefore_ElseIf) {
|
|||
ProgramBuilder b;
|
||||
b.Func("foo", {}, b.ty.void_(), {});
|
||||
auto* var = b.Decl(b.Var("a", b.ty.bool_()));
|
||||
auto* elseif = b.Else(b.Expr("a"), b.Block());
|
||||
auto* elseif = b.If(b.Expr("a"), b.Block(), b.Block());
|
||||
auto* s = b.If(b.Expr(true), b.Block(), //
|
||||
elseif, //
|
||||
b.Else(b.Block()));
|
||||
elseif);
|
||||
b.Func("f", {}, b.ty.void_(), {var, s});
|
||||
|
||||
Program original(std::move(b));
|
||||
|
|
|
@ -1842,36 +1842,20 @@ bool GeneratorImpl::EmitIf(const ast::IfStatement* stmt) {
|
|||
return false;
|
||||
}
|
||||
|
||||
for (auto* e : stmt->else_statements) {
|
||||
if (e->condition) {
|
||||
line() << "} else {";
|
||||
increment_indent();
|
||||
|
||||
{
|
||||
auto out = line();
|
||||
out << "if (";
|
||||
if (!EmitExpression(out, e->condition)) {
|
||||
return false;
|
||||
}
|
||||
out << ") {";
|
||||
if (stmt->else_statement) {
|
||||
line() << "} else {";
|
||||
if (auto* block = stmt->else_statement->As<ast::BlockStatement>()) {
|
||||
if (!EmitStatementsWithIndent(block->statements)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
line() << "} else {";
|
||||
}
|
||||
|
||||
if (!EmitStatementsWithIndent(e->body->statements)) {
|
||||
return false;
|
||||
if (!EmitStatementsWithIndent({stmt->else_statement})) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
line() << "}";
|
||||
|
||||
for (auto* e : stmt->else_statements) {
|
||||
if (e->condition) {
|
||||
decrement_indent();
|
||||
line() << "}";
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -360,10 +360,9 @@ TEST_F(GlslGeneratorImplTest_Binary, If_WithLogical) {
|
|||
auto* expr = If(create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd,
|
||||
Expr("a"), Expr("b")),
|
||||
Block(Return(1)),
|
||||
Else(create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr,
|
||||
Expr("b"), Expr("c")),
|
||||
Block(Return(2))),
|
||||
Else(Block(Return(3))));
|
||||
If(create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr,
|
||||
Expr("b"), Expr("c")),
|
||||
Block(Return(2)), Block(Return(3))));
|
||||
Func("func", {}, ty.i32(), {WrapInStatement(expr)});
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
|
|
@ -46,9 +46,7 @@ TEST_F(GlslGeneratorImplTest_If, Emit_IfWithElseIf) {
|
|||
|
||||
auto* cond = Expr("cond");
|
||||
auto* body = Block(Return());
|
||||
auto* i = If(
|
||||
cond, body,
|
||||
ast::ElseStatementList{create<ast::ElseStatement>(else_cond, else_body)});
|
||||
auto* i = If(cond, body, If(else_cond, else_body));
|
||||
WrapInFunction(i);
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
@ -73,9 +71,7 @@ TEST_F(GlslGeneratorImplTest_If, Emit_IfWithElse) {
|
|||
|
||||
auto* cond = Expr("cond");
|
||||
auto* body = Block(Return());
|
||||
auto* i = If(
|
||||
cond, body,
|
||||
ast::ElseStatementList{create<ast::ElseStatement>(nullptr, else_body)});
|
||||
auto* i = If(cond, body, else_body);
|
||||
WrapInFunction(i);
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
@ -103,11 +99,7 @@ TEST_F(GlslGeneratorImplTest_If, Emit_IfWithMultiple) {
|
|||
|
||||
auto* cond = Expr("cond");
|
||||
auto* body = Block(Return());
|
||||
auto* i = If(cond, body,
|
||||
ast::ElseStatementList{
|
||||
create<ast::ElseStatement>(else_cond, else_body),
|
||||
create<ast::ElseStatement>(nullptr, else_body_2),
|
||||
});
|
||||
auto* i = If(cond, body, If(else_cond, else_body, else_body_2));
|
||||
WrapInFunction(i);
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
|
|
@ -2691,36 +2691,20 @@ bool GeneratorImpl::EmitIf(const ast::IfStatement* stmt) {
|
|||
return false;
|
||||
}
|
||||
|
||||
for (auto* e : stmt->else_statements) {
|
||||
if (e->condition) {
|
||||
line() << "} else {";
|
||||
increment_indent();
|
||||
|
||||
{
|
||||
auto out = line();
|
||||
out << "if (";
|
||||
if (!EmitExpression(out, e->condition)) {
|
||||
return false;
|
||||
}
|
||||
out << ") {";
|
||||
if (stmt->else_statement) {
|
||||
line() << "} else {";
|
||||
if (auto* block = stmt->else_statement->As<ast::BlockStatement>()) {
|
||||
if (!EmitStatementsWithIndent(block->statements)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
line() << "} else {";
|
||||
}
|
||||
|
||||
if (!EmitStatementsWithIndent(e->body->statements)) {
|
||||
return false;
|
||||
if (!EmitStatementsWithIndent({stmt->else_statement})) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
line() << "}";
|
||||
|
||||
for (auto* e : stmt->else_statements) {
|
||||
if (e->condition) {
|
||||
decrement_indent();
|
||||
line() << "}";
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -348,10 +348,9 @@ TEST_F(HlslGeneratorImplTest_Binary, If_WithLogical) {
|
|||
auto* expr = If(create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd,
|
||||
Expr("a"), Expr("b")),
|
||||
Block(Return(1)),
|
||||
Else(create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr,
|
||||
Expr("b"), Expr("c")),
|
||||
Block(Return(2))),
|
||||
Else(Block(Return(3))));
|
||||
If(create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr,
|
||||
Expr("b"), Expr("c")),
|
||||
Block(Return(2)), Block(Return(3))));
|
||||
Func("func", {}, ty.i32(), {WrapInStatement(expr)});
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
|
|
@ -46,9 +46,7 @@ TEST_F(HlslGeneratorImplTest_If, Emit_IfWithElseIf) {
|
|||
|
||||
auto* cond = Expr("cond");
|
||||
auto* body = Block(Return());
|
||||
auto* i = If(
|
||||
cond, body,
|
||||
ast::ElseStatementList{create<ast::ElseStatement>(else_cond, else_body)});
|
||||
auto* i = If(cond, body, If(else_cond, else_body));
|
||||
WrapInFunction(i);
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
@ -73,9 +71,7 @@ TEST_F(HlslGeneratorImplTest_If, Emit_IfWithElse) {
|
|||
|
||||
auto* cond = Expr("cond");
|
||||
auto* body = Block(Return());
|
||||
auto* i = If(
|
||||
cond, body,
|
||||
ast::ElseStatementList{create<ast::ElseStatement>(nullptr, else_body)});
|
||||
auto* i = If(cond, body, else_body);
|
||||
WrapInFunction(i);
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
@ -103,11 +99,7 @@ TEST_F(HlslGeneratorImplTest_If, Emit_IfWithMultiple) {
|
|||
|
||||
auto* cond = Expr("cond");
|
||||
auto* body = Block(Return());
|
||||
auto* i = If(cond, body,
|
||||
ast::ElseStatementList{
|
||||
create<ast::ElseStatement>(else_cond, else_body),
|
||||
create<ast::ElseStatement>(nullptr, else_body_2),
|
||||
});
|
||||
auto* i = If(cond, body, If(else_cond, else_body, else_body_2));
|
||||
WrapInFunction(i);
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
|
|
@ -2051,36 +2051,20 @@ bool GeneratorImpl::EmitIf(const ast::IfStatement* stmt) {
|
|||
return false;
|
||||
}
|
||||
|
||||
for (auto* e : stmt->else_statements) {
|
||||
if (e->condition) {
|
||||
line() << "} else {";
|
||||
increment_indent();
|
||||
|
||||
{
|
||||
auto out = line();
|
||||
out << "if (";
|
||||
if (!EmitExpression(out, e->condition)) {
|
||||
return false;
|
||||
}
|
||||
out << ") {";
|
||||
if (stmt->else_statement) {
|
||||
line() << "} else {";
|
||||
if (auto* block = stmt->else_statement->As<ast::BlockStatement>()) {
|
||||
if (!EmitStatementsWithIndent(block->statements)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
line() << "} else {";
|
||||
}
|
||||
|
||||
if (!EmitStatementsWithIndent(e->body->statements)) {
|
||||
return false;
|
||||
if (!EmitStatementsWithIndent({stmt->else_statement})) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
line() << "}";
|
||||
|
||||
for (auto* e : stmt->else_statements) {
|
||||
if (e->condition) {
|
||||
decrement_indent();
|
||||
line() << "}";
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ TEST_F(MslGeneratorImplTest, Emit_If) {
|
|||
TEST_F(MslGeneratorImplTest, Emit_IfWithElseIf) {
|
||||
auto* cond = Var("cond", ty.bool_());
|
||||
auto* else_cond = Var("else_cond", ty.bool_());
|
||||
auto* i = If(cond, Block(Return()), Else(else_cond, Block(Return())));
|
||||
auto* i = If(cond, Block(Return()), If(else_cond, Block(Return())));
|
||||
WrapInFunction(cond, else_cond, i);
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
@ -58,7 +58,7 @@ TEST_F(MslGeneratorImplTest, Emit_IfWithElseIf) {
|
|||
|
||||
TEST_F(MslGeneratorImplTest, Emit_IfWithElse) {
|
||||
auto* cond = Var("cond", ty.bool_());
|
||||
auto* i = If(cond, Block(Return()), Else(nullptr, Block(Return())));
|
||||
auto* i = If(cond, Block(Return()), Block(Return()));
|
||||
WrapInFunction(cond, i);
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
@ -77,8 +77,8 @@ TEST_F(MslGeneratorImplTest, Emit_IfWithElse) {
|
|||
TEST_F(MslGeneratorImplTest, Emit_IfWithMultiple) {
|
||||
auto* cond = Var("cond", ty.bool_());
|
||||
auto* else_cond = Var("else_cond", ty.bool_());
|
||||
auto* i = If(cond, Block(Return()), Else(else_cond, Block(Return())),
|
||||
Else(nullptr, Block(Return())));
|
||||
auto* i = If(cond, Block(Return()),
|
||||
If(else_cond, Block(Return()), Block(Return())));
|
||||
WrapInFunction(cond, else_cond, i);
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
|
|
@ -3365,11 +3365,9 @@ uint32_t Builder::GenerateBitcastExpression(
|
|||
return result_id;
|
||||
}
|
||||
|
||||
bool Builder::GenerateConditionalBlock(
|
||||
const ast::Expression* cond,
|
||||
const ast::BlockStatement* true_body,
|
||||
size_t cur_else_idx,
|
||||
const ast::ElseStatementList& else_stmts) {
|
||||
bool Builder::GenerateConditionalBlock(const ast::Expression* cond,
|
||||
const ast::BlockStatement* true_body,
|
||||
const ast::Statement* else_stmt) {
|
||||
auto cond_id = GenerateExpressionWithLoadIfNeeded(cond);
|
||||
if (cond_id == 0) {
|
||||
return false;
|
||||
|
@ -3389,8 +3387,7 @@ bool Builder::GenerateConditionalBlock(
|
|||
|
||||
// if there are no more else statements we branch on false to the merge
|
||||
// block otherwise we branch to the false block
|
||||
auto false_block_id =
|
||||
cur_else_idx < else_stmts.size() ? next_id() : merge_block_id;
|
||||
auto false_block_id = else_stmt ? next_id() : merge_block_id;
|
||||
|
||||
if (!push_function_inst(spv::Op::OpBranchConditional,
|
||||
{Operand(cond_id), Operand(true_block_id),
|
||||
|
@ -3418,15 +3415,15 @@ bool Builder::GenerateConditionalBlock(
|
|||
return false;
|
||||
}
|
||||
|
||||
auto* else_stmt = else_stmts[cur_else_idx];
|
||||
// Handle the else case by just outputting the statements.
|
||||
if (!else_stmt->condition) {
|
||||
if (!GenerateBlockStatement(else_stmt->body)) {
|
||||
if (auto* block = else_stmt->As<ast::BlockStatement>()) {
|
||||
if (!GenerateBlockStatement(block)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!GenerateConditionalBlock(else_stmt->condition, else_stmt->body,
|
||||
cur_else_idx + 1, else_stmts)) {
|
||||
auto* elseif = else_stmt->As<ast::IfStatement>();
|
||||
if (!GenerateConditionalBlock(elseif->condition, elseif->body,
|
||||
elseif->else_statement)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -3460,7 +3457,7 @@ bool Builder::GenerateIfStatement(const ast::IfStatement* stmt) {
|
|||
return block && (block->statements.size() == 1) &&
|
||||
block->Last()->Is<ast::BreakStatement>();
|
||||
};
|
||||
if (is_just_a_break(stmt->body) && stmt->else_statements.empty()) {
|
||||
if (is_just_a_break(stmt->body) && stmt->else_statement == nullptr) {
|
||||
// It's a break-if.
|
||||
TINT_ASSERT(Writer, !backedge_stack_.empty());
|
||||
const auto cond_id = GenerateExpressionWithLoadIfNeeded(stmt->condition);
|
||||
|
@ -3473,9 +3470,8 @@ bool Builder::GenerateIfStatement(const ast::IfStatement* stmt) {
|
|||
Operand(ci.loop_header_id)});
|
||||
return true;
|
||||
} else if (stmt->body->Empty()) {
|
||||
const auto& es = stmt->else_statements;
|
||||
if (es.size() == 1 && !es.back()->condition &&
|
||||
is_just_a_break(es.back()->body)) {
|
||||
auto* es_block = As<ast::BlockStatement>(stmt->else_statement);
|
||||
if (es_block && is_just_a_break(es_block)) {
|
||||
// It's a break-unless.
|
||||
TINT_ASSERT(Writer, !backedge_stack_.empty());
|
||||
const auto cond_id =
|
||||
|
@ -3492,8 +3488,8 @@ bool Builder::GenerateIfStatement(const ast::IfStatement* stmt) {
|
|||
}
|
||||
}
|
||||
|
||||
if (!GenerateConditionalBlock(stmt->condition, stmt->body, 0,
|
||||
stmt->else_statements)) {
|
||||
if (!GenerateConditionalBlock(stmt->condition, stmt->body,
|
||||
stmt->else_statement)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -438,13 +438,11 @@ class Builder {
|
|||
/// Generates a conditional section merge block
|
||||
/// @param cond the condition
|
||||
/// @param true_body the statements making up the true block
|
||||
/// @param cur_else_idx the index of the current else statement to process
|
||||
/// @param else_stmts the list of all else statements
|
||||
/// @param else_stmt the statement for the else block
|
||||
/// @returns true on success, false on failure
|
||||
bool GenerateConditionalBlock(const ast::Expression* cond,
|
||||
const ast::BlockStatement* true_body,
|
||||
size_t cur_else_idx,
|
||||
const ast::ElseStatementList& else_stmts);
|
||||
const ast::Statement* else_stmt);
|
||||
/// Generates a statement
|
||||
/// @param stmt the statement to generate
|
||||
/// @returns true if the statement was generated
|
||||
|
|
|
@ -106,7 +106,7 @@ TEST_F(BuilderTest, If_WithElse) {
|
|||
auto* body = Block(Assign("v", 2));
|
||||
auto* else_body = Block(Assign("v", 3));
|
||||
|
||||
auto* expr = If(true, body, Else(else_body));
|
||||
auto* expr = If(true, body, else_body);
|
||||
WrapInFunction(expr);
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
@ -148,7 +148,7 @@ TEST_F(BuilderTest, If_WithElseIf) {
|
|||
auto* body = Block(Assign("v", 2));
|
||||
auto* else_body = Block(Assign("v", 3));
|
||||
|
||||
auto* expr = If(true, body, Else(true, else_body));
|
||||
auto* expr = If(true, body, If(true, else_body));
|
||||
WrapInFunction(expr);
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
@ -202,9 +202,9 @@ TEST_F(BuilderTest, If_WithMultiple) {
|
|||
auto* else_body = Block(Assign("v", 5));
|
||||
|
||||
auto* expr = If(true, body, //
|
||||
Else(true, elseif_1_body), //
|
||||
Else(false, elseif_2_body), //
|
||||
Else(else_body));
|
||||
If(true, elseif_1_body, //
|
||||
If(false, elseif_2_body, //
|
||||
else_body)));
|
||||
WrapInFunction(expr);
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
@ -305,7 +305,7 @@ TEST_F(BuilderTest, If_WithElseBreak) {
|
|||
// }
|
||||
auto* else_body = Block(Break());
|
||||
|
||||
auto* if_stmt = If(true, Block(), Else(else_body));
|
||||
auto* if_stmt = If(true, Block(), else_body);
|
||||
|
||||
auto* loop_body = Block(if_stmt);
|
||||
|
||||
|
@ -349,7 +349,7 @@ TEST_F(BuilderTest, If_WithContinueAndBreak) {
|
|||
// }
|
||||
// }
|
||||
|
||||
auto* if_stmt = If(true, Block(Continue()), Else(Block(Break())));
|
||||
auto* if_stmt = If(true, Block(Continue()), Block(Break()));
|
||||
|
||||
auto* expr = Loop(Block(if_stmt), Block());
|
||||
WrapInFunction(expr);
|
||||
|
@ -392,7 +392,7 @@ TEST_F(BuilderTest, If_WithElseContinue) {
|
|||
// }
|
||||
auto* else_body = Block(create<ast::ContinueStatement>());
|
||||
|
||||
auto* if_stmt = If(true, Block(), Else(else_body));
|
||||
auto* if_stmt = If(true, Block(), else_body);
|
||||
|
||||
auto* loop_body = Block(if_stmt, Break());
|
||||
|
||||
|
@ -493,7 +493,7 @@ TEST_F(BuilderTest, IfElse_BothReturn) {
|
|||
{
|
||||
If(true, //
|
||||
Block(Return(true)), //
|
||||
Else(Block(Return(true)))),
|
||||
Block(Return(true))),
|
||||
});
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
@ -589,7 +589,7 @@ TEST_F(BuilderTest, If_ElseIf_WithReturn) {
|
|||
// return;
|
||||
// }
|
||||
|
||||
auto* if_stmt = If(false, Block(), Else(true, Block(Return())));
|
||||
auto* if_stmt = If(false, Block(), If(true, Block(Return())));
|
||||
auto* fn = Func("f", {}, ty.void_(), {if_stmt});
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
@ -627,7 +627,7 @@ TEST_F(BuilderTest, Loop_If_ElseIf_WithBreak) {
|
|||
// }
|
||||
// }
|
||||
|
||||
auto* if_stmt = If(false, Block(), Else(true, Block(Break())));
|
||||
auto* if_stmt = If(false, Block(), If(true, Block(Break())));
|
||||
auto* fn = Func("f", {}, ty.void_(), {Loop(Block(if_stmt))});
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
|
|
@ -236,8 +236,7 @@ TEST_F(BuilderTest, Loop_WithContinuing_BreakIf) {
|
|||
// }
|
||||
// }
|
||||
|
||||
auto* if_stmt = create<ast::IfStatement>(Expr(true), Block(Break()),
|
||||
ast::ElseStatementList{});
|
||||
auto* if_stmt = If(Expr(true), Block(Break()));
|
||||
auto* continuing = Block(if_stmt);
|
||||
auto* loop = Loop(Block(), continuing);
|
||||
WrapInFunction(loop);
|
||||
|
@ -269,9 +268,7 @@ TEST_F(BuilderTest, Loop_WithContinuing_BreakUnless) {
|
|||
// if (true) {} else { break; }
|
||||
// }
|
||||
// }
|
||||
auto* if_stmt = create<ast::IfStatement>(
|
||||
Expr(true), Block(),
|
||||
ast::ElseStatementList{Else(nullptr, Block(Break()))});
|
||||
auto* if_stmt = If(Expr(true), Block(), Block(Break()));
|
||||
auto* continuing = Block(if_stmt);
|
||||
auto* loop = Loop(Block(), continuing);
|
||||
WrapInFunction(loop);
|
||||
|
@ -306,7 +303,7 @@ TEST_F(BuilderTest, Loop_WithContinuing_BreakIf_ConditionIsVar) {
|
|||
// }
|
||||
|
||||
auto* cond_var = Decl(Var("cond", nullptr, Expr(true)));
|
||||
auto* if_stmt = If(Expr("cond"), Block(Break()), ast::ElseStatementList{});
|
||||
auto* if_stmt = If(Expr("cond"), Block(Break()));
|
||||
auto* continuing = Block(cond_var, if_stmt);
|
||||
auto* loop = Loop(Block(), continuing);
|
||||
WrapInFunction(loop);
|
||||
|
@ -344,8 +341,7 @@ TEST_F(BuilderTest, Loop_WithContinuing_BreakUnless_ConditionIsVar) {
|
|||
// }
|
||||
// }
|
||||
auto* cond_var = Decl(Var("cond", nullptr, Expr(true)));
|
||||
auto* if_stmt = If(Expr("cond"), Block(),
|
||||
ast::ElseStatementList{Else(nullptr, Block(Break()))});
|
||||
auto* if_stmt = If(Expr("cond"), Block(), Block(Break()));
|
||||
auto* continuing = Block(cond_var, if_stmt);
|
||||
auto* loop = Loop(Block(), continuing);
|
||||
WrapInFunction(loop);
|
||||
|
@ -388,13 +384,11 @@ TEST_F(BuilderTest, Loop_WithContinuing_BreakIf_Nested) {
|
|||
// }
|
||||
// }
|
||||
|
||||
auto* inner_if_stmt = create<ast::IfStatement>(Expr(true), Block(Break()),
|
||||
ast::ElseStatementList{});
|
||||
auto* inner_if_stmt = If(Expr(true), Block(Break()));
|
||||
auto* inner_continuing = Block(inner_if_stmt);
|
||||
auto* inner_loop = Loop(Block(), inner_continuing);
|
||||
|
||||
auto* outer_if_stmt = create<ast::IfStatement>(Expr(true), Block(Break()),
|
||||
ast::ElseStatementList{});
|
||||
auto* outer_if_stmt = If(Expr(true), Block(Break()));
|
||||
auto* outer_continuing = Block(inner_loop, outer_if_stmt);
|
||||
auto* outer_loop = Loop(Block(), outer_continuing);
|
||||
|
||||
|
@ -443,15 +437,11 @@ TEST_F(BuilderTest, Loop_WithContinuing_BreakUnless_Nested) {
|
|||
// }
|
||||
// }
|
||||
|
||||
auto* inner_if_stmt = create<ast::IfStatement>(
|
||||
Expr(true), Block(),
|
||||
ast::ElseStatementList{Else(nullptr, Block(Break()))});
|
||||
auto* inner_if_stmt = If(Expr(true), Block(), Block(Break()));
|
||||
auto* inner_continuing = Block(inner_if_stmt);
|
||||
auto* inner_loop = Loop(Block(), inner_continuing);
|
||||
|
||||
auto* outer_if_stmt = create<ast::IfStatement>(
|
||||
Expr(true), Block(),
|
||||
ast::ElseStatementList{Else(nullptr, Block(Break()))});
|
||||
auto* outer_if_stmt = If(Expr(true), Block(), Block(Break()));
|
||||
auto* outer_continuing = Block(inner_loop, outer_if_stmt);
|
||||
auto* outer_loop = Loop(Block(), outer_continuing);
|
||||
|
||||
|
|
|
@ -1075,20 +1075,27 @@ bool GeneratorImpl::EmitIf(const ast::IfStatement* stmt) {
|
|||
return false;
|
||||
}
|
||||
|
||||
for (auto* e : stmt->else_statements) {
|
||||
if (e->condition) {
|
||||
auto out = line();
|
||||
out << "} else if (";
|
||||
if (!EmitExpression(out, e->condition)) {
|
||||
const ast::Statement* e = stmt->else_statement;
|
||||
while (e) {
|
||||
if (auto* elseif = e->As<ast::IfStatement>()) {
|
||||
{
|
||||
auto out = line();
|
||||
out << "} else if (";
|
||||
if (!EmitExpression(out, elseif->condition)) {
|
||||
return false;
|
||||
}
|
||||
out << ") {";
|
||||
}
|
||||
if (!EmitStatementsWithIndent(elseif->body->statements)) {
|
||||
return false;
|
||||
}
|
||||
out << ") {";
|
||||
e = elseif->else_statement;
|
||||
} else {
|
||||
line() << "} else {";
|
||||
}
|
||||
|
||||
if (!EmitStatementsWithIndent(e->body->statements)) {
|
||||
return false;
|
||||
if (!EmitStatementsWithIndent(e->As<ast::BlockStatement>()->statements)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,9 +47,7 @@ TEST_F(WgslGeneratorImplTest, Emit_IfWithElseIf) {
|
|||
|
||||
auto* cond = Expr("cond");
|
||||
auto* body = Block(Return());
|
||||
auto* i = If(
|
||||
cond, body,
|
||||
ast::ElseStatementList{create<ast::ElseStatement>(else_cond, else_body)});
|
||||
auto* i = If(cond, body, If(else_cond, else_body));
|
||||
WrapInFunction(i);
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
@ -72,9 +70,7 @@ TEST_F(WgslGeneratorImplTest, Emit_IfWithElse) {
|
|||
|
||||
auto* cond = Expr("cond");
|
||||
auto* body = Block(Return());
|
||||
auto* i = If(
|
||||
cond, body,
|
||||
ast::ElseStatementList{create<ast::ElseStatement>(nullptr, else_body)});
|
||||
auto* i = If(cond, body, else_body);
|
||||
WrapInFunction(i);
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
@ -102,11 +98,7 @@ TEST_F(WgslGeneratorImplTest, Emit_IfWithMultiple) {
|
|||
|
||||
auto* cond = Expr("cond");
|
||||
auto* body = Block(Return());
|
||||
auto* i = If(cond, body,
|
||||
ast::ElseStatementList{
|
||||
create<ast::ElseStatement>(else_cond, else_body),
|
||||
create<ast::ElseStatement>(nullptr, else_body_2),
|
||||
});
|
||||
auto* i = If(cond, body, If(else_cond, else_body, else_body_2));
|
||||
WrapInFunction(i);
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
|
|
@ -163,7 +163,6 @@ tint_unittests_source_set("tint_unittests_ast_src") {
|
|||
"../../src/tint/ast/depth_multisampled_texture_test.cc",
|
||||
"../../src/tint/ast/depth_texture_test.cc",
|
||||
"../../src/tint/ast/discard_statement_test.cc",
|
||||
"../../src/tint/ast/else_statement_test.cc",
|
||||
"../../src/tint/ast/enable_test.cc",
|
||||
"../../src/tint/ast/external_texture_test.cc",
|
||||
"../../src/tint/ast/f32_test.cc",
|
||||
|
@ -486,7 +485,6 @@ tint_unittests_source_set("tint_unittests_wgsl_reader_src") {
|
|||
"../../src/tint/reader/wgsl/parser_impl_continue_stmt_test.cc",
|
||||
"../../src/tint/reader/wgsl/parser_impl_continuing_stmt_test.cc",
|
||||
"../../src/tint/reader/wgsl/parser_impl_depth_texture_test.cc",
|
||||
"../../src/tint/reader/wgsl/parser_impl_elseif_stmt_test.cc",
|
||||
"../../src/tint/reader/wgsl/parser_impl_enable_directive_test.cc",
|
||||
"../../src/tint/reader/wgsl/parser_impl_equality_expression_test.cc",
|
||||
"../../src/tint/reader/wgsl/parser_impl_error_msg_test.cc",
|
||||
|
|
Loading…
Reference in New Issue