CloneContext: Add InsertBack()

Inserts a new node at the end of a (potentially empty) list.

Change-Id: Ibd487b995aee4a3ae2c3d17b5eb6e8562c43a1c4
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/52320
Commit-Queue: James Price <jrprice@google.com>
Auto-Submit: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/clone_context.h b/src/clone_context.h
index c743d3b..0b20d25 100644
--- a/src/clone_context.h
+++ b/src/clone_context.h
@@ -246,6 +246,9 @@
           }
         }
       }
+      for (auto* o : transforms.insert_back_) {
+        out.emplace_back(CheckedCast<T>(o));
+      }
     } else {
       for (auto& el : v) {
         out.emplace_back(Clone(el));
@@ -391,6 +394,20 @@
     return *this;
   }
 
+  /// Inserts `object` after any other objects of `vector`, when it is cloned.
+  /// @param vector the vector in #src
+  /// @param object a pointer to the object in #dst that will be inserted at the
+  /// end of the vector
+  /// @returns this CloneContext so calls can be chained
+  template <typename T, typename OBJECT>
+  CloneContext& InsertBack(const std::vector<T>& vector, OBJECT* object) {
+    TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(dst, object);
+    auto& transforms = list_transforms_[&vector];
+    auto& list = transforms.insert_back_;
+    list.emplace_back(object);
+    return *this;
+  }
+
   /// Inserts `object` before `before` whenever `vector` is cloned.
   /// @param vector the vector in #src
   /// @param before a pointer to the object in #src
@@ -502,6 +519,10 @@
     /// cloned.
     CloneableList insert_front_;
 
+    /// A list of objects in #dst to insert befor after any others when the
+    /// vector is cloned.
+    CloneableList insert_back_;
+
     /// A map of object in #src to the list of cloned objects in #dst.
     /// Clone(const std::vector<T*>& v) will use this to insert the map-value
     /// list into the target vector before cloning and inserting the map-key.
diff --git a/src/clone_context_test.cc b/src/clone_context_test.cc
index ccf18a9..bd469da 100644
--- a/src/clone_context_test.cc
+++ b/src/clone_context_test.cc
@@ -401,6 +401,79 @@
   EXPECT_EQ(cloned_root->vec[0]->name, cloned.Symbols().Get("insertion"));
 }
 
+TEST_F(CloneContextNodeTest, CloneWithInsertBack) {
+  Allocator a;
+
+  ProgramBuilder builder;
+  auto* original_root = a.Create<Node>(builder.Symbols().Register("root"));
+  original_root->a = a.Create<Node>(builder.Symbols().Register("a"));
+  original_root->b = a.Create<Node>(builder.Symbols().Register("b"));
+  original_root->c = a.Create<Node>(builder.Symbols().Register("c"));
+  original_root->vec = {original_root->a, original_root->b, original_root->c};
+  Program original(std::move(builder));
+
+  ProgramBuilder cloned;
+  auto* insertion = a.Create<Node>(cloned.Symbols().New("insertion"));
+
+  auto* cloned_root = CloneContext(&cloned, &original)
+                          .InsertBack(original_root->vec, insertion)
+                          .Clone(original_root);
+
+  EXPECT_EQ(cloned_root->vec.size(), 4u);
+
+  EXPECT_EQ(cloned_root->name, cloned.Symbols().Get("root"));
+  EXPECT_EQ(cloned_root->vec[0]->name, cloned.Symbols().Get("a"));
+  EXPECT_EQ(cloned_root->vec[1]->name, cloned.Symbols().Get("b"));
+  EXPECT_EQ(cloned_root->vec[2]->name, cloned.Symbols().Get("c"));
+  EXPECT_EQ(cloned_root->vec[3]->name, cloned.Symbols().Get("insertion"));
+}
+
+TEST_F(CloneContextNodeTest, CloneWithInsertBack_Empty) {
+  Allocator a;
+
+  ProgramBuilder builder;
+  auto* original_root = a.Create<Node>(builder.Symbols().Register("root"));
+  original_root->vec = {};
+  Program original(std::move(builder));
+
+  ProgramBuilder cloned;
+  auto* insertion = a.Create<Node>(cloned.Symbols().New("insertion"));
+
+  auto* cloned_root = CloneContext(&cloned, &original)
+                          .InsertBack(original_root->vec, insertion)
+                          .Clone(original_root);
+
+  EXPECT_EQ(cloned_root->vec.size(), 1u);
+
+  EXPECT_EQ(cloned_root->name, cloned.Symbols().Get("root"));
+  EXPECT_EQ(cloned_root->vec[0]->name, cloned.Symbols().Get("insertion"));
+}
+
+TEST_F(CloneContextNodeTest, CloneWithInsertFrontAndBack_Empty) {
+  Allocator a;
+
+  ProgramBuilder builder;
+  auto* original_root = a.Create<Node>(builder.Symbols().Register("root"));
+  original_root->vec = {};
+  Program original(std::move(builder));
+
+  ProgramBuilder cloned;
+  auto* insertion_front =
+      a.Create<Node>(cloned.Symbols().New("insertion_front"));
+  auto* insertion_back = a.Create<Node>(cloned.Symbols().New("insertion_back"));
+
+  auto* cloned_root = CloneContext(&cloned, &original)
+                          .InsertBack(original_root->vec, insertion_back)
+                          .InsertFront(original_root->vec, insertion_front)
+                          .Clone(original_root);
+
+  EXPECT_EQ(cloned_root->vec.size(), 2u);
+
+  EXPECT_EQ(cloned_root->name, cloned.Symbols().Get("root"));
+  EXPECT_EQ(cloned_root->vec[0]->name, cloned.Symbols().Get("insertion_front"));
+  EXPECT_EQ(cloned_root->vec[1]->name, cloned.Symbols().Get("insertion_back"));
+}
+
 TEST_F(CloneContextNodeTest, CloneWithInsertBefore) {
   Allocator a;