Import Tint changes from Dawn

Changes:
  - 6cbdd1d3013d7f24b8b349764eee47b97cf9a0b5 [ir] Track the index of a function parameter by James Price <jrprice@google.com>
  - 9af2b6ff49d992656023ece90c152f4eb1ab7cfc Tint: add InputAttachment type class & definition in wgsl... by Le Hoang Quyen <lehoangquyen@chromium.org>
  - 97b837aa6a215b112d8651da3386348e4a61ce76 Place guard for tint_executable outside of definition by Ryan Harrison <rharrison@chromium.org>
  - f9a9918278ec1142979c6b2d0e43dc3406f66eda Tint: add chromium_internal_input_attachments extension t... by Le Hoang Quyen <lehoangquyen@chromium.org>
  - 7e9f1a6fc5524980fe583bfddf9b0e130b36ad8b [tint][ir] Wrap SymbolTable and type::Manager once by Ben Clayton <bclayton@google.com>
  - 76030517393bd58e742830cd669dd8a29a92f0e3 [tint][core] Assert that Splat and Composite type matches... by Ben Clayton <bclayton@google.com>
  - e4076accc7ce8d21ccc1c50be435818ed8ddec5b [tint] Use templated types for Composite() where possible by Ben Clayton <bclayton@google.com>
  - fb7ee3d5a2d31fa862987dc30ebddae5522909be [tint][core] Splat: Infer count from type by Ben Clayton <bclayton@google.com>
GitOrigin-RevId: 6cbdd1d3013d7f24b8b349764eee47b97cf9a0b5
Change-Id: Ie29de169976b4e9f2378057ea63f5bdee7d2ba6a
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/189660
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: David Neto <dneto@google.com>
Commit-Queue: dan sinclair <dsinclair@google.com>
diff --git a/src/tint/cmd/fuzz/wgsl/dictionary.txt b/src/tint/cmd/fuzz/wgsl/dictionary.txt
index a8a1694..39c4691 100644
--- a/src/tint/cmd/fuzz/wgsl/dictionary.txt
+++ b/src/tint/cmd/fuzz/wgsl/dictionary.txt
@@ -159,6 +159,7 @@
 "chromium_experimental_subgroups"
 "chromium_internal_dual_source_blending"
 "chromium_internal_graphite"
+"chromium_internal_input_attachments"
 "chromium_internal_relaxed_uniform_layout"
 "chromium_testing_experimental"
 "chromium_testing_shipped"
@@ -231,6 +232,7 @@
 "id"
 "if"
 "info"
+"input_attachment"
 "insertBits"
 "instance_index"
 "interpolate"
diff --git a/src/tint/lang/core/constant/composite.cc b/src/tint/lang/core/constant/composite.cc
index 4c157da..fe8ba19 100644
--- a/src/tint/lang/core/constant/composite.cc
+++ b/src/tint/lang/core/constant/composite.cc
@@ -37,7 +37,11 @@
 
 Composite::Composite(const core::type::Type* t, VectorRef<const Value*> els, bool all_0, bool any_0)
     : type(t), elements(std::move(els)), all_zero(all_0), any_zero(any_0), hash(CalcHash()) {
-    TINT_ASSERT(!elements.IsEmpty());
+    const size_t n = elements.Length();
+    TINT_ASSERT(n == t->Elements().count);
+    for (size_t i = 0; i < n; i++) {
+        TINT_ASSERT(t->Element(static_cast<uint32_t>(i)) == elements[i]->Type());
+    }
 }
 
 Composite::~Composite() = default;
diff --git a/src/tint/lang/core/constant/eval.cc b/src/tint/lang/core/constant/eval.cc
index 105ed38..ee896b7 100644
--- a/src/tint/lang/core/constant/eval.cc
+++ b/src/tint/lang/core/constant/eval.cc
@@ -359,7 +359,7 @@
         if (auto* build = std::get_if<ActionBuildSplat>(&next)) {
             TINT_ASSERT(value_stack.Length() >= 1);
             auto* el = value_stack.Pop();
-            value_stack.Push(ctx.mgr.Splat(build->type, el, build->count));
+            value_stack.Push(ctx.mgr.Splat(build->type, el));
             continue;
         }
 
@@ -1331,7 +1331,7 @@
                             VectorRef<const Value*> args,
                             const Source&) {
     if (auto* arg = args[0]) {
-        return mgr.Splat(ty, arg, static_cast<const core::type::Vector*>(ty)->Width());
+        return mgr.Splat(ty, arg);
     }
     return nullptr;
 }
