Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -368,6 +368,16 @@ let LangOpts = [BlocksSupported]; } +// Aggregate attribute subject match rules are abstract match rules that can't +// be used directly in #pragma clang attribute. Instead, users have to use +// subject match rules that correspond to attribute subjects that derive from +// the specified subject. +class AttrSubjectMatcherAggregateRule { + AttrSubject Subject = subject; +} + +def SubjectMatcherForNamed : AttrSubjectMatcherAggregateRule; + class Attr { // The various ways in which an attribute can be spelled in source list Spellings; @@ -656,7 +666,7 @@ StringArgument<"definedIn", 1>, BoolArgument<"generatedDeclaration", 1>]; let HasCustomParsing = 1; -// let Subjects = SubjectList<[Named]>; + let Subjects = SubjectList<[Named]>; let Documentation = [ExternalSourceSymbolDocs]; } Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -2511,12 +2511,6 @@ assert(checkAttributeAtMostNumArgs(S, Attr, 3) && "Invalid number of arguments in an external_source_symbol attribute"); - if (!isa(D)) { - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << Attr.getName() << ExpectedNamedDecl; - return; - } - StringRef Language; if (const auto *SE = dyn_cast_or_null(Attr.getArgAsExpr(0))) Language = SE->getString(); @@ -5765,12 +5759,18 @@ static bool handleCommonAttributeFeatures(Sema &S, Scope *scope, Decl *D, const AttributeList &Attr) { // Several attributes carry different semantics than the parsing requires, so - // those are opted out of the common handling. + // those are opted out of the majority of common handling. // // We also bail on unknown and ignored attributes because those are handled // as part of the target-specific handling logic. - if (Attr.hasCustomParsing() || - Attr.getKind() == AttributeList::UnknownAttribute) + if (Attr.hasCustomParsing()) { + // Still check whether the attribute appertains to the given subject as it + // might include a subject list even when it has custom parsing. + if (!Attr.diagnoseAppertainsTo(S, D)) + return true; + return false; + } + if (Attr.getKind() == AttributeList::UnknownAttribute) return false; // Check whether the attribute requires specific language extensions to be Index: test/Misc/pragma-attribute-supported-attributes-list.test =================================================================== --- test/Misc/pragma-attribute-supported-attributes-list.test +++ test/Misc/pragma-attribute-supported-attributes-list.test @@ -2,7 +2,7 @@ // The number of supported attributes should never go down! -// CHECK: #pragma clang attribute supports 57 attributes: +// CHECK: #pragma clang attribute supports 58 attributes: // CHECK-NEXT: AMDGPUFlatWorkGroupSize (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumSGPR (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumVGPR (SubjectMatchRule_function) @@ -23,6 +23,7 @@ // CHECK-NEXT: DisableTailCalls (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: EnableIf (SubjectMatchRule_function) // CHECK-NEXT: EnumExtensibility (SubjectMatchRule_enum) +// CHECK-NEXT: ExternalSourceSymbol ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable)) // CHECK-NEXT: FlagEnum (SubjectMatchRule_enum) // CHECK-NEXT: Flatten (SubjectMatchRule_function) // CHECK-NEXT: IFunc (SubjectMatchRule_function) Index: utils/TableGen/ClangAttrEmitter.cpp =================================================================== --- utils/TableGen/ClangAttrEmitter.cpp +++ utils/TableGen/ClangAttrEmitter.cpp @@ -1611,7 +1611,36 @@ struct PragmaClangAttributeSupport { std::vector Rules; - llvm::DenseMap SubjectsToRules; + + class RuleOrAggregateRuleSet { + std::vector Rules; + bool IsRule; + RuleOrAggregateRuleSet(ArrayRef Rules, + bool IsRule) + : Rules(Rules.begin(), Rules.end()), IsRule(IsRule) {} + + public: + bool isRule() const { return IsRule; } + + AttributeSubjectMatchRule &getRule() { + assert(IsRule && "not a rule!"); + return Rules[0]; + } + + ArrayRef getAggregateRuleSet() const { + return Rules; + } + + static RuleOrAggregateRuleSet + getRule(const AttributeSubjectMatchRule &Rule) { + return RuleOrAggregateRuleSet(Rule, /*IsRule=*/true); + } + static RuleOrAggregateRuleSet + getAggregateRuleSet(ArrayRef Rules) { + return RuleOrAggregateRuleSet(Rules, /*IsRule=*/false); + } + }; + llvm::DenseMap SubjectsToRules; PragmaClangAttributeSupport(RecordKeeper &Records); @@ -1626,6 +1655,15 @@ } // end anonymous namespace +static bool doesDeclDeriveFrom(const Record *D, const Record *Base) { + const Record *CurrentBase = D->getValueAsDef("Base"); + if (!CurrentBase) + return false; + if (CurrentBase == Base) + return true; + return doesDeclDeriveFrom(CurrentBase, Base); +} + PragmaClangAttributeSupport::PragmaClangAttributeSupport( RecordKeeper &Records) { std::vector MetaSubjects = @@ -1638,7 +1676,11 @@ SubjectContainer->getValueAsListOfDefs("Subjects"); for (const auto *Subject : ApplicableSubjects) { bool Inserted = - SubjectsToRules.try_emplace(Subject, MetaSubject, Constraint).second; + SubjectsToRules + .try_emplace(Subject, RuleOrAggregateRuleSet::getRule( + AttributeSubjectMatchRule(MetaSubject, + Constraint))) + .second; if (!Inserted) { PrintFatalError("Attribute subject match rules should not represent" "same attribute subjects."); @@ -1652,6 +1694,37 @@ for (const auto *Constraint : Constraints) MapFromSubjectsToRules(Constraint, MetaSubject, Constraint); } + + std::vector Aggregates = + Records.getAllDerivedDefinitions("AttrSubjectMatcherAggregateRule"); + std::vector DeclNodes = Records.getAllDerivedDefinitions("DDecl"); + for (const Record *Aggregate : Aggregates) { + Record *SubjectDecl = Aggregate->getValueAsDef("Subject"); + + // Gather sub-classes of the aggregate subject that act as attribute + // subject rules. + std::vector Rules; + for (const auto *D : DeclNodes) { + if (doesDeclDeriveFrom(D, SubjectDecl)) { + auto It = SubjectsToRules.find(D); + if (It == SubjectsToRules.end()) + continue; + if (!It->second.isRule() || It->second.getRule().isSubRule()) + continue; // Assume that the rule will be included as well. + Rules.push_back(It->second.getRule()); + } + } + + bool Inserted = + SubjectsToRules + .try_emplace(SubjectDecl, + RuleOrAggregateRuleSet::getAggregateRuleSet(Rules)) + .second; + if (!Inserted) { + PrintFatalError("Attribute subject match rules should not represent" + "same attribute subjects."); + } + } } static PragmaClangAttributeSupport & @@ -1738,24 +1811,25 @@ auto It = SubjectsToRules.find(Subject); assert(It != SubjectsToRules.end() && "This attribute is unsupported by #pragma clang attribute"); - AttributeSubjectMatchRule Rule = It->getSecond(); - // The rule might be language specific, so only subtract it from the given - // rules if the specific language options are specified. - std::vector LangOpts = Rule.getLangOpts(); - SS << " MatchRules.push_back(std::make_pair(" << Rule.getEnumValue() - << ", /*IsSupported=*/"; - if (!LangOpts.empty()) { - for (auto I = LangOpts.begin(), E = LangOpts.end(); I != E; ++I) { - std::string Part = (*I)->getValueAsString("Name"); - if ((*I)->getValueAsBit("Negated")) - SS << "!"; - SS << "LangOpts." + Part; - if (I + 1 != E) - SS << " || "; - } - } else - SS << "true"; - SS << "));\n"; + for (const auto &Rule : It->getSecond().getAggregateRuleSet()) { + // The rule might be language specific, so only subtract it from the given + // rules if the specific language options are specified. + std::vector LangOpts = Rule.getLangOpts(); + SS << " MatchRules.push_back(std::make_pair(" << Rule.getEnumValue() + << ", /*IsSupported=*/"; + if (!LangOpts.empty()) { + for (auto I = LangOpts.begin(), E = LangOpts.end(); I != E; ++I) { + std::string Part = (*I)->getValueAsString("Name"); + if ((*I)->getValueAsBit("Negated")) + SS << "!"; + SS << "LangOpts." + Part; + if (I + 1 != E) + SS << " || "; + } + } else + SS << "true"; + SS << "));\n"; + } } SS << "}\n\n"; OS << SS.str(); @@ -2937,7 +3011,8 @@ Field = 1U << 12, CXXMethod = 1U << 13, ObjCProtocol = 1U << 14, - Enum = 1U << 15 + Enum = 1U << 15, + Named = 1U << 16, }; uint32_t SubMask = 0; @@ -2956,23 +3031,24 @@ Name = R.getName(); uint32_t V = StringSwitch(Name) - .Case("Function", Func) - .Case("Var", Var) - .Case("ObjCMethod", ObjCMethod) - .Case("ParmVar", Param) - .Case("TypedefName", Type) - .Case("ObjCIvar", ObjCIVar) - .Case("ObjCProperty", ObjCProp) - .Case("Record", GenericRecord) - .Case("ObjCInterface", ObjCInterface) - .Case("ObjCProtocol", ObjCProtocol) - .Case("Block", Block) - .Case("CXXRecord", Class) - .Case("Namespace", Namespace) - .Case("Field", Field) - .Case("CXXMethod", CXXMethod) - .Case("Enum", Enum) - .Default(0); + .Case("Function", Func) + .Case("Var", Var) + .Case("ObjCMethod", ObjCMethod) + .Case("ParmVar", Param) + .Case("TypedefName", Type) + .Case("ObjCIvar", ObjCIVar) + .Case("ObjCProperty", ObjCProp) + .Case("Record", GenericRecord) + .Case("ObjCInterface", ObjCInterface) + .Case("ObjCProtocol", ObjCProtocol) + .Case("Block", Block) + .Case("CXXRecord", Class) + .Case("Namespace", Namespace) + .Case("Field", Field) + .Case("CXXMethod", CXXMethod) + .Case("Enum", Enum) + .Case("Named", Named) + .Default(0); if (!V) { // Something wasn't in our mapping, so be helpful and let the developer // know about it. @@ -2998,7 +3074,7 @@ case Type: return "ExpectedType"; case ObjCInterface: return "ExpectedObjectiveCInterface"; case ObjCProtocol: return "ExpectedObjectiveCProtocol"; - + // "GenericRecord" means struct, union or class; check the language options // and if not compiling for C++, strip off the class part. Note that this // relies on the fact that the context for this declares "Sema &S". @@ -3030,6 +3106,9 @@ case ObjCProtocol | ObjCInterface: return "ExpectedObjectiveCInterfaceOrProtocol"; case Field | Var: return "ExpectedFieldOrGlobalVar"; + + case Named: + return "ExpectedNamedDecl"; } PrintFatalError(S.getLoc(), @@ -3754,9 +3833,18 @@ for (const auto &Subject : llvm::enumerate(Subjects)) { if (Subject.index()) OS << ", "; - OS << Support.SubjectsToRules.find(Subject.value()) - ->getSecond() - .getEnumValueName(); + auto RuleSet = Support.SubjectsToRules.find(Subject.value())->getSecond(); + if (RuleSet.isRule()) { + OS << RuleSet.getRule().getEnumValueName(); + continue; + } + OS << "("; + for (const auto &Rule : llvm::enumerate(RuleSet.getAggregateRuleSet())) { + if (Rule.index()) + OS << ", "; + OS << Rule.value().getEnumValueName(); + } + OS << ")"; } OS << ")\n"; }