Tint: Support `clip_distances` on HLSL

This patch implements the translation of built-in value `clip_distances`
on HLSL. We should take different ways to do the translation according
to the array length of `clip_distances`:
- Array length is 1:
  float clipDistance : SV_ClipDistance0
- Array length is 2 to 4:
  floatN clipDistance : SV_ClipDistance0
- Array length is 5:
  float4 clipDistance : SV_ClipDistance0
  float clipDistance1 : SV_ClipDistance1
- Array length is 6 to 8:
  float4 clipDistance : SV_ClipDistance0
  floatN clipDistance1 : SV_ClipDistance1

To correctly translate in HLSL backend we have to use
`core::BuiltinValue::kClipDistances` as `SV_ClipDistance0` and the
internal attribute `HLSLClipDistance1` as `SV_ClipDistance1`.

Bug: chromium:358408571
Change-Id: I5d0843cc846190c60a028e48fb8e13ae9a88a5bb
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/203775
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
diff --git a/src/tint/lang/hlsl/writer/ast_printer/ast_printer.cc b/src/tint/lang/hlsl/writer/ast_printer/ast_printer.cc
index e7fd33a..ce29693 100644
--- a/src/tint/lang/hlsl/writer/ast_printer/ast_printer.cc
+++ b/src/tint/lang/hlsl/writer/ast_printer/ast_printer.cc
@@ -397,6 +397,7 @@
                 wgsl::Extension::kChromiumExperimentalPushConstant,
                 wgsl::Extension::kChromiumExperimentalSubgroups,
                 wgsl::Extension::kChromiumInternalGraphite,
+                wgsl::Extension::kClipDistances,
                 wgsl::Extension::kF16,
                 wgsl::Extension::kDualSourceBlending,
                 wgsl::Extension::kSubgroups,
@@ -445,7 +446,7 @@
                     // instead of true structure.
                     // Structures used as uniform buffer are read from an array of
                     // vectors instead of true structure.
-                    return EmitStructType(current_buffer_, ty);
+                    return EmitStructType(current_buffer_, ty, str->members);
                 }
                 return true;
             },
@@ -3635,6 +3636,8 @@
             return "SV_SampleIndex";
         case core::BuiltinValue::kSampleMask:
             return "SV_Coverage";
+        case core::BuiltinValue::kClipDistances:
+            return "SV_ClipDistance0";
         default:
             break;
     }
@@ -4590,16 +4593,24 @@
     return true;
 }
 
-bool ASTPrinter::EmitStructType(TextBuffer* b, const core::type::Struct* str) {
+bool ASTPrinter::EmitStructType(TextBuffer* b,
+                                const core::type::Struct* str,
+                                VectorRef<const ast::StructMember*> ast_struct_members) {
     auto it = emitted_structs_.emplace(str);
     if (!it.second) {
         return true;
     }
 
+    const auto struct_type_members = str->Members();
+    size_t struct_type_member_length = struct_type_members.Length();
+    TINT_ASSERT(ast_struct_members.IsEmpty() ||
+                (struct_type_member_length == ast_struct_members.Length()));
+
     Line(b) << "struct " << StructName(str) << " {";
     {
         ScopedIndent si(b);
-        for (auto* mem : str->Members()) {
+        for (size_t i = 0; i < struct_type_member_length; ++i) {
+            auto* mem = struct_type_members[i];
             auto mem_name = mem->Name().Name();
             auto* ty = mem->Type();
             auto out = Line(b);
@@ -4655,6 +4666,11 @@
                 // See discussion here: https://github.com/gpuweb/gpuweb/issues/893
                 pre += "precise ";
             }
+            if (!ast_struct_members.IsEmpty() &&
+                ast::HasAttribute<ast::transform::CanonicalizeEntryPointIO::HLSLClipDistance1>(
+                    ast_struct_members[i]->attributes)) {
+                post += " : SV_ClipDistance1";
+            }
 
             out << pre;
             if (!EmitTypeAndName(out, ty, core::AddressSpace::kUndefined, core::Access::kReadWrite,
diff --git a/src/tint/lang/hlsl/writer/ast_printer/ast_printer.h b/src/tint/lang/hlsl/writer/ast_printer/ast_printer.h
index 3a16948..81ebfd7 100644
--- a/src/tint/lang/hlsl/writer/ast_printer/ast_printer.h
+++ b/src/tint/lang/hlsl/writer/ast_printer/ast_printer.h
@@ -462,8 +462,11 @@
     /// this function will simply return `true` without emitting anything.
     /// @param buffer the text buffer that the type declaration will be written to
     /// @param ty the struct to generate
+    /// @param ast_struct_members the definition of struct members in the AST if not empty.
     /// @returns true if the struct is emitted
-    bool EmitStructType(TextBuffer* buffer, const core::type::Struct* ty);
+    bool EmitStructType(TextBuffer* buffer,
+                        const core::type::Struct* ty,
+                        VectorRef<const ast::StructMember*> ast_struct_members = Empty);
     /// Handles a unary op expression
     /// @param out the output stream
     /// @param expr the expression to emit
diff --git a/src/tint/lang/wgsl/ast/disable_validation_attribute.cc b/src/tint/lang/wgsl/ast/disable_validation_attribute.cc
index 1e607bb..59f1ad2 100644
--- a/src/tint/lang/wgsl/ast/disable_validation_attribute.cc
+++ b/src/tint/lang/wgsl/ast/disable_validation_attribute.cc
@@ -60,6 +60,8 @@
             return "disable_validation__ignore_pointer_aliasing";
         case DisabledValidation::kIgnoreStructMemberLimit:
             return "disable_validation__ignore_struct_member";
+        case DisabledValidation::kIgnoreClipDistancesType:
+            return "disable_validation__ignore_clip_distances_type";
     }
     return "<invalid>";
 }
diff --git a/src/tint/lang/wgsl/ast/disable_validation_attribute.h b/src/tint/lang/wgsl/ast/disable_validation_attribute.h
index 4591ba9..c6127bc 100644
--- a/src/tint/lang/wgsl/ast/disable_validation_attribute.h
+++ b/src/tint/lang/wgsl/ast/disable_validation_attribute.h
@@ -60,6 +60,9 @@
     kIgnorePointerAliasing,
     /// When applied to a struct, validation of max number of members is skipped.
     kIgnoreStructMemberLimit,
+    /// When applied to a struct member, validation of the type of the builtin `clip_distances` is
+    /// skipped.
+    kIgnoreClipDistancesType,
 };
 
 /// An internal attribute used to tell the validator to ignore specific
diff --git a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.cc b/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.cc
index c9361e9..e31f686 100644
--- a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.cc
+++ b/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.cc
@@ -49,6 +49,7 @@
 TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::CanonicalizeEntryPointIO);
 TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::CanonicalizeEntryPointIO::Config);
 TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::CanonicalizeEntryPointIO::HLSLWaveIntrinsic);
+TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::CanonicalizeEntryPointIO::HLSLClipDistance1);
 
 namespace tint::ast::transform {
 
@@ -165,7 +166,8 @@
     Hashmap<const BuiltinAttribute*, core::BuiltinValue, 16> builtin_attrs;
     /// A map of builtin values to HLSL wave intrinsic functions.
     Hashmap<core::BuiltinValue, Symbol, 2> wave_intrinsics;
-    /// The array length of the struct member with builtin attribute `clip_distances`.
+    /// The array length of the struct member with builtin attribute `clip_distances`. Now it is
+    /// only set and used on HLSL and MSL backends.
     uint32_t clip_distances_size = 0;
 
     /// Constructor
@@ -409,7 +411,7 @@
                                      core::InterpolationSampling::kUndefined));
         }
 