diff --git a/src/tint/lang/core/constant/manager.cc b/src/tint/lang/core/constant/manager.cc
index 69feff2..ecd4a38 100644
--- a/src/tint/lang/core/constant/manager.cc
+++ b/src/tint/lang/core/constant/manager.cc
@@ -65,7 +65,7 @@
     bool all_equal = true;
     auto* first = elements.Front();
     for (auto* el : elements) {
-        if (!el) {
+        if (TINT_UNLIKELY(!el)) {
             return nullptr;
         }
         if (!any_zero && el->AnyZero()) {
@@ -79,16 +79,15 @@
         }
     }
     if (all_equal) {
-        return Splat(type, elements.Front(), elements.Length());
+        return Splat(type, elements.Front());
     }
 
     return Get<constant::Composite>(type, std::move(elements), all_zero, any_zero);
 }
 
 const constant::Splat* Manager::Splat(const core::type::Type* type,
-                                      const constant::Value* element,
-                                      size_t n) {
-    return Get<constant::Splat>(type, element, n);
+                                      const constant::Value* element) {
+    return Get<constant::Splat>(type, element);
 }
 
 const Scalar<i32>* Manager::Get(i32 value) {
@@ -124,16 +123,16 @@
         type,  //
         [&](const core::type::Vector* v) -> const Value* {
             auto* zero_el = Zero(v->type());
-            return Splat(type, zero_el, v->Width());
+            return Splat(type, zero_el);
         },
         [&](const core::type::Matrix* m) -> const Value* {
             auto* zero_el = Zero(m->ColumnType());
-            return Splat(type, zero_el, m->columns());
+            return Splat(type, zero_el);
         },
         [&](const core::type::Array* a) -> const Value* {
-            if (auto n = a->ConstantCount()) {
+            if (a->ConstantCount()) {
                 if (auto* zero_el = Zero(a->ElemType())) {
-                    return Splat(type, zero_el, n.value());
+                    return Splat(type, zero_el);
                 }
             }
             return nullptr;
@@ -152,7 +151,7 @@
             }
             if (zero_by_type.Count() == 1) {
                 // All members were of the same type, so the zero value is the same for all members.
-                return Splat(type, zeros[0], s->Members().Length());
+                return Splat(type, zeros[0]);
             }
             return Composite(s, std::move(zeros));
         },
diff --git a/src/tint/lang/core/constant/manager.h b/src/tint/lang/core/constant/manager.h
index 2eb5088..43497db 100644
--- a/src/tint/lang/core/constant/manager.h
+++ b/src/tint/lang/core/constant/manager.h
@@ -108,11 +108,8 @@
     /// Constructs a splat constant.
     /// @param type the splat type
     /// @param element the splat element
-    /// @param n the number of elements
     /// @returns the value pointer
-    const constant::Splat* Splat(const core::type::Type* type,
-                                 const constant::Value* element,
-                                 size_t n);
+    const constant::Splat* Splat(const core::type::Type* type, const constant::Value* element);
 
     /// @param value the constant value
     /// @return a Scalar holding the i32 value @p value
diff --git a/src/tint/lang/core/constant/splat.cc b/src/tint/lang/core/constant/splat.cc
index 07d8fbb..187d4c8 100644
--- a/src/tint/lang/core/constant/splat.cc
+++ b/src/tint/lang/core/constant/splat.cc
@@ -33,15 +33,29 @@
 
 namespace tint::core::constant {
 
-Splat::Splat(const core::type::Type* t, const constant::Value* e, size_t n)
-    : type(t), el(e), count(n) {}
+namespace {
+
+/// Asserts that the element type of @p in_type matches the type of @p value, and that the type has
+/// at least one element.
+/// @returns the number of elements in @p in_type
+inline size_t GetCountAndAssertType(const core::type::Type* in_type, const constant::Value* value) {
+    auto elements = in_type->Elements();
+    TINT_ASSERT(!elements.type || elements.type == value->Type());
+    TINT_ASSERT(elements.count > 0);
+    return elements.count;
+}
+
+}  // namespace
+
+Splat::Splat(const core::type::Type* t, const constant::Value* e)
+    : type(t), el(e), count(GetCountAndAssertType(t, e)) {}
 
 Splat::~Splat() = default;
 
 const Splat* Splat::Clone(CloneContext& ctx) const {
     auto* ty = type->Clone(ctx.type_ctx);
     auto* element = el->Clone(ctx);
-    return ctx.dst.Splat(ty, element, count);
+    return ctx.dst.Splat(ty, element);
 }
 
 }  // namespace tint::core::constant
diff --git a/src/tint/lang/core/constant/splat.h b/src/tint/lang/core/constant/splat.h
index bf7a0c4..def9bfe 100644
--- a/src/tint/lang/core/constant/splat.h
+++ b/src/tint/lang/core/constant/splat.h
@@ -44,8 +44,7 @@
     /// Constructor
     /// @param t the splat type
     /// @param e the splat element
-    /// @param n the number of items in the splat
-    Splat(const core::type::Type* t, const Value* e, size_t n);
+    Splat(const core::type::Type* t, const Value* e);
     ~Splat() override;
 
     /// @returns the type of the splat
diff --git a/src/tint/lang/core/constant/splat_test.cc b/src/tint/lang/core/constant/splat_test.cc
index d45f614..553cf14 100644
--- a/src/tint/lang/core/constant/splat_test.cc
+++ b/src/tint/lang/core/constant/splat_test.cc
@@ -46,9 +46,9 @@
     auto* fNeg0 = constants.Get(-0_f);
     auto* fPos1 = constants.Get(1_f);
 
-    auto* SpfPos0 = constants.Splat(vec3f, fPos0, 3);
-    auto* SpfNeg0 = constants.Splat(vec3f, fNeg0, 3);
-    auto* SpfPos1 = constants.Splat(vec3f, fPos1, 3);
+    auto* SpfPos0 = constants.Splat(vec3f, fPos0);
+    auto* SpfNeg0 = constants.Splat(vec3f, fNeg0);
+    auto* SpfPos1 = constants.Splat(vec3f, fPos1);
 
     EXPECT_TRUE(SpfPos0->AllZero());
     EXPECT_TRUE(SpfNeg0->AllZero());
@@ -62,9 +62,9 @@
     auto* fNeg0 = constants.Get(-0_f);
     auto* fPos1 = constants.Get(1_f);
 
-    auto* SpfPos0 = constants.Splat(vec3f, fPos0, 3);
-    auto* SpfNeg0 = constants.Splat(vec3f, fNeg0, 3);
-    auto* SpfPos1 = constants.Splat(vec3f, fPos1, 3);
+    auto* SpfPos0 = constants.Splat(vec3f, fPos0);
+    auto* SpfNeg0 = constants.Splat(vec3f, fNeg0);
+    auto* SpfPos1 = constants.Splat(vec3f, fPos1);
 
     EXPECT_TRUE(SpfPos0->AnyZero());
     EXPECT_TRUE(SpfNeg0->AnyZero());
@@ -75,20 +75,21 @@
     auto* vec3f = create<core::type::Vector>(create<core::type::F32>(), 3u);
 
     auto* f1 = constants.Get(1_f);
-    auto* sp = constants.Splat(vec3f, f1, 2);
+    auto* sp = constants.Splat(vec3f, f1);
 
     ASSERT_NE(sp->Index(0), nullptr);
     ASSERT_NE(sp->Index(1), nullptr);
-    ASSERT_EQ(sp->Index(2), nullptr);
+    ASSERT_NE(sp->Index(2), nullptr);
 
     EXPECT_EQ(sp->Index(0)->As<Scalar<f32>>()->ValueOf(), 1.f);
     EXPECT_EQ(sp->Index(1)->As<Scalar<f32>>()->ValueOf(), 1.f);
+    EXPECT_EQ(sp->Index(2)->As<Scalar<f32>>()->ValueOf(), 1.f);
 }
 
 TEST_F(ConstantTest_Splat, Clone) {
-    auto* vec3i = create<core::type::Vector>(create<core::type::I32>(), 3u);
+    auto* vec2i = create<core::type::Vector>(create<core::type::I32>(), 2u);
     auto* val = constants.Get(12_i);
-    auto* sp = constants.Splat(vec3i, val, 2);
+    auto* sp = constants.Splat(vec2i, val);
 
     constant::Manager mgr;
     constant::CloneContext ctx{core::type::CloneContext{{nullptr}, {nullptr, &mgr.types}}, mgr};
diff --git a/src/tint/lang/core/constant/value_test.cc b/src/tint/lang/core/constant/value_test.cc
index ad0c297..6bb31fd 100644
--- a/src/tint/lang/core/constant/value_test.cc
+++ b/src/tint/lang/core/constant/value_test.cc
@@ -54,8 +54,8 @@
 TEST_F(ConstantTest_Value, Equal_Splat_Splat) {
     auto* vec3f = create<core::type::Vector>(create<core::type::F32>(), 3u);
 
-    auto* vec3f_1_1_1 = constants.Splat(vec3f, constants.Get(1_f), 3);
-    auto* vec3f_2_2_2 = constants.Splat(vec3f, constants.Get(2_f), 3);
+    auto* vec3f_1_1_1 = constants.Splat(vec3f, constants.Get(1_f));
+    auto* vec3f_2_2_2 = constants.Splat(vec3f, constants.Get(2_f));
 
     EXPECT_TRUE(vec3f_1_1_1->Equal(vec3f_1_1_1));
     EXPECT_FALSE(vec3f_2_2_2->Equal(vec3f_1_1_1));
@@ -78,7 +78,7 @@
 TEST_F(ConstantTest_Value, Equal_Splat_Composite) {
     auto* vec3f = create<core::type::Vector>(create<core::type::F32>(), 3u);
 
-    auto* vec3f_1_1_1 = constants.Splat(vec3f, constants.Get(1_f), 3);
+    auto* vec3f_1_1_1 = constants.Splat(vec3f, constants.Get(1_f));
     auto* vec3f_1_2_1 = constants.Composite(
         vec3f, Vector{constants.Get(1_f), constants.Get(2_f), constants.Get(1_f)});
 
diff --git a/src/tint/lang/core/intrinsic/type_matchers.h b/src/tint/lang/core/intrinsic/type_matchers.h
index 349c2d4..cf04b7a 100644
--- a/src/tint/lang/core/intrinsic/type_matchers.h
+++ b/src/tint/lang/core/intrinsic/type_matchers.h
@@ -43,6 +43,7 @@
 #include "src/tint/lang/core/type/f16.h"
 #include "src/tint/lang/core/type/f32.h"
 #include "src/tint/lang/core/type/i32.h"
+#include "src/tint/lang/core/type/input_attachment.h"
 #include "src/tint/lang/core/type/manager.h"
 #include "src/tint/lang/core/type/matrix.h"
 #include "src/tint/lang/core/type/multisampled_texture.h"
@@ -532,6 +533,26 @@
     return state.types.Get<type::ExternalTexture>();
 }
 
+inline bool MatchInputAttachment(intrinsic::MatchState&,
+                                 const type::Type* ty,
+                                 const type::Type*& T) {
+    if (ty->Is<intrinsic::Any>()) {
+        T = ty;
+        return true;
+    }
+    if (auto* v = ty->As<type::InputAttachment>()) {
+        T = v->type();
+        return true;
+    }
+    return false;
+}
+
+inline const type::InputAttachment* BuildInputAttachment(intrinsic::MatchState& state,
+                                                         const type::Type*,
+                                                         const type::Type* T) {
+    return state.types.Get<type::InputAttachment>(T);
+}
+
 // Builtin types starting with a _ prefix cannot be declared in WGSL, so they
 // can only be used as return types. Because of this, they must only match Any,
 // which is used as the return type matcher.
diff --git a/src/tint/lang/core/ir/binary/decode.cc b/src/tint/lang/core/ir/binary/decode.cc
index c422433..780afd2 100644
--- a/src/tint/lang/core/ir/binary/decode.cc
+++ b/src/tint/lang/core/ir/binary/decode.cc
@@ -836,7 +836,7 @@
     const core::constant::Value* CreateConstantSplat(const pb::ConstantValueSplat& splat_in) {
         auto* type = Type(splat_in.type());
         auto* elem = ConstantValue(splat_in.elements());
-        return mod_out_.constant_values.Splat(type, elem, splat_in.count());
+        return mod_out_.constant_values.Splat(type, elem);
     }
 
     const core::constant::Value* ConstantValue(uint32_t id) {
diff --git a/src/tint/lang/core/ir/binary/roundtrip_test.cc b/src/tint/lang/core/ir/binary/roundtrip_test.cc
index 76f92b8..0dee1d2 100644
--- a/src/tint/lang/core/ir/binary/roundtrip_test.cc
+++ b/src/tint/lang/core/ir/binary/roundtrip_test.cc
@@ -396,32 +396,34 @@
 
 TEST_F(IRBinaryRoundtripTest, Return_vec3f_Splat) {
     auto* fn = b.Function("Function", ty.vec3<f32>());
-    b.Append(fn->Block(), [&] { b.Return(fn, b.Splat<vec3<f32>>(1_f, 3)); });
+    b.Append(fn->Block(), [&] { b.Return(fn, b.Splat<vec3<f32>>(1_f)); });
     RUN_TEST();
 }
 
 TEST_F(IRBinaryRoundtripTest, Return_mat2x3f_Composite) {
     auto* fn = b.Function("Function", ty.mat2x3<f32>());
-    b.Append(fn->Block(),
-             [&] { b.Return(fn, b.Composite<mat2x3<f32>>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f)); });
+    b.Append(fn->Block(), [&] {
+        b.Return(fn, b.Composite<mat2x3<f32>>(b.Composite<vec3<f32>>(1_f, 2_f, 3_f),
+                                              b.Composite<vec3<f32>>(4_f, 5_f, 6_f)));
+    });
     RUN_TEST();
 }
 
 TEST_F(IRBinaryRoundtripTest, Return_mat2x3f_Splat) {
     auto* fn = b.Function("Function", ty.mat2x3<f32>());
-    b.Append(fn->Block(), [&] { b.Return(fn, b.Splat<mat2x3<f32>>(1_f, 6)); });
+    b.Append(fn->Block(), [&] { b.Return(fn, b.Splat<mat2x3<f32>>(b.Splat<vec3<f32>>(1_f))); });
     RUN_TEST();
 }
 
 TEST_F(IRBinaryRoundtripTest, Return_array_f32_Composite) {
     auto* fn = b.Function("Function", ty.array<f32, 3>());
-    b.Append(fn->Block(), [&] { b.Return(fn, b.Composite<array<f32, 3>>(1_i, 2_i, 3_i)); });
+    b.Append(fn->Block(), [&] { b.Return(fn, b.Composite<array<f32, 3>>(1_f, 2_f, 3_f)); });
     RUN_TEST();
 }
 
 TEST_F(IRBinaryRoundtripTest, Return_array_f32_Splat) {
     auto* fn = b.Function("Function", ty.array<f32, 3>());
-    b.Append(fn->Block(), [&] { b.Return(fn, b.Splat<array<f32, 3>>(1_i, 3)); });
+    b.Append(fn->Block(), [&] { b.Return(fn, b.Splat<array<f32, 3>>(1_f)); });
     RUN_TEST();
 }
 
diff --git a/src/tint/lang/core/ir/builder.h b/src/tint/lang/core/ir/builder.h
index 240afc1..e3b797b 100644
--- a/src/tint/lang/core/ir/builder.h
+++ b/src/tint/lang/core/ir/builder.h
@@ -397,23 +397,20 @@
     /// Creates a new ir::Constant
     /// @param ty the splat type
     /// @param value the splat value
-    /// @param size the number of items
     /// @returns the new constant
     template <typename ARG>
-    ir::Constant* Splat(const core::type::Type* ty, ARG&& value, size_t size) {
-        return Constant(
-            ir.constant_values.Splat(ty, ConstantValue(std::forward<ARG>(value)), size));
+    ir::Constant* Splat(const core::type::Type* ty, ARG&& value) {
+        return Constant(ir.constant_values.Splat(ty, ConstantValue(std::forward<ARG>(value))));
     }
 
     /// Creates a new ir::Constant
     /// @tparam TYPE the splat type
     /// @param value the splat value
-    /// @param size the number of items
     /// @returns the new constant
     template <typename TYPE, typename ARG>
-    ir::Constant* Splat(ARG&& value, size_t size) {
+    ir::Constant* Splat(ARG&& value) {
         auto* type = ir.Types().Get<TYPE>();
-        return Splat(type, std::forward<ARG>(value), size);
+        return Splat(type, std::forward<ARG>(value));
     }
 
     /// Creates a new ir::Constant
@@ -918,7 +915,7 @@
     template <typename VAL>
     ir::CoreBinary* Not(const core::type::Type* type, VAL&& val) {
         if (auto* vec = type->As<core::type::Vector>()) {
-            return Equal(type, std::forward<VAL>(val), Splat(vec, false, vec->Width()));
+            return Equal(type, std::forward<VAL>(val), Splat(vec, false));
         } else {
             return Equal(type, std::forward<VAL>(val), Constant(false));
         }
diff --git a/src/tint/lang/core/ir/function.cc b/src/tint/lang/core/ir/function.cc
index f8a3e53..2cce808 100644
--- a/src/tint/lang/core/ir/function.cc
+++ b/src/tint/lang/core/ir/function.cc
@@ -71,8 +71,10 @@
     }
     params_ = std::move(params);
     TINT_ASSERT(!params_.Any(IsNull));
+    uint32_t index = 0;
     for (auto* param : params_) {
         param->SetFunction(this);
+        param->SetIndex(index++);
     }
 }
 
@@ -82,14 +84,17 @@
     }
     params_ = params;
     TINT_ASSERT(!params_.Any(IsNull));
+    uint32_t index = 0;
     for (auto* param : params_) {
         param->SetFunction(this);
+        param->SetIndex(index++);
     }
 }
 
 void Function::AppendParam(FunctionParam* param) {
     params_.Push(param);
     param->SetFunction(this);
+    param->SetIndex(static_cast<uint32_t>(params_.Length() - 1u));
 }
 
 void Function::Destroy() {
diff --git a/src/tint/lang/core/ir/function_param.h b/src/tint/lang/core/ir/function_param.h
index 14d8f92..c9a0cea 100644
--- a/src/tint/lang/core/ir/function_param.h
+++ b/src/tint/lang/core/ir/function_param.h
@@ -63,6 +63,13 @@
     /// @returns the function that this parameter belongs to, or nullptr
     const ir::Function* Function() const { return func_; }
 
+    /// Sets the index of the parameter in the function's parameter list
+    /// @param index the index
+    void SetIndex(uint32_t index) { index_ = index; }
+
+    /// @returns the index of the parameter in the function's parameter list
+    uint32_t Index() const { return index_; }
+
     /// @returns the type of the var
     const core::type::Type* Type() const override { return type_; }
 
@@ -121,6 +128,7 @@
 
   private:
     ir::Function* func_ = nullptr;
+    uint32_t index_ = 0xffffffff;
     const core::type::Type* type_ = nullptr;
     std::optional<core::BuiltinValue> builtin_;
     std::optional<struct Location> location_;
diff --git a/src/tint/lang/core/ir/function_test.cc b/src/tint/lang/core/ir/function_test.cc
index 9f0161a..eda7fdd 100644
--- a/src/tint/lang/core/ir/function_test.cc
+++ b/src/tint/lang/core/ir/function_test.cc
@@ -154,16 +154,23 @@
     EXPECT_EQ(param1->Function(), f);
     EXPECT_EQ(param2->Function(), f);
     EXPECT_EQ(param3->Function(), nullptr);
+    EXPECT_EQ(param1->Index(), 0u);
+    EXPECT_EQ(param2->Index(), 1u);
 
     f->SetParams({param1, param3});
     EXPECT_EQ(param1->Function(), f);
     EXPECT_EQ(param2->Function(), nullptr);
     EXPECT_EQ(param3->Function(), f);
+    EXPECT_EQ(param1->Index(), 0u);
+    EXPECT_EQ(param3->Index(), 1u);
 
     f->AppendParam(param2);
     EXPECT_EQ(param1->Function(), f);
     EXPECT_EQ(param2->Function(), f);
     EXPECT_EQ(param3->Function(), f);
+    EXPECT_EQ(param1->Index(), 0u);
+    EXPECT_EQ(param3->Index(), 1u);
+    EXPECT_EQ(param2->Index(), 2u);
 }
 
 }  // namespace
diff --git a/src/tint/lang/core/ir/transform/binary_polyfill.cc b/src/tint/lang/core/ir/transform/binary_polyfill.cc
index a5aec3c..e74a828 100644
--- a/src/tint/lang/core/ir/transform/binary_polyfill.cc
+++ b/src/tint/lang/core/ir/transform/binary_polyfill.cc
@@ -126,7 +126,7 @@
     /// @returns a value with the same number of vector components as @p match
     ir::Constant* MatchWidth(ir::Constant* element, const core::type::Type* match) {
         if (auto* vec = match->As<core::type::Vector>()) {
-            return b.Splat(MatchWidth(element->Type(), match), element, vec->Width());
+            return b.Splat(MatchWidth(element->Type(), match), element);
         }
         return element;
     }
