https://github.com/inbelic created 
https://github.com/llvm/llvm-project/pull/224139

This pr adds the following semantic analysis for semantics:

 - Validate scalar/vector shapes, element types, and supported widths for 
system semantics
 - Enforce indexing restrictions and SV_Target bounds
 - Add diag that shows previous use of overlapping semantics
 
As well as, simplifying some of the stages as well.

Resolves #189765

Assisted by: GPT-6 Astra

>From 182b95dc7d400d51ad161f466c6b5a5cc32d3446 Mon Sep 17 00:00:00 2001
From: Finn Plummer <[email protected]>
Date: Wed, 16 Sep 2026 19:03:13 +0000
Subject: [PATCH 1/5] validate system-semantic types in entry-point context

---
 .../clang/Basic/DiagnosticSemaKinds.td        |   5 +
 clang/include/clang/Sema/SemaHLSL.h           |  10 +-
 clang/lib/Sema/SemaHLSL.cpp                   | 154 ++++++++++++------
 .../Semantics/invalid_entry_parameter.hlsl    |  44 +++--
 .../SemaHLSL/Semantics/position.ps.size.hlsl  |  19 ++-
 .../Semantics/semantic-stage.vs-ps.hlsl       |  27 +++
 .../SemaHLSL/Semantics/semantic-types.hlsl    |  44 +++++
 .../test/SemaHLSL/Semantics/vertexid.vs.hlsl  |  29 ++--
 8 files changed, 248 insertions(+), 84 deletions(-)
 create mode 100644 clang/test/SemaHLSL/Semantics/semantic-stage.vs-ps.hlsl
 create mode 100644 clang/test/SemaHLSL/Semantics/semantic-types.hlsl

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a7b40a7b64c2d..f51e08097b991 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13676,6 +13676,11 @@ def note_hlsl_semantic_used_here : Note<"%0 used 
here">;
 def err_hlsl_unknown_semantic : Error<"unknown HLSL semantic %0">;
 def err_hlsl_semantic_indexing_not_supported
     : Error<"semantic %0 does not allow indexing">;
+def err_hlsl_semantic_invalid_type
+    : Error<"semantic %0 must be a "
+            "%select{scalar|scalar or vector of up to %2 components}1 of "
+            "%select{16 or 32 bit integer|32 bit integer|"
+            "16 or 32 bit floating-point}3 type (was %4)">;
 def err_hlsl_init_priority_unsupported : Error<
   "initializer priorities are not supported in HLSL">;
 def err_hlsl_semantic_index_overlap : Error<"semantic index overlap %0">;
diff --git a/clang/include/clang/Sema/SemaHLSL.h 
b/clang/include/clang/Sema/SemaHLSL.h
index 6c0e5b52f7cb3..09c0d3e7e0891 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -231,11 +231,6 @@ class SemaHLSL : public SemaBase {
   QualType ActOnTemplateShorthand(TemplateDecl *Template,
                                   SourceLocation NameLoc);
 
-  // Diagnose whether the index type is uint/unit2/uint3 type.
-  bool diagnoseIndexType(QualType T, const ParsedAttr &AL);
-  // Diagnose whether the type is float/float2/float3/float4 type.
-  bool diagnoseFloatType(QualType T, const ParsedAttr &AL);
-
   bool CanPerformScalarCast(QualType SrcTy, QualType DestTy);
   bool CanPerformElementwiseCast(Expr *Src, QualType DestType);
   bool CanPerformAggregateSplatCast(Expr *Src, QualType DestType);
@@ -340,6 +335,11 @@ class SemaHLSL : public SemaBase {
                                 llvm::hlsl::IOType CurrentIOType,
                                 llvm::dxbc::PSV::SemanticKind SemanticKind);
 
+  // Called only for system-value interpretations.
+  void diagnoseSystemSemanticType(const Decl *D,
+                                  const HLSLAppliedSemanticAttr *A,
+                                  llvm::dxbc::PSV::SemanticKind SemanticKind);
+
   void handleGlobalStructOrArrayOfWithResources(VarDecl *VD);
 
   // Infer a common global binding info for an Expr
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index d9978d1d171bb..b08c190c0ab2c 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1090,8 +1090,15 @@ void SemaHLSL::checkSemanticAnnotation(
       llvm::hlsl::getSemanticKind(SemanticAttr->getSemanticName());
   llvm::hlsl::SemanticInterpretation Interpretation =
       llvm::hlsl::getInterpretationKind(Kind, ST, SC.CurrentIOType);
-  if (Interpretation == llvm::hlsl::SemanticInterpretation::Invalid)
+  if (Interpretation == llvm::hlsl::SemanticInterpretation::Invalid) {
     diagnoseSemanticStageMismatch(SemanticAttr, ST, SC.CurrentIOType, Kind);
+    return;
+  }
+
+  // SV_-prefixed names can have arbitrary interpretations, e.g. SV_Position
+  // on a vertex input. Do not apply system-value constraints to them.
+  if (Interpretation == llvm::hlsl::SemanticInterpretation::Arbitrary)
+    return;
 
   switch (Kind) {
   case SemanticKind::DispatchThreadID:
@@ -1109,6 +1116,99 @@ void SemaHLSL::checkSemanticAnnotation(
   default:
     break;
   }
+
+  diagnoseSystemSemanticType(Param, SemanticAttr, Kind);
+}
+
+static QualType getElementTypeOf(QualType T, bool IncludeMatrix) {
+  if (const auto *VT = T->getAs<clang::VectorType>())
+    return VT->getElementType();
+  if (IncludeMatrix)
+    if (const auto *MT = T->getAs<clang::MatrixType>())
+      return MT->getElementType();
+  return T;
+}
+
+static unsigned getComponentCountOf(QualType T) {
+  if (const auto *VT = T->getAs<clang::VectorType>())
+    return VT->getNumElements();
+  return 1;
+}
+
+static bool isFloatOrHalfElement(QualType Elem) {
+  return Elem->isHalfType() || Elem->isFloat16Type() || Elem->isFloat32Type();
+}
+
+// System-value integer types exclude bool.
+static bool isIntElementOfWidth(const ASTContext &Ctx, QualType Elem,
+                                uint64_t Width) {
+  if (!Elem->isIntegerType() || Elem->isBooleanType())
+    return false;
+  return Ctx.getTypeSize(Elem) == Width;
+}
+
+static bool isIntUpTo32Element(const ASTContext &Ctx, QualType Elem) {
+  return isIntElementOfWidth(Ctx, Elem, 16) ||
+         isIntElementOfWidth(Ctx, Elem, 32);
+}
+
+void SemaHLSL::diagnoseSystemSemanticType(const Decl *D,
+                                          const HLSLAppliedSemanticAttr *A,
+                                          SemanticKind Kind) {
+  ASTContext &Ctx = getASTContext();
+
+  QualType T;
+  if (const auto *FD = dyn_cast<FunctionDecl>(D))
+    T = FD->getReturnType();
+  else
+    T = cast<ValueDecl>(D)->getType();
+
+  // `out` and `inout` parameters are passed by reference.
+  if (const auto *RT = T->getAs<ReferenceType>())
+    T = RT->getPointeeType();
+
+  // Array semantics constrain each element's type.
+  QualType DeclaredTy = T;
+  while (const ConstantArrayType *AT = Ctx.getAsConstantArrayType(T))
+    T = AT->getElementType();
+
+  QualType ElemTy = getElementTypeOf(T, /*IncludeMatrix=*/false);
+  unsigned Components = getComponentCountOf(T);
+
+  // Numeric selectors below choose the shape and element-type wording in
+  // err_hlsl_semantic_invalid_type.
+  switch (Kind) {
+  case SemanticKind::DispatchThreadID:
+  case SemanticKind::GroupID:
+  case SemanticKind::GroupThreadID:
+    if (!isIntUpTo32Element(Ctx, ElemTy) || Components > 3)
+      Diag(A->getLoc(), diag::err_hlsl_semantic_invalid_type)
+          << A->getAttrName() << /* scalar or vector of up to */ 1 << 3
+          << /* 16 or 32 bit integer */ 0 << DeclaredTy;
+    break;
+  case SemanticKind::GroupIndex:
+    if (!isIntElementOfWidth(Ctx, ElemTy, 32) || Components != 1)
+      Diag(A->getLoc(), diag::err_hlsl_semantic_invalid_type)
+          << A->getAttrName() << /* scalar */ 0 << 1 << /* 32 bit integer */ 1
+          << DeclaredTy;
+    break;
+  case SemanticKind::VertexID:
+    if (!isIntUpTo32Element(Ctx, ElemTy) || Components != 1)
+      Diag(A->getLoc(), diag::err_hlsl_semantic_invalid_type)
+          << A->getAttrName() << /* scalar */ 0 << 1
+          << /* 16 or 32 bit integer */ 0 << DeclaredTy;
+    break;
+  case SemanticKind::Position:
+  case SemanticKind::Target:
+    if (!isFloatOrHalfElement(ElemTy) || Components > 4)
+      Diag(A->getLoc(), diag::err_hlsl_semantic_invalid_type)
+          << A->getAttrName() << /* scalar or vector of up to */ 1 << 4
+          << /* 16 or 32 bit floating-point */ 2 << DeclaredTy;
+    break;
+  default:
+    // Other recognized system semantics do not yet have type checks.
+    break;
+  }
 }
 
 void SemaHLSL::diagnoseAttrStageMismatch(
@@ -1876,61 +1976,18 @@ void SemaHLSL::handleVkLocationAttr(Decl *D, const 
ParsedAttr &AL) {
                  HLSLVkLocationAttr(getASTContext(), AL, Location));
 }
 
-bool SemaHLSL::diagnoseIndexType(QualType T, const ParsedAttr &AL) {
-  const auto *VT = T->getAs<VectorType>();
-
-  if (!T->hasUnsignedIntegerRepresentation() ||
-      (VT && VT->getNumElements() > 3)) {
-    Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_type)
-        << AL << "uint/uint2/uint3";
-    return false;
-  }
-
-  return true;
-}
-
-bool SemaHLSL::diagnoseFloatType(QualType T, const ParsedAttr &AL) {
-  const auto *VT = T->getAs<VectorType>();
-  if (!T->hasFloatingRepresentation() || (VT && VT->getNumElements() > 4)) {
-    Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_type)
-        << AL << "float/float1/float2/float3/float4";
-    return false;
-  }
-
-  return true;
-}
-
 void SemaHLSL::diagnoseSystemSemanticAttr(Decl *D, const ParsedAttr &AL,
                                           SemanticKind Kind,
                                           std::optional<unsigned> Index) {
-  auto *VD = cast<ValueDecl>(D);
-  QualType ValueType = VD->getType();
-  if (auto *FD = dyn_cast<FunctionDecl>(D))
-    ValueType = FD->getReturnType();
-
-  // `out` and `inout` parameters are passed by reference.
-  if (HLSLParamModifierAttr *MA = D->getAttr<HLSLParamModifierAttr>())
-    if (MA->isAnyOut())
-      ValueType = cast<ReferenceType>(ValueType)->getPointeeType();
-
   switch (Kind) {
   case SemanticKind::DispatchThreadID:
   case SemanticKind::GroupThreadID:
   case SemanticKind::GroupID:
-    diagnoseIndexType(ValueType, AL);
-    break;
   case SemanticKind::GroupIndex:
-    break;
   case SemanticKind::Position:
   case SemanticKind::Target:
-    diagnoseFloatType(ValueType, AL);
-    break;
-  case SemanticKind::VertexID: {
-    uint64_t SizeInBits = SemaRef.Context.getTypeSize(ValueType);
-    if (!ValueType->isUnsignedIntegerType() || SizeInBits != 32)
-      Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_type) << AL << "uint";
+  case SemanticKind::VertexID:
     break;
-  }
   default:
     Diag(AL.getLoc(), diag::err_hlsl_unknown_semantic) << AL;
     return;
@@ -3333,11 +3390,8 @@ static bool CheckFloatRepresentation(Sema *S, 
SourceLocation Loc,
 static bool CheckFloatOrHalfRepresentation(Sema *S, SourceLocation Loc,
                                            int ArgOrdinal,
                                            clang::QualType PassedType) {
-  clang::QualType BaseType = PassedType;
-  if (const auto *VT = PassedType->getAs<clang::VectorType>())
-    BaseType = VT->getElementType();
-  else if (const auto *MT = PassedType->getAs<clang::MatrixType>())
-    BaseType = MT->getElementType();
+  clang::QualType BaseType =
+      getElementTypeOf(PassedType, /*IncludeMatrix=*/true);
 
   if (!BaseType->isHalfType() && !BaseType->isFloat32Type())
     return S->Diag(Loc, diag::err_builtin_invalid_arg_type)
diff --git a/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl 
b/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl
index cc457362f8793..1701d0bff0a95 100644
--- a/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl
+++ b/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl
@@ -1,7 +1,9 @@
-// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library 
-finclude-default-header -x hlsl -ast-dump -verify -o - %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library 
-finclude-default-header -x hlsl -fsyntax-only -verify %s
 
-[numthreads(8,8,1)]
-// expected-error@+1 {{attribute 'SV_DispatchThreadID' only applies to a field 
or parameter of type 'uint/uint2/uint3'}}
+// Type and index checks require an entry point.
+
+[shader("compute")][numthreads(8,8,1)]
+// expected-error@+1 {{semantic 'SV_DispatchThreadID' must be a scalar or 
vector of up to 3 components of 16 or 32 bit integer type (was 'float')}}
 void CSMain(float ID : SV_DispatchThreadID) {
 
 }
