diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -875,12 +875,33 @@ } Rules.clear(); } else { - for (const auto &Rule : StrictSubjectMatchRuleSet) { - if (Rules.erase(Rule.first)) { + // Each rule in Rules must be a strict subset of the attribute's + // SubjectMatch rules. I.e. we're allowed to use + // `apply_to=variables(is_global)` on an attrubute with SubjectList<[Var]>, + // but should not allow `apply_to=variables` on an attribute which has + // `SubjectList<[GlobalVar]>`. + for (const auto &StrictRule : StrictSubjectMatchRuleSet) { + // First, check for exact match. + if (Rules.erase(StrictRule.first)) { // Add the rule to the set of attribute receivers only if it's supported // in the current language mode. - if (Rule.second) - SubjectMatchRules.push_back(Rule.first); + if (StrictRule.second) + SubjectMatchRules.push_back(StrictRule.first); + } + } + // Check remaining rules for subset matches. + auto RulesToCheck = Rules; + for (const auto &Rule : RulesToCheck) { + attr::SubjectMatchRule MatchRule = attr::SubjectMatchRule(Rule.first); + if (auto ParentRule = getParentAttrMatcherRule(MatchRule)) { + if (llvm::any_of(StrictSubjectMatchRuleSet, + [ParentRule](const auto &StrictRule) { + return StrictRule.first == *ParentRule && + StrictRule.second; // IsEnabled + })) { + SubjectMatchRules.push_back(MatchRule); + Rules.erase(MatchRule); + } } } } diff --git a/clang/test/Parser/pragma-attribute.cpp b/clang/test/Parser/pragma-attribute.cpp --- a/clang/test/Parser/pragma-attribute.cpp +++ b/clang/test/Parser/pragma-attribute.cpp @@ -195,3 +195,12 @@ #pragma clang attribute pop #pragma clang attribute push([[clang::uninitialized]], apply_to = any(variable(is_parameter), variable(unless(is_parameter)))) // expected-error {{attribute 'uninitialized' can't be applied to 'variable(is_parameter)', and 'variable(unless(is_parameter))'}} #pragma clang attribute pop +// We're allowed to apply attributes to subsets of allowed subjects. +#pragma clang attribute push([[clang::no_destroy]], apply_to = variable) +#pragma clang attribute pop +#pragma clang attribute push([[clang::no_destroy]], apply_to = variable(is_thread_local)) +#pragma clang attribute pop +#pragma clang attribute push([[clang::no_destroy]], apply_to = variable(is_global)) +#pragma clang attribute pop +#pragma clang attribute push([[clang::no_destroy]], apply_to = any(variable(is_parameter), variable(unless(is_parameter)))) +#pragma clang attribute pop diff --git a/clang/test/Sema/pragma-attribute-strict-subjects.c b/clang/test/Sema/pragma-attribute-strict-subjects.c --- a/clang/test/Sema/pragma-attribute-strict-subjects.c +++ b/clang/test/Sema/pragma-attribute-strict-subjects.c @@ -55,9 +55,9 @@ // expected-error@-1 {{attribute 'abi_tag' can't be applied to 'enum'}} #pragma clang attribute pop -#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(enum_constant, function, record(unless(is_union)), variable, variable(is_parameter))) +#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(enum_constant, function, record(unless(is_union)), variable, variable(is_parameter), enum)) // FIXME: comma in this diagnostic is wrong. -// expected-error@-2 {{attribute 'abi_tag' can't be applied to 'enum_constant', and 'variable(is_parameter)'}} +// expected-error@-2 {{attribute 'abi_tag' can't be applied to 'enum_constant', and 'enum'}} #pragma clang attribute pop #pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(function, record(unless(is_union)), enum))