-        if (cfg.shader_style == ShaderStyle::kMsl &&
+        if ((cfg.shader_style == ShaderStyle::kMsl || cfg.shader_style == ShaderStyle::kHlsl) &&
             func_ast->PipelineStage() == PipelineStage::kVertex &&
             builtin_attr == core::BuiltinValue::kClipDistances) {
             const auto* arrayType = type->As<core::type::Array>();
@@ -676,6 +678,127 @@
         wrapper_ep_parameters.Push(param);
     }
 
+    /// Get an existing member symbol from a set of member names or create a new symbol
+    /// @param member_names the set of the existing member names
+    /// @param symbol_name the name of the symbol
+    /// @returns the symbol that represents the member name we want
+    Symbol GetOrCreateMemberName(std::unordered_set<std::string>* member_names,
+                                 std::string symbol_name) {
+        Symbol member_name;
+        if (member_names->count(symbol_name)) {
+            member_name = b.Symbols().New(symbol_name);
+        } else {
+            member_name = b.Symbols().Register(symbol_name);
+        }
+        member_names->insert(member_name.Name());
+        return member_name;
+    }
+
+    /// Handle `clip_distances` in MSL
+    /// @param assignments the assignments to the members of the output wrapper struct
+    /// @param member_names the set that contains all the member names in the output wrapper struct
+    /// @param clip_distances_inner_array_name the `clip_distances` member in inner struct
+    /// @param wrapper_struct_name the name of the output wrapper struct
+    /// @param old_struct_member the information of the old output wrapper struct member
+    void HandleClipDistancesInMSL(tint::Vector<const Statement*, 8>* assignments,
+                                  std::unordered_set<std::string>* member_names,
+                                  const Symbol& clip_distances_inner_array_name,
+                                  const Symbol& wrapper_struct_name,
+                                  const OutputValue& old_struct_member) {
+        auto new_member = GetOrCreateMemberName(member_names, "clip_distance");
+
+        for (uint32_t i = 0; i < clip_distances_size; ++i) {
+            assignments->Push(
+                b.Assign(b.IndexAccessor(b.MemberAccessor(wrapper_struct_name, new_member), u32(i)),
+                         b.IndexAccessor(clip_distances_inner_array_name, u32(i))));
+        }
+        wrapper_struct_output_members.Push({
+            /* member */ b.Member(new_member, old_struct_member.type,
+                                  std::move(old_struct_member.attributes)),
+            /* location */ std::nullopt,
+            /* blend_src */ std::nullopt,
+            /* color */ std::nullopt,
+        });
+    }
+
+    /// Handle `clip_distances` in HLSL
+    /// @param assignments the assignments to the members of the output wrapper struct
+    /// @param member_names the set that contains all the member names in the output wrapper struct
+    /// @param clip_distances_inner_array_name the `clip_distances` member in inner struct
+    /// @param wrapper_struct_name the symbol of the output wrapper struct
+    /// @param old_struct_member_attributes the attributes of the old `clip_distances` member
+    void HandleClipDistancesInHLSL(
+        tint::Vector<const Statement*, 8>* assignments,
+        std::unordered_set<std::string>* member_names,
+        const Symbol& clip_distances_inner_array_name,
+        const Symbol& wrapper_struct_name,
+        tint::Vector<const Attribute*, 8>&& old_struct_member_attributes) {
+        auto TranslateClipDistancesIntoF32 =
+            [&](const Symbol& new_member, core::u32 inner_array_index,
+                tint::VectorRef<const ast::Attribute*>&& new_member_attributes) {
+                assignments->Push(
+                    b.Assign(b.MemberAccessor(wrapper_struct_name, new_member),
+                             b.IndexAccessor(clip_distances_inner_array_name, inner_array_index)));
+                wrapper_struct_output_members.Push({
+                    /* member */ b.Member(new_member, b.ty.f32(), std::move(new_member_attributes)),
+                    /* location */ std::nullopt,
+                    /* blend_src */ std::nullopt,
+                    /* color */ std::nullopt,
+                });
+            };
+
+        auto TranslateClipDistancesIntoVector =
+            [&](const Symbol& new_member, uint32_t vector_size,
+                tint::VectorRef<const ast::Attribute*>&& new_member_attributes) {
+                for (uint32_t i = 0; i < vector_size; ++i) {
+                    assignments->Push(b.Assign(
+                        b.IndexAccessor(b.MemberAccessor(wrapper_struct_name, new_member), u32(i)),
+                        b.IndexAccessor(clip_distances_inner_array_name, u32(i))));
+                }
+                wrapper_struct_output_members.Push({
+                    /* member */ b.Member(new_member, b.ty.vec(b.ty.f32(), vector_size),
+                                          std::move(new_member_attributes)),
+                    /* location */ std::nullopt,
+                    /* blend_src */ std::nullopt,
+                    /* color */ std::nullopt,
+                });
+            };
+
+        // float clip_distance_0 : SV_ClipDistance0;
+        auto attribute_vector_0 = std::move(old_struct_member_attributes);
+        attribute_vector_0.Push(b.Disable(ast::DisabledValidation::kIgnoreClipDistancesType));
+        auto new_member_0 = GetOrCreateMemberName(member_names, "clip_distance_0");
+        if (clip_distances_size == 1u) {
+            TranslateClipDistancesIntoF32(new_member_0, 0_u, std::move(attribute_vector_0));
+            return;
+        }
+
+        // floatN clip_distance_0 : SV_ClipDistance0;
+        uint32_t clip_distance0_size = std::min(clip_distances_size, 4u);
+        TranslateClipDistancesIntoVector(new_member_0, clip_distance0_size,
+                                         std::move(attribute_vector_0));
+
+        // It is enough to just generate SV_ClipDistance0.
+        if (clip_distances_size <= 4u) {
+            return;
+        }
+
+        // float clip_distance_1 : SV_ClipDistance1;
+        auto attribute_vector_1 =
+            Vector{b.ASTNodes().Create<HLSLClipDistance1>(b.ID(), b.AllocateNodeID())};
+        auto new_member_1 = GetOrCreateMemberName(member_names, "clip_distance_1");
+
+        if (clip_distances_size == 5u) {
+            TranslateClipDistancesIntoF32(new_member_1, 4_u, std::move(attribute_vector_1));
+            return;
+        }
+
+        // floatN clip_distance_1 : SV_ClipDistance1;
+        uint32_t clip_distances1_size = clip_distances_size - 4u;
+        TranslateClipDistancesIntoVector(new_member_1, clip_distances1_size,
+                                         std::move(attribute_vector_1));
+    }
+
     /// Create and return the wrapper function's struct result object.
     /// @returns the struct type
     Struct* CreateOutputStruct() {
@@ -686,29 +809,39 @@
         // Create the struct members and their corresponding assignment statements.
         std::unordered_set<std::string> member_names;
         for (auto& outval : wrapper_output_values) {
-            // Use the original output name, unless that is already taken.
-            Symbol name;
-            if (member_names.count(outval.name)) {
-                name = b.Symbols().New(outval.name);
-            } else {
-                name = b.Symbols().Register(outval.name);
-            }
-            member_names.insert(name.Name());
-
             auto* builtin_attribute = GetAttribute<BuiltinAttribute>(outval.attributes);
-            if (cfg.shader_style == ShaderStyle::kMsl && builtin_attribute != nullptr &&
+            if ((cfg.shader_style == ShaderStyle::kMsl || cfg.shader_style == ShaderStyle::kHlsl) &&
+                builtin_attribute != nullptr &&
                 builtin_attribute->builtin == core::BuiltinValue::kClipDistances) {
                 Symbol clip_distances_inner_array = b.Symbols().New("tmp_inner_clip_distances");
-                assignments.Push(b.Decl(b.Let(clip_distances_inner_array, outval.value)));
-                for (uint32_t i = 0; i < clip_distances_size; ++i) {
-                    assignments.Push(
-                        b.Assign(b.IndexAccessor(b.MemberAccessor(wrapper_result, name), u32(i)),
-                                 b.IndexAccessor(clip_distances_inner_array, u32(i))));
+
+                switch (cfg.shader_style) {
+                    case ShaderStyle::kMsl:
+                        assignments.Push(b.Decl(b.Let(clip_distances_inner_array, outval.value)));
+                        HandleClipDistancesInMSL(&assignments, &member_names,
+                                                 clip_distances_inner_array, wrapper_result,
+                                                 outval);
+                        break;
+                    case ShaderStyle::kHlsl:
+                        // Consume outval.type in the let statement as in HLSL `SV_ClipDistance`
+                        // always has different type.
+                        assignments.Push(
+                            b.Decl(b.Let(clip_distances_inner_array, outval.type, outval.value)));
+                        HandleClipDistancesInHLSL(&assignments, &member_names,
+                                                  clip_distances_inner_array, wrapper_result,
+                                                  std::move(outval.attributes));
+                        break;
+                    default:
+                        break;
                 }
-            } else {
-                assignments.Push(b.Assign(b.MemberAccessor(wrapper_result, name), outval.value));
+                continue;
             }
 
+            // Use the original output name, unless that is already taken.
+            Symbol name = GetOrCreateMemberName(&member_names, outval.name);
+
+            assignments.Push(b.Assign(b.MemberAccessor(wrapper_result, name), outval.value));
+
             wrapper_struct_output_members.Push({
                 /* member */ b.Member(name, outval.type, std::move(outval.attributes)),
                 /* location */ outval.location,
@@ -1068,4 +1201,19 @@
         ctx.dst->ID(), ctx.dst->AllocateNodeID(), op);
 }
 
+CanonicalizeEntryPointIO::HLSLClipDistance1::HLSLClipDistance1(GenerationID pid, NodeID nid)
+    : Base(pid, nid, Empty) {}
+
+CanonicalizeEntryPointIO::HLSLClipDistance1::~HLSLClipDistance1() = default;
+
+std::string CanonicalizeEntryPointIO::HLSLClipDistance1::InternalName() const {
+    return "SV_ClipDistance1";
+}
+
+const CanonicalizeEntryPointIO::HLSLClipDistance1*
+CanonicalizeEntryPointIO::HLSLClipDistance1::Clone(ast::CloneContext& ctx) const {
+    return ctx.dst->ASTNodes().Create<CanonicalizeEntryPointIO::HLSLClipDistance1>(
+        ctx.dst->ID(), ctx.dst->AllocateNodeID());
+}
+
 }  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h b/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h
index 9899e3d..928fe56 100644
--- a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h
+++ b/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h
@@ -185,6 +185,25 @@
         const Op op;
     };
 
+    /// HLSLClipDistance1 is an InternalAttribute that is used to represent `SV_ClipDistance1`.
+    class HLSLClipDistance1 final : public Castable<HLSLClipDistance1, InternalAttribute> {
+      public:
+        /// Constructor
+        /// @param pid the identifier of the program that owns this node
+        /// @param nid the unique node identifier
+        HLSLClipDistance1(GenerationID pid, NodeID nid);
+        /// Destructor
+        ~HLSLClipDistance1() override;
+
+        /// @copydoc InternalAttribute::InternalName
+        std::string InternalName() const override;
+
+        /// Performs a deep clone of this object using the program::CloneContext `ctx`.
+        /// @param ctx the clone context
+        /// @return the newly cloned object
+        const HLSLClipDistance1* Clone(CloneContext& ctx) const override;
+    };
+
     /// Constructor
     CanonicalizeEntryPointIO();
     ~CanonicalizeEntryPointIO() override;
diff --git a/src/tint/lang/wgsl/resolver/validator.cc b/src/tint/lang/wgsl/resolver/validator.cc
index 4cf6a62..9056664 100644
--- a/src/tint/lang/wgsl/resolver/validator.cc
+++ b/src/tint/lang/wgsl/resolver/validator.cc
@@ -984,7 +984,8 @@
 bool Validator::BuiltinAttribute(const ast::BuiltinAttribute* attr,
                                  const core::type::Type* storage_ty,
                                  ast::PipelineStage stage,
-                                 const bool is_input) const {
+                                 const bool is_input,
+                                 const bool ignore_clip_distances_type_validation) const {
     auto* type = storage_ty->UnwrapRef();
     bool is_stage_mismatch = false;
     bool is_output = !is_input;
@@ -1128,7 +1129,8 @@
                 return false;
             }
             auto* arr = type->As<sem::Array>();
-            if (!(arr && arr->ElemType()->Is<core::type::F32>() &&
+            if (!ignore_clip_distances_type_validation &&
+                !(arr && arr->ElemType()->Is<core::type::F32>() &&
                   arr->ConstantCount().has_value() &&
                   *arr->ConstantCount() <= kMaxClipDistancesSize)) {
                 AddError(attr->source)
@@ -1346,6 +1348,8 @@
         const ast::BlendSrcAttribute* blend_src_attribute = nullptr;
         const ast::InterpolateAttribute* interpolate_attribute = nullptr;
         const ast::InvariantAttribute* invariant_attribute = nullptr;
+        bool ignore_clip_distances_type =
+            IsValidationDisabled(attrs, ast::DisabledValidation::kIgnoreClipDistancesType);
         for (auto* attr : attrs) {
             bool ok = Switch(
                 attr,  //
@@ -1370,9 +1374,9 @@
                         return false;
                     }
 
-                    if (!BuiltinAttribute(
-                            builtin_attr, ty, stage,
-                            /* is_input */ param_or_ret == ParamOrRetType::kParameter)) {
+                    if (!BuiltinAttribute(builtin_attr, ty, stage,
+                                          /* is_input */ param_or_ret == ParamOrRetType::kParameter,
+                                          ignore_clip_distances_type)) {
                         return false;
                     }
                     builtins.Add(builtin);
@@ -2509,6 +2513,8 @@
         const ast::ColorAttribute* color_attribute = nullptr;
         const ast::InvariantAttribute* invariant_attribute = nullptr;
         const ast::InterpolateAttribute* interpolate_attribute = nullptr;
+        bool ignore_clip_distances_type = IsValidationDisabled(
+            member->Declaration()->attributes, ast::DisabledValidation::kIgnoreClipDistancesType);
         for (auto* attr : member->Declaration()->attributes) {
             bool ok = Switch(
                 attr,  //
@@ -2533,7 +2539,7 @@
                 },
                 [&](const ast::BuiltinAttribute* builtin_attr) {
                     if (!BuiltinAttribute(builtin_attr, member->Type(), stage,
-                                          /* is_input */ false)) {
+                                          /* is_input */ false, ignore_clip_distances_type)) {
                         return false;
                     }
                     if (builtin_attr->builtin == core::BuiltinValue::kPosition) {
diff --git a/src/tint/lang/wgsl/resolver/validator.h b/src/tint/lang/wgsl/resolver/validator.h
index 82f3112..1db8f64 100644
--- a/src/tint/lang/wgsl/resolver/validator.h
+++ b/src/tint/lang/wgsl/resolver/validator.h
@@ -254,11 +254,13 @@
     /// @param storage_type the attribute storage type
     /// @param stage the current pipeline stage
     /// @param is_input true if this is an input attribute
+    /// @param ignore_clip_distances_type_validation true if ignore type check on clip_distances
     /// @returns true on success, false otherwise.
     bool BuiltinAttribute(const ast::BuiltinAttribute* attr,
                           const core::type::Type* storage_type,
                           ast::PipelineStage stage,
-                          const bool is_input) const;
+                          const bool is_input,
+                          const bool ignore_clip_distances_type_validation = false) const;
 
     /// Validates a continue statement
     /// @param stmt the continue statement to validate
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_1.wgsl.expected.dxc.hlsl b/test/tint/extensions/clip_distances/clip_distances_size_1.wgsl.expected.dxc.hlsl
index 6d69a54..8a9a6d3 100644
--- a/test/tint/extensions/clip_distances/clip_distances_size_1.wgsl.expected.dxc.hlsl
+++ b/test/tint/extensions/clip_distances/clip_distances_size_1.wgsl.expected.dxc.hlsl
@@ -1,23 +1,22 @@
-SKIP: FAILED
-
-
-enable clip_distances;
-
 struct VertexOutputs {
-  @builtin(position)
-  position : vec4<f32>,
-  @builtin(clip_distances)
-  clipDistance : array<f32, 1>,
+  float4 position;
+  float clipDistance[1];
+};
+struct tint_symbol {
+  float4 position : SV_Position;
+  float clip_distance_0 : SV_ClipDistance0;
+};
+
+VertexOutputs main_inner() {
+  VertexOutputs tint_symbol_1 = {float4(1.0f, 2.0f, 3.0f, 4.0f), (float[1])0};
+  return tint_symbol_1;
 }
 
-@vertex
-fn main() -> VertexOutputs {
-  return VertexOutputs(vec4<f32>(1.0, 2.0, 3.0, 4.0), array<f32, 1>(0.0));
+tint_symbol main() {
+  VertexOutputs inner_result = main_inner();
+  tint_symbol wrapper_result = (tint_symbol)0;
+  wrapper_result.position = inner_result.position;
+  float tmp_inner_clip_distances[1] = inner_result.clipDistance;
+  wrapper_result.clip_distance_0 = tmp_inner_clip_distances[0u];
+  return wrapper_result;
 }
-
-Failed to generate: <dawn>/test/tint/extensions/clip_distances/clip_distances_size_1.wgsl:1:8 error: HLSL backend does not support extension 'clip_distances'
-enable clip_distances;
-       ^^^^^^^^^^^^^^
-
-
-tint executable returned error: exit status 1
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_1.wgsl.expected.fxc.hlsl b/test/tint/extensions/clip_distances/clip_distances_size_1.wgsl.expected.fxc.hlsl
index 6d69a54..8a9a6d3 100644
--- a/test/tint/extensions/clip_distances/clip_distances_size_1.wgsl.expected.fxc.hlsl
+++ b/test/tint/extensions/clip_distances/clip_distances_size_1.wgsl.expected.fxc.hlsl
@@ -1,23 +1,22 @@
-SKIP: FAILED
-
-
-enable clip_distances;
-
 struct VertexOutputs {
-  @builtin(position)
-  position : vec4<f32>,
-  @builtin(clip_distances)
-  clipDistance : array<f32, 1>,
+  float4 position;
+  float clipDistance[1];
+};
+struct tint_symbol {
+  float4 position : SV_Position;
+  float clip_distance_0 : SV_ClipDistance0;
+};
+
+VertexOutputs main_inner() {
+  VertexOutputs tint_symbol_1 = {float4(1.0f, 2.0f, 3.0f, 4.0f), (float[1])0};
+  return tint_symbol_1;
 }
 
-@vertex
-fn main() -> VertexOutputs {
-  return VertexOutputs(vec4<f32>(1.0, 2.0, 3.0, 4.0), array<f32, 1>(0.0));
+tint_symbol main() {
+  VertexOutputs inner_result = main_inner();
+  tint_symbol wrapper_result = (tint_symbol)0;
+  wrapper_result.position = inner_result.position;
+  float tmp_inner_clip_distances[1] = inner_result.clipDistance;
+  wrapper_result.clip_distance_0 = tmp_inner_clip_distances[0u];
+  return wrapper_result;
 }
-
-Failed to generate: <dawn>/test/tint/extensions/clip_distances/clip_distances_size_1.wgsl:1:8 error: HLSL backend does not support extension 'clip_distances'
-enable clip_distances;
-       ^^^^^^^^^^^^^^
-
-
-tint executable returned error: exit status 1
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_1.wgsl.expected.glsl b/test/tint/extensions/clip_distances/clip_distances_size_1.wgsl.expected.glsl
index 7d7ee00..1db6fd3 100644
--- a/test/tint/extensions/clip_distances/clip_distances_size_1.wgsl.expected.glsl
+++ b/test/tint/extensions/clip_distances/clip_distances_size_1.wgsl.expected.glsl
@@ -19,3 +19,5 @@
 enable clip_distances;
        ^^^^^^^^^^^^^^
 
+
+tint executable returned error: exit status 1
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_1.wgsl.expected.ir.msl b/test/tint/extensions/clip_distances/clip_distances_size_1.wgsl.expected.ir.msl
index 10e52d9..af78b72 100644
--- a/test/tint/extensions/clip_distances/clip_distances_size_1.wgsl.expected.ir.msl
+++ b/test/tint/extensions/clip_distances/clip_distances_size_1.wgsl.expected.ir.msl
@@ -1,5 +1,3 @@
-SKIP: FAILED
-
 #include <metal_stdlib>
 using namespace metal;
 
@@ -33,10 +31,3 @@
   VertexOutputs const v = tint_symbol_inner();
   return tint_symbol_outputs{.VertexOutputs_position=v.position, .VertexOutputs_clipDistance=v.clipDistance};
 }
-program_source:23:53: error: type 'tint_array<float, 1>' is not valid for attribute 'clip_distance'
-  tint_array<float, 1> VertexOutputs_clipDistance [[clip_distance]];
-                                                    ^~~~~~~~~~~~~
-program_source:30:8: error: invalid return type 'tint_symbol_outputs' for vertex function
-vertex tint_symbol_outputs tint_symbol() {
-       ^
-
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_1.wgsl.expected.msl b/test/tint/extensions/clip_distances/clip_distances_size_1.wgsl.expected.msl
index fd5ca23..05ce84e 100644
--- a/test/tint/extensions/clip_distances/clip_distances_size_1.wgsl.expected.msl
+++ b/test/tint/extensions/clip_distances/clip_distances_size_1.wgsl.expected.msl
@@ -21,7 +21,7 @@
 
 struct tint_symbol_1 {
   float4 position [[position]];
-  float clipDistance [[clip_distance]] [1];
+  float clip_distance [[clip_distance]] [1];
 };
 
 VertexOutputs tint_symbol_inner() {
@@ -34,7 +34,7 @@
   tint_symbol_1 wrapper_result = {};
   wrapper_result.position = inner_result.position;
   tint_array<float, 1> const tmp_inner_clip_distances = inner_result.clipDistance;
-  wrapper_result.clipDistance[0u] = tmp_inner_clip_distances[0u];
+  wrapper_result.clip_distance[0u] = tmp_inner_clip_distances[0u];
   return wrapper_result;
 }
 
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl b/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl
new file mode 100644
index 0000000..5e0487b
--- /dev/null
+++ b/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl
@@ -0,0 +1,13 @@
+enable clip_distances;
+
+struct VertexOutputs {
+  @builtin(position)
+  position : vec4<f32>,
+  @builtin(clip_distances)
+  clipDistance : array<f32, 3>,
+}
+
+@vertex
+fn main() -> VertexOutputs {
+  return VertexOutputs(vec4<f32>(1.0, 2.0, 3.0, 4.0), array<f32, 3>(0.0, 0.0, 0.0));
+}
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.dxc.hlsl b/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.dxc.hlsl
new file mode 100644
index 0000000..8b76aeb
--- /dev/null
+++ b/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.dxc.hlsl
@@ -0,0 +1,24 @@
+struct VertexOutputs {
+  float4 position;
+  float clipDistance[3];
+};
+struct tint_symbol {
+  float4 position : SV_Position;
+  float3 clip_distance_0 : SV_ClipDistance0;
+};
+
+VertexOutputs main_inner() {
+  VertexOutputs tint_symbol_1 = {float4(1.0f, 2.0f, 3.0f, 4.0f), (float[3])0};
+  return tint_symbol_1;
+}
+
+tint_symbol main() {
+  VertexOutputs inner_result = main_inner();
+  tint_symbol wrapper_result = (tint_symbol)0;
+  wrapper_result.position = inner_result.position;
+  float tmp_inner_clip_distances[3] = inner_result.clipDistance;
+  wrapper_result.clip_distance_0[0u] = tmp_inner_clip_distances[0u];
+  wrapper_result.clip_distance_0[1u] = tmp_inner_clip_distances[1u];
+  wrapper_result.clip_distance_0[2u] = tmp_inner_clip_distances[2u];
+  return wrapper_result;
+}
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.fxc.hlsl b/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.fxc.hlsl
new file mode 100644
index 0000000..8b76aeb
--- /dev/null
+++ b/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.fxc.hlsl
@@ -0,0 +1,24 @@
+struct VertexOutputs {
+  float4 position;
+  float clipDistance[3];
+};
+struct tint_symbol {
+  float4 position : SV_Position;
+  float3 clip_distance_0 : SV_ClipDistance0;
+};
+
+VertexOutputs main_inner() {
+  VertexOutputs tint_symbol_1 = {float4(1.0f, 2.0f, 3.0f, 4.0f), (float[3])0};
+  return tint_symbol_1;
+}
+
+tint_symbol main() {
+  VertexOutputs inner_result = main_inner();
+  tint_symbol wrapper_result = (tint_symbol)0;
+  wrapper_result.position = inner_result.position;
+  float tmp_inner_clip_distances[3] = inner_result.clipDistance;
+  wrapper_result.clip_distance_0[0u] = tmp_inner_clip_distances[0u];
+  wrapper_result.clip_distance_0[1u] = tmp_inner_clip_distances[1u];
+  wrapper_result.clip_distance_0[2u] = tmp_inner_clip_distances[2u];
+  return wrapper_result;
+}
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.glsl b/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.glsl
new file mode 100644
index 0000000..39bbe86
--- /dev/null
+++ b/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.glsl
@@ -0,0 +1,23 @@
+SKIP: FAILED
+
+
+enable clip_distances;
+
+struct VertexOutputs {
+  @builtin(position)
+  position : vec4<f32>,
+  @builtin(clip_distances)
+  clipDistance : array<f32, 3>,
+}
+
+@vertex
+fn tint_symbol() -> VertexOutputs {
+  return VertexOutputs(vec4<f32>(1.0, 2.0, 3.0, 4.0), array<f32, 3>(0.0, 0.0, 0.0));
+}
+
+Failed to generate: <dawn>/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl:1:8 error: GLSL backend does not support extension 'clip_distances'
+enable clip_distances;
+       ^^^^^^^^^^^^^^
+
+
+tint executable returned error: exit status 1
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.ir.dxc.hlsl b/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.ir.dxc.hlsl
new file mode 100644
index 0000000..b0160ea
--- /dev/null
+++ b/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.ir.dxc.hlsl
@@ -0,0 +1,11 @@
+SKIP: FAILED
+
+..\..\src\tint\lang\hlsl\writer\raise\shader_io.cc:101 internal compiler error: TINT_UNREACHABLE 
+********************************************************************
+*  The tint shader compiler has encountered an unexpected error.   *
+*                                                                  *
+*  Please help us fix this issue by submitting a bug report at     *
+*  crbug.com/tint with the source program that triggered the bug.  *
+********************************************************************
+
+tint executable returned error: exit status 0xc000001d
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.ir.fxc.hlsl b/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.ir.fxc.hlsl
new file mode 100644
index 0000000..b0160ea
--- /dev/null
+++ b/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.ir.fxc.hlsl
@@ -0,0 +1,11 @@
+SKIP: FAILED
+
+..\..\src\tint\lang\hlsl\writer\raise\shader_io.cc:101 internal compiler error: TINT_UNREACHABLE 
+********************************************************************
+*  The tint shader compiler has encountered an unexpected error.   *
+*                                                                  *
+*  Please help us fix this issue by submitting a bug report at     *
+*  crbug.com/tint with the source program that triggered the bug.  *
+********************************************************************
+
+tint executable returned error: exit status 0xc000001d
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.ir.msl b/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.ir.msl
new file mode 100644
index 0000000..23310a1
--- /dev/null
+++ b/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.ir.msl
@@ -0,0 +1,33 @@
+#include <metal_stdlib>
+using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+  const constant T& operator[](size_t i) const constant { return elements[i]; }
+  device T& operator[](size_t i) device { return elements[i]; }
+  const device T& operator[](size_t i) const device { return elements[i]; }
+  thread T& operator[](size_t i) thread { return elements[i]; }
+  const thread T& operator[](size_t i) const thread { return elements[i]; }
+  threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+  const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+  T elements[N];
+};
+
+struct VertexOutputs {
+  float4 position;
+  tint_array<float, 3> clipDistance;
+};
+
+struct tint_symbol_outputs {
+  float4 VertexOutputs_position [[position]];
+  tint_array<float, 3> VertexOutputs_clipDistance [[clip_distance]];
+};
+
+VertexOutputs tint_symbol_inner() {
+  return VertexOutputs{.position=float4(1.0f, 2.0f, 3.0f, 4.0f), .clipDistance=tint_array<float, 3>{}};
+}
+
+vertex tint_symbol_outputs tint_symbol() {
+  VertexOutputs const v = tint_symbol_inner();
+  return tint_symbol_outputs{.VertexOutputs_position=v.position, .VertexOutputs_clipDistance=v.clipDistance};
+}
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.msl b/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.msl
new file mode 100644
index 0000000..972fd0e
--- /dev/null
+++ b/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.msl
@@ -0,0 +1,42 @@
+#include <metal_stdlib>
+
+using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
+struct VertexOutputs {
+  float4 position;
+  tint_array<float, 3> clipDistance;
+};
+
+struct tint_symbol_1 {
+  float4 position [[position]];
+  float clip_distance [[clip_distance]] [3];
+};
+
+VertexOutputs tint_symbol_inner() {
+  VertexOutputs const tint_symbol_2 = VertexOutputs{.position=float4(1.0f, 2.0f, 3.0f, 4.0f), .clipDistance=tint_array<float, 3>{}};
+  return tint_symbol_2;
+}
+
+vertex tint_symbol_1 tint_symbol() {
+  VertexOutputs const inner_result = tint_symbol_inner();
+  tint_symbol_1 wrapper_result = {};
+  wrapper_result.position = inner_result.position;
+  tint_array<float, 3> const tmp_inner_clip_distances = inner_result.clipDistance;
+  wrapper_result.clip_distance[0u] = tmp_inner_clip_distances[0u];
+  wrapper_result.clip_distance[1u] = tmp_inner_clip_distances[1u];
+  wrapper_result.clip_distance[2u] = tmp_inner_clip_distances[2u];
+  return wrapper_result;
+}
+
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.spvasm b/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.spvasm
new file mode 100644
index 0000000..f3e7b9e
--- /dev/null
+++ b/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.spvasm
@@ -0,0 +1,59 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 1
+; Bound: 30
+; Schema: 0
+               OpCapability Shader
+               OpCapability ClipDistance
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %main_position_Output %main_clip_distances_Output %main___point_size_Output
+               OpName %main_position_Output "main_position_Output"
+               OpName %main_clip_distances_Output "main_clip_distances_Output"
+               OpName %main___point_size_Output "main___point_size_Output"
+               OpName %main_inner "main_inner"
+               OpMemberName %VertexOutputs 0 "position"
+               OpMemberName %VertexOutputs 1 "clipDistance"
+               OpName %VertexOutputs "VertexOutputs"
+               OpName %main "main"
+               OpDecorate %main_position_Output BuiltIn Position
+               OpDecorate %_arr_float_uint_3 ArrayStride 4
+               OpDecorate %main_clip_distances_Output BuiltIn ClipDistance
+               OpDecorate %main___point_size_Output BuiltIn PointSize
+               OpMemberDecorate %VertexOutputs 0 Offset 0
+               OpMemberDecorate %VertexOutputs 1 Offset 16
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%main_position_Output = OpVariable %_ptr_Output_v4float Output
+       %uint = OpTypeInt 32 0
+     %uint_3 = OpConstant %uint 3
+%_arr_float_uint_3 = OpTypeArray %float %uint_3
+%_ptr_Output__arr_float_uint_3 = OpTypePointer Output %_arr_float_uint_3
+%main_clip_distances_Output = OpVariable %_ptr_Output__arr_float_uint_3 Output
+%_ptr_Output_float = OpTypePointer Output %float
+%main___point_size_Output = OpVariable %_ptr_Output_float Output
+%VertexOutputs = OpTypeStruct %v4float %_arr_float_uint_3
+         %14 = OpTypeFunction %VertexOutputs
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+    %float_3 = OpConstant %float 3
+    %float_4 = OpConstant %float 4
+         %17 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4
+         %22 = OpConstantNull %_arr_float_uint_3
+         %16 = OpConstantComposite %VertexOutputs %17 %22
+       %void = OpTypeVoid
+         %25 = OpTypeFunction %void
+ %main_inner = OpFunction %VertexOutputs None %14
+         %15 = OpLabel
+               OpReturnValue %16
+               OpFunctionEnd
+       %main = OpFunction %void None %25
+         %26 = OpLabel
+         %27 = OpFunctionCall %VertexOutputs %main_inner
+         %28 = OpCompositeExtract %v4float %27 0
+               OpStore %main_position_Output %28
+         %29 = OpCompositeExtract %_arr_float_uint_3 %27 1
+               OpStore %main_clip_distances_Output %29
+               OpStore %main___point_size_Output %float_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.wgsl b/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.wgsl
new file mode 100644
index 0000000..5e0487b
--- /dev/null
+++ b/test/tint/extensions/clip_distances/clip_distances_size_3.wgsl.expected.wgsl
@@ -0,0 +1,13 @@
+enable clip_distances;
+
+struct VertexOutputs {
+  @builtin(position)
+  position : vec4<f32>,
+  @builtin(clip_distances)
+  clipDistance : array<f32, 3>,
+}
+
+@vertex
+fn main() -> VertexOutputs {
+  return VertexOutputs(vec4<f32>(1.0, 2.0, 3.0, 4.0), array<f32, 3>(0.0, 0.0, 0.0));
+}
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_4.wgsl.expected.dxc.hlsl b/test/tint/extensions/clip_distances/clip_distances_size_4.wgsl.expected.dxc.hlsl
index e5d4dce..9d8db76 100644
--- a/test/tint/extensions/clip_distances/clip_distances_size_4.wgsl.expected.dxc.hlsl
+++ b/test/tint/extensions/clip_distances/clip_distances_size_4.wgsl.expected.dxc.hlsl
@@ -1,23 +1,25 @@
-SKIP: FAILED
-
-
-enable clip_distances;
-
 struct VertexOutputs {
-  @builtin(position)
-  position : vec4<f32>,
-  @builtin(clip_distances)
-  clipDistance : array<f32, 4>,
+  float4 position;
+  float clipDistance[4];
+};
+struct tint_symbol {
+  float4 position : SV_Position;
+  float4 clip_distance_0 : SV_ClipDistance0;
+};
+
+VertexOutputs main_inner() {
+  VertexOutputs tint_symbol_1 = {float4(1.0f, 2.0f, 3.0f, 4.0f), (float[4])0};
+  return tint_symbol_1;
 }
 
-@vertex
-fn main() -> VertexOutputs {
-  return VertexOutputs(vec4<f32>(1.0, 2.0, 3.0, 4.0), array<f32, 4>(0.0, 0.0, 0.0, 0.0));
+tint_symbol main() {
+  VertexOutputs inner_result = main_inner();
+  tint_symbol wrapper_result = (tint_symbol)0;
+  wrapper_result.position = inner_result.position;
+  float tmp_inner_clip_distances[4] = inner_result.clipDistance;
+  wrapper_result.clip_distance_0[0u] = tmp_inner_clip_distances[0u];
+  wrapper_result.clip_distance_0[1u] = tmp_inner_clip_distances[1u];
+  wrapper_result.clip_distance_0[2u] = tmp_inner_clip_distances[2u];
+  wrapper_result.clip_distance_0[3u] = tmp_inner_clip_distances[3u];
+  return wrapper_result;
 }
-
-Failed to generate: <dawn>/test/tint/extensions/clip_distances/clip_distances_size_4.wgsl:1:8 error: HLSL backend does not support extension 'clip_distances'
-enable clip_distances;
-       ^^^^^^^^^^^^^^
-
-
-tint executable returned error: exit status 1
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_4.wgsl.expected.fxc.hlsl b/test/tint/extensions/clip_distances/clip_distances_size_4.wgsl.expected.fxc.hlsl
index e5d4dce..9d8db76 100644
--- a/test/tint/extensions/clip_distances/clip_distances_size_4.wgsl.expected.fxc.hlsl
+++ b/test/tint/extensions/clip_distances/clip_distances_size_4.wgsl.expected.fxc.hlsl
@@ -1,23 +1,25 @@
-SKIP: FAILED
-
-
-enable clip_distances;
-
 struct VertexOutputs {
-  @builtin(position)
-  position : vec4<f32>,
-  @builtin(clip_distances)
-  clipDistance : array<f32, 4>,
+  float4 position;
+  float clipDistance[4];
+};
+struct tint_symbol {
+  float4 position : SV_Position;
+  float4 clip_distance_0 : SV_ClipDistance0;
+};
+
+VertexOutputs main_inner() {
+  VertexOutputs tint_symbol_1 = {float4(1.0f, 2.0f, 3.0f, 4.0f), (float[4])0};
+  return tint_symbol_1;
 }
 
-@vertex
-fn main() -> VertexOutputs {
-  return VertexOutputs(vec4<f32>(1.0, 2.0, 3.0, 4.0), array<f32, 4>(0.0, 0.0, 0.0, 0.0));
+tint_symbol main() {
+  VertexOutputs inner_result = main_inner();
+  tint_symbol wrapper_result = (tint_symbol)0;
+  wrapper_result.position = inner_result.position;
+  float tmp_inner_clip_distances[4] = inner_result.clipDistance;
+  wrapper_result.clip_distance_0[0u] = tmp_inner_clip_distances[0u];
+  wrapper_result.clip_distance_0[1u] = tmp_inner_clip_distances[1u];
+  wrapper_result.clip_distance_0[2u] = tmp_inner_clip_distances[2u];
+  wrapper_result.clip_distance_0[3u] = tmp_inner_clip_distances[3u];
+  return wrapper_result;
 }
-
-Failed to generate: <dawn>/test/tint/extensions/clip_distances/clip_distances_size_4.wgsl:1:8 error: HLSL backend does not support extension 'clip_distances'
-enable clip_distances;
-       ^^^^^^^^^^^^^^
-
-
-tint executable returned error: exit status 1
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_4.wgsl.expected.glsl b/test/tint/extensions/clip_distances/clip_distances_size_4.wgsl.expected.glsl
index 88551af..e4cdf12 100644
--- a/test/tint/extensions/clip_distances/clip_distances_size_4.wgsl.expected.glsl
+++ b/test/tint/extensions/clip_distances/clip_distances_size_4.wgsl.expected.glsl
@@ -19,3 +19,5 @@
 enable clip_distances;
        ^^^^^^^^^^^^^^
 
+
+tint executable returned error: exit status 1
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_4.wgsl.expected.ir.msl b/test/tint/extensions/clip_distances/clip_distances_size_4.wgsl.expected.ir.msl
index 917e0b2..ff0637d 100644
--- a/test/tint/extensions/clip_distances/clip_distances_size_4.wgsl.expected.ir.msl
+++ b/test/tint/extensions/clip_distances/clip_distances_size_4.wgsl.expected.ir.msl
@@ -1,5 +1,3 @@
-SKIP: FAILED
-
 #include <metal_stdlib>
 using namespace metal;
 
@@ -33,10 +31,3 @@
   VertexOutputs const v = tint_symbol_inner();
   return tint_symbol_outputs{.VertexOutputs_position=v.position, .VertexOutputs_clipDistance=v.clipDistance};
 }
-program_source:23:53: error: type 'tint_array<float, 4>' is not valid for attribute 'clip_distance'
-  tint_array<float, 4> VertexOutputs_clipDistance [[clip_distance]];
-                                                    ^~~~~~~~~~~~~
-program_source:30:8: error: invalid return type 'tint_symbol_outputs' for vertex function
-vertex tint_symbol_outputs tint_symbol() {
-       ^
-
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_4.wgsl.expected.msl b/test/tint/extensions/clip_distances/clip_distances_size_4.wgsl.expected.msl
index 8f88332..e1c3a43 100644
--- a/test/tint/extensions/clip_distances/clip_distances_size_4.wgsl.expected.msl
+++ b/test/tint/extensions/clip_distances/clip_distances_size_4.wgsl.expected.msl
@@ -21,7 +21,7 @@
 
 struct tint_symbol_1 {
   float4 position [[position]];
-  float clipDistance [[clip_distance]] [4];
+  float clip_distance [[clip_distance]] [4];
 };
 
 VertexOutputs tint_symbol_inner() {
@@ -34,10 +34,10 @@
   tint_symbol_1 wrapper_result = {};
   wrapper_result.position = inner_result.position;
   tint_array<float, 4> const tmp_inner_clip_distances = inner_result.clipDistance;
-  wrapper_result.clipDistance[0u] = tmp_inner_clip_distances[0u];
-  wrapper_result.clipDistance[1u] = tmp_inner_clip_distances[1u];
-  wrapper_result.clipDistance[2u] = tmp_inner_clip_distances[2u];
-  wrapper_result.clipDistance[3u] = tmp_inner_clip_distances[3u];
+  wrapper_result.clip_distance[0u] = tmp_inner_clip_distances[0u];
+  wrapper_result.clip_distance[1u] = tmp_inner_clip_distances[1u];
+  wrapper_result.clip_distance[2u] = tmp_inner_clip_distances[2u];
+  wrapper_result.clip_distance[3u] = tmp_inner_clip_distances[3u];
   return wrapper_result;
 }
 
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl b/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl
new file mode 100644
index 0000000..b82ef45
--- /dev/null
+++ b/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl
@@ -0,0 +1,13 @@
+enable clip_distances;
+
+struct VertexOutputs {
+  @builtin(position)
+  position : vec4<f32>,
+  @builtin(clip_distances)
+  clipDistance : array<f32, 5>,
+}
+
+@vertex
+fn main() -> VertexOutputs {
+  return VertexOutputs(vec4<f32>(1.0, 2.0, 3.0, 4.0), array<f32, 5>(0.0, 0.0, 0.0, 0.0, 0.0));
+}
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.dxc.hlsl b/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.dxc.hlsl
new file mode 100644
index 0000000..398a16a0
--- /dev/null
+++ b/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.dxc.hlsl
@@ -0,0 +1,27 @@
+struct VertexOutputs {
+  float4 position;
+  float clipDistance[5];
+};
+struct tint_symbol {
+  float4 position : SV_Position;
+  float4 clip_distance_0 : SV_ClipDistance0;
+  float clip_distance_1 : SV_ClipDistance1;
+};
+
+VertexOutputs main_inner() {
+  VertexOutputs tint_symbol_1 = {float4(1.0f, 2.0f, 3.0f, 4.0f), (float[5])0};
+  return tint_symbol_1;
+}
+
+tint_symbol main() {
+  VertexOutputs inner_result = main_inner();
+  tint_symbol wrapper_result = (tint_symbol)0;
+  wrapper_result.position = inner_result.position;
+  float tmp_inner_clip_distances[5] = inner_result.clipDistance;
+  wrapper_result.clip_distance_0[0u] = tmp_inner_clip_distances[0u];
+  wrapper_result.clip_distance_0[1u] = tmp_inner_clip_distances[1u];
+  wrapper_result.clip_distance_0[2u] = tmp_inner_clip_distances[2u];
+  wrapper_result.clip_distance_0[3u] = tmp_inner_clip_distances[3u];
+  wrapper_result.clip_distance_1 = tmp_inner_clip_distances[4u];
+  return wrapper_result;
+}
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.fxc.hlsl b/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.fxc.hlsl
new file mode 100644
index 0000000..398a16a0
--- /dev/null
+++ b/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.fxc.hlsl
@@ -0,0 +1,27 @@
+struct VertexOutputs {
+  float4 position;
+  float clipDistance[5];
+};
+struct tint_symbol {
+  float4 position : SV_Position;
+  float4 clip_distance_0 : SV_ClipDistance0;
+  float clip_distance_1 : SV_ClipDistance1;
+};
+
+VertexOutputs main_inner() {
+  VertexOutputs tint_symbol_1 = {float4(1.0f, 2.0f, 3.0f, 4.0f), (float[5])0};
+  return tint_symbol_1;
+}
+
+tint_symbol main() {
+  VertexOutputs inner_result = main_inner();
+  tint_symbol wrapper_result = (tint_symbol)0;
+  wrapper_result.position = inner_result.position;
+  float tmp_inner_clip_distances[5] = inner_result.clipDistance;
+  wrapper_result.clip_distance_0[0u] = tmp_inner_clip_distances[0u];
+  wrapper_result.clip_distance_0[1u] = tmp_inner_clip_distances[1u];
+  wrapper_result.clip_distance_0[2u] = tmp_inner_clip_distances[2u];
+  wrapper_result.clip_distance_0[3u] = tmp_inner_clip_distances[3u];
+  wrapper_result.clip_distance_1 = tmp_inner_clip_distances[4u];
+  return wrapper_result;
+}
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.glsl b/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.glsl
new file mode 100644
index 0000000..3801310
--- /dev/null
+++ b/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.glsl
@@ -0,0 +1,23 @@
+SKIP: FAILED
+
+
+enable clip_distances;
+
+struct VertexOutputs {
+  @builtin(position)
+  position : vec4<f32>,
+  @builtin(clip_distances)
+  clipDistance : array<f32, 5>,
+}
+
+@vertex
+fn tint_symbol() -> VertexOutputs {
+  return VertexOutputs(vec4<f32>(1.0, 2.0, 3.0, 4.0), array<f32, 5>(0.0, 0.0, 0.0, 0.0, 0.0));
+}
+
+Failed to generate: <dawn>/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl:1:8 error: GLSL backend does not support extension 'clip_distances'
+enable clip_distances;
+       ^^^^^^^^^^^^^^
+
+
+tint executable returned error: exit status 1
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.ir.dxc.hlsl b/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.ir.dxc.hlsl
new file mode 100644
index 0000000..b0160ea
--- /dev/null
+++ b/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.ir.dxc.hlsl
@@ -0,0 +1,11 @@
+SKIP: FAILED
+
+..\..\src\tint\lang\hlsl\writer\raise\shader_io.cc:101 internal compiler error: TINT_UNREACHABLE 
+********************************************************************
+*  The tint shader compiler has encountered an unexpected error.   *
+*                                                                  *
+*  Please help us fix this issue by submitting a bug report at     *
+*  crbug.com/tint with the source program that triggered the bug.  *
+********************************************************************
+
+tint executable returned error: exit status 0xc000001d
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.ir.fxc.hlsl b/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.ir.fxc.hlsl
new file mode 100644
index 0000000..b0160ea
--- /dev/null
+++ b/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.ir.fxc.hlsl
@@ -0,0 +1,11 @@
+SKIP: FAILED
+
+..\..\src\tint\lang\hlsl\writer\raise\shader_io.cc:101 internal compiler error: TINT_UNREACHABLE 
+********************************************************************
+*  The tint shader compiler has encountered an unexpected error.   *
+*                                                                  *
+*  Please help us fix this issue by submitting a bug report at     *
+*  crbug.com/tint with the source program that triggered the bug.  *
+********************************************************************
+
+tint executable returned error: exit status 0xc000001d
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.ir.msl b/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.ir.msl
new file mode 100644
index 0000000..c509098
--- /dev/null
+++ b/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.ir.msl
@@ -0,0 +1,33 @@
+#include <metal_stdlib>
+using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+  const constant T& operator[](size_t i) const constant { return elements[i]; }
+  device T& operator[](size_t i) device { return elements[i]; }
+  const device T& operator[](size_t i) const device { return elements[i]; }
+  thread T& operator[](size_t i) thread { return elements[i]; }
+  const thread T& operator[](size_t i) const thread { return elements[i]; }
+  threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+  const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+  T elements[N];
+};
+
+struct VertexOutputs {
+  float4 position;
+  tint_array<float, 5> clipDistance;
+};
+
+struct tint_symbol_outputs {
+  float4 VertexOutputs_position [[position]];
+  tint_array<float, 5> VertexOutputs_clipDistance [[clip_distance]];
+};
+
+VertexOutputs tint_symbol_inner() {
+  return VertexOutputs{.position=float4(1.0f, 2.0f, 3.0f, 4.0f), .clipDistance=tint_array<float, 5>{}};
+}
+
+vertex tint_symbol_outputs tint_symbol() {
+  VertexOutputs const v = tint_symbol_inner();
+  return tint_symbol_outputs{.VertexOutputs_position=v.position, .VertexOutputs_clipDistance=v.clipDistance};
+}
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.msl b/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.msl
new file mode 100644
index 0000000..872384f
--- /dev/null
+++ b/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.msl
@@ -0,0 +1,44 @@
+#include <metal_stdlib>
+
+using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
+struct VertexOutputs {
+  float4 position;
+  tint_array<float, 5> clipDistance;
+};
+
+struct tint_symbol_1 {
+  float4 position [[position]];
+  float clip_distance [[clip_distance]] [5];
+};
+
+VertexOutputs tint_symbol_inner() {
+  VertexOutputs const tint_symbol_2 = VertexOutputs{.position=float4(1.0f, 2.0f, 3.0f, 4.0f), .clipDistance=tint_array<float, 5>{}};
+  return tint_symbol_2;
+}
+
+vertex tint_symbol_1 tint_symbol() {
+  VertexOutputs const inner_result = tint_symbol_inner();
+  tint_symbol_1 wrapper_result = {};
+  wrapper_result.position = inner_result.position;
+  tint_array<float, 5> const tmp_inner_clip_distances = inner_result.clipDistance;
+  wrapper_result.clip_distance[0u] = tmp_inner_clip_distances[0u];
+  wrapper_result.clip_distance[1u] = tmp_inner_clip_distances[1u];
+  wrapper_result.clip_distance[2u] = tmp_inner_clip_distances[2u];
+  wrapper_result.clip_distance[3u] = tmp_inner_clip_distances[3u];
+  wrapper_result.clip_distance[4u] = tmp_inner_clip_distances[4u];
+  return wrapper_result;
+}
+
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.spvasm b/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.spvasm
new file mode 100644
index 0000000..202845d
--- /dev/null
+++ b/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.spvasm
@@ -0,0 +1,59 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 1
+; Bound: 30
+; Schema: 0
+               OpCapability Shader
+               OpCapability ClipDistance
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %main_position_Output %main_clip_distances_Output %main___point_size_Output
+               OpName %main_position_Output "main_position_Output"
+               OpName %main_clip_distances_Output "main_clip_distances_Output"
+               OpName %main___point_size_Output "main___point_size_Output"
+               OpName %main_inner "main_inner"
+               OpMemberName %VertexOutputs 0 "position"
+               OpMemberName %VertexOutputs 1 "clipDistance"
+               OpName %VertexOutputs "VertexOutputs"
+               OpName %main "main"
+               OpDecorate %main_position_Output BuiltIn Position
+               OpDecorate %_arr_float_uint_5 ArrayStride 4
+               OpDecorate %main_clip_distances_Output BuiltIn ClipDistance
+               OpDecorate %main___point_size_Output BuiltIn PointSize
+               OpMemberDecorate %VertexOutputs 0 Offset 0
+               OpMemberDecorate %VertexOutputs 1 Offset 16
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%main_position_Output = OpVariable %_ptr_Output_v4float Output
+       %uint = OpTypeInt 32 0
+     %uint_5 = OpConstant %uint 5
+%_arr_float_uint_5 = OpTypeArray %float %uint_5
+%_ptr_Output__arr_float_uint_5 = OpTypePointer Output %_arr_float_uint_5
+%main_clip_distances_Output = OpVariable %_ptr_Output__arr_float_uint_5 Output
+%_ptr_Output_float = OpTypePointer Output %float
+%main___point_size_Output = OpVariable %_ptr_Output_float Output
+%VertexOutputs = OpTypeStruct %v4float %_arr_float_uint_5
+         %14 = OpTypeFunction %VertexOutputs
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+    %float_3 = OpConstant %float 3
+    %float_4 = OpConstant %float 4
+         %17 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4
+         %22 = OpConstantNull %_arr_float_uint_5
+         %16 = OpConstantComposite %VertexOutputs %17 %22
+       %void = OpTypeVoid
+         %25 = OpTypeFunction %void
+ %main_inner = OpFunction %VertexOutputs None %14
+         %15 = OpLabel
+               OpReturnValue %16
+               OpFunctionEnd
+       %main = OpFunction %void None %25
+         %26 = OpLabel
+         %27 = OpFunctionCall %VertexOutputs %main_inner
+         %28 = OpCompositeExtract %v4float %27 0
+               OpStore %main_position_Output %28
+         %29 = OpCompositeExtract %_arr_float_uint_5 %27 1
+               OpStore %main_clip_distances_Output %29
+               OpStore %main___point_size_Output %float_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.wgsl b/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.wgsl
new file mode 100644
index 0000000..b82ef45
--- /dev/null
+++ b/test/tint/extensions/clip_distances/clip_distances_size_5.wgsl.expected.wgsl
@@ -0,0 +1,13 @@
+enable clip_distances;
+
+struct VertexOutputs {
+  @builtin(position)
+  position : vec4<f32>,
+  @builtin(clip_distances)
+  clipDistance : array<f32, 5>,
+}
+
+@vertex
+fn main() -> VertexOutputs {
+  return VertexOutputs(vec4<f32>(1.0, 2.0, 3.0, 4.0), array<f32, 5>(0.0, 0.0, 0.0, 0.0, 0.0));
+}
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_8.wgsl.expected.dxc.hlsl b/test/tint/extensions/clip_distances/clip_distances_size_8.wgsl.expected.dxc.hlsl
index 33be9e0..a693a6a 100644
--- a/test/tint/extensions/clip_distances/clip_distances_size_8.wgsl.expected.dxc.hlsl
+++ b/test/tint/extensions/clip_distances/clip_distances_size_8.wgsl.expected.dxc.hlsl
@@ -1,23 +1,30 @@
-SKIP: FAILED
-
-
-enable clip_distances;
-
 struct VertexOutputs {
-  @builtin(position)
-  position : vec4<f32>,
-  @builtin(clip_distances)
-  clipDistance : array<f32, 8>,
+  float4 position;
+  float clipDistance[8];
+};
+struct tint_symbol {
+  float4 position : SV_Position;
+  float4 clip_distance_0 : SV_ClipDistance0;
+  float4 clip_distance_1 : SV_ClipDistance1;
+};
+
+VertexOutputs main_inner() {
+  VertexOutputs tint_symbol_1 = {float4(1.0f, 2.0f, 3.0f, 4.0f), (float[8])0};
+  return tint_symbol_1;
 }
 
-@vertex
-fn main() -> VertexOutputs {
-  return VertexOutputs(vec4<f32>(1.0, 2.0, 3.0, 4.0), array<f32, 8>(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
+tint_symbol main() {
+  VertexOutputs inner_result = main_inner();
+  tint_symbol wrapper_result = (tint_symbol)0;
+  wrapper_result.position = inner_result.position;
+  float tmp_inner_clip_distances[8] = inner_result.clipDistance;
+  wrapper_result.clip_distance_0[0u] = tmp_inner_clip_distances[0u];
+  wrapper_result.clip_distance_0[1u] = tmp_inner_clip_distances[1u];
+  wrapper_result.clip_distance_0[2u] = tmp_inner_clip_distances[2u];
+  wrapper_result.clip_distance_0[3u] = tmp_inner_clip_distances[3u];
+  wrapper_result.clip_distance_1[0u] = tmp_inner_clip_distances[0u];
+  wrapper_result.clip_distance_1[1u] = tmp_inner_clip_distances[1u];
+  wrapper_result.clip_distance_1[2u] = tmp_inner_clip_distances[2u];
+  wrapper_result.clip_distance_1[3u] = tmp_inner_clip_distances[3u];
+  return wrapper_result;
 }
-
-Failed to generate: <dawn>/test/tint/extensions/clip_distances/clip_distances_size_8.wgsl:1:8 error: HLSL backend does not support extension 'clip_distances'
-enable clip_distances;
-       ^^^^^^^^^^^^^^
-
-
-tint executable returned error: exit status 1
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_8.wgsl.expected.fxc.hlsl b/test/tint/extensions/clip_distances/clip_distances_size_8.wgsl.expected.fxc.hlsl
index 33be9e0..a693a6a 100644
--- a/test/tint/extensions/clip_distances/clip_distances_size_8.wgsl.expected.fxc.hlsl
+++ b/test/tint/extensions/clip_distances/clip_distances_size_8.wgsl.expected.fxc.hlsl
@@ -1,23 +1,30 @@
-SKIP: FAILED
-
-
-enable clip_distances;
-
 struct VertexOutputs {
-  @builtin(position)
-  position : vec4<f32>,
-  @builtin(clip_distances)
-  clipDistance : array<f32, 8>,
+  float4 position;
+  float clipDistance[8];
+};
+struct tint_symbol {
+  float4 position : SV_Position;
+  float4 clip_distance_0 : SV_ClipDistance0;
+  float4 clip_distance_1 : SV_ClipDistance1;
+};
+
+VertexOutputs main_inner() {
+  VertexOutputs tint_symbol_1 = {float4(1.0f, 2.0f, 3.0f, 4.0f), (float[8])0};
+  return tint_symbol_1;
 }
 
-@vertex
-fn main() -> VertexOutputs {
-  return VertexOutputs(vec4<f32>(1.0, 2.0, 3.0, 4.0), array<f32, 8>(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
+tint_symbol main() {
+  VertexOutputs inner_result = main_inner();
+  tint_symbol wrapper_result = (tint_symbol)0;
+  wrapper_result.position = inner_result.position;
+  float tmp_inner_clip_distances[8] = inner_result.clipDistance;
+  wrapper_result.clip_distance_0[0u] = tmp_inner_clip_distances[0u];
+  wrapper_result.clip_distance_0[1u] = tmp_inner_clip_distances[1u];
+  wrapper_result.clip_distance_0[2u] = tmp_inner_clip_distances[2u];
+  wrapper_result.clip_distance_0[3u] = tmp_inner_clip_distances[3u];
+  wrapper_result.clip_distance_1[0u] = tmp_inner_clip_distances[0u];
+  wrapper_result.clip_distance_1[1u] = tmp_inner_clip_distances[1u];
+  wrapper_result.clip_distance_1[2u] = tmp_inner_clip_distances[2u];
+  wrapper_result.clip_distance_1[3u] = tmp_inner_clip_distances[3u];
+  return wrapper_result;
 }
-
-Failed to generate: <dawn>/test/tint/extensions/clip_distances/clip_distances_size_8.wgsl:1:8 error: HLSL backend does not support extension 'clip_distances'
-enable clip_distances;
-       ^^^^^^^^^^^^^^
-
-
-tint executable returned error: exit status 1
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_8.wgsl.expected.glsl b/test/tint/extensions/clip_distances/clip_distances_size_8.wgsl.expected.glsl
index 4eaae78..f89ccc0 100644
--- a/test/tint/extensions/clip_distances/clip_distances_size_8.wgsl.expected.glsl
+++ b/test/tint/extensions/clip_distances/clip_distances_size_8.wgsl.expected.glsl
@@ -19,3 +19,5 @@
 enable clip_distances;
        ^^^^^^^^^^^^^^
 
+
+tint executable returned error: exit status 1
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_8.wgsl.expected.ir.msl b/test/tint/extensions/clip_distances/clip_distances_size_8.wgsl.expected.ir.msl
index 9df70b1..d1697c8 100644
--- a/test/tint/extensions/clip_distances/clip_distances_size_8.wgsl.expected.ir.msl
+++ b/test/tint/extensions/clip_distances/clip_distances_size_8.wgsl.expected.ir.msl
@@ -1,5 +1,3 @@
-SKIP: FAILED
-
 #include <metal_stdlib>
 using namespace metal;
 
@@ -33,10 +31,3 @@
   VertexOutputs const v = tint_symbol_inner();
   return tint_symbol_outputs{.VertexOutputs_position=v.position, .VertexOutputs_clipDistance=v.clipDistance};
 }
-program_source:23:53: error: type 'tint_array<float, 8>' is not valid for attribute 'clip_distance'
-  tint_array<float, 8> VertexOutputs_clipDistance [[clip_distance]];
-                                                    ^~~~~~~~~~~~~
-program_source:30:8: error: invalid return type 'tint_symbol_outputs' for vertex function
-vertex tint_symbol_outputs tint_symbol() {
-       ^
-
diff --git a/test/tint/extensions/clip_distances/clip_distances_size_8.wgsl.expected.msl b/test/tint/extensions/clip_distances/clip_distances_size_8.wgsl.expected.msl
index 952455e..400aabd 100644
--- a/test/tint/extensions/clip_distances/clip_distances_size_8.wgsl.expected.msl
+++ b/test/tint/extensions/clip_distances/clip_distances_size_8.wgsl.expected.msl
@@ -21,7 +21,7 @@
 
 struct tint_symbol_1 {
   float4 position [[position]];
-  float clipDistance [[clip_distance]] [8];
+  float clip_distance [[clip_distance]] [8];
 };
 
 VertexOutputs tint_symbol_inner() {
@@ -34,14 +34,14 @@
   tint_symbol_1 wrapper_result = {};
   wrapper_result.position = inner_result.position;
   tint_array<float, 8> const tmp_inner_clip_distances = inner_result.clipDistance;
-  wrapper_result.clipDistance[0u] = tmp_inner_clip_distances[0u];
-  wrapper_result.clipDistance[1u] = tmp_inner_clip_distances[1u];
-  wrapper_result.clipDistance[2u] = tmp_inner_clip_distances[2u];
-  wrapper_result.clipDistance[3u] = tmp_inner_clip_distances[3u];
-  wrapper_result.clipDistance[4u] = tmp_inner_clip_distances[4u];
-  wrapper_result.clipDistance[5u] = tmp_inner_clip_distances[5u];
-  wrapper_result.clipDistance[6u] = tmp_inner_clip_distances[6u];
-  wrapper_result.clipDistance[7u] = tmp_inner_clip_distances[7u];
+  wrapper_result.clip_distance[0u] = tmp_inner_clip_distances[0u];
+  wrapper_result.clip_distance[1u] = tmp_inner_clip_distances[1u];
+  wrapper_result.clip_distance[2u] = tmp_inner_clip_distances[2u];
+  wrapper_result.clip_distance[3u] = tmp_inner_clip_distances[3u];
+  wrapper_result.clip_distance[4u] = tmp_inner_clip_distances[4u];
+  wrapper_result.clip_distance[5u] = tmp_inner_clip_distances[5u];
+  wrapper_result.clip_distance[6u] = tmp_inner_clip_distances[6u];
+  wrapper_result.clip_distance[7u] = tmp_inner_clip_distances[7u];
   return wrapper_result;
 }