@@ -10,9 +12,18 @@ struct ST {
   int a;
   float b;
 };
-[numthreads(8,8,1)]
-// expected-error@+1 {{attribute 'SV_DispatchThreadID' only applies to a field 
or parameter of type 'uint/uint2/uint3'}}
+
+// The second field gets index 1, which SV_DispatchThreadID rejects.
+[shader("compute")][numthreads(8,8,1)]
 void CSMain2(ST ID : SV_DispatchThreadID) {
+// expected-error@-1 {{semantic 'SV_DispatchThreadID' does not allow indexing}}
+// expected-error@-2 {{semantic 'SV_DispatchThreadID' must be a scalar or 
vector of up to 3 components of 16 or 32 bit integer type (was 'float')}}
+
+}
+
+[shader("compute")][numthreads(8,8,1)]
+// expected-error@+1 {{semantic 'SV_DispatchThreadID' must be a scalar or 
vector of up to 3 components of 16 or 32 bit integer type (was 'uint4' (aka 
'vector<uint, 4>'))}}
+void CSMain3(uint4 ID : SV_DispatchThreadID) {
 
 }
 
@@ -28,14 +39,15 @@ struct ST2 {
     uint s : SV_DispatchThreadID;
 };
 
-[numthreads(8,8,1)]
-// expected-error@+1 {{attribute 'SV_GroupID' only applies to a field or 
parameter of type 'uint/uint2/uint3'}}
+[shader("compute")][numthreads(8,8,1)]
+// expected-error@+1 {{semantic 'SV_GroupID' must be a scalar or vector of up 
to 3 components of 16 or 32 bit integer type (was 'float')}}
 void CSMain_GID(float ID : SV_GroupID) {
 }
 
-[numthreads(8,8,1)]
-// expected-error@+1 {{attribute 'SV_GroupID' only applies to a field or 
parameter of type 'uint/uint2/uint3'}}
+[shader("compute")][numthreads(8,8,1)]
 void CSMain2_GID(ST GID : SV_GroupID) {
+// expected-error@-1 {{semantic 'SV_GroupID' does not allow indexing}}
+// expected-error@-2 {{semantic 'SV_GroupID' must be a scalar or vector of up 
to 3 components of 16 or 32 bit integer type (was 'float')}}
 
 }
 
@@ -50,15 +62,15 @@ struct ST2_GID {
     uint s_gid : SV_GroupID;
 };
 
-[numthreads(8,8,1)]
-// expected-error@+1 {{attribute 'SV_GroupThreadID' only applies to a field or 
parameter of type 'uint/uint2/uint3'}}
+[shader("compute")][numthreads(8,8,1)]
+// expected-error@+1 {{semantic 'SV_GroupThreadID' must be a scalar or vector 
of up to 3 components of 16 or 32 bit integer type (was 'float')}}
 void CSMain_GThreadID(float ID : SV_GroupThreadID) {
 }
 
-[numthreads(8,8,1)]
-// expected-error@+1 {{attribute 'SV_GroupThreadID' only applies to a field or 
parameter of type 'uint/uint2/uint3'}}
+[shader("compute")][numthreads(8,8,1)]
 void CSMain2_GThreadID(ST GID : SV_GroupThreadID) {
-
+// expected-error@-1 {{semantic 'SV_GroupThreadID' does not allow indexing}}
+// expected-error@-2 {{semantic 'SV_GroupThreadID' must be a scalar or vector 
of up to 3 components of 16 or 32 bit integer type (was 'float')}}
 }
 
 void foo_GThreadID() {
@@ -72,6 +84,10 @@ struct ST2_GThreadID {
     uint s_gthreadid : SV_GroupThreadID;
 };
 
+[shader("compute")][numthreads(8,8,1)]
+// expected-error@+1 {{semantic 'SV_GroupIndex' must be a scalar of 32 bit 
integer type (was 'uint2' (aka 'vector<uint, 2>'))}}
+void CSMain_GIndex(uint2 GI : SV_GroupIndex) {
+}
 
 [shader("vertex")]
 // expected-error@+4 {{semantic 'SV_GroupIndex' is not supported in vertex 
shader inputs}}
diff --git a/clang/test/SemaHLSL/Semantics/position.ps.size.hlsl 
b/clang/test/SemaHLSL/Semantics/position.ps.size.hlsl
index 124d401a9990c..5c7fe8b423110 100644
--- a/clang/test/SemaHLSL/Semantics/position.ps.size.hlsl
+++ b/clang/test/SemaHLSL/Semantics/position.ps.size.hlsl
@@ -1,10 +1,21 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl 
-finclude-default-header -o - %s -verify -verify-ignore-unexpected
 // RUN: %clang_cc1 -triple spirv-unknown-vulkan1.3-library  -x hlsl 
-finclude-default-header -o - %s -verify -verify-ignore-unexpected
 
-// expected-error@+1 {{attribute 'SV_Position' only applies to a field or 
parameter of type 'float/float1/float2/float3/float4'}}
-void main(vector<float, 5> a : SV_Position) {
+[shader("pixel")]
+void too_many_components(vector<float, 5> a : SV_Position) {
+// expected-error@-1 {{semantic 'SV_Position' must be a scalar or vector of up 
to 4 components of 16 or 32 bit floating-point type (was 'vector<float, 5>' 
(vector of 5 'float' values))}}
 }
 
