Skip to content

Commit 6c8940c

Browse files
authored
Add support for anyAppleOS availability (#181953)
The number of Apple platforms has grown over the years, resulting in availability annotations becoming increasingly verbose. Now that OS version names have been unified starting with version 26.0, this patch introduces a shorthand syntax that applies availability across all Apple platforms: ``` // Declaration. void foo __attribute__((availability(anyAppleOS, introduced=26.0))); // Guard. if (__builtin_available(anyAppleOS 27.0, *)) ``` Implementation: The `anyAppleOS` platform name is expanded at parse time into implicit platform-specific availability attributes for the target platform. For example, when targeting `macOS`, `anyAppleOS` creates an implicit `macOS` availability attribute with the same version. A priority system ensures correct attribute merging. Attributes expanded from `anyAppleOS` have lower priority than existing availability attributes: - Direct platform-specific attributes on declarations - Platform-specific attributes from #pragma clang attribute push - Attributes inferred from other platforms Among `anyAppleOS` attributes themselves, direct `anyAppleOS` annotations have higher priority than `anyAppleOS` applied through `#pragma clang attribute push`. The minimum supported version for `anyAppleOS` is 26.0. Versions older than 26.0 are rejected with a diagnostic error. `AvailabilityAttr` gains an `origAnyAppleOSVersion` field that stores the original `anyAppleOS` version when a platform-specific availability attribute is implicitly derived from an `anyAppleOS` annotation. This field is used in `@available`/`__builtin_available` fix-it hints to emit the `anyAppleOS` platform name and version rather than the expanded platform-specific name. For `__builtin_available` checks, `anyAppleOS` is lowered to platform-specific version checks in CodeGen. This reduces the burden of adding availability annotations to new APIs in Apple's SDKs and simplifies guards in applications. rdar://159386357
1 parent 7e44db9 commit 6c8940c

16 files changed

+667
-14
lines changed

clang/include/clang/AST/Availability.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ class AvailabilitySpec {
5656

5757
/// Returns true when this represents the '*' case.
5858
bool isOtherPlatformSpec() const { return Version.empty(); }
59+
60+
/// Returns true if the anyAppleOS version is valid (empty or >= 26.0).
61+
static bool validateAnyAppleOSVersion(const llvm::VersionTuple &Version) {
62+
return Version.empty() || Version >= llvm::VersionTuple(26, 0);
63+
}
5964
};
6065

6166
class Decl;

clang/include/clang/Basic/Attr.td

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,8 @@ class VariadicParamOrParamIdxArgument<string name> : Argument<name, 1>;
284284
class ParamIdxArgument<string name, bit opt = 0> : Argument<name, opt>;
285285

286286
// A version of the form major.minor[.subminor].
287-
class VersionArgument<string name, bit opt = 0> : Argument<name, opt>;
287+
class VersionArgument<string name, bit opt = 0, bit fake = 0>
288+
: Argument<name, opt, fake>;
288289

289290
// This one's a doozy, so it gets its own special type
290291
// It can be an unsigned integer, or a type. Either can
@@ -1100,7 +1101,8 @@ def Availability : InheritableAttr {
11001101
VersionArgument<"deprecated">, VersionArgument<"obsoleted">,
11011102
BoolArgument<"unavailable">, StringArgument<"message">,
11021103
BoolArgument<"strict">, StringArgument<"replacement">,
1103-
IntArgument<"priority">, IdentifierArgument<"environment">];
1104+
IntArgument<"priority">, IdentifierArgument<"environment">,
1105+
VersionArgument<"origAnyAppleOSVersion", 0, 1>];
11041106
let AdditionalMembers =
11051107
[{static llvm::StringRef getPrettyPlatformName(llvm::StringRef Platform) {
11061108
return llvm::StringSwitch<llvm::StringRef>(Platform)
@@ -1119,6 +1121,7 @@ def Availability : InheritableAttr {
11191121
.Case("maccatalyst_app_extension", "macCatalyst (App Extension)")
11201122
.Case("xros", "visionOS")
11211123
.Case("xros_app_extension", "visionOS (App Extension)")
1124+
.Case("anyappleos", "any Apple OS")
11221125
.Case("swift", "Swift")
11231126
.Case("shadermodel", "Shader Model")
11241127
.Case("ohos", "OpenHarmony OS")
@@ -1138,6 +1141,7 @@ static llvm::StringRef getPlatformNameSourceSpelling(llvm::StringRef Platform) {
11381141
.Case("maccatalyst_app_extension", "macCatalystApplicationExtension")
11391142
.Case("xros", "visionOS")
11401143
.Case("xros_app_extension", "visionOSApplicationExtension")
1144+
.Case("anyappleos", "anyAppleOS")
11411145
.Case("zos", "z/OS")
11421146
.Case("shadermodel", "ShaderModel")
11431147
.Default(Platform);
@@ -1160,6 +1164,7 @@ static llvm::StringRef canonicalizePlatformName(llvm::StringRef Platform) {
11601164
.Case("visionOSApplicationExtension", "xros_app_extension")
11611165
.Case("visionos", "xros")
11621166
.Case("visionos_app_extension", "xros_app_extension")
1167+
.Case("anyAppleOS", "anyappleos")
11631168
.Case("ShaderModel", "shadermodel")
11641169
.Default(Platform);
11651170
}
@@ -1191,6 +1196,8 @@ static std::vector<llvm::StringRef> equivalentPlatformNames(llvm::StringRef Plat
11911196
.Case("xros_app_extension", {"visionOSApplicationExtension", "visionos_app_extension", "xros_app_extension"})
11921197
.Case("visionOSApplicationExtension", {"visionOSApplicationExtension", "visionos_app_extension", "xros_app_extension"})
11931198
.Case("visionos_app_extension", {"visionOSApplicationExtension", "visionos_app_extension", "xros_app_extension"})
1199+
.Case("anyappleos", {"anyappleos", "anyAppleOS"})
1200+
.Case("anyAppleOS", {"anyappleos", "anyAppleOS"})
11941201
.Default({Platform});
11951202
}
11961203
static llvm::Triple::EnvironmentType getEnvironmentType(llvm::StringRef Environment) {

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,6 +1213,8 @@ def err_avail_query_expected_platform_name : Error<
12131213

12141214
def err_avail_query_unrecognized_platform_name : Error<
12151215
"unrecognized platform name %0">;
1216+
def err_avail_query_anyappleos_min_version : Error<
1217+
"invalid anyAppleOS version '%0' in availability check">;
12161218
def err_availability_query_wildcard_required: Error<
12171219
"must handle potential future platforms with '*'">;
12181220
def err_availability_query_repeated_platform: Error<

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4249,6 +4249,8 @@ def warn_at_available_unchecked_use : Warning<
42494249
def warn_availability_invalid_os_version
42504250
: Warning<"invalid %1 version '%0' in availability attribute">, InGroup<DiagGroup<"invalid-version-availability">>;
42514251
def note_availability_invalid_os_version_adjusted: Note<"implicitly treating version as '%0'">;
4252+
def err_availability_invalid_anyappleos_version
4253+
: Error<"invalid anyAppleOS version '%0' in availability attribute">;
42524254

42534255
def warn_missing_sdksettings_for_availability_checking : Warning<
42544256
"%0 availability is ignored without a valid 'SDKSettings.json' in the SDK">,

clang/include/clang/Sema/Sema.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4862,7 +4862,16 @@ class Sema final : public SemaBase {
48624862

48634863
/// The availability attribute for a specific platform was inferred from
48644864
/// an availability attribute for another platform.
4865-
AP_InferredFromOtherPlatform = 2
4865+
AP_InferredFromOtherPlatform = 2,
4866+
4867+
/// The availability attribute was inferred from an 'anyAppleOS'
4868+
/// availability attribute.
4869+
AP_InferredFromAnyAppleOS = 3,
4870+
4871+
/// The availability attribute was inferred from an 'anyAppleOS'
4872+
/// availability attribute that was applied using '#pragma clang attribute'.
4873+
/// This has the lowest priority.
4874+
AP_PragmaClangAttribute_InferredFromAnyAppleOS = 4
48664875
};
48674876

48684877
/// Describes the reason a calling convention specification was ignored, used
@@ -4982,7 +4991,8 @@ class Sema final : public SemaBase {
49824991
VersionTuple Obsoleted, bool IsUnavailable,
49834992
StringRef Message, bool IsStrict, StringRef Replacement,
49844993
AvailabilityMergeKind AMK, int Priority,
4985-
const IdentifierInfo *IIEnvironment);
4994+
const IdentifierInfo *IIEnvironment,
4995+
VersionTuple OrigAnyAppleOSVersion = {});
49864996

49874997
TypeVisibilityAttr *
49884998
mergeTypeVisibilityAttr(Decl *D, const AttributeCommonInfo &CI,

clang/lib/Parse/ParseExpr.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3467,6 +3467,15 @@ std::optional<AvailabilitySpec> Parser::ParseAvailabilitySpec() {
34673467
return std::nullopt;
34683468
}
34693469

3470+
// Validate anyAppleOS version; reject versions older than 26.0.
3471+
if (Platform == "anyappleos" &&
3472+
!AvailabilitySpec::validateAnyAppleOSVersion(Version)) {
3473+
Diag(VersionRange.getBegin(),
3474+
diag::err_avail_query_anyappleos_min_version)
3475+
<< Version.getAsString();
3476+
return std::nullopt;
3477+
}
3478+
34703479
return AvailabilitySpec(Version, Platform, PlatformIdentifier->getLoc(),
34713480
VersionRange.getEnd());
34723481
}

clang/lib/Sema/SemaAPINotes.cpp

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

clang/lib/Sema/SemaAvailability.cpp

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -988,12 +988,26 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
988988
const char *ExtraIndentation = " ";
989989
std::string FixItString;
990990
llvm::raw_string_ostream FixItOS(FixItString);
991-
FixItOS << "if (" << (SemaRef.getLangOpts().ObjC ? "@available"
992-
: "__builtin_available")
993-
<< "("
994-
<< AvailabilityAttr::getPlatformNameSourceSpelling(
995-
SemaRef.getASTContext().getTargetInfo().getPlatformName())
996-
<< " " << Introduced.getAsString() << ", *)) {\n"
991+
// If the attr was derived from anyAppleOS, emit the fix-it using
992+
// anyAppleOS and the original anyAppleOS version rather than the
993+
// platform-specific name and version.
994+
VersionTuple OrigAnyAppleOSVersion = AA->getOrigAnyAppleOSVersion();
995+
StringRef FixItPlatformName;
996+
VersionTuple FixItVersion;
997+
998+
if (OrigAnyAppleOSVersion.empty()) {
999+
FixItPlatformName = AvailabilityAttr::getPlatformNameSourceSpelling(
1000+
SemaRef.getASTContext().getTargetInfo().getPlatformName());
1001+
FixItVersion = Introduced;
1002+
} else {
1003+
FixItPlatformName = "anyAppleOS";
1004+
FixItVersion = OrigAnyAppleOSVersion;
1005+
}
1006+
FixItOS << "if ("
1007+
<< (SemaRef.getLangOpts().ObjC ? "@available"
1008+
: "__builtin_available")
1009+
<< "(" << FixItPlatformName << " " << FixItVersion.getAsString()
1010+
<< ", *)) {\n"
9971011
<< Indentation << ExtraIndentation;
9981012
FixitDiag << FixItHint::CreateInsertion(IfInsertionLoc, FixItOS.str());
9991013
SourceLocation ElseInsertionLoc = Lexer::findLocationAfterToken(

clang/lib/Sema/SemaDecl.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2902,7 +2902,8 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
29022902
D, *AA, AA->getPlatform(), AA->isImplicit(), AA->getIntroduced(),
29032903
AA->getDeprecated(), AA->getObsoleted(), AA->getUnavailable(),
29042904
AA->getMessage(), AA->getStrict(), AA->getReplacement(), AMK,
2905-
AA->getPriority(), AA->getEnvironment());
2905+
AA->getPriority(), AA->getEnvironment(),
2906+
AA->getOrigAnyAppleOSVersion());
29062907
else if (const auto *VA = dyn_cast<VisibilityAttr>(Attr))
29072908
NewAttr = S.mergeVisibilityAttr(D, *VA, VA->getVisibility());
29082909
else if (const auto *VA = dyn_cast<TypeVisibilityAttr>(Attr))

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "clang/AST/ASTConsumer.h"
1515
#include "clang/AST/ASTContext.h"
1616
#include "clang/AST/ASTMutationListener.h"
17+
#include "clang/AST/Availability.h"
1718
#include "clang/AST/CXXInheritance.h"
1819
#include "clang/AST/Decl.h"
1920
#include "clang/AST/DeclCXX.h"
@@ -2358,7 +2359,8 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(
23582359
bool Implicit, VersionTuple Introduced, VersionTuple Deprecated,
23592360
VersionTuple Obsoleted, bool IsUnavailable, StringRef Message,
23602361
bool IsStrict, StringRef Replacement, AvailabilityMergeKind AMK,
2361-
int Priority, const IdentifierInfo *Environment) {
2362+
int Priority, const IdentifierInfo *Environment,
2363+
VersionTuple OrigAnyAppleOSVersion) {
23622364
VersionTuple MergedIntroduced = Introduced;
23632365
VersionTuple MergedDeprecated = Deprecated;
23642366
VersionTuple MergedObsoleted = Obsoleted;
@@ -2517,7 +2519,8 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(
25172519
!OverrideOrImpl) {
25182520
auto *Avail = ::new (Context) AvailabilityAttr(
25192521
Context, CI, Platform, Introduced, Deprecated, Obsoleted, IsUnavailable,
2520-
Message, IsStrict, Replacement, Priority, Environment);
2522+
Message, IsStrict, Replacement, Priority, Environment,
2523+
OrigAnyAppleOSVersion);
25212524
Avail->setImplicit(Implicit);
25222525
return Avail;
25232526
}
@@ -2703,6 +2706,63 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
27032706
}
27042707
}
27052708

2709+
// Handle anyAppleOS specially: create implicit platform-specific attributes
2710+
// instead of the original anyAppleOS attribute.
2711+
if (II->getName() == "anyappleos") {
2712+
// Validate anyAppleOS versions; reject versions older than 26.0.
2713+
auto ValidateVersion = [&](const llvm::VersionTuple &Version,
2714+
SourceLocation Loc) -> bool {
2715+
if (AvailabilitySpec::validateAnyAppleOSVersion(Version))
2716+
return true;
2717+
S.Diag(Loc, diag::err_availability_invalid_anyappleos_version)
2718+
<< Version.getAsString();
2719+
return false;
2720+
};
2721+
2722+
// Validate the versions; bail out if any are invalid.
2723+
bool Valid = ValidateVersion(Introduced.Version, Introduced.KeywordLoc);
2724+
Valid &= ValidateVersion(Deprecated.Version, Deprecated.KeywordLoc);
2725+
Valid &= ValidateVersion(Obsoleted.Version, Obsoleted.KeywordLoc);
2726+
if (!Valid)
2727+
return;
2728+
2729+
llvm::Triple T = S.Context.getTargetInfo().getTriple();
2730+
2731+
// Only create implicit attributes for Darwin OSes.
2732+
if (!T.isOSDarwin())
2733+
return;
2734+
2735+
StringRef PlatformName;
2736+
2737+
// Determine the platform name based on the target triple.
2738+
if (T.isMacOSX())
2739+
PlatformName = "macos";
2740+
else if (T.getOS() == llvm::Triple::IOS && T.isMacCatalystEnvironment())
2741+
PlatformName = "maccatalyst";
2742+
else // For iOS, tvOS, watchOS, visionOS, bridgeOS, etc.
2743+
PlatformName = llvm::Triple::getOSTypeName(T.getOS());
2744+
2745+
IdentifierInfo *NewII = &S.Context.Idents.get(PlatformName);
2746+
2747+
// Use the special low-priority value for pragma push anyAppleOS.
2748+
int ExpandedPriority =
2749+
(PriorityModifier == Sema::AP_PragmaClangAttribute)
2750+
? Sema::AP_PragmaClangAttribute_InferredFromAnyAppleOS
2751+
: Sema::AP_InferredFromAnyAppleOS;
2752+
2753+
AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr(
2754+
ND, AL, NewII, /*Implicit=*/true, Introduced.Version,
2755+
Deprecated.Version, Obsoleted.Version, IsUnavailable, Str, IsStrict,
2756+
Replacement, AvailabilityMergeKind::None, ExpandedPriority,
2757+
IIEnvironment, Introduced.Version);
2758+
if (NewAttr)
2759+
D->addAttr(NewAttr);
2760+
2761+
// Don't add the original anyAppleOS attribute - only the implicit
2762+
// platform-specific attributes.
2763+
return;
2764+
}
2765+
27062766
AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr(
27072767
ND, AL, II, false /*Implicit*/, Introduced.Version, Deprecated.Version,
27082768
Obsoleted.Version, IsUnavailable, Str, IsStrict, Replacement,

0 commit comments

Comments
 (0)