[clang] [llvm] [HLSL] Add implicit resource element type concepts to AST (PR #116413)

Joshua Batista via cfe-commits cfe-commits at lists.llvm.org
Wed Nov 20 15:22:43 PST 2024


https://github.com/bob80905 updated https://github.com/llvm/llvm-project/pull/116413

>From 92ccbe72ca95ad2df5a81b76244a8a8d7cedef40 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Fri, 15 Nov 2024 09:00:15 -0800
Subject: [PATCH 1/4] update new tests

---
 clang/lib/Sema/HLSLExternalSemaSource.cpp     | 210 +++++++++++++++++-
 .../AST/HLSL/AppendStructuredBuffer-AST.hlsl  |   4 +-
 .../AST/HLSL/ConsumeStructuredBuffer-AST.hlsl |   4 +-
 clang/test/AST/HLSL/RWBuffer-AST.hlsl         |  22 +-
 .../test/AST/HLSL/RWStructuredBuffer-AST.hlsl |   4 +-
 ...RasterizerOrderedStructuredBuffer-AST.hlsl |   4 +-
 clang/test/AST/HLSL/StructuredBuffer-AST.hlsl |   4 +-
 ...d_resource_element_compatible_concept.hlsl |  10 +
 clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl   |  16 +-
 .../SemaHLSL/BuiltIns/StructuredBuffers.hlsl  |   4 +-
 10 files changed, 253 insertions(+), 29 deletions(-)
 create mode 100644 clang/test/AST/HLSL/is_typed_resource_element_compatible_concept.hlsl

diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index cac15b974a276e..400d3334f6f0de 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -289,8 +289,9 @@ struct BuiltinTypeDeclBuilder {
   }
 
   TemplateParameterListBuilder addTemplateArgumentList(Sema &S);
-  BuiltinTypeDeclBuilder &addSimpleTemplateParams(Sema &S,
-                                                  ArrayRef<StringRef> Names);
+  BuiltinTypeDeclBuilder &
+  addSimpleTemplateParams(Sema &S, ArrayRef<StringRef> Names, ConceptDecl *CD);
+  BuiltinTypeDeclBuilder &addConceptSpecializationExpr(Sema &S);
 };
 
 struct TemplateParameterListBuilder {
@@ -312,8 +313,9 @@ struct TemplateParameterListBuilder {
         S.Context, Builder.Record->getDeclContext(), SourceLocation(),
         SourceLocation(), /* TemplateDepth */ 0, Position,
         &S.Context.Idents.get(Name, tok::TokenKind::identifier),
-        /* Typename */ false,
-        /* ParameterPack */ false);
+        /* Typename */ true,
+        /* ParameterPack */ false,
+        /* HasTypeConstraint*/ false);
     if (!DefaultValue.isNull())
       Decl->setDefaultArgument(
           S.Context, S.getTrivialTemplateArgumentLoc(DefaultValue, QualType(),
@@ -323,19 +325,114 @@ struct TemplateParameterListBuilder {
     return *this;
   }
 
-  BuiltinTypeDeclBuilder &finalizeTemplateArgs() {
+  /*
+The concept specialization expression (CSE) constructed in
+constructConceptSpecializationExpr is constructed so that it
+matches the CSE that is constructed when parsing the below C++ code:
+template<typename T>
+concept is_typed_resource_element_compatible = sizeof(T) <= 16;
+template<typename element_type> requires
+is_typed_resource_element_compatible<element_type>
+struct RWBuffer {
+    element_type Val;
+};
+int fn() {
+    RWBuffer<int> Buf;
+}
+When dumping the AST and filtering for "RWBuffer", the resulting AST
+structure is what we're trying to construct below, specifically the
+CSE portion.
+*/
+  ConceptSpecializationExpr *
+  constructConceptSpecializationExpr(Sema &S, ConceptDecl *CD) {
+    ASTContext &Context = S.getASTContext();
+    SourceLocation Loc = Builder.Record->getBeginLoc();
+    DeclarationNameInfo DNI(CD->getDeclName(), Loc);
+    NestedNameSpecifierLoc NNSLoc;
+    DeclContext *DC = Builder.Record->getDeclContext();
+    TemplateArgumentListInfo TALI(Loc, Loc);
+
+    // Assume that the concept decl has just one template parameter
+    // This parameter should have been added when CD was constructed
+    // in getTypedBufferConceptDecl
+    assert(CD->getTemplateParameters()->size() == 1 &&
+           "unexpected concept decl parameter count");
+    TemplateTypeParmDecl *ConceptTTPD = dyn_cast<TemplateTypeParmDecl>(
+        CD->getTemplateParameters()->getParam(0));
+
+    // this TemplateTypeParmDecl is the template for the resource, and is
+    // used to construct a template argumentthat will be used
+    // to construct the ImplicitConceptSpecializationDecl
+    TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create(
+        Context,                          // AST context
+        Builder.Record->getDeclContext(), // DeclContext
+        SourceLocation(), SourceLocation(),
+        /*depth=*/0,                // Depth in the template parameter list
+        /*position=*/0,             // Position in the template parameter list
+        /*id=*/nullptr,             // Identifier for 'T'
+        /*Typename=*/true,          // Indicates this is a 'typename' or 'class'
+        /*ParameterPack=*/false,    // Not a parameter pack
+        /*HasTypeConstraint=*/false // Has no type constraint
+    );
+
+    T->setDeclContext(DC);
+
+    QualType ConceptTType = Context.getTypeDeclType(ConceptTTPD);
+
+    // this is the 2nd template argument node, on which
+    // the concept constraint is actually being applied: 'element_type'
+    TemplateArgument ConceptTA = TemplateArgument(ConceptTType);
+
+    QualType CSETType = Context.getTypeDeclType(T);
+
+    // this is the 1st template argument node, which represents
+    // the abstract type that a concept would refer to: 'T'
+    TemplateArgument CSETA = TemplateArgument(CSETType);
+
+    ImplicitConceptSpecializationDecl *ImplicitCSEDecl =
+        ImplicitConceptSpecializationDecl::Create(
+            Context, Builder.Record->getDeclContext(), Loc, {CSETA});
+
+    // Constraint satisfaction is used to construct the
+    // ConceptSpecailizationExpr, and represents the 2nd Template Argument,
+    // located at the bottom of the sample AST above.
+    const ConstraintSatisfaction CS(CD, {ConceptTA});
+    TemplateArgumentLoc TAL = S.getTrivialTemplateArgumentLoc(
+        ConceptTA, QualType(), SourceLocation());
+
+    TALI.addArgument(TAL);
+    const ASTTemplateArgumentListInfo *ATALI =
+        ASTTemplateArgumentListInfo::Create(Context, TALI);
+
+    // In the concept reference, ATALI is what adds the extra
+    // TemplateArgument node underneath CSE
+    ConceptReference *CR =
+        ConceptReference::Create(Context, NNSLoc, Loc, DNI, CD, CD, ATALI);
+
+    ConceptSpecializationExpr *CSE =
+        ConceptSpecializationExpr::Create(Context, CR, ImplicitCSEDecl, &CS);
+
+    return CSE;
+  }
+
+  BuiltinTypeDeclBuilder &finalizeTemplateArgs(ConceptDecl *CD = nullptr) {
     if (Params.empty())
       return Builder;
+    ConceptSpecializationExpr *CSE =
+        CD ? constructConceptSpecializationExpr(S, CD) : nullptr;
+
     auto *ParamList = TemplateParameterList::Create(S.Context, SourceLocation(),
                                                     SourceLocation(), Params,
-                                                    SourceLocation(), nullptr);
+                                                    SourceLocation(), CSE);
     Builder.Template = ClassTemplateDecl::Create(
         S.Context, Builder.Record->getDeclContext(), SourceLocation(),
         DeclarationName(Builder.Record->getIdentifier()), ParamList,
         Builder.Record);
+
     Builder.Record->setDescribedClassTemplate(Builder.Template);
     Builder.Template->setImplicit(true);
     Builder.Template->setLexicalDeclContext(Builder.Record->getDeclContext());
+
     // NOTE: setPreviousDecl before addDecl so new decl replace old decl when
     // make visible.
     Builder.Template->setPreviousDecl(Builder.PrevTemplate);
@@ -355,13 +452,12 @@ BuiltinTypeDeclBuilder::addTemplateArgumentList(Sema &S) {
   return TemplateParameterListBuilder(S, *this);
 }
 
-BuiltinTypeDeclBuilder &
-BuiltinTypeDeclBuilder::addSimpleTemplateParams(Sema &S,
-                                                ArrayRef<StringRef> Names) {
+BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addSimpleTemplateParams(
+    Sema &S, ArrayRef<StringRef> Names, ConceptDecl *CD = nullptr) {
   TemplateParameterListBuilder Builder = this->addTemplateArgumentList(S);
   for (StringRef Name : Names)
     Builder.addTypeParameter(Name);
-  return Builder.finalizeTemplateArgs();
+  return Builder.finalizeTemplateArgs(CD);
 }
 
 HLSLExternalSemaSource::~HLSLExternalSemaSource() {}
@@ -472,10 +568,102 @@ static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
       .addDefaultHandleConstructor(S);
 }
 
+BinaryOperator *constructSizeOfLEQ16Expr(ASTContext &Context,
+                                         SourceLocation NameLoc,
+                                         TemplateTypeParmDecl *T) {
+  // Obtain the QualType for 'unsigned long'
+  QualType UnsignedLongType = Context.UnsignedLongTy;
+
+  // 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);
+
+  UnaryExprOrTypeTraitExpr *sizeOfExpr = new (Context) UnaryExprOrTypeTraitExpr(
+      UETT_SizeOf, TTypeSourceInfo, UnsignedLongType, NameLoc, NameLoc);
+
+  // Create an IntegerLiteral for the value '16' with size type
+  QualType SizeType = Context.getSizeType();
+  llvm::APInt SizeValue = llvm::APInt(Context.getTypeSize(SizeType), 16);
+  IntegerLiteral *SizeLiteral =
+      new (Context) IntegerLiteral(Context, SizeValue, SizeType, NameLoc);
+
+  QualType BoolTy = Context.BoolTy;
+
+  BinaryOperator *binaryOperator =
+      BinaryOperator::Create(Context, sizeOfExpr, // Left-hand side expression
+                             SizeLiteral,         // Right-hand side expression
+                             BO_LE,               // Binary operator kind (<=)
+                             BoolTy,              // Result type (bool)
+                             VK_LValue,           // Value kind
+                             OK_Ordinary,         // Object kind
+                             NameLoc,             // Source location of operator
+                             FPOptionsOverride());
+
+  return binaryOperator;
+}
+
+Expr *constructTypedBufferConstraintExpr(Sema &S, SourceLocation NameLoc,
+                                         TemplateTypeParmDecl *T) {
+  ASTContext &Context = S.getASTContext();
+
+  // first get the "sizeof(T) <= 16" expression, as a binary operator
+  BinaryOperator *SizeOfLEQ16 = constructSizeOfLEQ16Expr(Context, NameLoc, T);
+  // TODO: add the 'builtin_hlsl_is_typed_resource_element_compatible' builtin
+  // and return a binary operator that evaluates the builtin on the given
+  // template type parameter 'T'.
+  // Defined in issue https://github.com/llvm/llvm-project/issues/113223
+  return SizeOfLEQ16;
+}
+
+ConceptDecl *constructTypedBufferConceptDecl(Sema &S, NamespaceDecl *NSD) {
+  ASTContext &Context = S.getASTContext();
+  DeclContext *DC = NSD->getDeclContext();
+  SourceLocation DeclLoc = SourceLocation();
+
+  IdentifierInfo &ElementTypeII = Context.Idents.get("element_type");
+  TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create(
+      Context, NSD->getDeclContext(), DeclLoc, DeclLoc,
+      /*depth=*/0,
+      /*position=*/0,
+      /*id=*/&ElementTypeII,
+      /*Typename=*/true,
+      /*ParameterPack=*/false);
+
+  T->setDeclContext(DC);
+  T->setReferenced();
+
+  // Create and Attach Template Parameter List to ConceptDecl
+  TemplateParameterList *ConceptParams = TemplateParameterList::Create(
+      Context, DeclLoc, DeclLoc, {T}, DeclLoc, nullptr);
+
+  DeclarationName DeclName = DeclarationName(
+      &Context.Idents.get("__is_typed_resource_element_compatible"));
+  Expr *ConstraintExpr = constructTypedBufferConstraintExpr(S, DeclLoc, T);
+
+  // Create a ConceptDecl
+  ConceptDecl *CD =
+      ConceptDecl::Create(Context, NSD->getDeclContext(), DeclLoc, DeclName,
+                          ConceptParams, ConstraintExpr);
+
+  // Attach the template parameter list to the ConceptDecl
+  CD->setTemplateParameters(ConceptParams);
+
+  // Add the concept declaration to the Translation Unit Decl
+  NSD->getDeclContext()->addDecl(CD);
+
+  return CD;
+}
+
 void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
   CXXRecordDecl *Decl;
+  ConceptDecl *TypedBufferConcept =
+      constructTypedBufferConceptDecl(*SemaPtr, HLSLNamespace);
   Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer")
-             .addSimpleTemplateParams(*SemaPtr, {"element_type"})
+             .addSimpleTemplateParams(*SemaPtr, {"element_type"},
+                                      TypedBufferConcept)
              .Record;
 
   onCompletion(Decl, [this](CXXRecordDecl *Decl) {
diff --git a/clang/test/AST/HLSL/AppendStructuredBuffer-AST.hlsl b/clang/test/AST/HLSL/AppendStructuredBuffer-AST.hlsl
index 5a13ca7735f999..8dbab44024d34d 100644
--- a/clang/test/AST/HLSL/AppendStructuredBuffer-AST.hlsl
+++ b/clang/test/AST/HLSL/AppendStructuredBuffer-AST.hlsl
@@ -12,7 +12,7 @@
 // instantiated specialization.
 
 // EMPTY: ClassTemplateDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit AppendStructuredBuffer
-// EMPTY-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> class depth 0 index 0 element_type
+// EMPTY-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> typename depth 0 index 0 element_type
 // EMPTY-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit <undeserialized declarations> class AppendStructuredBuffer
 // EMPTY-NEXT: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
 
@@ -26,7 +26,7 @@ AppendStructuredBuffer<int> Buffer;
 #endif
 
 // CHECK: ClassTemplateDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit AppendStructuredBuffer
-// CHECK-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> class depth 0 index 0 element_type
+// CHECK-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> typename depth 0 index 0 element_type
 // CHECK-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit class AppendStructuredBuffer definition
 
 // CHECK: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
diff --git a/clang/test/AST/HLSL/ConsumeStructuredBuffer-AST.hlsl b/clang/test/AST/HLSL/ConsumeStructuredBuffer-AST.hlsl
index b75f3fcb959cfc..f27941b539b6a8 100644
--- a/clang/test/AST/HLSL/ConsumeStructuredBuffer-AST.hlsl
+++ b/clang/test/AST/HLSL/ConsumeStructuredBuffer-AST.hlsl
@@ -12,7 +12,7 @@
 // instantiated specialization.
 
 // EMPTY: ClassTemplateDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit ConsumeStructuredBuffer
-// EMPTY-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> class depth 0 index 0 element_type
+// EMPTY-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> typename depth 0 index 0 element_type
 // EMPTY-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit <undeserialized declarations> class ConsumeStructuredBuffer
 // EMPTY-NEXT: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
 
@@ -26,7 +26,7 @@ ConsumeStructuredBuffer<int> Buffer;
 #endif
 
 // CHECK: ClassTemplateDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit ConsumeStructuredBuffer
-// CHECK-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> class depth 0 index 0 element_type
+// CHECK-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> typename depth 0 index 0 element_type
 // CHECK-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit class ConsumeStructuredBuffer definition
 
 // CHECK: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
diff --git a/clang/test/AST/HLSL/RWBuffer-AST.hlsl b/clang/test/AST/HLSL/RWBuffer-AST.hlsl
index ebddd72ddb1e0e..ab8f8d142169f5 100644
--- a/clang/test/AST/HLSL/RWBuffer-AST.hlsl
+++ b/clang/test/AST/HLSL/RWBuffer-AST.hlsl
@@ -11,8 +11,15 @@
 // instantiated specialization.
 
 // EMPTY: ClassTemplateDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit RWBuffer
-// EMPTY-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> class depth 0 index 0 element_type
-// EMPTY-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit <undeserialized declarations> class RWBuffer
+// EMPTY-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> typename depth 0 index 0 element_type
+// EMPTY-NEXT: ConceptSpecializationExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'bool' Concept 0x{{[0-9A-Fa-f]+}} '__is_typed_resource_element_compatible'
+// EMPTY-NEXT: ImplicitConceptSpecializationDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc>
+// EMPTY-NEXT: TemplateArgument type 'type-parameter-0-0'
+// EMPTY-NEXT: TemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'type-parameter-0-0' dependent depth 0 index 0
+// EMPTY-NEXT: TemplateTypeParm 0x{{[0-9A-Fa-f]+}} ''
+// EMPTY-NEXT: TemplateArgument type 'element_type':'type-parameter-0-0'
+// EMPTY-NEXT: TemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'element_type' dependent depth 0 index 0
+// EMPTY-NEXT: TemplateTypeParm 0x{{[0-9A-Fa-f]+}} 'element_type'// EMPTY-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit <undeserialized declarations> class RWBuffer
 // EMPTY-NEXT: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
 
 // There should be no more occurrences of RWBuffer
@@ -25,8 +32,15 @@ RWBuffer<float> Buffer;
 #endif
 
 // CHECK: ClassTemplateDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit RWBuffer
-// CHECK-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> class depth 0 index 0 element_type
-// CHECK-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit class RWBuffer definition
+// CHECK-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> typename depth 0 index 0 element_type
+// CHECK-NEXT: ConceptSpecializationExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'bool' Concept 0x{{[0-9A-Fa-f]+}} '__is_typed_resource_element_compatible'
+// CHECK-NEXT: ImplicitConceptSpecializationDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc>
+// CHECK-NEXT: TemplateArgument type 'type-parameter-0-0'
+// CHECK-NEXT: TemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'type-parameter-0-0' dependent depth 0 index 0
+// CHECK-NEXT: TemplateTypeParm 0x{{[0-9A-Fa-f]+}} ''
+// CHECK-NEXT: TemplateArgument type 'element_type':'type-parameter-0-0'
+// CHECK-NEXT: TemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'element_type' dependent depth 0 index 0
+// CHECK-NEXT: TemplateTypeParm 0x{{[0-9A-Fa-f]+}} 'element_type'// CHECK-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit class RWBuffer definition
 
 // CHECK: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
 // CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit h '__hlsl_resource_t
diff --git a/clang/test/AST/HLSL/RWStructuredBuffer-AST.hlsl b/clang/test/AST/HLSL/RWStructuredBuffer-AST.hlsl
index 4a1e1d7570e5e9..5058eab40b1aeb 100644
--- a/clang/test/AST/HLSL/RWStructuredBuffer-AST.hlsl
+++ b/clang/test/AST/HLSL/RWStructuredBuffer-AST.hlsl
@@ -12,7 +12,7 @@
 // instantiated specialization.
 
 // EMPTY: ClassTemplateDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit RWStructuredBuffer
-// EMPTY-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> class depth 0 index 0 element_type
+// EMPTY-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> typename depth 0 index 0 element_type
 // EMPTY-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit <undeserialized declarations> class RWStructuredBuffer
 // EMPTY-NEXT: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
 
@@ -26,7 +26,7 @@ RWStructuredBuffer<int> Buffer;
 #endif
 
 // CHECK: ClassTemplateDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit RWStructuredBuffer
-// CHECK-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> class depth 0 index 0 element_type
+// CHECK-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> typename depth 0 index 0 element_type
 // CHECK-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit class RWStructuredBuffer definition
 
 // CHECK: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
diff --git a/clang/test/AST/HLSL/RasterizerOrderedStructuredBuffer-AST.hlsl b/clang/test/AST/HLSL/RasterizerOrderedStructuredBuffer-AST.hlsl
index f334e1bb6db3fc..bd05432a09e01c 100644
--- a/clang/test/AST/HLSL/RasterizerOrderedStructuredBuffer-AST.hlsl
+++ b/clang/test/AST/HLSL/RasterizerOrderedStructuredBuffer-AST.hlsl
@@ -12,7 +12,7 @@
 // instantiated specialization.
 
 // EMPTY: ClassTemplateDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit RasterizerOrderedStructuredBuffer
-// EMPTY-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> class depth 0 index 0 element_type
+// EMPTY-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> typename depth 0 index 0 element_type
 // EMPTY-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit <undeserialized declarations> class RasterizerOrderedStructuredBuffer
 // EMPTY-NEXT: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
 
@@ -26,7 +26,7 @@ RasterizerOrderedStructuredBuffer<int> Buffer;
 #endif
 
 // CHECK: ClassTemplateDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit RasterizerOrderedStructuredBuffer
-// CHECK-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> class depth 0 index 0 element_type
+// CHECK-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> typename depth 0 index 0 element_type
 // CHECK-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit class RasterizerOrderedStructuredBuffer definition
 
 // CHECK: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
diff --git a/clang/test/AST/HLSL/StructuredBuffer-AST.hlsl b/clang/test/AST/HLSL/StructuredBuffer-AST.hlsl
index 521c3d45b20225..081ab1355caaac 100644
--- a/clang/test/AST/HLSL/StructuredBuffer-AST.hlsl
+++ b/clang/test/AST/HLSL/StructuredBuffer-AST.hlsl
@@ -12,7 +12,7 @@
 // instantiated specialization.
 
 // EMPTY: ClassTemplateDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit StructuredBuffer
-// EMPTY-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> class depth 0 index 0 element_type
+// EMPTY-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> typename depth 0 index 0 element_type
 // EMPTY-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit <undeserialized declarations> class StructuredBuffer
 // EMPTY-NEXT: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
 
@@ -26,7 +26,7 @@ StructuredBuffer<float> Buffer;
 #endif
 
 // CHECK: ClassTemplateDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit StructuredBuffer
-// CHECK-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> class depth 0 index 0 element_type
+// CHECK-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> typename depth 0 index 0 element_type
 // CHECK-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit class StructuredBuffer definition
 
 // CHECK: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
diff --git a/clang/test/AST/HLSL/is_typed_resource_element_compatible_concept.hlsl b/clang/test/AST/HLSL/is_typed_resource_element_compatible_concept.hlsl
new file mode 100644
index 00000000000000..414ed6eb821246
--- /dev/null
+++ b/clang/test/AST/HLSL/is_typed_resource_element_compatible_concept.hlsl
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -ast-dump -ast-dump-filter=__is_typed_resource_element_compatible %s | FileCheck %s
+
+// CHECK: ConceptDecl 0x{{[0-9a-f]+}} <<invalid sloc>> <invalid sloc> __is_typed_resource_element_compatible
+// CHECK: |-TemplateTypeParmDecl 0x{{[0-9a-f]+}} <<invalid sloc>> <invalid sloc> referenced typename depth 0 index 0 element_type
+// CHECK: `-BinaryOperator 0x{{[0-9a-f]+}} <<invalid sloc>> 'bool' lvalue '<='
+// CHECK:   |-UnaryExprOrTypeTraitExpr 0x{{[0-9a-f]+}} <<invalid sloc>> 'unsigned long' sizeof 'element_type'
+// CHECK:   `-IntegerLiteral 0x{{[0-9a-f]+}} <<invalid sloc>> 'unsigned long' 16
+
+
+RWBuffer<float> Buffer;
diff --git a/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl b/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
index 76b5d01b8036eb..438f8021f96a95 100644
--- a/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
@@ -5,13 +5,25 @@ typedef vector<float, 3> float3;
 RWBuffer<float3> Buffer;
 
 // expected-error at +2 {{class template 'RWBuffer' requires template arguments}}
-// expected-note@*:* {{template declaration from hidden source: template <class element_type> class RWBuffer}}
+// expected-note@*:* {{template declaration from hidden source: template <typename element_type> requires __is_typed_resource_element_compatible<element_type> class RWBuffer {}}}
 RWBuffer BufferErr1;
 
 // expected-error at +2 {{too few template arguments for class template 'RWBuffer'}}
-// expected-note@*:* {{template declaration from hidden source: template <class element_type> class RWBuffer}}
+// expected-note@*:* {{template declaration from hidden source: template <typename element_type> requires __is_typed_resource_element_compatible<element_type> class RWBuffer {}}}
 RWBuffer<> BufferErr2;
 
+struct threeDoubles {
+  double a;
+  double b;
+  double c;
+};
+
+// expected-error at +3 {{constraints not satisfied for class template 'RWBuffer'}}
+// expected-note@*:* {{because 'threeDoubles' does not satisfy '__is_typed_resource_element_compatible'}}
+// expected-note@*:* {{because 'sizeof(threeDoubles) <= 16UL' (24 <= 16) evaluated to false}}
+RWBuffer<threeDoubles> BufferErr3;
+
+
 [numthreads(1,1,1)]
 void main() {
   (void)Buffer.h; // expected-error {{'h' is a private member of 'hlsl::RWBuffer<vector<float, 3>>'}}
diff --git a/clang/test/SemaHLSL/BuiltIns/StructuredBuffers.hlsl b/clang/test/SemaHLSL/BuiltIns/StructuredBuffers.hlsl
index a472d5519dc51f..552624f13ee5f8 100644
--- a/clang/test/SemaHLSL/BuiltIns/StructuredBuffers.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/StructuredBuffers.hlsl
@@ -5,11 +5,11 @@ typedef vector<float, 3> float3;
 StructuredBuffer<float3> Buffer;
 
 // expected-error at +2 {{class template 'StructuredBuffer' requires template arguments}}
-// expected-note@*:* {{template declaration from hidden source: template <class element_type> class StructuredBuffer}}
+// expected-note@*:* {{template declaration from hidden source: template <typename element_type> class StructuredBuffer {}}}
 StructuredBuffer BufferErr1;
 
 // expected-error at +2 {{too few template arguments for class template 'StructuredBuffer'}}
-// expected-note@*:* {{template declaration from hidden source: template <class element_type> class StructuredBuffer}}
+// expected-note@*:* {{template declaration from hidden source: template <typename element_type> class StructuredBuffer {}}}
 StructuredBuffer<> BufferErr2;
 
 [numthreads(1,1,1)]

>From f369fb5318a51db23595748cfd9b081afa874fb4 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Fri, 15 Nov 2024 09:35:59 -0800
Subject: [PATCH 2/4] fix test

---
 clang/test/AST/HLSL/RWBuffer-AST.hlsl | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/clang/test/AST/HLSL/RWBuffer-AST.hlsl b/clang/test/AST/HLSL/RWBuffer-AST.hlsl
index ab8f8d142169f5..e3060a2dfa9677 100644
--- a/clang/test/AST/HLSL/RWBuffer-AST.hlsl
+++ b/clang/test/AST/HLSL/RWBuffer-AST.hlsl
@@ -19,7 +19,8 @@
 // EMPTY-NEXT: TemplateTypeParm 0x{{[0-9A-Fa-f]+}} ''
 // EMPTY-NEXT: TemplateArgument type 'element_type':'type-parameter-0-0'
 // EMPTY-NEXT: TemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'element_type' dependent depth 0 index 0
-// EMPTY-NEXT: TemplateTypeParm 0x{{[0-9A-Fa-f]+}} 'element_type'// EMPTY-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit <undeserialized declarations> class RWBuffer
+// EMPTY-NEXT: TemplateTypeParm 0x{{[0-9A-Fa-f]+}} 'element_type'
+// EMPTY-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit <undeserialized declarations> class RWBuffer
 // EMPTY-NEXT: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
 
 // There should be no more occurrences of RWBuffer
@@ -40,7 +41,8 @@ RWBuffer<float> Buffer;
 // CHECK-NEXT: TemplateTypeParm 0x{{[0-9A-Fa-f]+}} ''
 // CHECK-NEXT: TemplateArgument type 'element_type':'type-parameter-0-0'
 // CHECK-NEXT: TemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'element_type' dependent depth 0 index 0
-// CHECK-NEXT: TemplateTypeParm 0x{{[0-9A-Fa-f]+}} 'element_type'// CHECK-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit class RWBuffer definition
+// CHECK-NEXT: TemplateTypeParm 0x{{[0-9A-Fa-f]+}} 'element_type'
+// CHECK-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit class RWBuffer definition
 
 // CHECK: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
 // CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit h '__hlsl_resource_t

>From 8d173d3697633bc20487f21dc835c3ac3c0a40db Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 18 Nov 2024 14:33:47 -0800
Subject: [PATCH 3/4] use // instead of /*..*/ for comment block

---
 clang/lib/Sema/HLSLExternalSemaSource.cpp | 36 +++++++++++------------
 1 file changed, 18 insertions(+), 18 deletions(-)

diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index 400d3334f6f0de..6c93afdfaa819b 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -325,24 +325,24 @@ struct TemplateParameterListBuilder {
     return *this;
   }
 
-  /*
-The concept specialization expression (CSE) constructed in
-constructConceptSpecializationExpr is constructed so that it
-matches the CSE that is constructed when parsing the below C++ code:
-template<typename T>
-concept is_typed_resource_element_compatible = sizeof(T) <= 16;
-template<typename element_type> requires
-is_typed_resource_element_compatible<element_type>
-struct RWBuffer {
-    element_type Val;
-};
-int fn() {
-    RWBuffer<int> Buf;
-}
-When dumping the AST and filtering for "RWBuffer", the resulting AST
-structure is what we're trying to construct below, specifically the
-CSE portion.
-*/
+  // The concept specialization expression (CSE) constructed in
+  // constructConceptSpecializationExpr is constructed so that it
+  // matches the CSE that is constructed when parsing the below C++ code:
+  //
+  // template<typename T>
+  // concept is_typed_resource_element_compatible = sizeof(T) <= 16;
+  // template<typename element_type> requires
+  // is_typed_resource_element_compatible<element_type>
+  // struct RWBuffer {
+  //     element_type Val;
+  // };
+  // int fn() {
+  //     RWBuffer<int> Buf;
+  // }
+  //
+  // When dumping the AST and filtering for "RWBuffer", the resulting AST
+  // structure is what we're trying to construct below, specifically the
+  // CSE portion.
   ConceptSpecializationExpr *
   constructConceptSpecializationExpr(Sema &S, ConceptDecl *CD) {
     ASTContext &Context = S.getASTContext();

>From 7c58efd1a9756dbde6e158ace0d01374cab04039 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Wed, 20 Nov 2024 15:22:28 -0800
Subject: [PATCH 4/4] add tests for rwbuffer concept validation

---
 "asa1qqqq\357\200\233"                        | 217 ++++++++++++++++++
 clang/lib/Sema/HLSLExternalSemaSource.cpp     |  66 +++---
 ...d_resource_element_compatible_concept.hlsl |  12 +-
 clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl   |  99 +++++++-
 ...sTypedResourceElementCompatibleErrors.hlsl |   3 +-
 5 files changed, 348 insertions(+), 49 deletions(-)
 create mode 100644 "asa1qqqq\357\200\233"

diff --git "a/asa1qqqq\357\200\233" "b/asa1qqqq\357\200\233"
new file mode 100644
index 00000000000000..7f93726bb4a45f
--- /dev/null
+++ "b/asa1qqqq\357\200\233"
@@ -0,0 +1,217 @@
+diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
+index 6c93afdfaa81..e36bfeab7b49 100644
+--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
++++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
+@@ -330,7 +330,7 @@ struct TemplateParameterListBuilder {
+   // matches the CSE that is constructed when parsing the below C++ code:
+   //
+   // template<typename T>
+-  // concept is_typed_resource_element_compatible = sizeof(T) <= 16;
++  // concept is_typed_resource_element_compatible = __builtin_hlsl_typed_resource_element_compatible<T> && !__builtin_hlsl_is_intangible<T>
+   // template<typename element_type> requires
+   // is_typed_resource_element_compatible<element_type>
+   // struct RWBuffer {
+@@ -568,11 +568,11 @@ static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
+       .addDefaultHandleConstructor(S);
+ }
+ 
+-BinaryOperator *constructSizeOfLEQ16Expr(ASTContext &Context,
++BinaryOperator *constructTypedResourceConstraintExpr(ASTContext &Context,
+                                          SourceLocation NameLoc,
+                                          TemplateTypeParmDecl *T) {
+   // Obtain the QualType for 'unsigned long'
+-  QualType UnsignedLongType = Context.UnsignedLongTy;
++  QualType BoolTy = Context.BoolTy;
+ 
+   // Create a QualType that points to this TemplateTypeParmDecl
+   QualType TType = Context.getTypeDeclType(T);
+@@ -581,21 +581,22 @@ BinaryOperator *constructSizeOfLEQ16Expr(ASTContext &Context,
+   TypeSourceInfo *TTypeSourceInfo =
+       Context.getTrivialTypeSourceInfo(TType, NameLoc);
+ 
+-  UnaryExprOrTypeTraitExpr *sizeOfExpr = new (Context) UnaryExprOrTypeTraitExpr(
+-      UETT_SizeOf, TTypeSourceInfo, UnsignedLongType, NameLoc, NameLoc);
++  TypeTraitExpr *TypedResExpr = TypeTraitExpr::Create(
++      Context, BoolTy, NameLoc, UTT_IsTypedResourceElementCompatible,
++      {TTypeSourceInfo}, NameLoc, true);
+ 
+-  // Create an IntegerLiteral for the value '16' with size type
+-  QualType SizeType = Context.getSizeType();
+-  llvm::APInt SizeValue = llvm::APInt(Context.getTypeSize(SizeType), 16);
+-  IntegerLiteral *SizeLiteral =
+-      new (Context) IntegerLiteral(Context, SizeValue, SizeType, NameLoc);
++  TypeTraitExpr *IsIntangibleExpr =
++      TypeTraitExpr::Create(Context, BoolTy, NameLoc, UTT_IsIntangibleType,
++      {TTypeSourceInfo}, NameLoc, true);
+ 
+-  QualType BoolTy = Context.BoolTy;
++  UnaryOperator *NotIntangibleExpr = UnaryOperator::Create(
++      Context, IsIntangibleExpr, UO_Not, BoolTy, VK_LValue, OK_Ordinary,
++      NameLoc, false, FPOptionsOverride());
+ 
+   BinaryOperator *binaryOperator =
+-      BinaryOperator::Create(Context, sizeOfExpr, // Left-hand side expression
+-                             SizeLiteral,         // Right-hand side expression
+-                             BO_LE,               // Binary operator kind (<=)
++      BinaryOperator::Create(Context, TypedResExpr, // Left-hand side expression
++                             NotIntangibleExpr,   // Right-hand side expression
++                             BO_LAnd,             // Binary operator kind (&&)
+                              BoolTy,              // Result type (bool)
+                              VK_LValue,           // Value kind
+                              OK_Ordinary,         // Object kind
+@@ -609,13 +610,10 @@ Expr *constructTypedBufferConstraintExpr(Sema &S, SourceLocation NameLoc,
+                                          TemplateTypeParmDecl *T) {
+   ASTContext &Context = S.getASTContext();
+ 
+-  // first get the "sizeof(T) <= 16" expression, as a binary operator
+-  BinaryOperator *SizeOfLEQ16 = constructSizeOfLEQ16Expr(Context, NameLoc, T);
+-  // TODO: add the 'builtin_hlsl_is_typed_resource_element_compatible' builtin
+-  // and return a binary operator that evaluates the builtin on the given
+-  // template type parameter 'T'.
+-  // Defined in issue https://github.com/llvm/llvm-project/issues/113223
+-  return SizeOfLEQ16;
++  BinaryOperator *TypedResourceConstraintExpr =
++      constructTypedResourceConstraintExpr(Context, NameLoc, T);
++
++  return TypedResourceConstraintExpr;
+ }
+ 
+ ConceptDecl *constructTypedBufferConceptDecl(Sema &S, NamespaceDecl *NSD) {
+diff --git a/clang/test/AST/HLSL/is_typed_resource_element_compatible_concept.hlsl b/clang/test/AST/HLSL/is_typed_resource_element_compatible_concept.hlsl
+index 414ed6eb8212..362ce96e808f 100644
+--- a/clang/test/AST/HLSL/is_typed_resource_element_compatible_concept.hlsl
++++ b/clang/test/AST/HLSL/is_typed_resource_element_compatible_concept.hlsl
+@@ -2,9 +2,13 @@
+ 
+ // CHECK: ConceptDecl 0x{{[0-9a-f]+}} <<invalid sloc>> <invalid sloc> __is_typed_resource_element_compatible
+ // CHECK: |-TemplateTypeParmDecl 0x{{[0-9a-f]+}} <<invalid sloc>> <invalid sloc> referenced typename depth 0 index 0 element_type
+-// CHECK: `-BinaryOperator 0x{{[0-9a-f]+}} <<invalid sloc>> 'bool' lvalue '<='
+-// CHECK:   |-UnaryExprOrTypeTraitExpr 0x{{[0-9a-f]+}} <<invalid sloc>> 'unsigned long' sizeof 'element_type'
+-// CHECK:   `-IntegerLiteral 0x{{[0-9a-f]+}} <<invalid sloc>> 'unsigned long' 16
+-
++// CHECK: `-BinaryOperator 0x{{[0-9a-f]+}} <<invalid sloc>> 'bool' lvalue '&&'

++// CHECK:   |-TypeTraitExpr 0x{{[0-9a-f]+}} <<invalid sloc>> 'bool' __builtin_hlsl_is_typed_resource_element_compatible

++// CHECK:   `-TemplateTypeParmType 0x{{[0-9a-f]+}} 'element_type' dependent depth 0 index 0

++// CHECK:     `-TemplateTypeParm 0x{{[0-9a-f]+}} 'element_type'

++// CHECK:  `-UnaryOperator 0x{{[0-9a-f]+}} <<invalid sloc>> 'bool' lvalue prefix '~' cannot overflow

++// CHECK:    `-TypeTraitExpr 0x{{[0-9a-f]+}} <<invalid sloc>> 'bool' __builtin_hlsl_is_intangible

++// CHECK:      `-TemplateTypeParmType 0x{{[0-9a-f]+}} 'element_type' dependent depth 0 index 0

++// CHECK:        `-TemplateTypeParm 0x{{[0-9a-f]+}} 'element_type'

+ 
+ RWBuffer<float> Buffer;
+diff --git a/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl b/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
+index 438f8021f96a..8d018bacea7f 100644
+--- a/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
++++ b/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
+@@ -1,16 +1,79 @@
+ // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -fsyntax-only -verify %s
+ 
+ typedef vector<float, 3> float3;
++typedef vector<double, 2> double2;
++typedef vector<double, 3> double3;
+ 
+-RWBuffer<float3> Buffer;
+ 
+-// expected-error at +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 BufferErr1;
++// expected-error at +4 {{constraints not satisfied for class template 'RWBuffer'}}
++// expected-note@*:* {{template declaration from hidden source: template <typename element_type> requires __is_typed_resource_element_compatible<element_type> class RWBuffer}}
++// expected-note@*:* {{because 'hlsl::RWBuffer<int>' does not satisfy '__is_typed_resource_element_compatible'}}
++// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(hlsl::RWBuffer<int>)' evaluated to false}}
++RWBuffer<RWBuffer<int> > r5;
++
++struct s {
++    int x;
++};
++
++struct Empty {};
++
++template<typename T> struct TemplatedBuffer {
++    T a;
++};
++
++template<typename T> struct TemplatedVector {
++    vector<T, 4> v;
++};
++
++
++// expected-error at +4 {{constraints not satisfied for class template 'RWBuffer'}}
++// expected-note@*:* {{template declaration from hidden source: template <typename element_type> requires __is_typed_resource_element_compatible<element_type> class RWBuffer}}
++// expected-note@*:* {{because 'TemplatedBuffer<int>' does not satisfy '__is_typed_resource_element_compatible'}}
++// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(TemplatedBuffer<int>)' evaluated to false}}
++RWBuffer<TemplatedBuffer<int> > r8;
++// expected-error at +4 {{constraints not satisfied for class template 'RWBuffer'}}
++// expected-note@*:* {{template declaration from hidden source: template <typename element_type> requires __is_typed_resource_element_compatible<element_type> class RWBuffer}}
++// expected-note@*:* {{because 'TemplatedVector<int>' does not satisfy '__is_typed_resource_element_compatible'}}
++// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(TemplatedVector<int>)' evaluated to false}}
++RWBuffer<TemplatedVector<int> > r9;
++
++// arrays not allowed
++// expected-error at +3 {{constraints not satisfied for class template 'RWBuffer'}}
++// expected-note@*:* {{because 'half[4]' does not satisfy '__is_typed_resource_element_compatible'}}
++// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(__fp16[4])' evaluated to false}}
++RWBuffer<half[4]> r10;
++
++typedef vector<int, 8> int8;
++// expected-error at +3 {{constraints not satisfied for class template 'RWBuffer'}}
++// expected-note@*:* {{because 'vector<int, 8>' (vector of 8 'int' values) does not satisfy '__is_typed_resource_element_compatible'}}
++// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(int __attribute__((ext_vector_type(8))))' evaluated to false}}
++RWBuffer<int8> r11;
++
++typedef int MyInt;
++RWBuffer<MyInt> r12;
++
++// expected-error at +3 {{constraints not satisfied for class template 'RWBuffer'}}
++// expected-note@*:* {{because 'bool' does not satisfy '__is_typed_resource_element_compatible'}}
++// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(_Bool)' evaluated to false}}
++RWBuffer<bool> r13;
++
++// expected-error at +3 {{constraints not satisfied for class template 'RWBuffer'}}
++// expected-note@*:* {{because 'vector<bool, 2>' (vector of 2 'bool' values) does not satisfy '__is_typed_resource_element_compatible'}}
++// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(_Bool __attribute__((ext_vector_type(2))))' evaluated to false}}
++RWBuffer<vector<bool, 2>> r14;
++
++enum numbers { one, two, three };
++
++// expected-error at +3 {{constraints not satisfied for class template 'RWBuffer'}}
++// expected-note@*:* {{because 'numbers' does not satisfy '__is_typed_resource_element_compatible'}}
++// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(numbers)' evaluated to false}}
++RWBuffer<numbers> r15;
++
++// expected-error at +3 {{constraints not satisfied for class template 'RWBuffer'}}
++// expected-note@*:* {{because 'vector<double, 3>' (vector of 3 'double' values) does not satisfy '__is_typed_resource_element_compatible'}}
++// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(double __attribute__((ext_vector_type(3))))' evaluated to false}}
++RWBuffer<double3> r16;
+ 
+-// expected-error at +2 {{too few template arguments for class template 'RWBuffer'}}
+-// expected-note@*:* {{template declaration from hidden source: template <typename element_type> requires __is_typed_resource_element_compatible<element_type> class RWBuffer {}}}
+-RWBuffer<> BufferErr2;
+ 
+ struct threeDoubles {
+   double a;
+@@ -20,7 +83,7 @@ struct threeDoubles {
+ 
+ // expected-error at +3 {{constraints not satisfied for class template 'RWBuffer'}}
+ // expected-note@*:* {{because 'threeDoubles' does not satisfy '__is_typed_resource_element_compatible'}}
+-// expected-note@*:* {{because 'sizeof(threeDoubles) <= 16UL' (24 <= 16) evaluated to false}}
++// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(threeDoubles)' evaluated to false}}
+ RWBuffer<threeDoubles> BufferErr3;
+ 
+ 
+diff --git a/clang/test/SemaHLSL/Types/Traits/IsTypedResourceElementCompatibleErrors.hlsl b/clang/test/SemaHLSL/Types/Traits/IsTypedResourceElementCompatibleErrors.hlsl
+index d3d79aa0499e..cb3e9ae7a615 100644
+--- a/clang/test/SemaHLSL/Types/Traits/IsTypedResourceElementCompatibleErrors.hlsl
++++ b/clang/test/SemaHLSL/Types/Traits/IsTypedResourceElementCompatibleErrors.hlsl
+@@ -1,9 +1,10 @@
+ // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -fnative-half-type -verify %s
+ 
+ // types must be complete
+-_Static_assert(!__builtin_hlsl_is_typed_resource_element_compatible(__hlsl_resource_t), "");
++_Static_assert(__builtin_hlsl_is_typed_resource_element_compatible(__hlsl_resource_t), "");

+ 
+ // expected-note at +1{{forward declaration of 'notComplete'}}
+ struct notComplete;
+ // expected-error at +1{{incomplete type 'notComplete' where a complete type is required}}
+ _Static_assert(!__builtin_hlsl_is_typed_resource_element_compatible(notComplete), "");
++ 
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index 6c93afdfaa819b..a2f6e46c4a112b 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -330,8 +330,9 @@ struct TemplateParameterListBuilder {
   // matches the CSE that is constructed when parsing the below C++ code:
   //
   // template<typename T>
-  // concept is_typed_resource_element_compatible = sizeof(T) <= 16;
-  // template<typename element_type> requires
+  // concept is_typed_resource_element_compatible =
+  // __builtin_hlsl_typed_resource_element_compatible<T> &&
+  // !__builtin_hlsl_is_intangible<T> template<typename element_type> requires
   // is_typed_resource_element_compatible<element_type>
   // struct RWBuffer {
   //     element_type Val;
@@ -568,11 +569,12 @@ static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
       .addDefaultHandleConstructor(S);
 }
 
-BinaryOperator *constructSizeOfLEQ16Expr(ASTContext &Context,
-                                         SourceLocation NameLoc,
+Expr *constructTypedBufferConstraintExpr(Sema &S, SourceLocation NameLoc,
                                          TemplateTypeParmDecl *T) {
+  ASTContext &Context = S.getASTContext();
+
   // Obtain the QualType for 'unsigned long'
-  QualType UnsignedLongType = Context.UnsignedLongTy;
+  QualType BoolTy = Context.BoolTy;
 
   // Create a QualType that points to this TemplateTypeParmDecl
   QualType TType = Context.getTypeDeclType(T);
@@ -581,41 +583,29 @@ BinaryOperator *constructSizeOfLEQ16Expr(ASTContext &Context,
   TypeSourceInfo *TTypeSourceInfo =
       Context.getTrivialTypeSourceInfo(TType, NameLoc);
 
-  UnaryExprOrTypeTraitExpr *sizeOfExpr = new (Context) UnaryExprOrTypeTraitExpr(
-      UETT_SizeOf, TTypeSourceInfo, UnsignedLongType, NameLoc, NameLoc);
-
-  // Create an IntegerLiteral for the value '16' with size type
-  QualType SizeType = Context.getSizeType();
-  llvm::APInt SizeValue = llvm::APInt(Context.getTypeSize(SizeType), 16);
-  IntegerLiteral *SizeLiteral =
-      new (Context) IntegerLiteral(Context, SizeValue, SizeType, NameLoc);
-
-  QualType BoolTy = Context.BoolTy;
-
-  BinaryOperator *binaryOperator =
-      BinaryOperator::Create(Context, sizeOfExpr, // Left-hand side expression
-                             SizeLiteral,         // Right-hand side expression
-                             BO_LE,               // Binary operator kind (<=)
-                             BoolTy,              // Result type (bool)
-                             VK_LValue,           // Value kind
-                             OK_Ordinary,         // Object kind
-                             NameLoc,             // Source location of operator
+  TypeTraitExpr *TypedResExpr = TypeTraitExpr::Create(
+      Context, BoolTy, NameLoc, UTT_IsTypedResourceElementCompatible,
+      {TTypeSourceInfo}, NameLoc, true);
+
+  TypeTraitExpr *IsIntangibleExpr =
+      TypeTraitExpr::Create(Context, BoolTy, NameLoc, UTT_IsIntangibleType,
+                            {TTypeSourceInfo}, NameLoc, true);
+
+  UnaryOperator *NotIntangibleExpr = UnaryOperator::Create(
+      Context, IsIntangibleExpr, UO_Not, BoolTy, VK_LValue, OK_Ordinary,
+      NameLoc, false, FPOptionsOverride());
+
+  BinaryOperator *TypedResourceConstraintExpr =
+      BinaryOperator::Create(Context, TypedResExpr, // Left-hand side expression
+                             NotIntangibleExpr, // Right-hand side expression
+                             BO_LAnd,           // Binary operator kind (&&)
+                             BoolTy,            // Result type (bool)
+                             VK_LValue,         // Value kind
+                             OK_Ordinary,       // Object kind
+                             NameLoc,           // Source location of operator
                              FPOptionsOverride());
 
-  return binaryOperator;
-}
-
-Expr *constructTypedBufferConstraintExpr(Sema &S, SourceLocation NameLoc,
-                                         TemplateTypeParmDecl *T) {
-  ASTContext &Context = S.getASTContext();
-
-  // first get the "sizeof(T) <= 16" expression, as a binary operator
-  BinaryOperator *SizeOfLEQ16 = constructSizeOfLEQ16Expr(Context, NameLoc, T);
-  // TODO: add the 'builtin_hlsl_is_typed_resource_element_compatible' builtin
-  // and return a binary operator that evaluates the builtin on the given
-  // template type parameter 'T'.
-  // Defined in issue https://github.com/llvm/llvm-project/issues/113223
-  return SizeOfLEQ16;
+  return TypedResourceConstraintExpr;
 }
 
 ConceptDecl *constructTypedBufferConceptDecl(Sema &S, NamespaceDecl *NSD) {
diff --git a/clang/test/AST/HLSL/is_typed_resource_element_compatible_concept.hlsl b/clang/test/AST/HLSL/is_typed_resource_element_compatible_concept.hlsl
index 414ed6eb821246..362ce96e808f62 100644
--- a/clang/test/AST/HLSL/is_typed_resource_element_compatible_concept.hlsl
+++ b/clang/test/AST/HLSL/is_typed_resource_element_compatible_concept.hlsl
@@ -2,9 +2,13 @@
 
 // CHECK: ConceptDecl 0x{{[0-9a-f]+}} <<invalid sloc>> <invalid sloc> __is_typed_resource_element_compatible
 // CHECK: |-TemplateTypeParmDecl 0x{{[0-9a-f]+}} <<invalid sloc>> <invalid sloc> referenced typename depth 0 index 0 element_type
-// CHECK: `-BinaryOperator 0x{{[0-9a-f]+}} <<invalid sloc>> 'bool' lvalue '<='
-// CHECK:   |-UnaryExprOrTypeTraitExpr 0x{{[0-9a-f]+}} <<invalid sloc>> 'unsigned long' sizeof 'element_type'
-// CHECK:   `-IntegerLiteral 0x{{[0-9a-f]+}} <<invalid sloc>> 'unsigned long' 16
-
+// CHECK: `-BinaryOperator 0x{{[0-9a-f]+}} <<invalid sloc>> 'bool' lvalue '&&'
+// CHECK:   |-TypeTraitExpr 0x{{[0-9a-f]+}} <<invalid sloc>> 'bool' __builtin_hlsl_is_typed_resource_element_compatible
+// CHECK:   `-TemplateTypeParmType 0x{{[0-9a-f]+}} 'element_type' dependent depth 0 index 0
+// CHECK:     `-TemplateTypeParm 0x{{[0-9a-f]+}} 'element_type'
+// CHECK:  `-UnaryOperator 0x{{[0-9a-f]+}} <<invalid sloc>> 'bool' lvalue prefix '~' cannot overflow
+// CHECK:    `-TypeTraitExpr 0x{{[0-9a-f]+}} <<invalid sloc>> 'bool' __builtin_hlsl_is_intangible
+// CHECK:      `-TemplateTypeParmType 0x{{[0-9a-f]+}} 'element_type' dependent depth 0 index 0
+// CHECK:        `-TemplateTypeParm 0x{{[0-9a-f]+}} 'element_type'
 
 RWBuffer<float> Buffer;
diff --git a/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl b/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
index 438f8021f96a95..15f5fae347411e 100644
--- a/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
@@ -1,17 +1,104 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -fsyntax-only -verify %s
 
 typedef vector<float, 3> float3;
+typedef vector<double, 2> double2;
+typedef vector<double, 3> double3;
 
-RWBuffer<float3> Buffer;
 
-// expected-error at +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 {}}}
+// expected-error at +1 {{class template 'RWBuffer' requires template arguments}}
 RWBuffer BufferErr1;
 
-// expected-error at +2 {{too few template arguments for class template 'RWBuffer'}}
-// expected-note@*:* {{template declaration from hidden source: template <typename element_type> requires __is_typed_resource_element_compatible<element_type> class RWBuffer {}}}
+// expected-error at +1 {{too few template arguments for class template 'RWBuffer'}}
 RWBuffer<> BufferErr2;
 
+// test implicit RWBuffer concept
+RWBuffer<int> r1;
+RWBuffer<float> r2;
+RWBuffer<float3> Buffer;
+RWBuffer<double2> r4;
+
+// expected-error at +4 {{constraints not satisfied for class template 'RWBuffer'}}
+// expected-note@*:* {{template declaration from hidden source: template <typename element_type> requires __is_typed_resource_element_compatible<element_type> class RWBuffer}}
+// expected-note@*:* {{because 'hlsl::RWBuffer<int>' does not satisfy '__is_typed_resource_element_compatible'}}
+// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(hlsl::RWBuffer<int>)' evaluated to false}}
+RWBuffer<RWBuffer<int> > r5;
+
+struct s {
+    int x;
+};
+
+struct Empty {};
+
+template<typename T> struct TemplatedBuffer {
+    T a;
+};
+
+template<typename T> struct TemplatedVector {
+    vector<T, 4> v;
+};
+
+// structs not allowed
+// expected-error at +4 {{constraints not satisfied for class template 'RWBuffer'}}
+// expected-note@*:* {{template declaration from hidden source: template <typename element_type> requires __is_typed_resource_element_compatible<element_type> class RWBuffer}}
+// expected-note@*:* {{because 's' does not satisfy '__is_typed_resource_element_compatible'}}
+// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(s)' evaluated to false}}
+RWBuffer<s> r6;
+// expected-error at +3 {{constraints not satisfied for class template 'RWBuffer'}}
+// expected-note@*:* {{because 'Empty' does not satisfy '__is_typed_resource_element_compatible'}}
+// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(Empty)' evaluated to false}}
+RWBuffer<Empty> r7;
+
+/* TODO: fix issue with incomplete type
+// error at +4 {{constraints not satisfied for class template 'RWBuffer'}}
+// note@*:* {{template declaration from hidden source: template <typename element_type> requires __is_typed_resource_element_compatible<element_type> class RWBuffer}}
+// note@*:* {{because 'TemplatedBuffer<int>' does not satisfy '__is_typed_resource_element_compatible'}}
+// note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(TemplatedBuffer<int>)' evaluated to false}}
+RWBuffer<TemplatedBuffer<int> > r8;
+// error at +4 {{constraints not satisfied for class template 'RWBuffer'}}
+// note@*:* {{template declaration from hidden source: template <typename element_type> requires __is_typed_resource_element_compatible<element_type> class RWBuffer}}
+// note@*:* {{because 'TemplatedVector<int>' does not satisfy '__is_typed_resource_element_compatible'}}
+// note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(TemplatedVector<int>)' evaluated to false}}
+RWBuffer<TemplatedVector<int> > r9;
+*/
+
+// arrays not allowed
+// expected-error at +3 {{constraints not satisfied for class template 'RWBuffer'}}
+// expected-note@*:* {{because 'half[4]' does not satisfy '__is_typed_resource_element_compatible'}}
+// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(__fp16[4])' evaluated to false}}
+RWBuffer<half[4]> r10;
+
+typedef vector<int, 8> int8;
+// expected-error at +3 {{constraints not satisfied for class template 'RWBuffer'}}
+// expected-note@*:* {{because 'vector<int, 8>' (vector of 8 'int' values) does not satisfy '__is_typed_resource_element_compatible'}}
+// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(int __attribute__((ext_vector_type(8))))' evaluated to false}}
+RWBuffer<int8> r11;
+
+typedef int MyInt;
+RWBuffer<MyInt> r12;
+
+// expected-error at +3 {{constraints not satisfied for class template 'RWBuffer'}}
+// expected-note@*:* {{because 'bool' does not satisfy '__is_typed_resource_element_compatible'}}
+// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(_Bool)' evaluated to false}}
+RWBuffer<bool> r13;
+
+// expected-error at +3 {{constraints not satisfied for class template 'RWBuffer'}}
+// expected-note@*:* {{because 'vector<bool, 2>' (vector of 2 'bool' values) does not satisfy '__is_typed_resource_element_compatible'}}
+// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(_Bool __attribute__((ext_vector_type(2))))' evaluated to false}}
+RWBuffer<vector<bool, 2>> r14;
+
+enum numbers { one, two, three };
+
+// expected-error at +3 {{constraints not satisfied for class template 'RWBuffer'}}
+// expected-note@*:* {{because 'numbers' does not satisfy '__is_typed_resource_element_compatible'}}
+// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(numbers)' evaluated to false}}
+RWBuffer<numbers> r15;
+
+// expected-error at +3 {{constraints not satisfied for class template 'RWBuffer'}}
+// expected-note@*:* {{because 'vector<double, 3>' (vector of 3 'double' values) does not satisfy '__is_typed_resource_element_compatible'}}
+// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(double __attribute__((ext_vector_type(3))))' evaluated to false}}
+RWBuffer<double3> r16;
+
+
 struct threeDoubles {
   double a;
   double b;
@@ -20,7 +107,7 @@ struct threeDoubles {
 
 // expected-error at +3 {{constraints not satisfied for class template 'RWBuffer'}}
 // expected-note@*:* {{because 'threeDoubles' does not satisfy '__is_typed_resource_element_compatible'}}
-// expected-note@*:* {{because 'sizeof(threeDoubles) <= 16UL' (24 <= 16) evaluated to false}}
+// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(threeDoubles)' evaluated to false}}
 RWBuffer<threeDoubles> BufferErr3;
 
 
diff --git a/clang/test/SemaHLSL/Types/Traits/IsTypedResourceElementCompatibleErrors.hlsl b/clang/test/SemaHLSL/Types/Traits/IsTypedResourceElementCompatibleErrors.hlsl
index d3d79aa0499e54..cb3e9ae7a61509 100644
--- a/clang/test/SemaHLSL/Types/Traits/IsTypedResourceElementCompatibleErrors.hlsl
+++ b/clang/test/SemaHLSL/Types/Traits/IsTypedResourceElementCompatibleErrors.hlsl
@@ -1,9 +1,10 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -fnative-half-type -verify %s
 
 // types must be complete
-_Static_assert(!__builtin_hlsl_is_typed_resource_element_compatible(__hlsl_resource_t), "");
+_Static_assert(__builtin_hlsl_is_typed_resource_element_compatible(__hlsl_resource_t), "");
 
 // expected-note at +1{{forward declaration of 'notComplete'}}
 struct notComplete;
 // expected-error at +1{{incomplete type 'notComplete' where a complete type is required}}
 _Static_assert(!__builtin_hlsl_is_typed_resource_element_compatible(notComplete), "");
+ 



More information about the cfe-commits mailing list