-// expected-error@+1 {{attribute 'SV_Position' only applies to a field or 
parameter of type 'float/float1/float2/float3/float4'}}
-void main(int2 a : SV_Position) {
+[shader("pixel")]
+void not_a_float(int2 a : SV_Position) {
+// expected-error@-1 {{semantic 'SV_Position' must be a scalar or vector of up 
to 4 components of 16 or 32 bit floating-point type (was 'int2' (aka 
'vector<int, 2>'))}}
+}
+
+[shader("pixel")]
+void too_wide(double4 a : SV_Position) {
+// expected-error@-1 {{semantic 'SV_Position' must be a scalar or vector of up 
to 4 components of 16 or 32 bit floating-point type (was 'double4' (aka 
'vector<double, 4>'))}}
+}
+
+[shader("pixel")]
+void ok(float4 a : SV_Position) {
 }
diff --git a/clang/test/SemaHLSL/Semantics/semantic-stage.vs-ps.hlsl 
b/clang/test/SemaHLSL/Semantics/semantic-stage.vs-ps.hlsl
new file mode 100644
index 0000000000000..6a616c5e2dd2c
--- /dev/null
+++ b/clang/test/SemaHLSL/Semantics/semantic-stage.vs-ps.hlsl
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library 
-finclude-default-header -x hlsl -fsyntax-only -verify %s
+
+[shader("vertex")]
+uint vs_vertexid_out(uint ID : SV_VertexID) : SV_VertexID { return ID; }
+// expected-error@-1 {{semantic 'SV_VertexID' is not supported in vertex 
shader outputs}}
+
+[shader("pixel")]
+float4 ps_dispatchthreadid_in(uint3 ID : SV_DispatchThreadID) : SV_Target { 
return 0; }
+// expected-error@-1 {{semantic 'SV_DispatchThreadID' is not supported in 
pixel shader inputs}}
+
+[shader("pixel")]
+float4 ps_groupindex_in(uint GI : SV_GroupIndex) : SV_Target { return 0; }
+// expected-error@-1 {{semantic 'SV_GroupIndex' is not supported in pixel 
shader inputs}}
+
+[shader("pixel")]
+void ps_vertexid_out(out uint ID : SV_VertexID) { ID = 0; }
+// expected-error@-1 {{semantic 'SV_VertexID' is not supported in pixel shader 
outputs}}
+
+// SV_Position is arbitrary on vertex inputs, so int2 is accepted.
+[shader("vertex")]
+float4 vs_position_in(int2 P : SV_Position) : SV_Position { return 0; }
+
+[shader("vertex")]
+float4 vs_user(float4 A : IN) : OUT { return A; }
+
+[shader("pixel")]
+float4 ps_user_in(float4 A : IN) : SV_Target { return A; }
diff --git a/clang/test/SemaHLSL/Semantics/semantic-types.hlsl 
b/clang/test/SemaHLSL/Semantics/semantic-types.hlsl
new file mode 100644
index 0000000000000..0078352ae2b92
--- /dev/null
+++ b/clang/test/SemaHLSL/Semantics/semantic-types.hlsl
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -fnative-half-type -triple dxil-pc-shadermodel6.3-library 
-finclude-default-header -x hlsl -fsyntax-only -verify %s
+
+[shader("pixel")]
+double4 target_double(float4 P : SV_Position) : SV_Target { return (double4)P; 
}
+// expected-error@-1 {{semantic 'SV_Target' must be a scalar or vector of up 
to 4 components of 16 or 32 bit floating-point type (was 'double4' (aka 
'vector<double, 4>'))}}
+
+[shader("pixel")]
+float4 position_double(double4 P : SV_Position) : SV_Target { return 
(float4)P; }
+// expected-error@-1 {{semantic 'SV_Position' must be a scalar or vector of up 
to 4 components of 16 or 32 bit floating-point type (was 'double4' (aka 
'vector<double, 4>'))}}
+
+[shader("pixel")]
+float4 position_half(half4 P : SV_Position) : SV_Target { return (float4)P; }
+
+[shader("compute")][numthreads(1,1,1)]
+void group_index_vector(uint2 GI : SV_GroupIndex) {}
+// expected-error@-1 {{semantic 'SV_GroupIndex' must be a scalar of 32 bit 
integer type (was 'uint2' (aka 'vector<uint, 2>'))}}
+
+[shader("compute")][numthreads(1,1,1)]
+void group_index_16bit(half GI : SV_GroupIndex) {}
+// expected-error@-1 {{semantic 'SV_GroupIndex' must be a scalar of 32 bit 
integer type (was 'half')}}
+
+[shader("compute")][numthreads(1,1,1)]
+void group_index_ok(uint GI : SV_GroupIndex) {}
+
+[shader("compute")][numthreads(1,1,1)]
+void thread_id_signed(int3 ID : SV_DispatchThreadID) {}
+
+[shader("compute")][numthreads(1,1,1)]
+void thread_id_scalar(uint ID : SV_GroupThreadID) {}
+
+[shader("compute")][numthreads(1,1,1)]
+void thread_id_too_wide(uint4 ID : SV_GroupID) {}
+// expected-error@-1 {{semantic 'SV_GroupID' must be a scalar or vector of up 
to 3 components of 16 or 32 bit integer type (was 'uint4' (aka 'vector<uint, 
4>'))}}
+
+[shader("compute")][numthreads(1,1,1)]
+void thread_id_float(float3 ID : SV_GroupID) {}
+// expected-error@-1 {{semantic 'SV_GroupID' must be a scalar or vector of up 
to 3 components of 16 or 32 bit integer type (was 'float3' (aka 'vector<float, 
3>'))}}
+
+// SV_Position on a vertex input is arbitrary, so double4 is allowed.
+[shader("vertex")]
+float4 position_vs_in(double4 P : SV_Position) : SV_Position { return 
(float4)P; }
+
+[shader("pixel")]
+float4 user_double(double4 P : USER) : SV_Target { return (float4)P; }
diff --git a/clang/test/SemaHLSL/Semantics/vertexid.vs.hlsl 
b/clang/test/SemaHLSL/Semantics/vertexid.vs.hlsl
index c90cc2113eb43..c29bc89ce0b45 100644
--- a/clang/test/SemaHLSL/Semantics/vertexid.vs.hlsl
+++ b/clang/test/SemaHLSL/Semantics/vertexid.vs.hlsl
@@ -1,36 +1,43 @@
 // RUN: %clang_cc1 -fnative-half-type -fnative-int16-type -triple 
dxil-pc-shadermodel6.3-library -finclude-default-header -x hlsl -verify -o - %s
 // RUN: %clang_cc1 -fnative-half-type -fnative-int16-type -triple 
spirv-pc-vulkan1.3-library -finclude-default-header -x hlsl -verify -o - %s
 
+// SV_VertexID accepts 16- and 32-bit signed or unsigned scalars.
+
+[shader("vertex")]
 float bad_type_float(float id : SV_VertexID) : A {
-// expected-error@-1 {{attribute 'SV_VertexID' only applies to a field or 
parameter of type 'uint'}}
+// expected-error@-1 {{semantic 'SV_VertexID' must be a scalar of 16 or 32 bit 
integer type (was 'float')}}
   return id;
 }
 
+[shader("vertex")]
 uint3 bad_type_vector(uint3 id : SV_VertexID) : A {
-// expected-error@-1 {{attribute 'SV_VertexID' only applies to a field or 
parameter of type 'uint'}}
+// expected-error@-1 {{semantic 'SV_VertexID' must be a scalar of 16 or 32 bit 
integer type (was 'uint3' (aka 'vector<uint, 3>'))}}
   return id;
 }
 
-int bad_type_signed(int id : SV_VertexID) : A {
-// expected-error@-1 {{attribute 'SV_VertexID' only applies to a field or 
parameter of type 'uint'}}
+[shader("vertex")]
+uint64_t bad_type_size(uint64_t id : SV_VertexID) : A {
+// expected-error@-1 {{semantic 'SV_VertexID' must be a scalar of 16 or 32 bit 
integer type (was 'uint64_t' (aka 'unsigned long'))}}
   return id;
 }
 
-uint64_t bad_type_size(uint64_t id : SV_VertexID) : A {
-// expected-error@-1 {{attribute 'SV_VertexID' only applies to a field or 
parameter of type 'uint'}}
+[shader("vertex")]
+float bad_type_bool(bool id : SV_VertexID) : A {
+// expected-error@-1 {{semantic 'SV_VertexID' must be a scalar of 16 or 32 bit 
integer type (was 'bool')}}
   return id;
 }
 
-uint32_t ok(uint32_t id : SV_VertexID) : A {
+[shader("vertex")]
+uint32_t ok_unsigned(uint32_t id : SV_VertexID) : A {
   return id;
 }
 
-uint16_t bad_type_size_2(uint16_t id : SV_VertexID) : A {
-// expected-error@-1 {{attribute 'SV_VertexID' only applies to a field or 
parameter of type 'uint'}}
+[shader("vertex")]
+int ok_signed(int id : SV_VertexID) : A {
   return id;
 }
 
-char bad_type_size_2(char id : SV_VertexID) : A {
-// expected-error@-1 {{attribute 'SV_VertexID' only applies to a field or 
parameter of type 'uint'}}
+[shader("vertex")]
+uint16_t ok_16bit(uint16_t id : SV_VertexID) : A {
   return id;
 }

