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
