ast: Add ForLoopStatement node
Currently entirely unused.
Bug: tint:952
Change-Id: I3ba2823024b81e927d8fe8a80fac4f82c02eac3e
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/56761
Kokoro: Kokoro <noreply+kokoro@google.com>
Auto-Submit: Ben Clayton <bclayton@google.com>
Reviewed-by: David Neto <dneto@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/BUILD.gn b/src/BUILD.gn
index 5b1a3d5..784edba 100644
--- a/src/BUILD.gn
+++ b/src/BUILD.gn
@@ -354,6 +354,8 @@
"ast/fallthrough_statement.h",
"ast/float_literal.cc",
"ast/float_literal.h",
+ "ast/for_loop_statement.cc",
+ "ast/for_loop_statement.h",
"ast/function.cc",
"ast/function.h",
"ast/group_decoration.cc",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 914e198..51dc420 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -98,6 +98,8 @@
ast/fallthrough_statement.h
ast/float_literal.cc
ast/float_literal.h
+ ast/for_loop_statement.cc
+ ast/for_loop_statement.h
ast/function.cc
ast/function.h
ast/group_decoration.cc
@@ -550,6 +552,7 @@
ast/f32_test.cc
ast/fallthrough_statement_test.cc
ast/float_literal_test.cc
+ ast/for_loop_statement_test.cc
ast/function_test.cc
ast/group_decoration_test.cc
ast/i32_test.cc
diff --git a/src/ast/for_loop_statement.cc b/src/ast/for_loop_statement.cc
new file mode 100644
index 0000000..dc77b80
--- /dev/null
+++ b/src/ast/for_loop_statement.cc
@@ -0,0 +1,92 @@
+// Copyright 2021 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/ast/for_loop_statement.h"
+
+#include "src/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::ForLoopStatement);
+
+namespace tint {
+namespace ast {
+
+ForLoopStatement::ForLoopStatement(ProgramID program_id,
+ const Source& source,
+ Statement* initializer,
+ Expression* condition,
+ Statement* continuing,
+ BlockStatement* body)
+ : Base(program_id, source),
+ initializer_(initializer),
+ condition_(condition),
+ continuing_(continuing),
+ body_(body) {
+ TINT_ASSERT(AST, body_);
+
+ TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, initializer_, program_id);
+ TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, condition_, program_id);
+ TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, continuing_, program_id);
+ TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, body_, program_id);
+}
+
+ForLoopStatement::ForLoopStatement(ForLoopStatement&&) = default;
+
+ForLoopStatement::~ForLoopStatement() = default;
+
+ForLoopStatement* ForLoopStatement::Clone(CloneContext* ctx) const {
+ // Clone arguments outside of create() call to have deterministic ordering
+ auto src = ctx->Clone(source());
+
+ auto* init = ctx->Clone(initializer_);
+ auto* cond = ctx->Clone(condition_);
+ auto* cont = ctx->Clone(continuing_);
+ auto* b = ctx->Clone(body_);
+ return ctx->dst->create<ForLoopStatement>(src, init, cond, cont, b);
+}
+
+void ForLoopStatement::to_str(const sem::Info& sem,
+ std::ostream& out,
+ size_t indent) const {
+ make_indent(out, indent);
+ out << "ForLoop {" << std::endl;
+
+ if (initializer_) {
+ make_indent(out, indent + 2);
+ out << "initializer:" << std::endl;
+ initializer_->to_str(sem, out, indent + 4);
+ }
+
+ if (condition_) {
+ make_indent(out, indent + 2);
+ out << "condition:" << std::endl;
+ condition_->to_str(sem, out, indent + 4);
+ }
+
+ if (continuing_) {
+ make_indent(out, indent + 2);
+ out << "continuing:" << std::endl;
+ continuing_->to_str(sem, out, indent + 4);
+ }
+
+ make_indent(out, indent + 2);
+ out << "body:" << std::endl;
+ for (auto* stmt : *body_) {
+ stmt->to_str(sem, out, indent + 4);
+ }
+
+ out << "}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/for_loop_statement.h b/src/ast/for_loop_statement.h
new file mode 100644
index 0000000..124a17f
--- /dev/null
+++ b/src/ast/for_loop_statement.h
@@ -0,0 +1,91 @@
+// Copyright 2021 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_AST_FOR_LOOP_STATEMENT_H_
+#define SRC_AST_FOR_LOOP_STATEMENT_H_
+
+#include "src/ast/block_statement.h"
+
+namespace tint {
+namespace ast {
+
+class Expression;
+
+/// A for loop statement
+class ForLoopStatement : public Castable<ForLoopStatement, Statement> {
+ public:
+ /// Constructor
+ /// @param program_id the identifier of the program that owns this node
+ /// @param source the for loop statement source
+ /// @param initializer the optional loop initializer statement
+ /// @param condition the optional loop condition expression
+ /// @param continuing the optional continuing statement
+ /// @param body the loop body
+ ForLoopStatement(ProgramID program_id,
+ const Source& source,
+ Statement* initializer,
+ Expression* condition,
+ Statement* continuing,
+ BlockStatement* body);
+ /// Move constructor
+ ForLoopStatement(ForLoopStatement&&);
+ ~ForLoopStatement() override;
+
+ /// @returns the initializer statement
+ const Statement* initializer() const { return initializer_; }
+ /// @returns the initializer statement
+ Statement* initializer() { return initializer_; }
+
+ /// @returns the condition expression
+ const Expression* condition() const { return condition_; }
+ /// @returns the condition expression
+ Expression* condition() { return condition_; }
+
+ /// @returns the continuing statement
+ const Statement* continuing() const { return continuing_; }
+ /// @returns the continuing statement
+ Statement* continuing() { return continuing_; }
+
+ /// @returns the loop body block
+ const BlockStatement* body() const { return body_; }
+ /// @returns the loop body block
+ BlockStatement* body() { return body_; }
+
+ /// Clones this node and all transitive child nodes using the `CloneContext`
+ /// `ctx`.
+ /// @param ctx the clone context
+ /// @return the newly cloned node
+ ForLoopStatement* Clone(CloneContext* ctx) const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param sem the semantic info for the program
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(const sem::Info& sem,
+ std::ostream& out,
+ size_t indent) const override;
+
+ private:
+ ForLoopStatement(const ForLoopStatement&) = delete;
+
+ Statement* const initializer_;
+ Expression* const condition_;
+ Statement* const continuing_;
+ BlockStatement* const body_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_FOR_LOOP_STATEMENT_H_
diff --git a/src/ast/for_loop_statement_test.cc b/src/ast/for_loop_statement_test.cc
new file mode 100644
index 0000000..1542cd0
--- /dev/null
+++ b/src/ast/for_loop_statement_test.cc
@@ -0,0 +1,175 @@
+// Copyright 2021 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/ast/binary_expression.h"
+#include "src/ast/test_helper.h"
+
+namespace tint {
+namespace ast {
+namespace {
+
+using ForLoopStatementTest = TestHelper;
+
+TEST_F(ForLoopStatementTest, Creation) {
+ auto* init = Decl(Var("i", ty.u32()));
+ auto* cond =
+ create<BinaryExpression>(BinaryOp::kLessThan, Expr("i"), Expr(5u));
+ auto* cont = Assign("i", Add("i", 1));
+ auto* body = Block(Return());
+ auto* l = For(init, cond, cont, body);
+
+ EXPECT_EQ(l->initializer(), init);
+ EXPECT_EQ(l->condition(), cond);
+ EXPECT_EQ(l->continuing(), cont);
+ EXPECT_EQ(l->body(), body);
+}
+
+TEST_F(ForLoopStatementTest, Creation_WithSource) {
+ auto* body = Block(Return());
+ auto* l = For(Source{{20u, 2u}}, nullptr, nullptr, nullptr, body);
+ auto src = l->source();
+ EXPECT_EQ(src.range.begin.line, 20u);
+ EXPECT_EQ(src.range.begin.column, 2u);
+}
+
+TEST_F(ForLoopStatementTest, Creation_Null_InitCondCont) {
+ auto* body = Block(Return());
+ auto* l = For(nullptr, nullptr, nullptr, body);
+ EXPECT_EQ(l->body(), body);
+}
+
+TEST_F(ForLoopStatementTest, Assert_Null_Body) {
+ EXPECT_FATAL_FAILURE(
+ {
+ ProgramBuilder b;
+ b.For(nullptr, nullptr, nullptr, nullptr);
+ },
+ "internal compiler error");
+}
+
+TEST_F(ForLoopStatementTest, Assert_DifferentProgramID_Initializer) {
+ EXPECT_FATAL_FAILURE(
+ {
+ ProgramBuilder b1;
+ ProgramBuilder b2;
+ b1.For(b2.Block(), nullptr, nullptr, b1.Block());
+ },
+ "internal compiler error");
+}
+
+TEST_F(ForLoopStatementTest, Assert_DifferentProgramID_Condition) {
+ EXPECT_FATAL_FAILURE(
+ {
+ ProgramBuilder b1;
+ ProgramBuilder b2;
+ b1.For(nullptr, b2.Expr(true), nullptr, b1.Block());
+ },
+ "internal compiler error");
+}
+
+TEST_F(ForLoopStatementTest, Assert_DifferentProgramID_Continuing) {
+ EXPECT_FATAL_FAILURE(
+ {
+ ProgramBuilder b1;
+ ProgramBuilder b2;
+ b1.For(nullptr, nullptr, b2.Block(), b1.Block());
+ },
+ "internal compiler error");
+}
+
+TEST_F(ForLoopStatementTest, Assert_DifferentProgramID_Body) {
+ EXPECT_FATAL_FAILURE(
+ {
+ ProgramBuilder b1;
+ ProgramBuilder b2;
+ b1.For(nullptr, nullptr, nullptr, b2.Block());
+ },
+ "internal compiler error");
+}
+
+TEST_F(ForLoopStatementTest, ToStr) {
+ auto* body = Block(Return());
+ auto* l = For(nullptr, nullptr, nullptr, body);
+
+ EXPECT_EQ(str(l), R"(ForLoop {
+ body:
+ Return{}
+}
+)");
+}
+
+TEST_F(ForLoopStatementTest, ToStr_With_Init) {
+ auto* body = Block(Return());
+ auto* l = For(Block(), nullptr, nullptr, body);
+
+ EXPECT_EQ(str(l), R"(ForLoop {
+ initializer:
+ Block{
+ }
+ body:
+ Return{}
+}
+)");
+}
+
+TEST_F(ForLoopStatementTest, ToStr_With_Cond) {
+ auto* body = Block(Return());
+ auto* l = For(nullptr, Expr(true), nullptr, body);
+
+ EXPECT_EQ(str(l), R"(ForLoop {
+ condition:
+ ScalarConstructor[not set]{true}
+ body:
+ Return{}
+}
+)");
+}
+
+TEST_F(ForLoopStatementTest, ToStr_With_Cont) {
+ auto* body = Block(Return());
+ auto* l = For(nullptr, nullptr, Block(), body);
+
+ EXPECT_EQ(str(l), R"(ForLoop {
+ continuing:
+ Block{
+ }
+ body:
+ Return{}
+}
+)");
+}
+
+TEST_F(ForLoopStatementTest, ToStr_With_All) {
+ auto* body = Block(Return());
+ auto* l = For(Block(), Expr(true), Block(), body);
+
+ EXPECT_EQ(str(l), R"(ForLoop {
+ initializer:
+ Block{
+ }
+ condition:
+ ScalarConstructor[not set]{true}
+ continuing:
+ Block{
+ }
+ body:
+ Return{}
+}
+)");
+}
+
+} // namespace
+} // namespace ast
+} // namespace tint
diff --git a/src/program_builder.h b/src/program_builder.h
index b5bd076..dcf311c 100644
--- a/src/program_builder.h
+++ b/src/program_builder.h
@@ -35,6 +35,7 @@
#include "src/ast/external_texture.h"
#include "src/ast/f32.h"
#include "src/ast/float_literal.h"
+#include "src/ast/for_loop_statement.h"
#include "src/ast/i32.h"
#include "src/ast/if_statement.h"
#include "src/ast/interpolate_decoration.h"
@@ -1920,6 +1921,40 @@
return create<ast::LoopStatement>(body, continuing);
}
+ /// Creates a ast::ForLoopStatement with input body and optional initializer,
+ /// condition and continuing.
+ /// @param source the source information
+ /// @param init the optional loop initializer
+ /// @param cond the optional loop condition
+ /// @param cont the optional loop continuing
+ /// @param body the loop body
+ /// @returns the for loop statement pointer
+ template <typename COND>
+ ast::ForLoopStatement* For(const Source& source,
+ ast::Statement* init,
+ COND&& cond,
+ ast::Statement* cont,
+ ast::BlockStatement* body) {
+ return create<ast::ForLoopStatement>(
+ source, init, Expr(std::forward<COND>(cond)), cont, body);
+ }
+
+ /// Creates a ast::ForLoopStatement with input body and optional initializer,
+ /// condition and continuing.
+ /// @param init the optional loop initializer
+ /// @param cond the optional loop condition
+ /// @param cont the optional loop continuing
+ /// @param body the loop body
+ /// @returns the for loop statement pointer
+ template <typename COND>
+ ast::ForLoopStatement* For(ast::Statement* init,
+ COND&& cond,
+ ast::Statement* cont,
+ ast::BlockStatement* body) {
+ return create<ast::ForLoopStatement>(init, Expr(std::forward<COND>(cond)),
+ cont, body);
+ }
+
/// Creates a ast::VariableDeclStatement for the input variable
/// @param source the source information
/// @param var the variable to wrap in a decl statement
diff --git a/test/BUILD.gn b/test/BUILD.gn
index dd4bcfc..14ce32f 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -169,6 +169,7 @@
"../src/ast/f32_test.cc",
"../src/ast/fallthrough_statement_test.cc",
"../src/ast/float_literal_test.cc",
+ "../src/ast/for_loop_statement_test.cc",
"../src/ast/function_test.cc",
"../src/ast/group_decoration_test.cc",
"../src/ast/i32_test.cc",