ast: Add Module.Clone()

Deep-clones all `Node`s and `Type`s into a new module.

Instead of writing a million standalone tests that'll only ever test the
existing fields of each type, I've opted to write the tests using
wgsl<->ast<->wgsl conversion. This means the tests require the enabling
of TINT_BUILD_WGSL_READER and TINT_BUILD_WGSL_WRITER, but I believe this
is much easier to maintain.

I'm aware there are probably gaps in the tests, and that even full
coverage is likely to rapidly rot, so I've also added
fuzzers/tint_ast_clone_fuzzer.cc - a fuzzer based test that ensures that
all AST modules can be cloned with identical reproduction.

I've run this across 100 cores of a 3990x for 4 hours, fixing the
single issue it detected.

Note: Expressions do not currently clone their `TypeManager` determined
types. This is for two reasons:
(a) This initial CL is mahoosive enough.
(b) I'm uncertain whether we actually want to clone this info, or to
    re-run the `TypeDeterminer` after each AST transform. Maybe it should
    be optional. Time will tell.

Fixed: tint:307
Change-Id: Id90fab06aaa740c805d12b66f3f11d1f452c6805
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/33300
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
Ben Clayton 2020-12-01 18:04:17 +00:00 committed by Commit Bot service account
parent f1b0e1ee57
commit ed2b97811e
136 changed files with 1373 additions and 1 deletions

View File

@ -251,6 +251,8 @@ source_set("libtint_core_src") {
"src/ast/call_statement.h",
"src/ast/case_statement.cc",
"src/ast/case_statement.h",
"src/ast/clone_context.cc",
"src/ast/clone_context.h",
"src/ast/constant_id_decoration.cc",
"src/ast/constant_id_decoration.h",
"src/ast/constructor_expression.cc",
@ -772,6 +774,7 @@ source_set("tint_unittests_core_src") {
"src/ast/loop_statement_test.cc",
"src/ast/member_accessor_expression_test.cc",
"src/ast/module_test.cc",
"src/ast/module_clone_test.cc",
"src/ast/null_literal_test.cc",
"src/ast/return_statement_test.cc",
"src/ast/scalar_constructor_expression_test.cc",
@ -1309,6 +1312,17 @@ if (build_with_chromium) {
]
}
}
if (tint_build_wgsl_reader && tint_build_wgsl_writer) {
fuzzer_test("tint_spv_reader_fuzzer") {
sources = [ "fuzzers/tint_ast_clone_fuzzer.cc" ]
deps = [
":libtint_wgsl_reader_src",
":libtint_wgsl_writer_src",
":tint_fuzzer_common",
]
}
}
}
###############################################################################

View File

@ -28,7 +28,7 @@ endif()
option(TINT_BUILD_DOCS "Build documentation" ON)
option(TINT_BUILD_SPV_READER "Build the SPIR-V input reader" ON)
option(TINT_BUILD_WGSL_READER "Builde the WGSL input reader" ON)
option(TINT_BUILD_WGSL_READER "Build the WGSL input reader" ON)
option(TINT_BUILD_HLSL_WRITER "Build the HLSL output writer" ON)
option(TINT_BUILD_MSL_WRITER "Build the MSL output writer" ON)
option(TINT_BUILD_SPV_WRITER "Build the SPIR-V output writer" ON)

View File

@ -26,3 +26,7 @@ endif()
if (${TINT_BUILD_SPV_READER})
add_tint_fuzzer(tint_spv_reader_fuzzer)
endif()
if (${TINT_BUILD_WGSL_READER} AND ${TINT_BUILD_WGSL_WRITER})
add_tint_fuzzer(tint_ast_clone_fuzzer)
endif()

View File

@ -0,0 +1,103 @@
// 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 <iostream>
#include <string>
#include <unordered_set>
#include "src/reader/wgsl/parser_impl.h"
#include "src/writer/wgsl/generator.h"
#define ASSERT_EQ(A, B) \
do { \
decltype(A) assert_a = (A); \
decltype(B) assert_b = (B); \
if (assert_a != assert_b) { \
std::cerr << "ASSERT_EQ(" #A ", " #B ") failed:\n" \
<< #A << " was: " << assert_a << "\n" \
<< #B << " was: " << assert_b << "\n"; \
__builtin_trap(); \
} \
} while (false)
#define ASSERT_TRUE(A) \
do { \
decltype(A) assert_a = (A); \
if (!assert_a) { \
std::cerr << "ASSERT_TRUE(" #A ") failed:\n" \
<< #A << " was: " << assert_a << "\n"; \
__builtin_trap(); \
} \
} while (false)
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
std::string str(reinterpret_cast<const char*>(data), size);
tint::Source::File file("test.wgsl", str);
// Parse the wgsl, create the src module
tint::Context ctx;
tint::reader::wgsl::ParserImpl parser(&ctx, &file);
parser.set_max_errors(1);
if (!parser.Parse()) {
return 0;
}
auto src = parser.module();
// Clone the src module to dst
auto dst = src.Clone();
// Expect the AST printed with to_str() to match
ASSERT_EQ(src.to_str(), dst.to_str());
// Check that none of the AST nodes or type pointers in dst are found in src
std::unordered_set<tint::ast::Node*> src_nodes;
for (auto& src_node : src.nodes()) {
src_nodes.emplace(src_node.get());
}
std::unordered_set<tint::ast::type::Type*> src_types;
for (auto& src_type : src.types()) {
src_types.emplace(src_type.second.get());
}
for (auto& dst_node : dst.nodes()) {
ASSERT_EQ(src_nodes.count(dst_node.get()), 0u);
}
for (auto& dst_type : dst.types()) {
ASSERT_EQ(src_types.count(dst_type.second.get()), 0u);
}
// Regenerate the wgsl for the src module. We use this instead of the original
// source so that reformatting doesn't impact the final wgsl comparision.
// Note that the src module is moved into the generator and this generator has
// a limited scope, so that the src module is released before we attempt to
// print the dst module.
// This guarantee that all the source module nodes and types are destructed
// and freed.
// ASAN should error if there's any remaining references in dst when we try to
// reconstruct the WGSL.
std::string src_wgsl;
{
tint::writer::wgsl::Generator src_gen(std::move(src));
ASSERT_TRUE(src_gen.Generate());
src_wgsl = src_gen.result();
}
// Print the dst module, check it matches the original source
tint::writer::wgsl::Generator dst_gen(std::move(dst));
ASSERT_TRUE(dst_gen.Generate());
auto dst_wgsl = dst_gen.result();
ASSERT_EQ(src_wgsl, dst_wgsl);
return 0;
}

