Skip to content

Commit 3089120

Browse files
authored
Redesign handling of anyAppleOS availability attribute (llvm#190817)
Previously, when processing an anyAppleOS availability attribute, clang replaced it with an implicit platform-specific attribute (e.g., ios, macos) inferred for the current target. Only the introduced version of the original anyAppleOS attribute was preserved (as a field on the inferred attr). This was insufficient for clients such as Swift that need access to the full original attribute, including deprecated, obsoleted, and message fields. This patch preserves the original anyAppleOS attribute on the decl and attaches the inferred platform-specific attribute to it as a child via the new InferredAttr field. Most callers use getEffectiveAttr() to transparently get the inferred attr when present, preserving existing behavior. Fix-it hints use the presence of an inferred attr to decide whether to emit "anyAppleOS" or a platform-specific name in the @available expression. The one behavioral change is in documentation XML, where availability info is now emitted for both the anyAppleOS attr and the inferred platform-specific attr. When an explicit platform-specific attribute (e.g. ios(introduced=26.0)) conflicts with an anyAppleOS-derived attribute for the same platform, the explicit attribute wins: the anyAppleOS attribute is erased from the decl so only the explicit one is used.
1 parent 47b5ad2 commit 3089120

File tree

16 files changed

+356
-127
lines changed

16 files changed

+356
-127
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,10 @@ class VariadicEnumArgument<string name, string type, bit is_string,
341341
}
342342

343343
// Represents an attribute wrapped by another attribute.
344-
class WrappedAttr<string name, bit opt = 0> : Argument<name, opt>;
344+
class WrappedAttr<string name, bit opt = 0, string type = ""> : Argument<name, opt> {
345+
// C++ class name for the wrapped attr (without the ' *' suffix).
346+
string AttrType = type;
347+
}
345348

