[clang] Implement resource binding type prefix mismatch diagnostic infrastructure (PR #97103)
Damyan Pepper via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 14 14:28:48 PDT 2024
================
@@ -459,7 +467,506 @@ 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;
+};
+
+bool isDeclaredWithinCOrTBuffer(const Decl *TheDecl) {
+ if (!TheDecl)
+ return false;
+
+ // Traverse up the parent contexts
+ const DeclContext *context = TheDecl->getDeclContext();
+ if (isa<HLSLBufferDecl>(context)) {
+ return true;
+ }
+
+ return false;
+}
+
+const CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *VD) {
+ const Type *Ty = VD->getType()->getPointeeOrArrayElementType();
+ assert(Ty && "Resource class must have an element type.");
+
+ if (const auto *TheBuiltinTy = dyn_cast<BuiltinType>(Ty))
+ return nullptr;
+
+ const CXXRecordDecl *TheRecordDecl = Ty->getAsCXXRecordDecl();
+ assert(TheRecordDecl &&
+ "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 HLSLResourceClassAttr *
+getHLSLResourceClassAttrFromEitherDecl(VarDecl *VD,
+ HLSLBufferDecl *CBufferOrTBuffer) {
+
+ if (VD) {
+ const CXXRecordDecl *TheRecordDecl = getRecordDeclFromVarDecl(VD);
+ if (!TheRecordDecl)
+ return nullptr;
+
+ // the resource class attr could be on the record decl itself or on one of
+ // its fields (the resource handle, most commonly)
+ const auto *Attr = TheRecordDecl->getAttr<HLSLResourceClassAttr>();
+ if (!Attr) {
+ for (auto *FD : TheRecordDecl->fields()) {
+ Attr = FD->getAttr<HLSLResourceClassAttr>();
+ if (Attr)
+ break;
+ }
+ }
+ 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) {
+
+ if (VD) {
+ const CXXRecordDecl *TheRecordDecl = getRecordDeclFromVarDecl(VD);
+ if (!TheRecordDecl)
+ return nullptr;
+
+ // the resource attr could be on the record decl itself or on one of
+ // its fields (the resource handle, most commonly)
+ const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
+ if (!Attr) {
+ for (auto *FD : TheRecordDecl->fields()) {
+ Attr = FD->getAttr<HLSLResourceAttr>();
+ if (Attr)
+ break;
+ }
+ }
+ 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 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;
+ }
+
+ // 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();
+ const auto *Attr = TheRecordDecl->getAttr<HLSLResourceClassAttr>();
+ if (!Attr) {
+ for (auto *FD : TheRecordDecl->fields()) {
+ Attr = FD->getAttr<HLSLResourceClassAttr>();
+ if (Attr)
+ break;
+ }
+ }
+ 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;
+ }
+ // 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;
+ }
+
+ if (!resClassSet) {
+ for (auto Field : SubRecordDecl->fields()) {
+ traverseType(Field->getType(), Flags);
+ }
+ }
+}
+
+void setResourceClassFlagsFromRecordDecl(RegisterBindingFlags &Flags,
+ const RecordDecl *RD) {
+ if (!RD)
+ return;
+
+ if (RD->isCompleteDefinition()) {
+ for (auto Field : RD->fields()) {
+ QualType T = Field->getType();
+ traverseType(T, Flags);
+ }
----------------
damyanp wrote:
nit: since there's an extremely similar loop to this in `traverseType` as well, it smells a bit to me that we need to have it repeated like this.
https://github.com/llvm/llvm-project/pull/97103
More information about the cfe-commits
mailing list