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

via cfe-commits cfe-commits at lists.llvm.org
Fri Aug 23 10:47:08 PDT 2024


Author: Joshua Batista
Date: 2024-08-23T10:47:05-07:00
New Revision: ebc4a66e9b525f7efc03053e3c7472d3e3fb0412

URL: https://github.com/llvm/llvm-project/commit/ebc4a66e9b525f7efc03053e3c7472d3e3fb0412
DIFF: https://github.com/llvm/llvm-project/commit/ebc4a66e9b525f7efc03053e3c7472d3e3fb0412.diff

LOG: Implement resource binding type prefix mismatch diagnostic infrastructure (#97103)

There are currently no diagnostics being emitted for when a resource is
bound to a register with an incorrect binding type prefix. For example,
a CBuffer type resource should be bound with a a binding type prefix of
'b', but if instead the prefix is 'u', no errors will be emitted. This
PR implements such diagnostics. The focus of this PR is to implement
both the flag setting and diagnostic emisison steps specified in the
relevant spec: https://github.com/microsoft/hlsl-specs/pull/230
The relevant issue is: https://github.com/llvm/llvm-project/issues/57886
This is a continuation / refresh of this PR:
https://github.com/llvm/llvm-project/pull/87578

Added: 
    clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
    clang/test/SemaHLSL/resource_binding_attr_error_other.hlsl
    clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
    clang/test/SemaHLSL/resource_binding_attr_error_silence_diags.hlsl
    clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl

Modified: 
    clang/include/clang/Basic/Attr.td
    clang/include/clang/Basic/DiagnosticGroups.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Parse/Parser.h
    clang/lib/Parse/ParseDecl.cpp
    clang/lib/Sema/HLSLExternalSemaSource.cpp
    clang/lib/Sema/SemaDeclAttr.cpp
    clang/lib/Sema/SemaHLSL.cpp
    clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl
    clang/test/AST/HLSL/cbuffer_tbuffer.hlsl
    clang/test/AST/HLSL/packoffset.hlsl
    clang/test/AST/HLSL/pch_hlsl_buffer.hlsl
    clang/test/AST/HLSL/resource_binding_attr.hlsl
    clang/test/Misc/pragma-attribute-supported-attributes-list.test
    clang/test/ParserHLSL/hlsl_resource_class_attr.hlsl
    clang/test/ParserHLSL/hlsl_resource_class_attr_error.hlsl
    clang/test/SemaHLSL/resource_binding_attr_error.hlsl

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 98bedfe20f5d98..a83e908899c83b 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4538,7 +4538,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];
@@ -4622,7 +4622,7 @@ def HLSLROV : InheritableAttr {
 
 def HLSLResourceClass : InheritableAttr {
   let Spellings = [CXX11<"hlsl", "resource_class">];
-  let Subjects = SubjectList<[Struct]>;
+  let Subjects = SubjectList<[Field]>;
   let LangOpts = [HLSL];
   let Args = [
 	EnumArgument<"ResourceClass", "llvm::hlsl::ResourceClass",

diff  --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 19c3f1e0433496..28d315f63e5c47 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1547,6 +1547,9 @@ def DXILValidation : DiagGroup<"dxil-validation">;
 // Warning for HLSL API availability
 def HLSLAvailability : DiagGroup<"hlsl-availability">;
 
+// Warnings for legacy binding behavior
+def LegacyConstantRegisterBinding : DiagGroup<"legacy-constant-register-binding">;
+
 // 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 4b6aadd635786a..ede3435d3e1b71 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12352,7 +12352,13 @@ 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 warn_hlsl_user_defined_type_missing_member: Warning<"binding type '%select{t|u|b|s|c}0' only applies to types containing %select{SRV resources|UAV resources|constant buffer resources|sampler state|numeric types}0">, InGroup<LegacyConstantRegisterBinding>;
+def err_hlsl_binding_type_mismatch: Error<"binding type '%select{t|u|b|s|c}0' only applies to %select{SRV resources|UAV resources|constant buffer resources|sampler state|numeric variables in the global scope}0">;
+def err_hlsl_binding_type_invalid: Error<"binding type '%0' is invalid">;
+def err_hlsl_duplicate_register_annotation: Error<"binding type '%select{t|u|b|s|c|i}0' cannot be applied more than once">;
+def warn_hlsl_register_type_c_packoffset: Warning<"binding type 'c' ignored in buffer declaration. Did you mean 'packoffset'?">, InGroup<LegacyConstantRegisterBinding>, DefaultError;
+def warn_hlsl_deprecated_register_type_b: Warning<"binding type 'b' only applies to constant buffers. The 'bool constant' binding type is no longer supported">, InGroup<LegacyConstantRegisterBinding>, DefaultError;
+def warn_hlsl_deprecated_register_type_i: Warning<"binding type 'i' ignored. The 'integer constant' binding type is no longer supported">, InGroup<LegacyConstantRegisterBinding>, DefaultError;
 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/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 99a0b0200fa06f..a7513069ff5da0 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3021,7 +3021,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");
@@ -3029,7 +3029,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 ed5d6ce90aa7d1..78d729c5ef7d8a 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2326,7 +2326,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/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index 89a0e391920cc6..9aacbe4ad9548e 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -503,9 +503,11 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
   Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer")
              .addSimpleTemplateParams(*SemaPtr, {"element_type"})
              .Record;
+
   onCompletion(Decl, [this](CXXRecordDecl *Decl) {
     setupBufferType(Decl, *SemaPtr, ResourceClass::UAV,
-                    ResourceKind::TypedBuffer, /*IsROV=*/false)
+                    ResourceKind::TypedBuffer,
+                    /*IsROV=*/false)
         .addArraySubscriptOperators()
         .completeDefinition();
   });

diff  --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 73d11ac972b020..1e074298ac5289 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -6889,6 +6889,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 df01549cc2eeb6..320e38b740a742 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -38,6 +38,14 @@ Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
   HLSLBufferDecl *Result = HLSLBufferDecl::Create(
       getASTContext(), LexicalParent, CBuffer, KwLoc, Ident, IdentLoc, LBrace);
 
+  // if CBuffer is false, then it's a TBuffer
+  auto RC = CBuffer ? llvm::hlsl::ResourceClass::CBuffer
+                    : llvm::hlsl::ResourceClass::SRV;
+  auto RK = CBuffer ? llvm::hlsl::ResourceKind::CBuffer
+                    : llvm::hlsl::ResourceKind::TBuffer;
+  Result->addAttr(HLSLResourceClassAttr::CreateImplicit(getASTContext(), RC));
+  Result->addAttr(HLSLResourceAttr::CreateImplicit(getASTContext(), RK));
+
   SemaRef.PushOnScopeChains(Result, BufferScope);
   SemaRef.PushDeclContext(BufferScope, Result);
 
@@ -459,7 +467,378 @@ void SemaHLSL::handleResourceClassAttr(Decl *D, const ParsedAttr &AL) {
   D->addAttr(HLSLResourceClassAttr::Create(getASTContext(), RC, ArgLoc));
 }
 
-void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) {
+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 ContainsNumeric = false;
+  bool DefaultGlobals = false;
+};
+
+static bool isDeclaredWithinCOrTBuffer(const Decl *TheDecl) {
+  return TheDecl && isa<HLSLBufferDecl>(TheDecl->getDeclContext());
+}
+
+// get the record decl from a var decl that we expect
+// represents a resource
+static CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *VD) {
+  const Type *Ty = VD->getType()->getPointeeOrArrayElementType();
+  assert(Ty && "Resource must have an element type.");
+
+  if (Ty->isBuiltinType())
+    return nullptr;
+
+  CXXRecordDecl *TheRecordDecl = Ty->getAsCXXRecordDecl();
+  assert(TheRecordDecl && "Resource should have a resource type declaration.");
+  return TheRecordDecl;
+}
+
+static void updateResourceClassFlagsFromDeclResourceClass(
+    RegisterBindingFlags &Flags, llvm::hlsl::ResourceClass DeclResourceClass) {
+  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;
+  }
+}
+
+template <typename T>
+static const T *getSpecifiedHLSLAttrFromRecordDecl(RecordDecl *TheRecordDecl) {
+  if (!TheRecordDecl)
+    return nullptr;
+
+  if (TheRecordDecl->hasAttr<T>())
+    return TheRecordDecl->getAttr<T>();
+  for (auto *FD : TheRecordDecl->fields()) {
+    const T *Attr = FD->getAttr<T>();
+    if (Attr)
+      return Attr;
+  }
+  return nullptr;
+}
+
+template <typename T>
+static const T *getSpecifiedHLSLAttrFromVarDecl(VarDecl *VD) {
+  RecordDecl *TheRecordDecl = nullptr;
+  if (VD) {
+    TheRecordDecl = getRecordDeclFromVarDecl(VD);
+    if (!TheRecordDecl)
+      return nullptr;
+  }
+
+  return getSpecifiedHLSLAttrFromRecordDecl<T>(TheRecordDecl);
+}
+
+static void updateFlagsFromType(QualType TheQualTy,
+                                RegisterBindingFlags &Flags);
+
+static void updateResourceClassFlagsFromRecordDecl(RegisterBindingFlags &Flags,
+                                                   const RecordDecl *RD) {
+  if (!RD)
+    return;
+
+  if (RD->isCompleteDefinition()) {
+    for (auto Field : RD->fields()) {
+      QualType T = Field->getType();
+      updateFlagsFromType(T, Flags);
+    }
+  }
+}
+
+static void updateFlagsFromType(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 clang::Type *TheBaseType = TheQualTy.getTypePtr();
+  while (TheBaseType->isArrayType())
+    TheBaseType = TheBaseType->getArrayElementTypeNoTypeQual();
+  // otherwise, if the member's base type is not a record type, return
+  const RecordType *TheRecordTy = TheBaseType->getAs<RecordType>();
+  if (!TheRecordTy)
+    return;
+
+  RecordDecl *SubRecordDecl = TheRecordTy->getDecl();
+  const HLSLResourceClassAttr *Attr =
+      getSpecifiedHLSLAttrFromRecordDecl<HLSLResourceClassAttr>(SubRecordDecl);
+  // find the attr if it's on the member, or on any of the member's fields
+  if (Attr) {
+    llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
+    updateResourceClassFlagsFromDeclResourceClass(Flags, DeclResourceClass);
+  }
+
+  // otherwise, dig deeper and recurse into the member
+  else {
+    updateResourceClassFlagsFromRecordDecl(Flags, SubRecordDecl);
+  }
+}
+
+static RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S,
+                                                         Decl *TheDecl) {
+
+  // Cbuffers and Tbuffers are HLSLBufferDecl types
+  HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(TheDecl);
+  // Samplers, UAVs, and SRVs are VarDecl types
+  VarDecl *TheVarDecl = dyn_cast<VarDecl>(TheDecl);
+
+  assert(((TheVarDecl && !CBufferOrTBuffer) ||
+          (!TheVarDecl && CBufferOrTBuffer)) &&
+         "either TheVarDecl 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 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;
+    }
+  }
+
+  if (CBufferOrTBuffer) {
+    Flags.Resource = true;
+    if (CBufferOrTBuffer->isCBuffer())
+      Flags.CBV = true;
+    else
+      Flags.SRV = true;
+  } else if (TheVarDecl) {
+    const HLSLResourceClassAttr *resClassAttr =
+        getSpecifiedHLSLAttrFromVarDecl<HLSLResourceClassAttr>(TheVarDecl);
+
+    if (resClassAttr) {
+      llvm::hlsl::ResourceClass DeclResourceClass =
+          resClassAttr->getResourceClass();
+      Flags.Resource = true;
+      updateResourceClassFlagsFromDeclResourceClass(Flags, DeclResourceClass);
+    } else {
+      const clang::Type *TheBaseType = TheVarDecl->getType().getTypePtr();
+      while (TheBaseType->isArrayType())
+        TheBaseType = TheBaseType->getArrayElementTypeNoTypeQual();
+      if (TheBaseType->isArithmeticType())
+        Flags.Basic = true;
+      else if (TheBaseType->isRecordType()) {
+        Flags.UDT = true;
+        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.
+        updateResourceClassFlagsFromRecordDecl(Flags, TheRecordDecl);
+      } else
+        Flags.Other = true;
+    }
+  }
+  return Flags;
+}
+
+enum class RegisterType { SRV, UAV, CBuffer, Sampler, C, I, Invalid };
+
+static RegisterType getRegisterType(StringRef Slot) {
+  switch (Slot[0]) {
+  case 't':
+  case 'T':
+    return RegisterType::SRV;
+  case 'u':
+  case 'U':
+    return RegisterType::UAV;
+  case 'b':
+  case 'B ':
+    return RegisterType::CBuffer;
+  case 's':
+  case 'S':
+    return RegisterType::Sampler;
+  case 'c':
+  case 'C':
+    return RegisterType::C;
+  case 'i':
+  case 'I':
+    return RegisterType::I;
+  default:
+    return RegisterType::Invalid;
+  }
+}
+
+static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *TheDecl,
+                                                RegisterType regType) {
+  // make sure that there are no two register annotations
+  // applied to the decl with the same register type
+  bool RegisterTypesDetected[5] = {false};
+
+  RegisterTypesDetected[static_cast<int>(regType)] = true;
+
+  // we need a static map to keep track of previous conflicts
+  // so that we don't emit the same error multiple times
+  static std::map<Decl *, std::set<RegisterType>> PreviousConflicts;
+
+  for (auto it = TheDecl->attr_begin(); it != TheDecl->attr_end(); ++it) {
+    if (HLSLResourceBindingAttr *attr =
+            dyn_cast<HLSLResourceBindingAttr>(*it)) {
+
+      RegisterType otherRegType = getRegisterType(attr->getSlot());
+      if (RegisterTypesDetected[static_cast<int>(otherRegType)]) {
+        if (PreviousConflicts[TheDecl].count(otherRegType))
+          continue;
+        int otherRegTypeNum = static_cast<int>(otherRegType);
+        S.Diag(TheDecl->getLocation(),
+               diag::err_hlsl_duplicate_register_annotation)
+            << otherRegTypeNum;
+        PreviousConflicts[TheDecl].insert(otherRegType);
+      } else {
+        RegisterTypesDetected[static_cast<int>(otherRegType)] = true;
+      }
+    }
+  }
+}
+
+static std::string getHLSLResourceTypeStr(Sema &S, Decl *TheDecl) {
+  if (VarDecl *TheVarDecl = dyn_cast<VarDecl>(TheDecl)) {
+    QualType TheQualTy = TheVarDecl->getType();
+    PrintingPolicy PP = S.getPrintingPolicy();
+    return QualType::getAsString(TheQualTy.split(), PP);
+  }
+  if (HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(TheDecl))
+    return CBufferOrTBuffer->isCBuffer() ? "cbuffer" : "tbuffer";
+}
+
+static void DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
+                                          Decl *TheDecl, RegisterType regType) {
+
+  // Samplers, UAVs, and SRVs are VarDecl types
+  VarDecl *TheVarDecl = dyn_cast<VarDecl>(TheDecl);
+  // Cbuffers and Tbuffers are HLSLBufferDecl types
+  HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(TheDecl);
+
+  // exactly one of these two types should be set
+  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");
+
+  int regTypeNum = static_cast<int>(regType);
+
+  // first, if "other" is set, emit an error
+  if (Flags.Other) {
+    S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << regTypeNum;
+    return;
+  }
+
+  // next, if multiple register annotations exist, check that none conflict.
+  ValidateMultipleRegisterAnnotations(S, TheDecl, regType);
+
+  // 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 HLSLResourceClassAttr *resClassAttr = nullptr;
+    if (CBufferOrTBuffer) {
+      resClassAttr = CBufferOrTBuffer->getAttr<HLSLResourceClassAttr>();
+    } else if (TheVarDecl) {
+      resClassAttr =
+          getSpecifiedHLSLAttrFromVarDecl<HLSLResourceClassAttr>(TheVarDecl);
+    }
+
+    assert(resClassAttr &&
+           "any decl that set the resource flag on analysis should "
+           "have a resource class attribute attached.");
+    const llvm::hlsl::ResourceClass DeclResourceClass =
+        resClassAttr->getResourceClass();
+
+    // confirm that the register type is bound to its expected resource class
+    static RegisterType ExpectedRegisterTypesForResourceClass[] = {
+        RegisterType::SRV,
+        RegisterType::UAV,
+        RegisterType::CBuffer,
+        RegisterType::Sampler,
+    };
+    assert((int)DeclResourceClass <
+               std::size(ExpectedRegisterTypesForResourceClass) &&
+           "DeclResourceClass has unexpected value");
+
+    RegisterType ExpectedRegisterType =
+        ExpectedRegisterTypesForResourceClass[(int)DeclResourceClass];
+    if (regType != ExpectedRegisterType) {
+      S.Diag(TheDecl->getLocation(), diag::err_hlsl_binding_type_mismatch)
+          << regTypeNum;
+    }
+    return;
+  }
+
+  // next, handle diagnostics for when the "basic" flag is set
+  if (Flags.Basic) {
+    if (Flags.DefaultGlobals) {
+      if (regType == RegisterType::CBuffer)
+        S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_b);
+      else if (regType != RegisterType::C)
+        S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << regTypeNum;
+      return;
+    }
+
+    if (regType == RegisterType::C)
+      S.Diag(ArgLoc, diag::warn_hlsl_register_type_c_packoffset);
+    else
+      S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << regTypeNum;
+
+    return;
+  }
+
+  // finally, we handle the udt case
+  if (Flags.UDT) {
+    const bool ExpectedRegisterTypesForUDT[] = {
+        Flags.SRV, Flags.UAV, Flags.CBV, Flags.Sampler, Flags.ContainsNumeric};
+    assert(regTypeNum < std::size(ExpectedRegisterTypesForUDT) &&
+           "regType has unexpected value");
+
+    if (!ExpectedRegisterTypesForUDT[regTypeNum])
+      S.Diag(TheDecl->getLocation(),
+             diag::warn_hlsl_user_defined_type_missing_member)
+          << regTypeNum;
+
+    return;
+  }
+}
+
+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;
+  }
   StringRef Space = "space0";
   StringRef Slot = "";
 
