https://github.com/s-perron updated 
https://github.com/llvm/llvm-project/pull/184207

>From e6a0e19d7f6682d02fcbb6014bd9bf97b53d9e49 Mon Sep 17 00:00:00 2001
From: Steven Perron <[email protected]>
Date: Mon, 2 Mar 2026 12:20:09 -0500
Subject: [PATCH 1/3] [HLSL] Implement Texture2D default template

The Texture2D type has a default template of float4. This can be written
in a couple way: `Texture2D<>` or `Texture2D`. This must be implemented
for consistenty with DXC in HLSL202x.

To implement `Texture2D<>` we simply add a default type for the template
parameter.

To implement `Texture2D`, we have to add a special case for a template
type without a template instantiation. For HLSL, we check if it is a
texture type. If so, the default type is filled in.

Note that HLSL202x does not support C++17 Class Template Argument
Deduction, so we cannot use that feature to give us `Texture2D`.

Part of #175630.

Assisted-by: Gemini
---
 clang/include/clang/Sema/SemaHLSL.h           |  3 ++
 clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 20 ++++++---
 clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h   |  3 ++
 clang/lib/Sema/HLSLExternalSemaSource.cpp     |  5 ++-
 clang/lib/Sema/SemaDecl.cpp                   | 11 +++++
 clang/lib/Sema/SemaHLSL.cpp                   | 43 +++++++++++++++++++
 .../AST/HLSL/Texture2D-shorthand-AST.hlsl     | 39 +++++++++++++++++
 .../Texture2D-default-explicit-binding.hlsl   | 24 +++++++++++
 .../resources/Texture2D-default.hlsl          | 16 +++++++
 .../Texture2D-shorthand-contexts.hlsl         | 31 +++++++++++++
 10 files changed, 189 insertions(+), 6 deletions(-)
 create mode 100644 clang/test/AST/HLSL/Texture2D-shorthand-AST.hlsl
 create mode 100644 
clang/test/CodeGenHLSL/resources/Texture2D-default-explicit-binding.hlsl
 create mode 100644 clang/test/CodeGenHLSL/resources/Texture2D-default.hlsl
 create mode 100644 
clang/test/CodeGenHLSL/resources/Texture2D-shorthand-contexts.hlsl