View File

@ -72,6 +72,8 @@ set(TINT_LIB_SRCS
ast/call_statement.h
ast/case_statement.cc
ast/case_statement.h
ast/clone_context.cc
ast/clone_context.h
ast/constant_id_decoration.cc
ast/constant_id_decoration.h
ast/constructor_expression.cc
@ -381,6 +383,7 @@ set(TINT_TEST_SRCS
ast/loop_statement_test.cc
ast/member_accessor_expression_test.cc
ast/module_test.cc
ast/module_clone_test.cc
ast/null_literal_test.cc
ast/binary_expression_test.cc
ast/return_statement_test.cc

View File

@ -14,6 +14,9 @@
#include "src/ast/access_decoration.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -27,5 +30,9 @@ void AccessDecoration::to_str(std::ostream& out, size_t indent) const {
out << "AccessDecoration{" << value_ << "}" << std::endl;
}
AccessDecoration* AccessDecoration::Clone(CloneContext* ctx) const {
return ctx->mod->create<AccessDecoration>(value_, ctx->Clone(source()));
}
} // namespace ast
} // namespace tint

View File

@ -40,6 +40,14 @@ class AccessDecoration : public Castable<AccessDecoration, TypeDecoration> {
/// @param indent number of spaces to indent the node when writing
void to_str(std::ostream& out, size_t indent) const override;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
AccessDecoration* Clone(CloneContext* ctx) const override;
private:
AccessControl value_ = ast::AccessControl::kReadWrite;
};

View File

