[clang] Implement resource binding type prefix mismatch diagnostic infrastructure (PR #97103)

Joshua Batista via cfe-commits cfe-commits at lists.llvm.org
Mon Jul 15 15:29:43 PDT 2024


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

>From 108c3e294acebdb9d5c6ee134976a00fedca2840 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Fri, 28 Jun 2024 12:40:56 -0700
Subject: [PATCH 01/26] update tests, update code

---
 clang/include/clang/Basic/Attr.td             |   2 +-
 .../clang/Basic/DiagnosticSemaKinds.td        |   5 +-
 clang/include/clang/Sema/SemaHLSL.h           |   2 +
 clang/lib/Sema/HLSLExternalSemaSource.cpp     |  23 +-
 clang/lib/Sema/SemaHLSL.cpp                   | 231 +++++++++++++++++-
 .../ast-dump-comment-cbuffe-tbufferr.hlsl     |   2 +
 clang/test/AST/HLSL/cbuffer_tbuffer.hlsl      |   6 +-
 clang/test/AST/HLSL/packoffset.hlsl           |   1 +
 clang/test/AST/HLSL/pch_hlsl_buffer.hlsl      |   2 +
 .../test/AST/HLSL/resource_binding_attr.hlsl  |   6 +-
 .../SemaHLSL/resource_binding_attr_error.hlsl |  21 +-
 .../resource_binding_attr_error_mismatch.hlsl |  74 ++++++
 12 files changed, 344 insertions(+), 31 deletions(-)
 create mode 100644 clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl

diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 1293d0ddbc117..6dd37105fd345 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4509,7 +4509,7 @@ def HLSLSV_GroupIndex: HLSLAnnotationAttr {
 
 def HLSLResourceBinding: InheritableAttr {
   let Spellings = [HLSLAnnotation<"register">];
-  let Subjects = SubjectList<[HLSLBufferObj, ExternalGlobalVar]>;
+  let Subjects = SubjectList<[HLSLBufferObj, ExternalGlobalVar], ErrorDiag>;
   let LangOpts = [HLSL];
   let Args = [StringArgument<"Slot">, StringArgument<"Space", 1>];
   let Documentation = [HLSLResourceBindingDocs];
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 0ea3677355169..73ff7b46799c3 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12313,7 +12313,10 @@ def err_hlsl_missing_semantic_annotation : Error<
 def err_hlsl_init_priority_unsupported : Error<
   "initializer priorities are not supported in HLSL">;
 
-def err_hlsl_unsupported_register_type : Error<"invalid resource class specifier '%0' used; expected 'b', 's', 't', or 'u'">;
+def err_hlsl_mismatching_register_resource_type_and_name: Error<"invalid register name prefix '%0' for register resource type '%1' (expected %select{'t'|'u'|'b'|'s'}2)">;
+def err_hlsl_mismatching_register_builtin_type_and_name: Error<"invalid register name prefix '%0' for '%1' (expected %2)">;
+def err_hlsl_unsupported_register_prefix : Error<"invalid resource class specifier '%0' used; expected 't', 'u', 'b', or 's'">;
+def err_hlsl_unsupported_register_resource_type : Error<"invalid resource '%0' used">;
 def err_hlsl_unsupported_register_number : Error<"register number should be an integer">;
 def err_hlsl_expected_space : Error<"invalid space specifier '%0' used; expected 'space' followed by an integer, like space1">;
 def warn_hlsl_packoffset_mix : Warning<"cannot mix packoffset elements with nonpackoffset elements in a cbuffer">,
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 2ddbee67c414b..609bd6b1cf7d7 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -22,6 +22,7 @@
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Sema/Scope.h"
 #include "clang/Sema/SemaBase.h"
+#include "clang/Sema/Sema.h"
 #include <initializer_list>
 
 namespace clang {
@@ -31,6 +32,7 @@ class SemaHLSL : public SemaBase {
 public:
   SemaHLSL(Sema &S);
 
+  HLSLResourceAttr *mergeHLSLResourceAttr(bool CBuffer);
   Decl *ActOnStartBuffer(Scope *BufferScope, bool CBuffer, SourceLocation KwLoc,
                          IdentifierInfo *Ident, SourceLocation IdentLoc,
                          SourceLocation LBrace);
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index 7fcf5754f9dd7..6a71ee2f20333 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -491,9 +491,8 @@ void HLSLExternalSemaSource::defineTrivialHLSLTypes() {
 }
 
 /// Set up common members and attributes for buffer types
-static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
-                                              ResourceClass RC, ResourceKind RK,
-                                              bool IsROV) {
+static BuiltinTypeDeclBuilder setupBufferHandle(CXXRecordDecl *Decl, Sema &S,
+                                                ResourceClass RC) {
   return BuiltinTypeDeclBuilder(Decl)
       .addHandleMember()
       .addDefaultHandleConstructor(S, RC)
@@ -502,12 +501,15 @@ static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
 
 void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
   CXXRecordDecl *Decl;
-  Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer")
-             .addSimpleTemplateParams(*SemaPtr, {"element_type"})
-             .Record;
+  Decl =
+      BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer")
+          .addSimpleTemplateParams(*SemaPtr, {"element_type"})
+          .annotateResourceClass(ResourceClass::UAV, ResourceKind::TypedBuffer,
+                                 /*IsROV=*/false)
+          .Record;
+
   onCompletion(Decl, [this](CXXRecordDecl *Decl) {
-    setupBufferType(Decl, *SemaPtr, ResourceClass::UAV,
-                    ResourceKind::TypedBuffer, /*IsROV=*/false)
+    setupBufferHandle(Decl, *SemaPtr, ResourceClass::UAV)
         .addArraySubscriptOperators()
         .completeDefinition();
   });
@@ -515,10 +517,11 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
   Decl =
       BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RasterizerOrderedBuffer")
           .addSimpleTemplateParams(*SemaPtr, {"element_type"})
+          .annotateResourceClass(ResourceClass::UAV, ResourceKind::TypedBuffer,
+                                 /*IsROV=*/true)
           .Record;
   onCompletion(Decl, [this](CXXRecordDecl *Decl) {
-    setupBufferType(Decl, *SemaPtr, ResourceClass::UAV,
-                    ResourceKind::TypedBuffer, /*IsROV=*/true)
+    setupBufferHandle(Decl, *SemaPtr, ResourceClass::UAV)
         .addArraySubscriptOperators()
         .completeDefinition();
   });
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 9940bc5b4a606..395ad8161d604 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -29,6 +29,25 @@ using namespace clang;
 
 SemaHLSL::SemaHLSL(Sema &S) : SemaBase(S) {}
 
+HLSLResourceAttr *SemaHLSL::mergeHLSLResourceAttr(bool CBuffer) {
+  // cbuffer case
+  if (CBuffer) {
+    HLSLResourceAttr *attr = HLSLResourceAttr::CreateImplicit(
+        getASTContext(), llvm::hlsl::ResourceClass::CBuffer,
+        llvm::hlsl::ResourceKind::CBuffer,
+        /*IsROV=*/false);
+    return attr;
+  }
+  // tbuffer case
+  else {
+    HLSLResourceAttr *attr = HLSLResourceAttr::CreateImplicit(
+        getASTContext(), llvm::hlsl::ResourceClass::SRV,
+        llvm::hlsl::ResourceKind::TBuffer,
+        /*IsROV=*/false);
+    return attr;
+  }
+}
+
 Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
                                  SourceLocation KwLoc, IdentifierInfo *Ident,
                                  SourceLocation IdentLoc,
@@ -38,6 +57,10 @@ Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
   HLSLBufferDecl *Result = HLSLBufferDecl::Create(
       getASTContext(), LexicalParent, CBuffer, KwLoc, Ident, IdentLoc, LBrace);
 
+  HLSLResourceAttr *NewAttr = mergeHLSLResourceAttr(CBuffer);
+  if (NewAttr)
+    Result->addAttr(NewAttr);
+
   SemaRef.PushOnScopeChains(Result, BufferScope);
   SemaRef.PushDeclContext(BufferScope, Result);
 
@@ -459,7 +482,205 @@ void SemaHLSL::handleResourceClassAttr(Decl *D, const ParsedAttr &AL) {
   D->addAttr(HLSLResourceClassAttr::Create(getASTContext(), RC, ArgLoc));
 }
 
+struct register_binding_flags {
+  bool resource = false;
+  bool udt = false;
+  bool other = false;
+  bool basic = false;
+
+  bool srv = false;
+  bool uav = false;
+  bool cbv = false;
+  bool sampler = false;
+
+  bool contains_numeric = false;
+  bool default_globals = false;
+};
+
+bool isDeclaredWithinCOrTBuffer(const Decl *decl) {
+  if (!decl)
+    return false;
+
+  // Traverse up the parent contexts
+  const DeclContext *context = decl->getDeclContext();
+  while (context) {
+    if (isa<HLSLBufferDecl>(context)) {
+      return true;
+    }
+    context = context->getParent();
+  }
+
+  return false;
+}
+
+const HLSLResourceAttr *
+getHLSLResourceAttrFromVarDecl(VarDecl *SamplerUAVOrSRV) {
+  const Type *Ty = SamplerUAVOrSRV->getType()->getPointeeOrArrayElementType();
+  if (!Ty)
+    llvm_unreachable("Resource class must have an element type.");
+
+  if (const BuiltinType *BTy = dyn_cast<BuiltinType>(Ty)) {
+    /* QualType QT = SamplerUAVOrSRV->getType();
+    PrintingPolicy PP = S.getPrintingPolicy();
+    std::string typestr = QualType::getAsString(QT.split(), PP);
+
+    S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_resource_type)
+        << typestr;
+    return; */
+    return nullptr;
+  }
+
+  const CXXRecordDecl *TheRecordDecl = Ty->getAsCXXRecordDecl();
+  if (!TheRecordDecl)
+    llvm_unreachable("Resource class should have a resource type declaration.");
+
+  if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(TheRecordDecl))
+    TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
+  TheRecordDecl = TheRecordDecl->getCanonicalDecl();
+  const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
+  return Attr;
+}
+
+void traverseType(QualType T, register_binding_flags &r) {
+  if (T->isIntegralOrEnumerationType() || T->isFloatingType()) {
+    r.contains_numeric = true;
+    return;
+  } else if (const RecordType *RT = T->getAs<RecordType>()) {
+    RecordDecl *SubRD = RT->getDecl();
+    if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(SubRD)) {
+      auto TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
+      TheRecordDecl = TheRecordDecl->getCanonicalDecl();
+      const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
+      llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
+      switch (DeclResourceClass) {
+      case llvm::hlsl::ResourceClass::SRV: {
+        r.srv = true;
+        break;
+      }
+      case llvm::hlsl::ResourceClass::UAV: {
+        r.uav = true;
+        break;
+      }
+      case llvm::hlsl::ResourceClass::CBuffer: {
+        r.cbv = true;
+        break;
+      }
+      case llvm::hlsl::ResourceClass::Sampler: {
+        r.sampler = true;
+        break;
+      }     
+      }
+    }
+
+    else if (SubRD->isCompleteDefinition()) {
+      for (auto Field : SubRD->fields()) {
+        QualType T = Field->getType();
+        traverseType(T, r);
+      }
+    }
+  }
+}
+
+void setResourceClassFlagsFromRecordDecl(register_binding_flags &r,
+                                         const RecordDecl *RD) {
+  if (!RD)
+    return;
+
+  if (RD->isCompleteDefinition()) {
+    for (auto Field : RD->fields()) {
+      QualType T = Field->getType();
+      traverseType(T, r);
+    }
+  }
+}
+
+register_binding_flags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
+  register_binding_flags r;
+  if (!isDeclaredWithinCOrTBuffer(D)) {
+    // make sure the type is a basic / numeric type
+    if (VarDecl *v = dyn_cast<VarDecl>(D)) {
+      QualType t = v->getType();
+      // a numeric variable will inevitably end up in $Globals buffer
+      if (t->isIntegralType(S.getASTContext()) || t->isFloatingType())
+        r.default_globals = true;
+    }
+  }
+  // Cbuffers and Tbuffers are HLSLBufferDecl types
+  HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(D);
+  // Samplers, UAVs, and SRVs are VarDecl types
+  VarDecl *SamplerUAVOrSRV = dyn_cast<VarDecl>(D);
+
+  if (CBufferOrTBuffer) {
+    r.resource = true;
+    if (CBufferOrTBuffer->isCBuffer())
+      r.cbv = true;
+    else
+      r.srv = true;
+  } else if (SamplerUAVOrSRV) {
+    const HLSLResourceAttr *res_attr =
+        getHLSLResourceAttrFromVarDecl(SamplerUAVOrSRV);
+    if (res_attr) {
+      llvm::hlsl::ResourceClass DeclResourceClass =
+          res_attr->getResourceClass();
+      r.resource = true;
+      switch (DeclResourceClass) {
+      case llvm::hlsl::ResourceClass::SRV: {
+        r.srv = true;
+        break;
+      }
+      case llvm::hlsl::ResourceClass::UAV: {
+        r.uav = true;
+        break;
+      }
+      case llvm::hlsl::ResourceClass::CBuffer: {
+        r.cbv = true;
+        break;
+      }
+      case llvm::hlsl::ResourceClass::Sampler: {
+        r.sampler = true;
+        break;
+      }      
+      }
+    } else {
+      if (SamplerUAVOrSRV->getType()->isBuiltinType())
+        r.basic = true;
+      else if (SamplerUAVOrSRV->getType()->isAggregateType()) {
+        r.udt = true;
+        QualType VarType = SamplerUAVOrSRV->getType();
+        if (const RecordType *RT = VarType->getAs<RecordType>()) {
+          const RecordDecl *RD = RT->getDecl();
+          // recurse through members, set appropriate resource class flags.
+          setResourceClassFlagsFromRecordDecl(r, RD);
+        }
+      } else
+        r.other = true;
+    }
+  } else {
+    llvm_unreachable("unknown decl type");
+  }
+  return r;
+}
+
+static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
+                                        Decl *D, StringRef &Slot) {
+
+  register_binding_flags f = HLSLFillRegisterBindingFlags(S, D);
+  // Samplers, UAVs, and SRVs are VarDecl types
+  VarDecl *SamplerUAVOrSRV = dyn_cast<VarDecl>(D);
+  // Cbuffers and Tbuffers are HLSLBufferDecl types
+  HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(D);
+  if (!SamplerUAVOrSRV && !CBufferOrTBuffer)
+    return;
+
+  // TODO: emit diagnostic code based on the flags set in f.
+}
+
 void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) {
+  if (dyn_cast<VarDecl>(D)) {  
+    if (SemaRef.RequireCompleteType(D->getBeginLoc(), cast<ValueDecl>(D)->getType(),
+                              diag::err_incomplete_type))
+      return;
+  }
   StringRef Space = "space0";
   StringRef Slot = "";
 
@@ -492,13 +713,15 @@ void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) {
   // Validate.
   if (!Slot.empty()) {
     switch (Slot[0]) {
+    case 't':
     case 'u':
     case 'b':
     case 's':
-    case 't':
+    case 'c':
+    case 'i':
       break;
     default:
-      Diag(ArgLoc, diag::err_hlsl_unsupported_register_type)
+      Diag(ArgLoc, diag::err_hlsl_unsupported_register_prefix)
           << Slot.substr(0, 1);
       return;
     }
@@ -522,8 +745,8 @@ void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) {
     return;
   }
 
-  // FIXME: check reg type match decl. Issue
-  // https://github.com/llvm/llvm-project/issues/57886.
+  DiagnoseHLSLResourceRegType(SemaRef, ArgLoc, D, Slot);
+
   HLSLResourceBindingAttr *NewAttr =
       HLSLResourceBindingAttr::Create(getASTContext(), Slot, Space, AL);
   if (NewAttr)
diff --git a/clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl b/clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl
index a98dc0f4ce431..727d505471bcb 100644
--- a/clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl
+++ b/clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl
@@ -38,12 +38,14 @@ tbuffer B {
 }
 
 // AST:HLSLBufferDecl {{.*}}:11:1, line:20:1> line:11:9 cbuffer A
+// AST-NEXT:-HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer CBuffer
 // AST-NEXT:FullComment {{.*}}<line:10:4, col:17>
 // AST-NEXT:`-ParagraphComment {{.*}}<col:4, col:17>
 // AST-NEXT:`-TextComment {{.*}}<col:4, col:17> Text=" CBuffer decl."
 // AST-NEXT:-VarDecl {{.*}}<line:15:5, col:11> col:11 a 'float'
 // AST-NEXT:`-VarDecl {{.*}}<line:19:5, col:9> col:9 b 'int'
 // AST-NEXT:HLSLBufferDecl {{.*}}<line:29:1, line:38:1> line:29:9 tbuffer B
+// AST-NEXT:-HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit SRV TBuffer
 // AST-NEXT:-FullComment {{.*}}<line:28:4, col:17>
 // AST-NEXT: `-ParagraphComment {{.*}}<col:4, col:17>
 // AST-NEXT:  `-TextComment {{.*}}<col:4, col:17> Text=" TBuffer decl."
diff --git a/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl b/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl
index 7204dcd16e0a9..a67688d510ea6 100644
--- a/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl
+++ b/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl
@@ -1,12 +1,14 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -ast-dump -o - %s | FileCheck %s
 
-// CHECK:HLSLBufferDecl 0x[[CB:[0-9a-f]+]] {{.*}} line:5:9 cbuffer CB
+// CHECK:HLSLBufferDecl 0x[[CB:[0-9a-f]+]] {{.*}} line:6:9 cbuffer CB
+// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer CBuffer
 // CHECK-NEXT:VarDecl 0x[[A:[0-9a-f]+]] {{.*}} col:9 used a 'float'
 cbuffer CB {
   float a;
 }
 
-// CHECK:HLSLBufferDecl 0x[[TB:[0-9a-f]+]] {{.*}} line:11:9 tbuffer TB
+// CHECK:HLSLBufferDecl 0x[[TB:[0-9a-f]+]] {{.*}} line:13:9 tbuffer TB
+// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit SRV TBuffer
 // CHECK-NEXT:VarDecl 0x[[B:[0-9a-f]+]] {{.*}} col:9 used b 'float'
 tbuffer TB {
   float b;
diff --git a/clang/test/AST/HLSL/packoffset.hlsl b/clang/test/AST/HLSL/packoffset.hlsl
index 060288c2f7f76..1dc99620c55c4 100644
--- a/clang/test/AST/HLSL/packoffset.hlsl
+++ b/clang/test/AST/HLSL/packoffset.hlsl
@@ -4,6 +4,7 @@
 // CHECK: HLSLBufferDecl {{.*}} cbuffer A
 cbuffer A
 {
+    // CHECK-NEXT:-HLSLResourceAttr {{.*}} <<invalid sloc>> line:5:9 cbuffer A
     // CHECK-NEXT: VarDecl {{.*}} A1 'float4'
     // CHECK-NEXT: HLSLPackOffsetAttr {{.*}} 0 0
     float4 A1 : packoffset(c);
diff --git a/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl b/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl
index e9a6ea1a16312..e264241e62e92 100644
--- a/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl
+++ b/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl
@@ -17,8 +17,10 @@ float foo() {
 }
 // Make sure cbuffer/tbuffer works for PCH.
 // CHECK:HLSLBufferDecl 0x{{[0-9a-f]+}} <{{.*}}:7:1, line:9:1> line:7:9 imported <undeserialized declarations> cbuffer A
+// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer CBuffer
 // CHECK-NEXT:`-VarDecl 0x[[A:[0-9a-f]+]] <line:8:3, col:9> col:9 imported used a 'float'
 // CHECK-NEXT:HLSLBufferDecl 0x{{[0-9a-f]+}} <line:11:1, line:13:1> line:11:9 imported <undeserialized declarations> tbuffer B
+// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit SRV TBuffer
 // CHECK-NEXT:`-VarDecl 0x[[B:[0-9a-f]+]] <line:12:3, col:9> col:9 imported used b 'float'
 // CHECK-NEXT:FunctionDecl 0x{{[0-9a-f]+}} <line:15:1, line:17:1> line:15:7 imported foo 'float ()'
 // CHECK-NEXT:CompoundStmt 0x{{[0-9a-f]+}} <col:13, line:17:1>
diff --git a/clang/test/AST/HLSL/resource_binding_attr.hlsl b/clang/test/AST/HLSL/resource_binding_attr.hlsl
index 71900f2dbda55..9752494f5adc9 100644
--- a/clang/test/AST/HLSL/resource_binding_attr.hlsl
+++ b/clang/test/AST/HLSL/resource_binding_attr.hlsl
@@ -1,13 +1,15 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -ast-dump -o - %s | FileCheck %s
 
-// CHECK:HLSLBufferDecl 0x[[CB:[0-9a-f]+]] {{.*}} line:6:9 cbuffer CB
+// CHECK:HLSLBufferDecl 0x[[CB:[0-9a-f]+]] {{.*}} line:7:9 cbuffer CB
+// CHECK-NEXT:HLSLResourceAttr 0x[[CB:[0-9a-f]+]] {{.*}} Implicit CBuffer CBuffer
 // CHECK-NEXT:HLSLResourceBindingAttr 0x{{[0-9a-f]+}} <col:14> "b3" "space2"
 // CHECK-NEXT:VarDecl 0x[[A:[0-9a-f]+]] {{.*}} col:9 used a 'float'
 cbuffer CB : register(b3, space2) {
   float a;
 }
 
-// CHECK:HLSLBufferDecl 0x[[TB:[0-9a-f]+]] {{.*}} line:13:9 tbuffer TB
+// CHECK:HLSLBufferDecl 0x[[TB:[0-9a-f]+]] {{.*}} line:15:9 tbuffer TB
+// CHECK-NEXT:HLSLResourceAttr 0x[[CB:[0-9a-f]+]] {{.*}} Implicit SRV TBuffer
 // CHECK-NEXT:HLSLResourceBindingAttr 0x{{[0-9a-f]+}} <col:14> "t2" "space1"
 // CHECK-NEXT:VarDecl 0x[[B:[0-9a-f]+]] {{.*}} col:9 used b 'float'
 tbuffer TB : register(t2, space1) {
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
index 2f8aa098db701..58a1f3f1f64d7 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
@@ -1,9 +1,9 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
 
-// expected-error at +1 {{invalid resource class specifier 'c' used; expected 'b', 's', 't', or 'u'}}
+// FIXME: emit a diagnostic because float doesn't match the 'c' register type
 float a : register(c0, space1);
 
-// expected-error at +1 {{invalid resource class specifier 'i' used; expected 'b', 's', 't', or 'u'}}
+// FIXME: emit a diagnostic because cbuffer doesn't match the 'i' register type
 cbuffer b : register(i0) {
 
 }
@@ -33,28 +33,27 @@ cbuffer C : register(b 2) {}
 // expected-error at +1 {{wrong argument format for hlsl attribute, use space3 instead}}
 cbuffer D : register(b 2, space 3) {}
 
-// expected-warning at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
+// expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
 static RWBuffer<float> U : register(u5);
 
 void foo() {
-  // expected-warning at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
+  // expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
   RWBuffer<float> U : register(u3);
 }
 void foo2() {
-  // expected-warning at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
+  // expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
   extern RWBuffer<float> U2 : register(u5);
 }
-// FIXME: expect-error once fix https://github.com/llvm/llvm-project/issues/57886.
+
+// FIXME: emit a diagnostic because float doesn't match the 'u' register type
 float b : register(u0, space1);
 
-// expected-warning at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
+// expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
 void bar(RWBuffer<float> U : register(u3)) {
 
 }
 
-struct S {
-  // FIXME: generate better error when support semantic on struct field.
-  // See https://github.com/llvm/llvm-project/issues/57889.
-  // expected-warning at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
+struct S {  
+  // expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
   RWBuffer<float> U : register(u3);
 };
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl
new file mode 100644
index 0000000000000..b47171f878436
--- /dev/null
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl
@@ -0,0 +1,74 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
+
+
+// expected-error at +1  {{invalid register name prefix 'b' for register resource type 'RWBuffer' (expected 'u')}}
+RWBuffer<int> a : register(b2, space1);
+
+// expected-error at +1  {{invalid register name prefix 't' for register resource type 'RWBuffer' (expected 'u')}}
+RWBuffer<int> b : register(t2, space1);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'Texture1D' (expected 't')}}
+// NOT YET IMPLEMENTED Texture1D<float> tex : register(u3);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 's' for register type 'Texture2D' (expected 't')}}
+// NOT YET IMPLEMENTED Texture2D<float> Texture : register(s0);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'Texture2DMS' (expected 't')}}
+// NOT YET IMPLEMENTED Texture2DMS<float4, 4> T2DMS_t2 : register(u2)
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 't' for register type 'RWTexture3D' (expected 'u')}}
+// NOT YET IMPLEMENTED RWTexture3D<float4> RWT3D_u1 : register(t1)
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'b' for register type 'TextureCube' (expected 't')}}
+// NOT YET IMPLEMENTED TextureCube <float>  t8 : register(b8);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'b' for register type 'TextureCubeArray' (expected 't')}}
+// NOT YET IMPLEMENTED TextureCubeArray TCubeArray_t2 : register(b2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'b' for register type 'Texture1DArray' (expected 't')}}
+// NOT YET IMPLEMENTED Texture1DArray T1DArray_t2 : register(b2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'B' for register type 'Texture2DArray' (expected 't')}}
+// NOT YET IMPLEMENTED Texture2DArray T2DArray_b2 : register(B2);
+
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'Texture2DMSArray' (expected 't')}}
+// NOT YET IMPLEMENTED Texture2DMSArray<float4> msTextureArray : register(u2, space2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'TextureCubeArray' (expected 't')}}
+// NOT YET IMPLEMENTED TextureCubeArray TCubeArray_f2 : register(u2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'TypedBuffer' (expected 't')}}
+// NOT YET IMPLEMENTED TypedBuffer tbuf : register(u2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'RawBuffer' (expected 't')}}
+// NOT YET IMPLEMENTED RawBuffer rbuf : register(u2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 't' for register type 'StructuredBuffer' (expected 'u')}}
+// NOT YET IMPLEMENTED StructuredBuffer ROVStructuredBuff_t2  : register(T2);
+
+// expected-error at +1 {{invalid register name prefix 's' for register resource type 'cbuffer' (expected 'b')}}
+cbuffer f : register(s2, space1) {}
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 't' for register type 'Sampler' (expected 's')}}
+// Can this type just be Sampler instead of SamplerState?
+// NOT YET IMPLEMENTED SamplerState MySampler : register(t3, space1);
+
+// expected-error at +1 {{invalid register name prefix 's' for register resource type 'tbuffer' (expected 't')}}
+tbuffer f : register(s2, space1) {}
+
+// NOT YET IMPLEMENTED : RTAccelerationStructure doesn't have any example tests in DXC
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'FeedbackTexture2D' (expected 't')}}
+// NOT YET IMPLEMENTED FeedbackTexture2D<float> FBTex2D[3][] : register(u0, space26);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'FeedbackTexture2DArray' (expected 't')}}
+// NOT YET IMPLEMENTED FeedbackTexture2DArray<float> FBTex2DArr[3][2][] : register(u0, space27);
+
+
+// empty binding prefix cases:
+// expected-error at +1 {{expected identifier}}
+RWBuffer<int> c: register();
+
+// expected-error at +1 {{expected identifier}}
+RWBuffer<int> d: register("");