diff --git a/src/tint/lang/core/ir/transform/builtin_polyfill.cc b/src/tint/lang/core/ir/transform/builtin_polyfill.cc
index ef68d5b..f62c7c6 100644
--- a/src/tint/lang/core/ir/transform/builtin_polyfill.cc
+++ b/src/tint/lang/core/ir/transform/builtin_polyfill.cc
@@ -225,7 +225,7 @@
     /// @returns a value with the same number of vector components as @p match
     ir::Constant* MatchWidth(ir::Constant* element, const core::type::Type* match) {
         if (auto* vec = match->As<core::type::Vector>()) {
-            return b.Splat(MatchWidth(element->Type(), match), element, vec->Width());
+            return b.Splat(MatchWidth(element->Type(), match), element);
         }
         return element;
     }
@@ -575,13 +575,12 @@
         auto* sampler = call->Args()[1];
         auto* coords = call->Args()[2];
         b.InsertBefore(call, [&] {
-            auto* vec2f = ty.vec2<f32>();
-            auto* dims = b.Call(ty.vec2<u32>(), core::BuiltinFn::kTextureDimensions, texture);
-            auto* fdims = b.Convert(vec2f, dims);
-            auto* half_texel = b.Divide(vec2f, b.Splat(vec2f, 0.5_f, 2), fdims);
-            auto* one_minus_half_texel = b.Subtract(vec2f, b.Splat(vec2f, 1_f, 2), half_texel);
-            auto* clamped =
-                b.Call(vec2f, core::BuiltinFn::kClamp, coords, half_texel, one_minus_half_texel);
+            auto* dims = b.Call<vec2<u32>>(core::BuiltinFn::kTextureDimensions, texture);
+            auto* fdims = b.Convert<vec2<f32>>(dims);
+            auto* half_texel = b.Divide<vec2<f32>>(b.Splat<vec2<f32>>(0.5_f), fdims);
+            auto* one_minus_half_texel = b.Subtract<vec2<f32>>(b.Splat<vec2<f32>>(1_f), half_texel);
+            auto* clamped = b.Call<vec2<f32>>(core::BuiltinFn::kClamp, coords, half_texel,
+                                              one_minus_half_texel);
             b.CallWithResult(call->DetachResult(), core::BuiltinFn::kTextureSampleLevel, texture,
                              sampler, clamped, 0_f);
         });
diff --git a/src/tint/lang/core/ir/transform/conversion_polyfill.cc b/src/tint/lang/core/ir/transform/conversion_polyfill.cc
index 76d1920..36ea764 100644
--- a/src/tint/lang/core/ir/transform/conversion_polyfill.cc
+++ b/src/tint/lang/core/ir/transform/conversion_polyfill.cc
@@ -207,7 +207,7 @@
     /// @returns a value with the same number of vector components as @p match
     ir::Constant* MatchWidth(ir::Constant* element, const core::type::Type* match) {
         if (auto* vec = match->As<core::type::Vector>()) {
-            return b.Splat(MatchWidth(element->Type(), match), element, vec->Width());
+            return b.Splat(MatchWidth(element->Type(), match), element);
         }
         return element;
     }
diff --git a/src/tint/lang/core/ir/transform/demote_to_helper_test.cc b/src/tint/lang/core/ir/transform/demote_to_helper_test.cc
index e26ff8d..db15893 100644
--- a/src/tint/lang/core/ir/transform/demote_to_helper_test.cc
+++ b/src/tint/lang/core/ir/transform/demote_to_helper_test.cc
@@ -550,7 +550,7 @@
             b.ExitIf(ifelse);
         });
         b.Call(ty.void_(), core::BuiltinFn::kTextureStore, b.Load(texture), coord,
-               b.Splat(b.ir.Types().vec4<f32>(), 0.5_f, 4));
+               b.Splat(b.ir.Types().vec4<f32>(), 0.5_f));
         b.Return(ep, 0.5_f);
     });
 
diff --git a/src/tint/lang/core/ir/transform/direct_variable_access_test.cc b/src/tint/lang/core/ir/transform/direct_variable_access_test.cc
index cf464d3..fd89e61 100644
--- a/src/tint/lang/core/ir/transform/direct_variable_access_test.cc
+++ b/src/tint/lang/core/ir/transform/direct_variable_access_test.cc
@@ -1393,7 +1393,7 @@
         b.FunctionParam("post", ty.i32()),
     });
     b.Append(fn_a->Block(), [&] {
-        b.Store(fn_a_p, b.Splat(ty.array<i32, 4>(), 0_i, 4));
+        b.Store(fn_a_p, b.Splat<array<i32, 4>>(0_i));
         b.Return(fn_a);
     });
 
@@ -1475,7 +1475,7 @@
         b.FunctionParam("post", ty.i32()),
     });
     b.Append(fn_a->Block(), [&] {
-        b.Store(fn_a_p, b.Splat(ty.vec4<i32>(), 0_i, 4));
+        b.Store(fn_a_p, b.Splat<vec4<i32>>(0_i));
         b.Return(fn_a);
     });
 
@@ -2068,7 +2068,7 @@
         b.FunctionParam("post", ty.i32()),
     });
     b.Append(fn_a->Block(), [&] {
-        b.Store(fn_a_p, b.Splat(ty.vec4<i32>(), 0_i, 4));
+        b.Store(fn_a_p, b.Splat<vec4<i32>>(0_i));
         b.Return(fn_a);
     });
 
@@ -2870,7 +2870,7 @@
         b.FunctionParam("post", ty.i32()),
     });
     b.Append(fn_a->Block(), [&] {
-        b.Store(fn_a_p, b.Splat(ty.array<i32, 4>(), 0_i, 4));
+        b.Store(fn_a_p, b.Splat<array<i32, 4>>(0_i));
         b.Return(fn_a);
     });
 
@@ -2956,7 +2956,7 @@
         b.FunctionParam("post", ty.i32()),
     });
     b.Append(fn_a->Block(), [&] {
-        b.Store(fn_a_p, b.Splat(ty.array<i32, 4>(), 0_i, 4));
+        b.Store(fn_a_p, b.Splat<array<i32, 4>>(0_i));
         b.Return(fn_a);
     });
 
@@ -4138,7 +4138,7 @@
         b.FunctionParam("post", ty.i32()),
     });
     b.Append(fn_a->Block(), [&] {
-        b.Store(fn_a_p, b.Splat(ty.array<i32, 4>(), 0_i, 4));
+        b.Store(fn_a_p, b.Splat<array<i32, 4>>(0_i));
         b.Return(fn_a);
     });
 
@@ -4373,7 +4373,7 @@
         b.FunctionParam("post", ty.i32()),
     });
     b.Append(fn_a->Block(), [&] {
-        b.Store(fn_a_p, b.Splat(ty.array<i32, 4>(), 0_i, 4));
+        b.Store(fn_a_p, b.Splat<array<i32, 4>>(0_i));
         b.Return(fn_a);
     });
 
diff --git a/src/tint/lang/core/ir/transform/robustness.cc b/src/tint/lang/core/ir/transform/robustness.cc
index 9284b9a..56f2839 100644
--- a/src/tint/lang/core/ir/transform/robustness.cc
+++ b/src/tint/lang/core/ir/transform/robustness.cc
@@ -292,7 +292,7 @@
             auto* one = b.Constant(1_u);
             if (auto* vec = args[idx]->Type()->As<type::Vector>()) {
                 type = ty.vec(type, vec->Width());
-                one = b.Splat(type, one, vec->Width());
+                one = b.Splat(type, one);
             }
             auto* dims = clamped_level ? b.Call(type, core::BuiltinFn::kTextureDimensions, args[0],
                                                 clamped_level)
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc
index b72475c..4a9a866 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -491,6 +491,8 @@
     Vector<const Block*, 8> block_stack_;
     ScopeStack scope_stack_;
     Vector<std::function<void()>, 16> tasks_;
+    SymbolTable symbols_ = SymbolTable::Wrap(mod_.symbols);
+    type::Manager type_mgr_ = type::Manager::Wrap(mod_.Types());
 };
 
 Validator::Validator(const Module& mod, Capabilities capabilities)
