| // 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" | |
| #include "gtest/gtest.h" | |
| #include "src/ast/module.h" | |
| namespace tint { | |
| namespace ast { | |
| namespace { | |
| struct Cloneable : public Castable<Cloneable, Node> { | |
| Cloneable() : Base(Source{}) {} | |
| Cloneable* a = nullptr; | |
| Cloneable* b = nullptr; | |
| Cloneable* c = nullptr; | |
| Cloneable* Clone(CloneContext* ctx) const override { | |
| auto* out = ctx->mod->create<Cloneable>(); | |
| out->a = ctx->Clone(a); | |
| out->b = ctx->Clone(b); | |
| out->c = ctx->Clone(c); | |
| return out; | |
| } | |
| bool IsValid() const override { return true; } | |
| void to_str(std::ostream&, size_t) const override {} | |
| }; | |
| struct Replaceable : public Castable<Replaceable, Cloneable> {}; | |
| struct Replacement : public Castable<Replacement, Replaceable> {}; | |
| TEST(CloneContext, Clone) { | |
| ast::Module original; | |
| auto* original_root = original.create<Cloneable>(); | |
| original_root->a = original.create<Cloneable>(); | |
| original_root->a->b = original.create<Cloneable>(); | |
| original_root->b = original.create<Cloneable>(); | |
| original_root->b->a = original_root->a; // Aliased | |
| original_root->b->b = original.create<Cloneable>(); | |
| original_root->c = original_root->b; // Aliased | |
| // root | |
| // ╭──────────────────┼──────────────────╮ | |
| // (a) (b) (c) | |
| // C <──────┐ C <───────────────┘ | |
| // ╭────┼────╮ │ ╭────┼────╮ | |
| // (a) (b) (c) │ (a) (b) (c) | |
| // C └───┘ C | |
| // | |
| // C: Clonable | |
| ast::Module cloned; | |
| auto* cloned_root = CloneContext(&cloned, &original).Clone(original_root); | |
| EXPECT_NE(cloned_root->a, nullptr); | |
| EXPECT_EQ(cloned_root->a->a, nullptr); | |
| EXPECT_NE(cloned_root->a->b, nullptr); | |
| EXPECT_EQ(cloned_root->a->c, nullptr); | |
| EXPECT_NE(cloned_root->b, nullptr); | |
| EXPECT_NE(cloned_root->b->a, nullptr); | |
| EXPECT_NE(cloned_root->b->b, nullptr); | |
| EXPECT_EQ(cloned_root->b->c, nullptr); | |
| EXPECT_NE(cloned_root->c, nullptr); | |
| EXPECT_NE(cloned_root->a, original_root->a); | |
| EXPECT_NE(cloned_root->a->b, original_root->a->b); | |
| EXPECT_NE(cloned_root->b, original_root->b); | |
| EXPECT_NE(cloned_root->b->a, original_root->b->a); | |
| EXPECT_NE(cloned_root->b->b, original_root->b->b); | |
| EXPECT_NE(cloned_root->c, original_root->c); | |
| EXPECT_EQ(cloned_root->b->a, cloned_root->a); // Aliased | |
| EXPECT_EQ(cloned_root->c, cloned_root->b); // Aliased | |
| } | |
| TEST(CloneContext, CloneWithReplacements) { | |
| ast::Module original; | |
| auto* original_root = original.create<Cloneable>(); | |
| original_root->a = original.create<Cloneable>(); | |
| original_root->a->b = original.create<Replaceable>(); | |
| original_root->b = original.create<Replaceable>(); | |
| original_root->b->a = original_root->a; // Aliased | |
| original_root->c = original_root->b; // Aliased | |
| // root | |
| // ╭──────────────────┼──────────────────╮ | |
| // (a) (b) (c) | |
| // C <──────┐ R <───────────────┘ | |
| // ╭────┼────╮ │ ╭────┼────╮ | |
| // (a) (b) (c) │ (a) (b) (c) | |
| // R └───┘ | |
| // | |
| // C: Clonable | |
| // R: Replaceable | |
| ast::Module cloned; | |
| auto* cloned_root = CloneContext(&cloned, &original) | |
| .ReplaceAll([&](CloneContext* ctx, Replaceable* in) { | |
| auto* out = cloned.create<Replacement>(); | |
| out->b = cloned.create<Cloneable>(); | |
| out->c = ctx->Clone(in->a); | |
| return out; | |
| }) | |
| .Clone(original_root); | |
| // root | |
| // ╭─────────────────┼──────────────────╮ | |
| // (a) (b) (c) | |
| // C <──────┐ R <───────────────┘ | |
| // ╭────┼────╮ │ ╭────┼────╮ | |
| // (a) (b) (c) │ (a) (b) (c) | |
| // R │ C | | |
| // ╭────┼────╮ └────────────┘ | |
| // (a) (b) (c) | |
| // C | |
| // | |
| // C: Clonable | |
| // R: Replacement | |
| EXPECT_NE(cloned_root->a, nullptr); | |
| EXPECT_EQ(cloned_root->a->a, nullptr); | |
| EXPECT_NE(cloned_root->a->b, nullptr); // Replaced | |
| EXPECT_EQ(cloned_root->a->b->a, nullptr); // From replacement | |
| EXPECT_NE(cloned_root->a->b->b, nullptr); // From replacement | |
| EXPECT_EQ(cloned_root->a->b->c, nullptr); // From replacement | |
| EXPECT_EQ(cloned_root->a->c, nullptr); | |
| EXPECT_NE(cloned_root->b, nullptr); | |
| EXPECT_EQ(cloned_root->b->a, nullptr); // From replacement | |
| EXPECT_NE(cloned_root->b->b, nullptr); // From replacement | |
| EXPECT_NE(cloned_root->b->c, nullptr); // From replacement | |
| EXPECT_NE(cloned_root->c, nullptr); | |
| EXPECT_NE(cloned_root->a, original_root->a); | |
| EXPECT_NE(cloned_root->a->b, original_root->a->b); | |
| EXPECT_NE(cloned_root->b, original_root->b); | |
| EXPECT_NE(cloned_root->b->a, original_root->b->a); | |
| EXPECT_NE(cloned_root->c, original_root->c); | |
| EXPECT_EQ(cloned_root->b->c, cloned_root->a); // Aliased | |
| EXPECT_EQ(cloned_root->c, cloned_root->b); // Aliased | |
| EXPECT_FALSE(cloned_root->a->Is<Replacement>()); | |
| EXPECT_TRUE(cloned_root->a->b->Is<Replacement>()); | |
| EXPECT_FALSE(cloned_root->a->b->b->Is<Replacement>()); | |
| EXPECT_TRUE(cloned_root->b->Is<Replacement>()); | |
| EXPECT_FALSE(cloned_root->b->b->Is<Replacement>()); | |
| } | |
| } // namespace | |
| } // namespace ast | |
| TINT_INSTANTIATE_CLASS_ID(ast::Cloneable); | |
| TINT_INSTANTIATE_CLASS_ID(ast::Replaceable); | |
| TINT_INSTANTIATE_CLASS_ID(ast::Replacement); | |
| } // namespace tint |