>From b7b8372764c0e55b3e14c54da6504b2972e9cf54 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Fri, 28 Jun 2024 16:18:43 -0700
Subject: [PATCH 02/26] remove mismatch test, will be applied in step 2 PR.
 update packoffset

---
 clang/test/AST/HLSL/packoffset.hlsl           |  2 +-
 .../resource_binding_attr_error_mismatch.hlsl | 74 -------------------
 2 files changed, 1 insertion(+), 75 deletions(-)
 delete mode 100644 clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl

diff --git a/clang/test/AST/HLSL/packoffset.hlsl b/clang/test/AST/HLSL/packoffset.hlsl
index 1dc99620c55c4..9deb63caa500a 100644
--- a/clang/test/AST/HLSL/packoffset.hlsl
+++ b/clang/test/AST/HLSL/packoffset.hlsl
@@ -4,7 +4,7 @@
 // CHECK: HLSLBufferDecl {{.*}} cbuffer A
 cbuffer A
 {
-    // CHECK-NEXT:-HLSLResourceAttr {{.*}} <<invalid sloc>> line:5:9 cbuffer A
+    // CHECK-NEXT:-HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer CBuffer
     // CHECK-NEXT: VarDecl {{.*}} A1 'float4'
     // CHECK-NEXT: HLSLPackOffsetAttr {{.*}} 0 0
     float4 A1 : packoffset(c);
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl
deleted file mode 100644
index b47171f878436..0000000000000
--- a/clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl
+++ /dev/null
@@ -1,74 +0,0 @@
-// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
-
-
-// expected-error at +1  {{invalid register name prefix 'b' for register resource type 'RWBuffer' (expected 'u')}}
-RWBuffer<int> a : register(b2, space1);
-
-// expected-error at +1  {{invalid register name prefix 't' for register resource type 'RWBuffer' (expected 'u')}}
-RWBuffer<int> b : register(t2, space1);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'Texture1D' (expected 't')}}
-// NOT YET IMPLEMENTED Texture1D<float> tex : register(u3);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 's' for register type 'Texture2D' (expected 't')}}
-// NOT YET IMPLEMENTED Texture2D<float> Texture : register(s0);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'Texture2DMS' (expected 't')}}
-// NOT YET IMPLEMENTED Texture2DMS<float4, 4> T2DMS_t2 : register(u2)
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 't' for register type 'RWTexture3D' (expected 'u')}}
-// NOT YET IMPLEMENTED RWTexture3D<float4> RWT3D_u1 : register(t1)
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'b' for register type 'TextureCube' (expected 't')}}
-// NOT YET IMPLEMENTED TextureCube <float>  t8 : register(b8);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'b' for register type 'TextureCubeArray' (expected 't')}}
-// NOT YET IMPLEMENTED TextureCubeArray TCubeArray_t2 : register(b2);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'b' for register type 'Texture1DArray' (expected 't')}}
-// NOT YET IMPLEMENTED Texture1DArray T1DArray_t2 : register(b2);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'B' for register type 'Texture2DArray' (expected 't')}}
-// NOT YET IMPLEMENTED Texture2DArray T2DArray_b2 : register(B2);
-
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'Texture2DMSArray' (expected 't')}}
-// NOT YET IMPLEMENTED Texture2DMSArray<float4> msTextureArray : register(u2, space2);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'TextureCubeArray' (expected 't')}}
-// NOT YET IMPLEMENTED TextureCubeArray TCubeArray_f2 : register(u2);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'TypedBuffer' (expected 't')}}
-// NOT YET IMPLEMENTED TypedBuffer tbuf : register(u2);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'RawBuffer' (expected 't')}}
-// NOT YET IMPLEMENTED RawBuffer rbuf : register(u2);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 't' for register type 'StructuredBuffer' (expected 'u')}}
-// NOT YET IMPLEMENTED StructuredBuffer ROVStructuredBuff_t2  : register(T2);
-
-// expected-error at +1 {{invalid register name prefix 's' for register resource type 'cbuffer' (expected 'b')}}
-cbuffer f : register(s2, space1) {}
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 't' for register type 'Sampler' (expected 's')}}
-// Can this type just be Sampler instead of SamplerState?
-// NOT YET IMPLEMENTED SamplerState MySampler : register(t3, space1);
-
-// expected-error at +1 {{invalid register name prefix 's' for register resource type 'tbuffer' (expected 't')}}
-tbuffer f : register(s2, space1) {}
-
-// NOT YET IMPLEMENTED : RTAccelerationStructure doesn't have any example tests in DXC
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'FeedbackTexture2D' (expected 't')}}
-// NOT YET IMPLEMENTED FeedbackTexture2D<float> FBTex2D[3][] : register(u0, space26);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'FeedbackTexture2DArray' (expected 't')}}
-// NOT YET IMPLEMENTED FeedbackTexture2DArray<float> FBTex2DArr[3][2][] : register(u0, space27);
-
-
-// empty binding prefix cases:
-// expected-error at +1 {{expected identifier}}
-RWBuffer<int> c: register();
-
-// expected-error at +1 {{expected identifier}}
-RWBuffer<int> d: register("");

>From e7d0d4bc9ba26bfcf43ceace01579c90aa387e0a Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Fri, 28 Jun 2024 16:29:20 -0700
Subject: [PATCH 03/26] clang-format

---
 clang/include/clang/Sema/SemaHLSL.h |  2 +-
 clang/lib/Sema/SemaHLSL.cpp         | 11 ++++++-----
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 609bd6b1cf7d7..19223563fb948 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -21,8 +21,8 @@
 #include "clang/Basic/IdentifierTable.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Sema/Scope.h"
-#include "clang/Sema/SemaBase.h"
 #include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaBase.h"
 #include <initializer_list>
 
 namespace clang {
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 395ad8161d604..fed26e5d2da18 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -568,7 +568,7 @@ void traverseType(QualType T, register_binding_flags &r) {
       case llvm::hlsl::ResourceClass::Sampler: {
         r.sampler = true;
         break;
-      }     
+      }
       }
     }
 
@@ -639,7 +639,7 @@ register_binding_flags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
       case llvm::hlsl::ResourceClass::Sampler: {
         r.sampler = true;
         break;
-      }      
+      }
       }
     } else {
       if (SamplerUAVOrSRV->getType()->isBuiltinType())
@@ -676,9 +676,10 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
 }
 
 void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) {
-  if (dyn_cast<VarDecl>(D)) {  
-    if (SemaRef.RequireCompleteType(D->getBeginLoc(), cast<ValueDecl>(D)->getType(),
-                              diag::err_incomplete_type))
+  if (dyn_cast<VarDecl>(D)) {
+    if (SemaRef.RequireCompleteType(D->getBeginLoc(),
+                                    cast<ValueDecl>(D)->getType(),
+                                    diag::err_incomplete_type))
       return;
   }
   StringRef Space = "space0";

>From 41816a4ddb2251bf64e2cd393d4104e7b3564f12 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 1 Jul 2024 10:36:02 -0700
Subject: [PATCH 04/26] remove unnecessary header

---
 clang/include/clang/Sema/SemaHLSL.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 19223563fb948..49cd05f0cd9cf 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -21,7 +21,6 @@
 #include "clang/Basic/IdentifierTable.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Sema/Scope.h"
-#include "clang/Sema/Sema.h"
 #include "clang/Sema/SemaBase.h"
 #include <initializer_list>
 

>From 2de8ca4ea36c5f2eba0eac6061723de07f74bd37 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 1 Jul 2024 17:55:03 -0700
Subject: [PATCH 05/26] full implementation

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  10 ++
 clang/lib/Sema/SemaHLSL.cpp                   | 162 +++++++++++++++++-
 2 files changed, 170 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 73ff7b46799c3..f7033ca867489 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12313,6 +12313,16 @@ def err_hlsl_missing_semantic_annotation : Error<
 def err_hlsl_init_priority_unsupported : Error<
   "initializer priorities are not supported in HLSL">;
 
+def err_hlsl_mismatching_register_type_and_variable_type: Error<"unsupported resource register binding '%select{t|u|b|s}1' on variable of type '%0'">;
+def err_hlsl_unsupported_register_type_and_variable_type: Error<"register binding type '%1' not supported for variable of type '%0'">;
+def err_hlsl_mismatching_register_type_and_resource_type: Error<"%select{srv|uav|cbv|sampler}2 type '%0' requires register type '%select{t|u|b|s}2', but register type '%1' was used">;
+def err_hlsl_unsupported_register_type_and_resource_type: Error<"invalid register type '%0' used; expected 't', 'u', 'b', or 's'">;
+def err_hlsl_conflicting_register_annotations: Error<"conflicting register annotations: %0 and %0">;
+def warn_hlsl_deprecated_register_type_b: Warning<"deprecated legacy bool constant register binding 'b' used. 'b' is only used for constant buffer resource binding">;
+def warn_hlsl_deprecated_register_type_i: Warning<"deprecated legacy int constant register binding 'i' used">;
+def warn_hlsl_register_type_c_not_in_global_scope: Warning<"register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead">;
+def warn_hlsl_UDT_missing_resource_type_member: Warning<"variable of type '%0' bound to register type '%1' does not contain a matching '%select{srv|uav|cbv|sampler}2' resource">;
+def warn_hlsl_UDT_missing_basic_type: Warning<"register 'c' used on type with no contents to allocate in a constant buffer">;
 def err_hlsl_mismatching_register_resource_type_and_name: Error<"invalid register name prefix '%0' for register resource type '%1' (expected %select{'t'|'u'|'b'|'s'}2)">;
 def err_hlsl_mismatching_register_builtin_type_and_name: Error<"invalid register name prefix '%0' for '%1' (expected %2)">;
 def err_hlsl_unsupported_register_prefix : Error<"invalid resource class specifier '%0' used; expected 't', 'u', 'b', or 's'">;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index fed26e5d2da18..91af3f349c4ce 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -661,10 +661,26 @@ register_binding_flags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
   return r;
 }
 
+static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *D,
+                                                StringRef &Slot) {
+  std::set<std::string> s; // store unique register type + numbers
+  for (auto it = D->attr_begin(); it != D->attr_end(); ++it) {
+    
+    if (HLSLResourceBindingAttr *attr =
+            dyn_cast<HLSLResourceBindingAttr>(*it)) {
+      std::string regInfo(Slot);   
+      auto p = s.insert(regInfo);
+      if (!p.second) {
+        S.Diag(attr->getLoc(), diag::err_hlsl_conflicting_register_annotations)
+            << Slot.substr(0, 1);
+      }
+    }
+  }
+}
+
 static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
                                         Decl *D, StringRef &Slot) {
 
-  register_binding_flags f = HLSLFillRegisterBindingFlags(S, D);
   // Samplers, UAVs, and SRVs are VarDecl types
   VarDecl *SamplerUAVOrSRV = dyn_cast<VarDecl>(D);
   // Cbuffers and Tbuffers are HLSLBufferDecl types
@@ -672,7 +688,149 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
   if (!SamplerUAVOrSRV && !CBufferOrTBuffer)
     return;
 
-  // TODO: emit diagnostic code based on the flags set in f.
+  register_binding_flags f = HLSLFillRegisterBindingFlags(S, D);
+  assert((int)f.other + (int)f.resource + (int)f.basic + (int)f.udt == 1 && "only one resource analysis result should be expected");
+
+  // get the variable type
+  std::string typestr;
+  if (SamplerUAVOrSRV) {
+    QualType QT = SamplerUAVOrSRV->getType();
+    PrintingPolicy PP = S.getPrintingPolicy();
+    typestr = QualType::getAsString(QT.split(), PP);
+  } else 
+    typestr = CBufferOrTBuffer->isCBuffer() ? "cbuffer" : "tbuffer";
+  
+
+  // first, if "other" is set, emit an error
+  if (f.other) {  
+    S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
+        << Slot << typestr;
+  }
+
+  // next, if multiple register annotations exist, check that none conflict.
+  ValidateMultipleRegisterAnnotations(S, D, Slot);
+  
+  // next, if resource is set, make sure the register type in the register annotation
+  // is compatible with the variable's resource type.
+  if (f.resource) {
+    VarDecl *VD = dyn_cast<VarDecl>(D);
+
+    const HLSLResourceAttr *res_attr =
+        getHLSLResourceAttrFromVarDecl(VD);
+    assert(res_attr && "any decl that set the resource flag on analysis should have a resource attribute attached.");
+    llvm::hlsl::ResourceClass DeclResourceClass = res_attr->getResourceClass();
+    switch (DeclResourceClass) {
+    case llvm::hlsl::ResourceClass::SRV: {
+      if (!f.srv) {
+        S.Diag(
+            res_attr->getLoc(), diag::err_hlsl_mismatching_register_type_and_resource_type)
+            << typestr << Slot[0] << 0 /*srv*/;
+      }
+      break;
+    }
+    case llvm::hlsl::ResourceClass::UAV: {
+      if (!f.uav) {
+        S.Diag(res_attr->getLoc(),
+               diag::err_hlsl_mismatching_register_type_and_resource_type)
+            << typestr << Slot[0] << 1 /*uav*/;
+      }
+      break;
+    }
+    case llvm::hlsl::ResourceClass::CBuffer: {
+      if (!f.cbv) {
+        S.Diag(res_attr->getLoc(),
+               diag::err_hlsl_mismatching_register_type_and_resource_type)
+            << typestr << Slot[0] << 2 /*cbv*/;
+      }
+      break;
+    }
+    case llvm::hlsl::ResourceClass::Sampler: {
+      if (!f.sampler) {
+        S.Diag(res_attr->getLoc(),
+               diag::err_hlsl_mismatching_register_type_and_resource_type)
+            << typestr << Slot[0] << 3 /*sampler*/;
+      }
+      break;
+    }
+    }
+  }
+  
+  // next, handle diagnostics for when the "basic" flag is set,
+  // including the legacy "i" and "b" register types.
+  if (f.basic) {
+    if (f.default_globals) {
+      if (Slot[0] == 'b')
+        S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_b);      
+      if (Slot[0] == 'i')
+        S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_i);      
+    }
+
+    else if (Slot[0] == 'c') {
+      if (!f.default_globals){
+        S.Diag(ArgLoc, diag::warn_hlsl_register_type_c_not_in_global_scope);
+      }      
+    } 
+    else if (Slot[0] == 't')
+        S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type) << 0 << typestr;
+    else if (Slot[0] == 'u')
+      S.Diag(ArgLoc,
+              diag::err_hlsl_mismatching_register_type_and_variable_type)
+          << 1 << typestr;
+    else if (Slot[0] == 's')
+      S.Diag(ArgLoc,
+              diag::err_hlsl_mismatching_register_type_and_variable_type)
+          << 3 << typestr;
+    // any other register type should emit err_hlsl_unsupported_register_type_and_variable_type
+    else {
+      S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
+          << Slot[0] << typestr;
+    }    
+  }
+
+  // finally, we handle the udt case
+  
+  if (f.udt) {
+    for (auto it = D->attr_begin(); it != D->attr_end(); ++it) {
+      if (HLSLResourceBindingAttr *attr =
+              dyn_cast<HLSLResourceBindingAttr>(*it)) {
+        llvm::StringRef registerTypeSlotRef = attr->getSlot();
+        std::string registerTypeSlot(
+            registerTypeSlotRef);
+        if (registerTypeSlot[0] == 't') {        
+          if (!f.srv) {
+            S.Diag(attr->getLoc(),
+                   diag::warn_hlsl_UDT_missing_resource_type_member)
+                << typestr << "t"
+                << 0;
+          }
+        } else if (registerTypeSlot[0] == 'u') {
+          if (!f.uav) {
+            S.Diag(attr->getLoc(),
+                   diag::warn_hlsl_UDT_missing_resource_type_member)
+                << typestr << "u" << 1;
+          }
+        } else if (registerTypeSlot[0] == 'b') {
+          if (!f.cbv) {
+            S.Diag(attr->getLoc(), diag::warn_hlsl_UDT_missing_resource_type_member)
+                << typestr << "b" << 2;
+          }
+        } else if (registerTypeSlot[0] == 's') {
+          if (!f.sampler) {
+            S.Diag(attr->getLoc(), diag::warn_hlsl_UDT_missing_resource_type_member)
+                << typestr << "s" << 3;
+          }
+        } else if (registerTypeSlot[0] == 'c') {
+          if (!f.srv)
+            S.Diag(attr->getLoc(), diag::warn_hlsl_UDT_missing_basic_type);          
+        } 
+        else {
+          S.Diag(attr->getLoc(),
+                 diag::err_hlsl_unsupported_register_type_and_variable_type)
+              << registerTypeSlot[0] << typestr;
+        }
+      }
+    }
+  } 
 }
 
 void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) {

>From 80975186632fc155bcfb3927f6c8aecc13ab633d Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 1 Jul 2024 17:55:33 -0700
Subject: [PATCH 06/26] clang format

---
 clang/lib/Sema/SemaHLSL.cpp | 74 ++++++++++++++++++-------------------
 1 file changed, 36 insertions(+), 38 deletions(-)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 91af3f349c4ce..53d0173ee13e3 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -689,7 +689,8 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
     return;
 
   register_binding_flags f = HLSLFillRegisterBindingFlags(S, D);
-  assert((int)f.other + (int)f.resource + (int)f.basic + (int)f.udt == 1 && "only one resource analysis result should be expected");
+  assert((int)f.other + (int)f.resource + (int)f.basic + (int)f.udt == 1 &&
+         "only one resource analysis result should be expected");
 
   // get the variable type
   std::string typestr;
@@ -697,33 +698,32 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
     QualType QT = SamplerUAVOrSRV->getType();
     PrintingPolicy PP = S.getPrintingPolicy();
     typestr = QualType::getAsString(QT.split(), PP);
-  } else 
+  } else
     typestr = CBufferOrTBuffer->isCBuffer() ? "cbuffer" : "tbuffer";
-  
 
   // first, if "other" is set, emit an error
-  if (f.other) {  
+  if (f.other) {
     S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
         << Slot << typestr;
   }
 
   // next, if multiple register annotations exist, check that none conflict.
   ValidateMultipleRegisterAnnotations(S, D, Slot);
-  
-  // next, if resource is set, make sure the register type in the register annotation
-  // is compatible with the variable's resource type.
+
+  // next, if resource is set, make sure the register type in the register
+  // annotation is compatible with the variable's resource type.
   if (f.resource) {
     VarDecl *VD = dyn_cast<VarDecl>(D);
 
-    const HLSLResourceAttr *res_attr =
-        getHLSLResourceAttrFromVarDecl(VD);
-    assert(res_attr && "any decl that set the resource flag on analysis should have a resource attribute attached.");
+    const HLSLResourceAttr *res_attr = getHLSLResourceAttrFromVarDecl(VD);
+    assert(res_attr && "any decl that set the resource flag on analysis should "
+                       "have a resource attribute attached.");
     llvm::hlsl::ResourceClass DeclResourceClass = res_attr->getResourceClass();
     switch (DeclResourceClass) {
     case llvm::hlsl::ResourceClass::SRV: {
       if (!f.srv) {
-        S.Diag(
-            res_attr->getLoc(), diag::err_hlsl_mismatching_register_type_and_resource_type)
+        S.Diag(res_attr->getLoc(),
+               diag::err_hlsl_mismatching_register_type_and_resource_type)
             << typestr << Slot[0] << 0 /*srv*/;
       }
       break;
@@ -754,54 +754,51 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
     }
     }
   }
-  
+
   // next, handle diagnostics for when the "basic" flag is set,
   // including the legacy "i" and "b" register types.
   if (f.basic) {
     if (f.default_globals) {
       if (Slot[0] == 'b')
-        S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_b);      
+        S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_b);
       if (Slot[0] == 'i')
-        S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_i);      
+        S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_i);
     }
 
     else if (Slot[0] == 'c') {
-      if (!f.default_globals){
+      if (!f.default_globals) {
         S.Diag(ArgLoc, diag::warn_hlsl_register_type_c_not_in_global_scope);
-      }      
-    } 
-    else if (Slot[0] == 't')
-        S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type) << 0 << typestr;
+      }
+    } else if (Slot[0] == 't')
+      S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
+          << 0 << typestr;
     else if (Slot[0] == 'u')
-      S.Diag(ArgLoc,
-              diag::err_hlsl_mismatching_register_type_and_variable_type)
+      S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
           << 1 << typestr;
     else if (Slot[0] == 's')
-      S.Diag(ArgLoc,
-              diag::err_hlsl_mismatching_register_type_and_variable_type)
+      S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
           << 3 << typestr;
-    // any other register type should emit err_hlsl_unsupported_register_type_and_variable_type
+    // any other register type should emit
+    // err_hlsl_unsupported_register_type_and_variable_type
     else {
       S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
           << Slot[0] << typestr;
-    }    
+    }
   }
 
   // finally, we handle the udt case
