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

Reply via email to