Author: Steven Perron Date: 2026-05-08T14:06:51Z New Revision: 90a2a8e6d0427804233b0aa58969b65bae87e425
URL: https://github.com/llvm/llvm-project/commit/90a2a8e6d0427804233b0aa58969b65bae87e425 DIFF: https://github.com/llvm/llvm-project/commit/90a2a8e6d0427804233b0aa58969b65bae87e425.diff LOG: [HLSL] Add type traits for ConstantBuffers templates (#195154) 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 <!-- branch-stack-start --> ------------------------- - main - https://github.com/llvm/llvm-project/pull/195151 - https://github.com/llvm/llvm-project/pull/195152 - https://github.com/llvm/llvm-project/pull/195153 - https://github.com/llvm/llvm-project/pull/195154 :point_left: <sup>[Stack](https://www.git-town.com/how-to/proposal-breadcrumb.html) generated by [Git Town](https://github.com/git-town/git-town)</sup> <!-- branch-stack-end --> Added: clang/test/SemaHLSL/Resources/ConstantBuffers.hlsl Modified: clang/include/clang/Basic/TokenKinds.def clang/include/clang/Sema/SemaHLSL.h clang/lib/Sema/HLSLExternalSemaSource.cpp clang/lib/Sema/SemaHLSL.cpp clang/lib/Sema/SemaTypeTraits.cpp Removed: clang/test/SemaHLSL/BuiltIns/ConstantBuffers.hlsl ################################################################################ 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 deleted file mode 100644 index 10c65031b79f2..0000000000000 --- a/clang/test/SemaHLSL/BuiltIns/ConstantBuffers.hlsl +++ /dev/null @@ -1,35 +0,0 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -finclude-default-header -fsyntax-only -verify %s - -struct S { // expected-note 3 {{candidate constructor}} - float a; - int b; -}; - -struct Empty {}; - -struct ContainsResource { - Texture2D tex; -}; - -union U { - float a; - int b; -}; - -// Valid -ConstantBuffer<S> cb; -ConstantBuffer<Empty> cb_empty; - -void takes_inout_s(inout S s) {} - -void foo() { - // This case should fail because we cannot writeback to `cb` after the call. - // expected-error@+1 {{no viable constructor copying parameter of type 'const hlsl_constant S'}} - takes_inout_s(cb); -} - -void test_direct_assignment() { - // expected-error@+2 {{cannot assign to return value because function 'operator const hlsl_constant S &' returns a const value}} - // expected-note@* {{function 'operator const hlsl_constant S &' which returns const-qualified type 'const hlsl_constant S &' declared here}} - cb.a = 5.0; -} diff --git a/clang/test/SemaHLSL/Resources/ConstantBuffers.hlsl b/clang/test/SemaHLSL/Resources/ConstantBuffers.hlsl new file mode 100644 index 0000000000000..0ef3ada50c988 --- /dev/null +++ b/clang/test/SemaHLSL/Resources/ConstantBuffers.hlsl @@ -0,0 +1,74 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -finclude-default-header -fsyntax-only -verify %s + +struct S { // expected-note 3 {{candidate constructor}} + float a; + int b; +}; + +struct Empty {}; + +struct ContainsResource { + Texture2D tex; +}; + +union U { + float a; + int b; +}; + +// Valid +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: 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; +// 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() { + // This case should fail because we cannot writeback to `cb` after the call. + // expected-error@+1 {{no viable constructor copying parameter of type 'const hlsl_constant S'}} + takes_inout_s(cb); +} + +void test_direct_assignment() { + // expected-error@+2 {{cannot assign to return value because function 'operator const hlsl_constant S &' returns a const value}} + // expected-note@* {{function 'operator const hlsl_constant S &' which returns const-qualified type 'const hlsl_constant S &' declared here}} + cb.a = 5.0; +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