@ -14,6 +14,9 @@
#include "src/ast/array_accessor_expression.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -33,6 +36,12 @@ ArrayAccessorExpression::ArrayAccessorExpression(ArrayAccessorExpression&&) =
ArrayAccessorExpression::~ArrayAccessorExpression() = default;
ArrayAccessorExpression* ArrayAccessorExpression::Clone(
CloneContext* ctx) const {
return ctx->mod->create<ArrayAccessorExpression>(ctx->Clone(array_),
ctx->Clone(idx_expr_));
}
bool ArrayAccessorExpression::IsValid() const {
if (array_ == nullptr || !array_->IsValid())
return false;

View File

@ -57,6 +57,14 @@ class ArrayAccessorExpression
/// @returns the index expression
Expression* idx_expr() const { return idx_expr_; }
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
ArrayAccessorExpression* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;

View File

@ -14,6 +14,9 @@
#include "src/ast/assignment_statement.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -31,6 +34,11 @@ AssignmentStatement::AssignmentStatement(AssignmentStatement&&) = default;
AssignmentStatement::~AssignmentStatement() = default;
AssignmentStatement* AssignmentStatement::Clone(CloneContext* ctx) const {
return ctx->mod->create<AssignmentStatement>(
ctx->Clone(source()), ctx->Clone(lhs_), ctx->Clone(rhs_));
}
bool AssignmentStatement::IsValid() const {
if (lhs_ == nullptr || !lhs_->IsValid())
return false;

View File

@ -55,6 +55,14 @@ class AssignmentStatement : public Castable<AssignmentStatement, Statement> {
/// @returns the right side expression
Expression* rhs() const { return rhs_; }
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
AssignmentStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;

View File

@ -14,6 +14,9 @@
#include "src/ast/binary_expression.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -34,6 +37,11 @@ BinaryExpression::BinaryExpression(BinaryExpression&&) = default;
BinaryExpression::~BinaryExpression() = default;
BinaryExpression* BinaryExpression::Clone(CloneContext* ctx) const {
return ctx->mod->create<BinaryExpression>(ctx->Clone(source()), op_,
ctx->Clone(lhs_), ctx->Clone(rhs_));
}
bool BinaryExpression::IsValid() const {
if (lhs_ == nullptr || !lhs_->IsValid()) {
return false;

View File

@ -125,6 +125,14 @@ class BinaryExpression : public Castable<BinaryExpression, Expression> {
/// @returns the right side expression
Expression* rhs() const { return rhs_; }
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
BinaryExpression* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;

View File

@ -14,6 +14,9 @@
#include "src/ast/binding_decoration.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -27,5 +30,9 @@ void BindingDecoration::to_str(std::ostream& out, size_t indent) const {
out << "BindingDecoration{" << value_ << "}" << std::endl;
}
BindingDecoration* BindingDecoration::Clone(CloneContext* ctx) const {
return ctx->mod->create<BindingDecoration>(value_, ctx->Clone(source()));
}
} // namespace ast
} // namespace tint

View File

@ -40,6 +40,14 @@ class BindingDecoration
/// @param indent number of spaces to indent the node when writing
void to_str(std::ostream& out, size_t indent) const override;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
BindingDecoration* Clone(CloneContext* ctx) const override;
private:
uint32_t value_;
};

View File

@ -14,6 +14,9 @@
#include "src/ast/bitcast_expression.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -30,6 +33,11 @@ BitcastExpression::BitcastExpression(const Source& source,
BitcastExpression::BitcastExpression(BitcastExpression&&) = default;
BitcastExpression::~BitcastExpression() = default;
BitcastExpression* BitcastExpression::Clone(CloneContext* ctx) const {
return ctx->mod->create<BitcastExpression>(
ctx->Clone(source()), ctx->Clone(type_), ctx->Clone(expr_));
}
bool BitcastExpression::IsValid() const {
if (expr_ == nullptr || !expr_->IsValid())
return false;

View File

@ -55,6 +55,14 @@ class BitcastExpression : public Castable<BitcastExpression, Expression> {
/// @returns the expression
Expression* expr() const { return expr_; }
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
BitcastExpression* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;

View File

@ -14,6 +14,9 @@
#include "src/ast/block_statement.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -25,6 +28,12 @@ BlockStatement::BlockStatement(BlockStatement&&) = default;
BlockStatement::~BlockStatement() = default;
BlockStatement* BlockStatement::Clone(CloneContext* ctx) const {
auto* cloned = ctx->mod->create<BlockStatement>(ctx->Clone(source()));
cloned->statements_ = ctx->Clone(statements_);
return cloned;
}
bool BlockStatement::IsValid() const {
for (auto* stmt : *this) {
if (stmt == nullptr || !stmt->IsValid()) {

View File

@ -85,6 +85,14 @@ class BlockStatement : public Castable<BlockStatement, Statement> {
return statements_.end();
}
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
BlockStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;

View File

@ -14,6 +14,9 @@
#include "src/ast/bool_literal.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -30,5 +33,9 @@ std::string BoolLiteral::name() const {
return value_ ? "__bool_true" : "__bool_false";
}
BoolLiteral* BoolLiteral::Clone(CloneContext* ctx) const {
return ctx->mod->create<BoolLiteral>(ctx->Clone(type()), value_);
}
} // namespace ast
} // namespace tint

View File

@ -42,6 +42,14 @@ class BoolLiteral : public Castable<BoolLiteral, Literal> {
/// @returns the literal as a string
std::string to_str() const override;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
BoolLiteral* Clone(CloneContext* ctx) const override;
private:
bool value_;
};

View File

@ -14,6 +14,9 @@
#include "src/ast/break_statement.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -25,6 +28,10 @@ BreakStatement::BreakStatement(BreakStatement&&) = default;
BreakStatement::~BreakStatement() = default;
BreakStatement* BreakStatement::Clone(CloneContext* ctx) const {
return ctx->mod->create<BreakStatement>(ctx->Clone(source()));
}
bool BreakStatement::IsValid() const {
return true;
}

View File

@ -32,6 +32,14 @@ class BreakStatement : public Castable<BreakStatement, Statement> {
BreakStatement(BreakStatement&&);
~BreakStatement() override;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
BreakStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;

View File

@ -14,6 +14,9 @@
#include "src/ast/builtin_decoration.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -27,5 +30,9 @@ void BuiltinDecoration::to_str(std::ostream& out, size_t indent) const {
out << "BuiltinDecoration{" << builtin_ << "}" << std::endl;
}
BuiltinDecoration* BuiltinDecoration::Clone(CloneContext* ctx) const {
return ctx->mod->create<BuiltinDecoration>(builtin_, ctx->Clone(source()));
}
} // namespace ast
} // namespace tint

View File

@ -39,6 +39,14 @@ class BuiltinDecoration
/// @param indent number of spaces to indent the node when writing
void to_str(std::ostream& out, size_t indent) const override;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
BuiltinDecoration* Clone(CloneContext* ctx) const override;
private:
Builtin builtin_ = Builtin::kNone;
};

View File

@ -14,6 +14,9 @@
#include "src/ast/call_expression.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -31,6 +34,11 @@ CallExpression::CallExpression(CallExpression&&) = default;
CallExpression::~CallExpression() = default;
CallExpression* CallExpression::Clone(CloneContext* ctx) const {
return ctx->mod->create<CallExpression>(
ctx->Clone(source()), ctx->Clone(func_), ctx->Clone(params_));
}
bool CallExpression::IsValid() const {
if (func_ == nullptr || !func_->IsValid())
return false;

View File

@ -54,6 +54,14 @@ class CallExpression : public Castable<CallExpression, Expression> {
/// @returns the parameters
const ExpressionList& params() const { return params_; }
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
CallExpression* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;

View File

@ -15,6 +15,8 @@
#include "src/ast/call_statement.h"
#include "src/ast/call_expression.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -27,6 +29,10 @@ CallStatement::CallStatement(CallStatement&&) = default;
CallStatement::~CallStatement() = default;
CallStatement* CallStatement::Clone(CloneContext* ctx) const {
return ctx->mod->create<CallStatement>(ctx->Clone(call_));
}
bool CallStatement::IsValid() const {
return call_ != nullptr && call_->IsValid();
}

View File

@ -42,6 +42,14 @@ class CallStatement : public Castable<CallStatement, Statement> {
/// @returns the call expression
CallExpression* expr() const { return call_; }
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
CallStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;

View File

@ -14,6 +14,9 @@
#include "src/ast/case_statement.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -31,6 +34,11 @@ CaseStatement::CaseStatement(CaseStatement&&) = default;
CaseStatement::~CaseStatement() = default;
CaseStatement* CaseStatement::Clone(CloneContext* ctx) const {
return ctx->mod->create<CaseStatement>(
ctx->Clone(source()), ctx->Clone(selectors_), ctx->Clone(body_));
}
bool CaseStatement::IsValid() const {
return body_ != nullptr && body_->IsValid();
}

View File

@ -70,6 +70,14 @@ class CaseStatement : public Castable<CaseStatement, Statement> {
/// @returns the case body
BlockStatement* body() { return body_; }
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
CaseStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;

24
src/ast/clone_context.cc Normal file
View File

@ -0,0 +1,24 @@
// 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/ast/clone_context.h"
namespace tint {
namespace ast {
CloneContext::CloneContext(Module* m) : mod(m) {}
CloneContext::~CloneContext() = default;
} // namespace ast
} // namespace tint

90
src/ast/clone_context.h Normal file
View File

@ -0,0 +1,90 @@
// 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_AST_CLONE_CONTEXT_H_
#define SRC_AST_CLONE_CONTEXT_H_
#include <unordered_map>
#include <vector>
#include "src/source.h"
namespace tint {
namespace ast {
class Module;
/// CloneContext holds the state used while cloning AST nodes and types.
class CloneContext {
public:
/// Constructor
/// @param m the target module to clone into
explicit CloneContext(Module* m);
/// Destructor
~CloneContext();
/// Clones the `Node` or `type::Type` @p a into the module #mod if @p a is not
/// null. If @p a is null, then Clone() returns null. If @p a has been cloned
/// already by this CloneContext then the same cloned pointer is returned.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param a the `Node` or `type::Type` to clone
/// @return the cloned node
template <typename T>
T* Clone(T* a) {
if (a == nullptr) {
return nullptr;
}
auto it = cloned_.find(a);
if (it != cloned_.end()) {
return static_cast<T*>(it->second);
}
auto* c = a->Clone(this);
cloned_.emplace(a, c);
return static_cast<T*>(c);
}
/// Clones the `Source` @p s into @p mod
/// TODO(bclayton) - Currently this 'clone' is a shallow copy. If/when
/// `Source.File`s are owned by the `Module` this should make a copy of the
/// file.
/// @param s the `Source` to clone
/// @return the cloned source
Source Clone(const Source& s) { return s; }
/// Clones each of the elements of the vector @p v into the module #mod.
/// @param v the vector to clone
/// @return the cloned vector
template <typename T>
std::vector<T> Clone(const std::vector<T>& v) {
std::vector<T> out;
out.reserve(v.size());
for (auto& el : v) {
out.emplace_back(Clone(el));
}
return out;
}
/// The target module to clone into.
Module* const mod;
private:
std::unordered_map<void*, void*> cloned_;
};
} // namespace ast
} // namespace tint
#endif // SRC_AST_CLONE_CONTEXT_H_

View File

@ -14,6 +14,9 @@
#include "src/ast/constant_id_decoration.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -27,5 +30,9 @@ void ConstantIdDecoration::to_str(std::ostream& out, size_t indent) const {
out << "ConstantIdDecoration{" << value_ << "}" << std::endl;
}
ConstantIdDecoration* ConstantIdDecoration::Clone(CloneContext* ctx) const {
return ctx->mod->create<ConstantIdDecoration>(value_, ctx->Clone(source()));
}
} // namespace ast
} // namespace tint

View File

@ -39,6 +39,14 @@ class ConstantIdDecoration
/// @param indent number of spaces to indent the node when writing
void to_str(std::ostream& out, size_t indent) const override;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
ConstantIdDecoration* Clone(CloneContext* ctx) const override;
private:
uint32_t value_ = 0;
};

View File

@ -14,6 +14,9 @@
#include "src/ast/continue_statement.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -25,6 +28,10 @@ ContinueStatement::ContinueStatement(ContinueStatement&&) = default;
ContinueStatement::~ContinueStatement() = default;
ContinueStatement* ContinueStatement::Clone(CloneContext* ctx) const {
return ctx->mod->create<ContinueStatement>(ctx->Clone(source()));
}
bool ContinueStatement::IsValid() const {
return true;
}

View File

@ -35,6 +35,14 @@ class ContinueStatement : public Castable<ContinueStatement, Statement> {
ContinueStatement(ContinueStatement&&);
~ContinueStatement() override;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
ContinueStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;

View File

@ -17,8 +17,10 @@
#include <cassert>
#include "src/ast/builtin_decoration.h"
#include "src/ast/clone_context.h"
#include "src/ast/constant_id_decoration.h"
#include "src/ast/location_decoration.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -69,6 +71,18 @@ uint32_t DecoratedVariable::constant_id() const {
return 0;
}
DecoratedVariable* DecoratedVariable::Clone(CloneContext* ctx) const {
auto* cloned = ctx->mod->create<DecoratedVariable>();
cloned->set_source(ctx->Clone(source()));
cloned->set_name(name());
cloned->set_storage_class(storage_class());
cloned->set_type(ctx->Clone(type()));
cloned->set_constructor(ctx->Clone(constructor()));
cloned->set_is_const(is_const());
cloned->set_decorations(ctx->Clone(decorations()));
return cloned;
}
bool DecoratedVariable::IsValid() const {
return Variable::IsValid();
}

View File

@ -56,6 +56,14 @@ class DecoratedVariable : public Castable<DecoratedVariable, Variable> {
/// |HasConstantIdDecoration| has been called first.
uint32_t constant_id() const;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
DecoratedVariable* Clone(CloneContext* ctx) const override;
/// @returns true if the name and path are both present
bool IsValid() const override;

View File

@ -14,6 +14,9 @@
#include "src/ast/discard_statement.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -25,6 +28,10 @@ DiscardStatement::DiscardStatement(DiscardStatement&&) = default;
DiscardStatement::~DiscardStatement() = default;
DiscardStatement* DiscardStatement::Clone(CloneContext* ctx) const {
return ctx->mod->create<DiscardStatement>(ctx->Clone(source()));
}
bool DiscardStatement::IsValid() const {
return true;
}

View File

@ -32,6 +32,14 @@ class DiscardStatement : public Castable<DiscardStatement, Statement> {
DiscardStatement(DiscardStatement&&);
~DiscardStatement() override;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
DiscardStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;

View File

@ -14,6 +14,9 @@
#include "src/ast/else_statement.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -34,6 +37,11 @@ ElseStatement::ElseStatement(ElseStatement&&) = default;
ElseStatement::~ElseStatement() = default;
ElseStatement* ElseStatement::Clone(CloneContext* ctx) const {
return ctx->mod->create<ElseStatement>(
ctx->Clone(source()), ctx->Clone(condition_), ctx->Clone(body_));
}
bool ElseStatement::IsValid() const {
if (body_ == nullptr || !body_->IsValid()) {
return false;

View File

@ -67,6 +67,14 @@ class ElseStatement : public Castable<ElseStatement, Statement> {
/// @returns the else body
BlockStatement* body() { return body_; }
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
ElseStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;

View File

@ -26,6 +26,7 @@ class Expr : public Expression {
public:
Expr() : Expression() {}
Expr* Clone(CloneContext*) const override { return nullptr; }
bool IsValid() const override { return true; }
void to_str(std::ostream&, size_t) const override {}
};

View File

@ -14,6 +14,9 @@
#include "src/ast/fallthrough_statement.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -26,6 +29,10 @@ FallthroughStatement::FallthroughStatement(FallthroughStatement&&) = default;
FallthroughStatement::~FallthroughStatement() = default;
FallthroughStatement* FallthroughStatement::Clone(CloneContext* ctx) const {
return ctx->mod->create<FallthroughStatement>(ctx->Clone(source()));
}
bool FallthroughStatement::IsValid() const {
return true;
}

View File

@ -32,6 +32,14 @@ class FallthroughStatement : public Castable<FallthroughStatement, Statement> {
FallthroughStatement(FallthroughStatement&&);
~FallthroughStatement() override;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
FallthroughStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;

View File

@ -17,6 +17,9 @@
#include <limits>
#include <sstream>
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -37,5 +40,9 @@ std::string FloatLiteral::name() const {
return out.str();
}
FloatLiteral* FloatLiteral::Clone(CloneContext* ctx) const {
return ctx->mod->create<FloatLiteral>(ctx->Clone(type()), value_);
}
} // namespace ast
} // namespace tint

View File

@ -40,6 +40,14 @@ class FloatLiteral : public Castable<FloatLiteral, Literal> {
/// @returns the literal as a string
std::string to_str() const override;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
FloatLiteral* Clone(CloneContext* ctx) const override;
private:
float value_;
};

View File

@ -16,7 +16,9 @@
#include <sstream>
#include "src/ast/clone_context.h"
#include "src/ast/decorated_variable.h"
#include "src/ast/module.h"
#include "src/ast/stage_decoration.h"
#include "src/ast/type/multisampled_texture_type.h"
#include "src/ast/type/sampled_texture_type.h"
@ -213,6 +215,14 @@ const Statement* Function::get_last_statement() const {
return body_->last();
}
Function* Function::Clone(CloneContext* ctx) const {
auto* cloned = ctx->mod->create<Function>(
ctx->Clone(source()), name_, ctx->Clone(params_),
ctx->Clone(return_type_), ctx->Clone(body_));
cloned->set_decorations(ctx->Clone(decorations_));
return cloned;
}
bool Function::IsValid() const {
for (auto* param : params_) {
if (param == nullptr || !param->IsValid())

View File

@ -190,6 +190,14 @@ class Function : public Castable<Function, Node> {
/// @returns the function body
BlockStatement* body() { return body_; }
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
Function* Clone(CloneContext* ctx) const override;
/// @returns true if the name and type are both present
bool IsValid() const override;

View File

@ -14,6 +14,9 @@
#include "src/ast/identifier_expression.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -28,6 +31,10 @@ IdentifierExpression::IdentifierExpression(IdentifierExpression&&) = default;
IdentifierExpression::~IdentifierExpression() = default;
IdentifierExpression* IdentifierExpression::Clone(CloneContext* ctx) const {
return ctx->mod->create<IdentifierExpression>(ctx->Clone(source()), name_);
}
bool IdentifierExpression::IsValid() const {
return !name_.empty();
}

View File

@ -61,6 +61,14 @@ class IdentifierExpression : public Castable<IdentifierExpression, Expression> {
/// @returns true if this identifier is for an intrinsic
bool IsIntrinsic() const { return intrinsic_ != Intrinsic::kNone; }
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
IdentifierExpression* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;

View File

@ -14,7 +14,9 @@
#include "src/ast/if_statement.h"
#include "src/ast/clone_context.h"
#include "src/ast/else_statement.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -31,6 +33,13 @@ IfStatement::IfStatement(IfStatement&&) = default;
IfStatement::~IfStatement() = default;
IfStatement* IfStatement::Clone(CloneContext* ctx) const {
auto* cloned = ctx->mod->create<IfStatement>(
ctx->Clone(source()), ctx->Clone(condition_), ctx->Clone(body_));
cloned->else_statements_ = ctx->Clone(else_statements_);
return cloned;
}
bool IfStatement::IsValid() const {
if (condition_ == nullptr || !condition_->IsValid()) {
return false;

View File

@ -71,6 +71,14 @@ class IfStatement : public Castable<IfStatement, Statement> {
/// @returns true if there are else statements
bool has_else_statements() const { return !else_statements_.empty(); }
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
IfStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;

View File

@ -14,6 +14,9 @@
#include "src/ast/location_decoration.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -27,5 +30,9 @@ void LocationDecoration::to_str(std::ostream& out, size_t indent) const {
out << "LocationDecoration{" << value_ << "}" << std::endl;
}
LocationDecoration* LocationDecoration::Clone(CloneContext* ctx) const {
return ctx->mod->create<LocationDecoration>(value_, ctx->Clone(source()));
}
} // namespace ast
} // namespace tint

View File

@ -40,6 +40,14 @@ class LocationDecoration
/// @param indent number of spaces to indent the node when writing
void to_str(std::ostream& out, size_t indent) const override;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
LocationDecoration* Clone(CloneContext* ctx) const override;
private:
uint32_t value_;
};

View File

@ -14,6 +14,9 @@
#include "src/ast/loop_statement.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -29,6 +32,11 @@ LoopStatement::LoopStatement(LoopStatement&&) = default;
LoopStatement::~LoopStatement() = default;
LoopStatement* LoopStatement::Clone(CloneContext* ctx) const {
return ctx->mod->create<LoopStatement>(
ctx->Clone(source()), ctx->Clone(body_), ctx->Clone(continuing_));
}
bool LoopStatement::IsValid() const {
if (body_ == nullptr || !body_->IsValid()) {
return false;

View File

@ -62,6 +62,14 @@ class LoopStatement : public Castable<LoopStatement, Statement> {
return continuing_ != nullptr && !continuing_->empty();
}
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
LoopStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;

View File

@ -14,6 +14,9 @@
#include "src/ast/member_accessor_expression.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -33,6 +36,12 @@ MemberAccessorExpression::MemberAccessorExpression(MemberAccessorExpression&&) =
MemberAccessorExpression::~MemberAccessorExpression() = default;
MemberAccessorExpression* MemberAccessorExpression::Clone(
CloneContext* ctx) const {
return ctx->mod->create<MemberAccessorExpression>(
ctx->Clone(source()), ctx->Clone(struct_), ctx->Clone(member_));
}
bool MemberAccessorExpression::IsValid() const {
if (struct_ == nullptr || !struct_->IsValid()) {
return false;

View File

@ -59,6 +59,14 @@ class MemberAccessorExpression
/// @returns the member expression
IdentifierExpression* member() const { return member_; }
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
MemberAccessorExpression* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;

View File

@ -16,6 +16,7 @@
#include <sstream>
#include "src/ast/clone_context.h"
#include "src/ast/type/struct_type.h"
namespace tint {
@ -27,6 +28,23 @@ Module::Module(Module&&) = default;
Module::~Module() = default;
Module Module::Clone() {
Module out;
CloneContext ctx(&out);
for (auto* ty : constructed_types_) {
out.constructed_types_.emplace_back(ctx.Clone(ty));
}
for (auto* var : global_variables_) {
out.global_variables_.emplace_back(ctx.Clone(var));
}
for (auto* func : functions_) {
out.functions_.emplace_back(ctx.Clone(func));
}
return out;
}
Function* Module::FindFunctionByName(const std::string& name) const {
for (auto* func : functions_) {
if (func->name() == name) {

View File

@ -42,6 +42,9 @@ class Module {
Module(Module&&);
~Module();
/// @return a deep copy of this module
Module Clone();
/// Add a global variable to the module
/// @param var the variable to add
void AddGlobalVariable(Variable* var) { global_variables_.push_back(var); }
@ -135,6 +138,9 @@ class Module {
return type_mgr_.types();
}
/// @returns all the declared nodes in the module
const std::vector<std::unique_ptr<ast::Node>>& nodes() { return ast_nodes_; }
private:
Module(const Module&) = delete;

View File

@ -0,0 +1,168 @@
// 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/ast/case_statement.h"
#include "gtest/gtest.h"
#include "src/reader/wgsl/parser.h"
#include "src/writer/wgsl/generator.h"
namespace tint {
namespace ast {
namespace {
TEST(ModuleCloneTest, Clone) {
#if TINT_BUILD_WGSL_READER && TINT_BUILD_WGSL_WRITER
// Shader that exercises the bulk of the AST nodes and types.
// See also fuzzers/tint_ast_clone_fuzzer.cc for further coverage of cloning.
Source::File file("test.wgsl", R"([[block]]
struct S {
[[offset(0)]]
m0 : u32;
[[offset(4)]]
m1 : array<u32>;
};
type t0 = [[stride(16)]] array<vec4<f32>>;
type t1 = [[stride(32)]] array<vec4<f32>>;
const c0 : i32 = 10;
const c1 : bool = true;
var<uniform> g0 : u32 = 20u;
var<out> g1 : f32 = 123.0;
var<uniform> g2 : texture_2d<f32>;
var<uniform> g3 : texture_storage_ro_2d<r32uint>;
var<uniform> g4 : texture_storage_wo_2d<rg32float>;
var<uniform> g5 : texture_storage_ro_2d<r32uint>;
var<uniform> g6 : texture_storage_wo_2d<rg32float>;
[[builtin(position)]] var<uniform> g7 : vec3<f32>;
[[set(10), binding(20)]] var<storage_buffer> g7 : S;
[[set(10), binding(20)]] var<storage_buffer> g8 : [[access(read)]]
S;
[[set(10), binding(20)]] var<storage_buffer> g9 : [[access(read_write)]]
S;
fn f0(p0 : bool) -> f32 {
if (p0) {
return 1.0;
}
return 0.0;
}
fn f1(p0 : f32, p1 : i32) -> f32 {
var l0 : i32 = 3;
var l1 : f32 = 8;
var l2 : u32 = bitcast<u32>(4);
var l3 : vec2<u32> = vec2<u32>(l0, l1);
var l4 : S;
var l5 : u32 = l4.m1[5];
var l6 : ptr<private, u32>;
l6 = null;
loop {
l0 = (p1 + 2);
if (((l0 % 4) == 0)) {
continue;
}
continuing {
if (1 == 2) {
l0 = l0 - 1;
} else {
l0 = l0 - 2;
}
}
}
switch(l2) {
case 0: {
break;
}
case 1: {
return f0(true);
}
default: {
discard;
}
}
return 1.0;
}
[[stage(fragment)]]
fn main() -> void {
f1(1.0, 2);
}
)");
// Parse the wgsl, create the src module
Context ctx;
reader::wgsl::Parser parser(&ctx, &file);
ASSERT_TRUE(parser.Parse()) << parser.error();
auto src = parser.module();
// Clone the src module to dst
auto dst = src.Clone();
// Expect the AST printed with to_str() to match
EXPECT_EQ(src.to_str(), dst.to_str());
// Check that none of the AST nodes or type pointers in dst are found in src
std::unordered_set<ast::Node*> src_nodes;
for (auto& src_node : src.nodes()) {
src_nodes.emplace(src_node.get());
}
std::unordered_set<ast::type::Type*> src_types;
for (auto& src_type : src.types()) {
src_types.emplace(src_type.second.get());
}
for (auto& dst_node : dst.nodes()) {
ASSERT_EQ(src_nodes.count(dst_node.get()), 0u) << dst_node->str();
}
for (auto& dst_type : dst.types()) {
ASSERT_EQ(src_types.count(dst_type.second.get()), 0u)
<< dst_type.second->type_name();
}
// Regenerate the wgsl for the src module. We use this instead of the original
// source so that reformatting doesn't impact the final wgsl comparision.
// Note that the src module is moved into the generator and this generator has
// a limited scope, so that the src module is released before we attempt to
// print the dst module.
// This guarantee that all the source module nodes and types are destructed
// and freed.
// ASAN should error if there's any remaining references in dst when we try to
// reconstruct the WGSL.
std::string src_wgsl;
{
writer::wgsl::Generator src_gen(std::move(src));
ASSERT_TRUE(src_gen.Generate());
src_wgsl = src_gen.result();
}
// Print the dst module, check it matches the original source
writer::wgsl::Generator dst_gen(std::move(dst));
ASSERT_TRUE(dst_gen.Generate());
auto dst_wgsl = dst_gen.result();
ASSERT_EQ(src_wgsl, dst_wgsl);
#else // #if TINT_BUILD_WGSL_READER && TINT_BUILD_WGSL_WRITER
GTEST_SKIP() << "ModuleCloneTest requires TINT_BUILD_WGSL_READER and "
"TINT_BUILD_WGSL_WRITER to be enabled";
#endif
}
} // namespace
} // namespace ast
} // namespace tint

View File

@ -17,6 +17,7 @@
#include <ostream>
#include <string>
#include <vector>
#include "src/castable.h"
#include "src/source.h"
@ -24,11 +25,26 @@
namespace tint {
namespace ast {
class Module;
class CloneContext;
namespace type {
class Type;
}
/// AST base class node
class Node : public Castable<Node> {
public:
~Node() override;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
virtual Node* Clone(CloneContext* ctx) const = 0;
/// @returns the node source data
const Source& source() const { return source_; }
/// Sets the source data

View File

@ -14,6 +14,9 @@
#include "src/ast/null_literal.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -29,5 +32,9 @@ std::string NullLiteral::name() const {
return "__null" + type()->type_name();
}
NullLiteral* NullLiteral::Clone(CloneContext* ctx) const {
return ctx->mod->create<NullLiteral>(ctx->Clone(type()));
}
} // namespace ast
} // namespace tint

View File

@ -35,6 +35,14 @@ class NullLiteral : public Castable<NullLiteral, Literal> {
/// @returns the literal as a string
std::string to_str() const override;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
NullLiteral* Clone(CloneContext* ctx) const override;
};
} // namespace ast

View File

@ -14,6 +14,9 @@
#include "src/ast/return_statement.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -30,6 +33,11 @@ ReturnStatement::ReturnStatement(ReturnStatement&&) = default;
ReturnStatement::~ReturnStatement() = default;
ReturnStatement* ReturnStatement::Clone(CloneContext* ctx) const {
return ctx->mod->create<ReturnStatement>(ctx->Clone(source()),
ctx->Clone(value_));
}
bool ReturnStatement::IsValid() const {
if (value_ != nullptr) {
return value_->IsValid();

View File

@ -51,6 +51,14 @@ class ReturnStatement : public Castable<ReturnStatement, Statement> {
/// @returns true if the return has a value
bool has_value() const { return value_ != nullptr; }
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
ReturnStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;

View File

@ -14,6 +14,9 @@
#include "src/ast/scalar_constructor_expression.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -31,6 +34,12 @@ ScalarConstructorExpression::ScalarConstructorExpression(
ScalarConstructorExpression::~ScalarConstructorExpression() = default;
ScalarConstructorExpression* ScalarConstructorExpression::Clone(
CloneContext* ctx) const {
return ctx->mod->create<ScalarConstructorExpression>(ctx->Clone(source()),
ctx->Clone(literal_));
}
bool ScalarConstructorExpression::IsValid() const {
return literal_ != nullptr;
}

View File

@ -47,6 +47,14 @@ class ScalarConstructorExpression
/// @returns the literal value
Literal* literal() const { return literal_; }
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
ScalarConstructorExpression* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;

View File

@ -14,6 +14,9 @@
#include "src/ast/set_decoration.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -27,5 +30,9 @@ void SetDecoration::to_str(std::ostream& out, size_t indent) const {
out << "SetDecoration{" << value_ << "}" << std::endl;
}
SetDecoration* SetDecoration::Clone(CloneContext* ctx) const {
return ctx->mod->create<SetDecoration>(value_, ctx->Clone(source()));
}
} // namespace ast
} // namespace tint

View File

@ -39,6 +39,14 @@ class SetDecoration : public Castable<SetDecoration, VariableDecoration> {
/// @param indent number of spaces to indent the node when writing
void to_str(std::ostream& out, size_t indent) const override;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
SetDecoration* Clone(CloneContext* ctx) const override;
private:
uint32_t value_;
};

View File

@ -14,6 +14,9 @@
#include "src/ast/sint_literal.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -30,5 +33,9 @@ std::string SintLiteral::name() const {
return "__sint" + type()->type_name() + "_" + std::to_string(value_);
}
SintLiteral* SintLiteral::Clone(CloneContext* ctx) const {
return ctx->mod->create<SintLiteral>(ctx->Clone(type()), value_);
}
} // namespace ast
} // namespace tint

View File

@ -43,6 +43,14 @@ class SintLiteral : public Castable<SintLiteral, IntLiteral> {
/// @returns the literal as a string
std::string to_str() const override;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
SintLiteral* Clone(CloneContext* ctx) const override;
private:
int32_t value_;
};

View File

@ -14,6 +14,9 @@
#include "src/ast/stage_decoration.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -27,5 +30,9 @@ void StageDecoration::to_str(std::ostream& out, size_t indent) const {
out << "StageDecoration{" << stage_ << "}" << std::endl;
}
StageDecoration* StageDecoration::Clone(CloneContext* ctx) const {
return ctx->mod->create<StageDecoration>(stage_, ctx->Clone(source()));
}
} // namespace ast
} // namespace tint

View File

@ -38,6 +38,14 @@ class StageDecoration : public Castable<StageDecoration, FunctionDecoration> {
/// @param indent number of spaces to indent the node when writing
void to_str(std::ostream& out, size_t indent) const override;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
StageDecoration* Clone(CloneContext* ctx) const override;
private:
PipelineStage stage_ = PipelineStage::kNone;
};

View File

@ -14,6 +14,9 @@
#include "src/ast/stride_decoration.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -27,5 +30,9 @@ void StrideDecoration::to_str(std::ostream& out, size_t indent) const {
out << "stride " << stride_;
}
StrideDecoration* StrideDecoration::Clone(CloneContext* ctx) const {
return ctx->mod->create<StrideDecoration>(stride_, ctx->Clone(source()));
}
} // namespace ast
} // namespace tint

View File

@ -41,6 +41,14 @@ class StrideDecoration : public Castable<StrideDecoration, ArrayDecoration> {
/// @param indent number of spaces to indent the node when writing
void to_str(std::ostream& out, size_t indent) const override;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
StrideDecoration* Clone(CloneContext* ctx) const override;
private:
uint32_t stride_;
};

View File

@ -14,6 +14,8 @@
#include "src/ast/struct.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
#include "src/ast/struct_block_decoration.h"
namespace tint {
@ -61,6 +63,11 @@ bool Struct::IsBlockDecorated() const {
return false;
}
Struct* Struct::Clone(CloneContext* ctx) const {
return ctx->mod->create<Struct>(
ctx->Clone(source()), ctx->Clone(decorations_), ctx->Clone(members_));
}
bool Struct::IsValid() const {
for (auto* mem : members_) {
if (mem == nullptr || !mem->IsValid()) {

View File

@ -76,6 +76,14 @@ class Struct : public Castable<Struct, Node> {
/// @returns true if the struct is block decorated
bool IsBlockDecorated() const;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
Struct* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;

View File

@ -14,6 +14,9 @@
#include "src/ast/struct_block_decoration.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -27,5 +30,9 @@ void StructBlockDecoration::to_str(std::ostream& out, size_t indent) const {
out << "block";
}
StructBlockDecoration* StructBlockDecoration::Clone(CloneContext* ctx) const {
return ctx->mod->create<StructBlockDecoration>(ctx->Clone(source()));
}
} // namespace ast
} // namespace tint

View File

@ -37,6 +37,14 @@ class StructBlockDecoration
/// @param out the stream to write to
/// @param indent number of spaces to indent the node when writing
void to_str(std::ostream& out, size_t indent) const override;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
StructBlockDecoration* Clone(CloneContext* ctx) const override;
};
/// List of struct decorations

View File

@ -14,6 +14,8 @@
#include "src/ast/struct_member.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
#include "src/ast/struct_member_offset_decoration.h"
namespace tint {
@ -57,6 +59,11 @@ uint32_t StructMember::offset() const {
return 0;
}
StructMember* StructMember::Clone(CloneContext* ctx) const {
return ctx->mod->create<StructMember>(
ctx->Clone(source()), name_, ctx->Clone(type_), ctx->Clone(decorations_));
}
bool StructMember::IsValid() const {
if (name_.empty() || type_ == nullptr) {
return false;

View File

@ -77,6 +77,14 @@ class StructMember : public Castable<StructMember, Node> {
/// @returns the offset decoration value.
uint32_t offset() const;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
StructMember* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;

View File

@ -14,6 +14,9 @@
#include "src/ast/struct_member_offset_decoration.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -29,5 +32,11 @@ void StructMemberOffsetDecoration::to_str(std::ostream& out,
out << "offset " << std::to_string(offset_);
}
StructMemberOffsetDecoration* StructMemberOffsetDecoration::Clone(
CloneContext* ctx) const {
return ctx->mod->create<StructMemberOffsetDecoration>(offset_,
ctx->Clone(source()));
}
} // namespace ast
} // namespace tint

View File

@ -42,6 +42,14 @@ class StructMemberOffsetDecoration
/// @param indent number of spaces to indent the node when writing
void to_str(std::ostream& out, size_t indent) const override;
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
StructMemberOffsetDecoration* Clone(CloneContext* ctx) const override;
private:
uint32_t offset_;
};

View File

@ -15,6 +15,8 @@
#include "src/ast/switch_statement.h"
#include "src/ast/case_statement.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
@ -33,6 +35,11 @@ SwitchStatement::SwitchStatement(SwitchStatement&&) = default;
SwitchStatement::~SwitchStatement() = default;
SwitchStatement* SwitchStatement::Clone(CloneContext* ctx) const {
return ctx->mod->create<SwitchStatement>(
ctx->Clone(source()), ctx->Clone(condition_), ctx->Clone(body_));
}
bool SwitchStatement::IsValid() const {
if (condition_ == nullptr || !condition_->IsValid()) {
return false;

View File

@ -60,6 +60,14 @@ class SwitchStatement : public Castable<SwitchStatement, Statement> {
/// @returns the Switch body
const CaseStatementList& body() const { return body_; }
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @note Semantic information such as resolved expression type and intrinsic
/// information is not cloned.
/// @param ctx the clone context
/// @return the newly cloned node
SwitchStatement* Clone(CloneContext* ctx) const override;
/// @returns true if the node is valid
bool IsValid() const override;

View File

@ -16,6 +16,9 @@
#include <assert.h>
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
namespace type {
@ -54,6 +57,10 @@ uint64_t AccessControl::BaseAlignment(MemoryLayout mem_layout) const {
return subtype_->BaseAlignment(mem_layout);
}
AccessControl* AccessControl::Clone(CloneContext* ctx) const {
return ctx->mod->create<AccessControl>(access_, ctx->Clone(subtype_));
}
} // namespace type
} // namespace ast
} // namespace tint

View File

@ -60,6 +60,11 @@ class AccessControl : public Castable<AccessControl, Type> {
/// 0 for non-host shareable types.
uint64_t BaseAlignment(MemoryLayout mem_layout) const override;
/// Clones this type and all transitive types using the `CloneContext` `ctx`.
/// @param ctx the clone context
/// @return the newly cloned type
AccessControl* Clone(CloneContext* ctx) const override;
private:
ast::AccessControl access_ = ast::AccessControl::kReadOnly;
Type* subtype_ = nullptr;

View File

@ -16,6 +16,9 @@
#include <assert.h>
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
namespace type {
@ -41,6 +44,10 @@ uint64_t Alias::BaseAlignment(MemoryLayout mem_layout) const {
return subtype_->BaseAlignment(mem_layout);
}
Alias* Alias::Clone(CloneContext* ctx) const {
return ctx->mod->create<Alias>(name_, ctx->Clone(subtype_));
}
} // namespace type
} // namespace ast
} // namespace tint

View File

@ -52,6 +52,11 @@ class Alias : public Castable<Alias, Type> {
/// 0 for non-host shareable types.
uint64_t BaseAlignment(MemoryLayout mem_layout) const override;
/// Clones this type and all transitive types using the `CloneContext` `ctx`.
/// @param ctx the clone context
/// @return the newly cloned type
Alias* Clone(CloneContext* ctx) const override;
private:
std::string name_;
Type* subtype_ = nullptr;

View File

@ -15,7 +15,10 @@
#include "src/ast/type/array_type.h"
#include <cmath>
#include <memory>
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
#include "src/ast/stride_decoration.h"
#include "src/ast/type/vector_type.h"
@ -92,6 +95,12 @@ std::string Array::type_name() const {
return type_name;
}
Array* Array::Clone(CloneContext* ctx) const {
auto cloned = std::make_unique<Array>(ctx->Clone(subtype_), size_);
cloned->set_decorations(ctx->Clone(decorations()));
return ctx->mod->unique_type(std::move(cloned));
}
} // namespace type
} // namespace ast
} // namespace tint

View File

@ -74,6 +74,11 @@ class Array : public Castable<Array, Type> {
/// @returns the name for the type
std::string type_name() const override;
/// Clones this type and all transitive types using the `CloneContext` `ctx`.
/// @param ctx the clone context
/// @return the newly cloned type
Array* Clone(CloneContext* ctx) const override;
private:
Type* subtype_ = nullptr;
uint32_t size_ = 0;

View File

@ -14,6 +14,9 @@
#include "src/ast/type/bool_type.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
namespace type {
@ -28,6 +31,10 @@ std::string Bool::type_name() const {
return "__bool";
}
Bool* Bool::Clone(CloneContext* ctx) const {
return ctx->mod->create<Bool>();
}
} // namespace type
} // namespace ast
} // namespace tint

View File

@ -34,6 +34,11 @@ class Bool : public Castable<Bool, Type> {
/// @returns the name for this type
std::string type_name() const override;
/// Clones this type and all transitive types using the `CloneContext` `ctx`.
/// @param ctx the clone context
/// @return the newly cloned type
Bool* Clone(CloneContext* ctx) const override;
};
} // namespace type

View File

@ -17,6 +17,9 @@
#include <cassert>
#include <sstream>
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
namespace type {
@ -47,6 +50,10 @@ std::string DepthTexture::type_name() const {
return out.str();
}
DepthTexture* DepthTexture::Clone(CloneContext* ctx) const {
return ctx->mod->create<DepthTexture>(dim());
}
} // namespace type
} // namespace ast
} // namespace tint

View File

@ -35,6 +35,11 @@ class DepthTexture : public Castable<DepthTexture, Texture> {
/// @returns the name for this type
std::string type_name() const override;
/// Clones this type and all transitive types using the `CloneContext` `ctx`.
/// @param ctx the clone context
/// @return the newly cloned type
DepthTexture* Clone(CloneContext* ctx) const override;
};
} // namespace type

View File

@ -14,6 +14,9 @@
#include "src/ast/type/f32_type.h"
#include "src/ast/clone_context.h"
#include "src/ast/module.h"
namespace tint {
namespace ast {
namespace type {
@ -36,6 +39,10 @@ uint64_t F32::BaseAlignment(MemoryLayout) const {
return 4;
}
F32* F32::Clone(CloneContext* ctx) const {
return ctx->mod->create<F32>();
}
} // namespace type
} // namespace ast
} // namespace tint

View File

@ -44,6 +44,11 @@ class F32 : public Castable<F32, Type> {
/// @returns base alignment for the type, in bytes.
/// 0 for non-host shareable types.
uint64_t BaseAlignment(MemoryLayout mem_layout) const override;
/// Clones this type and all transitive types using the `CloneContext` `ctx`.
/// @param ctx the clone context
/// @return the newly cloned type
F32* Clone(CloneContext* ctx) const override;
};
} // namespace type

Some files were not shown because too many files have changed in this diff Show More