diff --git a/clang/include/clang/Sema/SemaHLSL.h 
b/clang/include/clang/Sema/SemaHLSL.h
index a6a38531ac284..6341d3cd1cc00 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -203,6 +203,9 @@ class SemaHLSL : public SemaBase {
 
   bool CheckCompatibleParameterABI(FunctionDecl *New, FunctionDecl *Old);
 
+  QualType ActOnTemplateShorthand(TemplateDecl *Template,
+                                  SourceLocation NameLoc);
+
   // Diagnose whether the input ID is uint/unit2/uint3 type.
   bool diagnoseInputIDType(QualType T, const ParsedAttr &AL);
   bool diagnosePositionType(QualType T, const ParsedAttr &AL);
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp 
b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
index f99c16c8fe92e..2e535c02e5752 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -1741,9 +1741,8 @@ QualType BuiltinTypeDeclBuilder::getHandleElementType() {
   if (Template)
     return getFirstTemplateTypeParam();
 
-  if (auto *PartialSpec =
-          dyn_cast<ClassTemplatePartialSpecializationDecl>(Record)) {
-    const auto &Args = PartialSpec->getTemplateArgs();
+  if (auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(Record)) {
+    const auto &Args = Spec->getTemplateArgs();
     if (Args.size() > 0 && Args[0].getKind() == TemplateArgument::Type)
       return Args[0].getAsType();
   }
@@ -1784,6 +1783,12 @@ Expr 
*BuiltinTypeDeclBuilder::getConstantUnsignedIntExpr(unsigned value) {
 BuiltinTypeDeclBuilder &
 BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef<StringRef> Names,
                                                 ConceptDecl *CD = nullptr) {
+  return addSimpleTemplateParams(Names, {}, CD);
+}
+
+BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addSimpleTemplateParams(
+    ArrayRef<StringRef> Names, ArrayRef<QualType> DefaultValues,
+    ConceptDecl *CD) {
   if (Record->isCompleteDefinition()) {
     assert(Template && "existing record it not a template");
     assert(Template->getTemplateParameters()->size() == Names.size() &&
@@ -1791,9 +1796,14 @@ 
BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef<StringRef> Names,
     return *this;
   }
 
+  assert((DefaultValues.empty() || DefaultValues.size() == Names.size()) &&
+         "template default argument count mismatch");
+
   TemplateParameterListBuilder Builder = TemplateParameterListBuilder(*this);
-  for (StringRef Name : Names)
-    Builder.addTypeParameter(Name);
+  for (unsigned i = 0; i < Names.size(); ++i) {
+    QualType DefaultTy = DefaultValues.empty() ? QualType() : DefaultValues[i];
+    Builder.addTypeParameter(Names[i], DefaultTy);
+  }
   return Builder.finalizeTemplateArgs(CD);
 }
 
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h 
b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
index c27ff30c6ff73..2b4c36802a512 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
@@ -65,6 +65,9 @@ class BuiltinTypeDeclBuilder {
 
   BuiltinTypeDeclBuilder &addSimpleTemplateParams(ArrayRef<StringRef> Names,
                                                   ConceptDecl *CD);
+  BuiltinTypeDeclBuilder &
+  addSimpleTemplateParams(ArrayRef<StringRef> Names,
+                          ArrayRef<QualType> DefaultValues, ConceptDecl *CD);
   CXXRecordDecl *finalizeForwardDeclaration() { return Record; }
   BuiltinTypeDeclBuilder &completeDefinition();
 
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp 
b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index 788a129ec5390..bb86ef4e063e8 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -460,6 +460,7 @@ static ConceptDecl *constructBufferConceptDecl(Sema &S, 
NamespaceDecl *NSD,
 }
 
 void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
+  ASTContext &AST = SemaPtr->getASTContext();
   CXXRecordDecl *Decl;
   ConceptDecl *TypedBufferConcept = constructBufferConceptDecl(
       *SemaPtr, HLSLNamespace, /*isTypedBuffer*/ true);
@@ -612,8 +613,10 @@ void 
HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
     setupSamplerType(Decl, *SemaPtr).completeDefinition();
   });
 
+  QualType Float4Ty = AST.getExtVectorType(AST.FloatTy, 4);
   Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "Texture2D")
-             .addSimpleTemplateParams({"element_type"}, TypedBufferConcept)
+             .addSimpleTemplateParams({"element_type"}, {Float4Ty},
+                                      TypedBufferConcept)
              .finalizeForwardDeclaration();
 
   onCompletion(Decl, [this](CXXRecordDecl *Decl) {
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index be84974c70f27..90d21b8229f84 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -553,6 +553,17 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, 
SourceLocation NameLoc,
     }
     return CreateParsedType(T, TLB.getTypeSourceInfo(Context, T));
   }
+
+  if (getLangOpts().HLSL) {
+    if (auto *TD = dyn_cast_or_null<TemplateDecl>(
+            getAsTemplateNameDecl(IIDecl, /*AllowFunctionTemplates=*/false,
+                                  /*AllowDependent=*/false))) {
+      QualType ShorthandTy = HLSL().ActOnTemplateShorthand(TD, NameLoc);
+      if (!ShorthandTy.isNull())
+        return ParsedType::make(ShorthandTy);
+    }
+  }
+
   if (ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(IIDecl)) {
     (void)DiagnoseUseOfDecl(IDecl, NameLoc);
     if (!HasTrailingDot) {
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 5701b76427d60..d34811db10cbb 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -5548,3 +5548,46 @@ bool SemaHLSL::handleInitialization(VarDecl *VDecl, Expr 
*&Init) {
   Init = C;
   return true;
 }
+
+QualType SemaHLSL::ActOnTemplateShorthand(TemplateDecl *Template,
+                                          SourceLocation NameLoc) {
+  if (!Template)
+    return QualType();
+
+  DeclContext *DC = Template->getDeclContext();
+  if (!DC->isNamespace() || !cast<NamespaceDecl>(DC)->getIdentifier() ||
+      cast<NamespaceDecl>(DC)->getName() != "hlsl")
+    return QualType();
+
+  TemplateParameterList *Params = Template->getTemplateParameters();
+  if (!Params || Params->size() != 1)
+    return QualType();
+
+  if (Template->getName() != "Texture2D")
+    return QualType();
+
+  TemplateArgumentListInfo TemplateArgs(NameLoc, NameLoc);
+  for (NamedDecl *P : *Params) {
+    if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(P)) {
+      if (TTP->hasDefaultArgument()) {
+        TemplateArgs.addArgument(TTP->getDefaultArgument());
+        continue;
+      }
+    } else if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(P)) {
+      if (NTTP->hasDefaultArgument()) {
+        TemplateArgs.addArgument(NTTP->getDefaultArgument());
+        continue;
+      }
+    } else if (auto *TTPD = dyn_cast<TemplateTemplateParmDecl>(P)) {
+      if (TTPD->hasDefaultArgument()) {
+        TemplateArgs.addArgument(TTPD->getDefaultArgument());
+        continue;
+      }
+    }
+    return QualType();
+  }
+
+  return SemaRef.CheckTemplateIdType(
+      ElaboratedTypeKeyword::None, TemplateName(Template), NameLoc,
+      TemplateArgs, nullptr, /*ForNestedNameSpecifier=*/false);
+}
diff --git a/clang/test/AST/HLSL/Texture2D-shorthand-AST.hlsl 
b/clang/test/AST/HLSL/Texture2D-shorthand-AST.hlsl
new file mode 100644
index 0000000000000..7e23474266a01
--- /dev/null
+++ b/clang/test/AST/HLSL/Texture2D-shorthand-AST.hlsl
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -ast-dump 
-disable-llvm-passes -finclude-default-header -o - %s | FileCheck %s
+
+// CHECK: VarDecl {{.*}} t1 'hlsl::Texture2D<vector<float, 
4>>':'hlsl::Texture2D<>'
+Texture2D t1;
+
+// CHECK: VarDecl {{.*}} t1_explicit 'Texture2D<>':'hlsl::Texture2D<>'
+Texture2D<> t1_explicit;
+
+// CHECK: VarDecl {{.*}} t2 'Texture2D<float>':'hlsl::Texture2D<float>'
+Texture2D<float> t2;
+
+// CHECK: VarDecl {{.*}} t3 'Texture2D<float4>':'hlsl::Texture2D<>'
+Texture2D<float4> t3;
+
+// CHECK: TypedefDecl {{.*}} tex_alias 'hlsl::Texture2D<vector<float, 
4>>':'hlsl::Texture2D<>'
+typedef Texture2D tex_alias;
+
+struct S {
+  // CHECK: FieldDecl {{.*}} tex 'hlsl::Texture2D<vector<float, 
4>>':'hlsl::Texture2D<>'
+  Texture2D tex;
+};
+
+// CHECK: FunctionDecl {{.*}} foo 'hlsl::Texture2D<vector<float, 4>> 
(hlsl::Texture2D<vector<float, 4>>)'
+// CHECK: ParmVarDecl {{.*}} p 'hlsl::Texture2D<vector<float, 
4>>':'hlsl::Texture2D<>'
+Texture2D foo(Texture2D p) {
+  // CHECK: VarDecl {{.*}} local 'hlsl::Texture2D<vector<float, 
4>>':'hlsl::Texture2D<>'
+  Texture2D local;
+  return local;
+}
+
+template<typename T>
+void template_foo(T p) {
+  // CHECK: VarDecl {{.*}} local 'hlsl::Texture2D<vector<float, 
4>>':'hlsl::Texture2D<>'
+  Texture2D local;
+}
+
+void main() {
+  template_foo(1);
+}
diff --git 
a/clang/test/CodeGenHLSL/resources/Texture2D-default-explicit-binding.hlsl 
b/clang/test/CodeGenHLSL/resources/Texture2D-default-explicit-binding.hlsl
new file mode 100644
index 0000000000000..17ffc7a85aa3a
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-default-explicit-binding.hlsl
@@ -0,0 +1,24 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl 
-finclude-default-header -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl 
-finclude-default-header -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s 
--check-prefix=SPIRV
+
+SamplerState g_s : register(s0);
+Texture2D<> default_template : register(t1, space2);
+Texture2D implicit_template : register(t0, space1);
+
+// CHECK: @{{.*}}default_template = internal global %"class.hlsl::Texture2D" 
poison, align {{[0-9]+}}
+// CHECK: @{{.*}}implicit_template = internal global %"class.hlsl::Texture2D" 
poison, align {{[0-9]+}}
+// SPIRV: @{{.*}}default_template = internal global %"class.hlsl::Texture2D" 
poison, align {{[0-9]+}}
+// SPIRV: @{{.*}}implicit_template = internal global %"class.hlsl::Texture2D" 
poison, align {{[0-9]+}}
+
+[shader("pixel")]
+float4 main(float2 uv : TEXCOORD) : SV_Target {
+  return implicit_template.Sample(g_s, uv) + default_template.Sample(g_s, uv);
+}
+
+// CHECK: call void @{{.*}}__createFromBinding{{.*}}(ptr 
{{.*}}@{{.*}}default_template, i32 noundef 1, i32 noundef 2, i32 noundef 1, i32 
noundef 0, ptr noundef @{{.*}})
+// CHECK: call void @{{.*}}__createFromBinding{{.*}}(ptr 
{{.*}}@{{.*}}implicit_template, i32 noundef 0, i32 noundef 1, i32 noundef 1, 
i32 noundef 0, ptr noundef @{{.*}})
+// SPIRV: call void @{{.*}}__createFromBinding{{.*}}(ptr 
{{.*}}@{{.*}}default_template, i32 noundef 1, i32 noundef 2, i32 noundef 1, i32 
noundef 0, ptr noundef @{{.*}})
+// SPIRV: call void @{{.*}}__createFromBinding{{.*}}(ptr 
{{.*}}@{{.*}}implicit_template, i32 noundef 0, i32 noundef 1, i32 noundef 1, 
i32 noundef 0, ptr noundef @{{.*}})
+// CHECK: define void @main()
+// SPIRV: define void @main()
+
diff --git a/clang/test/CodeGenHLSL/resources/Texture2D-default.hlsl 
b/clang/test/CodeGenHLSL/resources/Texture2D-default.hlsl
new file mode 100644
index 0000000000000..5b5bb737a7958
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-default.hlsl
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl 
-std=hlsl202x -emit-llvm -disable-llvm-passes -finclude-default-header -o - %s 
| FileCheck %s
+
+// CHECK: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 
0, 0, 0, 2) }
+// CHECK: %"class.hlsl::Texture2D.0" = type { target("dx.Texture", float, 0, 
0, 0, 2) }
+
+// CHECK: @{{.*}}t1 = internal global %"class.hlsl::Texture2D" poison, align 4
+Texture2D<> t1;
+
+// CHECK: @{{.*}}t2 = internal global %"class.hlsl::Texture2D.0" poison, align 
4
+Texture2D<float> t2;
+
+// CHECK: @{{.*}}t3 = internal global %"class.hlsl::Texture2D" poison, align 4
+Texture2D t3;
+
+void main() {
+}
diff --git a/clang/test/CodeGenHLSL/resources/Texture2D-shorthand-contexts.hlsl 
b/clang/test/CodeGenHLSL/resources/Texture2D-shorthand-contexts.hlsl
new file mode 100644
index 0000000000000..71ce46232d088
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-shorthand-contexts.hlsl
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl 
-finclude-default-header -emit-llvm -disable-llvm-passes -o - %s | llvm-cxxfilt 
| FileCheck %s
+
+// CHECK: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 
0, 0, 0, 2) }
+
+SamplerState g_s : register(s0);
+
+struct S {
+  Texture2D tex;
+};
+
+// CHECK: define {{.*}}void @use_struct(S)(ptr noundef {{.*}}%s)
+void use_struct(S s) {
+  // CHECK: call {{.*}} <4 x float> @hlsl::Texture2D<float 
vector[4]>::Sample(hlsl::SamplerState, float vector[2])
+  float4 val = s.tex.Sample(g_s, float2(0.5, 0.5));
+}
+
+// CHECK: define {{.*}}void @use_param(hlsl::Texture2D<float vector[4]>)(ptr 
noundef {{.*}}%p)
+void use_param(Texture2D p) {
+  // CHECK: call {{.*}} <4 x float> @hlsl::Texture2D<float 
vector[4]>::Sample(hlsl::SamplerState, float vector[2])
+  float4 val = p.Sample(g_s, float2(0.5, 0.5));
+}
+
+[shader("pixel")]
+float4 main() : SV_Target {
+  // CHECK: %local = alloca %"class.hlsl::Texture2D"
+  Texture2D local;
+  // CHECK: call {{.*}} <4 x float> @hlsl::Texture2D<float 
vector[4]>::Sample(hlsl::SamplerState, float vector[2])
+  return local.Sample(g_s, float2(0.5, 0.5));
+}
+
+// CHECK: declare <4 x float> @llvm.dx.resource.sample.v4f32

