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>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index f2a1ce3..691b435 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -222,8 +222,6 @@
"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",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 35b5f69..ac9723f 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -110,8 +110,6 @@
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 @@
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 @@
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
diff --git a/src/tint/ast/else_statement.cc b/src/tint/ast/else_statement.cc
deleted file mode 100644
index d047177..0000000
--- a/src/tint/ast/else_statement.cc
+++ /dev/null
@@ -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
diff --git a/src/tint/ast/else_statement.h b/src/tint/ast/else_statement.h
deleted file mode 100644
index c8c6e51..0000000
--- a/src/tint/ast/else_statement.h
+++ /dev/null
@@ -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_
diff --git a/src/tint/ast/else_statement_test.cc b/src/tint/ast/else_statement_test.cc
deleted file mode 100644
index a088438..0000000
--- a/src/tint/ast/else_statement_test.cc
+++ /dev/null
@@ -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
diff --git a/src/tint/ast/if_statement.cc b/src/tint/ast/if_statement.cc
index 4b78da3..0c693d4 100644
--- a/src/tint/ast/if_statement.cc
+++ b/src/tint/ast/if_statement.cc
@@ -24,18 +24,17 @@
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 @@
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);
}
diff --git a/src/tint/ast/if_statement.h b/src/tint/ast/if_statement.h
index df843f8..4d98dba 100644
--- a/src/tint/ast/if_statement.h
+++ b/src/tint/ast/if_statement.h
@@ -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 @@
/// @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 @@
/// 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
diff --git a/src/tint/ast/if_statement_test.cc b/src/tint/ast/if_statement_test.cc
index 83f998a..abd11a1 100644
--- a/src/tint/ast/if_statement_test.cc
+++ b/src/tint/ast/if_statement_test.cc
@@ -25,16 +25,15 @@
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 @@
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
- b.create<IfStatement>(nullptr, b.Block(), ElseStatementList{});
+ b.If(nullptr, b.Block());
},
"internal compiler error");
}
@@ -51,17 +50,16 @@
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 @@
{
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 @@
{
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 @@
{
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");
}
diff --git a/src/tint/ast/statement.cc b/src/tint/ast/statement.cc
index 0d8750d..f003d1d 100644
--- a/src/tint/ast/statement.cc
+++ b/src/tint/ast/statement.cc
@@ -58,9 +58,6 @@
if (Is<DiscardStatement>()) {
return "discard statement";
}
- if (Is<ElseStatement>()) {
- return "else statement";
- }
if (Is<FallthroughStatement>()) {
return "fallthrough statement";
}
diff --git a/src/tint/program_builder.h b/src/tint/program_builder.h
index f64bade..249a55c 100644
--- a/src/tint/program_builder.h
+++ b/src/tint/program_builder.h
@@ -2190,56 +2190,34 @@
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
diff --git a/src/tint/reader/spirv/function.cc b/src/tint/reader/spirv/function.cc
index e148050..d3e4e5e 100644
--- a/src/tint/reader/spirv/function.cc
+++ b/src/tint/reader/spirv/function.cc
@@ -681,15 +681,15 @@
/// @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 @@
// 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 @@
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;
}
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index d1a0dc2..2a93197 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -1729,58 +1729,33 @@
}
// 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 @@
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
diff --git a/src/tint/reader/wgsl/parser_impl.h b/src/tint/reader/wgsl/parser_impl.h
index 2f5b5e2..9290300 100644
--- a/src/tint/reader/wgsl/parser_impl.h
+++ b/src/tint/reader/wgsl/parser_impl.h
@@ -513,9 +513,6 @@
/// 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();
diff --git a/src/tint/reader/wgsl/parser_impl_elseif_stmt_test.cc b/src/tint/reader/wgsl/parser_impl_elseif_stmt_test.cc
deleted file mode 100644
index f0fb963..0000000
--- a/src/tint/reader/wgsl/parser_impl_elseif_stmt_test.cc
+++ /dev/null
@@ -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
diff --git a/src/tint/reader/wgsl/parser_impl_if_stmt_test.cc b/src/tint/reader/wgsl/parser_impl_if_stmt_test.cc
index 18c30e9..1b81e0b 100644
--- a/src/tint/reader/wgsl/parser_impl_if_stmt_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_if_stmt_test.cc
@@ -29,7 +29,7 @@
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 @@
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 @@
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) {
diff --git a/src/tint/resolver/compound_statement_test.cc b/src/tint/resolver/compound_statement_test.cc
index 29c0384..f15bf28 100644
--- a/src/tint/resolver/compound_statement_test.cc
+++ b/src/tint/resolver/compound_statement_test.cc
@@ -232,8 +232,8 @@
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 @@
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 @@
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 @@
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>());
}
diff --git a/src/tint/resolver/dependency_graph.cc b/src/tint/resolver/dependency_graph.cc
index 37496fd..3d69472 100644
--- a/src/tint/resolver/dependency_graph.cc
+++ b/src/tint/resolver/dependency_graph.cc
@@ -248,9 +248,8 @@
[&](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) { //
diff --git a/src/tint/resolver/dependency_graph_test.cc b/src/tint/resolver/dependency_graph_test.cc
index 64ae95d..639991a 100644
--- a/src/tint/resolver/dependency_graph_test.cc
+++ b/src/tint/resolver/dependency_graph_test.cc
@@ -1259,8 +1259,8 @@
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), //
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 483b9a9..d01fc2c 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -892,13 +892,6 @@
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->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::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_,
diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h
index 22e5d5e..740ea31 100644
--- a/src/tint/resolver/resolver.h
+++ b/src/tint/resolver/resolver.h
@@ -59,7 +59,6 @@
class BlockStatement;
class Builtin;
class CaseStatement;
-class ElseStatement;
class ForLoopStatement;
class IfStatement;
class LoopStatement;
@@ -222,7 +221,6 @@
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*);
diff --git a/src/tint/resolver/resolver_behavior_test.cc b/src/tint/resolver/resolver_behavior_test.cc
index fe855bf..7fcf348 100644
--- a/src/tint/resolver/resolver_behavior_test.cc
+++ b/src/tint/resolver/resolver_behavior_test.cc
@@ -348,7 +348,7 @@
}
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_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, 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();
diff --git a/src/tint/resolver/resolver_test.cc b/src/tint/resolver/resolver_test.cc
index ef4d927..c019e04 100644
--- a/src/tint/resolver/resolver_test.cc
+++ b/src/tint/resolver/resolver_test.cc
@@ -160,7 +160,7 @@
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 @@
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();
diff --git a/src/tint/resolver/type_validation_test.cc b/src/tint/resolver/type_validation_test.cc
index 45b8793..1d1ac38 100644
--- a/src/tint/resolver/type_validation_test.cc
+++ b/src/tint/resolver/type_validation_test.cc
@@ -149,9 +149,7 @@
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);
diff --git a/src/tint/resolver/validation_test.cc b/src/tint/resolver/validation_test.cc
index 15f9418..045adf5 100644
--- a/src/tint/resolver/validation_test.cc
+++ b/src/tint/resolver/validation_test.cc
@@ -124,16 +124,16 @@
"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 @@
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 @@
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_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_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_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_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 @@
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));
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index 22ecb97..87a374f 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -1383,10 +1383,6 @@
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 @@
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 @@
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());
diff --git a/src/tint/resolver/validator.h b/src/tint/resolver/validator.h
index 146f7ba..77e5172 100644
--- a/src/tint/resolver/validator.h
+++ b/src/tint/resolver/validator.h
@@ -51,7 +51,6 @@
class BlockStatement;
class Builtin;
class CaseStatement;
-class ElseStatement;
class ForLoopStatement;
class IfStatement;
class LoopStatement;
@@ -194,11 +193,6 @@
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
diff --git a/src/tint/resolver/var_let_validation_test.cc b/src/tint/resolver/var_let_validation_test.cc
index fca81cf..be41da8 100644
--- a/src/tint/resolver/var_let_validation_test.cc
+++ b/src/tint/resolver/var_let_validation_test.cc
@@ -205,9 +205,7 @@
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);
diff --git a/src/tint/sem/if_statement.cc b/src/tint/sem/if_statement.cc
index 5102b89..a600d21 100644
--- a/src/tint/sem/if_statement.cc
+++ b/src/tint/sem/if_statement.cc
@@ -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 @@
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
diff --git a/src/tint/sem/if_statement.h b/src/tint/sem/if_statement.h
index 8e4fb27..112bcd1 100644
--- a/src/tint/sem/if_statement.h
+++ b/src/tint/sem/if_statement.h
@@ -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 @@
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_
diff --git a/src/tint/sem/type_mappings.h b/src/tint/sem/type_mappings.h
index 1bbc46f..4aea326 100644
--- a/src/tint/sem/type_mappings.h
+++ b/src/tint/sem/type_mappings.h
@@ -22,7 +22,6 @@
class Array;
class CallExpression;
class Expression;
-class ElseStatement;
class ForLoopStatement;
class Function;
class IfStatement;
@@ -39,7 +38,6 @@
class Array;
class Call;
class Expression;
-class ElseStatement;
class ForLoopStatement;
class Function;
class IfStatement;
@@ -63,7 +61,6 @@
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*);
diff --git a/src/tint/transform/loop_to_for_loop.cc b/src/tint/transform/loop_to_for_loop.cc
index d01ac48..f5bc05d 100644
--- a/src/tint/transform/loop_to_for_loop.cc
+++ b/src/tint/transform/loop_to_for_loop.cc
@@ -83,14 +83,14 @@
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;
diff --git a/src/tint/transform/loop_to_for_loop_test.cc b/src/tint/transform/loop_to_for_loop_test.cc
index c34b0e8..d4a7693 100644
--- a/src/tint/transform/loop_to_for_loop_test.cc
+++ b/src/tint/transform/loop_to_for_loop_test.cc
@@ -302,5 +302,57 @@
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
diff --git a/src/tint/transform/multiplanar_external_texture.cc b/src/tint/transform/multiplanar_external_texture.cc
index 37d1fa0..a5a2134 100644
--- a/src/tint/transform/multiplanar_external_texture.cc
+++ b/src/tint/transform/multiplanar_external_texture.cc
@@ -288,7 +288,7 @@
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 @@
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))};
}
diff --git a/src/tint/transform/promote_side_effects_to_decl.cc b/src/tint/transform/promote_side_effects_to_decl.cc
index 8eb62eb..146d9a7 100644
--- a/src/tint/transform/promote_side_effects_to_decl.cc
+++ b/src/tint/transform/promote_side_effects_to_decl.cc
@@ -393,9 +393,6 @@
[&](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 @@
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;
diff --git a/src/tint/transform/unwind_discard_functions.cc b/src/tint/transform/unwind_discard_functions.cc
index b15f3a0..81b1d6d 100644
--- a/src/tint/transform/unwind_discard_functions.cc
+++ b/src/tint/transform/unwind_discard_functions.cc
@@ -43,15 +43,6 @@
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 @@
}
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 @@
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)) {
diff --git a/src/tint/transform/utils/hoist_to_decl_before.cc b/src/tint/transform/utils/hoist_to_decl_before.cc
index 15d0926..78ba992 100644
--- a/src/tint/transform/utils/hoist_to_decl_before.cc
+++ b/src/tint/transform/utils/hoist_to_decl_before.cc
@@ -39,25 +39,17 @@
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 @@
}
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 @@
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);
}
diff --git a/src/tint/transform/utils/hoist_to_decl_before_test.cc b/src/tint/transform/utils/hoist_to_decl_before_test.cc
index 589eb9a..e313d91 100644
--- a/src/tint/transform/utils/hoist_to_decl_before_test.cc
+++ b/src/tint/transform/utils/hoist_to_decl_before_test.cc
@@ -187,8 +187,8 @@
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 @@
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 @@
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));
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index f21f96d..b60b6ff 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -1842,36 +1842,20 @@
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;
}
diff --git a/src/tint/writer/glsl/generator_impl_binary_test.cc b/src/tint/writer/glsl/generator_impl_binary_test.cc
index 744eef4..c6b4ca7 100644
--- a/src/tint/writer/glsl/generator_impl_binary_test.cc
+++ b/src/tint/writer/glsl/generator_impl_binary_test.cc
@@ -360,10 +360,9 @@
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();
diff --git a/src/tint/writer/glsl/generator_impl_if_test.cc b/src/tint/writer/glsl/generator_impl_if_test.cc
index 6975943..9efa861 100644
--- a/src/tint/writer/glsl/generator_impl_if_test.cc
+++ b/src/tint/writer/glsl/generator_impl_if_test.cc
@@ -46,9 +46,7 @@
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 @@
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 @@
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();
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index c34a1c6..c257989 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -2691,36 +2691,20 @@
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;
}
diff --git a/src/tint/writer/hlsl/generator_impl_binary_test.cc b/src/tint/writer/hlsl/generator_impl_binary_test.cc
index 8fc89eb..d2f075b 100644
--- a/src/tint/writer/hlsl/generator_impl_binary_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_binary_test.cc
@@ -348,10 +348,9 @@
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();
diff --git a/src/tint/writer/hlsl/generator_impl_if_test.cc b/src/tint/writer/hlsl/generator_impl_if_test.cc
index f2e092c..0812282 100644
--- a/src/tint/writer/hlsl/generator_impl_if_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_if_test.cc
@@ -46,9 +46,7 @@
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 @@
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 @@
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();
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index dbe1114..de38102 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -2051,36 +2051,20 @@
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;
}
diff --git a/src/tint/writer/msl/generator_impl_if_test.cc b/src/tint/writer/msl/generator_impl_if_test.cc
index ff0cabc..4979838 100644
--- a/src/tint/writer/msl/generator_impl_if_test.cc
+++ b/src/tint/writer/msl/generator_impl_if_test.cc
@@ -38,7 +38,7 @@
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_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_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();
diff --git a/src/tint/writer/spirv/builder.cc b/src/tint/writer/spirv/builder.cc
index aab35fd..672d10e 100644
--- a/src/tint/writer/spirv/builder.cc
+++ b/src/tint/writer/spirv/builder.cc
@@ -3365,11 +3365,9 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
}
}
- if (!GenerateConditionalBlock(stmt->condition, stmt->body, 0,
- stmt->else_statements)) {
+ if (!GenerateConditionalBlock(stmt->condition, stmt->body,
+ stmt->else_statement)) {
return false;
}
return true;
diff --git a/src/tint/writer/spirv/builder.h b/src/tint/writer/spirv/builder.h
index 7ef316f..8bdbcb9 100644
--- a/src/tint/writer/spirv/builder.h
+++ b/src/tint/writer/spirv/builder.h
@@ -438,13 +438,11 @@
/// 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
diff --git a/src/tint/writer/spirv/builder_if_test.cc b/src/tint/writer/spirv/builder_if_test.cc
index 661bbd8..43453c8 100644
--- a/src/tint/writer/spirv/builder_if_test.cc
+++ b/src/tint/writer/spirv/builder_if_test.cc
@@ -106,7 +106,7 @@
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 @@
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 @@
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 @@
// }
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 @@
// }
// }
- 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 @@
// }
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 @@
{
If(true, //
Block(Return(true)), //
- Else(Block(Return(true)))),
+ Block(Return(true))),
});
spirv::Builder& b = Build();
@@ -589,7 +589,7 @@
// 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 @@
// }
// }
- 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();
diff --git a/src/tint/writer/spirv/builder_loop_test.cc b/src/tint/writer/spirv/builder_loop_test.cc
index 555c167..34b0182 100644
--- a/src/tint/writer/spirv/builder_loop_test.cc
+++ b/src/tint/writer/spirv/builder_loop_test.cc
@@ -236,8 +236,7 @@
// }
// }
- 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 @@
// 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 @@
// }
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 @@
// }
// }
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 @@
// }
// }
- 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 @@
// }
// }
- 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);
diff --git a/src/tint/writer/wgsl/generator_impl.cc b/src/tint/writer/wgsl/generator_impl.cc
index a733765..b836800 100644
--- a/src/tint/writer/wgsl/generator_impl.cc
+++ b/src/tint/writer/wgsl/generator_impl.cc
@@ -1075,20 +1075,27 @@
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;
}
}
diff --git a/src/tint/writer/wgsl/generator_impl_if_test.cc b/src/tint/writer/wgsl/generator_impl_if_test.cc
index 4818019..950da1a 100644
--- a/src/tint/writer/wgsl/generator_impl_if_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_if_test.cc
@@ -47,9 +47,7 @@
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 @@
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 @@
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();