@@ -489,17 +868,17 @@ void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) {
     Slot = Str;
   }
 
+  RegisterType regType;
+
   // Validate.
   if (!Slot.empty()) {
-    switch (Slot[0]) {
-    case 'u':
-    case 'b':
-    case 's':
-    case 't':
-      break;
-    default:
-      Diag(ArgLoc, diag::err_hlsl_unsupported_register_type)
-          << Slot.substr(0, 1);
+    regType = getRegisterType(Slot);
+    if (regType == RegisterType::I) {
+      Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_i);
+      return;
+    }
+    if (regType == RegisterType::Invalid) {
+      Diag(ArgLoc, diag::err_hlsl_binding_type_invalid) << Slot.substr(0, 1);
       return;
     }
 
@@ -522,12 +901,12 @@ void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) {
     return;
   }
 
-  // FIXME: check reg type match decl. Issue
-  // https://github.com/llvm/llvm-project/issues/57886.
+  DiagnoseHLSLRegisterAttribute(SemaRef, ArgLoc, TheDecl, regType);
+
   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/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl b/clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl
index a98dc0f4ce4312..e6a2ea7c6d2dc6 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,16 @@ tbuffer B {
 }
 
 // AST:HLSLBufferDecl {{.*}}:11:1, line:20:1> line:11:9 cbuffer A