>From fa149e2e114d086be32fe42e96af4303ef77c8f1 Mon Sep 17 00:00:00 2001
From: Steven Perron <[email protected]>
Date: Fri, 6 Mar 2026 09:40:12 -0500
Subject: [PATCH 2/3] [HLSL] Improve Texture2D default template and add
 resource negative tests

- Replace name check for Texture2D with isImplicit() in SemaHLSL to avoid 
conflicts with user-defined types.
- Add LLVM IR type assertions for class.hlsl::Texture2D in CodeGen tests.
- Add negative tests to verify that resource types (Buffer, RWBuffer, 
StructuredBuffer, RWStructuredBuffer) require template arguments when used as 
function parameters, return types, or struct members.
- Rename template parameter variable in HLSLBuiltinTypeDeclBuilder for clarity.

Part of #175630.
---
 clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 11 ++---
 clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h   |  2 +-
 clang/lib/Sema/SemaHLSL.cpp                   |  2 +-
 .../Texture2D-default-explicit-binding.hlsl   |  3 ++
 clang/test/SemaHLSL/BuiltIns/Buffers.hlsl     | 14 ++++++
 clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl   | 14 ++++++
 .../BuiltIns/RWStructuredBuffers.hlsl         | 44 +++++++++++++++++++
 .../SemaHLSL/BuiltIns/StructuredBuffers.hlsl  | 14 ++++++
 8 files changed, 97 insertions(+), 7 deletions(-)
 create mode 100644 clang/test/SemaHLSL/BuiltIns/RWStructuredBuffers.hlsl

diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp 
b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
index 2e535c02e5752..f9f57824bb48c 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -1786,9 +1786,10 @@ 
BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef<StringRef> Names,
   return addSimpleTemplateParams(Names, {}, CD);
 }
 
-BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addSimpleTemplateParams(
-    ArrayRef<StringRef> Names, ArrayRef<QualType> DefaultValues,
-    ConceptDecl *CD) {
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef<StringRef> Names,
+                                                ArrayRef<QualType> 
DefaultTypes,
+                                                ConceptDecl *CD) {
   if (Record->isCompleteDefinition()) {
     assert(Template && "existing record it not a template");
     assert(Template->getTemplateParameters()->size() == Names.size() &&
@@ -1796,12 +1797,12 @@ BuiltinTypeDeclBuilder 
&BuiltinTypeDeclBuilder::addSimpleTemplateParams(
     return *this;
   }
 
-  assert((DefaultValues.empty() || DefaultValues.size() == Names.size()) &&
+  assert((DefaultTypes.empty() || DefaultTypes.size() == Names.size()) &&
          "template default argument count mismatch");
 
   TemplateParameterListBuilder Builder = TemplateParameterListBuilder(*this);
   for (unsigned i = 0; i < Names.size(); ++i) {
-    QualType DefaultTy = DefaultValues.empty() ? QualType() : DefaultValues[i];
+    QualType DefaultTy = DefaultTypes.empty() ? QualType() : DefaultTypes[i];
     Builder.addTypeParameter(Names[i], DefaultTy);
   }
   return Builder.finalizeTemplateArgs(CD);
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h 
b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
index 2b4c36802a512..6e68c1f33d723 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
@@ -67,7 +67,7 @@ class BuiltinTypeDeclBuilder {
                                                   ConceptDecl *CD);
   BuiltinTypeDeclBuilder &
   addSimpleTemplateParams(ArrayRef<StringRef> Names,
-                          ArrayRef<QualType> DefaultValues, ConceptDecl *CD);
+                          ArrayRef<QualType> DefaultTypes, ConceptDecl *CD);
   CXXRecordDecl *finalizeForwardDeclaration() { return Record; }
   BuiltinTypeDeclBuilder &completeDefinition();
 
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index d34811db10cbb..6f1b8c52b36ac 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -5563,7 +5563,7 @@ QualType SemaHLSL::ActOnTemplateShorthand(TemplateDecl 
*Template,
   if (!Params || Params->size() != 1)
     return QualType();
 
-  if (Template->getName() != "Texture2D")
+  if (!Template->isImplicit())
     return QualType();
 
   TemplateArgumentListInfo TemplateArgs(NameLoc, NameLoc);
diff --git 
a/clang/test/CodeGenHLSL/resources/Texture2D-default-explicit-binding.hlsl 
b/clang/test/CodeGenHLSL/resources/Texture2D-default-explicit-binding.hlsl
index 17ffc7a85aa3a..06b4cb7ec9900 100644
--- a/clang/test/CodeGenHLSL/resources/Texture2D-default-explicit-binding.hlsl
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-default-explicit-binding.hlsl
@@ -5,6 +5,9 @@ SamplerState g_s : register(s0);
 Texture2D<> default_template : register(t1, space2);
 Texture2D implicit_template : register(t0, space1);
 
+// CHECK: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 
0, 0, 0, 2) }
+// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 
0, 0, 1, 0) }
+
 // CHECK: @{{.*}}default_template = internal global %"class.hlsl::Texture2D" 