>From 2f1c5298bf29b471949e66b614a45fb0610ca973 Mon Sep 17 00:00:00 2001
From: Finn Plummer <[email protected]>
Date: Wed, 16 Sep 2026 19:03:36 +0000
Subject: [PATCH 2/5] validate semantic indices and render-target bounds

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  2 +
 clang/include/clang/Sema/SemaHLSL.h           |  8 ++-
 clang/lib/Sema/SemaHLSL.cpp                   | 52 ++++++++++++-------
 .../Semantics/invalid_entry_parameter.hlsl    |  7 +++
 .../Semantics/position.ps.struct.hlsl         |  7 +--
 .../SemaHLSL/Semantics/semantic-indexing.hlsl | 17 ++++++
 .../test/SemaHLSL/Semantics/target.index.hlsl | 37 +++++++++++++
 7 files changed, 107 insertions(+), 23 deletions(-)
 create mode 100644 clang/test/SemaHLSL/Semantics/target.index.hlsl

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index f51e08097b991..15b682a2c6b43 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13676,6 +13676,8 @@ def note_hlsl_semantic_used_here : Note<"%0 used here">;
 def err_hlsl_unknown_semantic : Error<"unknown HLSL semantic %0">;
 def err_hlsl_semantic_indexing_not_supported
     : Error<"semantic %0 does not allow indexing">;
+def err_hlsl_semantic_index_out_of_range
+    : Error<"semantic %0 index %1 exceeds the maximum supported index %2">;
 def err_hlsl_semantic_invalid_type
     : Error<"semantic %0 must be a "
             "%select{scalar|scalar or vector of up to %2 components}1 of "
diff --git a/clang/include/clang/Sema/SemaHLSL.h 
b/clang/include/clang/Sema/SemaHLSL.h
index 09c0d3e7e0891..4812c36ceac82 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -309,7 +309,8 @@ class SemaHLSL : public SemaBase {
 
   void checkSemanticAnnotation(FunctionDecl *EntryPoint, const Decl *Param,
                                const HLSLAppliedSemanticAttr *SemanticAttr,
-                               const SemanticContext &SC);
+                               const SemanticContext &SC,
+                               unsigned ElementCount);
 
   bool determineActiveSemanticOnScalar(FunctionDecl *FD,
                                        DeclaratorDecl *OutputDecl,
@@ -335,6 +336,11 @@ class SemaHLSL : public SemaBase {
                                 llvm::hlsl::IOType CurrentIOType,
                                 llvm::dxbc::PSV::SemanticKind SemanticKind);
 
+  // Check ElementCount consecutive indices for a system-value interpretation.
+  void diagnoseSemanticIndex(const HLSLAppliedSemanticAttr *A,
+                             llvm::dxbc::PSV::SemanticKind SemanticKind,
+                             unsigned ElementCount);
+
   // Called only for system-value interpretations.
   void diagnoseSystemSemanticType(const Decl *D,
                                   const HLSLAppliedSemanticAttr *A,
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index b08c190c0ab2c..30ff68b5849d0 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -913,7 +913,11 @@ bool 
SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD,
   if (!A)
     return false;
 
-  checkSemanticAnnotation(FD, D, A, SC);
+  // Each array element occupies a separate semantic index.
+  const ConstantArrayType *AT = dyn_cast<ConstantArrayType>(D->getType());
+  unsigned ElementCount = AT ? AT->getZExtSize() : 1;
+
+  checkSemanticAnnotation(FD, D, A, SC, ElementCount);
   OutputDecl->addAttr(A);
 
   unsigned Location = ActiveSemantic.Index.value_or(0);
@@ -933,8 +937,6 @@ bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl 
*FD,
     SC.UsesExplicitVkLocations = HasVkLocation;
   }
 
-  const ConstantArrayType *AT = dyn_cast<ConstantArrayType>(D->getType());
-  unsigned ElementCount = AT ? AT->getZExtSize() : 1;
   ActiveSemantic.Index = Location + ElementCount;
 
   Twine BaseName = Twine(ActiveSemantic.Semantic->getAttrName()->getName());
@@ -1081,7 +1083,8 @@ void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) {
 
 void SemaHLSL::checkSemanticAnnotation(
     FunctionDecl *EntryPoint, const Decl *Param,
-    const HLSLAppliedSemanticAttr *SemanticAttr, const SemanticContext &SC) {
+    const HLSLAppliedSemanticAttr *SemanticAttr, const SemanticContext &SC,
+    unsigned ElementCount) {
   auto *ShaderAttr = EntryPoint->getAttr<HLSLShaderAttr>();
   assert(ShaderAttr && "Entry point has no shader attribute");
   llvm::Triple::EnvironmentType ST = ShaderAttr->getType();
@@ -1100,24 +1103,35 @@ void SemaHLSL::checkSemanticAnnotation(
   if (Interpretation == llvm::hlsl::SemanticInterpretation::Arbitrary)
     return;
 
+  diagnoseSemanticIndex(SemanticAttr, Kind, ElementCount);
+  diagnoseSystemSemanticType(Param, SemanticAttr, Kind);
+}
+
+void SemaHLSL::diagnoseSemanticIndex(const HLSLAppliedSemanticAttr *A,
+                                     SemanticKind Kind, unsigned ElementCount) 
{
+  assert(ElementCount > 0 && "a semantic covers at least one element");
+  uint32_t LastIndex = A->getSemanticIndex() + ElementCount - 1;
+  if (LastIndex == 0)
+    return;
+
   switch (Kind) {
-  case SemanticKind::DispatchThreadID:
-  case SemanticKind::GroupID:
-  case SemanticKind::GroupIndex:
-  case SemanticKind::GroupThreadID:
-    if (SemanticAttr->getSemanticIndex() != 0) {
-      std::string PrettyName =
-          "'" + SemanticAttr->getSemanticName().str() + "'";
-      Diag(SemanticAttr->getLoc(),
-           diag::err_hlsl_semantic_indexing_not_supported)
-          << PrettyName;
-    }
-    break;
+  // These semantics are limited by signature packing, not semantic indices.
+  case SemanticKind::Arbitrary:
+  case SemanticKind::ClipDistance:
+  case SemanticKind::CullDistance:
+    return;
+  case SemanticKind::Target: {
+    constexpr unsigned MaxTargetIndex = 7;
+    if (LastIndex > MaxTargetIndex)
+      Diag(A->getLoc(), diag::err_hlsl_semantic_index_out_of_range)
+          << A->getAttrName() << LastIndex << MaxTargetIndex;
+  }
+    return;
   default:
-    break;
+    Diag(A->getLoc(), diag::err_hlsl_semantic_indexing_not_supported)
+        << A->getAttrName();
+    return;
   }
-
-  diagnoseSystemSemanticType(Param, SemanticAttr, Kind);
 }
 
 static QualType getElementTypeOf(QualType T, bool IncludeMatrix) {
diff --git a/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl 
b/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl
index 1701d0bff0a95..1e0fc13517ba5 100644
--- a/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl
+++ b/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl
@@ -27,6 +27,13 @@ void CSMain3(uint4 ID : SV_DispatchThreadID) {
 
 }
 
+// Array elements also require distinct semantic indices.
+[shader("compute")][numthreads(8,8,1)]
+void CSMain4(uint3 ID[2] : SV_DispatchThreadID) {
+// expected-error@-1 {{semantic 'SV_DispatchThreadID' does not allow indexing}}
+
+}
+
 void foo() {
 // expected-warning@+1 {{'SV_DispatchThreadID' attribute only applies to 
parameters, non-static data members, and functions}}
   uint V : SV_DispatchThreadID;
diff --git a/clang/test/SemaHLSL/Semantics/position.ps.struct.hlsl 
b/clang/test/SemaHLSL/Semantics/position.ps.struct.hlsl
index d8fdd58ba0855..480c9b41a1899 100644
--- a/clang/test/SemaHLSL/Semantics/position.ps.struct.hlsl
+++ b/clang/test/SemaHLSL/Semantics/position.ps.struct.hlsl
@@ -4,16 +4,17 @@ struct S {
   float4 f0 : SV_Position;
 // CHECK: FieldDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> col:10 f0 
'float4':'vector<float, 4>'
 // CHECK-NEXT:  HLSLParsedSemanticAttr 0x{{[0-9a-f]+}} <col:15> "SV_Position" 0
-  float4 f1 : SV_Position3;
+// Use a user semantic to test non-zero index propagation.
+  float4 f1 : USER3;
 // CHECK: FieldDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> col:10 referenced f1 
'float4':'vector<float, 4>'
-// CHECK-NEXT:  HLSLParsedSemanticAttr 0x{{[0-9a-f]+}} <col:15> "SV_Position" 3
+// CHECK-NEXT:  HLSLParsedSemanticAttr 0x{{[0-9a-f]+}} <col:15> "USER" 3
 };
 
 float4 main(S s) : SV_Target {
 // CHECK: FunctionDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> line:[[@LINE-1]]:8 main 
'float4 (S)'
 // CHECK-NEXT: ParmVarDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> col:15 used s 'S'
 // CHECK-NEXT:  HLSLAppliedSemanticAttr 0x{{[0-9a-f]+}} <line:4:15> 
"SV_Position" 0
-// CHECK-NEXT:  HLSLAppliedSemanticAttr 0x{{[0-9a-f]+}} <line:7:15> 
"SV_Position" 3
+// CHECK-NEXT:  HLSLAppliedSemanticAttr 0x{{[0-9a-f]+}} <line:8:15> "USER" 3
 
 // CHECK:       HLSLAppliedSemanticAttr 0x{{[0-9a-f]+}} <col:20> "SV_Target" 0
   return s.f1;
diff --git a/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl 
b/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl
index b78d165c164b1..c0a24929dfb23 100644
--- a/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl
+++ b/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl
@@ -17,3 +17,20 @@ void derived_index(Pair GI : SV_GroupIndex) {}
 
 [shader("compute")][numthreads(1,1,1)]
 void no_index(uint GI : SV_GroupIndex) {}
+
+// An array also derives an index per element.
+[shader("compute")][numthreads(1,1,1)]
+void array_index(uint3 ID[2] : SV_DispatchThreadID) {}
+// expected-error@-1 {{semantic 'SV_DispatchThreadID' does not allow indexing}}
+
+// SV_Position is non-indexable on pixel shader inputs.
+[shader("pixel")]
+float4 position_ps(float4 P : SV_Position1) : SV_Target { return P; }
+// expected-error@-1 {{semantic 'SV_Position' does not allow indexing}}
+
+// On vertex shader inputs, SV_Position is arbitrary and may be indexed.
+[shader("vertex")]
+float4 position_vs(float4 P : SV_Position1) : SV_Position { return P; }
+
+[shader("pixel")]
+float4 user_index(float4 P : USER7) : SV_Target { return P; }
diff --git a/clang/test/SemaHLSL/Semantics/target.index.hlsl 
b/clang/test/SemaHLSL/Semantics/target.index.hlsl
new file mode 100644
index 0000000000000..b711c94471b5f
--- /dev/null
+++ b/clang/test/SemaHLSL/Semantics/target.index.hlsl
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library 
-finclude-default-header -x hlsl -fsyntax-only -verify %s
+
+// SV_Target indices select one of eight render target slots.
+[shader("pixel")]
+float4 target_zero(float4 P : SV_Position) : SV_Target { return P; }
+
+[shader("pixel")]
+float4 target_last(float4 P : SV_Position) : SV_Target7 { return P; }
+
+[shader("pixel")]
+float4 target_past_last(float4 P : SV_Position) : SV_Target8 { return P; }
+// expected-error@-1 {{semantic 'SV_Target' index 8 exceeds the maximum 
supported index 7}}
+
+[shader("pixel")]
+float4 target_way_past_last(float4 P : SV_Position) : SV_Target100 { return P; 
}
+// expected-error@-1 {{semantic 'SV_Target' index 100 exceeds the maximum 
supported index 7}}
+
+// Both array elements must fit in the render-target index range.
+[shader("pixel")]
+void target_array_fits(out float4 T[2] : SV_Target6) { T[0] = 0; T[1] = 0; }
+
+[shader("pixel")]
+void target_array_overflows(out float4 T[2] : SV_Target7) { T[0] = 0; T[1] = 
0; }
+// expected-error@-1 {{semantic 'SV_Target' index 8 exceeds the maximum 
supported index 7}}
+
+// The return semantic assigns consecutive indices to the fields.
+struct TwoTargets {
+  float4 A;
+  float4 B;
+};
+
+[shader("pixel")]
+TwoTargets target_struct_fits() : SV_Target6 { return (TwoTargets)0; }
+
+[shader("pixel")]
+TwoTargets target_struct_overflows() : SV_Target7 { return (TwoTargets)0; }
+// expected-error@-1 {{semantic 'SV_Target' index 8 exceeds the maximum 
supported index 7}}

