transform: Don't create detached AST nodes

Detached AST nodes will become an ICE, so only create nodes if they are going to be used.

Bug: tint:469
Change-Id: I29b3275a355e30c934e6e508185a19981dcc8a42
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/48050
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/transform/canonicalize_entry_point_io.cc b/src/transform/canonicalize_entry_point_io.cc
index 9f7e881..38f7d68 100644
--- a/src/transform/canonicalize_entry_point_io.cc
+++ b/src/transform/canonicalize_entry_point_io.cc
@@ -16,6 +16,7 @@
 
 #include <algorithm>
 #include <utility>
+#include <vector>
 
 #include "src/program_builder.h"
 #include "src/sem/function.h"
@@ -104,11 +105,11 @@
         auto* param_ty = ctx.src->Sem().Get(param)->Type();
         auto* param_declared_ty = ctx.src->Sem().Get(param)->DeclaredType();
 
-        ast::Expression* func_const_initializer = nullptr;
+        std::function<ast::Expression*()> func_const_initializer;
 
         if (auto* struct_ty = param_ty->As<type::Struct>()) {
           // Pull out all struct members and build initializer list.
-          ast::ExpressionList init_values;
+          std::vector<Symbol> member_names;
           for (auto* member : struct_ty->impl()->members()) {
             if (member->type()->UnwrapAll()->Is<type::Struct>()) {
               TINT_ICE(ctx.dst->Diagnostics()) << "nested pipeline IO struct";
@@ -122,12 +123,19 @@
             auto member_name = ctx.Clone(member->symbol());
             new_struct_members.push_back(ctx.dst->Member(
                 member_name, ctx.Clone(member->type()), new_decorations));
-            init_values.push_back(
-                ctx.dst->MemberAccessor(new_struct_param_symbol, member_name));
+            member_names.emplace_back(member_name);
           }
 
-          func_const_initializer =
-              ctx.dst->Construct(ctx.Clone(param_declared_ty), init_values);
+          func_const_initializer = [&ctx, new_struct_param_symbol,
+                                    param_declared_ty, member_names]() {
+            ast::ExpressionList init_values;
+            for (auto name : member_names) {
+              init_values.push_back(
+                  ctx.dst->MemberAccessor(new_struct_param_symbol, name));
+            }
+            return ctx.dst->Construct(ctx.Clone(param_declared_ty),
+                                      init_values);
+          };
         } else {
           ast::DecorationList new_decorations = RemoveDecorations(
               &ctx, param->decorations(), [](const ast::Decoration* deco) {
@@ -136,8 +144,10 @@
               });
           new_struct_members.push_back(ctx.dst->Member(
               param_name, ctx.Clone(param_declared_ty), new_decorations));
-          func_const_initializer =
-              ctx.dst->MemberAccessor(new_struct_param_symbol, param_name);
+          func_const_initializer = [&ctx, new_struct_param_symbol,
+                                    param_name]() {
+            return ctx.dst->MemberAccessor(new_struct_param_symbol, param_name);
+          };
         }
 
         if (func->body()->empty()) {
@@ -148,7 +158,7 @@
         // Create a function-scope const to replace the parameter.
         // Initialize it with the value extracted from the new struct parameter.
         auto* func_const = ctx.dst->Const(
-            param_name, ctx.Clone(param_declared_ty), func_const_initializer);
+            param_name, ctx.Clone(param_declared_ty), func_const_initializer());
         ctx.InsertBefore(func->body()->statements(), *func->body()->begin(),
                          ctx.dst->WrapInStatement(func_const));
 
diff --git a/src/transform/spirv.cc b/src/transform/spirv.cc
index 411213d..0077198 100644
--- a/src/transform/spirv.cc
+++ b/src/transform/spirv.cc
@@ -271,12 +271,12 @@
   }
 
   // Recurse into struct members and build the initializer list.
-  ast::ExpressionList init_values;
+  std::vector<Symbol> init_value_names;
   auto* struct_ty = ty->As<type::Struct>();
   for (auto* member : struct_ty->impl()->members()) {
     auto member_var = HoistToInputVariables(
         ctx, func, member->type(), member->type(), member->decorations());
-    init_values.push_back(ctx.dst->Expr(member_var));
+    init_value_names.emplace_back(member_var);
   }
 
   auto func_var_symbol = ctx.dst->Symbols().New();
@@ -285,6 +285,11 @@
     return func_var_symbol;
   }
 
+  ast::ExpressionList init_values;
+  for (auto name : init_value_names) {
+    init_values.push_back(ctx.dst->Expr(name));
+  }
+
   // Create a function-scope variable for the struct.
   auto* initializer = ctx.dst->Construct(ctx.Clone(declared_ty), init_values);
   auto* func_var =