poison, align {{[0-9]+}}
 // CHECK: @{{.*}}implicit_template = internal global %"class.hlsl::Texture2D" 
poison, align {{[0-9]+}}
 // SPIRV: @{{.*}}default_template = internal global %"class.hlsl::Texture2D" 
poison, align {{[0-9]+}}
diff --git a/clang/test/SemaHLSL/BuiltIns/Buffers.hlsl 
b/clang/test/SemaHLSL/BuiltIns/Buffers.hlsl
index 999372c95554e..8debd38801bd4 100644
--- a/clang/test/SemaHLSL/BuiltIns/Buffers.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/Buffers.hlsl
@@ -116,3 +116,17 @@ void main() {
   // expected-note@* {{function 'operator[]' which returns const-qualified 
type 'vector<float const hlsl_device &, 3>' declared here}}
   Buff[0] = 0.0;
 }
+
+// expected-error@+2 {{class template 'Buffer' requires template arguments}}
+// expected-note@*:* {{template declaration from hidden source: template 
<typename element_type> requires 
__is_typed_resource_element_compatible<element_type> class Buffer {}}}
+void f1(Buffer B) {}
+
+// expected-error@+2 {{class template 'Buffer' requires template arguments}}
+// expected-note@*:* {{template declaration from hidden source: template 
<typename element_type> requires 
__is_typed_resource_element_compatible<element_type> class Buffer {}}}
+Buffer f2();
+
+struct S {
+  // expected-error@+2 {{class template 'Buffer' requires template arguments}}
+  // expected-note@*:* {{template declaration from hidden source: template 
<typename element_type> requires 
__is_typed_resource_element_compatible<element_type> class Buffer {}}}
+  Buffer B;
+};
diff --git a/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl 
b/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
index b33f2af8a1117..a767743a0eccc 100644
--- a/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
@@ -112,3 +112,17 @@ void main() {
   (void)Buff.__handle; // expected-error {{'__handle' is a private member of 
'hlsl::RWBuffer<vector<float, 3>>'}}
   // expected-note@* {{implicitly declared private here}}
 }
