https://github.com/s-perron updated https://github.com/llvm/llvm-project/pull/195154
>From 18e85499053dc9811c98bce4667244d471462125 Mon Sep 17 00:00:00 2001 From: Steven Perron <[email protected]> Date: Thu, 30 Apr 2026 11:52:48 -0400 Subject: [PATCH 1/2] [HLSL] Add type traits for ConstantBuffers templates This commit adds the type traits to restrict the template type in a ConstantBuffer to structs or classes that do not contain a resource type. Assisted-by: Gemini --- clang/include/clang/Basic/TokenKinds.def | 1 + clang/include/clang/Sema/SemaHLSL.h | 1 + clang/lib/Sema/HLSLExternalSemaSource.cpp | 50 ++++++++++++++++--- clang/lib/Sema/SemaHLSL.cpp | 13 +++++ clang/lib/Sema/SemaTypeTraits.cpp | 9 ++++ .../SemaHLSL/BuiltIns/ConstantBuffers.hlsl | 33 ++++++++++++ 6 files changed, 101 insertions(+), 6 deletions(-) diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 005d81b5b9282..f07d8ebb75035 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -691,6 +691,7 @@ KEYWORD(column_major , KEYHLSL) TYPE_TRAIT_2(__builtin_hlsl_is_scalarized_layout_compatible, IsScalarizedLayoutCompatible, KEYHLSL) TYPE_TRAIT_1(__builtin_hlsl_is_intangible, IsIntangibleType, KEYHLSL) TYPE_TRAIT_1(__builtin_hlsl_is_typed_resource_element_compatible, IsTypedResourceElementCompatible, KEYHLSL) +TYPE_TRAIT_1(__builtin_hlsl_is_constant_buffer_element_compatible, IsConstantBufferElementCompatible, KEYHLSL) // OpenMP Type Traits UNARY_EXPR_OR_TYPE_TRAIT(__builtin_omp_required_simd_align, OpenMPRequiredSimdAlign, KEYALL) diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h index 68c2f209976c4..e65de5d4aa4c3 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -216,6 +216,7 @@ class SemaHLSL : public SemaBase { // HLSL Type trait implementations bool IsScalarizedLayoutCompatible(QualType T1, QualType T2) const; bool IsTypedResourceElementCompatible(QualType T1); + bool IsConstantBufferElementCompatible(QualType T1); bool CheckCompatibleParameterABI(FunctionDecl *New, FunctionDecl *Old); diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index 9769eee10ae2f..449b32a215631 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -365,6 +365,32 @@ static Expr *constructTypedBufferConstraintExpr(Sema &S, SourceLocation NameLoc, return TypedResExpr; } +// This function is responsible for constructing the constraint expression for +// this concept: +// template<typename T> concept is_constant_buffer_element_compatible = +// std::is_class_v<T> && !__is_intangible(T); +static Expr *constructConstantBufferConstraintExpr(Sema &S, + SourceLocation NameLoc, + TemplateTypeParmDecl *T) { + ASTContext &Context = S.getASTContext(); + + // Obtain the QualType for 'bool' + QualType BoolTy = Context.BoolTy; + + // Create a QualType that points to this TemplateTypeParmDecl + QualType TType = Context.getTypeDeclType(T); + + // Create a TypeSourceInfo for the template type parameter 'T' + TypeSourceInfo *TTypeSourceInfo = + Context.getTrivialTypeSourceInfo(TType, NameLoc); + + TypeTraitExpr *ResExpr = TypeTraitExpr::Create( + Context, BoolTy, NameLoc, UTT_IsConstantBufferElementCompatible, + {TTypeSourceInfo}, NameLoc, true); + + return ResExpr; +} + // This function is responsible for constructing the constraint expression for // this concept: // template<typename T> concept is_structured_resource_element_compatible = @@ -415,8 +441,10 @@ static Expr *constructStructuredBufferConstraintExpr(Sema &S, return CombinedExpr; } +enum class HLSLBufferType { Typed, Structured, Constant }; + static ConceptDecl *constructBufferConceptDecl(Sema &S, NamespaceDecl *NSD, - bool isTypedBuffer) { + HLSLBufferType BT) { ASTContext &Context = S.getASTContext(); DeclContext *DC = NSD->getDeclContext(); SourceLocation DeclLoc = SourceLocation(); @@ -440,14 +468,22 @@ static ConceptDecl *constructBufferConceptDecl(Sema &S, NamespaceDecl *NSD, DeclarationName DeclName; Expr *ConstraintExpr = nullptr; - if (isTypedBuffer) { + switch (BT) { + case HLSLBufferType::Typed: DeclName = DeclarationName( &Context.Idents.get("__is_typed_resource_element_compatible")); ConstraintExpr = constructTypedBufferConstraintExpr(S, DeclLoc, T); - } else { + break; + case HLSLBufferType::Structured: DeclName = DeclarationName( &Context.Idents.get("__is_structured_resource_element_compatible")); ConstraintExpr = constructStructuredBufferConstraintExpr(S, DeclLoc, T); + break; + case HLSLBufferType::Constant: + DeclName = DeclarationName( + &Context.Idents.get("__is_constant_buffer_element_compatible")); + ConstraintExpr = constructConstantBufferConstraintExpr(S, DeclLoc, T); + break; } // Create a ConceptDecl @@ -468,12 +504,14 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { ASTContext &AST = SemaPtr->getASTContext(); CXXRecordDecl *Decl; ConceptDecl *TypedBufferConcept = constructBufferConceptDecl( - *SemaPtr, HLSLNamespace, /*isTypedBuffer*/ true); + *SemaPtr, HLSLNamespace, HLSLBufferType::Typed); ConceptDecl *StructuredBufferConcept = constructBufferConceptDecl( - *SemaPtr, HLSLNamespace, /*isTypedBuffer*/ false); + *SemaPtr, HLSLNamespace, HLSLBufferType::Structured); + ConceptDecl *ConstantBufferConcept = constructBufferConceptDecl( + *SemaPtr, HLSLNamespace, HLSLBufferType::Constant); Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "ConstantBuffer") - .addSimpleTemplateParams({"element_type"}) + .addSimpleTemplateParams({"element_type"}, ConstantBufferConcept) .finalizeForwardDeclaration(); onCompletion(Decl, [this](CXXRecordDecl *Decl) { diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 594a18f0b8c78..4a7df5b4266f6 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -4700,6 +4700,19 @@ static void BuildFlattenedTypeList(QualType BaseTy, } } +bool SemaHLSL::IsConstantBufferElementCompatible(clang::QualType QT) { + if (QT.isNull()) + return false; + + // Must be a class/struct. + const auto *RD = QT->getAsCXXRecordDecl(); + if (!RD || RD->isUnion()) + return false; + + // Cannot be a resource type or contain one. + return !QT->isHLSLIntangibleType(); +} + bool SemaHLSL::IsTypedResourceElementCompatible(clang::QualType QT) { // null and array types are not allowed. if (QT.isNull() || QT->isArrayType()) diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index a94a59e8add7b..c79b3f7045ca6 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -367,6 +367,7 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT, case UTT_IsCompound: case UTT_IsMemberPointer: case UTT_IsTypedResourceElementCompatible: + case UTT_IsConstantBufferElementCompatible: // Fall-through // These traits are modeled on type predicates in C++0x [meta.unary.prop] @@ -1131,6 +1132,14 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT, return false; return Self.HLSL().IsTypedResourceElementCompatible(T); + + case UTT_IsConstantBufferElementCompatible: + assert(Self.getLangOpts().HLSL && + "constant buffer element compatible types are an HLSL-only feature"); + if (T->isIncompleteType()) + return false; + + return Self.HLSL().IsConstantBufferElementCompatible(T); } } diff --git a/clang/test/SemaHLSL/BuiltIns/ConstantBuffers.hlsl b/clang/test/SemaHLSL/BuiltIns/ConstantBuffers.hlsl index 10c65031b79f2..e6c42429a7d97 100644 --- a/clang/test/SemaHLSL/BuiltIns/ConstantBuffers.hlsl +++ b/clang/test/SemaHLSL/BuiltIns/ConstantBuffers.hlsl @@ -20,6 +20,39 @@ union U { ConstantBuffer<S> cb; ConstantBuffer<Empty> cb_empty; +// Invalid: non-struct/class +// expected-error@+1 {{constraints not satisfied for class template 'ConstantBuffer'}} +ConstantBuffer<float> cb_float; +// expected-note@* {{because 'float' does not satisfy '__is_constant_buffer_element_compatible'}} +// expected-note@* {{because '__builtin_hlsl_is_constant_buffer_element_compatible(float)' evaluated to false}} + +// expected-error@+1 {{constraints not satisfied for class template 'ConstantBuffer'}} +ConstantBuffer<float4> cb_float4; +// expected-note@* {{because 'float4' (aka 'vector<float, 4>') does not satisfy '__is_constant_buffer_element_compatible'}} +// expected-note@* {{because '__builtin_hlsl_is_constant_buffer_element_compatible(vector<float, 4>)' evaluated to false}} + +// expected-error@+1 {{constraints not satisfied for class template 'ConstantBuffer'}} +ConstantBuffer<float[4]> cb_array; +// expected-note@* {{because 'float[4]' does not satisfy '__is_constant_buffer_element_compatible'}} +// expected-note@* {{because '__builtin_hlsl_is_constant_buffer_element_compatible(float[4])' evaluated to false}} + +// Invalid: contains resource +// expected-error@+1 {{constraints not satisfied for class template 'ConstantBuffer'}} +ConstantBuffer<ContainsResource> cb_res; +// expected-note@* {{because 'ContainsResource' does not satisfy '__is_constant_buffer_element_compatible'}} +// expected-note@* {{because '__builtin_hlsl_is_constant_buffer_element_compatible(ContainsResource)' evaluated to false}} + +// Invalid: intangible type +// expected-error@+1 {{use of class template 'Texture2D' requires template arguments}} +ConstantBuffer<Texture2D> cb_tex; +// expected-note@* {{template declaration from hidden source}} + +// Invalid: union +// expected-error@+1 {{constraints not satisfied for class template 'ConstantBuffer'}} +ConstantBuffer<U> cb_union; +// expected-note@* {{because 'U' does not satisfy '__is_constant_buffer_element_compatible'}} +// expected-note@* {{because '__builtin_hlsl_is_constant_buffer_element_compatible(U)' evaluated to false}} + void takes_inout_s(inout S s) {} void foo() { >From 14dd30edaeb6f2b8cc2e4588a1b057e505211a35 Mon Sep 17 00:00:00 2001 From: Steven Perron <[email protected]> Date: Thu, 7 May 2026 16:30:50 -0400 Subject: [PATCH 2/2] Move test file, and add new test. --- .../SemaHLSL/{BuiltIns => Resources}/ConstantBuffers.hlsl | 6 ++++++ 1 file changed, 6 insertions(+) rename clang/test/SemaHLSL/{BuiltIns => Resources}/ConstantBuffers.hlsl (88%) diff --git a/clang/test/SemaHLSL/BuiltIns/ConstantBuffers.hlsl b/clang/test/SemaHLSL/Resources/ConstantBuffers.hlsl similarity index 88% rename from clang/test/SemaHLSL/BuiltIns/ConstantBuffers.hlsl rename to clang/test/SemaHLSL/Resources/ConstantBuffers.hlsl index e6c42429a7d97..0ef3ada50c988 100644 --- a/clang/test/SemaHLSL/BuiltIns/ConstantBuffers.hlsl +++ b/clang/test/SemaHLSL/Resources/ConstantBuffers.hlsl @@ -47,6 +47,12 @@ ConstantBuffer<ContainsResource> cb_res; ConstantBuffer<Texture2D> cb_tex; // expected-note@* {{template declaration from hidden source}} +// Invalid: intangible type +// expected-error@+1 {{constraints not satisfied for class template 'ConstantBuffer'}} +ConstantBuffer<Texture2D<float>> cb_tex; +// expected-note@* {{because 'Texture2D<float>' does not satisfy '__is_constant_buffer_element_compatible'}} +// expected-note@*:* {{because '__builtin_hlsl_is_constant_buffer_element_compatible(hlsl::Texture2D<float>)' evaluated to false}} + // Invalid: union // expected-error@+1 {{constraints not satisfied for class template 'ConstantBuffer'}} ConstantBuffer<U> cb_union; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