-  
+
   if (f.udt) {
     for (auto it = D->attr_begin(); it != D->attr_end(); ++it) {
       if (HLSLResourceBindingAttr *attr =
               dyn_cast<HLSLResourceBindingAttr>(*it)) {
         llvm::StringRef registerTypeSlotRef = attr->getSlot();
-        std::string registerTypeSlot(
-            registerTypeSlotRef);
-        if (registerTypeSlot[0] == 't') {        
+        std::string registerTypeSlot(registerTypeSlotRef);
+        if (registerTypeSlot[0] == 't') {
           if (!f.srv) {
             S.Diag(attr->getLoc(),
                    diag::warn_hlsl_UDT_missing_resource_type_member)
-                << typestr << "t"
-                << 0;
+                << typestr << "t" << 0;
           }
         } else if (registerTypeSlot[0] == 'u') {
           if (!f.uav) {
@@ -811,26 +808,27 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
           }
         } else if (registerTypeSlot[0] == 'b') {
           if (!f.cbv) {
-            S.Diag(attr->getLoc(), diag::warn_hlsl_UDT_missing_resource_type_member)
+            S.Diag(attr->getLoc(),
+                   diag::warn_hlsl_UDT_missing_resource_type_member)
                 << typestr << "b" << 2;
           }
         } else if (registerTypeSlot[0] == 's') {
           if (!f.sampler) {
-            S.Diag(attr->getLoc(), diag::warn_hlsl_UDT_missing_resource_type_member)
+            S.Diag(attr->getLoc(),
+                   diag::warn_hlsl_UDT_missing_resource_type_member)
                 << typestr << "s" << 3;
           }
         } else if (registerTypeSlot[0] == 'c') {
           if (!f.srv)
-            S.Diag(attr->getLoc(), diag::warn_hlsl_UDT_missing_basic_type);          
-        } 
-        else {
+            S.Diag(attr->getLoc(), diag::warn_hlsl_UDT_missing_basic_type);
+        } else {
           S.Diag(attr->getLoc(),
                  diag::err_hlsl_unsupported_register_type_and_variable_type)
               << registerTypeSlot[0] << typestr;
         }
       }
     }
-  } 
+  }
 }
 
 void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) {

>From f03716829be70264d3c0aa0813407cacfe52576c Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 2 Jul 2024 16:41:39 -0700
Subject: [PATCH 07/26] fix mismatching register type diagnostic logic

---
 clang/lib/Sema/SemaHLSL.cpp                   | 113 ++++++++++--------
 .../SemaHLSL/resource_binding_attr_error.hlsl |   7 +-
 .../resource_binding_attr_error_mismatch.hlsl |  74 ++++++++++++
 3 files changed, 142 insertions(+), 52 deletions(-)
 create mode 100644 clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 53d0173ee13e3..cf4a84506daee 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -514,31 +514,39 @@ bool isDeclaredWithinCOrTBuffer(const Decl *decl) {
 }
 
 const HLSLResourceAttr *
-getHLSLResourceAttrFromVarDecl(VarDecl *SamplerUAVOrSRV) {
-  const Type *Ty = SamplerUAVOrSRV->getType()->getPointeeOrArrayElementType();
-  if (!Ty)
-    llvm_unreachable("Resource class must have an element type.");
+getHLSLResourceAttrFromEitherDecl(VarDecl *SamplerUAVOrSRV,
+                                  HLSLBufferDecl *CBufferOrTBuffer) {
 
-  if (const BuiltinType *BTy = dyn_cast<BuiltinType>(Ty)) {
-    /* QualType QT = SamplerUAVOrSRV->getType();
-    PrintingPolicy PP = S.getPrintingPolicy();
-    std::string typestr = QualType::getAsString(QT.split(), PP);
-
-    S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_resource_type)
-        << typestr;
-    return; */
-    return nullptr;
-  }
+  if (SamplerUAVOrSRV) {
+    const Type *Ty = SamplerUAVOrSRV->getType()->getPointeeOrArrayElementType();
+    if (!Ty)
+      llvm_unreachable("Resource class must have an element type.");
+
+    if (const BuiltinType *BTy = dyn_cast<BuiltinType>(Ty)) {
+      /* QualType QT = SamplerUAVOrSRV->getType();
+      PrintingPolicy PP = S.getPrintingPolicy();
+      std::string typestr = QualType::getAsString(QT.split(), PP);
+
+      S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_resource_type)
+          << typestr;
+      return; */
+      return nullptr;
+    }
 
-  const CXXRecordDecl *TheRecordDecl = Ty->getAsCXXRecordDecl();
-  if (!TheRecordDecl)
-    llvm_unreachable("Resource class should have a resource type declaration.");
+    const CXXRecordDecl *TheRecordDecl = Ty->getAsCXXRecordDecl();
+    if (!TheRecordDecl)
+      llvm_unreachable(
+          "Resource class should have a resource type declaration.");
 
-  if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(TheRecordDecl))
-    TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
-  TheRecordDecl = TheRecordDecl->getCanonicalDecl();
-  const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
-  return Attr;
+    if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(TheRecordDecl))
+      TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
+    TheRecordDecl = TheRecordDecl->getCanonicalDecl();
+    const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
+    return Attr;
+  } else if (CBufferOrTBuffer) {
+    const auto *Attr = CBufferOrTBuffer->getAttr<HLSLResourceAttr>();
+    return Attr;
+  }
 }
 
 void traverseType(QualType T, register_binding_flags &r) {
@@ -618,7 +626,7 @@ register_binding_flags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
       r.srv = true;
   } else if (SamplerUAVOrSRV) {
     const HLSLResourceAttr *res_attr =
-        getHLSLResourceAttrFromVarDecl(SamplerUAVOrSRV);
+        getHLSLResourceAttrFromEitherDecl(SamplerUAVOrSRV, CBufferOrTBuffer);
     if (res_attr) {
       llvm::hlsl::ResourceClass DeclResourceClass =
           res_attr->getResourceClass();
@@ -685,8 +693,12 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
   VarDecl *SamplerUAVOrSRV = dyn_cast<VarDecl>(D);
   // Cbuffers and Tbuffers are HLSLBufferDecl types
   HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(D);
+
+  // exactly one of these two types should be set
   if (!SamplerUAVOrSRV && !CBufferOrTBuffer)
     return;
+  if (SamplerUAVOrSRV && CBufferOrTBuffer)
+    return;
 
   register_binding_flags f = HLSLFillRegisterBindingFlags(S, D);
   assert((int)f.other + (int)f.resource + (int)f.basic + (int)f.udt == 1 &&
@@ -701,6 +713,8 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
   } else
     typestr = CBufferOrTBuffer->isCBuffer() ? "cbuffer" : "tbuffer";
 
+  std::string registerType(Slot.substr(0, 1));
+
   // first, if "other" is set, emit an error
   if (f.other) {
     S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
@@ -713,42 +727,43 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
   // next, if resource is set, make sure the register type in the register
   // annotation is compatible with the variable's resource type.
   if (f.resource) {
-    VarDecl *VD = dyn_cast<VarDecl>(D);
-
-    const HLSLResourceAttr *res_attr = getHLSLResourceAttrFromVarDecl(VD);
+    const HLSLResourceAttr *res_attr =
+        getHLSLResourceAttrFromEitherDecl(SamplerUAVOrSRV, CBufferOrTBuffer);
     assert(res_attr && "any decl that set the resource flag on analysis should "
                        "have a resource attribute attached.");
-    llvm::hlsl::ResourceClass DeclResourceClass = res_attr->getResourceClass();
+    const llvm::hlsl::ResourceClass DeclResourceClass =
+        res_attr->getResourceClass();
+
     switch (DeclResourceClass) {
     case llvm::hlsl::ResourceClass::SRV: {
-      if (!f.srv) {
-        S.Diag(res_attr->getLoc(),
+      if (registerType != "t") {
+        S.Diag(D->getLocation(),
                diag::err_hlsl_mismatching_register_type_and_resource_type)
-            << typestr << Slot[0] << 0 /*srv*/;
+            << typestr << registerType << 0 /*srv*/;
       }
       break;
     }
     case llvm::hlsl::ResourceClass::UAV: {
-      if (!f.uav) {
-        S.Diag(res_attr->getLoc(),
+      if (registerType != "u") {
+        S.Diag(D->getLocation(),
                diag::err_hlsl_mismatching_register_type_and_resource_type)
-            << typestr << Slot[0] << 1 /*uav*/;
+            << typestr << registerType << 1 /*uav*/;
       }
       break;
     }
     case llvm::hlsl::ResourceClass::CBuffer: {
-      if (!f.cbv) {
-        S.Diag(res_attr->getLoc(),
+      if (registerType != "b") {
+        S.Diag(D->getLocation(),
                diag::err_hlsl_mismatching_register_type_and_resource_type)
-            << typestr << Slot[0] << 2 /*cbv*/;
+            << typestr << registerType << 2 /*cbv*/;
       }
       break;
     }
     case llvm::hlsl::ResourceClass::Sampler: {
-      if (!f.sampler) {
-        S.Diag(res_attr->getLoc(),
+      if (registerType != "s") {
+        S.Diag(D->getLocation(),
                diag::err_hlsl_mismatching_register_type_and_resource_type)
-            << typestr << Slot[0] << 3 /*sampler*/;
+            << typestr << registerType << 3 /*sampler*/;
       }
       break;
     }
@@ -769,18 +784,18 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
       if (!f.default_globals) {
         S.Diag(ArgLoc, diag::warn_hlsl_register_type_c_not_in_global_scope);
       }
-    } else if (Slot[0] == 't')
+    } else if (Slot[0] == 't') {
       S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
           << 0 << typestr;
-    else if (Slot[0] == 'u')
+    } else if (Slot[0] == 'u') {
       S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
           << 1 << typestr;
-    else if (Slot[0] == 's')
+    } else if (Slot[0] == 's') {
       S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
           << 3 << typestr;
     // any other register type should emit
     // err_hlsl_unsupported_register_type_and_variable_type
-    else {
+    } else {
       S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
           << Slot[0] << typestr;
     }
@@ -794,37 +809,37 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
               dyn_cast<HLSLResourceBindingAttr>(*it)) {
         llvm::StringRef registerTypeSlotRef = attr->getSlot();
         std::string registerTypeSlot(registerTypeSlotRef);
-        if (registerTypeSlot[0] == 't') {
+        if (registerTypeSlot.front() == 't') {
           if (!f.srv) {
             S.Diag(attr->getLoc(),
                    diag::warn_hlsl_UDT_missing_resource_type_member)
                 << typestr << "t" << 0;
           }
-        } else if (registerTypeSlot[0] == 'u') {
+        } else if (registerTypeSlot.front() == 'u') {
           if (!f.uav) {
             S.Diag(attr->getLoc(),
                    diag::warn_hlsl_UDT_missing_resource_type_member)
                 << typestr << "u" << 1;
           }
-        } else if (registerTypeSlot[0] == 'b') {
+        } else if (registerTypeSlot.front() == 'b') {
           if (!f.cbv) {
             S.Diag(attr->getLoc(),
                    diag::warn_hlsl_UDT_missing_resource_type_member)
                 << typestr << "b" << 2;
           }
-        } else if (registerTypeSlot[0] == 's') {
+        } else if (registerTypeSlot.front() == 's') {
           if (!f.sampler) {
             S.Diag(attr->getLoc(),
                    diag::warn_hlsl_UDT_missing_resource_type_member)
                 << typestr << "s" << 3;
           }
-        } else if (registerTypeSlot[0] == 'c') {
+        } else if (registerTypeSlot.front() == 'c') {
           if (!f.srv)
             S.Diag(attr->getLoc(), diag::warn_hlsl_UDT_missing_basic_type);
         } else {
           S.Diag(attr->getLoc(),
                  diag::err_hlsl_unsupported_register_type_and_variable_type)
-              << registerTypeSlot[0] << typestr;
+              << registerTypeSlot.front() << typestr;
         }
       }
     }
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
index 58a1f3f1f64d7..ce81574a79608 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
@@ -1,9 +1,10 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
 
-// FIXME: emit a diagnostic because float doesn't match the 'c' register type
-float a : register(c0, space1);
+// valid, The register keyword in this statement isn't binding a resource, rather it is
+// specifying a constant register binding offset within the $Globals cbuffer, which is legacy behavior from DX9.
+float a : register(c0);
 
-// FIXME: emit a diagnostic because cbuffer doesn't match the 'i' register type
+// expected-error at +1 {{cbv type 'cbuffer' requires register type 'b', but register type 'i' was used}}
 cbuffer b : register(i0) {
 
 }
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl
new file mode 100644
index 0000000000000..54e852483f832
--- /dev/null
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl
@@ -0,0 +1,74 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
+
+
+// expected-error at +1  {{uav type 'RWBuffer<int>' requires register type 'u', but register type 'b' was used}}
+RWBuffer<int> a : register(b2, space1);
+
+// expected-error at +1  {{uav type 'RWBuffer<int>' requires register type 'u', but register type 't' was used}}
+RWBuffer<int> b : register(t2, space1);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'Texture1D' (expected 't')}}
+// NOT YET IMPLEMENTED Texture1D<float> tex : register(u3);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 's' for register type 'Texture2D' (expected 't')}}
+// NOT YET IMPLEMENTED Texture2D<float> Texture : register(s0);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'Texture2DMS' (expected 't')}}
+// NOT YET IMPLEMENTED Texture2DMS<float4, 4> T2DMS_t2 : register(u2)
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 't' for register type 'RWTexture3D' (expected 'u')}}
+// NOT YET IMPLEMENTED RWTexture3D<float4> RWT3D_u1 : register(t1)
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'b' for register type 'TextureCube' (expected 't')}}
+// NOT YET IMPLEMENTED TextureCube <float>  t8 : register(b8);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'b' for register type 'TextureCubeArray' (expected 't')}}
+// NOT YET IMPLEMENTED TextureCubeArray TCubeArray_t2 : register(b2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'b' for register type 'Texture1DArray' (expected 't')}}
+// NOT YET IMPLEMENTED Texture1DArray T1DArray_t2 : register(b2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'B' for register type 'Texture2DArray' (expected 't')}}
+// NOT YET IMPLEMENTED Texture2DArray T2DArray_b2 : register(B2);
+
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'Texture2DMSArray' (expected 't')}}
+// NOT YET IMPLEMENTED Texture2DMSArray<float4> msTextureArray : register(u2, space2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'TextureCubeArray' (expected 't')}}
+// NOT YET IMPLEMENTED TextureCubeArray TCubeArray_f2 : register(u2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'TypedBuffer' (expected 't')}}
+// NOT YET IMPLEMENTED TypedBuffer tbuf : register(u2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'RawBuffer' (expected 't')}}
+// NOT YET IMPLEMENTED RawBuffer rbuf : register(u2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 't' for register type 'StructuredBuffer' (expected 'u')}}
+// NOT YET IMPLEMENTED StructuredBuffer ROVStructuredBuff_t2  : register(T2);
+
+// expected-error at +1 {{cbv type 'cbuffer' requires register type 'b', but register type 's' was used}}
+cbuffer f : register(s2, space1) {}
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 't' for register type 'Sampler' (expected 's')}}
+// Can this type just be Sampler instead of SamplerState?
+// NOT YET IMPLEMENTED SamplerState MySampler : register(t3, space1);
+
+// expected-error at +1 {{srv type 'tbuffer' requires register type 't', but register type 's' was used}}
+tbuffer f : register(s2, space1) {}
+
+// NOT YET IMPLEMENTED : RTAccelerationStructure doesn't have any example tests in DXC
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'FeedbackTexture2D' (expected 't')}}
+// NOT YET IMPLEMENTED FeedbackTexture2D<float> FBTex2D[3][] : register(u0, space26);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'FeedbackTexture2DArray' (expected 't')}}
+// NOT YET IMPLEMENTED FeedbackTexture2DArray<float> FBTex2DArr[3][2][] : register(u0, space27);
+
+
+// empty binding prefix cases:
+// expected-error at +1 {{expected identifier}}
+RWBuffer<int> c: register();
+
+// expected-error at +1 {{expected identifier}}
+RWBuffer<int> d: register("");

>From 13efada345d5c1b39ac1139206f2217ed5a19864 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 2 Jul 2024 18:09:06 -0700
Subject: [PATCH 08/26] add udt tests

---
 clang/lib/Sema/SemaHLSL.cpp                   | 133 ++++++++++--------
 ...resource_binding_attr_error_resource.hlsl} |   2 +
 .../resource_binding_attr_error_udt.hlsl      |  89 ++++++++++++
 3 files changed, 163 insertions(+), 61 deletions(-)
 rename clang/test/SemaHLSL/{resource_binding_attr_error_mismatch.hlsl => resource_binding_attr_error_resource.hlsl} (94%)
 create mode 100644 clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index cf4a84506daee..bc4ad01ca151a 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -513,40 +513,47 @@ bool isDeclaredWithinCOrTBuffer(const Decl *decl) {
   return false;
 }
 
+const CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *SamplerUAVOrSRV) {
+  const Type *Ty = SamplerUAVOrSRV->getType()->getPointeeOrArrayElementType();
+  if (!Ty)
+    llvm_unreachable("Resource class must have an element type.");
+
+  if (const BuiltinType *BTy = dyn_cast<BuiltinType>(Ty)) {
+    /* QualType QT = SamplerUAVOrSRV->getType();
+    PrintingPolicy PP = S.getPrintingPolicy();
+    std::string typestr = QualType::getAsString(QT.split(), PP);
+
+    S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_resource_type)
+        << typestr;
+    return; */
+    return nullptr;
+  }
+
+  const CXXRecordDecl *TheRecordDecl = Ty->getAsCXXRecordDecl();
+  if (!TheRecordDecl)
+    llvm_unreachable("Resource class should have a resource type declaration.");
+
+  if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(TheRecordDecl))
+    TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
+  TheRecordDecl = TheRecordDecl->getCanonicalDecl();
+  return TheRecordDecl;
+}
+
 const HLSLResourceAttr *
 getHLSLResourceAttrFromEitherDecl(VarDecl *SamplerUAVOrSRV,
                                   HLSLBufferDecl *CBufferOrTBuffer) {
 
   if (SamplerUAVOrSRV) {
-    const Type *Ty = SamplerUAVOrSRV->getType()->getPointeeOrArrayElementType();
-    if (!Ty)
-      llvm_unreachable("Resource class must have an element type.");
-
-    if (const BuiltinType *BTy = dyn_cast<BuiltinType>(Ty)) {
-      /* QualType QT = SamplerUAVOrSRV->getType();
-      PrintingPolicy PP = S.getPrintingPolicy();
-      std::string typestr = QualType::getAsString(QT.split(), PP);
-
-      S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_resource_type)
-          << typestr;
-      return; */
-      return nullptr;
-    }
-
-    const CXXRecordDecl *TheRecordDecl = Ty->getAsCXXRecordDecl();
-    if (!TheRecordDecl)
-      llvm_unreachable(
-          "Resource class should have a resource type declaration.");
-
-    if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(TheRecordDecl))
-      TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
-    TheRecordDecl = TheRecordDecl->getCanonicalDecl();
+    const CXXRecordDecl *TheRecordDecl =
+        getRecordDeclFromVarDecl(SamplerUAVOrSRV);
     const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
     return Attr;
   } else if (CBufferOrTBuffer) {
     const auto *Attr = CBufferOrTBuffer->getAttr<HLSLResourceAttr>();
     return Attr;
   }
+  llvm_unreachable("one of the two conditions should be true.");
+  return nullptr;
 }
 
 void traverseType(QualType T, register_binding_flags &r) {
@@ -802,46 +809,50 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
   }
 
   // finally, we handle the udt case