>From 24e0a148da1573b561151684346ea11dbe0c7306 Mon Sep 17 00:00:00 2001
From: Finn Plummer <[email protected]>
Date: Wed, 16 Sep 2026 19:03:44 +0000
Subject: [PATCH 3/5] simplify parsedsemanticattr creation

---
 clang/include/clang/Sema/SemaHLSL.h           |  3 --
 clang/lib/Sema/SemaHLSL.cpp                   | 30 ++++---------------
 clang/test/ParserHLSL/semantic_parsing.hlsl   |  4 +++
 .../SemaHLSL/Semantics/semantic-indexing.hlsl | 12 ++++++++
 4 files changed, 22 insertions(+), 27 deletions(-)

diff --git a/clang/include/clang/Sema/SemaHLSL.h 
b/clang/include/clang/Sema/SemaHLSL.h
index 4812c36ceac82..e94b4f28c67f7 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -207,9 +207,6 @@ class SemaHLSL : public SemaBase {
           Location.value_or(0));
   }
 
-  void diagnoseSystemSemanticAttr(Decl *D, const ParsedAttr &AL,
-                                  llvm::dxbc::PSV::SemanticKind SemanticKind,
-                                  std::optional<unsigned> Index);
   void handleSemanticAttr(Decl *D, const ParsedAttr &AL);
 
   void handleVkExtBuiltinInputAttr(Decl *D, const ParsedAttr &AL);
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 30ff68b5849d0..111764187ee6a 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1990,26 +1990,6 @@ void SemaHLSL::handleVkLocationAttr(Decl *D, const 
ParsedAttr &AL) {
                  HLSLVkLocationAttr(getASTContext(), AL, Location));
 }
 
-void SemaHLSL::diagnoseSystemSemanticAttr(Decl *D, const ParsedAttr &AL,
-                                          SemanticKind Kind,
-                                          std::optional<unsigned> Index) {
-  switch (Kind) {
-  case SemanticKind::DispatchThreadID:
-  case SemanticKind::GroupThreadID:
-  case SemanticKind::GroupID:
-  case SemanticKind::GroupIndex:
-  case SemanticKind::Position:
-  case SemanticKind::Target:
-  case SemanticKind::VertexID:
-    break;
-  default:
-    Diag(AL.getLoc(), diag::err_hlsl_unknown_semantic) << AL;
-    return;
-  }
-
-  D->addAttr(createSemanticAttr<HLSLParsedSemanticAttr>(AL, Index));
-}
-
 void SemaHLSL::handleSemanticAttr(Decl *D, const ParsedAttr &AL) {
   uint32_t IndexValue(0), ExplicitIndex(0);
   if (!SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(0), IndexValue) ||
@@ -2021,10 +2001,12 @@ void SemaHLSL::handleSemanticAttr(Decl *D, const 
ParsedAttr &AL) {
       ExplicitIndex ? std::optional<unsigned>(IndexValue) : std::nullopt;
 
   SemanticKind Kind = llvm::hlsl::getSemanticKind(AL.getAttrName()->getName());
-  if (Kind == SemanticKind::Arbitrary)
-    D->addAttr(createSemanticAttr<HLSLParsedSemanticAttr>(AL, Index));
-  else
-    diagnoseSystemSemanticAttr(D, AL, Kind, Index);
+  if (Kind == SemanticKind::Invalid) {
+    Diag(AL.getLoc(), diag::err_hlsl_unknown_semantic) << AL;
+    return;
+  }
+
+  D->addAttr(createSemanticAttr<HLSLParsedSemanticAttr>(AL, Index));
 }
 
 void SemaHLSL::handlePackOffsetAttr(Decl *D, const ParsedAttr &AL) {
diff --git a/clang/test/ParserHLSL/semantic_parsing.hlsl 
b/clang/test/ParserHLSL/semantic_parsing.hlsl
index 232d47a4da7fc..30218d2334e0e 100644
--- a/clang/test/ParserHLSL/semantic_parsing.hlsl
+++ b/clang/test/ParserHLSL/semantic_parsing.hlsl
@@ -7,6 +7,10 @@ void Entry(int GI : ) { }
 // expected-error@+1 {{unknown HLSL semantic 'SV_IWantAPony'}}
 void Pony(int GI : SV_IWantAPony) { }
 
+// A lowercase SV_ prefix must not turn an unknown name into a user semantic.
+// expected-error@+1 {{unknown HLSL semantic 'sv_iwantapony'}}
+void IndexedPony(int GI : sv_iwantapony1) { }
+
 // expected-error@+3 {{expected HLSL Semantic identifier}}
 // expected-error@+2 {{expected ')'}}
 // expected-note@+1 {{to match this '('}}
diff --git a/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl 
b/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl
index c0a24929dfb23..3c0f37900bc35 100644
--- a/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl
+++ b/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl
@@ -34,3 +34,15 @@ float4 position_vs(float4 P : SV_Position1) : SV_Position { 
return P; }
 
 [shader("pixel")]
 float4 user_index(float4 P : USER7) : SV_Target { return P; }
+
+// Clip/cull indices are allowed even with a system-value interpretation.
+[shader("pixel")]
+float4 clip_cull_index(float Clip : SV_ClipDistance1,
+                      float Cull : sv_culldistance1) : SV_Target {
+  return Clip + Cull;
+}
+
+// Recognizing the name does not bypass shader-stage validation.
+[shader("compute")][numthreads(1,1,1)]
+void clip_compute(float Clip : SV_ClipDistance1) {}
+// expected-error@-1 {{semantic 'SV_ClipDistance' is not supported in compute 
shader inputs}}

>From 58dd2ea3f0a5750e51bbc301523582a158cb4df4 Mon Sep 17 00:00:00 2001
From: Finn Plummer <[email protected]>
Date: Wed, 16 Sep 2026 19:04:01 +0000
Subject: [PATCH 4/5] diagnose case-insensitive semantic overlaps with prior
 locations

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  1 +
 clang/include/clang/Sema/SemaHLSL.h           |  8 +--
 clang/lib/Sema/SemaHLSL.cpp                   |  8 ++-
 .../SemaHLSL/Semantics/output-parameters.hlsl |  1 +
 .../SemaHLSL/Semantics/semantic-overlap.hlsl  | 62 +++++++++++++++++++
 5 files changed, 73 insertions(+), 7 deletions(-)
 create mode 100644 clang/test/SemaHLSL/Semantics/semantic-overlap.hlsl

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 15b682a2c6b43..377304b405f05 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13686,6 +13686,7 @@ def err_hlsl_semantic_invalid_type
 def err_hlsl_init_priority_unsupported : Error<
   "initializer priorities are not supported in HLSL">;
 def err_hlsl_semantic_index_overlap : Error<"semantic index overlap %0">;