@@ -963,14 +965,11 @@
 }
 
 void Validator::CheckBuiltinCall(const BuiltinCall* call) {
-    auto symbols = SymbolTable::Wrap(mod_.symbols);
-    auto type_mgr = type::Manager::Wrap(mod_.Types());
-
     auto args = Transform<8>(call->Args(), [&](const ir::Value* v) { return v->Type(); });
     intrinsic::Context context{
         call->TableData(),
-        type_mgr,
-        symbols,
+        type_mgr_,
+        symbols_,
     };
 
     auto result = core::intrinsic::LookupFn(context, call->FriendlyName().c_str(), call->FuncId(),
@@ -1118,9 +1117,7 @@
 void Validator::CheckBinary(const Binary* b) {
     CheckOperandsNotNull(b, Binary::kLhsOperandOffset, Binary::kRhsOperandOffset);
     if (b->LHS() && b->RHS()) {
-        auto symbols = SymbolTable::Wrap(mod_.symbols);
-        auto type_mgr = type::Manager::Wrap(mod_.Types());
-        intrinsic::Context context{b->TableData(), type_mgr, symbols};
+        intrinsic::Context context{b->TableData(), type_mgr_, symbols_};
 
         auto overload =
             core::intrinsic::LookupBinary(context, b->Op(), b->LHS()->Type(), b->RHS()->Type(),
@@ -1144,9 +1141,7 @@
 void Validator::CheckUnary(const Unary* u) {
     CheckOperandNotNull(u, u->Val(), Unary::kValueOperandOffset);
     if (u->Val()) {
-        auto symbols = SymbolTable::Wrap(mod_.symbols);
-        auto type_mgr = type::Manager::Wrap(mod_.Types());
-        intrinsic::Context context{u->TableData(), type_mgr, symbols};
+        intrinsic::Context context{u->TableData(), type_mgr_, symbols_};
 
         auto overload = core::intrinsic::LookupUnary(context, u->Op(), u->Val()->Type(),
                                                      core::EvaluationStage::kRuntime);
diff --git a/src/tint/lang/core/type/BUILD.bazel b/src/tint/lang/core/type/BUILD.bazel
index 35a5221..5682974 100644
--- a/src/tint/lang/core/type/BUILD.bazel
+++ b/src/tint/lang/core/type/BUILD.bazel
@@ -53,6 +53,7 @@
     "f16.cc",
     "f32.cc",
     "i32.cc",
+    "input_attachment.cc",
     "invalid.cc",
     "manager.cc",
     "matrix.cc",
@@ -92,6 +93,7 @@
     "f16.h",
     "f32.h",
     "i32.h",
+    "input_attachment.h",
     "invalid.h",
     "manager.h",
     "matrix.h",
@@ -149,6 +151,7 @@
     "f32_test.cc",
     "helper_test.h",
     "i32_test.cc",
+    "input_attachment_test.cc",
     "manager_test.cc",
     "matrix_test.cc",
     "multisampled_texture_test.cc",
diff --git a/src/tint/lang/core/type/BUILD.cmake b/src/tint/lang/core/type/BUILD.cmake
index 285a088..be27de5 100644
--- a/src/tint/lang/core/type/BUILD.cmake
+++ b/src/tint/lang/core/type/BUILD.cmake
@@ -68,6 +68,8 @@
   lang/core/type/f32.h
   lang/core/type/i32.cc
   lang/core/type/i32.h
+  lang/core/type/input_attachment.cc
+  lang/core/type/input_attachment.h
   lang/core/type/invalid.cc
   lang/core/type/invalid.h
   lang/core/type/manager.cc
@@ -147,6 +149,7 @@
   lang/core/type/f32_test.cc
   lang/core/type/helper_test.h
   lang/core/type/i32_test.cc
+  lang/core/type/input_attachment_test.cc
   lang/core/type/manager_test.cc
   lang/core/type/matrix_test.cc
   lang/core/type/multisampled_texture_test.cc
diff --git a/src/tint/lang/core/type/BUILD.gn b/src/tint/lang/core/type/BUILD.gn
index 7f64802..c635ccf 100644
--- a/src/tint/lang/core/type/BUILD.gn
+++ b/src/tint/lang/core/type/BUILD.gn
@@ -73,6 +73,8 @@
     "f32.h",
     "i32.cc",
     "i32.h",
+    "input_attachment.cc",
+    "input_attachment.h",
     "invalid.cc",
     "invalid.h",
     "manager.cc",
@@ -149,6 +151,7 @@
       "f32_test.cc",
       "helper_test.h",
       "i32_test.cc",
+      "input_attachment_test.cc",
       "manager_test.cc",
       "matrix_test.cc",
       "multisampled_texture_test.cc",
diff --git a/src/tint/lang/core/type/depth_texture_test.cc b/src/tint/lang/core/type/depth_texture_test.cc
index 849dcc1..771d8f7 100644
--- a/src/tint/lang/core/type/depth_texture_test.cc
+++ b/src/tint/lang/core/type/depth_texture_test.cc
@@ -29,6 +29,7 @@
 
 #include "src/tint/lang/core/type/external_texture.h"
 #include "src/tint/lang/core/type/helper_test.h"
+#include "src/tint/lang/core/type/input_attachment.h"
 #include "src/tint/lang/core/type/sampled_texture.h"
 #include "src/tint/lang/core/type/storage_texture.h"
 #include "src/tint/lang/core/type/texture_dimension.h"
@@ -69,6 +70,7 @@
     Texture* ty = &d;
     EXPECT_TRUE(ty->Is<DepthTexture>());
     EXPECT_FALSE(ty->Is<ExternalTexture>());
+    EXPECT_FALSE(ty->Is<InputAttachment>());
     EXPECT_FALSE(ty->Is<SampledTexture>());
     EXPECT_FALSE(ty->Is<StorageTexture>());
 }
diff --git a/src/tint/lang/core/type/external_texture_test.cc b/src/tint/lang/core/type/external_texture_test.cc
index d798b90..d856dd1 100644
--- a/src/tint/lang/core/type/external_texture_test.cc
+++ b/src/tint/lang/core/type/external_texture_test.cc
@@ -29,6 +29,7 @@
 
 #include "src/tint/lang/core/type/depth_texture.h"
 #include "src/tint/lang/core/type/helper_test.h"
+#include "src/tint/lang/core/type/input_attachment.h"
 #include "src/tint/lang/core/type/multisampled_texture.h"
 #include "src/tint/lang/core/type/sampled_texture.h"
 #include "src/tint/lang/core/type/storage_texture.h"
@@ -64,6 +65,7 @@
     Texture* ty = &s;
     EXPECT_FALSE(ty->Is<DepthTexture>());
     EXPECT_TRUE(ty->Is<ExternalTexture>());
+    EXPECT_FALSE(ty->Is<InputAttachment>());
     EXPECT_FALSE(ty->Is<MultisampledTexture>());
     EXPECT_FALSE(ty->Is<SampledTexture>());
     EXPECT_FALSE(ty->Is<StorageTexture>());
diff --git a/src/tint/lang/core/type/input_attachment.cc b/src/tint/lang/core/type/input_attachment.cc
new file mode 100644
index 0000000..6697e33
--- /dev/null
+++ b/src/tint/lang/core/type/input_attachment.cc
@@ -0,0 +1,67 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/tint/lang/core/type/input_attachment.h"
+
+#include "src/tint/lang/core/type/manager.h"
+#include "src/tint/lang/core/type/texture_dimension.h"
+#include "src/tint/utils/diagnostic/diagnostic.h"
+#include "src/tint/utils/ice/ice.h"
+#include "src/tint/utils/math/hash.h"
+#include "src/tint/utils/text/string_stream.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::core::type::InputAttachment);
+
+namespace tint::core::type {
+
+InputAttachment::InputAttachment(const Type* type)
+    : Base(Hash(TypeCode::Of<InputAttachment>().bits, type), TextureDimension::k2d), type_(type) {
+    TINT_ASSERT(type_);
+}
+
+InputAttachment::~InputAttachment() = default;
+
+bool InputAttachment::Equals(const UniqueNode& other) const {
+    if (auto* o = other.As<InputAttachment>()) {
+        return o->type_ == type_;
+    }
+    return false;
+}
+
+std::string InputAttachment::FriendlyName() const {
+    StringStream out;
+    out << "input_attachment"
+        << "<" << type_->FriendlyName() << ">";
+    return out.str();
+}
+
+InputAttachment* InputAttachment::Clone(CloneContext& ctx) const {
+    auto* ty = type_->Clone(ctx);
+    return ctx.dst.mgr->Get<InputAttachment>(ty);
+}
+
+}  // namespace tint::core::type
diff --git a/src/tint/lang/core/type/input_attachment.h b/src/tint/lang/core/type/input_attachment.h
new file mode 100644
index 0000000..7feae62
--- /dev/null
+++ b/src/tint/lang/core/type/input_attachment.h
@@ -0,0 +1,67 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_TINT_LANG_CORE_TYPE_INPUT_ATTACHMENT_H_
+#define SRC_TINT_LANG_CORE_TYPE_INPUT_ATTACHMENT_H_
+
+#include <string>
+
+#include "src/tint/lang/core/type/texture.h"
+
+namespace tint::core::type {
+
+/// An input attachment type.
+class InputAttachment final : public Castable<InputAttachment, Texture> {
+  public:
+    /// Constructor
+    /// @param type the data type of the input attachment
+    explicit InputAttachment(const Type* type);
+    /// Destructor
+    ~InputAttachment() override;
+
+    /// @param other the other node to compare against
+    /// @returns true if the this type is equal to @p other
+    bool Equals(const UniqueNode& other) const override;
+
+    /// @returns the subtype of the input attachment
+    Type* type() const { return const_cast<Type*>(type_); }
+
+    /// @returns the name for this type that closely resembles how it would be
+    /// declared in WGSL.
+    std::string FriendlyName() const override;
+
+    /// @param ctx the clone context
+    /// @returns a clone of this type
+    InputAttachment* Clone(CloneContext& ctx) const override;
+
+  private:
+    const Type* const type_;
+};
+
+}  // namespace tint::core::type
+
+#endif  // SRC_TINT_LANG_CORE_TYPE_INPUT_ATTACHMENT_H_
diff --git a/src/tint/lang/core/type/input_attachment_test.cc b/src/tint/lang/core/type/input_attachment_test.cc
new file mode 100644
index 0000000..2a0584c
--- /dev/null
+++ b/src/tint/lang/core/type/input_attachment_test.cc
@@ -0,0 +1,110 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/tint/lang/core/type/input_attachment.h"
+
+#include "src/tint/lang/core/type/depth_texture.h"
+#include "src/tint/lang/core/type/external_texture.h"
+#include "src/tint/lang/core/type/helper_test.h"
+#include "src/tint/lang/core/type/multisampled_texture.h"
+#include "src/tint/lang/core/type/sampled_texture.h"
+#include "src/tint/lang/core/type/storage_texture.h"
+#include "src/tint/lang/core/type/texture_dimension.h"
+
+namespace tint::core::type {
+namespace {
+
+using InputAttachmentTest = TestHelper;
+
+TEST_F(InputAttachmentTest, Creation) {
+    auto* a = create<InputAttachment>(create<F32>());
+    auto* b = create<InputAttachment>(create<F32>());
+    auto* c = create<InputAttachment>(create<U32>());
+    auto* d = create<InputAttachment>(create<I32>());
+    EXPECT_EQ(a, b);
+    EXPECT_NE(a, c);
+    EXPECT_NE(a, d);
+    EXPECT_NE(c, d);
+}
+
+TEST_F(InputAttachmentTest, Hash) {
+    auto* a = create<InputAttachment>(create<F32>());
+    auto* b = create<InputAttachment>(create<F32>());
+    EXPECT_EQ(a->unique_hash, b->unique_hash);
+}
+
+TEST_F(InputAttachmentTest, Equals) {
+    auto* a = create<InputAttachment>(create<F32>());
+    auto* b = create<InputAttachment>(create<F32>());
+    auto* c = create<InputAttachment>(create<I32>());
+    EXPECT_TRUE(a->Equals(*b));
+    EXPECT_FALSE(a->Equals(*c));
+    EXPECT_FALSE(a->Equals(Void{}));
+}
+
+TEST_F(InputAttachmentTest, IsTexture) {
+    F32 f32;
+    InputAttachment s(&f32);
+    Texture* ty = &s;
+    EXPECT_FALSE(ty->Is<DepthTexture>());
+    EXPECT_FALSE(ty->Is<ExternalTexture>());
+    EXPECT_TRUE(ty->Is<InputAttachment>());
+    EXPECT_FALSE(ty->Is<MultisampledTexture>());
+    EXPECT_FALSE(ty->Is<SampledTexture>());
+    EXPECT_FALSE(ty->Is<StorageTexture>());
+}
+
+TEST_F(InputAttachmentTest, Dim) {
+    F32 f32;
+    InputAttachment s(&f32);
+    EXPECT_EQ(s.dim(), TextureDimension::k2d);
+}
+
+TEST_F(InputAttachmentTest, FriendlyName) {
+    F32 f32;
+    InputAttachment s(&f32);
+    EXPECT_EQ(s.FriendlyName(), "input_attachment<f32>");
+}
+
+TEST_F(InputAttachmentTest, Clone) {
+    auto* a = create<InputAttachment>(create<F32>());
+    auto* b = create<InputAttachment>(create<I32>());
+
+    core::type::Manager mgr;
+    core::type::CloneContext ctx{{nullptr}, {nullptr, &mgr}};
+
+    auto* c = a->Clone(ctx);
+    ASSERT_TRUE(c->Is<InputAttachment>());
+    EXPECT_TRUE(c->type()->Is<F32>());
+
+    auto* d = b->Clone(ctx);
+    ASSERT_TRUE(d->Is<InputAttachment>());
+    EXPECT_TRUE(d->type()->Is<I32>());
+}
+
+}  // namespace
+}  // namespace tint::core::type
diff --git a/src/tint/lang/core/type/multisampled_texture_test.cc b/src/tint/lang/core/type/multisampled_texture_test.cc
index 2eb6739..1368c00 100644
--- a/src/tint/lang/core/type/multisampled_texture_test.cc
+++ b/src/tint/lang/core/type/multisampled_texture_test.cc
@@ -30,6 +30,7 @@
 #include "src/tint/lang/core/type/depth_texture.h"
 #include "src/tint/lang/core/type/external_texture.h"
 #include "src/tint/lang/core/type/helper_test.h"
+#include "src/tint/lang/core/type/input_attachment.h"
 #include "src/tint/lang/core/type/sampled_texture.h"
 #include "src/tint/lang/core/type/storage_texture.h"
 #include "src/tint/lang/core/type/texture_dimension.h"
@@ -72,6 +73,7 @@
     Texture* ty = &s;
     EXPECT_FALSE(ty->Is<DepthTexture>());
     EXPECT_FALSE(ty->Is<ExternalTexture>());
+    EXPECT_FALSE(ty->Is<InputAttachment>());
     EXPECT_TRUE(ty->Is<MultisampledTexture>());
     EXPECT_FALSE(ty->Is<SampledTexture>());
     EXPECT_FALSE(ty->Is<StorageTexture>());
diff --git a/src/tint/lang/core/type/sampled_texture_test.cc b/src/tint/lang/core/type/sampled_texture_test.cc
index 4de7609..0c67600 100644
--- a/src/tint/lang/core/type/sampled_texture_test.cc
+++ b/src/tint/lang/core/type/sampled_texture_test.cc
@@ -30,6 +30,7 @@
 #include "src/tint/lang/core/type/depth_texture.h"
 #include "src/tint/lang/core/type/external_texture.h"
 #include "src/tint/lang/core/type/helper_test.h"
+#include "src/tint/lang/core/type/input_attachment.h"
 #include "src/tint/lang/core/type/storage_texture.h"
 #include "src/tint/lang/core/type/texture_dimension.h"
 
@@ -77,6 +78,7 @@
     Texture* ty = &s;
     EXPECT_FALSE(ty->Is<DepthTexture>());
     EXPECT_FALSE(ty->Is<ExternalTexture>());
+    EXPECT_FALSE(ty->Is<InputAttachment>());
     EXPECT_TRUE(ty->Is<SampledTexture>());
     EXPECT_FALSE(ty->Is<StorageTexture>());
 }
diff --git a/src/tint/lang/msl/writer/printer/constant_test.cc b/src/tint/lang/msl/writer/printer/constant_test.cc
index e4910d5..f4dd3b2 100644
--- a/src/tint/lang/msl/writer/printer/constant_test.cc
+++ b/src/tint/lang/msl/writer/printer/constant_test.cc
@@ -134,7 +134,7 @@
 }
 
 TEST_F(MslPrinterTest, Constant_Vector_Splat) {
-    auto* c = b.Splat(ty.vec3<f32>(), 1.5_f, 3);
+    auto* c = b.Splat<vec3<f32>>(1.5_f);
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
         b.Let("a", c);
@@ -150,7 +150,7 @@
 }
 
 TEST_F(MslPrinterTest, Constant_Vector_Composite) {
-    auto* c = b.Composite(ty.vec3<f32>(), 1.5_f, 1.0_f, 1.5_f);
+    auto* c = b.Composite<vec3<f32>>(1.5_f, 1.0_f, 1.5_f);
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
         b.Let("a", c);
@@ -166,7 +166,7 @@
 }
 
 TEST_F(MslPrinterTest, Constant_Vector_Composite_AnyZero) {
-    auto* c = b.Composite(ty.vec3<f32>(), 1.0_f, 0.0_f, 1.5_f);
+    auto* c = b.Composite<vec3<f32>>(1.0_f, 0.0_f, 1.5_f);
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
         b.Let("a", c);
@@ -182,7 +182,7 @@
 }
 
 TEST_F(MslPrinterTest, Constant_Vector_Composite_AllZero) {
-    auto* c = b.Composite(ty.vec3<f32>(), 0.0_f, 0.0_f, 0.0_f);
+    auto* c = b.Composite<vec3<f32>>(0.0_f, 0.0_f, 0.0_f);
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
         b.Let("a", c);
@@ -198,7 +198,7 @@
 }
 
 TEST_F(MslPrinterTest, Constant_Matrix_Splat) {
-    auto* c = b.Splat(ty.mat3x2<f32>(), 1.5_f, 3);
+    auto* c = b.Splat<mat3x2<f32>>(b.Splat<vec2<f32>>(1.5_f));
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
         b.Let("a", c);
@@ -208,16 +208,16 @@
     ASSERT_TRUE(Generate()) << err_ << output_;
     EXPECT_EQ(output_, MetalHeader() + R"(
 void foo() {
-  float3x2 const a = float3x2(1.5f, 1.5f, 1.5f);
+  float3x2 const a = float3x2(float2(1.5f), float2(1.5f), float2(1.5f));
 }
 )");
 }
 
 TEST_F(MslPrinterTest, Constant_Matrix_Composite) {
-    auto* c = b.Composite(ty.mat3x2<f32>(),                           //
-                          b.Composite(ty.vec2<f32>(), 1.5_f, 1.0_f),  //
-                          b.Composite(ty.vec2<f32>(), 1.5_f, 2.0_f),  //
-                          b.Composite(ty.vec2<f32>(), 2.5_f, 3.5_f));
+    auto* c = b.Composite<mat3x2<f32>>(        //
+        b.Composite<vec2<f32>>(1.5_f, 1.0_f),  //
+        b.Composite<vec2<f32>>(1.5_f, 2.0_f),  //
+        b.Composite<vec2<f32>>(2.5_f, 3.5_f));
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
         b.Let("a", c);
@@ -233,9 +233,9 @@
 }
 
 TEST_F(MslPrinterTest, Constant_Matrix_Composite_AnyZero) {
-    auto* c = b.Composite(ty.mat2x2<f32>(),                           //
-                          b.Composite(ty.vec2<f32>(), 1.0_f, 0.0_f),  //
-                          b.Composite(ty.vec2<f32>(), 1.5_f, 2.5_f));
+    auto* c = b.Composite<mat2x2<f32>>(        //
+        b.Composite<vec2<f32>>(1.0_f, 0.0_f),  //
+        b.Composite<vec2<f32>>(1.5_f, 2.5_f));
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
         b.Let("a", c);
@@ -251,10 +251,10 @@
 }
 
 TEST_F(MslPrinterTest, Constant_Matrix_Composite_AllZero) {
-    auto* c = b.Composite(ty.mat3x2<f32>(),                           //
-                          b.Composite(ty.vec2<f32>(), 0.0_f, 0.0_f),  //
-                          b.Composite(ty.vec2<f32>(), 0.0_f, 0.0_f),  //
-                          b.Composite(ty.vec2<f32>(), 0.0_f, 0.0_f));
+    auto* c = b.Composite<mat3x2<f32>>(        //
+        b.Composite<vec2<f32>>(0.0_f, 0.0_f),  //
+        b.Composite<vec2<f32>>(0.0_f, 0.0_f),  //
+        b.Composite<vec2<f32>>(0.0_f, 0.0_f));
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
         b.Let("a", c);
@@ -270,7 +270,7 @@
 }
 
 TEST_F(MslPrinterTest, Constant_Array_Splat) {
-    auto* c = b.Splat(ty.array<f32, 3>(), 1.5_f, 3);
+    auto* c = b.Splat<array<f32, 3>>(1.5_f);
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
         b.Let("a", c);
@@ -286,7 +286,7 @@
 }
 
 TEST_F(MslPrinterTest, Constant_Array_Composite) {
-    auto* c = b.Composite(ty.array<f32, 3>(), 1.5_f, 1.0_f, 2.0_f);
+    auto* c = b.Composite<array<f32, 3>>(1.5_f, 1.0_f, 2.0_f);
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
         b.Let("a", c);
@@ -302,7 +302,7 @@
 }
 
 TEST_F(MslPrinterTest, Constant_Array_Composite_AnyZero) {
-    auto* c = b.Composite(ty.array<f32, 2>(), 1.0_f, 0.0_f);
+    auto* c = b.Composite<array<f32, 2>>(1.0_f, 0.0_f);
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
         b.Let("a", c);
@@ -318,7 +318,7 @@
 }
 
 TEST_F(MslPrinterTest, Constant_Array_Composite_AllZero) {
-    auto* c = b.Composite(ty.array<f32, 3>(), 0.0_f, 0.0_f, 0.0_f);
+    auto* c = b.Composite<array<f32, 3>>(0.0_f, 0.0_f, 0.0_f);
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
         b.Let("a", c);
@@ -338,7 +338,7 @@
                                                   {mod.symbols.Register("a"), ty.f32()},
                                                   {mod.symbols.Register("b"), ty.f32()},
                                               });
-    auto* c = b.Splat(s, 1.5_f, 2);
+    auto* c = b.Splat(s, 1.5_f);
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
         b.Let("a", c);
diff --git a/src/tint/lang/msl/writer/printer/let_test.cc b/src/tint/lang/msl/writer/printer/let_test.cc
index 0221bf7..85248bb 100644
--- a/src/tint/lang/msl/writer/printer/let_test.cc
+++ b/src/tint/lang/msl/writer/printer/let_test.cc
@@ -114,7 +114,7 @@
 TEST_F(MslPrinterTest, LetVec3F32) {
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
-        b.Let("l", b.Composite(ty.vec3<f32>(), 1_f, 2_f, 3_f));
+        b.Let("l", b.Composite<vec3<f32>>(1_f, 2_f, 3_f));
         b.Return(func);
     });
 
@@ -130,7 +130,7 @@
     // Enable f16?
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
-        b.Let("l", b.Composite(ty.vec3<f16>(), 1_h, 2_h, 3_h));
+        b.Let("l", b.Composite<vec3<f16>>(1_h, 2_h, 3_h));
         b.Return(func);
     });
 