-
   if (f.udt) {
-    for (auto it = D->attr_begin(); it != D->attr_end(); ++it) {
-      if (HLSLResourceBindingAttr *attr =
-              dyn_cast<HLSLResourceBindingAttr>(*it)) {
-        llvm::StringRef registerTypeSlotRef = attr->getSlot();
-        std::string registerTypeSlot(registerTypeSlotRef);
-        if (registerTypeSlot.front() == 't') {
-          if (!f.srv) {
-            S.Diag(attr->getLoc(),
-                   diag::warn_hlsl_UDT_missing_resource_type_member)
-                << typestr << "t" << 0;
-          }
-        } else if (registerTypeSlot.front() == 'u') {
-          if (!f.uav) {
-            S.Diag(attr->getLoc(),
-                   diag::warn_hlsl_UDT_missing_resource_type_member)
-                << typestr << "u" << 1;
-          }
-        } else if (registerTypeSlot.front() == 'b') {
-          if (!f.cbv) {
-            S.Diag(attr->getLoc(),
-                   diag::warn_hlsl_UDT_missing_resource_type_member)
-                << typestr << "b" << 2;
-          }
-        } else if (registerTypeSlot.front() == 's') {
-          if (!f.sampler) {
-            S.Diag(attr->getLoc(),
-                   diag::warn_hlsl_UDT_missing_resource_type_member)
-                << typestr << "s" << 3;
-          }
-        } else if (registerTypeSlot.front() == 'c') {
-          if (!f.srv)
-            S.Diag(attr->getLoc(), diag::warn_hlsl_UDT_missing_basic_type);
-        } else {
-          S.Diag(attr->getLoc(),
-                 diag::err_hlsl_unsupported_register_type_and_variable_type)
-              << registerTypeSlot.front() << typestr;
-        }
+    switch (Slot[0]) {
+    case 't': {
+      if (!f.srv) {
+        S.Diag(D->getLocation(),
+               diag::warn_hlsl_UDT_missing_resource_type_member)
+            << typestr << "t" << 0;
+      }
+      break;
+    }
+    case 'u': {
+      if (!f.uav) {
+        S.Diag(D->getLocation(),
+               diag::warn_hlsl_UDT_missing_resource_type_member)
+            << typestr << "u" << 1;
+      }
+      break;
+    }
+    case 'b': {
+      if (!f.cbv) {
+        S.Diag(D->getLocation(),
+               diag::warn_hlsl_UDT_missing_resource_type_member)
+            << typestr << "b" << 2;
       }
+      break;
+    }
+    case 's': {
+      if (!f.sampler) {
+        S.Diag(D->getLocation(),
+               diag::warn_hlsl_UDT_missing_resource_type_member)
+            << typestr << "s" << 3;
+      }
+      break;
+    }
+    case 'c': {
+      if (!f.srv)
+        S.Diag(D->getLocation(), diag::warn_hlsl_UDT_missing_basic_type);
+      break;
+    }
+    default: {
+      S.Diag(D->getLocation(),
+             diag::err_hlsl_unsupported_register_type_and_variable_type)
+          << Slot.front() << typestr;
+    }
     }
   }
 }
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
similarity index 94%
rename from clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl
rename to clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
index 54e852483f832..2be78b4431499 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
@@ -1,5 +1,7 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
 
+// This test validates the diagnostics that are emitted when a variable with a "resource" type
+// is bound to a register using the register annotation
 
 // expected-error at +1  {{uav type 'RWBuffer<int>' requires register type 'u', but register type 'b' was used}}
 RWBuffer<int> a : register(b2, space1);
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
new file mode 100644
index 0000000000000..2f4d20b40a0fd
--- /dev/null
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
@@ -0,0 +1,89 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
+
+// TODO: Implement "Buffer"
+struct Eg1 {
+  float f;
+  // Buffer<float> Buf;
+  RWBuffer<float> RWBuf;
+  };
+Eg1 e1 : /* register(t0) :*/ register(u0); 
+// Valid: f is skipped, Buf is bound to t0, RWBuf is bound to u0
+
+
+struct Eg2 {
+  float f;
+  // Buffer<float> Buf;
+  RWBuffer<float> RWBuf;
+  RWBuffer<float> RWBuf2;
+  };
+Eg2 e2 : /* register(t0) :*/ register(u0); 
+// Valid: f is skipped, Buf is bound to t0, RWBuf is bound to u0. 
+// RWBuf2 gets automatically assigned to u1 even though there is no explicit binding for u1.
+
+/*
+struct Eg3 {
+  float f;
+  // Buffer<float> Buf;
+  }; 
+Eg3 e3 : register(t0) : register(u0);
+// Valid: Buf gets bound to t0. Buf will also be bound to u0.
+*/
+
+struct Eg4 {
+  struct Bar {
+    RWBuffer<int> a;
+    };
+    Bar b;
+};
+Eg4 e4 : register(u0);
+// Valid: Bar, the struct within Eg4, has a valid resource that can be bound to t0. 
+
+/* Light up this test when SamplerState is implemented
+struct Eg5 {
+  SamplerState s[3];
+};
+
+Eg5 e5 : register(s5);
+// Valid: the first sampler state object within Eg5's s is bound to slot 5
+*/
+
+struct Eg6 {
+  float f;
+}; 
+// expected-warning at +1{{variable of type 'Eg6' bound to register type 't' does not contain a matching 'srv' resource}}
+Eg6 e6 : register(t0);
+
+struct Eg7 {
+  struct Bar {
+    float f;
+  };
+  Bar b;
+};
+// expected-warning at +1{{variable of type 'Eg7' bound to register type 't' does not contain a matching 'srv' resource}}
+Eg7 e7 : register(t0);
+
+struct Eg8 {
+  RWBuffer<int> a;
+}; 
+// expected-warning at +1{{register 'c' used on type with no contents to allocate in a constant buffer}}
+Eg8 e8 : register(c0);
+
+
+struct Eg9{
+  // expected-error at +1{{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
+  RWBuffer<int> a : register(u9);
+};
+
+Eg9 e9;
+
+
+/* Light up this test when Texture2D is implemented
+template<typename R>
+struct Eg10 {
+    R b;
+};
+// expecting warning: {{variable of type 'Eg10' bound to register type 'u' does not contain a matching 'uav' resource}}
+Eg10<Texture2D> t : register(u0);
+
+// invalid because after template expansion, there are no valid resources inside Eg10 to bind as a UAV.
+*/ 

>From 9f4b8148d952d8e201bf3f8960b223d90a1a7158 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 2 Jul 2024 19:33:44 -0700
Subject: [PATCH 09/26] add warnings to warning-flags.c file, do a nullptr
 check

---
 clang/lib/Sema/SemaHLSL.cpp     | 2 ++
 clang/test/Misc/warning-flags.c | 5 +++++
 2 files changed, 7 insertions(+)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index bc4ad01ca151a..1851df1e1bbfa 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -546,6 +546,8 @@ getHLSLResourceAttrFromEitherDecl(VarDecl *SamplerUAVOrSRV,
   if (SamplerUAVOrSRV) {
     const CXXRecordDecl *TheRecordDecl =
         getRecordDeclFromVarDecl(SamplerUAVOrSRV);
+    if (!TheRecordDecl)
+      return nullptr;
     const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
     return Attr;
   } else if (CBufferOrTBuffer) {
diff --git a/clang/test/Misc/warning-flags.c b/clang/test/Misc/warning-flags.c
index 7b993f6849363..5933934fdfbae 100644
--- a/clang/test/Misc/warning-flags.c
+++ b/clang/test/Misc/warning-flags.c
@@ -53,6 +53,11 @@ CHECK-NEXT:   warn_fe_backend_unsupported
 CHECK-NEXT:   warn_fe_cc_log_diagnostics_failure
 CHECK-NEXT:   warn_fe_cc_print_header_failure
 CHECK-NEXT:   warn_fe_macro_contains_embedded_newline
+CHECK-NEXT:   warn_hlsl_UDT_missing_basic_type
+CHECK-NEXT:   warn_hlsl_UDT_missing_resource_type_member
+CHECK-NEXT:   warn_hlsl_deprecated_register_type_b
+CHECK-NEXT:   warn_hlsl_deprecated_register_type_i
+CHECK-NEXT:   warn_hlsl_register_type_c_not_in_global_scope
 CHECK-NEXT:   warn_ignoring_ftabstop_value
 CHECK-NEXT:   warn_implements_nscopying
 CHECK-NEXT:   warn_incompatible_qualified_id

>From 8a7878e3f5eac51d8754a3a5013785aaf6387479 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Wed, 3 Jul 2024 01:51:15 -0700
Subject: [PATCH 10/26] clang format

---
 clang/lib/Sema/SemaHLSL.cpp | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 1851df1e1bbfa..988239130ced4 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -682,10 +682,9 @@ static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *D,
                                                 StringRef &Slot) {
   std::set<std::string> s; // store unique register type + numbers
   for (auto it = D->attr_begin(); it != D->attr_end(); ++it) {
-    
     if (HLSLResourceBindingAttr *attr =
             dyn_cast<HLSLResourceBindingAttr>(*it)) {
-      std::string regInfo(Slot);   
+      std::string regInfo(Slot);
       auto p = s.insert(regInfo);
       if (!p.second) {
         S.Diag(attr->getLoc(), diag::err_hlsl_conflicting_register_annotations)
@@ -802,8 +801,8 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
     } else if (Slot[0] == 's') {
       S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
           << 3 << typestr;
-    // any other register type should emit
-    // err_hlsl_unsupported_register_type_and_variable_type
+      // any other register type should emit
+      // err_hlsl_unsupported_register_type_and_variable_type
     } else {
       S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
           << Slot[0] << typestr;

>From d426639640f28a24673544cd3f73c3c7d5c70e7e Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Fri, 5 Jul 2024 13:58:25 -0700
Subject: [PATCH 11/26] allow multiple reg annotations, complete udt tests

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  3 +-
 clang/include/clang/Parse/Parser.h            |  4 +-
 clang/lib/Parse/ParseDecl.cpp                 |  3 +-
 clang/lib/Sema/SemaHLSL.cpp                   | 38 +++++++++++++++----
 .../resource_binding_attr_error_udt.hlsl      | 17 ++++++++-
 5 files changed, 53 insertions(+), 12 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index f7033ca867489..1aeeec6b191ce 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12317,7 +12317,7 @@ def err_hlsl_mismatching_register_type_and_variable_type: Error<"unsupported res
 def err_hlsl_unsupported_register_type_and_variable_type: Error<"register binding type '%1' not supported for variable of type '%0'">;
 def err_hlsl_mismatching_register_type_and_resource_type: Error<"%select{srv|uav|cbv|sampler}2 type '%0' requires register type '%select{t|u|b|s}2', but register type '%1' was used">;
 def err_hlsl_unsupported_register_type_and_resource_type: Error<"invalid register type '%0' used; expected 't', 'u', 'b', or 's'">;
-def err_hlsl_conflicting_register_annotations: Error<"conflicting register annotations: %0 and %0">;
+def err_hlsl_conflicting_register_annotations: Error<"conflicting register annotations: multiple register numbers detected for register type '%0'">;
 def warn_hlsl_deprecated_register_type_b: Warning<"deprecated legacy bool constant register binding 'b' used. 'b' is only used for constant buffer resource binding">;
 def warn_hlsl_deprecated_register_type_i: Warning<"deprecated legacy int constant register binding 'i' used">;
 def warn_hlsl_register_type_c_not_in_global_scope: Warning<"register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead">;
@@ -12325,7 +12325,6 @@ def warn_hlsl_UDT_missing_resource_type_member: Warning<"variable of type '%0' b
 def warn_hlsl_UDT_missing_basic_type: Warning<"register 'c' used on type with no contents to allocate in a constant buffer">;
 def err_hlsl_mismatching_register_resource_type_and_name: Error<"invalid register name prefix '%0' for register resource type '%1' (expected %select{'t'|'u'|'b'|'s'}2)">;
 def err_hlsl_mismatching_register_builtin_type_and_name: Error<"invalid register name prefix '%0' for '%1' (expected %2)">;
-def err_hlsl_unsupported_register_prefix : Error<"invalid resource class specifier '%0' used; expected 't', 'u', 'b', or 's'">;
 def err_hlsl_unsupported_register_resource_type : Error<"invalid resource '%0' used">;
 def err_hlsl_unsupported_register_number : Error<"register number should be an integer">;
 def err_hlsl_expected_space : Error<"invalid space specifier '%0' used; expected 'space' followed by an integer, like space1">;
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index b4f5270a35956..42d8a4850341c 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3016,7 +3016,7 @@ class Parser : public CodeCompletionHandler {
           SemaCodeCompletion::AttributeCompletion::None,
       const IdentifierInfo *EnclosingScope = nullptr);
 
-  void MaybeParseHLSLAnnotations(Declarator &D,
+  bool MaybeParseHLSLAnnotations(Declarator &D,
                                  SourceLocation *EndLoc = nullptr,
                                  bool CouldBeBitField = false) {
     assert(getLangOpts().HLSL && "MaybeParseHLSLAnnotations is for HLSL only");
@@ -3024,7 +3024,9 @@ class Parser : public CodeCompletionHandler {
       ParsedAttributes Attrs(AttrFactory);
       ParseHLSLAnnotations(Attrs, EndLoc, CouldBeBitField);
       D.takeAttributes(Attrs);
+      return true;
     }
+    return false;
   }
 
   void MaybeParseHLSLAnnotations(ParsedAttributes &Attrs,
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 7ce9a9cea1c7a..54d9e8706db8d 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2319,7 +2319,8 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
   }
 
   if (getLangOpts().HLSL)
-    MaybeParseHLSLAnnotations(D);
+    while (MaybeParseHLSLAnnotations(D))
+      ;
 
   if (Tok.is(tok::kw_requires))
     ParseTrailingRequiresClause(D);
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 988239130ced4..e695b55b3f7ef 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -680,18 +680,38 @@ register_binding_flags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
 
 static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *D,
                                                 StringRef &Slot) {
-  std::set<std::string> s; // store unique register type + numbers
+  // make sure that there are no register annotations applied to the decl
+  // with the same register type but different numbers
+  std::unordered_map<char, std::set<char>>
+      s; // store unique register type + numbers
+  std::set<char> starting_set = {Slot[1]};
+  s.insert(std::make_pair(Slot[0], starting_set));
   for (auto it = D->attr_begin(); it != D->attr_end(); ++it) {
     if (HLSLResourceBindingAttr *attr =
             dyn_cast<HLSLResourceBindingAttr>(*it)) {
-      std::string regInfo(Slot);
-      auto p = s.insert(regInfo);
-      if (!p.second) {
-        S.Diag(attr->getLoc(), diag::err_hlsl_conflicting_register_annotations)
-            << Slot.substr(0, 1);
+      std::string otherSlot(attr->getSlot().data());
+
+      // insert into hash map
+      if (s.find(otherSlot[0]) != s.end()) {
+        // if the register type is already in the map, insert the number
+        // into the set (if it's not already there
+        s[otherSlot[0]].insert(otherSlot[1]);
+      } else {
+        // if the register type is not in the map, insert it with the number
+        std::set<char> otherSet;
+        otherSet.insert(otherSlot[1]);
+        s.insert(std::make_pair(otherSlot[0], otherSet));
       }
     }
   }
+
+  for (auto regType : s) {
+    if (regType.second.size() > 1) {
+      std::string regTypeStr(1, regType.first);
+      S.Diag(D->getLocation(), diag::err_hlsl_conflicting_register_annotations)
+          << regTypeStr;
+    }
+  }
 }
 
 static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
@@ -727,6 +747,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
   if (f.other) {
     S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
         << Slot << typestr;
+    return;
   }
 
   // next, if multiple register annotations exist, check that none conflict.
@@ -776,6 +797,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
       break;
     }
     }
+    return;
   }
 
   // next, handle diagnostics for when the "basic" flag is set,
@@ -807,6 +829,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
       S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
           << Slot[0] << typestr;
     }
+    return;
   }
 
   // finally, we handle the udt case
@@ -855,6 +878,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
           << Slot.front() << typestr;
     }
     }
+    return;
   }
 }
 
@@ -905,7 +929,7 @@ void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) {
     case 'i':
       break;
     default:
-      Diag(ArgLoc, diag::err_hlsl_unsupported_register_prefix)
+      Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_resource_type)
           << Slot.substr(0, 1);
       return;
     }
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
index 2f4d20b40a0fd..28a747b965b22 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
@@ -83,7 +83,22 @@ struct Eg10 {
     R b;
 };
 // expecting warning: {{variable of type 'Eg10' bound to register type 'u' does not contain a matching 'uav' resource}}
-Eg10<Texture2D> t : register(u0);
+Eg10<Texture2D> e10 : register(u0);
 
 // invalid because after template expansion, there are no valid resources inside Eg10 to bind as a UAV.
 */ 
+
+struct Eg11{
+  RWBuffer<int> a;
+  RWBuffer<int> b;
+};
+
+// expected-error at +1{{conflicting register annotations: multiple register numbers detected for register type 'u'}}
+Eg11 e11 : register(u9) : register(u10);
+
+struct Eg12{
+  RWBuffer<int> a;  
+};
+
+// expected-warning at +1{{register 'c' used on type with no contents to allocate in a constant buffer}}
+Eg12 e12 : register(c9);
\ No newline at end of file

>From d51b71b82f9eb4c060d76849725a0a4c958545fb Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Fri, 5 Jul 2024 17:01:17 -0700
Subject: [PATCH 12/26] add other test, xfail it

---
 clang/include/clang/Basic/DiagnosticGroups.td |  3 ++
 .../clang/Basic/DiagnosticSemaKinds.td        |  8 ++---
 clang/lib/Sema/SemaHLSL.cpp                   |  6 ++--
 .../resource_binding_attr_error_basic.hlsl    | 31 +++++++++++++++++++
 .../resource_binding_attr_error_other.hlsl    |  9 ++++++
 5 files changed, 50 insertions(+), 7 deletions(-)
 create mode 100644 clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
 create mode 100644 clang/test/SemaHLSL/resource_binding_attr_error_other.hlsl

diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 2241f8481484e..8c26a76f9e178 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1543,6 +1543,9 @@ def DXILValidation : DiagGroup<"dxil-validation">;
 // Warning for HLSL API availability
 def HLSLAvailability : DiagGroup<"hlsl-availability">;
 
+// Warnings for legacy binding behavior
+def DisallowLegacyBindingRules : DiagGroup<"disallow-legacy-binding-rules">;
+
 // Warnings and notes related to const_var_decl_type attribute checks
 def ReadOnlyPlacementChecks : DiagGroup<"read-only-types">;
 
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 1aeeec6b191ce..d34c5773afec3 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12313,14 +12313,14 @@ def err_hlsl_missing_semantic_annotation : Error<
 def err_hlsl_init_priority_unsupported : Error<
   "initializer priorities are not supported in HLSL">;
 
-def err_hlsl_mismatching_register_type_and_variable_type: Error<"unsupported resource register binding '%select{t|u|b|s}1' on variable of type '%0'">;
+def err_hlsl_mismatching_register_type_and_variable_type: Error<"unsupported resource register binding '%select{t|u|b|s}0' on variable of type '%1'">;
 def err_hlsl_unsupported_register_type_and_variable_type: Error<"register binding type '%1' not supported for variable of type '%0'">;
 def err_hlsl_mismatching_register_type_and_resource_type: Error<"%select{srv|uav|cbv|sampler}2 type '%0' requires register type '%select{t|u|b|s}2', but register type '%1' was used">;
 def err_hlsl_unsupported_register_type_and_resource_type: Error<"invalid register type '%0' used; expected 't', 'u', 'b', or 's'">;
 def err_hlsl_conflicting_register_annotations: Error<"conflicting register annotations: multiple register numbers detected for register type '%0'">;
-def warn_hlsl_deprecated_register_type_b: Warning<"deprecated legacy bool constant register binding 'b' used. 'b' is only used for constant buffer resource binding">;
-def warn_hlsl_deprecated_register_type_i: Warning<"deprecated legacy int constant register binding 'i' used">;
-def warn_hlsl_register_type_c_not_in_global_scope: Warning<"register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead">;
+def warn_hlsl_deprecated_register_type_b: Warning<"deprecated legacy bool constant register binding 'b' used. 'b' is only used for constant buffer resource binding">, InGroup<DisallowLegacyBindingRules>, DefaultError;
+def warn_hlsl_deprecated_register_type_i: Warning<"deprecated legacy int constant register binding 'i' used">, InGroup<DisallowLegacyBindingRules>, DefaultError;
+def warn_hlsl_register_type_c_not_in_global_scope: Warning<"register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead">, InGroup<DisallowLegacyBindingRules>, DefaultError;
 def warn_hlsl_UDT_missing_resource_type_member: Warning<"variable of type '%0' bound to register type '%1' does not contain a matching '%select{srv|uav|cbv|sampler}2' resource">;
 def warn_hlsl_UDT_missing_basic_type: Warning<"register 'c' used on type with no contents to allocate in a constant buffer">;
 def err_hlsl_mismatching_register_resource_type_and_name: Error<"invalid register name prefix '%0' for register resource type '%1' (expected %select{'t'|'u'|'b'|'s'}2)">;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index e695b55b3f7ef..3392e068ec427 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -810,7 +810,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
         S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_i);
     }
 
-    else if (Slot[0] == 'c') {
+    if (Slot[0] == 'c') {
       if (!f.default_globals) {
         S.Diag(ArgLoc, diag::warn_hlsl_register_type_c_not_in_global_scope);
       }
@@ -825,9 +825,9 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
           << 3 << typestr;
       // any other register type should emit
       // err_hlsl_unsupported_register_type_and_variable_type
-    } else {
+    } else if (!f.default_globals){
       S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
-          << Slot[0] << typestr;
+          << registerType << typestr;
     }
     return;
   }
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
new file mode 100644
index 0000000000000..64cff79274edd
--- /dev/null
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
+
+// expected-error at +1{{unsupported resource register binding 't' on variable of type 'float'}}
+float f1 : register(t9);
+
+// expected-error at +1{{deprecated legacy bool constant register binding 'b' used. 'b' is only used for constant buffer resource binding}}
+float f2 : register(b9);
+
+// expected-error at +1{{deprecated legacy int constant register binding 'i' used}}
+float f3 : register(i9);
+
+
+cbuffer g_cbuffer1 {
+// expected-error at +1{{register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead}}
+    float f4 : register(c2);
+};
+
+tbuffer g_tbuffer1 {
+// expected-error at +1{{register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead}}
+    float f5 : register(c2);
+};
+
+tbuffer g_cbuffer2 {
+// expected-error at +1{{register binding type 'float' not supported for variable of type 'b'}}
+    float f6 : register(b2);
+};
+
+tbuffer g_tbuffer2 {
+// expected-error at +1{{register binding type 'float' not supported for variable of type 'i'}}
+    float f7 : register(i2);
+};
\ No newline at end of file
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_other.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_other.hlsl
new file mode 100644
index 0000000000000..4c9e9a6b44c92
--- /dev/null
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_other.hlsl
@@ -0,0 +1,9 @@
+// RUN: not %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s  | FileCheck %s
+
+// XFAIL: *
+// This expectedly fails because RayQuery is an unsupported type.
+// When it becomes supported, we should expect an error due to 
+// the variable type being classified as "other", and according
+// to the spec, err_hlsl_unsupported_register_type_and_variable_type
+// should be emitted.
+RayQuery<0> r1: register(t0);

>From 5744663d95e3aed52c8f799c6a42532475166fbe Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Sun, 7 Jul 2024 00:26:38 -0700
Subject: [PATCH 13/26] fix warning flag errs

---
 clang/include/clang/Basic/DiagnosticSemaKinds.td     | 4 ++--
 clang/lib/Sema/SemaHLSL.cpp                          | 2 +-
 clang/test/Misc/warning-flags.c                      | 5 -----
 clang/test/SemaHLSL/resource_binding_attr_error.hlsl | 2 +-
 4 files changed, 4 insertions(+), 9 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d34c5773afec3..aff21f599a0ed 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12321,8 +12321,8 @@ def err_hlsl_conflicting_register_annotations: Error<"conflicting register annot
 def warn_hlsl_deprecated_register_type_b: Warning<"deprecated legacy bool constant register binding 'b' used. 'b' is only used for constant buffer resource binding">, InGroup<DisallowLegacyBindingRules>, DefaultError;
 def warn_hlsl_deprecated_register_type_i: Warning<"deprecated legacy int constant register binding 'i' used">, InGroup<DisallowLegacyBindingRules>, DefaultError;
 def warn_hlsl_register_type_c_not_in_global_scope: Warning<"register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead">, InGroup<DisallowLegacyBindingRules>, DefaultError;
-def warn_hlsl_UDT_missing_resource_type_member: Warning<"variable of type '%0' bound to register type '%1' does not contain a matching '%select{srv|uav|cbv|sampler}2' resource">;
-def warn_hlsl_UDT_missing_basic_type: Warning<"register 'c' used on type with no contents to allocate in a constant buffer">;
+def warn_hlsl_UDT_missing_resource_type_member: Warning<"variable of type '%0' bound to register type '%1' does not contain a matching '%select{srv|uav|cbv|sampler}2' resource">, InGroup<DisallowLegacyBindingRules>;
+def warn_hlsl_UDT_missing_basic_type: Warning<"register 'c' used on type with no contents to allocate in a constant buffer">, InGroup<DisallowLegacyBindingRules>;
 def err_hlsl_mismatching_register_resource_type_and_name: Error<"invalid register name prefix '%0' for register resource type '%1' (expected %select{'t'|'u'|'b'|'s'}2)">;
 def err_hlsl_mismatching_register_builtin_type_and_name: Error<"invalid register name prefix '%0' for '%1' (expected %2)">;
 def err_hlsl_unsupported_register_resource_type : Error<"invalid resource '%0' used">;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 3392e068ec427..94770093061fd 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -825,7 +825,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
           << 3 << typestr;
       // any other register type should emit
       // err_hlsl_unsupported_register_type_and_variable_type
-    } else if (!f.default_globals){
+    } else if (!f.default_globals) {
       S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
           << registerType << typestr;
     }
diff --git a/clang/test/Misc/warning-flags.c b/clang/test/Misc/warning-flags.c
index 5933934fdfbae..7b993f6849363 100644
--- a/clang/test/Misc/warning-flags.c
+++ b/clang/test/Misc/warning-flags.c
@@ -53,11 +53,6 @@ CHECK-NEXT:   warn_fe_backend_unsupported
 CHECK-NEXT:   warn_fe_cc_log_diagnostics_failure
 CHECK-NEXT:   warn_fe_cc_print_header_failure
 CHECK-NEXT:   warn_fe_macro_contains_embedded_newline
-CHECK-NEXT:   warn_hlsl_UDT_missing_basic_type
-CHECK-NEXT:   warn_hlsl_UDT_missing_resource_type_member
-CHECK-NEXT:   warn_hlsl_deprecated_register_type_b
-CHECK-NEXT:   warn_hlsl_deprecated_register_type_i
-CHECK-NEXT:   warn_hlsl_register_type_c_not_in_global_scope
 CHECK-NEXT:   warn_ignoring_ftabstop_value
 CHECK-NEXT:   warn_implements_nscopying
 CHECK-NEXT:   warn_incompatible_qualified_id
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
index ce81574a79608..95de3ad55af54 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
@@ -46,7 +46,7 @@ void foo2() {
   extern RWBuffer<float> U2 : register(u5);
 }
 
-// FIXME: emit a diagnostic because float doesn't match the 'u' register type
+// expected-error at +1 {{unsupported resource register binding 'u' on variable of type 'float'}}
 float b : register(u0, space1);
 
 // expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}

>From 97b739d9a148a81a92b4d08bc6a1843d33b1a418 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 8 Jul 2024 10:21:25 -0700
Subject: [PATCH 14/26] clean up unused diags, remove comment

---
 clang/include/clang/Basic/DiagnosticSemaKinds.td | 3 ---
 clang/lib/Sema/SemaHLSL.cpp                      | 7 -------
 2 files changed, 10 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index aff21f599a0ed..64a7ac4bef47a 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12323,9 +12323,6 @@ def warn_hlsl_deprecated_register_type_i: Warning<"deprecated legacy int constan
 def warn_hlsl_register_type_c_not_in_global_scope: Warning<"register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead">, InGroup<DisallowLegacyBindingRules>, DefaultError;
 def warn_hlsl_UDT_missing_resource_type_member: Warning<"variable of type '%0' bound to register type '%1' does not contain a matching '%select{srv|uav|cbv|sampler}2' resource">, InGroup<DisallowLegacyBindingRules>;
 def warn_hlsl_UDT_missing_basic_type: Warning<"register 'c' used on type with no contents to allocate in a constant buffer">, InGroup<DisallowLegacyBindingRules>;