+def note_hlsl_semantic_index_previous_use : Note<"previous use is here">;
 def err_hlsl_semantic_unsupported_iotype_for_stage
     : Error<"semantic %0 is not supported in %1 shader %2">;
 def err_hlsl_semantic_partial_explicit_indexing
diff --git a/clang/include/clang/Sema/SemaHLSL.h 
b/clang/include/clang/Sema/SemaHLSL.h
index e94b4f28c67f7..3c54efe00c830 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -23,7 +23,7 @@
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/StringSet.h"
+#include "llvm/ADT/StringMap.h"
 #include "llvm/Frontend/HLSL/SemanticSignatures.h"
 #include "llvm/TargetParser/Triple.h"
 #include <initializer_list>
@@ -292,9 +292,9 @@ class SemaHLSL : public SemaBase {
     // Present if any semantic sharing the same IO type has an explicit or
     // implicit SPIR-V location index assigned.
     std::optional<bool> UsesExplicitVkLocations = std::nullopt;
-    // The set of semantics found to be active during flattening. Used to 
detect
-    // index collisions.
-    llvm::StringSet<> ActiveSemantics = {};
+    // Lowercased semantic names with indices, mapped to their first use for
+    // overlap diagnostics.
+    llvm::StringMap<SourceLocation> ActiveSemantics = {};
     // The IOType of this semantic set.
     llvm::hlsl::IOType CurrentIOType;
   };
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 111764187ee6a..6a2b70955e512 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -941,12 +941,14 @@ bool 
SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD,
 
   Twine BaseName = Twine(ActiveSemantic.Semantic->getAttrName()->getName());
   for (unsigned I = 0; I < ElementCount; ++I) {
-    Twine VariableName = BaseName.concat(Twine(Location + I));
+    std::string VariableName = BaseName.concat(Twine(Location + I)).str();
 
-    auto [_, Inserted] = SC.ActiveSemantics.insert(VariableName.str());
+    auto [It, Inserted] = SC.ActiveSemantics.try_emplace(
+        StringRef(VariableName).lower(), D->getLocation());
     if (!Inserted) {
       Diag(D->getLocation(), diag::err_hlsl_semantic_index_overlap)
-          << VariableName.str();
+          << VariableName;
+      Diag(It->second, diag::note_hlsl_semantic_index_previous_use);
       return false;
     }
   }
diff --git a/clang/test/SemaHLSL/Semantics/output-parameters.hlsl 
b/clang/test/SemaHLSL/Semantics/output-parameters.hlsl
index 78dfeb23a2570..06a3b17b13f9f 100644
--- a/clang/test/SemaHLSL/Semantics/output-parameters.hlsl
+++ b/clang/test/SemaHLSL/Semantics/output-parameters.hlsl
@@ -21,6 +21,7 @@ void cs_group_index_out(out uint GI : SV_GroupIndex) { GI = 
0; }
 [shader("pixel")]
 float4 ps_overlap(out float4 Color : SV_Target) : SV_Target {
 // expected-error@-1 {{semantic index overlap SV_Target0}}
+// expected-note@-2 {{previous use is here}}
   Color = 0;
   return 0;
 }
diff --git a/clang/test/SemaHLSL/Semantics/semantic-overlap.hlsl 
b/clang/test/SemaHLSL/Semantics/semantic-overlap.hlsl
new file mode 100644
index 0000000000000..ad5717c72b7a8
--- /dev/null
+++ b/clang/test/SemaHLSL/Semantics/semantic-overlap.hlsl
@@ -0,0 +1,62 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library 
-finclude-default-header -x hlsl -fsyntax-only -verify %s
+
+struct Collide {
+  float4 A : USER0;
+// expected-note@-1 {{previous use is here}}
+  float4 B : USER0;
+// expected-error@-1 {{semantic index overlap USER0}}
+// expected-note@-2 {{'B' used here}}
+};
+
+[shader("pixel")]
+float4 collide(Collide C) : SV_Target { return C.A; }
+// expected-note@-1 {{'C' declared here}}
+
+// Semantic names are case-insensitive.
+struct CollideCase {
+  float4 A : user0;
+// expected-note@-1 {{previous use is here}}
+  float4 B : USER0;
+// expected-error@-1 {{semantic index overlap USER0}}
+// expected-note@-2 {{'B' used here}}
+};
+
+[shader("pixel")]
+float4 collide_case(CollideCase C) : SV_Target { return C.A; }
+// expected-note@-1 {{'C' declared here}}
+
+struct CollidePosition {
+  float4 A : SV_Position;
+// expected-note@-1 {{previous use is here}}
+  float4 B : sv_position;
+// expected-error@-1 {{semantic index overlap sv_position0}}
+// expected-note@-2 {{'B' used here}}
+};
+
+[shader("pixel")]
+float4 collide_position(CollidePosition C) : SV_Target { return C.A; }
+// expected-note@-1 {{'C' declared here}}
+
+// The array reserves USER0 and USER1.
+struct CollideArray {
+  float4 A[2] : USER0;
+// expected-note@-1 {{previous use is here}}
+  float4 B : USER1;
+// expected-error@-1 {{semantic index overlap USER1}}
+// expected-note@-2 {{'B' used here}}
+};
+
+[shader("pixel")]
+float4 collide_array(CollideArray C) : SV_Target { return C.B; }
+// expected-note@-1 {{'C' declared here}}
+
+struct NoCollide {
+  float4 A : USER0;
+  float4 B : USER1;
+};
+
+[shader("pixel")]
+float4 no_collide(NoCollide C) : SV_Target { return C.A; }
+
+[shader("pixel")]
+float4 separate_signatures(float4 C : USER0) : SV_Target { return C; }

>From 1b47917a4fba2a844d5bffde413f1da39caa8b81 Mon Sep 17 00:00:00 2001
From: Finn Plummer <[email protected]>
Date: Wed, 16 Sep 2026 20:02:31 +0000
Subject: [PATCH 5/5] review: clean-ups and corrections

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  3 +-
 clang/include/clang/Sema/SemaHLSL.h           |  9 --
 clang/lib/Sema/SemaHLSL.cpp                   | 91 ++++++++++++-------
 .../SemaHLSL/Semantics/semantic-indexing.hlsl | 16 ++--
 .../SemaHLSL/Semantics/semantic-overlap.hlsl  | 28 +++++-
 .../SemaHLSL/Semantics/semantic-types.hlsl    |  9 ++
 .../Semantics/semantic-zero-sized-array.hlsl  | 34 +++++++
 .../test/SemaHLSL/Semantics/target.index.hlsl | 27 ++++++
 8 files changed, 162 insertions(+), 55 deletions(-)
 create mode 100644 clang/test/SemaHLSL/Semantics/semantic-zero-sized-array.hlsl

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 377304b405f05..7068a52b46edb 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13678,6 +13678,8 @@ def err_hlsl_semantic_indexing_not_supported
     : Error<"semantic %0 does not allow indexing">;
 def err_hlsl_semantic_index_out_of_range
     : Error<"semantic %0 index %1 exceeds the maximum supported index %2">;
+def err_hlsl_semantic_zero_sized_array
+    : Error<"semantic %0 cannot be applied to a zero-sized array">;
 def err_hlsl_semantic_invalid_type
     : Error<"semantic %0 must be a "
             "%select{scalar|scalar or vector of up to %2 components}1 of "
@@ -13686,7 +13688,6 @@ def err_hlsl_semantic_invalid_type
 def err_hlsl_init_priority_unsupported : Error<
   "initializer priorities are not supported in HLSL">;
 def err_hlsl_semantic_index_overlap : Error<"semantic index overlap %0">;
-def note_hlsl_semantic_index_previous_use : Note<"previous use is here">;
 def err_hlsl_semantic_unsupported_iotype_for_stage
     : Error<"semantic %0 is not supported in %1 shader %2">;
 def err_hlsl_semantic_partial_explicit_indexing
