[clang] [SemaHLSL] Add missing validations of existing semantics (PR #224139)
Finn Plummer via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 16 14:10:05 PDT 2026
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 <mail at inbelic.dev>
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 at +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 at +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 at +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 at -1 {{semantic 'SV_DispatchThreadID' does not allow indexing}}
+// expected-error at -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 at +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 at +1 {{attribute 'SV_GroupID' only applies to a field or parameter of type 'uint/uint2/uint3'}}
+[shader("compute")][numthreads(8,8,1)]
+// expected-error at +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 at +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 at -1 {{semantic 'SV_GroupID' does not allow indexing}}
+// expected-error at -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 at +1 {{attribute 'SV_GroupThreadID' only applies to a field or parameter of type 'uint/uint2/uint3'}}
+[shader("compute")][numthreads(8,8,1)]
+// expected-error at +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 at +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 at -1 {{semantic 'SV_GroupThreadID' does not allow indexing}}
+// expected-error at -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 at +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 at +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 at +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 at -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 at +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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -1 {{attribute 'SV_VertexID' only applies to a field or parameter of type 'uint'}}
+// expected-error at -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 at -1 {{attribute 'SV_VertexID' only applies to a field or parameter of type 'uint'}}
+// expected-error at -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 at -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 at -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 at -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 at -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 at -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 at -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 <mail at inbelic.dev>
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 at -1 {{semantic 'SV_DispatchThreadID' does not allow indexing}}
+
+}
+
void foo() {
// expected-warning at +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 at -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 at -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 at -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 at -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 at -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 at -1 {{semantic 'SV_Target' index 8 exceeds the maximum supported index 7}}
>From 24e0a148da1573b561151684346ea11dbe0c7306 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
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 at +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 at +1 {{unknown HLSL semantic 'sv_iwantapony'}}
+void IndexedPony(int GI : sv_iwantapony1) { }
+
// expected-error at +3 {{expected HLSL Semantic identifier}}
// expected-error at +2 {{expected ')'}}
// expected-note at +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 at -1 {{semantic 'SV_ClipDistance' is not supported in compute shader inputs}}
>From 58dd2ea3f0a5750e51bbc301523582a158cb4df4 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
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 at -1 {{semantic index overlap SV_Target0}}
+// expected-note at -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 at -1 {{previous use is here}}
+ float4 B : USER0;
+// expected-error at -1 {{semantic index overlap USER0}}
+// expected-note at -2 {{'B' used here}}
+};
+
+[shader("pixel")]
+float4 collide(Collide C) : SV_Target { return C.A; }
+// expected-note at -1 {{'C' declared here}}
+
+// Semantic names are case-insensitive.
+struct CollideCase {
+ float4 A : user0;
+// expected-note at -1 {{previous use is here}}
+ float4 B : USER0;
+// expected-error at -1 {{semantic index overlap USER0}}
+// expected-note at -2 {{'B' used here}}
+};
+
+[shader("pixel")]
+float4 collide_case(CollideCase C) : SV_Target { return C.A; }
+// expected-note at -1 {{'C' declared here}}
+
+struct CollidePosition {
+ float4 A : SV_Position;
+// expected-note at -1 {{previous use is here}}
+ float4 B : sv_position;
+// expected-error at -1 {{semantic index overlap sv_position0}}
+// expected-note at -2 {{'B' used here}}
+};
+
+[shader("pixel")]
+float4 collide_position(CollidePosition C) : SV_Target { return C.A; }
+// expected-note at -1 {{'C' declared here}}
+
+// The array reserves USER0 and USER1.
+struct CollideArray {
+ float4 A[2] : USER0;
+// expected-note at -1 {{previous use is here}}
+ float4 B : USER1;
+// expected-error at -1 {{semantic index overlap USER1}}
+// expected-note at -2 {{'B' used here}}
+};
+
+[shader("pixel")]
+float4 collide_array(CollideArray C) : SV_Target { return C.B; }
+// expected-note at -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 <mail at inbelic.dev>
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 at -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 at -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 at -1 {{semantic 'SV_ClipDistance' is not supported in compute shader inputs}}
+void position_compute(float4 P : SV_Position) {}
+// expected-error at -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 at -1 {{'C' declared here}}
+// All four elements of the nested array reserve semantic indices.
+struct CollideNestedArray {
+ float4 A[2][2] : USER0;
+// expected-note at -1 {{previous use is here}}
+ float4 B : USER3;
+// expected-error at -1 {{semantic index overlap USER3}}
+// expected-note at -2 {{'B' used here}}
+};
+
+[shader("pixel")]
+float4 collide_nested_array(CollideNestedArray C) : SV_Target { return C.B; }
+// expected-note at -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 at -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 at -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 at -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 at -1 {{semantic 'SV_GroupIndex' cannot be applied to a zero-sized array}}
+// expected-note at -2 {{'V' declared here}}
+
+[shader("pixel")]
+void empty_output(out float4 V[0] : SV_Target) {}
+// expected-error at -1 {{semantic 'SV_Target' cannot be applied to a zero-sized array}}
+// expected-note at -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 at -1 {{semantic 'SV_GroupIndex' cannot be applied to a zero-sized array}}
+// expected-note at -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 at -1 {{semantic 'USER' cannot be applied to a zero-sized array}}
+// expected-note at -2 {{'V' declared here}}
+
+struct EmptyOutput {
+ EmptyArray V : SV_Target;
+// expected-error at -1 {{semantic 'SV_Target' cannot be applied to a zero-sized array}}
+// expected-note at -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 at -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 at -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 at -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;
More information about the cfe-commits
mailing list