346349
// This handles one spelling of an attribute.
347350
class Spelling<string name, string variety, int version = 1> {
@@ -1108,7 +1111,7 @@ def Availability : InheritableAttr {
11081111
BoolArgument<"unavailable">, StringArgument<"message">,
11091112
BoolArgument<"strict">, StringArgument<"replacement">,
11101113
IntArgument<"priority">, IdentifierArgument<"environment">,
1111-
VersionArgument<"origAnyAppleOSVersion", 0, 1>];
1114+
WrappedAttr<"InferredAttr", 1, "AvailabilityAttr">];
11121115
let AdditionalMembers =
11131116
[{static llvm::StringRef getPrettyPlatformName(llvm::StringRef Platform) {
11141117
return llvm::StringSwitch<llvm::StringRef>(Platform)
@@ -1246,6 +1249,20 @@ static llvm::Triple::OSType getOSType(llvm::StringRef Platform) {
12461249
.Default(OSType::UnknownOS);
12471250
}
12481251

1252+
const AvailabilityAttr *getEffectiveAttr() const {
1253+
if (auto *Inf = getInferredAttrAs())
1254+
return Inf;
1255+
return this;
1256+
}
1257+
AvailabilityAttr *getEffectiveAttr() {
1258+
return const_cast<AvailabilityAttr *>(
1259+
static_cast<const AvailabilityAttr *>(this)->getEffectiveAttr());
1260+
}
1261+
VersionTuple getEffectiveIntroduced() const { return getEffectiveAttr()->getIntroduced(); }
1262+
const IdentifierInfo *getEffectiveEnvironment() const {
1263+
return getEffectiveAttr()->getEnvironment();
1264+
}
1265+
12491266
}];
12501267
let HasCustomParsing = 1;
12511268
let InheritEvenIfAlreadyPresent = 1;

clang/include/clang/Basic/AttrDocs.td

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2198,6 +2198,7 @@ attributes are ignored. Supported platforms are:
21982198
``visionOS``
21992199
``visionOSApplicationExtension``
22002200
``driverkit``
2201+
``anyAppleOS``
22012202
``swift``
22022203
``android``
22032204
``fuchsia``
@@ -2221,6 +2222,7 @@ Some platforms have alias names:
22212222
``maccatalyst_app_extension``
22222223
``visionos``
22232224
``visionos_app_extension``
2225+
``anyappleos``
22242226
``shadermodel``
22252227

22262228
Supported environment names for the ShaderModel platform:
@@ -2241,6 +2243,13 @@ Supported environment names for the ShaderModel platform:
22412243
``amplification``
22422244
``library``
22432245

2246+
The special platform ``anyAppleOS`` (alias: ``anyappleos``) is a shorthand that
2247+
applies the availability attribute to all Apple Darwin platforms. An explicit
2248+
platform-specific availability attribute takes precedence over an ``anyAppleOS``
2249+
attribute for that platform. Versions specified with ``anyAppleOS`` must be at
2250+
least 26.0, which is the first OS release where all supported Apple platforms
2251+
share a unified version number.
2252+
22442253
A declaration can typically be used even when deploying back to a platform
22452254
version prior to when the declaration was introduced. When this happens, the
22462255
declaration is `weakly linked

clang/include/clang/Sema/Sema.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4996,7 +4996,16 @@ class Sema final : public SemaBase {
49964996
StringRef Message, bool IsStrict, StringRef Replacement,
49974997
AvailabilityMergeKind AMK, int Priority,
49984998
const IdentifierInfo *IIEnvironment,
4999-
VersionTuple OrigAnyAppleOSVersion = {});
4999+
const IdentifierInfo *InferredPlatformII = nullptr);
5000+
5001+
AvailabilityAttr *mergeAndInferAvailabilityAttr(
5002+
NamedDecl *D, const AttributeCommonInfo &CI,
5003+
const IdentifierInfo *Platform, bool Implicit, VersionTuple Introduced,
5004+
VersionTuple Deprecated, VersionTuple Obsoleted, bool IsUnavailable,
5005+
StringRef Message, bool IsStrict, StringRef Replacement,
5006+
AvailabilityMergeKind AMK, int Priority,
5007+
const IdentifierInfo *IIEnvironment,
5008+
const IdentifierInfo *InferredPlatformII);
50005009

50015010
TypeVisibilityAttr *
50025011
mergeTypeVisibilityAttr(Decl *D, const AttributeCommonInfo &CI,

clang/lib/AST/AttrImpl.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,8 +292,7 @@ namespace {
292292
#define USE_DEFAULT_EQUALITY \
293293
(std::is_same_v<T, StringRef> || std::is_same_v<T, VersionTuple> || \
294294
std::is_same_v<T, IdentifierInfo *> || std::is_same_v<T, ParamIdx> || \
295-
std::is_same_v<T, Attr *> || std::is_same_v<T, char *> || \
296-
std::is_enum_v<T> || std::is_integral_v<T>)
295+
std::is_same_v<T, char *> || std::is_enum_v<T> || std::is_integral_v<T>)
297296

298297
template <class T>
299298
typename std::enable_if_t<!USE_DEFAULT_EQUALITY, bool>
@@ -323,6 +322,8 @@ bool equalAttrArgs(T *A1_B, T *A1_E, T *A2_B, T *A2_E,
323322
template <>
324323
bool equalAttrArgs<Attr *>(Attr *A1, Attr *A2,
325324
StructuralEquivalenceContext &Context) {
325+
if (!A1 || !A2)
326+
return A1 == A2;
326327
return A1->isEquivalent(*A2, Context);
327328
}
328329

clang/lib/AST/DeclBase.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,7 @@ AvailabilityResult Decl::getAvailability(std::string *Message,
802802
}
803803

804804
if (const auto *Availability = dyn_cast<AvailabilityAttr>(A)) {
805+
Availability = Availability->getEffectiveAttr();
805806
AvailabilityResult AR = CheckAvailability(getASTContext(), Availability,
806807
Message, EnclosingVersion);
807808

@@ -830,10 +831,11 @@ VersionTuple Decl::getVersionIntroduced() const {
830831
StringRef TargetPlatform = Context.getTargetInfo().getPlatformName();
831832
for (const auto *A : attrs()) {
832833
if (const auto *Availability = dyn_cast<AvailabilityAttr>(A)) {
833-
if (getRealizedPlatform(Availability, Context) != TargetPlatform)
834-
continue;
835-
if (!Availability->getIntroduced().empty())
836-
return Availability->getIntroduced();
834+
Availability = Availability->getEffectiveAttr();
835+
if (getRealizedPlatform(Availability, Context) == TargetPlatform) {
836+
if (!Availability->getIntroduced().empty())
837+
return Availability->getIntroduced();
838+
}
837839
}
838840
}
839841
return {};
@@ -878,6 +880,7 @@ bool Decl::isWeakImported() const {
878880
return true;
879881

880882
if (const auto *Availability = dyn_cast<AvailabilityAttr>(A)) {
883+
Availability = Availability->getEffectiveAttr();
881884
if (CheckAvailability(getASTContext(), Availability, nullptr,
882885
VersionTuple()) == AR_NotYetIntroduced)
883886
return true;

clang/lib/Index/CommentToXML.cpp

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,47 +1029,50 @@ void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
10291029
}
10301030

10311031
// 'availability' attribute.
1032-
Result << "<Availability";
1033-
StringRef Distribution;
1034-
if (AA->getPlatform()) {
1035-
Distribution = AvailabilityAttr::getPrettyPlatformName(
1036-
AA->getPlatform()->getName());
1037-
if (Distribution.empty())
1038-
Distribution = AA->getPlatform()->getName();
1039-
}
1040-
Result << " distribution=\"" << Distribution << "\">";
1041-
VersionTuple IntroducedInVersion = AA->getIntroduced();
1042-
if (!IntroducedInVersion.empty()) {
1043-
Result << "<IntroducedInVersion>"
1044-
<< IntroducedInVersion.getAsString()
1045-
<< "</IntroducedInVersion>";
1046-
}
1047-
VersionTuple DeprecatedInVersion = AA->getDeprecated();
1048-
if (!DeprecatedInVersion.empty()) {
1049-
Result << "<DeprecatedInVersion>"
1050-
<< DeprecatedInVersion.getAsString()
1051-
<< "</DeprecatedInVersion>";
1052-
}
1053-
VersionTuple RemovedAfterVersion = AA->getObsoleted();
1054-
if (!RemovedAfterVersion.empty()) {
1055-
Result << "<RemovedAfterVersion>"
1056-
<< RemovedAfterVersion.getAsString()
1057-
<< "</RemovedAfterVersion>";
1058-
}
1059-
StringRef DeprecationSummary = AA->getMessage();
1060-
if (!DeprecationSummary.empty()) {
1061-
Result << "<DeprecationSummary>";
1062-
appendToResultWithXMLEscaping(DeprecationSummary);
1063-
Result << "</DeprecationSummary>";
1064-
}
1065-
if (AA->getUnavailable())
1066-
Result << "<Unavailable/>";
1032+
auto EmitAvailability = [&](const AvailabilityAttr *AA) {
1033+
Result << "<Availability";
1034+
StringRef Distribution;
1035+
if (AA->getPlatform()) {
1036+
Distribution = AvailabilityAttr::getPrettyPlatformName(
1037+
AA->getPlatform()->getName());
1038+
if (Distribution.empty())
1039+
Distribution = AA->getPlatform()->getName();
1040+
}
1041+
Result << " distribution=\"" << Distribution << "\">";
1042+
VersionTuple IntroducedInVersion = AA->getIntroduced();
1043+
if (!IntroducedInVersion.empty()) {
1044+
Result << "<IntroducedInVersion>" << IntroducedInVersion.getAsString()
1045+
<< "</IntroducedInVersion>";
1046+
}
1047+
VersionTuple DeprecatedInVersion = AA->getDeprecated();
1048+
if (!DeprecatedInVersion.empty()) {
1049+
Result << "<DeprecatedInVersion>" << DeprecatedInVersion.getAsString()
1050+
<< "</DeprecatedInVersion>";
1051+
}
1052+
VersionTuple RemovedAfterVersion = AA->getObsoleted();
1053+
if (!RemovedAfterVersion.empty()) {
1054+
Result << "<RemovedAfterVersion>" << RemovedAfterVersion.getAsString()
1055+
<< "</RemovedAfterVersion>";
1056+
}
1057+
StringRef DeprecationSummary = AA->getMessage();
1058+
if (!DeprecationSummary.empty()) {
1059+
Result << "<DeprecationSummary>";
1060+
appendToResultWithXMLEscaping(DeprecationSummary);
1061+
Result << "</DeprecationSummary>";
1062+
}
1063+
if (AA->getUnavailable())
1064+
Result << "<Unavailable/>";
1065+
const IdentifierInfo *Environment = AA->getEnvironment();
1066+
if (Environment) {
1067+
Result << "<Environment>" << Environment->getName()
1068+
<< "</Environment>";
1069+
}
1070+
Result << "</Availability>";
1071+
};
10671072

1068-
const IdentifierInfo *Environment = AA->getEnvironment();
1069-
if (Environment) {
1070-
Result << "<Environment>" << Environment->getName() << "</Environment>";
1071-
}
1072-
Result << "</Availability>";
1073+
EmitAvailability(AA);
1074+
if (const AvailabilityAttr *Inf = AA->getInferredAttrAs())
1075+
EmitAvailability(Inf);
10731076
}
10741077
}
10751078

clang/lib/Sema/SemaAPINotes.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,8 +271,7 @@ static void ProcessAPINotes(Sema &S, Decl *D,
271271
/*Strict=*/false,
272272
/*Replacement=*/StringRef(),
273273
/*Priority=*/Sema::AP_Explicit,
274-
/*Environment=*/nullptr,
275-
/*OrigAnyAppleOSVersion=*/VersionTuple());
274+
/*Environment=*/nullptr);
276275
},
277276
[](const Decl *D) {
278277
return llvm::find_if(D->attrs(), [](const Attr *next) -> bool {

clang/lib/Sema/SemaAvailability.cpp

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,14 @@ static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context,
5858
// FIXME: this is copied from CheckAvailability. We should try to
5959
// de-duplicate.
6060

61+
// If this attr has an inferred platform-specific attr (e.g. anyappleos
62+
// → ios/macos/...), use that for platform matching but return the
63+
// original.
64+
const AvailabilityAttr *EffectiveAvail = Avail->getEffectiveAttr();
65+
6166
// Check if this is an App Extension "platform", and if so chop off
6267
// the suffix for matching with the actual platform.
63-
StringRef ActualPlatform = Avail->getPlatform()->getName();
68+
StringRef ActualPlatform = EffectiveAvail->getPlatform()->getName();
6469
StringRef RealizedPlatform = ActualPlatform;
6570
if (Context.getLangOpts().AppExt) {
6671
size_t suffix = RealizedPlatform.rfind("_app_extension");
@@ -73,7 +78,7 @@ static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context,
7378
// Match the platform name.
7479
if (RealizedPlatform == TargetPlatform) {
7580
// Find the best matching attribute for this environment
76-
if (hasMatchingEnvironmentOrNone(Context, Avail))
81+
if (hasMatchingEnvironmentOrNone(Context, EffectiveAvail))
7782
return Avail;
7883
PartialMatch = Avail;
7984
}
@@ -201,8 +206,8 @@ static bool ShouldDiagnoseAvailabilityInContext(
201206
auto CheckContext = [&](const Decl *C) {
202207
if (K == AR_NotYetIntroduced) {
203208
if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, C))
204-
if (AA->getIntroduced() >= DeclVersion &&
205-
AA->getEnvironment() == DeclEnv)
209+
if (AA->getEffectiveIntroduced() >= DeclVersion &&
210+
AA->getEffectiveEnvironment() == DeclEnv)
206211
return true;
207212
} else if (K == AR_Deprecated) {
208213
if (C->isDeprecated())
@@ -423,8 +428,8 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
423428
const AvailabilityAttr *AA = getAttrForPlatform(S.Context, OffendingDecl);
424429
const IdentifierInfo *IIEnv = nullptr;
425430
if (AA) {
426-
DeclVersion = AA->getIntroduced();
427-
IIEnv = AA->getEnvironment();
431+
DeclVersion = AA->getEffectiveIntroduced();
432+
IIEnv = AA->getEffectiveEnvironment();
428433
}
429434

430435
if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, IIEnv, Ctx,
@@ -456,9 +461,9 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
456461
// for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
457462
// later.
458463
assert(AA != nullptr && "expecting valid availability attribute");
459-
VersionTuple Introduced = AA->getIntroduced();
464+
VersionTuple Introduced = AA->getEffectiveIntroduced();
460465
bool EnvironmentMatchesOrNone =
461-
hasMatchingEnvironmentOrNone(S.getASTContext(), AA);
466+
hasMatchingEnvironmentOrNone(S.getASTContext(), AA->getEffectiveAttr());
462467

463468
const TargetInfo &TI = S.getASTContext().getTargetInfo();
464469
std::string PlatformName(
@@ -892,17 +897,17 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
892897
const AvailabilityAttr *AA =
893898
getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl);
894899
assert(AA != nullptr && "expecting valid availability attribute");
895-
bool EnvironmentMatchesOrNone =
896-
hasMatchingEnvironmentOrNone(SemaRef.getASTContext(), AA);
897-
VersionTuple Introduced = AA->getIntroduced();
900+
bool EnvironmentMatchesOrNone = hasMatchingEnvironmentOrNone(
901+
SemaRef.getASTContext(), AA->getEffectiveAttr());
902+
VersionTuple Introduced = AA->getEffectiveIntroduced();
898903

899904
if (EnvironmentMatchesOrNone && AvailabilityStack.back() >= Introduced)
900905
return;
901906

902907
// If the context of this function is less available than D, we should not
903908
// emit a diagnostic.
904909
if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced,
905-
AA->getEnvironment(), Ctx,
910+
AA->getEffectiveEnvironment(), Ctx,
906911
OffendingDecl))
907912
return;
908913

@@ -982,20 +987,16 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
982987
const char *ExtraIndentation = " ";
983988
std::string FixItString;
984989
llvm::raw_string_ostream FixItOS(FixItString);
985-
// If the attr was derived from anyAppleOS, emit the fix-it using
986-
// anyAppleOS and the original anyAppleOS version rather than the
987-
// platform-specific name and version.
988-
VersionTuple OrigAnyAppleOSVersion = AA->getOrigAnyAppleOSVersion();
989990
StringRef FixItPlatformName;
990991
VersionTuple FixItVersion;
991992

992-
if (OrigAnyAppleOSVersion.empty()) {
993+
if (AA->getInferredAttr()) {
994+
FixItPlatformName = "anyAppleOS";
995+
FixItVersion = AA->getIntroduced();
996+
} else {
993997
FixItPlatformName = AvailabilityAttr::getPlatformNameSourceSpelling(
994998
SemaRef.getASTContext().getTargetInfo().getPlatformName());
995-
FixItVersion = Introduced;
996-
} else {
997-
FixItPlatformName = "anyAppleOS";
998-
FixItVersion = OrigAnyAppleOSVersion;
999+
FixItVersion = AA->getEffectiveIntroduced();
9991000
}
10001001
FixItOS << "if ("
10011002
<< (SemaRef.getLangOpts().ObjC ? "@available"

clang/lib/Sema/SemaDecl.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2899,14 +2899,16 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
28992899
// previous decl", for example if the attribute needs to be consistent
29002900
// between redeclarations, you need to call a custom merge function here.
29012901
InheritableAttr *NewAttr = nullptr;
2902-
if (const auto *AA = dyn_cast<AvailabilityAttr>(Attr))
2903-
NewAttr = S.mergeAvailabilityAttr(
2902+
if (const auto *AA = dyn_cast<AvailabilityAttr>(Attr)) {
2903+
const IdentifierInfo *InferredPlatformII = nullptr;
2904+
if (AvailabilityAttr *Inf = AA->getInferredAttrAs())
2905+
InferredPlatformII = Inf->getPlatform();
2906+
NewAttr = S.mergeAndInferAvailabilityAttr(
29042907
D, *AA, AA->getPlatform(), AA->isImplicit(), AA->getIntroduced(),
29052908
AA->getDeprecated(), AA->getObsoleted(), AA->getUnavailable(),
29062909
AA->getMessage(), AA->getStrict(), AA->getReplacement(), AMK,
2907-
AA->getPriority(), AA->getEnvironment(),
2908-
AA->getOrigAnyAppleOSVersion());
2909-
else if (const auto *VA = dyn_cast<VisibilityAttr>(Attr))
2910+
AA->getPriority(), AA->getEnvironment(), InferredPlatformII);
2911+
} else if (const auto *VA = dyn_cast<VisibilityAttr>(Attr))
29102912
NewAttr = S.mergeVisibilityAttr(D, *VA, VA->getVisibility());
29112913
else if (const auto *VA = dyn_cast<TypeVisibilityAttr>(Attr))
29122914
NewAttr = S.mergeTypeVisibilityAttr(D, *VA, VA->getVisibility());

0 commit comments

Comments
 (0)