diff --git a/clang/include/clang/Sema/SemaHLSL.h 
b/clang/include/clang/Sema/SemaHLSL.h
index 3c54efe00c830..43852a1ec0804 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -199,14 +199,6 @@ class SemaHLSL : public SemaBase {
   void propagateContextualMatrixLayout(Expr *E, QualType DestType);
   bool handleResourceTypeAttr(QualType T, const ParsedAttr &AL);
 
-  template <typename T>
-  T *createSemanticAttr(const AttributeCommonInfo &ACI,
-                        std::optional<unsigned> Location) {
-    return ::new (getASTContext())
-        T(getASTContext(), ACI, ACI.getAttrName()->getName(),
-          Location.value_or(0));
-  }
-
   void handleSemanticAttr(Decl *D, const ParsedAttr &AL);
 
   void handleVkExtBuiltinInputAttr(Decl *D, const ParsedAttr &AL);
@@ -338,7 +330,6 @@ class SemaHLSL : public SemaBase {
                              llvm::dxbc::PSV::SemanticKind SemanticKind,
                              unsigned ElementCount);
 
-  // Called only for system-value interpretations.
   void diagnoseSystemSemanticType(const Decl *D,
                                   const HLSLAppliedSemanticAttr *A,
                                   llvm::dxbc::PSV::SemanticKind SemanticKind);
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 6a2b70955e512..99557a55647dd 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -914,8 +914,15 @@ bool 
SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD,
     return false;
 
   // Each array element occupies a separate semantic index.
-  const ConstantArrayType *AT = dyn_cast<ConstantArrayType>(D->getType());
-  unsigned ElementCount = AT ? AT->getZExtSize() : 1;
+  QualType T = D == FD ? FD->getReturnType() : D->getType();
+  const ConstantArrayType *AT =
+      getASTContext().getAsConstantArrayType(T.getNonReferenceType());
+  if (isZeroSizedArray(AT)) {
+    Diag(A->getLoc(), diag::err_hlsl_semantic_zero_sized_array)
+        << A->getAttrName();
+    return false;
+  }
+  unsigned ElementCount = AT ? ASTContext::getConstantArrayElementCount(AT) : 
1;
 
   checkSemanticAnnotation(FD, D, A, SC, ElementCount);
   OutputDecl->addAttr(A);
@@ -939,16 +946,15 @@ bool 
SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD,
 
   ActiveSemantic.Index = Location + ElementCount;
 
-  Twine BaseName = Twine(ActiveSemantic.Semantic->getAttrName()->getName());
+  StringRef BaseName = ActiveSemantic.Semantic->getAttrName()->getName();
+  std::string LowerName = BaseName.lower();
   for (unsigned I = 0; I < ElementCount; ++I) {
-    std::string VariableName = BaseName.concat(Twine(Location + I)).str();
-
     auto [It, Inserted] = SC.ActiveSemantics.try_emplace(
-        StringRef(VariableName).lower(), D->getLocation());
+        (Twine(LowerName) + Twine(Location + I)).str(), D->getLocation());
     if (!Inserted) {
       Diag(D->getLocation(), diag::err_hlsl_semantic_index_overlap)
-          << VariableName;
-      Diag(It->second, diag::note_hlsl_semantic_index_previous_use);
+          << (BaseName + Twine(Location + I)).str();
+      Diag(It->second, diag::note_previous_use);
       return false;
     }
   }