+
+// expected-error@+2 {{class template 'RWBuffer' requires template arguments}}
+// expected-note@*:* {{template declaration from hidden source: template 
<typename element_type> requires 
__is_typed_resource_element_compatible<element_type> class RWBuffer {}}}
+void f1(RWBuffer B) {}
+
+// expected-error@+2 {{class template 'RWBuffer' requires template arguments}}
+// expected-note@*:* {{template declaration from hidden source: template 
<typename element_type> requires 
__is_typed_resource_element_compatible<element_type> class RWBuffer {}}}
+RWBuffer f2();
+
+struct S {
+  // expected-error@+2 {{class template 'RWBuffer' requires template 
arguments}}
+  // expected-note@*:* {{template declaration from hidden source: template 
<typename element_type> requires 
__is_typed_resource_element_compatible<element_type> class RWBuffer {}}}
+  RWBuffer B;
+};
diff --git a/clang/test/SemaHLSL/BuiltIns/RWStructuredBuffers.hlsl 
b/clang/test/SemaHLSL/BuiltIns/RWStructuredBuffers.hlsl
new file mode 100644
index 0000000000000..30c04937f28e3
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/RWStructuredBuffers.hlsl
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl 
-fsyntax-only -verify %s
+
+typedef vector<float, 3> float3;
+
+RWStructuredBuffer<float3> Buff;
+
+// expected-error@+2 {{class template 'RWStructuredBuffer' requires template 
arguments}}
+// expected-note@*:* {{template declaration from hidden source: template 
<typename element_type> requires 
__is_structured_resource_element_compatible<element_type> class 
RWStructuredBuffer {}}}
+RWStructuredBuffer BufferErr1;
+
+// expected-error@+2 {{too few template arguments for class template 
'RWStructuredBuffer'}}
+// expected-note@*:* {{template declaration from hidden source: template 
<typename element_type> requires 
__is_structured_resource_element_compatible<element_type> class 
RWStructuredBuffer {}}}
+RWStructuredBuffer<> BufferErr2;
+
+// test elements of 0 size
+// expected-error@+3{{constraints not satisfied for class template 
'RWStructuredBuffer' [with element_type = int[0]]}}
+// expected-note@*:*{{because 'int[0]' does not satisfy 
'__is_structured_resource_element_compatible'}}
+// expected-note@*:*{{because 'sizeof(int[0]) >= 1UL' (0 >= 1) evaluated to 
false}}
+RWStructuredBuffer<int[0]> BufferErr3;
+
+// In C++, empty structs do have a size of 1. So should HLSL.
+// The concept will accept empty structs as element types, despite it being 
unintuitive.
+struct Empty {};
+RWStructuredBuffer<Empty> BufferErr4;
+
+[numthreads(1,1,1)]
+void main() {
+  (void)Buff.__handle; // expected-error {{'__handle' is a private member of 
'hlsl::RWStructuredBuffer<vector<float, 3>>'}}
+  // expected-note@* {{implicitly declared private here}}
+}
+
+// expected-error@+2 {{class template 'RWStructuredBuffer' requires template 
arguments}}
+// expected-note@*:* {{template declaration from hidden source: template 
<typename element_type> requires 
__is_structured_resource_element_compatible<element_type> class 
RWStructuredBuffer {}}}
+void f1(RWStructuredBuffer B) {}
+
+// expected-error@+2 {{class template 'RWStructuredBuffer' requires template 
arguments}}
+// expected-note@*:* {{template declaration from hidden source: template 
<typename element_type> requires 
__is_structured_resource_element_compatible<element_type> class 
RWStructuredBuffer {}}}
+RWStructuredBuffer f2();
+
+struct S {
+// expected-error@+2 {{class template 'RWStructuredBuffer' requires template 
arguments}}
+// expected-note@*:* {{template declaration from hidden source: template 
<typename element_type> requires 
__is_structured_resource_element_compatible<element_type> class 
RWStructuredBuffer {}}}
+  RWStructuredBuffer B;
+};
diff --git a/clang/test/SemaHLSL/BuiltIns/StructuredBuffers.hlsl 
b/clang/test/SemaHLSL/BuiltIns/StructuredBuffers.hlsl
index e5b1125b873e1..ba63fc25819e5 100644
--- a/clang/test/SemaHLSL/BuiltIns/StructuredBuffers.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/StructuredBuffers.hlsl
@@ -33,3 +33,17 @@ void main() {
   // expected-note@* {{function 'operator[]' which returns const-qualified 
type 'vector<float const hlsl_device &, 3>' declared here}}
   Buff[0] = 0.0;
 }