-def err_hlsl_mismatching_register_resource_type_and_name: Error<"invalid register name prefix '%0' for register resource type '%1' (expected %select{'t'|'u'|'b'|'s'}2)">;
-def err_hlsl_mismatching_register_builtin_type_and_name: Error<"invalid register name prefix '%0' for '%1' (expected %2)">;
-def err_hlsl_unsupported_register_resource_type : Error<"invalid resource '%0' used">;
 def err_hlsl_unsupported_register_number : Error<"register number should be an integer">;
 def err_hlsl_expected_space : Error<"invalid space specifier '%0' used; expected 'space' followed by an integer, like space1">;
 def warn_hlsl_packoffset_mix : Warning<"cannot mix packoffset elements with nonpackoffset elements in a cbuffer">,
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 94770093061fd..1447639e64b46 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -519,13 +519,6 @@ const CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *SamplerUAVOrSRV) {
     llvm_unreachable("Resource class must have an element type.");
 
   if (const BuiltinType *BTy = dyn_cast<BuiltinType>(Ty)) {
-    /* QualType QT = SamplerUAVOrSRV->getType();
-    PrintingPolicy PP = S.getPrintingPolicy();
-    std::string typestr = QualType::getAsString(QT.split(), PP);
-
-    S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_resource_type)
-        << typestr;
-    return; */
     return nullptr;
   }
 

>From d81e5f8cf510358f8d89f520899961c2edf51e6a Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 8 Jul 2024 17:32:54 -0700
Subject: [PATCH 15/26] address Damyan

---
 clang/lib/Sema/SemaHLSL.cpp                   | 214 +++++++++---------
 .../resource_binding_attr_error_udt.hlsl      |   2 +-
 2 files changed, 106 insertions(+), 110 deletions(-)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 1447639e64b46..40399d7974568 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -29,37 +29,30 @@ using namespace clang;
 
 SemaHLSL::SemaHLSL(Sema &S) : SemaBase(S) {}
 
-HLSLResourceAttr *SemaHLSL::mergeHLSLResourceAttr(bool CBuffer) {
-  // cbuffer case
+Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
+                                 SourceLocation KwLoc, IdentifierInfo *Ident,
+                                 SourceLocation IdentLoc,
+                                 SourceLocation LBrace) {
+  // For anonymous namespace, take the location of the left brace.
+  DeclContext *LexicalParent = SemaRef.getCurLexicalContext();
+  HLSLBufferDecl *Result = HLSLBufferDecl::Create(
+      getASTContext(), LexicalParent, CBuffer, KwLoc, Ident, IdentLoc, LBrace);
+
+  HLSLResourceAttr *NewAttr;
   if (CBuffer) {
-    HLSLResourceAttr *attr = HLSLResourceAttr::CreateImplicit(
+    NewAttr = HLSLResourceAttr::CreateImplicit(
         getASTContext(), llvm::hlsl::ResourceClass::CBuffer,
         llvm::hlsl::ResourceKind::CBuffer,
         /*IsROV=*/false);
-    return attr;
   }
   // tbuffer case
   else {
-    HLSLResourceAttr *attr = HLSLResourceAttr::CreateImplicit(
+    NewAttr = HLSLResourceAttr::CreateImplicit(
         getASTContext(), llvm::hlsl::ResourceClass::SRV,
         llvm::hlsl::ResourceKind::TBuffer,
         /*IsROV=*/false);
-    return attr;
   }
-}
-
-Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
-                                 SourceLocation KwLoc, IdentifierInfo *Ident,
-                                 SourceLocation IdentLoc,
-                                 SourceLocation LBrace) {
-  // For anonymous namespace, take the location of the left brace.
-  DeclContext *LexicalParent = SemaRef.getCurLexicalContext();
-  HLSLBufferDecl *Result = HLSLBufferDecl::Create(
-      getASTContext(), LexicalParent, CBuffer, KwLoc, Ident, IdentLoc, LBrace);
-
-  HLSLResourceAttr *NewAttr = mergeHLSLResourceAttr(CBuffer);
-  if (NewAttr)
-    Result->addAttr(NewAttr);
+  Result->addAttr(NewAttr);
 
   SemaRef.PushOnScopeChains(Result, BufferScope);
   SemaRef.PushDeclContext(BufferScope, Result);
@@ -482,19 +475,19 @@ void SemaHLSL::handleResourceClassAttr(Decl *D, const ParsedAttr &AL) {
   D->addAttr(HLSLResourceClassAttr::Create(getASTContext(), RC, ArgLoc));
 }
 
-struct register_binding_flags {
-  bool resource = false;
-  bool udt = false;
-  bool other = false;
-  bool basic = false;
+struct RegisterBindingFlags {
+  bool Resource = false;
+  bool Udt = false;
+  bool Other = false;
+  bool Basic = false;
 
-  bool srv = false;
-  bool uav = false;
-  bool cbv = false;
-  bool sampler = false;
+  bool Srv = false;
+  bool Uav = false;
+  bool Cbv = false;
+  bool Sampler = false;
 
-  bool contains_numeric = false;
-  bool default_globals = false;
+  bool ContainsNumeric = false;
+  bool DefaultGlobals = false;
 };
 
 bool isDeclaredWithinCOrTBuffer(const Decl *decl) {
@@ -513,8 +506,8 @@ bool isDeclaredWithinCOrTBuffer(const Decl *decl) {
   return false;
 }
 
-const CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *SamplerUAVOrSRV) {
-  const Type *Ty = SamplerUAVOrSRV->getType()->getPointeeOrArrayElementType();
+const CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *VD) {
+  const Type *Ty = VD->getType()->getPointeeOrArrayElementType();
   if (!Ty)
     llvm_unreachable("Resource class must have an element type.");
 
@@ -533,12 +526,11 @@ const CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *SamplerUAVOrSRV) {
 }
 
 const HLSLResourceAttr *
-getHLSLResourceAttrFromEitherDecl(VarDecl *SamplerUAVOrSRV,
+getHLSLResourceAttrFromEitherDecl(VarDecl *VD,
                                   HLSLBufferDecl *CBufferOrTBuffer) {
 
-  if (SamplerUAVOrSRV) {
-    const CXXRecordDecl *TheRecordDecl =
-        getRecordDeclFromVarDecl(SamplerUAVOrSRV);
+  if (VD) {
+    const CXXRecordDecl *TheRecordDecl = getRecordDeclFromVarDecl(VD);
     if (!TheRecordDecl)
       return nullptr;
     const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
@@ -551,9 +543,9 @@ getHLSLResourceAttrFromEitherDecl(VarDecl *SamplerUAVOrSRV,
   return nullptr;
 }
 
-void traverseType(QualType T, register_binding_flags &r) {
+void traverseType(QualType T, RegisterBindingFlags &r) {
   if (T->isIntegralOrEnumerationType() || T->isFloatingType()) {
-    r.contains_numeric = true;
+    r.ContainsNumeric = true;
     return;
   } else if (const RecordType *RT = T->getAs<RecordType>()) {
     RecordDecl *SubRD = RT->getDecl();
@@ -564,19 +556,19 @@ void traverseType(QualType T, register_binding_flags &r) {
       llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
       switch (DeclResourceClass) {
       case llvm::hlsl::ResourceClass::SRV: {
-        r.srv = true;
+        r.Srv = true;
         break;
       }
       case llvm::hlsl::ResourceClass::UAV: {
-        r.uav = true;
+        r.Uav = true;
         break;
       }
       case llvm::hlsl::ResourceClass::CBuffer: {
-        r.cbv = true;
+        r.Cbv = true;
         break;
       }
       case llvm::hlsl::ResourceClass::Sampler: {
-        r.sampler = true;
+        r.Sampler = true;
         break;
       }
       }
@@ -591,7 +583,7 @@ void traverseType(QualType T, register_binding_flags &r) {
   }
 }
 
-void setResourceClassFlagsFromRecordDecl(register_binding_flags &r,
+void setResourceClassFlagsFromRecordDecl(RegisterBindingFlags &r,
                                          const RecordDecl *RD) {
   if (!RD)
     return;
@@ -604,66 +596,66 @@ void setResourceClassFlagsFromRecordDecl(register_binding_flags &r,
   }
 }
 
-register_binding_flags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
-  register_binding_flags r;
+RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
+  RegisterBindingFlags r;
   if (!isDeclaredWithinCOrTBuffer(D)) {
     // make sure the type is a basic / numeric type
     if (VarDecl *v = dyn_cast<VarDecl>(D)) {
       QualType t = v->getType();
       // a numeric variable will inevitably end up in $Globals buffer
       if (t->isIntegralType(S.getASTContext()) || t->isFloatingType())
-        r.default_globals = true;
+        r.DefaultGlobals = true;
     }
   }
   // Cbuffers and Tbuffers are HLSLBufferDecl types
   HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(D);
   // Samplers, UAVs, and SRVs are VarDecl types
-  VarDecl *SamplerUAVOrSRV = dyn_cast<VarDecl>(D);
+  VarDecl *VD = dyn_cast<VarDecl>(D);
 
   if (CBufferOrTBuffer) {
-    r.resource = true;
+    r.Resource = true;
     if (CBufferOrTBuffer->isCBuffer())
-      r.cbv = true;
+      r.Cbv = true;
     else
-      r.srv = true;
-  } else if (SamplerUAVOrSRV) {
+      r.Srv = true;
+  } else if (VD) {
     const HLSLResourceAttr *res_attr =
-        getHLSLResourceAttrFromEitherDecl(SamplerUAVOrSRV, CBufferOrTBuffer);
+        getHLSLResourceAttrFromEitherDecl(VD, CBufferOrTBuffer);
     if (res_attr) {
       llvm::hlsl::ResourceClass DeclResourceClass =
           res_attr->getResourceClass();
-      r.resource = true;
+      r.Resource = true;
       switch (DeclResourceClass) {
       case llvm::hlsl::ResourceClass::SRV: {
-        r.srv = true;
+        r.Srv = true;
         break;
       }
       case llvm::hlsl::ResourceClass::UAV: {
-        r.uav = true;
+        r.Uav = true;
         break;
       }
       case llvm::hlsl::ResourceClass::CBuffer: {
-        r.cbv = true;
+        r.Cbv = true;
         break;
       }
       case llvm::hlsl::ResourceClass::Sampler: {
-        r.sampler = true;
+        r.Sampler = true;
         break;
       }
       }
     } else {
-      if (SamplerUAVOrSRV->getType()->isBuiltinType())
-        r.basic = true;
-      else if (SamplerUAVOrSRV->getType()->isAggregateType()) {
-        r.udt = true;
-        QualType VarType = SamplerUAVOrSRV->getType();
+      if (VD->getType()->isBuiltinType())
+        r.Basic = true;
+      else if (VD->getType()->isAggregateType()) {
+        r.Udt = true;
+        QualType VarType = VD->getType();
         if (const RecordType *RT = VarType->getAs<RecordType>()) {
           const RecordDecl *RD = RT->getDecl();
           // recurse through members, set appropriate resource class flags.
           setResourceClassFlagsFromRecordDecl(r, RD);
         }
       } else
-        r.other = true;
+        r.Other = true;
     }
   } else {
     llvm_unreachable("unknown decl type");
@@ -707,39 +699,43 @@ static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *D,
   }
 }
 
+std::string getHLSLResourceTypeStr(Sema &S, Decl *D) {
+  VarDecl *VD = dyn_cast<VarDecl>(D);
+  HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(D);
+
+  if (VD) {
+    QualType QT = VD->getType();
+    PrintingPolicy PP = S.getPrintingPolicy();
+    return QualType::getAsString(QT.split(), PP);
+  } else {
+    return CBufferOrTBuffer->isCBuffer() ? "cbuffer" : "tbuffer";
+  }
+}
+
 static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
                                         Decl *D, StringRef &Slot) {
 
   // Samplers, UAVs, and SRVs are VarDecl types
-  VarDecl *SamplerUAVOrSRV = dyn_cast<VarDecl>(D);
+  VarDecl *VD = dyn_cast<VarDecl>(D);
   // Cbuffers and Tbuffers are HLSLBufferDecl types
   HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(D);
 
   // exactly one of these two types should be set
-  if (!SamplerUAVOrSRV && !CBufferOrTBuffer)
+  if (!VD && !CBufferOrTBuffer)
     return;
-  if (SamplerUAVOrSRV && CBufferOrTBuffer)
+  if (VD && CBufferOrTBuffer)
     return;
 
-  register_binding_flags f = HLSLFillRegisterBindingFlags(S, D);
-  assert((int)f.other + (int)f.resource + (int)f.basic + (int)f.udt == 1 &&
+  RegisterBindingFlags f = HLSLFillRegisterBindingFlags(S, D);
+  assert((int)f.Other + (int)f.Resource + (int)f.Basic + (int)f.Udt == 1 &&
          "only one resource analysis result should be expected");
 
-  // get the variable type
-  std::string typestr;
-  if (SamplerUAVOrSRV) {
-    QualType QT = SamplerUAVOrSRV->getType();
-    PrintingPolicy PP = S.getPrintingPolicy();
-    typestr = QualType::getAsString(QT.split(), PP);
-  } else
-    typestr = CBufferOrTBuffer->isCBuffer() ? "cbuffer" : "tbuffer";
-
   std::string registerType(Slot.substr(0, 1));
 
   // first, if "other" is set, emit an error
-  if (f.other) {
+  if (f.Other) {
     S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
-        << Slot << typestr;
+        << Slot << getHLSLResourceTypeStr(S, D);
     return;
   }
 
@@ -748,9 +744,9 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
 
   // next, if resource is set, make sure the register type in the register
   // annotation is compatible with the variable's resource type.
-  if (f.resource) {
+  if (f.Resource) {
     const HLSLResourceAttr *res_attr =
-        getHLSLResourceAttrFromEitherDecl(SamplerUAVOrSRV, CBufferOrTBuffer);
+        getHLSLResourceAttrFromEitherDecl(VD, CBufferOrTBuffer);
     assert(res_attr && "any decl that set the resource flag on analysis should "
                        "have a resource attribute attached.");
     const llvm::hlsl::ResourceClass DeclResourceClass =
@@ -758,34 +754,34 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
 
     switch (DeclResourceClass) {
     case llvm::hlsl::ResourceClass::SRV: {
-      if (registerType != "t") {
+      if (Slot[0] != 't') {
         S.Diag(D->getLocation(),
                diag::err_hlsl_mismatching_register_type_and_resource_type)
-            << typestr << registerType << 0 /*srv*/;
+            << getHLSLResourceTypeStr(S, D) << registerType << 0 /*srv*/;
       }
       break;
     }
     case llvm::hlsl::ResourceClass::UAV: {
-      if (registerType != "u") {
+      if (Slot[0] != 'u') {
         S.Diag(D->getLocation(),
                diag::err_hlsl_mismatching_register_type_and_resource_type)
-            << typestr << registerType << 1 /*uav*/;
+            << getHLSLResourceTypeStr(S, D) << registerType << 1 /*uav*/;
       }
       break;
     }
     case llvm::hlsl::ResourceClass::CBuffer: {
-      if (registerType != "b") {
+      if (Slot[0] != 'b') {
         S.Diag(D->getLocation(),
                diag::err_hlsl_mismatching_register_type_and_resource_type)
-            << typestr << registerType << 2 /*cbv*/;
+            << getHLSLResourceTypeStr(S, D) << registerType << 2 /*cbv*/;
       }
       break;
     }
     case llvm::hlsl::ResourceClass::Sampler: {
-      if (registerType != "s") {
+      if (Slot[0] != 's') {
         S.Diag(D->getLocation(),
                diag::err_hlsl_mismatching_register_type_and_resource_type)
-            << typestr << registerType << 3 /*sampler*/;
+            << getHLSLResourceTypeStr(S, D) << registerType << 3 /*sampler*/;
       }
       break;
     }
@@ -795,8 +791,8 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
 
   // next, handle diagnostics for when the "basic" flag is set,
   // including the legacy "i" and "b" register types.
-  if (f.basic) {
-    if (f.default_globals) {
+  if (f.Basic) {
+    if (f.DefaultGlobals) {
       if (Slot[0] == 'b')
         S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_b);
       if (Slot[0] == 'i')
@@ -804,71 +800,71 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
     }
 
     if (Slot[0] == 'c') {
-      if (!f.default_globals) {
+      if (!f.DefaultGlobals) {
         S.Diag(ArgLoc, diag::warn_hlsl_register_type_c_not_in_global_scope);
       }
     } else if (Slot[0] == 't') {
       S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
-          << 0 << typestr;
+          << 0 << getHLSLResourceTypeStr(S, D);
     } else if (Slot[0] == 'u') {
       S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
-          << 1 << typestr;
+          << 1 << getHLSLResourceTypeStr(S, D);
     } else if (Slot[0] == 's') {
       S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
-          << 3 << typestr;
+          << 3 << getHLSLResourceTypeStr(S, D);
       // any other register type should emit
       // err_hlsl_unsupported_register_type_and_variable_type
-    } else if (!f.default_globals) {
+    } else if (!f.DefaultGlobals) {
       S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
-          << registerType << typestr;
+          << registerType << getHLSLResourceTypeStr(S, D);
     }
     return;
   }
 
   // finally, we handle the udt case
-  if (f.udt) {
+  if (f.Udt) {
     switch (Slot[0]) {
     case 't': {
-      if (!f.srv) {
+      if (!f.Srv) {
         S.Diag(D->getLocation(),
                diag::warn_hlsl_UDT_missing_resource_type_member)
-            << typestr << "t" << 0;
+            << getHLSLResourceTypeStr(S, D) << "t" << 0;
       }
       break;
     }
     case 'u': {
-      if (!f.uav) {
+      if (!f.Uav) {
         S.Diag(D->getLocation(),
                diag::warn_hlsl_UDT_missing_resource_type_member)
-            << typestr << "u" << 1;
+            << getHLSLResourceTypeStr(S, D) << "u" << 1;
       }
       break;
     }
     case 'b': {
-      if (!f.cbv) {
+      if (!f.Cbv) {
         S.Diag(D->getLocation(),
                diag::warn_hlsl_UDT_missing_resource_type_member)
-            << typestr << "b" << 2;
+            << getHLSLResourceTypeStr(S, D) << "b" << 2;
       }
       break;
     }
     case 's': {
-      if (!f.sampler) {
+      if (!f.Sampler) {
         S.Diag(D->getLocation(),
                diag::warn_hlsl_UDT_missing_resource_type_member)
-            << typestr << "s" << 3;
+            << getHLSLResourceTypeStr(S, D) << "s" << 3;
       }
       break;
     }
     case 'c': {
-      if (!f.srv)
+      if (!f.Srv)
         S.Diag(D->getLocation(), diag::warn_hlsl_UDT_missing_basic_type);
       break;
     }
     default: {
       S.Diag(D->getLocation(),
              diag::err_hlsl_unsupported_register_type_and_variable_type)
-          << Slot.front() << typestr;
+          << Slot.front() << getHLSLResourceTypeStr(S, D);
     }
     }
     return;
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
index 28a747b965b22..73708535f8d8f 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
@@ -95,7 +95,7 @@ struct Eg11{
 
 // expected-error at +1{{conflicting register annotations: multiple register numbers detected for register type 'u'}}
 Eg11 e11 : register(u9) : register(u10);
-
+Eg11 e11a : register(u9, space0) : register(u9, space1);
 struct Eg12{
   RWBuffer<int> a;  
 };

>From 4c635ff4f6b2db1d0cdb5358886570f6818c9942 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 8 Jul 2024 19:04:53 -0700
Subject: [PATCH 16/26] fix some asserts

---
 clang/lib/Sema/SemaHLSL.cpp                         | 13 +++++--------
 .../SemaHLSL/resource_binding_attr_error_udt.hlsl   |  4 +---
 2 files changed, 6 insertions(+), 11 deletions(-)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 40399d7974568..bc1e3324a4e46 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -508,16 +508,15 @@ bool isDeclaredWithinCOrTBuffer(const Decl *decl) {
 
 const CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *VD) {
   const Type *Ty = VD->getType()->getPointeeOrArrayElementType();
-  if (!Ty)
-    llvm_unreachable("Resource class must have an element type.");
+  assert(Ty && "Resource class must have an element type.");
 
   if (const BuiltinType *BTy = dyn_cast<BuiltinType>(Ty)) {
     return nullptr;
   }
 
   const CXXRecordDecl *TheRecordDecl = Ty->getAsCXXRecordDecl();
-  if (!TheRecordDecl)
-    llvm_unreachable("Resource class should have a resource type declaration.");
+  assert(TheRecordDecl &&
+         "Resource class should have a resource type declaration.");
 
   if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(TheRecordDecl))
     TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
@@ -721,10 +720,8 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
   HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(D);
 
   // exactly one of these two types should be set
-  if (!VD && !CBufferOrTBuffer)
-    return;
-  if (VD && CBufferOrTBuffer)
-    return;
+  assert(((VD && !CBufferOrTBuffer) || (!VD && CBufferOrTBuffer)) &&
+         "either VD or CBufferOrTBuffer should be set");
 
   RegisterBindingFlags f = HLSLFillRegisterBindingFlags(S, D);
   assert((int)f.Other + (int)f.Resource + (int)f.Basic + (int)f.Udt == 1 &&
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
index 73708535f8d8f..080db7d87a681 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
@@ -75,8 +75,6 @@ struct Eg9{
 };
 
 Eg9 e9;
-
-
 /* Light up this test when Texture2D is implemented
 template<typename R>
 struct Eg10 {
@@ -86,7 +84,7 @@ struct Eg10 {
 Eg10<Texture2D> e10 : register(u0);
 
 // invalid because after template expansion, there are no valid resources inside Eg10 to bind as a UAV.
-*/ 
+*/
 
 struct Eg11{
   RWBuffer<int> a;

>From 319787260631b53f3f8a05913ecba9f931dcf312 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 8 Jul 2024 19:08:07 -0700
Subject: [PATCH 17/26] add test for infeasible reg type

---
 clang/test/SemaHLSL/resource_binding_attr_error.hlsl | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
index 95de3ad55af54..bd2e25f0db24e 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
@@ -58,3 +58,6 @@ struct S {
   // expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
   RWBuffer<float> U : register(u3);
 };
+
+// expected-error at +1 {{invalid register type 'z' used; expected 't', 'u', 'b', or 's'}}
+RWBuffer<float> U3 : register(z5);
\ No newline at end of file

>From 34086bf6c1758e3b53dfb8c00b2d53a14e66e417 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 9 Jul 2024 16:10:18 -0700
Subject: [PATCH 18/26] update tests, change multiple annotation diagnostic
 behavior, rename some variables

---
 .../clang/Basic/DiagnosticSemaKinds.td        |   2 +-
 clang/lib/Sema/SemaHLSL.cpp                   | 178 +++++++++---------
 .../resource_binding_attr_error_udt.hlsl      |   3 +-
 3 files changed, 91 insertions(+), 92 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 64a7ac4bef47a..a65bc8fc7bff3 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12317,7 +12317,7 @@ def err_hlsl_mismatching_register_type_and_variable_type: Error<"unsupported res
 def err_hlsl_unsupported_register_type_and_variable_type: Error<"register binding type '%1' not supported for variable of type '%0'">;
 def err_hlsl_mismatching_register_type_and_resource_type: Error<"%select{srv|uav|cbv|sampler}2 type '%0' requires register type '%select{t|u|b|s}2', but register type '%1' was used">;
 def err_hlsl_unsupported_register_type_and_resource_type: Error<"invalid register type '%0' used; expected 't', 'u', 'b', or 's'">;
-def err_hlsl_conflicting_register_annotations: Error<"conflicting register annotations: multiple register numbers detected for register type '%0'">;
+def err_hlsl_conflicting_register_annotations: Error<"conflicting register annotations: multiple register annotations detected for register type '%0'">;
 def warn_hlsl_deprecated_register_type_b: Warning<"deprecated legacy bool constant register binding 'b' used. 'b' is only used for constant buffer resource binding">, InGroup<DisallowLegacyBindingRules>, DefaultError;
 def warn_hlsl_deprecated_register_type_i: Warning<"deprecated legacy int constant register binding 'i' used">, InGroup<DisallowLegacyBindingRules>, DefaultError;
 def warn_hlsl_register_type_c_not_in_global_scope: Warning<"register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead">, InGroup<DisallowLegacyBindingRules>, DefaultError;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index bc1e3324a4e46..0b348e7b84bbc 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -38,21 +38,12 @@ Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
   HLSLBufferDecl *Result = HLSLBufferDecl::Create(
       getASTContext(), LexicalParent, CBuffer, KwLoc, Ident, IdentLoc, LBrace);
 
-  HLSLResourceAttr *NewAttr;
-  if (CBuffer) {
-    NewAttr = HLSLResourceAttr::CreateImplicit(
-        getASTContext(), llvm::hlsl::ResourceClass::CBuffer,
-        llvm::hlsl::ResourceKind::CBuffer,
-        /*IsROV=*/false);
-  }
-  // tbuffer case
-  else {
-    NewAttr = HLSLResourceAttr::CreateImplicit(
-        getASTContext(), llvm::hlsl::ResourceClass::SRV,
-        llvm::hlsl::ResourceKind::TBuffer,
-        /*IsROV=*/false);
-  }
-  Result->addAttr(NewAttr);
+  auto RC = CBuffer ? llvm::hlsl::ResourceClass::CBuffer
+                    : llvm::hlsl::ResourceClass::SRV;
+  auto RK = CBuffer ? llvm::hlsl::ResourceKind::CBuffer
+                    : llvm::hlsl::ResourceKind::TBuffer;
+  Result->addAttr(HLSLResourceAttr::CreateImplicit(getASTContext(), RC, RK,
+                                                   /*IsROV=*/false));
 
   SemaRef.PushOnScopeChains(Result, BufferScope);
   SemaRef.PushDeclContext(BufferScope, Result);
@@ -477,13 +468,13 @@ void SemaHLSL::handleResourceClassAttr(Decl *D, const ParsedAttr &AL) {
 
 struct RegisterBindingFlags {
   bool Resource = false;
-  bool Udt = false;
+  bool UDT = false;
   bool Other = false;
   bool Basic = false;
 
-  bool Srv = false;
-  bool Uav = false;
-  bool Cbv = false;
+  bool SRV = false;
+  bool UAV = false;
+  bool CBV = false;
   bool Sampler = false;
 
   bool ContainsNumeric = false;
@@ -510,9 +501,8 @@ const CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *VD) {
   const Type *Ty = VD->getType()->getPointeeOrArrayElementType();
   assert(Ty && "Resource class must have an element type.");
 
-  if (const BuiltinType *BTy = dyn_cast<BuiltinType>(Ty)) {
+  if (const auto *BTy = dyn_cast<BuiltinType>(Ty))
     return nullptr;
-  }
 
   const CXXRecordDecl *TheRecordDecl = Ty->getAsCXXRecordDecl();
   assert(TheRecordDecl &&
@@ -546,38 +536,37 @@ void traverseType(QualType T, RegisterBindingFlags &r) {
   if (T->isIntegralOrEnumerationType() || T->isFloatingType()) {
     r.ContainsNumeric = true;
     return;
-  } else if (const RecordType *RT = T->getAs<RecordType>()) {
-    RecordDecl *SubRD = RT->getDecl();
-    if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(SubRD)) {
-      auto TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
-      TheRecordDecl = TheRecordDecl->getCanonicalDecl();
-      const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
-      llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
-      switch (DeclResourceClass) {
-      case llvm::hlsl::ResourceClass::SRV: {
-        r.Srv = true;
-        break;
-      }
-      case llvm::hlsl::ResourceClass::UAV: {
-        r.Uav = true;
-        break;
-      }
-      case llvm::hlsl::ResourceClass::CBuffer: {
-        r.Cbv = true;
-        break;
-      }
-      case llvm::hlsl::ResourceClass::Sampler: {
-        r.Sampler = true;
-        break;
-      }
-      }
+  }
+  const RecordType *RT = T->getAs<RecordType>();
+  if (!RT)
+    return;
+
+  RecordDecl *SubRD = RT->getDecl();
+  if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(SubRD)) {
+    auto TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
+    TheRecordDecl = TheRecordDecl->getCanonicalDecl();
+    const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
+    llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
+    switch (DeclResourceClass) {
+    case llvm::hlsl::ResourceClass::SRV:
+      r.SRV = true;
+      break;
+    case llvm::hlsl::ResourceClass::UAV:
+      r.UAV = true;
+      break;
+    case llvm::hlsl::ResourceClass::CBuffer:
+      r.CBV = true;
+      break;
+    case llvm::hlsl::ResourceClass::Sampler:
+      r.Sampler = true;
+      break;
     }
+  }
 
-    else if (SubRD->isCompleteDefinition()) {
-      for (auto Field : SubRD->fields()) {
-        QualType T = Field->getType();
-        traverseType(T, r);
-      }
+  else if (SubRD->isCompleteDefinition()) {
+    for (auto Field : SubRD->fields()) {
+      QualType T = Field->getType();
+      traverseType(T, r);
     }
   }
 }
@@ -611,12 +600,15 @@ RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
   // Samplers, UAVs, and SRVs are VarDecl types
   VarDecl *VD = dyn_cast<VarDecl>(D);
 
+  assert(((VD && !CBufferOrTBuffer) || (!VD && CBufferOrTBuffer)) &&
+         "either VD or CBufferOrTBuffer should be set");
+
   if (CBufferOrTBuffer) {
     r.Resource = true;
     if (CBufferOrTBuffer->isCBuffer())
-      r.Cbv = true;
+      r.CBV = true;
     else
-      r.Srv = true;
+      r.SRV = true;
   } else if (VD) {
     const HLSLResourceAttr *res_attr =
         getHLSLResourceAttrFromEitherDecl(VD, CBufferOrTBuffer);
@@ -626,15 +618,15 @@ RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
       r.Resource = true;
       switch (DeclResourceClass) {
       case llvm::hlsl::ResourceClass::SRV: {
-        r.Srv = true;
+        r.SRV = true;
         break;
       }
       case llvm::hlsl::ResourceClass::UAV: {
-        r.Uav = true;
+        r.UAV = true;
         break;
       }
       case llvm::hlsl::ResourceClass::CBuffer: {
-        r.Cbv = true;
+        r.CBV = true;
         break;
       }
       case llvm::hlsl::ResourceClass::Sampler: {
@@ -646,7 +638,7 @@ RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
       if (VD->getType()->isBuiltinType())
         r.Basic = true;
       else if (VD->getType()->isAggregateType()) {
-        r.Udt = true;
+        r.UDT = true;
         QualType VarType = VD->getType();
         if (const RecordType *RT = VarType->getAs<RecordType>()) {
           const RecordDecl *RD = RT->getDecl();
@@ -656,46 +648,52 @@ RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
       } else
         r.Other = true;
     }
-  } else {
-    llvm_unreachable("unknown decl type");
   }
   return r;
 }
 
+int getRegisterTypeIndex(StringRef Slot) {
+  switch (Slot[0]) {
+  case 't':
+    return 0;
+  case 'u':
+    return 1;
+  case 'b':
+    return 2;
+  case 's':
+    return 3;
+  case 'c':
+    return 4;
+  case 'i':
+    return 5;
+  default:
+    llvm_unreachable("invalid register type");
+  }
+}
+
 static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *D,
                                                 StringRef &Slot) {
-  // make sure that there are no register annotations applied to the decl
-  // with the same register type but different numbers
-  std::unordered_map<char, std::set<char>>
-      s; // store unique register type + numbers
-  std::set<char> starting_set = {Slot[1]};
-  s.insert(std::make_pair(Slot[0], starting_set));
+  // make sure that there are no tworegister annotations
+  // applied to the decl with the same register type
+  bool registerTypesDetectedCount[6];
+  for (int i = 0; i < 6; i++)
+    registerTypesDetectedCount[i] = false;
+  registerTypesDetectedCount[getRegisterTypeIndex(Slot)] = true;
+
   for (auto it = D->attr_begin(); it != D->attr_end(); ++it) {
     if (HLSLResourceBindingAttr *attr =
             dyn_cast<HLSLResourceBindingAttr>(*it)) {
       std::string otherSlot(attr->getSlot().data());
-
-      // insert into hash map
-      if (s.find(otherSlot[0]) != s.end()) {
-        // if the register type is already in the map, insert the number
-        // into the set (if it's not already there
-        s[otherSlot[0]].insert(otherSlot[1]);
-      } else {
-        // if the register type is not in the map, insert it with the number
-        std::set<char> otherSet;
-        otherSet.insert(otherSlot[1]);
-        s.insert(std::make_pair(otherSlot[0], otherSet));
+      int registerTypeIndex = getRegisterTypeIndex(otherSlot);
+      if (registerTypesDetectedCount[registerTypeIndex])
+        S.Diag(D->getLocation(),
+               diag::err_hlsl_conflicting_register_annotations)
+            << otherSlot.substr(0, 1);
+      else {
+        registerTypesDetectedCount[registerTypeIndex] = true;
       }
     }
   }
-
-  for (auto regType : s) {
-    if (regType.second.size() > 1) {
-      std::string regTypeStr(1, regType.first);
-      S.Diag(D->getLocation(), diag::err_hlsl_conflicting_register_annotations)
-          << regTypeStr;
-    }
-  }
 }
 
 std::string getHLSLResourceTypeStr(Sema &S, Decl *D) {
@@ -724,7 +722,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
          "either VD or CBufferOrTBuffer should be set");
 
   RegisterBindingFlags f = HLSLFillRegisterBindingFlags(S, D);
-  assert((int)f.Other + (int)f.Resource + (int)f.Basic + (int)f.Udt == 1 &&
+  assert((int)f.Other + (int)f.Resource + (int)f.Basic + (int)f.UDT == 1 &&
          "only one resource analysis result should be expected");
 
   std::string registerType(Slot.substr(0, 1));
@@ -819,10 +817,10 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
   }
 
   // finally, we handle the udt case
-  if (f.Udt) {
+  if (f.UDT) {
     switch (Slot[0]) {
     case 't': {
-      if (!f.Srv) {
+      if (!f.SRV) {
         S.Diag(D->getLocation(),
                diag::warn_hlsl_UDT_missing_resource_type_member)
             << getHLSLResourceTypeStr(S, D) << "t" << 0;
@@ -830,7 +828,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
       break;
     }
     case 'u': {
-      if (!f.Uav) {
+      if (!f.UAV) {
         S.Diag(D->getLocation(),
                diag::warn_hlsl_UDT_missing_resource_type_member)
             << getHLSLResourceTypeStr(S, D) << "u" << 1;
@@ -838,7 +836,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
       break;
     }
     case 'b': {
-      if (!f.Cbv) {
+      if (!f.CBV) {
         S.Diag(D->getLocation(),
                diag::warn_hlsl_UDT_missing_resource_type_member)
             << getHLSLResourceTypeStr(S, D) << "b" << 2;
@@ -854,7 +852,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
       break;
     }
     case 'c': {
-      if (!f.Srv)
+      if (!f.ContainsNumeric)
         S.Diag(D->getLocation(), diag::warn_hlsl_UDT_missing_basic_type);
       break;
     }
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
index 080db7d87a681..9c93224add077 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
@@ -91,8 +91,9 @@ struct Eg11{
   RWBuffer<int> b;
 };
 
-// expected-error at +1{{conflicting register annotations: multiple register numbers detected for register type 'u'}}
+// expected-error at +1{{conflicting register annotations: multiple register annotations detected for register type 'u'}}
 Eg11 e11 : register(u9) : register(u10);
+// expected-error at +1{{conflicting register annotations: multiple register annotations detected for register type 'u'}}
 Eg11 e11a : register(u9, space0) : register(u9, space1);
 struct Eg12{
   RWBuffer<int> a;  

>From 6e385d85520a9cc38053ff4c5e9f083cc4c703fa Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 9 Jul 2024 17:28:03 -0700
Subject: [PATCH 19/26] make register types case insensitive, improve array
 initializatoin

---
 clang/lib/Sema/SemaHLSL.cpp                   | 52 ++++++++++++-------
 .../resource_binding_attr_error_basic.hlsl    |  4 +-
 2 files changed, 34 insertions(+), 22 deletions(-)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 0b348e7b84bbc..d43140e7fa6ee 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -487,11 +487,8 @@ bool isDeclaredWithinCOrTBuffer(const Decl *decl) {
 
   // Traverse up the parent contexts
   const DeclContext *context = decl->getDeclContext();
-  while (context) {
-    if (isa<HLSLBufferDecl>(context)) {
-      return true;
-    }
-    context = context->getParent();
+  if (isa<HLSLBufferDecl>(context)) {
+    return true;
   }
 
   return false;
@@ -655,16 +652,22 @@ RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
 int getRegisterTypeIndex(StringRef Slot) {
   switch (Slot[0]) {
   case 't':
+  case 'T':
     return 0;
   case 'u':
+  case 'U':
     return 1;
   case 'b':
+  case 'B ':
     return 2;
   case 's':
+  case 'S':
     return 3;
   case 'c':
+  case 'C':
     return 4;
   case 'i':
+  case 'I':
     return 5;
   default:
     llvm_unreachable("invalid register type");
@@ -675,22 +678,20 @@ static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *D,
                                                 StringRef &Slot) {
   // make sure that there are no tworegister annotations
   // applied to the decl with the same register type
-  bool registerTypesDetectedCount[6];
-  for (int i = 0; i < 6; i++)
-    registerTypesDetectedCount[i] = false;
-  registerTypesDetectedCount[getRegisterTypeIndex(Slot)] = true;
+  bool RegisterTypesDetected[6] = {false};
+  RegisterTypesDetected[getRegisterTypeIndex(Slot)] = true;
 
   for (auto it = D->attr_begin(); it != D->attr_end(); ++it) {
     if (HLSLResourceBindingAttr *attr =
             dyn_cast<HLSLResourceBindingAttr>(*it)) {
-      std::string otherSlot(attr->getSlot().data());
-      int registerTypeIndex = getRegisterTypeIndex(otherSlot);
-      if (registerTypesDetectedCount[registerTypeIndex])
+
+      int registerTypeIndex = getRegisterTypeIndex(attr->getSlot());
+      if (RegisterTypesDetected[registerTypeIndex]) {
         S.Diag(D->getLocation(),
                diag::err_hlsl_conflicting_register_annotations)
-            << otherSlot.substr(0, 1);
-      else {
-        registerTypesDetectedCount[registerTypeIndex] = true;
+            << attr->getSlot().substr(0, 1);
+      } else {
+        RegisterTypesDetected[registerTypeIndex] = true;
       }
     }
   }
@@ -819,7 +820,8 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
   // finally, we handle the udt case
   if (f.UDT) {
     switch (Slot[0]) {
-    case 't': {
+    case 't':
+    case 'T': {
       if (!f.SRV) {
         S.Diag(D->getLocation(),
                diag::warn_hlsl_UDT_missing_resource_type_member)
@@ -827,7 +829,8 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
       }
       break;
     }
-    case 'u': {
+    case 'u':
+    case 'U': {
       if (!f.UAV) {
         S.Diag(D->getLocation(),
                diag::warn_hlsl_UDT_missing_resource_type_member)
@@ -835,7 +838,8 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
       }
       break;
     }
-    case 'b': {
+    case 'b':
+    case 'B': {
       if (!f.CBV) {
         S.Diag(D->getLocation(),
                diag::warn_hlsl_UDT_missing_resource_type_member)
@@ -843,7 +847,8 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
       }
       break;
     }
-    case 's': {
+    case 's':
+    case 'S': {
       if (!f.Sampler) {
         S.Diag(D->getLocation(),
                diag::warn_hlsl_UDT_missing_resource_type_member)
@@ -851,7 +856,8 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
       }
       break;
     }
-    case 'c': {
+    case 'c':
+    case 'C': {
       if (!f.ContainsNumeric)
         S.Diag(D->getLocation(), diag::warn_hlsl_UDT_missing_basic_type);
       break;
@@ -906,11 +912,17 @@ void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) {
   if (!Slot.empty()) {
     switch (Slot[0]) {
     case 't':
+    case 'T':
     case 'u':
+    case 'U':
     case 'b':
+    case 'B':
     case 's':
+    case 'S':
     case 'c':
+    case 'C':
     case 'i':
+    case 'I':
       break;
     default:
       Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_resource_type)
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
index 64cff79274edd..787fd54a3258f 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
@@ -20,7 +20,7 @@ tbuffer g_tbuffer1 {
     float f5 : register(c2);
 };
 
-tbuffer g_cbuffer2 {
+cbuffer g_cbuffer2 {
 // expected-error at +1{{register binding type 'float' not supported for variable of type 'b'}}
     float f6 : register(b2);
 };
@@ -28,4 +28,4 @@ tbuffer g_cbuffer2 {
 tbuffer g_tbuffer2 {
 // expected-error at +1{{register binding type 'float' not supported for variable of type 'i'}}
     float f7 : register(i2);
-};
\ No newline at end of file
+};

>From b470fb7f4afa98ce7d2bb04ebd102ba19e23bbb6 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Wed, 10 Jul 2024 12:25:32 -0700
Subject: [PATCH 20/26] variable name changes, add diag group silence test,
 centralize slot interpretation

---
 .../clang/Basic/DiagnosticSemaKinds.td        |   2 +-
 clang/lib/Sema/SemaHLSL.cpp                   | 309 +++++++++---------
 .../resource_binding_attr_error_basic.hlsl    |   4 +-
 ...urce_binding_attr_error_silence_diags.hlsl |  26 ++
 4 files changed, 179 insertions(+), 162 deletions(-)
 create mode 100644 clang/test/SemaHLSL/resource_binding_attr_error_silence_diags.hlsl

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a65bc8fc7bff3..b2d70670e4ae5 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12314,7 +12314,7 @@ def err_hlsl_init_priority_unsupported : Error<
   "initializer priorities are not supported in HLSL">;
 
 def err_hlsl_mismatching_register_type_and_variable_type: Error<"unsupported resource register binding '%select{t|u|b|s}0' on variable of type '%1'">;
-def err_hlsl_unsupported_register_type_and_variable_type: Error<"register binding type '%1' not supported for variable of type '%0'">;
+def err_hlsl_unsupported_register_type_and_variable_type: Error<"register binding type '%0' not supported for variable of type '%1'">;
 def err_hlsl_mismatching_register_type_and_resource_type: Error<"%select{srv|uav|cbv|sampler}2 type '%0' requires register type '%select{t|u|b|s}2', but register type '%1' was used">;
 def err_hlsl_unsupported_register_type_and_resource_type: Error<"invalid register type '%0' used; expected 't', 'u', 'b', or 's'">;
 def err_hlsl_conflicting_register_annotations: Error<"conflicting register annotations: multiple register annotations detected for register type '%0'">;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index d43140e7fa6ee..5962f69c7fbf7 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -481,12 +481,12 @@ struct RegisterBindingFlags {
   bool DefaultGlobals = false;
 };
 
-bool isDeclaredWithinCOrTBuffer(const Decl *decl) {
-  if (!decl)
+bool isDeclaredWithinCOrTBuffer(const Decl *TheDecl) {
+  if (!TheDecl)
     return false;
 
   // Traverse up the parent contexts
-  const DeclContext *context = decl->getDeclContext();
+  const DeclContext *context = TheDecl->getDeclContext();
   if (isa<HLSLBufferDecl>(context)) {
     return true;
   }
@@ -498,7 +498,7 @@ const CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *VD) {
   const Type *Ty = VD->getType()->getPointeeOrArrayElementType();
   assert(Ty && "Resource class must have an element type.");
 
-  if (const auto *BTy = dyn_cast<BuiltinType>(Ty))
+  if (const auto *TheBuiltinTy = dyn_cast<BuiltinType>(Ty))
     return nullptr;
 
   const CXXRecordDecl *TheRecordDecl = Ty->getAsCXXRecordDecl();
@@ -529,46 +529,45 @@ getHLSLResourceAttrFromEitherDecl(VarDecl *VD,
   return nullptr;
 }
 
-void traverseType(QualType T, RegisterBindingFlags &r) {
-  if (T->isIntegralOrEnumerationType() || T->isFloatingType()) {
-    r.ContainsNumeric = true;
+void traverseType(QualType TheQualTy, RegisterBindingFlags &Flags) {
+  if (TheQualTy->isIntegralOrEnumerationType() || TheQualTy->isFloatingType()) {
+    Flags.ContainsNumeric = true;
     return;
   }
-  const RecordType *RT = T->getAs<RecordType>();
-  if (!RT)
+  const RecordType *TheRecordTy = TheQualTy->getAs<RecordType>();
+  if (!TheRecordTy)
     return;
 
-  RecordDecl *SubRD = RT->getDecl();
-  if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(SubRD)) {
+  RecordDecl *SubRecordDecl = TheRecordTy->getDecl();
+  if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(SubRecordDecl)) {
     auto TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
     TheRecordDecl = TheRecordDecl->getCanonicalDecl();
     const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
     llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
     switch (DeclResourceClass) {
     case llvm::hlsl::ResourceClass::SRV:
-      r.SRV = true;
+      Flags.SRV = true;
       break;
     case llvm::hlsl::ResourceClass::UAV:
-      r.UAV = true;
+      Flags.UAV = true;
       break;
     case llvm::hlsl::ResourceClass::CBuffer:
-      r.CBV = true;
+      Flags.CBV = true;
       break;
     case llvm::hlsl::ResourceClass::Sampler:
-      r.Sampler = true;
+      Flags.Sampler = true;
       break;
     }
   }
 
-  else if (SubRD->isCompleteDefinition()) {
-    for (auto Field : SubRD->fields()) {
-      QualType T = Field->getType();
-      traverseType(T, r);
+  else if (SubRecordDecl->isCompleteDefinition()) {
+    for (auto Field : SubRecordDecl->fields()) {
+      traverseType(Field->getType(), Flags);
     }
   }
 }
 
-void setResourceClassFlagsFromRecordDecl(RegisterBindingFlags &r,
+void setResourceClassFlagsFromRecordDecl(RegisterBindingFlags &Flags,
                                          const RecordDecl *RD) {
   if (!RD)
     return;
@@ -576,77 +575,77 @@ void setResourceClassFlagsFromRecordDecl(RegisterBindingFlags &r,
   if (RD->isCompleteDefinition()) {
     for (auto Field : RD->fields()) {
       QualType T = Field->getType();
-      traverseType(T, r);
+      traverseType(T, Flags);
     }
   }
 }
 
-RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
-  RegisterBindingFlags r;
-  if (!isDeclaredWithinCOrTBuffer(D)) {
-    // make sure the type is a basic / numeric type
-    if (VarDecl *v = dyn_cast<VarDecl>(D)) {
-      QualType t = v->getType();
-      // a numeric variable will inevitably end up in $Globals buffer
-      if (t->isIntegralType(S.getASTContext()) || t->isFloatingType())
-        r.DefaultGlobals = true;
-    }
-  }
+RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *TheDecl) {
+
   // Cbuffers and Tbuffers are HLSLBufferDecl types
-  HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(D);
+  HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(TheDecl);
   // Samplers, UAVs, and SRVs are VarDecl types
-  VarDecl *VD = dyn_cast<VarDecl>(D);
+  VarDecl *TheVarDecl = dyn_cast<VarDecl>(TheDecl);
 
-  assert(((VD && !CBufferOrTBuffer) || (!VD && CBufferOrTBuffer)) &&
+  assert(((TheVarDecl && !CBufferOrTBuffer) ||
+          (!TheVarDecl && CBufferOrTBuffer)) &&
          "either VD or CBufferOrTBuffer should be set");
 
+  RegisterBindingFlags Flags;
+  if (!isDeclaredWithinCOrTBuffer(TheDecl)) {
+    // make sure the type is a basic / numeric type
+    if (TheVarDecl) {
+      QualType TheQualTy = TheVarDecl->getType();
+      // a numeric variable will inevitably end up in $Globals buffer
+      if (TheQualTy->isIntegralType(S.getASTContext()) ||
+          TheQualTy->isFloatingType())
+        Flags.DefaultGlobals = true;
+    }
+  }
+
   if (CBufferOrTBuffer) {
-    r.Resource = true;
+    Flags.Resource = true;
     if (CBufferOrTBuffer->isCBuffer())
-      r.CBV = true;
+      Flags.CBV = true;
     else
-      r.SRV = true;
-  } else if (VD) {
+      Flags.SRV = true;
+  } else if (TheVarDecl) {
     const HLSLResourceAttr *res_attr =
-        getHLSLResourceAttrFromEitherDecl(VD, CBufferOrTBuffer);
+        getHLSLResourceAttrFromEitherDecl(TheVarDecl, CBufferOrTBuffer);
     if (res_attr) {
       llvm::hlsl::ResourceClass DeclResourceClass =
           res_attr->getResourceClass();
-      r.Resource = true;
+      Flags.Resource = true;
       switch (DeclResourceClass) {
-      case llvm::hlsl::ResourceClass::SRV: {
-        r.SRV = true;
+      case llvm::hlsl::ResourceClass::SRV:
+        Flags.SRV = true;
         break;
-      }
-      case llvm::hlsl::ResourceClass::UAV: {
-        r.UAV = true;
+      case llvm::hlsl::ResourceClass::UAV:
+        Flags.UAV = true;
         break;
-      }
-      case llvm::hlsl::ResourceClass::CBuffer: {
-        r.CBV = true;
+      case llvm::hlsl::ResourceClass::CBuffer:
+        Flags.CBV = true;
         break;
-      }
-      case llvm::hlsl::ResourceClass::Sampler: {
-        r.Sampler = true;
+      case llvm::hlsl::ResourceClass::Sampler:
+        Flags.Sampler = true;
         break;
       }
-      }
     } else {
-      if (VD->getType()->isBuiltinType())
-        r.Basic = true;
-      else if (VD->getType()->isAggregateType()) {
-        r.UDT = true;
-        QualType VarType = VD->getType();
-        if (const RecordType *RT = VarType->getAs<RecordType>()) {
-          const RecordDecl *RD = RT->getDecl();
+      if (TheVarDecl->getType()->isBuiltinType())
+        Flags.Basic = true;
+      else if (TheVarDecl->getType()->isAggregateType()) {
+        Flags.UDT = true;
+        QualType TheQualTy = TheVarDecl->getType();
+        if (const RecordType *TheRecordTy = TheQualTy->getAs<RecordType>()) {
+          const RecordDecl *TheRecordDecl = TheRecordTy->getDecl();
           // recurse through members, set appropriate resource class flags.
-          setResourceClassFlagsFromRecordDecl(r, RD);
+          setResourceClassFlagsFromRecordDecl(Flags, TheRecordDecl);
         }
       } else
-        r.Other = true;
+        Flags.Other = true;
     }
   }
-  return r;
+  return Flags;
 }
 
 int getRegisterTypeIndex(StringRef Slot) {
@@ -674,20 +673,20 @@ int getRegisterTypeIndex(StringRef Slot) {
   }
 }
 
-static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *D,
+static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *TheDecl,
                                                 StringRef &Slot) {
   // make sure that there are no tworegister annotations
   // applied to the decl with the same register type
   bool RegisterTypesDetected[6] = {false};
   RegisterTypesDetected[getRegisterTypeIndex(Slot)] = true;
 
-  for (auto it = D->attr_begin(); it != D->attr_end(); ++it) {
+  for (auto it = TheDecl->attr_begin(); it != TheDecl->attr_end(); ++it) {
     if (HLSLResourceBindingAttr *attr =
             dyn_cast<HLSLResourceBindingAttr>(*it)) {
 
       int registerTypeIndex = getRegisterTypeIndex(attr->getSlot());
       if (RegisterTypesDetected[registerTypeIndex]) {
-        S.Diag(D->getLocation(),
+        S.Diag(TheDecl->getLocation(),
                diag::err_hlsl_conflicting_register_annotations)
             << attr->getSlot().substr(0, 1);
       } else {
@@ -697,185 +696,177 @@ static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *D,
   }
 }
 
-std::string getHLSLResourceTypeStr(Sema &S, Decl *D) {
-  VarDecl *VD = dyn_cast<VarDecl>(D);
-  HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(D);
+std::string getHLSLResourceTypeStr(Sema &S, Decl *TheDecl) {
+  VarDecl *TheVarDecl = dyn_cast<VarDecl>(TheDecl);
+  HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(TheDecl);
 
-  if (VD) {
-    QualType QT = VD->getType();
+  if (TheVarDecl) {
+    QualType TheQualTy = TheVarDecl->getType();
     PrintingPolicy PP = S.getPrintingPolicy();
-    return QualType::getAsString(QT.split(), PP);
+    return QualType::getAsString(TheQualTy.split(), PP);
   } else {
     return CBufferOrTBuffer->isCBuffer() ? "cbuffer" : "tbuffer";
   }
 }
 
 static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
-                                        Decl *D, StringRef &Slot) {
+                                        Decl *TheDecl, StringRef &Slot) {
 
   // Samplers, UAVs, and SRVs are VarDecl types
-  VarDecl *VD = dyn_cast<VarDecl>(D);
+  VarDecl *TheVarDecl = dyn_cast<VarDecl>(TheDecl);
   // Cbuffers and Tbuffers are HLSLBufferDecl types
-  HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(D);
+  HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(TheDecl);
 
   // exactly one of these two types should be set
-  assert(((VD && !CBufferOrTBuffer) || (!VD && CBufferOrTBuffer)) &&
-         "either VD or CBufferOrTBuffer should be set");
-
-  RegisterBindingFlags f = HLSLFillRegisterBindingFlags(S, D);
-  assert((int)f.Other + (int)f.Resource + (int)f.Basic + (int)f.UDT == 1 &&
+  assert(((TheVarDecl && !CBufferOrTBuffer) ||
+          (!TheVarDecl && CBufferOrTBuffer)) &&
+         "either TheVarDecl or CBufferOrTBuffer should be set");
+
+  RegisterBindingFlags Flags = HLSLFillRegisterBindingFlags(S, TheDecl);
+  assert((int)Flags.Other + (int)Flags.Resource + (int)Flags.Basic +
+                 (int)Flags.UDT ==
+             1 &&
          "only one resource analysis result should be expected");
 
-  std::string registerType(Slot.substr(0, 1));
-
   // first, if "other" is set, emit an error
-  if (f.Other) {
+  if (Flags.Other) {
     S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
-        << Slot << getHLSLResourceTypeStr(S, D);
+        << Slot << getHLSLResourceTypeStr(S, TheDecl);
     return;
   }
 
   // next, if multiple register annotations exist, check that none conflict.
-  ValidateMultipleRegisterAnnotations(S, D, Slot);
+  ValidateMultipleRegisterAnnotations(S, TheDecl, Slot);
 
   // next, if resource is set, make sure the register type in the register
   // annotation is compatible with the variable's resource type.
-  if (f.Resource) {
+  if (Flags.Resource) {
     const HLSLResourceAttr *res_attr =
-        getHLSLResourceAttrFromEitherDecl(VD, CBufferOrTBuffer);
+        getHLSLResourceAttrFromEitherDecl(TheVarDecl, CBufferOrTBuffer);
     assert(res_attr && "any decl that set the resource flag on analysis should "
                        "have a resource attribute attached.");
     const llvm::hlsl::ResourceClass DeclResourceClass =
         res_attr->getResourceClass();
 
     switch (DeclResourceClass) {
-    case llvm::hlsl::ResourceClass::SRV: {
-      if (Slot[0] != 't') {
-        S.Diag(D->getLocation(),
+    case llvm::hlsl::ResourceClass::SRV:
+      if (getRegisterTypeIndex(Slot) != 0)
+        S.Diag(TheDecl->getLocation(),
                diag::err_hlsl_mismatching_register_type_and_resource_type)
-            << getHLSLResourceTypeStr(S, D) << registerType << 0 /*srv*/;
-      }
+            << getHLSLResourceTypeStr(S, TheDecl) << Slot.substr(0, 1)
+            << 0 /*srv*/;
       break;
-    }
-    case llvm::hlsl::ResourceClass::UAV: {
-      if (Slot[0] != 'u') {
-        S.Diag(D->getLocation(),
+    case llvm::hlsl::ResourceClass::UAV:
+      if (getRegisterTypeIndex(Slot) != 1)
+        S.Diag(TheDecl->getLocation(),
                diag::err_hlsl_mismatching_register_type_and_resource_type)
-            << getHLSLResourceTypeStr(S, D) << registerType << 1 /*uav*/;
-      }
+            << getHLSLResourceTypeStr(S, TheDecl) << Slot.substr(0, 1)
+            << 1 /*uav*/;
       break;
-    }
-    case llvm::hlsl::ResourceClass::CBuffer: {
-      if (Slot[0] != 'b') {
-        S.Diag(D->getLocation(),
+    case llvm::hlsl::ResourceClass::CBuffer:
+      if (getRegisterTypeIndex(Slot) != 2)
+        S.Diag(TheDecl->getLocation(),
                diag::err_hlsl_mismatching_register_type_and_resource_type)
-            << getHLSLResourceTypeStr(S, D) << registerType << 2 /*cbv*/;
-      }
+            << getHLSLResourceTypeStr(S, TheDecl) << Slot.substr(0, 1)
+            << 2 /*cbv*/;
       break;
-    }
-    case llvm::hlsl::ResourceClass::Sampler: {
-      if (Slot[0] != 's') {
-        S.Diag(D->getLocation(),
+    case llvm::hlsl::ResourceClass::Sampler:
+      if (getRegisterTypeIndex(Slot) != 3)
+        S.Diag(TheDecl->getLocation(),
                diag::err_hlsl_mismatching_register_type_and_resource_type)
-            << getHLSLResourceTypeStr(S, D) << registerType << 3 /*sampler*/;
-      }
+            << getHLSLResourceTypeStr(S, TheDecl) << Slot.substr(0, 1)
+            << 3 /*sampler*/;
       break;
     }
-    }
     return;
   }
 
   // next, handle diagnostics for when the "basic" flag is set,
   // including the legacy "i" and "b" register types.
-  if (f.Basic) {
-    if (f.DefaultGlobals) {
-      if (Slot[0] == 'b')
+  if (Flags.Basic) {
+    if (Flags.DefaultGlobals) {
+      if (getRegisterTypeIndex(Slot) == 2)
         S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_b);
-      if (Slot[0] == 'i')
+      if (getRegisterTypeIndex(Slot) == 5)
         S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_i);
     }
 
-    if (Slot[0] == 'c') {
-      if (!f.DefaultGlobals) {
+    if (getRegisterTypeIndex(Slot) == 4) {
+      if (!Flags.DefaultGlobals) {
         S.Diag(ArgLoc, diag::warn_hlsl_register_type_c_not_in_global_scope);
       }
-    } else if (Slot[0] == 't') {
+    } else if (getRegisterTypeIndex(Slot) == 0) {
       S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
-          << 0 << getHLSLResourceTypeStr(S, D);
-    } else if (Slot[0] == 'u') {
+          << 0 << getHLSLResourceTypeStr(S, TheDecl);
+    } else if (getRegisterTypeIndex(Slot) == 1) {
       S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
-          << 1 << getHLSLResourceTypeStr(S, D);
-    } else if (Slot[0] == 's') {
+          << 1 << getHLSLResourceTypeStr(S, TheDecl);
+    } else if (getRegisterTypeIndex(Slot) == 3) {
       S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
-          << 3 << getHLSLResourceTypeStr(S, D);
+          << 3 << getHLSLResourceTypeStr(S, TheDecl);
       // any other register type should emit
       // err_hlsl_unsupported_register_type_and_variable_type
-    } else if (!f.DefaultGlobals) {
+    } else if (!Flags.DefaultGlobals) {
       S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
-          << registerType << getHLSLResourceTypeStr(S, D);
+          << Slot.substr(0, 1) << getHLSLResourceTypeStr(S, TheDecl);
     }
     return;
   }
 
   // finally, we handle the udt case
-  if (f.UDT) {
-    switch (Slot[0]) {
-    case 't':
-    case 'T': {
-      if (!f.SRV) {
-        S.Diag(D->getLocation(),
+  if (Flags.UDT) {
+    switch (getRegisterTypeIndex(Slot)) {
+    case 0: {
+      if (!Flags.SRV) {
+        S.Diag(TheDecl->getLocation(),
                diag::warn_hlsl_UDT_missing_resource_type_member)
-            << getHLSLResourceTypeStr(S, D) << "t" << 0;
+            << getHLSLResourceTypeStr(S, TheDecl) << "t" << 0;
       }
       break;
     }
-    case 'u':
-    case 'U': {
-      if (!f.UAV) {
-        S.Diag(D->getLocation(),
+    case 1: {
+      if (!Flags.UAV) {
+        S.Diag(TheDecl->getLocation(),
                diag::warn_hlsl_UDT_missing_resource_type_member)
-            << getHLSLResourceTypeStr(S, D) << "u" << 1;
+            << getHLSLResourceTypeStr(S, TheDecl) << "u" << 1;
       }
       break;
     }
-    case 'b':
-    case 'B': {
-      if (!f.CBV) {
-        S.Diag(D->getLocation(),
+    case 2: {
+      if (!Flags.CBV) {
+        S.Diag(TheDecl->getLocation(),
                diag::warn_hlsl_UDT_missing_resource_type_member)
-            << getHLSLResourceTypeStr(S, D) << "b" << 2;
+            << getHLSLResourceTypeStr(S, TheDecl) << "b" << 2;
       }
       break;
     }
-    case 's':
-    case 'S': {
-      if (!f.Sampler) {
-        S.Diag(D->getLocation(),
+    case 3: {
+      if (!Flags.Sampler) {
+        S.Diag(TheDecl->getLocation(),
                diag::warn_hlsl_UDT_missing_resource_type_member)
-            << getHLSLResourceTypeStr(S, D) << "s" << 3;
+            << getHLSLResourceTypeStr(S, TheDecl) << "s" << 3;
       }
       break;
     }
-    case 'c':
-    case 'C': {
-      if (!f.ContainsNumeric)
-        S.Diag(D->getLocation(), diag::warn_hlsl_UDT_missing_basic_type);
+    case 4: {
+      if (!Flags.ContainsNumeric)
+        S.Diag(TheDecl->getLocation(), diag::warn_hlsl_UDT_missing_basic_type);
       break;
     }
     default: {
-      S.Diag(D->getLocation(),
+      S.Diag(TheDecl->getLocation(),
              diag::err_hlsl_unsupported_register_type_and_variable_type)
-          << Slot.front() << getHLSLResourceTypeStr(S, D);
+          << Slot.front() << getHLSLResourceTypeStr(S, TheDecl);
     }
     }
     return;
   }
 }
 
-void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) {
-  if (dyn_cast<VarDecl>(D)) {
-    if (SemaRef.RequireCompleteType(D->getBeginLoc(),
-                                    cast<ValueDecl>(D)->getType(),
+void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) {
+  if (dyn_cast<VarDecl>(TheDecl)) {
+    if (SemaRef.RequireCompleteType(TheDecl->getBeginLoc(),
+                                    cast<ValueDecl>(TheDecl)->getType(),
                                     diag::err_incomplete_type))
       return;
   }
@@ -949,12 +940,12 @@ void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) {
     return;
   }
 
-  DiagnoseHLSLResourceRegType(SemaRef, ArgLoc, D, Slot);
+  DiagnoseHLSLResourceRegType(SemaRef, ArgLoc, TheDecl, Slot);
 
   HLSLResourceBindingAttr *NewAttr =
       HLSLResourceBindingAttr::Create(getASTContext(), Slot, Space, AL);
   if (NewAttr)
-    D->addAttr(NewAttr);
+    TheDecl->addAttr(NewAttr);
 }
 
 void SemaHLSL::handleParamModifierAttr(Decl *D, const ParsedAttr &AL) {
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
index 787fd54a3258f..056941a651d73 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
@@ -21,11 +21,11 @@ tbuffer g_tbuffer1 {
 };
 
 cbuffer g_cbuffer2 {
-// expected-error at +1{{register binding type 'float' not supported for variable of type 'b'}}
+// expected-error at +1{{register binding type 'b' not supported for variable of type 'float'}}
     float f6 : register(b2);
 };
 
 tbuffer g_tbuffer2 {
-// expected-error at +1{{register binding type 'float' not supported for variable of type 'i'}}
+// expected-error at +1{{register binding type 'i' not supported for variable of type 'float'}}
     float f7 : register(i2);
 };
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_silence_diags.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_silence_diags.hlsl
new file mode 100644
index 0000000000000..4e227c85e6bcf
--- /dev/null
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_silence_diags.hlsl
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only -Wno-disallow-legacy-binding-rules %s -verify
+
+// expected-no-diagnostics
+float f2 : register(b9);
+
+float f3 : register(i9);
+
+cbuffer g_cbuffer1 {
+    float f4 : register(c2);
+};
+
+
+struct Eg12{
+  RWBuffer<int> a;  
+};
+
+Eg12 e12 : register(c9);
+
+
+struct Eg7 {
+  struct Bar {
+    float f;
+  };
+  Bar b;
+};
+Eg7 e7 : register(t0);
\ No newline at end of file

>From a702ac8f3a933dc1521e704e70e2bbadb95b8f01 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Fri, 12 Jul 2024 15:55:30 -0700
Subject: [PATCH 21/26] merge in spelled attribute change / rebase

---
 clang/lib/Sema/HLSLExternalSemaSource.cpp     | 23 +++++-----
 clang/lib/Sema/SemaHLSL.cpp                   | 42 ++++++++++++++-----
 .../ast-dump-comment-cbuffe-tbufferr.hlsl     |  6 ++-
 clang/test/AST/HLSL/cbuffer_tbuffer.hlsl      | 10 +++--
 clang/test/AST/HLSL/packoffset.hlsl           |  3 +-
 clang/test/AST/HLSL/pch_hlsl_buffer.hlsl      |  6 ++-
 .../test/AST/HLSL/resource_binding_attr.hlsl  | 10 +++--
 7 files changed, 67 insertions(+), 33 deletions(-)

diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index 6a71ee2f20333..485e96075cd0b 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -491,12 +491,12 @@ void HLSLExternalSemaSource::defineTrivialHLSLTypes() {
 }
 
 /// Set up common members and attributes for buffer types
-static BuiltinTypeDeclBuilder setupBufferHandle(CXXRecordDecl *Decl, Sema &S,
-                                                ResourceClass RC) {
+static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
+                                              ResourceClass RC, ResourceKind RK,
+                                              bool IsROV) {
   return BuiltinTypeDeclBuilder(Decl)
       .addHandleMember()
-      .addDefaultHandleConstructor(S, RC)
-      .annotateHLSLResource(RC, RK, IsROV);
+      .addDefaultHandleConstructor(S, RC);
 }
 
 void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
@@ -504,12 +504,14 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
   Decl =
       BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer")
           .addSimpleTemplateParams(*SemaPtr, {"element_type"})
-          .annotateResourceClass(ResourceClass::UAV, ResourceKind::TypedBuffer,
-                                 /*IsROV=*/false)
+          .annotateHLSLResource(ResourceClass::UAV, ResourceKind::TypedBuffer,
+                                /*IsROV=*/false)
           .Record;
 
   onCompletion(Decl, [this](CXXRecordDecl *Decl) {
-    setupBufferHandle(Decl, *SemaPtr, ResourceClass::UAV)
+    setupBufferType(Decl, *SemaPtr, ResourceClass::UAV,
+                    ResourceKind::TypedBuffer,
+                    /*IsROV=*/false)
         .addArraySubscriptOperators()
         .completeDefinition();
   });
@@ -517,11 +519,12 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
   Decl =
       BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RasterizerOrderedBuffer")
           .addSimpleTemplateParams(*SemaPtr, {"element_type"})
-          .annotateResourceClass(ResourceClass::UAV, ResourceKind::TypedBuffer,
-                                 /*IsROV=*/true)
+          .annotateHLSLResource(ResourceClass::UAV, ResourceKind::TypedBuffer,
+                                /*IsROV=*/true)
           .Record;
   onCompletion(Decl, [this](CXXRecordDecl *Decl) {
-    setupBufferHandle(Decl, *SemaPtr, ResourceClass::UAV)
+    setupBufferType(Decl, *SemaPtr, ResourceClass::UAV,
+                    ResourceKind::TypedBuffer, /*IsROV=*/true)
         .addArraySubscriptOperators()
         .completeDefinition();
   });
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 5962f69c7fbf7..f47c4f50f9b02 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -42,7 +42,8 @@ Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
                     : llvm::hlsl::ResourceClass::SRV;
   auto RK = CBuffer ? llvm::hlsl::ResourceKind::CBuffer
                     : llvm::hlsl::ResourceKind::TBuffer;
-  Result->addAttr(HLSLResourceAttr::CreateImplicit(getASTContext(), RC, RK,
+  Result->addAttr(HLSLResourceClassAttr::CreateImplicit(getASTContext(), RC));
+  Result->addAttr(HLSLResourceAttr::CreateImplicit(getASTContext(), RK,
                                                    /*IsROV=*/false));
 
   SemaRef.PushOnScopeChains(Result, BufferScope);
@@ -511,6 +512,24 @@ const CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *VD) {
   return TheRecordDecl;
 }
 
+const HLSLResourceClassAttr *
+getHLSLResourceClassAttrFromEitherDecl(VarDecl *VD,
+                                       HLSLBufferDecl *CBufferOrTBuffer) {
+
+  if (VD) {
+    const CXXRecordDecl *TheRecordDecl = getRecordDeclFromVarDecl(VD);
+    if (!TheRecordDecl)
+      return nullptr;
+    const auto *Attr = TheRecordDecl->getAttr<HLSLResourceClassAttr>();
+    return Attr;
+  } else if (CBufferOrTBuffer) {
+    const auto *Attr = CBufferOrTBuffer->getAttr<HLSLResourceClassAttr>();
+    return Attr;
+  }
+  llvm_unreachable("one of the two conditions should be true.");
+  return nullptr;
+}
+
 const HLSLResourceAttr *
 getHLSLResourceAttrFromEitherDecl(VarDecl *VD,
                                   HLSLBufferDecl *CBufferOrTBuffer) {
@@ -542,7 +561,7 @@ void traverseType(QualType TheQualTy, RegisterBindingFlags &Flags) {
   if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(SubRecordDecl)) {
     auto TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
     TheRecordDecl = TheRecordDecl->getCanonicalDecl();
-    const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
+    const auto *Attr = TheRecordDecl->getAttr<HLSLResourceClassAttr>();
     llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
     switch (DeclResourceClass) {
     case llvm::hlsl::ResourceClass::SRV:
@@ -610,11 +629,11 @@ RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *TheDecl) {
     else
       Flags.SRV = true;
   } else if (TheVarDecl) {
-    const HLSLResourceAttr *res_attr =
-        getHLSLResourceAttrFromEitherDecl(TheVarDecl, CBufferOrTBuffer);
-    if (res_attr) {
+    const HLSLResourceClassAttr *resClassAttr =
+        getHLSLResourceClassAttrFromEitherDecl(TheVarDecl, CBufferOrTBuffer);
+    if (resClassAttr) {
       llvm::hlsl::ResourceClass DeclResourceClass =
-          res_attr->getResourceClass();
+          resClassAttr->getResourceClass();
       Flags.Resource = true;
       switch (DeclResourceClass) {
       case llvm::hlsl::ResourceClass::SRV:
@@ -741,12 +760,15 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
   // next, if resource is set, make sure the register type in the register
   // annotation is compatible with the variable's resource type.
   if (Flags.Resource) {
-    const HLSLResourceAttr *res_attr =
+    const HLSLResourceAttr *resAttr =
         getHLSLResourceAttrFromEitherDecl(TheVarDecl, CBufferOrTBuffer);
-    assert(res_attr && "any decl that set the resource flag on analysis should "
-                       "have a resource attribute attached.");
+    const HLSLResourceClassAttr *resClassAttr =
+        getHLSLResourceClassAttrFromEitherDecl(TheVarDecl, CBufferOrTBuffer);
+    assert(resAttr && resClassAttr &&
+           "any decl that set the resource flag on analysis should "
+           "have a resource attribute and resource class attribute attached.");
     const llvm::hlsl::ResourceClass DeclResourceClass =
-        res_attr->getResourceClass();
+        resClassAttr->getResourceClass();
 
     switch (DeclResourceClass) {
     case llvm::hlsl::ResourceClass::SRV:
diff --git a/clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl b/clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl
index 727d505471bcb..e6a2ea7c6d2dc 100644
--- a/clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl
+++ b/clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl
@@ -38,14 +38,16 @@ tbuffer B {
 }
 
 // AST:HLSLBufferDecl {{.*}}:11:1, line:20:1> line:11:9 cbuffer A
-// AST-NEXT:-HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer CBuffer
+// AST-NEXT:-HLSLResourceClassAttr {{.*}} <<invalid sloc>> Implicit CBuffer
+// AST-NEXT:-HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer
 // AST-NEXT:FullComment {{.*}}<line:10:4, col:17>
 // AST-NEXT:`-ParagraphComment {{.*}}<col:4, col:17>
 // AST-NEXT:`-TextComment {{.*}}<col:4, col:17> Text=" CBuffer decl."
 // AST-NEXT:-VarDecl {{.*}}<line:15:5, col:11> col:11 a 'float'
 // AST-NEXT:`-VarDecl {{.*}}<line:19:5, col:9> col:9 b 'int'
 // AST-NEXT:HLSLBufferDecl {{.*}}<line:29:1, line:38:1> line:29:9 tbuffer B
-// AST-NEXT:-HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit SRV TBuffer
+// AST-NEXT:-HLSLResourceClassAttr {{.*}} <<invalid sloc>> Implicit SRV
+// AST-NEXT:-HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit TBuffer
 // AST-NEXT:-FullComment {{.*}}<line:28:4, col:17>
 // AST-NEXT: `-ParagraphComment {{.*}}<col:4, col:17>
 // AST-NEXT:  `-TextComment {{.*}}<col:4, col:17> Text=" TBuffer decl."
diff --git a/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl b/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl
index a67688d510ea6..5e558354cd3a0 100644
--- a/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl
+++ b/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl
@@ -1,14 +1,16 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -ast-dump -o - %s | FileCheck %s
 
-// CHECK:HLSLBufferDecl 0x[[CB:[0-9a-f]+]] {{.*}} line:6:9 cbuffer CB
-// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer CBuffer
+// CHECK:HLSLBufferDecl 0x[[CB:[0-9a-f]+]] {{.*}} line:7:9 cbuffer CB
+// CHECK:HLSLResourceClassAttr 0x{{[0-9a-f]+}} <<invalid sloc>> Implicit CBuffer
+// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer
 // CHECK-NEXT:VarDecl 0x[[A:[0-9a-f]+]] {{.*}} col:9 used a 'float'
 cbuffer CB {
   float a;
 }
 
-// CHECK:HLSLBufferDecl 0x[[TB:[0-9a-f]+]] {{.*}} line:13:9 tbuffer TB
-// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit SRV TBuffer
+// CHECK:HLSLBufferDecl 0x[[TB:[0-9a-f]+]] {{.*}} line:15:9 tbuffer TB
+// CHECK:HLSLResourceClassAttr 0x{{[0-9a-f]+}} <<invalid sloc>> Implicit SRV
+// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit TBuffer
 // CHECK-NEXT:VarDecl 0x[[B:[0-9a-f]+]] {{.*}} col:9 used b 'float'
 tbuffer TB {
   float b;
diff --git a/clang/test/AST/HLSL/packoffset.hlsl b/clang/test/AST/HLSL/packoffset.hlsl
index 9deb63caa500a..9c928bd6d922e 100644
--- a/clang/test/AST/HLSL/packoffset.hlsl
+++ b/clang/test/AST/HLSL/packoffset.hlsl
@@ -4,7 +4,8 @@
 // CHECK: HLSLBufferDecl {{.*}} cbuffer A
 cbuffer A
 {
-    // CHECK-NEXT:-HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer CBuffer
+    // CHECK-NEXT:-HLSLResourceClassAttr {{.*}} <<invalid sloc>> Implicit CBuffer
+    // CHECK-NEXT:-HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer
     // CHECK-NEXT: VarDecl {{.*}} A1 'float4'
     // CHECK-NEXT: HLSLPackOffsetAttr {{.*}} 0 0
     float4 A1 : packoffset(c);
diff --git a/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl b/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl
index e264241e62e92..281d8be8addf0 100644
--- a/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl
+++ b/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl
@@ -17,10 +17,12 @@ float foo() {
 }
 // Make sure cbuffer/tbuffer works for PCH.
 // CHECK:HLSLBufferDecl 0x{{[0-9a-f]+}} <{{.*}}:7:1, line:9:1> line:7:9 imported <undeserialized declarations> cbuffer A
-// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer CBuffer
+// CHECK-NEXT:HLSLResourceClassAttr {{.*}} <<invalid sloc>> Implicit CBuffer
+// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer
 // CHECK-NEXT:`-VarDecl 0x[[A:[0-9a-f]+]] <line:8:3, col:9> col:9 imported used a 'float'
 // CHECK-NEXT:HLSLBufferDecl 0x{{[0-9a-f]+}} <line:11:1, line:13:1> line:11:9 imported <undeserialized declarations> tbuffer B
-// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit SRV TBuffer
+// CHECK-NEXT:HLSLResourceClassAttr {{.*}} <<invalid sloc>> Implicit SRV
+// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit TBuffer
 // CHECK-NEXT:`-VarDecl 0x[[B:[0-9a-f]+]] <line:12:3, col:9> col:9 imported used b 'float'
 // CHECK-NEXT:FunctionDecl 0x{{[0-9a-f]+}} <line:15:1, line:17:1> line:15:7 imported foo 'float ()'
 // CHECK-NEXT:CompoundStmt 0x{{[0-9a-f]+}} <col:13, line:17:1>
diff --git a/clang/test/AST/HLSL/resource_binding_attr.hlsl b/clang/test/AST/HLSL/resource_binding_attr.hlsl
index 9752494f5adc9..13957ad3c1fcc 100644
--- a/clang/test/AST/HLSL/resource_binding_attr.hlsl
+++ b/clang/test/AST/HLSL/resource_binding_attr.hlsl
@@ -1,15 +1,17 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -ast-dump -o - %s | FileCheck %s
 
-// CHECK:HLSLBufferDecl 0x[[CB:[0-9a-f]+]] {{.*}} line:7:9 cbuffer CB
-// CHECK-NEXT:HLSLResourceAttr 0x[[CB:[0-9a-f]+]] {{.*}} Implicit CBuffer CBuffer
+// CHECK:HLSLBufferDecl 0x[[CB:[0-9a-f]+]] {{.*}} line:8:9 cbuffer CB
+// CHECK-NEXT:HLSLResourceClassAttr 0x[[CB:[0-9a-f]+]] {{.*}} Implicit CBuffer
+// CHECK-NEXT:HLSLResourceAttr 0x[[CB:[0-9a-f]+]] {{.*}} Implicit CBuffer
 // CHECK-NEXT:HLSLResourceBindingAttr 0x{{[0-9a-f]+}} <col:14> "b3" "space2"
 // CHECK-NEXT:VarDecl 0x[[A:[0-9a-f]+]] {{.*}} col:9 used a 'float'
 cbuffer CB : register(b3, space2) {
   float a;
 }
 
-// CHECK:HLSLBufferDecl 0x[[TB:[0-9a-f]+]] {{.*}} line:15:9 tbuffer TB
-// CHECK-NEXT:HLSLResourceAttr 0x[[CB:[0-9a-f]+]] {{.*}} Implicit SRV TBuffer
+// CHECK:HLSLBufferDecl 0x[[TB:[0-9a-f]+]] {{.*}} line:17:9 tbuffer TB
+// CHECK-NEXT:HLSLResourceClassAttr 0x[[CB:[0-9a-f]+]] {{.*}} Implicit SRV
+// CHECK-NEXT:HLSLResourceAttr 0x[[CB:[0-9a-f]+]] {{.*}} Implicit TBuffer
 // CHECK-NEXT:HLSLResourceBindingAttr 0x{{[0-9a-f]+}} <col:14> "t2" "space1"
 // CHECK-NEXT:VarDecl 0x[[B:[0-9a-f]+]] {{.*}} col:9 used b 'float'
 tbuffer TB : register(t2, space1) {

>From c6fef886d6454bbb69405e3311a01de73dbf2b38 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Fri, 12 Jul 2024 16:34:01 -0700
Subject: [PATCH 22/26] add test case for static global var

---
 clang/test/SemaHLSL/resource_binding_attr_error.hlsl | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
index bd2e25f0db24e..698465eefbc08 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
@@ -37,6 +37,9 @@ cbuffer D : register(b 2, space 3) {}
 // expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
 static RWBuffer<float> U : register(u5);
 
+// expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
+static float sa : register(c1);
+
 void foo() {
   // expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
   RWBuffer<float> U : register(u3);

>From 4824f667341f6077c9675c793a2c033f2c07de25 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Fri, 12 Jul 2024 18:58:18 -0700
Subject: [PATCH 23/26] add groupshared test

---
 clang/test/SemaHLSL/resource_binding_attr_error.hlsl | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
index 698465eefbc08..0697c3c43ae3f 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
@@ -40,6 +40,9 @@ static RWBuffer<float> U : register(u5);
 // expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
 static float sa : register(c1);
 
+// expected-warning at +1 {{register 'c' used on type with no contents to allocate in a constant buffer}}
+ groupshared float fa[10] : register(c1);
+
 void foo() {
   // expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
   RWBuffer<float> U : register(u3);

>From 2779ab4d7c412cb90037f5cb5d35af87292940b6 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 15 Jul 2024 12:48:15 -0700
Subject: [PATCH 24/26] add groupshared attr handling, and improve tests for
 groupshared basic types / array types

---
 clang/lib/Sema/SemaDeclAttr.cpp               |  3 ++
 clang/lib/Sema/SemaHLSL.cpp                   | 38 +++++++++++++------
 .../SemaHLSL/resource_binding_attr_error.hlsl |  8 +++-
 3 files changed, 35 insertions(+), 14 deletions(-)

diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 20f46c003a464..2bd7bd4096450 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -7041,6 +7041,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
   case ParsedAttr::AT_HLSLSV_GroupIndex:
     handleSimpleAttribute<HLSLSV_GroupIndexAttr>(S, D, AL);
     break;
+  case ParsedAttr::AT_HLSLGroupSharedAddressSpace:
+    handleSimpleAttribute<HLSLGroupSharedAddressSpaceAttr>(S, D, AL);
+    break;
   case ParsedAttr::AT_HLSLSV_DispatchThreadID:
     S.HLSL().handleSV_DispatchThreadIDAttr(D, AL);
     break;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index f47c4f50f9b02..4e60eee76419c 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -611,13 +611,24 @@ RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *TheDecl) {
          "either VD or CBufferOrTBuffer should be set");
 
   RegisterBindingFlags Flags;
+
+  // check if the decl type is groupshared
+  if (TheDecl->hasAttr<HLSLGroupSharedAddressSpaceAttr>()) {
+    Flags.Other = true;
+    return Flags;
+  }
+
   if (!isDeclaredWithinCOrTBuffer(TheDecl)) {
     // make sure the type is a basic / numeric type
     if (TheVarDecl) {
       QualType TheQualTy = TheVarDecl->getType();
-      // a numeric variable will inevitably end up in $Globals buffer
-      if (TheQualTy->isIntegralType(S.getASTContext()) ||
-          TheQualTy->isFloatingType())
+      // a numeric variable or an array of numeric variables
+      // will inevitably end up in $Globals buffer
+      const clang::Type *TheBaseType = TheQualTy.getTypePtr();
+      while (TheBaseType->isArrayType())
+        TheBaseType = TheBaseType->getArrayElementTypeNoTypeQual();
+      if (TheBaseType->isIntegralType(S.getASTContext()) ||
+          TheBaseType->isFloatingType())
         Flags.DefaultGlobals = true;
     }
   }
@@ -631,6 +642,10 @@ RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *TheDecl) {
   } else if (TheVarDecl) {
     const HLSLResourceClassAttr *resClassAttr =
         getHLSLResourceClassAttrFromEitherDecl(TheVarDecl, CBufferOrTBuffer);
+    const clang::Type *TheBaseType = TheVarDecl->getType().getTypePtr();
+    while (TheBaseType->isArrayType())
+      TheBaseType = TheBaseType->getArrayElementTypeNoTypeQual();
+
     if (resClassAttr) {
       llvm::hlsl::ResourceClass DeclResourceClass =
           resClassAttr->getResourceClass();
@@ -650,16 +665,15 @@ RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *TheDecl) {
         break;
       }
     } else {
-      if (TheVarDecl->getType()->isBuiltinType())
+      if (TheBaseType->isArithmeticType())
         Flags.Basic = true;
-      else if (TheVarDecl->getType()->isAggregateType()) {
+      else if (TheBaseType->isRecordType()) {
         Flags.UDT = true;
-        QualType TheQualTy = TheVarDecl->getType();
-        if (const RecordType *TheRecordTy = TheQualTy->getAs<RecordType>()) {
-          const RecordDecl *TheRecordDecl = TheRecordTy->getDecl();
-          // recurse through members, set appropriate resource class flags.
-          setResourceClassFlagsFromRecordDecl(Flags, TheRecordDecl);
-        }
+        const RecordType *TheRecordTy = TheBaseType->getAs<RecordType>();
+        assert(TheRecordTy && "The Qual Type should be Record Type");
+        const RecordDecl *TheRecordDecl = TheRecordTy->getDecl();
+        // recurse through members, set appropriate resource class flags.
+        setResourceClassFlagsFromRecordDecl(Flags, TheRecordDecl);
       } else
         Flags.Other = true;
     }
@@ -750,7 +764,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
   // first, if "other" is set, emit an error
   if (Flags.Other) {
     S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
-        << Slot << getHLSLResourceTypeStr(S, TheDecl);
+        << Slot.substr(0, 1) << getHLSLResourceTypeStr(S, TheDecl);
     return;
   }
 
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
index 0697c3c43ae3f..b61e11243a313 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
@@ -40,8 +40,12 @@ static RWBuffer<float> U : register(u5);
 // expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
 static float sa : register(c1);
 
-// expected-warning at +1 {{register 'c' used on type with no contents to allocate in a constant buffer}}
- groupshared float fa[10] : register(c1);
+float x[2] : register(c2); // valid
+float y[2][2] : register(c3); // valid
+float z[2][2][3] : register(c4); // valid
+
+// expected-error at +1 {{register binding type 'c' not supported for variable of type 'groupshared float[10]}}
+groupshared float fa[10] : register(c5);
 
 void foo() {
   // expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}

>From 4223ca2111f5106efea79929ba81ffd6d2e399d0 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 15 Jul 2024 15:25:14 -0700
Subject: [PATCH 25/26] get resource class attr from non template
 specialization decls, use spelled attr in tests

---
 clang/lib/Sema/SemaHLSL.cpp                   | 34 ++++++-
 .../resource_binding_attr_error_udt.hlsl      | 96 +++++++++++--------
 2 files changed, 88 insertions(+), 42 deletions(-)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 4e60eee76419c..6501b2d7a5b13 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -549,15 +549,25 @@ getHLSLResourceAttrFromEitherDecl(VarDecl *VD,
 }
 
 void traverseType(QualType TheQualTy, RegisterBindingFlags &Flags) {
+  // if the member's type is a numeric type, set the ContainsNumeric flag
   if (TheQualTy->isIntegralOrEnumerationType() || TheQualTy->isFloatingType()) {
     Flags.ContainsNumeric = true;
     return;
   }
-  const RecordType *TheRecordTy = TheQualTy->getAs<RecordType>();
+
+  // otherwise, if the member's base type is not a record type, return
+  const clang::Type *TheBaseType = TheQualTy.getTypePtr();
+  while (TheBaseType->isArrayType())
+    TheBaseType = TheBaseType->getArrayElementTypeNoTypeQual();
+
+  const RecordType *TheRecordTy = TheBaseType->getAs<RecordType>();
   if (!TheRecordTy)
     return;
 
   RecordDecl *SubRecordDecl = TheRecordTy->getDecl();
+  bool resClassSet = false;
+  // if the member's base type is a ClassTemplateSpecializationDecl,
+  // check if it has a resource class attr
   if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(SubRecordDecl)) {
     auto TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
     TheRecordDecl = TheRecordDecl->getCanonicalDecl();
@@ -577,9 +587,29 @@ void traverseType(QualType TheQualTy, RegisterBindingFlags &Flags) {
       Flags.Sampler = true;
       break;
     }
+    resClassSet = true;
+  }
+  // otherwise, check if the member has a resource class attr
+  else if (auto *Attr = SubRecordDecl->getAttr<HLSLResourceClassAttr>()) {
+    llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
+    switch (DeclResourceClass) {
+    case llvm::hlsl::ResourceClass::SRV:
+      Flags.SRV = true;
+      break;
+    case llvm::hlsl::ResourceClass::UAV:
+      Flags.UAV = true;
+      break;
+    case llvm::hlsl::ResourceClass::CBuffer:
+      Flags.CBV = true;
+      break;
+    case llvm::hlsl::ResourceClass::Sampler:
+      Flags.Sampler = true;
+      break;
+    }
+    resClassSet = true;
   }
 
-  else if (SubRecordDecl->isCompleteDefinition()) {
+  if (!resClassSet) {
     for (auto Field : SubRecordDecl->fields()) {
       traverseType(Field->getType(), Flags);
     }
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
index 9c93224add077..29c7fdf593eff 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
@@ -1,103 +1,119 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
 
-// TODO: Implement "Buffer"
+// TODO: Implement "Buffer", we use a substitute UDT
+// to test the 't' binding type for this test.
+
+template<typename T>
+struct [[hlsl::resource_class(SRV)]] Buffer {
+  T x;
+};
+
+
+template<typename T>
+struct [[hlsl::resource_class(SRV)]] [[hlsl::resource_class(UAV)]] MultiClassBuffer {
+  T x;
+};
+
+// TODO: Implement "SamplerState", we use a substitute UDT
+// to test the 's' binding type for this test.
+struct [[hlsl::resource_class(Sampler)]] SamplerState {
+  int x;
+};
+
+// TODO: Implement "Texture2D", we use a substitute UDT
+// to test a non-templated 't' binding type for this test.
+struct [[hlsl::resource_class(UAV)]] Texture2D {
+  int x;
+};
+
 struct Eg1 {
   float f;
-  // Buffer<float> Buf;
+  Buffer<float> Buf;
   RWBuffer<float> RWBuf;
   };
-Eg1 e1 : /* register(t0) :*/ register(u0); 
+Eg1 e1 : register(t0) : register(u0); 
 // Valid: f is skipped, Buf is bound to t0, RWBuf is bound to u0
 
 
 struct Eg2 {
   float f;
-  // Buffer<float> Buf;
+  Buffer<float> Buf;
   RWBuffer<float> RWBuf;
   RWBuffer<float> RWBuf2;
   };
-Eg2 e2 : /* register(t0) :*/ register(u0); 
+Eg2 e2 : register(t0) : register(u0); 
 // Valid: f is skipped, Buf is bound to t0, RWBuf is bound to u0. 
 // RWBuf2 gets automatically assigned to u1 even though there is no explicit binding for u1.
 
-/*
 struct Eg3 {
-  float f;
-  // Buffer<float> Buf;
-  }; 
-Eg3 e3 : register(t0) : register(u0);
-// Valid: Buf gets bound to t0. Buf will also be bound to u0.
-*/
-
-struct Eg4 {
   struct Bar {
     RWBuffer<int> a;
     };
     Bar b;
 };
-Eg4 e4 : register(u0);
-// Valid: Bar, the struct within Eg4, has a valid resource that can be bound to t0. 
+Eg3 e3 : register(u0);
+// Valid: Bar, the struct within Eg3, has a valid resource that can be bound to t0. 
 
-/* Light up this test when SamplerState is implemented
-struct Eg5 {
+struct Eg4 {
   SamplerState s[3];
 };
 
-Eg5 e5 : register(s5);
+Eg4 e4 : register(s5);
 // Valid: the first sampler state object within Eg5's s is bound to slot 5
-*/
 
-struct Eg6 {
+
+struct Eg5 {
   float f;
 }; 
-// expected-warning at +1{{variable of type 'Eg6' bound to register type 't' does not contain a matching 'srv' resource}}
-Eg6 e6 : register(t0);
+// expected-warning at +1{{variable of type 'Eg5' bound to register type 't' does not contain a matching 'srv' resource}}
+Eg5 e5 : register(t0);
 
-struct Eg7 {
+struct Eg6 {
   struct Bar {
     float f;
   };
   Bar b;
 };
-// expected-warning at +1{{variable of type 'Eg7' bound to register type 't' does not contain a matching 'srv' resource}}
-Eg7 e7 : register(t0);
+// expected-warning at +1{{variable of type 'Eg6' bound to register type 't' does not contain a matching 'srv' resource}}
+Eg6 e6 : register(t0);
 
-struct Eg8 {
+struct Eg7 {
   RWBuffer<int> a;
 }; 
 // expected-warning at +1{{register 'c' used on type with no contents to allocate in a constant buffer}}
-Eg8 e8 : register(c0);
+Eg7 e7 : register(c0);
 
 
-struct Eg9{
+struct Eg8{
   // expected-error at +1{{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
   RWBuffer<int> a : register(u9);
 };
+Eg8 e8;
+
 
-Eg9 e9;
-/* Light up this test when Texture2D is implemented
 template<typename R>
-struct Eg10 {
+struct Eg9 {
     R b;
 };
-// expecting warning: {{variable of type 'Eg10' bound to register type 'u' does not contain a matching 'uav' resource}}
-Eg10<Texture2D> e10 : register(u0);
+// expecting warning: {{variable of type 'Eg9' bound to register type 'u' does not contain a matching 'uav' resource}}
+Eg9<Texture2D> e9 : register(u0);
 
 // invalid because after template expansion, there are no valid resources inside Eg10 to bind as a UAV.
-*/
 
-struct Eg11{
+
+struct Eg10{
   RWBuffer<int> a;
   RWBuffer<int> b;
 };
 
 // expected-error at +1{{conflicting register annotations: multiple register annotations detected for register type 'u'}}
-Eg11 e11 : register(u9) : register(u10);
+Eg10 e10 : register(u9) : register(u10);
 // expected-error at +1{{conflicting register annotations: multiple register annotations detected for register type 'u'}}
-Eg11 e11a : register(u9, space0) : register(u9, space1);
-struct Eg12{
+Eg10 e10a : register(u9, space0) : register(u9, space1);
+
+struct Eg11{
   RWBuffer<int> a;  
 };
 
 // expected-warning at +1{{register 'c' used on type with no contents to allocate in a constant buffer}}
-Eg12 e12 : register(c9);
\ No newline at end of file
+Eg11 e11 : register(c9);
\ No newline at end of file

>From b55b9f6ab5c8373b251544fe6f136b032d5b32ba Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 15 Jul 2024 15:29:14 -0700
Subject: [PATCH 26/26] remove unneeded type

---
 clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
index 29c7fdf593eff..db3a4b71aaaaf 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
@@ -8,12 +8,6 @@ struct [[hlsl::resource_class(SRV)]] Buffer {
   T x;
 };
 
-
-template<typename T>
-struct [[hlsl::resource_class(SRV)]] [[hlsl::resource_class(UAV)]] MultiClassBuffer {
-  T x;
-};
-
 // TODO: Implement "SamplerState", we use a substitute UDT
 // to test the 's' binding type for this test.
 struct [[hlsl::resource_class(Sampler)]] SamplerState {



More information about the cfe-commits mailing list