+// 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:-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 7204dcd16e0a92..5e558354cd3a03 100644
--- a/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl
+++ b/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl
@@ -1,12 +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:5:9 cbuffer CB
+// 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:11:9 tbuffer TB
+// 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 060288c2f7f76c..9c928bd6d922ed 100644
--- a/clang/test/AST/HLSL/packoffset.hlsl
+++ b/clang/test/AST/HLSL/packoffset.hlsl
@@ -4,6 +4,8 @@
 // CHECK: HLSLBufferDecl {{.*}} cbuffer A
 cbuffer A
 {
+    // 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 e9a6ea1a16312c..281d8be8addf09 100644
--- a/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl
+++ b/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl
@@ -17,8 +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: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: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 71900f2dbda550..13957ad3c1fcc7 100644
--- a/clang/test/AST/HLSL/resource_binding_attr.hlsl
+++ b/clang/test/AST/HLSL/resource_binding_attr.hlsl
@@ -1,13 +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:6:9 cbuffer CB
+// 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:13:9 tbuffer TB
+// 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) {

diff  --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index a7e425e3d5f431..5ebbd29b316bfa 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -83,7 +83,7 @@
 // CHECK-NEXT: GNUInline (SubjectMatchRule_function)
 // CHECK-NEXT: HIPManaged (SubjectMatchRule_variable)
 // CHECK-NEXT: HLSLROV (SubjectMatchRule_record_not_is_union)
-// CHECK-NEXT: HLSLResourceClass (SubjectMatchRule_record_not_is_union)
+// CHECK-NEXT: HLSLResourceClass (SubjectMatchRule_field)
 // CHECK-NEXT: Hot (SubjectMatchRule_function)
 // CHECK-NEXT: HybridPatchable (SubjectMatchRule_function)
 // CHECK-NEXT: IBAction (SubjectMatchRule_objc_method_is_instance)

diff  --git a/clang/test/ParserHLSL/hlsl_resource_class_attr.hlsl b/clang/test/ParserHLSL/hlsl_resource_class_attr.hlsl
index 410b4524f1c3df..4b002e2d890093 100644
--- a/clang/test/ParserHLSL/hlsl_resource_class_attr.hlsl
+++ b/clang/test/ParserHLSL/hlsl_resource_class_attr.hlsl
@@ -1,31 +1,31 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -ast-dump -o - %s | FileCheck %s
 
 
-// CHECK: -HLSLResourceClassAttr 0x{{[0-9a-f]+}} <col:31> SRV
-struct [[hlsl::resource_class(SRV)]] Eg1 {
-  int i;  
+// CHECK: -HLSLResourceClassAttr 0x{{[0-9a-f]+}} <col:26> SRV
+struct Eg1 {
+  [[hlsl::resource_class(SRV)]] int i;  
 };
 
 Eg1 e1;
 
-// CHECK: -CXXRecordDecl 0x{{[0-9a-f]+}} <line:13:1, line:15:1> line:13:38 referenced struct Eg2 definition
-// CHECK: -HLSLResourceClassAttr 0x{{[0-9a-f]+}} <col:31> UAV
-struct [[hlsl::resource_class(UAV)]] Eg2 {
-  int i;
+// CHECK: -CXXRecordDecl 0x{{[0-9a-f]+}} <line:13:1, line:15:1> line:13:8 referenced struct Eg2 definition
+// CHECK: -HLSLResourceClassAttr 0x{{[0-9a-f]+}} <col:26> UAV
+struct Eg2 {
+  [[hlsl::resource_class(UAV)]] int i;
 };
 Eg2 e2;
 
-// CHECK: -CXXRecordDecl 0x{{[0-9a-f]+}} <line:20:1, line:22:1> line:20:42 referenced struct Eg3 definition
-// CHECK: -HLSLResourceClassAttr 0x{{[0-9a-f]+}} <col:31> CBuffer
-struct [[hlsl::resource_class(CBuffer)]] Eg3 {
-  int i;
+// CHECK: -CXXRecordDecl 0x{{[0-9a-f]+}} <line:20:1, line:22:1> line:20:8 referenced struct Eg3 definition
+// CHECK: -HLSLResourceClassAttr 0x{{[0-9a-f]+}} <col:26> CBuffer
+struct Eg3 {
+  [[hlsl::resource_class(CBuffer)]] int i;
 }; 
 Eg3 e3;
 
-// CHECK: -CXXRecordDecl 0x{{[0-9a-f]+}} <line:27:1, line:29:1> line:27:42 referenced struct Eg4 definition
-// CHECK: -HLSLResourceClassAttr 0x{{[0-9a-f]+}} <col:31> Sampler
-struct [[hlsl::resource_class(Sampler)]] Eg4 {
-  int i;
+// CHECK: -CXXRecordDecl 0x{{[0-9a-f]+}} <line:27:1, line:29:1> line:27:8 referenced struct Eg4 definition
+// CHECK: -HLSLResourceClassAttr 0x{{[0-9a-f]+}} <col:26> Sampler
+struct Eg4 {
+  [[hlsl::resource_class(Sampler)]] int i;
 };
 Eg4 e4;
 

diff  --git a/clang/test/ParserHLSL/hlsl_resource_class_attr_error.hlsl b/clang/test/ParserHLSL/hlsl_resource_class_attr_error.hlsl
index 00fcd769760bba..76bed2f0607830 100644
--- a/clang/test/ParserHLSL/hlsl_resource_class_attr_error.hlsl
+++ b/clang/test/ParserHLSL/hlsl_resource_class_attr_error.hlsl
@@ -1,15 +1,22 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -ast-dump -o - %s -verify
 
+struct Eg1 {
 // expected-error at +1{{'resource_class' attribute takes one argument}}
-struct [[hlsl::resource_class()]] Eg1 {
-  int i;  
+  [[hlsl::resource_class()]] int i;  
 };
 
 Eg1 e1;
 
+struct Eg2 {
 // expected-warning at +1{{ResourceClass attribute argument not supported: gibberish}}
-struct [[hlsl::resource_class(gibberish)]] Eg2 {
-  int i;  
+  [[hlsl::resource_class(gibberish)]] int i;  
 };
 
 Eg2 e2;
+
+// expected-warning at +1{{'resource_class' attribute only applies to non-static data members}}
+struct [[hlsl::resource_class(SRV)]] Eg3 {
+  int i;  
+};
+
+Eg3 e3;

diff  --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
index 2f8aa098db701a..6a0b5956545dd8 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
@@ -1,9 +1,15 @@
 // 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'}}
-float a : register(c0, space1);
+template<typename T>
+struct MyTemplatedSRV {
+  [[hlsl::resource_class(SRV)]] T x;
+};
+
+// 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);
 
-// expected-error at +1 {{invalid resource class specifier 'i' used; expected 'b', 's', 't', or 'u'}}
+// expected-error at +1 {{binding type 'i' ignored. The 'integer constant' binding type is no longer supported}}
 cbuffer b : register(i0) {
 
 }
@@ -33,28 +39,40 @@ 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}}
-static RWBuffer<float> U : register(u5);
+// expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
+static MyTemplatedSRV<float> U : register(u5);
+
+// expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
+static float sa : 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 {{binding type 'c' only applies to numeric variables in the global scope}}
+groupshared float fa[10] : register(c5);
 
 void foo() {
-  // expected-warning at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
-  RWBuffer<float> U : register(u3);
+  // expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
+  MyTemplatedSRV<float> U : register(u3);
 }
 void foo2() {
-  // expected-warning at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
-  extern RWBuffer<float> U2 : register(u5);
+  // expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
+  extern MyTemplatedSRV<float> U2 : register(u5);
 }
-// FIXME: expect-error once fix https://github.com/llvm/llvm-project/issues/57886.
+
+// expected-error at +1 {{binding type 'u' only applies to UAV resources}}
 float b : register(u0, space1);
 
-// expected-warning at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
-void bar(RWBuffer<float> U : register(u3)) {
+// expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
+void bar(MyTemplatedSRV<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}}
-  RWBuffer<float> U : register(u3);
+struct S {  
+  // expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
+  MyTemplatedSRV<float> U : register(u3);
 };
+
+// expected-error at +1 {{binding type 'z' is invalid}}
+MyTemplatedSRV<float> U3 : register(z5);

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 00000000000000..0a547ed66af0a2
--- /dev/null
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
+
+// expected-error at +1{{binding type 't' only applies to SRV resources}}
+float f1 : register(t0);
+
+
+float f2 : register(c0);
+
+// expected-error at +1{{binding type 'b' only applies to constant buffers. The 'bool constant' binding type is no longer supported}}
+float f3 : register(b9);
+
+// expected-error at +1{{binding type 'i' ignored. The 'integer constant' binding type is no longer supported}}
+float f4 : register(i9);
+
+// expected-error at +1{{binding type 'x' is invalid}}
+float f5 : register(x9);
+
+cbuffer g_cbuffer1 {
+// expected-error at +1{{binding type 'c' ignored in buffer declaration. Did you mean 'packoffset'?}}
+    float f6 : register(c2);
+};
+
+tbuffer g_tbuffer1 {
+// expected-error at +1{{binding type 'c' ignored in buffer declaration. Did you mean 'packoffset'?}}
+    float f7 : register(c2);
+};
+
+cbuffer g_cbuffer2 {
+// expected-error at +1{{binding type 'b' only applies to constant buffer resources}}
+    float f8 : register(b2);
+};
+
+tbuffer g_tbuffer2 {
+// expected-error at +1{{binding type 'i' ignored. The 'integer constant' binding type is no longer supported}}
+    float f9 : register(i2);
+};
+
+// expected-error at +1{{binding type 'c' only applies to numeric variables in the global scope}}
+RWBuffer<float> f10 : register(c3);

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 00000000000000..4c9e9a6b44c928
--- /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);

diff  --git a/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
new file mode 100644
index 00000000000000..c40d1d7f60b347
--- /dev/null
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
@@ -0,0 +1,49 @@
+// 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
+
+
+template<typename T>
+struct MyTemplatedSRV {
+  [[hlsl::resource_class(SRV)]] T x;
+};
+
+struct MySRV {
+  [[hlsl::resource_class(SRV)]] int x;
+};
+
+struct MySampler {
+  [[hlsl::resource_class(Sampler)]] int x;
+};
+
+struct MyUAV {
+  [[hlsl::resource_class(UAV)]] int x;
+};
+
+struct MyCBuffer {
+  [[hlsl::resource_class(CBuffer)]] int x;
+};
+
+
+// expected-error at +1  {{binding type 'i' ignored. The 'integer constant' binding type is no longer supported}}
+MySRV invalid : register(i2);
+
+// expected-error at +1  {{binding type 't' only applies to SRV resources}}
+MyUAV a : register(t2, space1);
+
+// expected-error at +1  {{binding type 'u' only applies to UAV resources}}
+MySampler b : register(u2, space1);
+
+// expected-error at +1  {{binding type 'b' only applies to constant buffer resources}}
+MyTemplatedSRV<int> c : register(b2);
+
+// expected-error at +1  {{binding type 's' only applies to sampler state}}
+MyUAV d : register(s2, space1);
+
+// empty binding prefix cases:
+// expected-error at +1 {{expected identifier}}
+MyTemplatedSRV<int> e: register();
+
+// expected-error at +1 {{expected identifier}}
+MyTemplatedSRV<int> f: register("");

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 00000000000000..e63f264452da79
--- /dev/null
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_silence_diags.hlsl
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only -Wno-legacy-constant-register-binding %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);
+
+Eg12 bar : register(i1);
+
+struct Eg7 {
+  struct Bar {
+    float f;
+  };
+  Bar b;
+};
+Eg7 e7 : register(t0);

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 00000000000000..f8e38b6d2851d9
--- /dev/null
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
@@ -0,0 +1,128 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
+
+template<typename T>
+struct MyTemplatedUAV {
+  [[hlsl::resource_class(UAV)]] T x;
+};
+
+struct MySRV {
+  [[hlsl::resource_class(SRV)]] int x;
+};
+
+struct MySampler {
+  [[hlsl::resource_class(Sampler)]] int x;
+};
+
+struct MyUAV {
+  [[hlsl::resource_class(UAV)]] int x;
+};
+
+struct MyCBuffer {
+  [[hlsl::resource_class(CBuffer)]] int x;
+};
+
+// Valid: f is skipped, SRVBuf is bound to t0, UAVBuf is bound to u0
+struct Eg1 {
+  float f;
+  MySRV SRVBuf;
+  MyUAV UAVBuf;
+  };
+Eg1 e1 : register(t0) : register(u0); 
+
+// Valid: f is skipped, SRVBuf is bound to t0, UAVBuf is bound to u0. 
+// UAVBuf2 gets automatically assigned to u1 even though there is no explicit binding for u1.
+struct Eg2 {
+  float f;
+  MySRV SRVBuf;
+  MyUAV UAVBuf;
+  MyUAV UAVBuf2;
+  };
+Eg2 e2 : register(t0) : register(u0); 
+
+// Valid: Bar, the struct within Eg3, has a valid resource that can be bound to t0. 
+struct Eg3 {
+  struct Bar {
+    MyUAV a;
+  };
+  Bar b;
+};
+Eg3 e3 : register(u0);
+
+// Valid: the first sampler state object within 's' is bound to slot 5
+struct Eg4 {
+  MySampler s[3];
+};
+
+Eg4 e4 : register(s5);
+
+
+struct Eg5 {
+  float f;
+}; 
+// expected-warning at +1{{binding type 't' only applies to types containing SRV resources}}
+Eg5 e5 : register(t0);
+
+struct Eg6 {
+  float f;
+}; 
+// expected-warning at +1{{binding type 'u' only applies to types containing UAV resources}}
+Eg6 e6 : register(u0);
+
+struct Eg7 {
+  float f;
+}; 
+// expected-warning at +1{{binding type 'b' only applies to types containing constant buffer resources}}
+Eg7 e7 : register(b0);
+
+struct Eg8 {
+  float f;
+}; 
+// expected-warning at +1{{binding type 's' only applies to types containing sampler state}}
+Eg8 e8 : register(s0);
+
+struct Eg9 {
+  MySRV s;
+}; 
+// expected-warning at +1{{binding type 'c' only applies to types containing numeric types}}
+Eg9 e9 : register(c0);
+
+struct Eg10{
+  // expected-error at +1{{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
+  MyTemplatedUAV<int> a : register(u9);
+};
+Eg10 e10;
+
+
+template<typename R>
+struct Eg11 {
+    R b;
+};
+// expected-warning at +1{{binding type 'u' only applies to types containing UAV resources}}
+Eg11<MySRV> e11 : register(u0);
+// invalid because after template expansion, there are no valid resources inside Eg11 to bind as a UAV, only an SRV
+
+
+struct Eg12{
+  MySRV s1;
+  MySRV s2;
+};
+// expected-warning at +3{{binding type 'u' only applies to types containing UAV resources}}
+// expected-warning at +2{{binding type 'u' only applies to types containing UAV resources}}
+// expected-error at +1{{binding type 'u' cannot be applied more than once}}
+Eg12 e12 : register(u9) : register(u10);
+
+struct Eg13{
+  MySRV s1;
+  MySRV s2;
+};
+// expected-warning at +4{{binding type 'u' only applies to types containing UAV resources}}
+// expected-warning at +3{{binding type 'u' only applies to types containing UAV resources}}
+// expected-warning at +2{{binding type 'u' only applies to types containing UAV resources}}
+// expected-error at +1{{binding type 'u' cannot be applied more than once}}
+Eg13 e13 : register(u9) : register(u10) : register(u11);
+
+struct Eg14{
+ MyTemplatedUAV<int> r1;  
+};
+// expected-warning at +1{{binding type 't' only applies to types containing SRV resources}}
+Eg14 e14 : register(t9);


        


More information about the cfe-commits mailing list