@@ -145,9 +145,8 @@
 TEST_F(MslPrinterTest, LetMat2x3F32) {
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
-        b.Let("l", b.Composite(ty.mat2x3<f32>(),  //
-                               b.Composite(ty.vec3<f32>(), 1_f, 2_f, 3_f),
-                               b.Composite(ty.vec3<f32>(), 4_f, 5_f, 6_f)));
+        b.Let("l", b.Composite<mat2x3<f32>>(b.Composite<vec3<f32>>(1_f, 2_f, 3_f),
+                                            b.Composite<vec3<f32>>(4_f, 5_f, 6_f)));
         b.Return(func);
     });
 
@@ -163,9 +162,8 @@
     // Enable f16?
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
-        b.Let("l", b.Composite(ty.mat2x3<f16>(),  //
-                               b.Composite(ty.vec3<f16>(), 1_h, 2_h, 3_h),
-                               b.Composite(ty.vec3<f16>(), 4_h, 5_h, 6_h)));
+        b.Let("l", b.Composite<mat2x3<f16>>(b.Composite<vec3<f16>>(1_h, 2_h, 3_h),
+                                            b.Composite<vec3<f16>>(4_h, 5_h, 6_h)));
         b.Return(func);
     });
 
@@ -180,7 +178,7 @@
 TEST_F(MslPrinterTest, LetArrF32) {
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
-        b.Let("l", b.Composite(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
+        b.Let("l", b.Composite<array<f32, 3>>(1_f, 2_f, 3_f));
         b.Return(func);
     });
 
@@ -195,10 +193,9 @@
 TEST_F(MslPrinterTest, LetArrVec2Bool) {
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
-        b.Let("l", b.Composite(ty.array<vec2<bool>, 3>(),  //
-                               b.Composite(ty.vec2<bool>(), true, false),
-                               b.Composite(ty.vec2<bool>(), false, true),
-                               b.Composite(ty.vec2<bool>(), true, false)));
+        b.Let("l", b.Composite<array<vec2<bool>, 3>>(b.Composite<vec2<bool>>(true, false),
+                                                     b.Composite<vec2<bool>>(false, true),
+                                                     b.Composite<vec2<bool>>(true, false)));
         b.Return(func);
     });
 
diff --git a/src/tint/lang/msl/writer/printer/var_test.cc b/src/tint/lang/msl/writer/printer/var_test.cc
index b3e141a..b91c2bd 100644
--- a/src/tint/lang/msl/writer/printer/var_test.cc
+++ b/src/tint/lang/msl/writer/printer/var_test.cc
@@ -183,7 +183,7 @@
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
         auto* v = b.Var("a", ty.ptr<core::AddressSpace::kFunction, vec3<f32>>());
-        v->SetInitializer(b.Splat(ty.vec3<f32>(), 0_f, 3));
+        v->SetInitializer(b.Splat<vec3<f32>>(0_f));
         b.Return(func);
     });
 
@@ -200,7 +200,7 @@
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
         auto* v = b.Var("a", ty.ptr<core::AddressSpace::kFunction, vec3<f16>>());
-        v->SetInitializer(b.Splat(ty.vec3<f16>(), 0_h, 3));
+        v->SetInitializer(b.Splat<vec3<f16>>(0_h));
         b.Return(func);
     });
 
@@ -216,8 +216,8 @@
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
         auto* v = b.Var("a", ty.ptr<core::AddressSpace::kFunction, mat2x3<f32>>());
-        v->SetInitializer(b.Composite(ty.mat2x3<f32>(), b.Splat(ty.vec3<f32>(), 0_f, 3),
-                                      b.Splat(ty.vec3<f32>(), 0_f, 3)));
+        v->SetInitializer(
+            b.Composite<mat2x3<f32>>(b.Splat<vec3<f32>>(0_f), b.Splat<vec3<f32>>(0_f)));
         b.Return(func);
     });
 
@@ -234,8 +234,8 @@
     auto* func = b.Function("foo", ty.void_());
     b.Append(func->Block(), [&] {
         auto* v = b.Var("a", ty.ptr<core::AddressSpace::kFunction, mat2x3<f16>>());
-        v->SetInitializer(b.Composite(ty.mat2x3<f16>(), b.Splat(ty.vec3<f16>(), 0_h, 3),
-                                      b.Splat(ty.vec3<f16>(), 0_h, 3)));
+        v->SetInitializer(
+            b.Composite<mat2x3<f16>>(b.Splat<vec3<f16>>(0_h), b.Splat<vec3<f16>>(0_h)));
         b.Return(func);
     });
 
diff --git a/src/tint/lang/spirv/reader/lower/shader_io_test.cc b/src/tint/lang/spirv/reader/lower/shader_io_test.cc
index d004762..79beb46 100644
--- a/src/tint/lang/spirv/reader/lower/shader_io_test.cc
+++ b/src/tint/lang/spirv/reader/lower/shader_io_test.cc
@@ -1082,7 +1082,7 @@
 
     auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kVertex);
     b.Append(ep->Block(), [&] {  //
-        b.Store(position, b.Splat<vec4<f32>>(1_f, 4));
+        b.Store(position, b.Splat<vec4<f32>>(1_f));
         b.Return(ep);
     });
 
@@ -1137,7 +1137,7 @@
 
     auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kVertex);
     b.Append(ep->Block(), [&] {  //
-        b.Store(position, b.Splat<vec4<f32>>(1_f, 4));
+        b.Store(position, b.Splat<vec4<f32>>(1_f));
         b.Return(ep);
     });
 
@@ -1191,7 +1191,7 @@
 
     auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
     b.Append(ep->Block(), [&] {  //
-        b.Store(color, b.Splat<vec4<f32>>(1_f, 4));
+        b.Store(color, b.Splat<vec4<f32>>(1_f));
         b.Return(ep);
     });
 
@@ -1247,7 +1247,7 @@
 
     auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
     b.Append(ep->Block(), [&] {  //
-        b.Store(color, b.Splat<vec4<f32>>(1_f, 4));
+        b.Store(color, b.Splat<vec4<f32>>(1_f));
         b.Return(ep);
     });
 
@@ -1318,9 +1318,9 @@
 
     auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kVertex);
     b.Append(ep->Block(), [&] {  //
-        b.Store(position, b.Splat<vec4<f32>>(1_f, 4));
-        b.Store(color1, b.Splat<vec4<f32>>(0.5_f, 4));
-        b.Store(color2, b.Splat<vec4<f32>>(0.25_f, 4));
+        b.Store(position, b.Splat<vec4<f32>>(1_f));
+        b.Store(color1, b.Splat<vec4<f32>>(0.5_f));
+        b.Store(color2, b.Splat<vec4<f32>>(0.25_f));
         b.Return(ep);
     });
 