@@ -1111,6 +1117,8 @@ void SemaHLSL::checkSemanticAnnotation(
 
 void SemaHLSL::diagnoseSemanticIndex(const HLSLAppliedSemanticAttr *A,
                                      SemanticKind Kind, unsigned ElementCount) 
{
+  assert(Kind != SemanticKind::Invalid && Kind != SemanticKind::Arbitrary &&
+         "expected a recognized system semantic");
   assert(ElementCount > 0 && "a semantic covers at least one element");
   uint32_t LastIndex = A->getSemanticIndex() + ElementCount - 1;
   if (LastIndex == 0)
@@ -1118,7 +1126,6 @@ void SemaHLSL::diagnoseSemanticIndex(const 
HLSLAppliedSemanticAttr *A,
 
   switch (Kind) {
   // These semantics are limited by signature packing, not semantic indices.
-  case SemanticKind::Arbitrary:
   case SemanticKind::ClipDistance:
   case SemanticKind::CullDistance:
     return;
@@ -1127,8 +1134,8 @@ void SemaHLSL::diagnoseSemanticIndex(const 
HLSLAppliedSemanticAttr *A,
     if (LastIndex > MaxTargetIndex)
       Diag(A->getLoc(), diag::err_hlsl_semantic_index_out_of_range)
           << A->getAttrName() << LastIndex << MaxTargetIndex;
-  }
     return;
+  }
   default:
     Diag(A->getLoc(), diag::err_hlsl_semantic_indexing_not_supported)
         << A->getAttrName();
@@ -1171,6 +1178,8 @@ static bool isIntUpTo32Element(const ASTContext &Ctx, 
QualType Elem) {
 void SemaHLSL::diagnoseSystemSemanticType(const Decl *D,
                                           const HLSLAppliedSemanticAttr *A,
                                           SemanticKind Kind) {
+  assert(Kind != SemanticKind::Invalid && Kind != SemanticKind::Arbitrary &&
+         "expected a recognized system semantic");
   ASTContext &Ctx = getASTContext();
 
   QualType T;
@@ -1180,8 +1189,7 @@ void SemaHLSL::diagnoseSystemSemanticType(const Decl *D,
     T = cast<ValueDecl>(D)->getType();
 
   // `out` and `inout` parameters are passed by reference.
-  if (const auto *RT = T->getAs<ReferenceType>())
-    T = RT->getPointeeType();
+  T = T.getNonReferenceType();
 
   // Array semantics constrain each element's type.
   QualType DeclaredTy = T;
@@ -1999,8 +2007,6 @@ void SemaHLSL::handleSemanticAttr(Decl *D, const 
ParsedAttr &AL) {
     assert(0 && "HLSLUnparsedSemantic is expected to have 2 int arguments.");
   }
   assert(IndexValue > 0 ? ExplicitIndex : true);
-  std::optional<unsigned> Index =
-      ExplicitIndex ? std::optional<unsigned>(IndexValue) : std::nullopt;
 
   SemanticKind Kind = llvm::hlsl::getSemanticKind(AL.getAttrName()->getName());
   if (Kind == SemanticKind::Invalid) {
@@ -2008,7 +2014,39 @@ void SemaHLSL::handleSemanticAttr(Decl *D, const 
ParsedAttr &AL) {
     return;
   }
 
-  D->addAttr(createSemanticAttr<HLSLParsedSemanticAttr>(AL, Index));
+  switch (Kind) {
+  // FIXME: These semantics do not yet have CodeGen support.
+  case SemanticKind::InstanceID:
+  case SemanticKind::RenderTargetArrayIndex:
+  case SemanticKind::ViewPortArrayIndex:
+  case SemanticKind::ClipDistance:
+  case SemanticKind::CullDistance:
+  case SemanticKind::OutputControlPointID:
+  case SemanticKind::DomainLocation:
+  case SemanticKind::PrimitiveID:
+  case SemanticKind::GSInstanceID:
+  case SemanticKind::SampleIndex:
+  case SemanticKind::IsFrontFace:
+  case SemanticKind::Coverage:
+  case SemanticKind::InnerCoverage:
+  case SemanticKind::Depth:
+  case SemanticKind::DepthLessEqual:
+  case SemanticKind::DepthGreaterEqual:
+  case SemanticKind::StencilRef:
+  case SemanticKind::TessFactor:
+  case SemanticKind::InsideTessFactor:
+  case SemanticKind::ViewID:
+  case SemanticKind::Barycentrics:
+  case SemanticKind::ShadingRate:
+  case SemanticKind::CullPrimitive:
+    Diag(AL.getLoc(), diag::err_hlsl_unknown_semantic) << AL;
+    return;
+  default:
+    break;
+  }
+
+  D->addAttr(HLSLParsedSemanticAttr::Create(
+      getASTContext(), AL.getAttrName()->getName(), IndexValue, AL));
 }
 
 void SemaHLSL::handlePackOffsetAttr(Decl *D, const ParsedAttr &AL) {
@@ -3375,9 +3413,7 @@ static bool CheckFloatRepresentation(Sema *S, 
SourceLocation Loc,
                                      int ArgOrdinal,
                                      clang::QualType PassedType) {
   clang::QualType BaseType =
-      PassedType->isVectorType()
-          ? PassedType->castAs<clang::VectorType>()->getElementType()
-          : PassedType;
+      getElementTypeOf(PassedType, /*IncludeMatrix=*/false);
   if (!BaseType->isFloat32Type())
     return S->Diag(Loc, diag::err_builtin_invalid_arg_type)
            << ArgOrdinal << /* scalar or vector of */ 5 << /* no int */ 0
@@ -3402,11 +3438,7 @@ static bool CheckAnyDoubleRepresentation(Sema *S, 
SourceLocation Loc,
                                          int ArgOrdinal,
                                          clang::QualType PassedType) {
   clang::QualType BaseType =
-      PassedType->isVectorType()
-          ? PassedType->castAs<clang::VectorType>()->getElementType()
-      : PassedType->isMatrixType()
-          ? PassedType->castAs<clang::MatrixType>()->getElementType()
-          : PassedType;
+      getElementTypeOf(PassedType, /*IncludeMatrix=*/true);
   if (!BaseType->isDoubleType()) {
     // FIXME: adopt standard `err_builtin_invalid_arg_type` instead of using
     // this custom error.
@@ -3806,22 +3838,13 @@ static StringRef getCurrentResourceMethodName(Sema &S, 
StringRef DefaultName) {
   return MD->getName();
 }
 
-// Returns the element type of a typed resource's contained type. Typed 
resource
-// element types are scalars or vectors of scalars, so anything that is not a
-// vector is already the element type.
-static QualType getTypedResourceElementType(QualType ContainedType) {
-  if (const auto *VecTy = ContainedType->getAs<VectorType>())
-    return VecTy->getElementType();
-  return ContainedType;
-}
-
 // Sampling from and gathering on resources with a 'double' element type is not
 // supported. Such resources are still valid declarations whose contents can be
 // accessed by other means, like Load or the subscript operator.
 static bool CheckNoDoubleElementType(Sema &S, CallExpr *TheCall,
                                      QualType ContainedType,
                                      StringRef DefaultName) {
-  QualType EltTy = getTypedResourceElementType(ContainedType);
+  QualType EltTy = getElementTypeOf(ContainedType, /*IncludeMatrix=*/false);
   if (!EltTy->isSpecificBuiltinType(BuiltinType::Double))
     return false;
 
@@ -3843,7 +3866,7 @@ static bool CheckIntegerElementTypeShaderModel(Sema &S, 
CallExpr *TheCall,
 
   // 'bool' is an integer type in HLSL, but sampling bool resources is never
   // allowed, so it must not be reported as requiring shader model 6.7.
-  QualType EltTy = getTypedResourceElementType(ContainedType);
+  QualType EltTy = getElementTypeOf(ContainedType, /*IncludeMatrix=*/false);
   if (!EltTy->isIntegerType() || EltTy->isBooleanType())
     return false;
 
diff --git a/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl 
b/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl
index 3c0f37900bc35..733df5f14b02b 100644
--- a/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl
+++ b/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl
@@ -23,6 +23,11 @@ void no_index(uint GI : SV_GroupIndex) {}
 void array_index(uint3 ID[2] : SV_DispatchThreadID) {}
 // expected-error@-1 {{semantic 'SV_DispatchThreadID' does not allow indexing}}
 
+// Inner dimensions also derive semantic indices.
+[shader("compute")][numthreads(1,1,1)]
+void nested_array_index(uint3 ID[1][2] : SV_DispatchThreadID) {}
+// expected-error@-1 {{semantic 'SV_DispatchThreadID' does not allow indexing}}
+
 // SV_Position is non-indexable on pixel shader inputs.
 [shader("pixel")]
 float4 position_ps(float4 P : SV_Position1) : SV_Target { return P; }
@@ -35,14 +40,7 @@ float4 position_vs(float4 P : SV_Position1) : SV_Position { 
return P; }
 [shader("pixel")]
 float4 user_index(float4 P : USER7) : SV_Target { return P; }
 
-// Clip/cull indices are allowed even with a system-value interpretation.
-[shader("pixel")]
-float4 clip_cull_index(float Clip : SV_ClipDistance1,
-                      float Cull : sv_culldistance1) : SV_Target {
-  return Clip + Cull;
-}
-
 // Recognizing the name does not bypass shader-stage validation.
 [shader("compute")][numthreads(1,1,1)]
-void clip_compute(float Clip : SV_ClipDistance1) {}
-// expected-error@-1 {{semantic 'SV_ClipDistance' is not supported in compute 
shader inputs}}
+void position_compute(float4 P : SV_Position) {}
+// expected-error@-1 {{semantic 'SV_Position' is not supported in compute 
shader inputs}}
diff --git a/clang/test/SemaHLSL/Semantics/semantic-overlap.hlsl 
b/clang/test/SemaHLSL/Semantics/semantic-overlap.hlsl
index ad5717c72b7a8..ad52208ea7654 100644
--- a/clang/test/SemaHLSL/Semantics/semantic-overlap.hlsl
+++ b/clang/test/SemaHLSL/Semantics/semantic-overlap.hlsl
@@ -50,6 +50,29 @@ struct CollideArray {
 float4 collide_array(CollideArray C) : SV_Target { return C.B; }
 // expected-note@-1 {{'C' declared here}}
 
+// All four elements of the nested array reserve semantic indices.
+struct CollideNestedArray {
+  float4 A[2][2] : USER0;
+// expected-note@-1 {{previous use is here}}
+  float4 B : USER3;
+// expected-error@-1 {{semantic index overlap USER3}}
+// expected-note@-2 {{'B' used here}}
+};
+
+[shader("pixel")]
+float4 collide_nested_array(CollideNestedArray C) : SV_Target { return C.B; }
+// expected-note@-1 {{'C' declared here}}
+
+struct NoCollideNestedArray {
+  float4 A[2][2] : USER0;
+  float4 B : USER4;
+};
+
+[shader("pixel")]
+float4 no_collide_nested_array(NoCollideNestedArray C) : SV_Target {
+  return C.B;
+}
+
 struct NoCollide {
   float4 A : USER0;
   float4 B : USER1;
@@ -58,5 +81,6 @@ struct NoCollide {
 [shader("pixel")]
 float4 no_collide(NoCollide C) : SV_Target { return C.A; }
 
-[shader("pixel")]
-float4 separate_signatures(float4 C : USER0) : SV_Target { return C; }
+// The same semantic index is allowed in separate input and output signatures.
+[shader("vertex")]
+float4 separate_signatures(float4 C : USER0) : USER0 { return C; }
diff --git a/clang/test/SemaHLSL/Semantics/semantic-types.hlsl 
b/clang/test/SemaHLSL/Semantics/semantic-types.hlsl
index 0078352ae2b92..84c5347f641a1 100644
--- a/clang/test/SemaHLSL/Semantics/semantic-types.hlsl
+++ b/clang/test/SemaHLSL/Semantics/semantic-types.hlsl
@@ -11,6 +11,15 @@ float4 position_double(double4 P : SV_Position) : SV_Target 
{ return (float4)P;
 [shader("pixel")]
 float4 position_half(half4 P : SV_Position) : SV_Target { return (float4)P; }
 
+// A single matrix element does not make a matrix a scalar semantic value.
+[shader("pixel")]
+float4 position_matrix(float1x1 P : SV_Position) : SV_Target { return 0; }
+// expected-error@-1 {{semantic 'SV_Position' must be a scalar or vector of up 
to 4 components of 16 or 32 bit floating-point type (was 'float1x1' (aka 
'matrix<float, 1, 1>'))}}
+
+[shader("compute")][numthreads(1,1,1)]
+void group_index_matrix(uint1x1 GI : SV_GroupIndex) {}
+// expected-error@-1 {{semantic 'SV_GroupIndex' must be a scalar of 32 bit 
integer type (was 'uint1x1' (aka 'matrix<uint, 1, 1>'))}}
+
 [shader("compute")][numthreads(1,1,1)]
 void group_index_vector(uint2 GI : SV_GroupIndex) {}
 // expected-error@-1 {{semantic 'SV_GroupIndex' must be a scalar of 32 bit 
integer type (was 'uint2' (aka 'vector<uint, 2>'))}}
diff --git a/clang/test/SemaHLSL/Semantics/semantic-zero-sized-array.hlsl 
b/clang/test/SemaHLSL/Semantics/semantic-zero-sized-array.hlsl
new file mode 100644
index 0000000000000..3805c6c20a837
--- /dev/null
+++ b/clang/test/SemaHLSL/Semantics/semantic-zero-sized-array.hlsl
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library 
-finclude-default-header -x hlsl -fsyntax-only -verify %s
+
+[shader("compute")][numthreads(1,1,1)]
+void empty_input(uint V[0] : SV_GroupIndex) {}
+// expected-error@-1 {{semantic 'SV_GroupIndex' cannot be applied to a 
zero-sized array}}
+// expected-note@-2 {{'V' declared here}}
+
+[shader("pixel")]
+void empty_output(out float4 V[0] : SV_Target) {}
+// expected-error@-1 {{semantic 'SV_Target' cannot be applied to a zero-sized 
array}}
+// expected-note@-2 {{'V' declared here}}
+
+// A zero in any array dimension makes the semantic range empty.
+[shader("compute")][numthreads(1,1,1)]
+void empty_inner_dimension(uint V[2][0] : SV_GroupIndex) {}
+// expected-error@-1 {{semantic 'SV_GroupIndex' cannot be applied to a 
zero-sized array}}
+// expected-note@-2 {{'V' declared here}}
+
+// User semantics and typedefs must not bypass the check.
+typedef float4 EmptyArray[0];
+
+[shader("vertex")]
+float4 empty_user_input(EmptyArray V : USER) : SV_Position { return 0; }
+// expected-error@-1 {{semantic 'USER' cannot be applied to a zero-sized 
array}}
+// expected-note@-2 {{'V' declared here}}
+
+struct EmptyOutput {
+  EmptyArray V : SV_Target;
+// expected-error@-1 {{semantic 'SV_Target' cannot be applied to a zero-sized 
array}}
+// expected-note@-2 {{'V' used here}}
+};
+
+[shader("pixel")]
+EmptyOutput empty_return() { return (EmptyOutput)0; }
diff --git a/clang/test/SemaHLSL/Semantics/target.index.hlsl 
b/clang/test/SemaHLSL/Semantics/target.index.hlsl
index b711c94471b5f..ee17e0516481c 100644
--- a/clang/test/SemaHLSL/Semantics/target.index.hlsl
+++ b/clang/test/SemaHLSL/Semantics/target.index.hlsl
@@ -23,6 +23,33 @@ void target_array_fits(out float4 T[2] : SV_Target6) { T[0] 
= 0; T[1] = 0; }
 void target_array_overflows(out float4 T[2] : SV_Target7) { T[0] = 0; T[1] = 
0; }
 // expected-error@-1 {{semantic 'SV_Target' index 8 exceeds the maximum 
supported index 7}}
 
+// Every dimension contributes to the semantic index range.
+typedef float4 TargetGrid[2][2];
+
+[shader("pixel")]
+void target_nested_array_fits(out TargetGrid T : SV_Target4) {}
+
+[shader("pixel")]
+void target_nested_array_overflows(out float4 T[2][2] : SV_Target5) {}
+// expected-error@-1 {{semantic 'SV_Target' index 8 exceeds the maximum 
supported index 7}}
+
+struct NestedTargets {
+  TargetGrid A;
+  float4 B;
+};
+
+// Inherited indices must advance past every element of the nested array.
+[shader("pixel")]
+NestedTargets target_nested_struct_fits() : SV_Target3 {
+  return (NestedTargets)0;
+}
+
+[shader("pixel")]
+NestedTargets target_nested_struct_overflows() : SV_Target4 {
+// expected-error@-1 {{semantic 'SV_Target' index 8 exceeds the maximum 
supported index 7}}
+  return (NestedTargets)0;
+}
+
 // The return semantic assigns consecutive indices to the fields.
 struct TwoTargets {
   float4 A;

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to