From 65cd25951af95b03be139946e0c2408acb300347 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Fri, 2 Jul 2021 19:27:42 +0000 Subject: [PATCH] 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 Auto-Submit: Ben Clayton Reviewed-by: David Neto Commit-Queue: Ben Clayton --- src/BUILD.gn | 2 + src/CMakeLists.txt | 3 + src/ast/for_loop_statement.cc | 92 +++++++++++++++ src/ast/for_loop_statement.h | 91 +++++++++++++++ src/ast/for_loop_statement_test.cc | 175 +++++++++++++++++++++++++++++ src/program_builder.h | 35 ++++++ test/BUILD.gn | 1 + 7 files changed, 399 insertions(+) create mode 100644 src/ast/for_loop_statement.cc create mode 100644 src/ast/for_loop_statement.h create mode 100644 src/ast/for_loop_statement_test.cc diff --git a/src/BUILD.gn b/src/BUILD.gn index 5b1a3d5d15..784edba108 100644 --- a/src/BUILD.gn +++ b/src/BUILD.gn @@ -354,6 +354,8 @@ libtint_source_set("libtint_core_all_src") { "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 914e1984ef..51dc4208e6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -98,6 +98,8 @@ set(TINT_LIB_SRCS 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 @@ if(${TINT_BUILD_TESTS}) 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 0000000000..dc77b80720 --- /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(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 0000000000..124a17f868 --- /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 { + 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 0000000000..1542cd035a --- /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(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 b5bd0764aa..dcf311c910 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 @@ class ProgramBuilder { return create(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 + ast::ForLoopStatement* For(const Source& source, + ast::Statement* init, + COND&& cond, + ast::Statement* cont, + ast::BlockStatement* body) { + return create( + source, init, Expr(std::forward(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 + ast::ForLoopStatement* For(ast::Statement* init, + COND&& cond, + ast::Statement* cont, + ast::BlockStatement* body) { + return create(init, Expr(std::forward(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 dd4bcfc6d7..14ce32f2cc 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -169,6 +169,7 @@ tint_unittests_source_set("tint_unittests_core_src") { "../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",