@@ -1413,9 +1413,9 @@
     auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kVertex);
     b.Append(ep->Block(), [&] {  //
         auto* ptr = ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>());
-        b.Store(b.Access(ptr, builtins, 0_u), b.Splat<vec4<f32>>(1_f, 4));
-        b.Store(b.Access(ptr, colors, 0_u), b.Splat<vec4<f32>>(0.5_f, 4));
-        b.Store(b.Access(ptr, colors, 1_u), b.Splat<vec4<f32>>(0.25_f, 4));
+        b.Store(b.Access(ptr, builtins, 0_u), b.Splat<vec4<f32>>(1_f));
+        b.Store(b.Access(ptr, colors, 0_u), b.Splat<vec4<f32>>(0.5_f));
+        b.Store(b.Access(ptr, colors, 1_u), b.Splat<vec4<f32>>(0.25_f));
         b.Return(ep);
     });
 
@@ -1545,9 +1545,9 @@
     auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kVertex);
     b.Append(ep->Block(), [&] {  //
         auto* ptr = ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>());
-        b.Store(b.Access(ptr, builtins, 0_u), b.Splat<vec4<f32>>(1_f, 4));
-        b.Store(b.Access(ptr, colors, 0_u), b.Splat<vec4<f32>>(0.5_f, 4));
-        b.Store(b.Access(ptr, colors, 1_u), b.Splat<vec4<f32>>(0.25_f, 4));
+        b.Store(b.Access(ptr, builtins, 0_u), b.Splat<vec4<f32>>(1_f));
+        b.Store(b.Access(ptr, colors, 0_u), b.Splat<vec4<f32>>(0.5_f));
+        b.Store(b.Access(ptr, colors, 1_u), b.Splat<vec4<f32>>(0.25_f));
         b.Return(ep);
     });
 
@@ -1670,9 +1670,9 @@
     auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kVertex);
     b.Append(ep->Block(), [&] {  //
         auto* ptr = ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>());
-        b.Store(b.Access(ptr, builtins, 0_u), b.Splat<vec4<f32>>(1_f, 4));
-        b.Store(b.Access(ptr, colors, 0_u), b.Splat<vec4<f32>>(0.5_f, 4));
-        b.Store(b.Access(ptr, colors, 1_u), b.Splat<vec4<f32>>(0.25_f, 4));
+        b.Store(b.Access(ptr, builtins, 0_u), b.Splat<vec4<f32>>(1_f));
+        b.Store(b.Access(ptr, colors, 0_u), b.Splat<vec4<f32>>(0.5_f));
+        b.Store(b.Access(ptr, colors, 1_u), b.Splat<vec4<f32>>(0.25_f));
         b.Return(ep);
     });
 
@@ -1785,21 +1785,21 @@
 
     auto* ep1 = b.Function("main1", ty.void_(), core::ir::Function::PipelineStage::kVertex);
     b.Append(ep1->Block(), [&] {  //
-        b.Store(position, b.Splat<vec4<f32>>(1_f, 4));
+        b.Store(position, b.Splat<vec4<f32>>(1_f));
         b.Return(ep1);
     });
 
     auto* ep2 = b.Function("main2", ty.void_(), core::ir::Function::PipelineStage::kVertex);
     b.Append(ep2->Block(), [&] {  //
-        b.Store(position, b.Splat<vec4<f32>>(1_f, 4));
-        b.Store(color1, b.Splat<vec4<f32>>(0.5_f, 4));
+        b.Store(position, b.Splat<vec4<f32>>(1_f));
+        b.Store(color1, b.Splat<vec4<f32>>(0.5_f));
         b.Return(ep2);
     });
 
     auto* ep3 = b.Function("main3", ty.void_(), core::ir::Function::PipelineStage::kVertex);
     b.Append(ep3->Block(), [&] {  //
-        b.Store(position, b.Splat<vec4<f32>>(1_f, 4));
-        b.Store(color2, b.Splat<vec4<f32>>(0.25_f, 4));
+        b.Store(position, b.Splat<vec4<f32>>(1_f));
+        b.Store(color2, b.Splat<vec4<f32>>(0.25_f));
         b.Return(ep3);
     });
 
@@ -1913,7 +1913,7 @@
 
     auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
     b.Append(ep->Block(), [&] {  //
-        b.Store(color, b.Splat<vec4<f32>>(1_f, 4));
+        b.Store(color, b.Splat<vec4<f32>>(1_f));
         auto* load = b.Load(color);
         auto* mul = b.Multiply<vec4<f32>>(load, 2_f);
         b.Store(color, mul);
@@ -1976,7 +1976,7 @@
 
     auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
     b.Append(ep->Block(), [&] {  //
-        b.Store(color, b.Splat<vec4<f32>>(1_f, 4));
+        b.Store(color, b.Splat<vec4<f32>>(1_f));
         auto* load = b.LoadVectorElement(color, 2_u);
         auto* mul = b.Multiply<f32>(load, 2_f);
         b.StoreVectorElement(color, 2_u, mul);
@@ -2042,7 +2042,7 @@
         auto* access_1 = b.Access(ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>()), color);
         auto* access_2 = b.Access(ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>()), access_1);
         auto* load = b.LoadVectorElement(access_2, 2_u);
-        auto* mul = b.Multiply<vec4<f32>>(b.Splat<vec4<f32>>(1_f, 4), load);
+        auto* mul = b.Multiply<vec4<f32>>(b.Splat<vec4<f32>>(1_f), load);
         b.Store(access_2, mul);
         b.Return(ep);
     });
diff --git a/src/tint/lang/spirv/writer/constant_test.cc b/src/tint/lang/spirv/writer/constant_test.cc
index 9f24475..a194261 100644
--- a/src/tint/lang/spirv/writer/constant_test.cc
+++ b/src/tint/lang/spirv/writer/constant_test.cc
@@ -86,7 +86,7 @@
 
 TEST_F(SpirvWriterTest, Constant_Vec4Bool) {
     b.Append(b.ir.root_block, [&] {
-        b.Var<private_, read_write>("v", b.Composite(ty.vec4<bool>(), true, false, false, true));
+        b.Var<private_, read_write>("v", b.Composite<vec4<bool>>(true, false, false, true));
     });
     ASSERT_TRUE(Generate()) << Error() << output_;
     EXPECT_INST(" = OpConstantComposite %v4bool %true %false %false %true");
@@ -94,14 +94,14 @@
 
 TEST_F(SpirvWriterTest, Constant_Vec2i) {
     b.Append(b.ir.root_block,
-             [&] { b.Var<private_, read_write>("v", b.Composite(ty.vec2<i32>(), 42_i, -1_i)); });
+             [&] { b.Var<private_, read_write>("v", b.Composite<vec2<i32>>(42_i, -1_i)); });
     ASSERT_TRUE(Generate()) << Error() << output_;
     EXPECT_INST(" = OpConstantComposite %v2int %int_42 %int_n1");
 }
 
 TEST_F(SpirvWriterTest, Constant_Vec3u) {
     b.Append(b.ir.root_block, [&] {
-        b.Var<private_, read_write>("v", b.Composite(ty.vec3<u32>(), 42_u, 0_u, 4000000000_u));
+        b.Var<private_, read_write>("v", b.Composite<vec3<u32>>(42_u, 0_u, 4000000000_u));
     });
     ASSERT_TRUE(Generate()) << Error() << output_;
     EXPECT_INST(" = OpConstantComposite %v3uint %uint_42 %uint_0 %uint_4000000000");
@@ -109,7 +109,7 @@
 
 TEST_F(SpirvWriterTest, Constant_Vec4f) {
     b.Append(b.ir.root_block, [&] {
-        b.Var<private_, read_write>("v", b.Composite(ty.vec4<f32>(), 42_f, 0_f, 0.25_f, -1_f));
+        b.Var<private_, read_write>("v", b.Composite<vec4<f32>>(42_f, 0_f, 0.25_f, -1_f));
     });
     ASSERT_TRUE(Generate()) << Error() << output_;
     EXPECT_INST(" = OpConstantComposite %v4float %float_42 %float_0 %float_0_25 %float_n1");
@@ -117,7 +117,7 @@
 
 TEST_F(SpirvWriterTest, Constant_Vec2h) {
     b.Append(b.ir.root_block,
-             [&] { b.Var<private_, read_write>("v", b.Composite(ty.vec2<f16>(), 42_h, 0.25_h)); });
+             [&] { b.Var<private_, read_write>("v", b.Composite<vec2<f16>>(42_h, 0.25_h)); });
     ASSERT_TRUE(Generate()) << Error() << output_;
     EXPECT_INST(" = OpConstantComposite %v2half %half_0x1_5p_5 %half_0x1pn2");
 }
@@ -125,9 +125,9 @@
 TEST_F(SpirvWriterTest, Constant_Mat2x3f) {
     b.Append(b.ir.root_block, [&] {
         b.Var<private_, read_write>("v",
-                                    b.Composite(ty.mat2x3<f32>(),  //
-                                                b.Composite(ty.vec3<f32>(), 42_f, -1_f, 0.25_f),
-                                                b.Composite(ty.vec3<f32>(), -42_f, 0_f, -0.25_f)));
+                                    b.Composite<mat2x3<f32>>(  //
+                                        b.Composite<vec3<f32>>(42_f, -1_f, 0.25_f),
+                                        b.Composite<vec3<f32>>(-42_f, 0_f, -0.25_f)));
     });
     ASSERT_TRUE(Generate()) << Error() << output_;
     EXPECT_INST(R"(
@@ -145,11 +145,11 @@
 
 TEST_F(SpirvWriterTest, Constant_Mat4x2h) {
     b.Append(b.ir.root_block, [&] {
-        b.Var<private_, read_write>("v", b.Composite(ty.mat4x2<f16>(),                          //
-                                                     b.Composite(ty.vec2<f16>(), 42_h, -1_h),   //
-                                                     b.Composite(ty.vec2<f16>(), 0_h, 0.25_h),  //
-                                                     b.Composite(ty.vec2<f16>(), -42_h, 1_h),   //
-                                                     b.Composite(ty.vec2<f16>(), 0.5_h, f16(-0))));
+        b.Var<private_, read_write>("v", b.Composite<mat4x2<f16>>(                 //
+                                             b.Composite<vec2<f16>>(42_h, -1_h),   //
+                                             b.Composite<vec2<f16>>(0_h, 0.25_h),  //
+                                             b.Composite<vec2<f16>>(-42_h, 1_h),   //
+                                             b.Composite<vec2<f16>>(0.5_h, f16(-0))));
     });
     ASSERT_TRUE(Generate()) << Error() << output_;
     EXPECT_INST(R"(
@@ -170,7 +170,7 @@
 
 TEST_F(SpirvWriterTest, Constant_Array_I32) {
     b.Append(b.ir.root_block, [&] {
-        b.Var<private_, read_write>("v", b.Composite(ty.array<i32, 4>(), 1_i, 2_i, 3_i, 4_i));
+        b.Var<private_, read_write>("v", b.Composite<array<i32, 4>>(1_i, 2_i, 3_i, 4_i));
     });
     ASSERT_TRUE(Generate()) << Error() << output_;
     EXPECT_INST(" = OpConstantComposite %_arr_int_uint_4 %int_1 %int_2 %int_3 %int_4");
@@ -178,9 +178,9 @@
 
 TEST_F(SpirvWriterTest, Constant_Array_Array_I32) {
     b.Append(b.ir.root_block, [&] {
-        auto* inner = b.Composite(ty.array<i32, 4>(), 1_i, 2_i, 3_i, 4_i);
+        auto* inner = b.Composite<array<i32, 4>>(1_i, 2_i, 3_i, 4_i);
         b.Var<private_, read_write>(
-            "v", b.Composite(ty.array(ty.array<i32, 4>(), 4), inner, inner, inner, inner));
+            "v", b.Composite<array<array<i32, 4>, 4>>(inner, inner, inner, inner));
     });
     ASSERT_TRUE(Generate()) << Error() << output_;
     EXPECT_INST(R"(
diff --git a/src/tint/lang/spirv/writer/printer/printer.cc b/src/tint/lang/spirv/writer/printer/printer.cc
index 73066a6..e1a00e0 100644
--- a/src/tint/lang/spirv/writer/printer/printer.cc
+++ b/src/tint/lang/spirv/writer/printer/printer.cc
@@ -1791,8 +1791,8 @@
 
             if (auto* vec = res_ty->As<core::type::Vector>()) {
                 // Splat the scalars into vectors.
-                one = b_.Splat(vec, one, vec->Width());
-                zero = b_.Splat(vec, zero, vec->Width());
+                one = b_.Splat(vec, one);
+                zero = b_.Splat(vec, zero);
             }
 
             op = spv::Op::OpSelect;
diff --git a/src/tint/lang/spirv/writer/raise/builtin_polyfill_test.cc b/src/tint/lang/spirv/writer/raise/builtin_polyfill_test.cc
index 1049aac..a215e73 100644
--- a/src/tint/lang/spirv/writer/raise/builtin_polyfill_test.cc
+++ b/src/tint/lang/spirv/writer/raise/builtin_polyfill_test.cc
@@ -1466,7 +1466,7 @@
 
     b.Append(func->Block(), [&] {
         auto* result = b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureSample, t, s, coords,
-                              b.Splat(ty.vec2<i32>(), 1_i, 2));
+                              b.Splat<vec2<i32>>(1_i));
         b.Return(func, result);
     });
 
@@ -1506,7 +1506,7 @@
 
     b.Append(func->Block(), [&] {
         auto* result = b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureSample, t, s, coords,
-                              array_idx, b.Splat(ty.vec2<i32>(), 1_i, 2));
+                              array_idx, b.Splat<vec2<i32>>(1_i));
         b.Return(func, result);
     });
 
@@ -1588,7 +1588,7 @@
 
     b.Append(func->Block(), [&] {
         auto* result = b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureSampleBias, t, s, coords,
-                              bias, b.Splat(ty.vec2<i32>(), 1_i, 2));
+                              bias, b.Splat<vec2<i32>>(1_i));
         b.Return(func, result);
     });
 