+
+// expected-error@+2 {{class template 'StructuredBuffer' requires template 
arguments}}
+// expected-note@*:* {{template declaration from hidden source: template 
<typename element_type> requires 
__is_structured_resource_element_compatible<element_type> class 
StructuredBuffer {}}}
+void f1(StructuredBuffer B) {}
+
+// expected-error@+2 {{class template 'StructuredBuffer' requires template 
arguments}}
+// expected-note@*:* {{template declaration from hidden source: template 
<typename element_type> requires 
__is_structured_resource_element_compatible<element_type> class 
StructuredBuffer {}}}
+StructuredBuffer f2();
+
+struct S {
+// expected-error@+2 {{class template 'StructuredBuffer' requires template 
arguments}}
+// expected-note@*:* {{template declaration from hidden source: template 
<typename element_type> requires 
__is_structured_resource_element_compatible<element_type> class 
StructuredBuffer {}}}
+  StructuredBuffer B;
+};

>From f5eaedddbb8928e0d3249d5e86b008fe701eaaa1 Mon Sep 17 00:00:00 2001
From: Steven Perron <[email protected]>
Date: Fri, 6 Mar 2026 10:00:48 -0500
Subject: [PATCH 3/3] [HLSL] Explain manual default argument extraction for
 template shorthand

Add a comment explaining that manually extracting default arguments in
ActOnTemplateShorthand is necessary to provide better diagnostic error
messages. It ensures that resource types without a default argument
(like Buffer) trigger a 'requires template arguments' error instead of
a 'too few template arguments' error.

Part of #175630.
---
 clang/lib/Sema/SemaHLSL.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 6f1b8c52b36ac..87eaa754ed0d4 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -5566,6 +5566,11 @@ QualType SemaHLSL::ActOnTemplateShorthand(TemplateDecl 
*Template,
   if (!Template->isImplicit())
     return QualType();
 
+  // We manually extract default arguments here instead of letting
+  // CheckTemplateIdType handle it. This ensures that for resource types that
+  // lack a default argument (like Buffer), we return a null QualType, which
+  // triggers the "requires template arguments" error rather than a less
+  // descriptive "too few template arguments" error.
   TemplateArgumentListInfo TemplateArgs(NameLoc, NameLoc);
   for (NamedDecl *P : *Params) {
     if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(P)) {

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

Reply via email to