@@ -1629,7 +1629,7 @@
 
     b.Append(func->Block(), [&] {
         auto* result = b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureSampleBias, t, s, coords,
-                              array_idx, bias, b.Splat(ty.vec2<i32>(), 1_i, 2));
+                              array_idx, bias, b.Splat<vec2<i32>>(1_i));
         b.Return(func, result);
     });
 
@@ -1710,7 +1710,7 @@
 
     b.Append(func->Block(), [&] {
         auto* result = b.Call(ty.f32(), core::BuiltinFn::kTextureSampleCompare, t, s, coords, dref,
-                              b.Splat(ty.vec2<i32>(), 1_i, 2));
+                              b.Splat<vec2<i32>>(1_i));
         b.Return(func, result);
     });
 
@@ -1751,7 +1751,7 @@
 
     b.Append(func->Block(), [&] {
         auto* result = b.Call(ty.f32(), core::BuiltinFn::kTextureSampleCompare, t, s, coords,
-                              array_idx, bias, b.Splat(ty.vec2<i32>(), 1_i, 2));
+                              array_idx, bias, b.Splat<vec2<i32>>(1_i));
         b.Return(func, result);
     });
 
@@ -1833,7 +1833,7 @@
 
     b.Append(func->Block(), [&] {
         auto* result = b.Call(ty.f32(), core::BuiltinFn::kTextureSampleCompareLevel, t, s, coords,
-                              dref, b.Splat(ty.vec2<i32>(), 1_i, 2));
+                              dref, b.Splat<vec2<i32>>(1_i));
         b.Return(func, result);
     });
 
@@ -1874,7 +1874,7 @@
 
     b.Append(func->Block(), [&] {
         auto* result = b.Call(ty.f32(), core::BuiltinFn::kTextureSampleCompareLevel, t, s, coords,
-                              array_idx, bias, b.Splat(ty.vec2<i32>(), 1_i, 2));
+                              array_idx, bias, b.Splat<vec2<i32>>(1_i));
         b.Return(func, result);
     });
 
@@ -1958,7 +1958,7 @@
 
     b.Append(func->Block(), [&] {
         auto* result = b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureSampleGrad, t, s, coords,
-                              ddx, ddy, b.Splat(ty.vec2<i32>(), 1_i, 2));
+                              ddx, ddy, b.Splat<vec2<i32>>(1_i));
         b.Return(func, result);
     });
 
@@ -2000,7 +2000,7 @@
 
     b.Append(func->Block(), [&] {
         auto* result = b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureSampleGrad, t, s, coords,
-                              array_idx, ddx, ddy, b.Splat(ty.vec2<i32>(), 1_i, 2));
+                              array_idx, ddx, ddy, b.Splat<vec2<i32>>(1_i));
         b.Return(func, result);
     });
 
@@ -2082,7 +2082,7 @@
 
     b.Append(func->Block(), [&] {
         auto* result = b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureSampleLevel, t, s, coords,
-                              lod, b.Splat(ty.vec2<i32>(), 1_i, 2));
+                              lod, b.Splat<vec2<i32>>(1_i));
         b.Return(func, result);
     });
 
@@ -2123,7 +2123,7 @@
 
     b.Append(func->Block(), [&] {
         auto* result = b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureSampleLevel, t, s, coords,
-                              array_idx, lod, b.Splat(ty.vec2<i32>(), 1_i, 2));
+                              array_idx, lod, b.Splat<vec2<i32>>(1_i));
         b.Return(func, result);
     });
 
@@ -2205,7 +2205,7 @@
 
     b.Append(func->Block(), [&] {
         auto* result = b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureGather, component, t, s,
-                              coords, b.Splat(ty.vec2<i32>(), 1_i, 2));
+                              coords, b.Splat<vec2<i32>>(1_i));
         b.Return(func, result);
     });
 
@@ -2246,7 +2246,7 @@
 
     b.Append(func->Block(), [&] {
         auto* result = b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureGather, component, t, s,
-                              coords, array_idx, b.Splat(ty.vec2<i32>(), 1_i, 2));
+                              coords, array_idx, b.Splat<vec2<i32>>(1_i));
         b.Return(func, result);
     });
 
@@ -2366,7 +2366,7 @@
 
     b.Append(func->Block(), [&] {
         auto* result = b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureGatherCompare, t, s, coords,
-                              depth, b.Splat(ty.vec2<i32>(), 1_i, 2));
+                              depth, b.Splat<vec2<i32>>(1_i));
         b.Return(func, result);
     });
 
diff --git a/src/tint/lang/spirv/writer/texture_builtin_test.cc b/src/tint/lang/spirv/writer/texture_builtin_test.cc
index 773dd2f..6c35730 100644
--- a/src/tint/lang/spirv/writer/texture_builtin_test.cc
+++ b/src/tint/lang/spirv/writer/texture_builtin_test.cc
@@ -190,7 +190,7 @@
             for (const auto& arg : params.args) {
                 auto* value = MakeScalarValue(arg.type, arg_value++);
                 if (arg.width > 1) {
-                    value = b.Splat(ty.vec(value->Type(), arg.width), value, arg.width);
+                    value = b.Splat(ty.vec(value->Type(), arg.width), value);
                 }
                 args.Push(value);
                 mod.SetName(value, arg.name);
diff --git a/src/tint/lang/wgsl/extension.cc b/src/tint/lang/wgsl/extension.cc
index 1240173..e6790ec 100644
--- a/src/tint/lang/wgsl/extension.cc
+++ b/src/tint/lang/wgsl/extension.cc
@@ -63,6 +63,9 @@
     if (str == "chromium_internal_graphite") {
         return Extension::kChromiumInternalGraphite;
     }
+    if (str == "chromium_internal_input_attachments") {
+        return Extension::kChromiumInternalInputAttachments;
+    }
     if (str == "chromium_internal_relaxed_uniform_layout") {
         return Extension::kChromiumInternalRelaxedUniformLayout;
     }
@@ -90,6 +93,8 @@
             return "chromium_internal_dual_source_blending";
         case Extension::kChromiumInternalGraphite:
             return "chromium_internal_graphite";
+        case Extension::kChromiumInternalInputAttachments:
+            return "chromium_internal_input_attachments";
         case Extension::kChromiumInternalRelaxedUniformLayout:
             return "chromium_internal_relaxed_uniform_layout";
         case Extension::kF16:
diff --git a/src/tint/lang/wgsl/extension.h b/src/tint/lang/wgsl/extension.h
index 3eccedf..ed1ee13 100644
--- a/src/tint/lang/wgsl/extension.h
+++ b/src/tint/lang/wgsl/extension.h
@@ -53,6 +53,7 @@
     kChromiumExperimentalSubgroups,
     kChromiumInternalDualSourceBlending,
     kChromiumInternalGraphite,
+    kChromiumInternalInputAttachments,
     kChromiumInternalRelaxedUniformLayout,
     kF16,
 };
@@ -82,6 +83,7 @@
     "chromium_experimental_subgroups",
     "chromium_internal_dual_source_blending",
     "chromium_internal_graphite",
+    "chromium_internal_input_attachments",
     "chromium_internal_relaxed_uniform_layout",
     "f16",
 };
@@ -95,6 +97,7 @@
     Extension::kChromiumExperimentalSubgroups,
     Extension::kChromiumInternalDualSourceBlending,
     Extension::kChromiumInternalGraphite,
+    Extension::kChromiumInternalInputAttachments,
     Extension::kChromiumInternalRelaxedUniformLayout,
     Extension::kF16,
 };
diff --git a/src/tint/lang/wgsl/extension_bench.cc b/src/tint/lang/wgsl/extension_bench.cc
index c2128d3..7e209ff 100644
--- a/src/tint/lang/wgsl/extension_bench.cc
+++ b/src/tint/lang/wgsl/extension_bench.cc
@@ -94,20 +94,27 @@
         "chromium_intenalNNgraphite",
         "chromiuminternal_gvaphite",
         "chromium_internal_grphitQQ",
-        "chromirm_intenal_rfflaxed_unifrm_layout",
-        "chromium_internal_jelaxed_uniform_layout",
-        "chromium_interna_relNNxed_uwwiform_lay82t",
+        "chromirm_ffnternalinpt_attachments",
+        "chromium_internal_input_attachmenjs",
+        "chwwomiu2_interNNal_inpu_att8chments",
+        "chromium_internal_input_attachments",
+        "chromium_internalinput_attachments",
+        "crrromium_internal_input_attachments",
+        "Ghromium_internal_input_attachments",
+        "chromium_internalFFrelaxed_uniform_layout",
+        "chromEum_internal_relaxed_unifrmlyout",
+        "chromium_internalrrrelaxd_uniform_layout",
         "chromium_internal_relaxed_uniform_layout",
-        "chromium_internal_relaxed_uniform_layut",
-        "chromium_internal_relaxed_rrniform_layout",
-        "chromium_internal_relaxedGuniform_layout",
-        "FF16",
-        "",
-        "rr1",
+        "chromiuminternal_relaxed_uniform_layut",
+        "cXroDium_internal_rJJlaed_uniform_layout",
+        "chromium_int8nal_relaed_uniform_layut",
+        "k",
+        "16",
+        "J1",
         "f16",
-        "1",
-        "DJ1",
-        "",
+        "c16",
+        "fO6",
+        "_KKttvv",
     };
     for (auto _ : state) {
         for (auto* str : kStrings) {
diff --git a/src/tint/lang/wgsl/extension_test.cc b/src/tint/lang/wgsl/extension_test.cc
index 11a75b0..9e6c182 100644
--- a/src/tint/lang/wgsl/extension_test.cc
+++ b/src/tint/lang/wgsl/extension_test.cc
@@ -64,6 +64,7 @@
     {"chromium_experimental_subgroups", Extension::kChromiumExperimentalSubgroups},
     {"chromium_internal_dual_source_blending", Extension::kChromiumInternalDualSourceBlending},
     {"chromium_internal_graphite", Extension::kChromiumInternalGraphite},
+    {"chromium_internal_input_attachments", Extension::kChromiumInternalInputAttachments},
     {"chromium_internal_relaxed_uniform_layout", Extension::kChromiumInternalRelaxedUniformLayout},
     {"f16", Extension::kF16},
 };
@@ -90,12 +91,15 @@
     {"chromi44m_internal_graphite", Extension::kUndefined},
     {"chromSSuVV_internal_graphite", Extension::kUndefined},
     {"cRromium_nternR22_graphite", Extension::kUndefined},
-    {"chromium_int9rnal_relaxed_Fnifor_layout", Extension::kUndefined},
-    {"chrmium_internal_relaxed_uniform_layout", Extension::kUndefined},
-    {"VRhHomium_internal_relaxd_uniform_OOayout", Extension::kUndefined},
-    {"y1", Extension::kUndefined},
-    {"l77rrn6", Extension::kUndefined},
-    {"4016", Extension::kUndefined},
+    {"chromium_int9rnaF_inpu_attachments", Extension::kUndefined},
+    {"chrmium_internal_input_attachments", Extension::kUndefined},
+    {"cOOromium_internVlHinput_ttachRRents", Extension::kUndefined},
+    {"chromium_internl_relaxyd_uniform_layout", Extension::kUndefined},
+    {"chromnnum_internrr77_Gelaxell_uniform_layout", Extension::kUndefined},
+    {"chromium_intern4l_relaxe00_uniform_layout", Extension::kUndefined},
+    {"5", Extension::kUndefined},
+    {"u16", Extension::kUndefined},
+    {"f", Extension::kUndefined},
 };
 
 using ExtensionParseTest = testing::TestWithParam<Case>;
diff --git a/src/tint/lang/wgsl/intrinsic/data.cc b/src/tint/lang/wgsl/intrinsic/data.cc
index 1ce3cb6..5c5b46d 100644
--- a/src/tint/lang/wgsl/intrinsic/data.cc
+++ b/src/tint/lang/wgsl/intrinsic/data.cc
@@ -973,6 +973,26 @@
 };
 
 
+/// TypeMatcher for 'type input_attachment'
+constexpr TypeMatcher kInputAttachmentMatcher {
+/* match */ [](MatchState& state, const Type* ty) -> const Type* {
+  const Type* T = nullptr;
+    if (!MatchInputAttachment(state, ty, T)) {
+      return nullptr;
+    }
+    T = state.Type(T);
+    if (T == nullptr) {
+      return nullptr;
+    }
+    return BuildInputAttachment(state, ty, T);
+  },
+/* print */ []([[maybe_unused]] MatchState* state, StyledText& out) {StyledText T;
+  state->PrintType(T);
+    out << style::Type("input_attachment", "<", T, ">");
+  }
+};
+
+
 /// TypeMatcher for 'type __modf_result'
 constexpr TypeMatcher kModfResultMatcher {
 /* match */ [](MatchState& state, const Type* ty) -> const Type* {
@@ -1787,31 +1807,32 @@
   /* [46] */ kTextureStorage3DMatcher,
   /* [47] */ kTextureExternalMatcher,
   /* [48] */ kPackedVec3Matcher,
-  /* [49] */ kModfResultMatcher,
-  /* [50] */ kModfResultVecMatcher,
-  /* [51] */ kFrexpResultMatcher,
-  /* [52] */ kFrexpResultVecMatcher,
-  /* [53] */ kAtomicCompareExchangeResultMatcher,
-  /* [54] */ kScalarMatcher,
-  /* [55] */ kConcreteScalarMatcher,
-  /* [56] */ kScalarNoF32Matcher,
-  /* [57] */ kScalarNoF16Matcher,
-  /* [58] */ kScalarNoI32Matcher,
-  /* [59] */ kScalarNoU32Matcher,
-  /* [60] */ kScalarNoBoolMatcher,
-  /* [61] */ kFiaFiu32F16Matcher,
-  /* [62] */ kFiaFi32F16Matcher,
-  /* [63] */ kFiaFiu32Matcher,
-  /* [64] */ kFaF32Matcher,
-  /* [65] */ kFaF32F16Matcher,
-  /* [66] */ kIaIu32Matcher,
-  /* [67] */ kIaI32Matcher,
-  /* [68] */ kFiu32F16Matcher,
-  /* [69] */ kFiu32Matcher,
-  /* [70] */ kFi32F16Matcher,
-  /* [71] */ kFi32Matcher,
-  /* [72] */ kF32F16Matcher,
-  /* [73] */ kIu32Matcher,
+  /* [49] */ kInputAttachmentMatcher,
+  /* [50] */ kModfResultMatcher,
+  /* [51] */ kModfResultVecMatcher,
+  /* [52] */ kFrexpResultMatcher,
+  /* [53] */ kFrexpResultVecMatcher,
+  /* [54] */ kAtomicCompareExchangeResultMatcher,
+  /* [55] */ kScalarMatcher,
+  /* [56] */ kConcreteScalarMatcher,
+  /* [57] */ kScalarNoF32Matcher,
+  /* [58] */ kScalarNoF16Matcher,
+  /* [59] */ kScalarNoI32Matcher,
+  /* [60] */ kScalarNoU32Matcher,
+  /* [61] */ kScalarNoBoolMatcher,
+  /* [62] */ kFiaFiu32F16Matcher,
+  /* [63] */ kFiaFi32F16Matcher,
+  /* [64] */ kFiaFiu32Matcher,
+  /* [65] */ kFaF32Matcher,
+  /* [66] */ kFaF32F16Matcher,
+  /* [67] */ kIaIu32Matcher,
+  /* [68] */ kIaI32Matcher,
+  /* [69] */ kFiu32F16Matcher,
+  /* [70] */ kFiu32Matcher,
+  /* [71] */ kFi32F16Matcher,
+  /* [72] */ kFi32Matcher,
+  /* [73] */ kF32F16Matcher,
+  /* [74] */ kIu32Matcher,
 };
 
 /// The template numbers, and number matchers
@@ -1895,7 +1916,7 @@
   /* [58] */ MatcherIndex(0),
   /* [59] */ MatcherIndex(23),
   /* [60] */ MatcherIndex(1),
-  /* [61] */ MatcherIndex(69),
+  /* [61] */ MatcherIndex(70),
   /* [62] */ MatcherIndex(23),
   /* [63] */ MatcherIndex(1),
   /* [64] */ MatcherIndex(2),
@@ -1911,13 +1932,13 @@
   /* [74] */ MatcherIndex(23),
   /* [75] */ MatcherIndex(0),
   /* [76] */ MatcherIndex(9),
-  /* [77] */ MatcherIndex(52),
+  /* [77] */ MatcherIndex(53),
   /* [78] */ MatcherIndex(0),
   /* [79] */ MatcherIndex(1),
   /* [80] */ MatcherIndex(23),
   /* [81] */ MatcherIndex(0),
   /* [82] */ MatcherIndex(2),
-  /* [83] */ MatcherIndex(50),
+  /* [83] */ MatcherIndex(51),
   /* [84] */ MatcherIndex(0),
   /* [85] */ MatcherIndex(1),
   /* [86] */ MatcherIndex(43),
@@ -1953,16 +1974,16 @@
   /* [116] */ MatcherIndex(11),
   /* [117] */ MatcherIndex(10),
   /* [118] */ MatcherIndex(11),
-  /* [119] */ MatcherIndex(69),
+  /* [119] */ MatcherIndex(70),
   /* [120] */ MatcherIndex(13),
   /* [121] */ MatcherIndex(10),
   /* [122] */ MatcherIndex(11),
   /* [123] */ MatcherIndex(1),
   /* [124] */ MatcherIndex(12),
   /* [125] */ MatcherIndex(0),
-  /* [126] */ MatcherIndex(51),
+  /* [126] */ MatcherIndex(52),
   /* [127] */ MatcherIndex(0),
-  /* [128] */ MatcherIndex(49),
+  /* [128] */ MatcherIndex(50),
   /* [129] */ MatcherIndex(0),
   /* [130] */ MatcherIndex(11),
   /* [131] */ MatcherIndex(9),
@@ -2014,7 +2035,7 @@
   /* [177] */ MatcherIndex(0),
   /* [178] */ MatcherIndex(12),
   /* [179] */ MatcherIndex(1),
-  /* [180] */ MatcherIndex(53),
+  /* [180] */ MatcherIndex(54),
   /* [181] */ MatcherIndex(0),
   /* [182] */ MatcherIndex(11),
   /* [183] */ MatcherIndex(5),
@@ -2078,12 +2099,12 @@
   /* [241] */ MatcherIndex(10),
   /* [242] */ MatcherIndex(48),
   /* [243] */ MatcherIndex(0),
-  /* [244] */ MatcherIndex(61),
-  /* [245] */ MatcherIndex(65),
-  /* [246] */ MatcherIndex(73),
-  /* [247] */ MatcherIndex(67),
-  /* [248] */ MatcherIndex(54),
-  /* [249] */ MatcherIndex(62),
+  /* [244] */ MatcherIndex(62),
+  /* [245] */ MatcherIndex(66),
+  /* [246] */ MatcherIndex(74),
+  /* [247] */ MatcherIndex(68),
+  /* [248] */ MatcherIndex(55),
+  /* [249] */ MatcherIndex(63),
   /* [250] */ MatcherIndex(38),
   /* [251] */ MatcherIndex(39),
   /* [252] */ MatcherIndex(40),
@@ -2093,15 +2114,15 @@
   /* [256] */ MatcherIndex(29),
   /* [257] */ MatcherIndex(30),
   /* [258] */ MatcherIndex(6),
-  /* [259] */ MatcherIndex(68),
-  /* [260] */ MatcherIndex(66),
-  /* [261] */ MatcherIndex(58),
-  /* [262] */ MatcherIndex(59),
-  /* [263] */ MatcherIndex(56),
-  /* [264] */ MatcherIndex(57),
-  /* [265] */ MatcherIndex(60),
-  /* [266] */ MatcherIndex(55),
-  /* [267] */ MatcherIndex(72),
+  /* [259] */ MatcherIndex(69),
+  /* [260] */ MatcherIndex(67),
+  /* [261] */ MatcherIndex(59),
+  /* [262] */ MatcherIndex(60),
+  /* [263] */ MatcherIndex(57),
+  /* [264] */ MatcherIndex(58),
+  /* [265] */ MatcherIndex(61),
+  /* [266] */ MatcherIndex(56),
+  /* [267] */ MatcherIndex(73),
 };
 
 static_assert(MatcherIndicesIndex::CanIndex(kMatcherIndices),
diff --git a/src/tint/lang/wgsl/reader/parser/enable_directive_test.cc b/src/tint/lang/wgsl/reader/parser/enable_directive_test.cc
index adb1d1c..34d0d00 100644
--- a/src/tint/lang/wgsl/reader/parser/enable_directive_test.cc
+++ b/src/tint/lang/wgsl/reader/parser/enable_directive_test.cc
@@ -205,7 +205,7 @@
     // Error when unknown extension found
     EXPECT_TRUE(p->has_error());
     EXPECT_EQ(p->error(), R"(1:8: expected extension
-Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_framebuffer_fetch', 'chromium_experimental_pixel_local', 'chromium_experimental_push_constant', 'chromium_experimental_subgroups', 'chromium_internal_dual_source_blending', 'chromium_internal_graphite', 'chromium_internal_relaxed_uniform_layout', 'f16')");
+Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_framebuffer_fetch', 'chromium_experimental_pixel_local', 'chromium_experimental_push_constant', 'chromium_experimental_subgroups', 'chromium_internal_dual_source_blending', 'chromium_internal_graphite', 'chromium_internal_input_attachments', 'chromium_internal_relaxed_uniform_layout', 'f16')");
     auto program = p->program();
     auto& ast = program.AST();
     EXPECT_EQ(ast.Enables().Length(), 0u);
diff --git a/src/tint/lang/wgsl/wgsl.def b/src/tint/lang/wgsl/wgsl.def
index acf3735..2fbd877 100644
--- a/src/tint/lang/wgsl/wgsl.def
+++ b/src/tint/lang/wgsl/wgsl.def
@@ -78,6 +78,9 @@
   chromium_experimental_subgroups
   // A Chromium-specific extension that enables features for graphite
   chromium_internal_graphite
+  // A Chromium-specific extension that enables features for input
+  // attachments
+  chromium_internal_input_attachments
   // A Chromium-specific extension that relaxes memory layout requirements for uniform storage.
   chromium_internal_relaxed_uniform_layout
   // A Chromium-specific extension that enables dual source blending.
@@ -155,6 +158,7 @@
 type texture_storage_3d<F: texel_format, A: access>
 type texture_external
 type packedVec3<T>
+type input_attachment<T>
 
 @display("__modf_result_{T}")        type __modf_result<T>
 @display("__modf_result_vec{N}_{T}") type __modf_result_vec<N: num, T>
diff --git a/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program_test.cc b/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program_test.cc
index 7b9b3cd..766469f 100644
--- a/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program_test.cc
+++ b/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program_test.cc
@@ -151,7 +151,7 @@
     auto* fn = b.Function("f", ty.vec4<f32>(), core::ir::Function::PipelineStage::kVertex);
     fn->SetReturnBuiltin(core::BuiltinValue::kPosition);
 
-    fn->Block()->Append(b.Return(fn, b.Splat(ty.vec4<f32>(), 0_f, 4)));
+    fn->Block()->Append(b.Return(fn, b.Splat<vec4<f32>>(0_f)));
 
     EXPECT_WGSL(R"(
 @vertex
@@ -194,7 +194,7 @@
     fn->SetReturnBuiltin(core::BuiltinValue::kPosition);
     fn->SetReturnInvariant(true);
 
-    fn->Block()->Append(b.Return(fn, b.Splat(ty.vec4<f32>(), 0_f, 4)));
+    fn->Block()->Append(b.Return(fn, b.Splat<vec4<f32>>(0_f)));
 
     EXPECT_WGSL(R"(
 @vertex
@@ -208,7 +208,7 @@
     auto* fn = b.Function("f", ty.vec4<f32>(), core::ir::Function::PipelineStage::kFragment);
     fn->SetReturnLocation(1, std::nullopt);
 
-    fn->Block()->Append(b.Return(fn, b.Splat(ty.vec4<f32>(), 0_f, 4)));
+    fn->Block()->Append(b.Return(fn, b.Splat<vec4<f32>>(0_f)));
 
     EXPECT_WGSL(R"(
 @fragment
diff --git a/src/tint/tint.gni b/src/tint/tint.gni
index 8f3d528..177fa29 100644
--- a/src/tint/tint.gni
+++ b/src/tint/tint.gni
@@ -88,8 +88,8 @@
 ###############################################################################
 # Executables - only built when tint_build_cmds is enabled
 ###############################################################################
-template("tint_executable") {
-  if (tint_build_cmds) {
+if (tint_build_cmds) {
+  template("tint_executable") {
     executable(target_name) {
       forward_variables_from(invoker, "*")
     }