Skip to content

Commit 1bf2e8f

Browse files
authored
Merge pull request #85256 from tshortli/any-apple-os-availability
AST: Introduce experimental support for an `anyAppleOS` availability domain
2 parents 8c1e0c8 + 3b77e2e commit 1bf2e8f

File tree

8 files changed

+252
-23
lines changed

8 files changed

+252
-23
lines changed

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,9 @@ SUPPRESSIBLE_EXPERIMENTAL_FEATURE(TildeSendable, false)
555555
/// Allow use of protocol typed values in Embedded mode (`Any` and friends)
556556
EXPERIMENTAL_FEATURE(EmbeddedExistentials, false)
557557

558+
/// Allow use of the 'anyAppleOS' availability domain.
559+
EXPERIMENTAL_FEATURE(AnyAppleOSAvailability, true)
560+
558561
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
559562
#undef EXPERIMENTAL_FEATURE
560563
#undef UPCOMING_FEATURE

lib/AST/Availability.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -684,9 +684,10 @@ SemanticAvailableAttrRequest::evaluate(swift::Evaluator &evaluator,
684684
if (!version)
685685
return false;
686686

687+
auto diagLoc = sourceRange.isValid() ? sourceRange.Start : attrLoc;
687688
if (!VersionRange::isValidVersion(*version)) {
688689
diags
689-
.diagnose(attrLoc, diag::availability_unsupported_version_number,
690+
.diagnose(diagLoc, diag::availability_unsupported_version_number,
690691
*version)
691692
.highlight(sourceRange);
692693
return true;
@@ -696,7 +697,7 @@ SemanticAvailableAttrRequest::evaluate(swift::Evaluator &evaluator,
696697
// 17 will never exist.
697698
if (domain->isVersioned() && !domain->isVersionValid(*version)) {
698699
diags
699-
.diagnose(attrLoc,
700+
.diagnose(diagLoc,
700701
diag::availability_invalid_version_number_for_domain,
701702
*version, *domain)
702703
.highlight(sourceRange);

lib/AST/AvailabilityDomain.cpp

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,17 @@ bool AvailabilityDomain::isVersionValid(
169169
return true;
170170

171171
case Kind::Platform:
172+
// If the platform kind corresponds to a specific OS, LLVM is the source of
173+
// truth for version validity.
172174
if (auto osType = tripleOSTypeForPlatform(getPlatformKind()))
173175
return llvm::Triple::isValidVersionForOS(*osType, version);
176+
177+
// Unified versioning for Apple's operating systems starts at 26.0.
178+
if (getPlatformKind() == PlatformKind::anyAppleOS)
179+
return (version.getMajor() >= 26);
180+
174181
return true;
182+
175183
case Kind::Custom:
176184
return true;
177185
}
@@ -400,12 +408,36 @@ AvailabilityDomain AvailabilityDomain::getRootDomain() const {
400408
return *this;
401409
}
402410

411+
static std::optional<PlatformKind>
412+
getApplePlatformKindForTarget(const llvm::Triple &target) {
413+
if (target.isMacOSX())
414+
return PlatformKind::macOS;
415+
if (target.isTvOS()) // Must be checked before isiOS.
416+
return PlatformKind::tvOS;
417+
if (target.isiOS())
418+
return PlatformKind::iOS;
419+
if (target.isWatchOS())
420+
return PlatformKind::watchOS;
421+
if (target.isXROS())
422+
return PlatformKind::visionOS;
423+
424+
return std::nullopt;
425+
}
426+
403427
std::optional<AvailabilityDomain>
404428
AvailabilityDomain::getRemappedDomainOrNull(const ASTContext &ctx) const {
405429
if (getPlatformKind() == PlatformKind::iOS &&
406430
isPlatformActive(PlatformKind::visionOS, ctx.LangOpts))
407431
return AvailabilityDomain::forPlatform(PlatformKind::visionOS);
408432

433+
if (getPlatformKind() == PlatformKind::anyAppleOS) {
434+
if (auto applePlatformKind =
435+
getApplePlatformKindForTarget(ctx.LangOpts.Target))
436+
return AvailabilityDomain::forPlatform(*applePlatformKind);
437+
438+
return std::nullopt;
439+
}
440+
409441
return std::nullopt;
410442
}
411443

@@ -450,23 +482,29 @@ AvailabilityDomainAndRange AvailabilityDomain::getRemappedDomainAndRange(
450482
if (!remappedDomain)
451483
return {*this, AvailabilityRange{version}};
452484

453-
std::optional<clang::VersionTuple> remappedVersion;
454-
switch (versionKind) {
455-
case AvailabilityVersionKind::Introduced:
456-
remappedVersion =
457-
getRemappedIntroducedVersionForFallbackPlatform(ctx, version);
458-
break;
459-
case AvailabilityVersionKind::Deprecated:
460-
case AvailabilityVersionKind::Obsoleted:
461-
remappedVersion =
462-
getRemappedDeprecatedObsoletedVersionForFallbackPlatform(ctx, version);
463-
break;
464-
}
485+
if (getPlatformKind() == PlatformKind::anyAppleOS)
486+
return {*remappedDomain, AvailabilityRange{version}};
487+
488+
if (getPlatformKind() == PlatformKind::iOS) {
489+
std::optional<clang::VersionTuple> remappedVersion;
490+
switch (versionKind) {
491+
case AvailabilityVersionKind::Introduced:
492+
remappedVersion =
493+
getRemappedIntroducedVersionForFallbackPlatform(ctx, version);
494+
break;
495+
case AvailabilityVersionKind::Deprecated:
496+
case AvailabilityVersionKind::Obsoleted:
497+
remappedVersion =
498+
getRemappedDeprecatedObsoletedVersionForFallbackPlatform(ctx,
499+
version);
500+
break;
501+
}
465502

466-
if (!remappedVersion)
467-
return {*this, AvailabilityRange{version}};
503+
if (remappedVersion)
504+
return {*remappedDomain, AvailabilityRange{*remappedVersion}};
505+
}
468506

469-
return {*remappedDomain, AvailabilityRange{*remappedVersion}};
507+
return {*this, AvailabilityRange{version}};
470508
}
471509

472510
bool IsCustomAvailabilityDomainPermanentlyEnabled::evaluate(
@@ -627,6 +665,13 @@ AvailabilityDomainOrIdentifier::lookUpInDeclContext(
627665
return std::nullopt;
628666
}
629667

668+
if (domain->getPlatformKind() == PlatformKind::anyAppleOS &&
669+
!ctx.LangOpts.hasFeature(Feature::AnyAppleOSAvailability)) {
670+
diags.diagnose(loc, diag::availability_domain_requires_feature, *domain,
671+
"AnyAppleOSAvailability");
672+
return std::nullopt;
673+
}
674+
630675
// Use of the 'Swift' domain requires the 'SwiftRuntimeAvailability' feature.
631676
if (!hasSwiftRuntimeAvailability && domain->isStandaloneSwiftRuntime()) {
632677
diags.diagnose(loc, diag::availability_domain_requires_feature, *domain,

lib/AST/FeatureSet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ static bool usesFeatureTildeSendable(Decl *decl) {
463463
});
464464
}
465465

466+
UNINTERESTING_FEATURE(AnyAppleOSAvailability)
466467

467468
// ----------------------------------------------------------------------------
468469
// MARK: - FeatureSet

lib/AST/PlatformKindUtils.cpp

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,12 @@ swift::basePlatformForExtensionPlatform(PlatformKind Platform) {
130130

131131
static bool isPlatformActiveForTarget(PlatformKind Platform,
132132
const llvm::Triple &Target,
133-
bool EnableAppExtensionRestrictions,
133+
const LangOptions &LangOpts,
134134
bool ForRuntimeQuery) {
135135
if (Platform == PlatformKind::none)
136136
return true;
137137

138-
if (!EnableAppExtensionRestrictions &&
138+
if (!LangOpts.EnableAppExtensionRestrictions &&
139139
isApplicationExtensionPlatform(Platform))
140140
return false;
141141

@@ -186,12 +186,11 @@ bool swift::isPlatformActive(PlatformKind Platform, const LangOptions &LangOpts,
186186
if (ForTargetVariant) {
187187
assert(LangOpts.TargetVariant && "Must have target variant triple");
188188
return isPlatformActiveForTarget(Platform, *LangOpts.TargetVariant,
189-
LangOpts.EnableAppExtensionRestrictions,
190-
ForRuntimeQuery);
189+
LangOpts, ForRuntimeQuery);
191190
}
192191

193-
return isPlatformActiveForTarget(Platform, LangOpts.Target,
194-
LangOpts.EnableAppExtensionRestrictions, ForRuntimeQuery);
192+
return isPlatformActiveForTarget(Platform, LangOpts.Target, LangOpts,
193+
ForRuntimeQuery);
195194
}
196195

197196
static PlatformKind platformForTriple(const llvm::Triple &triple,
@@ -250,6 +249,33 @@ PlatformKind swift::targetVariantPlatform(const LangOptions &LangOpts) {
250249
return PlatformKind::none;
251250
}
252251

252+
static bool inheritsAvailabilityFromAnyAppleOS(PlatformKind platform) {
253+
switch (platform) {
254+
case PlatformKind::macOSApplicationExtension:
255+
case PlatformKind::iOSApplicationExtension:
256+
case PlatformKind::macCatalystApplicationExtension:
257+
case PlatformKind::tvOSApplicationExtension:
258+
case PlatformKind::watchOSApplicationExtension:
259+
case PlatformKind::visionOSApplicationExtension:
260+
case PlatformKind::macOS:
261+
case PlatformKind::iOS:
262+
case PlatformKind::macCatalyst:
263+
case PlatformKind::tvOS:
264+
case PlatformKind::watchOS:
265+
case PlatformKind::visionOS:
266+
return true;
267+
case PlatformKind::DriverKit:
268+
case PlatformKind::anyAppleOS:
269+
case PlatformKind::Swift:
270+
case PlatformKind::FreeBSD:
271+
case PlatformKind::OpenBSD:
272+
case PlatformKind::Windows:
273+
case PlatformKind::Android:
274+
case PlatformKind::none:
275+
return false;
276+
}
277+
}
278+
253279
bool swift::inheritsAvailabilityFromPlatform(PlatformKind Child,
254280
PlatformKind Parent) {
255281
if (auto ChildPlatformBase = basePlatformForExtensionPlatform(Child)) {
@@ -277,6 +303,10 @@ bool swift::inheritsAvailabilityFromPlatform(PlatformKind Child,
277303
}
278304
}
279305

306+
if (Parent == PlatformKind::anyAppleOS &&
307+
inheritsAvailabilityFromAnyAppleOS(Child))
308+
return true;
309+
280310
return false;
281311
}
282312

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// RUN: %target-typecheck-verify-swift -parse-stdlib -enable-experimental-feature AnyAppleOSAvailability -target %target-cpu-apple-macos26 -verify-additional-prefix apple- -verify-additional-prefix macos-
2+
// RUN: %target-typecheck-verify-swift -parse-stdlib -enable-experimental-feature AnyAppleOSAvailability -target %target-cpu-apple-ios26 -verify-additional-prefix apple- -verify-additional-prefix ios-
3+
// RUN: %target-typecheck-verify-swift -parse-stdlib -enable-experimental-feature AnyAppleOSAvailability -target %target-cpu-apple-watchos26 -verify-additional-prefix apple- -verify-additional-prefix watchos-
4+
// RUN: %target-typecheck-verify-swift -parse-stdlib -enable-experimental-feature AnyAppleOSAvailability -target %target-cpu-apple-tvos26 -verify-additional-prefix apple- -verify-additional-prefix tvos-
5+
// RUN: %target-typecheck-verify-swift -parse-stdlib -enable-experimental-feature AnyAppleOSAvailability -target %target-cpu-apple-visionos26 -verify-additional-prefix apple- -verify-additional-prefix visionos-
6+
// RUN: %target-typecheck-verify-swift -parse-stdlib -enable-experimental-feature AnyAppleOSAvailability -target x86_64-unknown-linux-gnu
7+
// RUN: %target-typecheck-verify-swift -parse-stdlib -enable-experimental-feature AnyAppleOSAvailability -target x86_64-unknown-windows-msvc
8+
9+
// REQUIRES: swift_feature_AnyAppleOSAvailability
10+
11+
@available(anyAppleOS 26.1, *)
12+
func availableInAnyAppleOS26_1() { }
13+
14+
@available(anyAppleOS, deprecated: 26)
15+
func deprecatedInAnyAppleOS26() { }
16+
17+
@available(anyAppleOS, obsoleted: 26)
18+
func obsoletedInAnyAppleOS26() { }
19+
// expected-macos-note@-1 {{'obsoletedInAnyAppleOS26()' was obsoleted in macOS 26}}
20+
// expected-ios-note@-2 {{'obsoletedInAnyAppleOS26()' was obsoleted in iOS 26}}
21+
// expected-watchos-note@-3 {{'obsoletedInAnyAppleOS26()' was obsoleted in watchOS 26}}
22+
// expected-tvos-note@-4 {{'obsoletedInAnyAppleOS26()' was obsoleted in tvOS 26}}
23+
// expected-visionos-note@-5 {{'obsoletedInAnyAppleOS26()' was obsoleted in visionOS 26}}
24+
25+
@available(anyAppleOS 26, macOS 26.1, *)
26+
func availableInAnyAppleOS26AndMacOS26_1() { }
27+
28+
@available(macOS 26.1, anyAppleOS 26, *)
29+
func availableInMacOS26_1AndAnyAppleOS26() { }
30+
31+
@available(macOS 26.1, iOS 26.1, watchOS 26.1, tvOS 26.1, visionOS 26.1, *)
32+
func availableInEveryAppleOS26_1() { }
33+
34+
@available(anyAppleOS, unavailable)
35+
func unavailableInAnyAppleOS() { } // expected-apple-note {{'unavailableInAnyAppleOS()' has been explicitly marked unavailable here}}
36+
37+
// FIXME: [availability] Ensure the fix-it suggests @available(anyAppleOS ...) rdar://163819878
38+
func availableAtDeploymentTarget() {
39+
// expected-apple-note@-1 {{add '@available' attribute to enclosing global function}}
40+
// expected-macos-note@-2 2 {{add '@available' attribute to enclosing global function}}
41+
42+
// FIXME: [availability] Ensure the fix-it suggests if #available(anyAppleOS ...) rdar://163819878
43+
availableInAnyAppleOS26_1()
44+
// expected-macos-error@-1 {{'availableInAnyAppleOS26_1()' is only available in macOS 26.1 or newer}}
45+
// expected-ios-error@-2 {{'availableInAnyAppleOS26_1()' is only available in iOS 26.1 or newer}}
46+
// expected-watchos-error@-3 {{'availableInAnyAppleOS26_1()' is only available in watchOS 26.1 or newer}}
47+
// expected-tvos-error@-4 {{'availableInAnyAppleOS26_1()' is only available in tvOS 26.1 or newer}}
48+
// expected-visionos-error@-5 {{'availableInAnyAppleOS26_1()' is only available in visionOS 26.1 or newer}}
49+
// expected-apple-note@-6 {{add 'if #available' version check}}
50+
51+
// FIXME: [availability] Remap domain/version in deprecation diagnostics
52+
deprecatedInAnyAppleOS26()
53+
// expected-apple-warning@-1 {{'deprecatedInAnyAppleOS26()' was deprecated in any Apple OS 26}}
54+
55+
obsoletedInAnyAppleOS26()
56+
// expected-macos-error@-1 {{'obsoletedInAnyAppleOS26()' is unavailable in macOS}}
57+
// expected-ios-error@-2 {{'obsoletedInAnyAppleOS26()' is unavailable in iOS}}
58+
// expected-watchos-error@-3 {{'obsoletedInAnyAppleOS26()' is unavailable in watchOS}}
59+
// expected-tvos-error@-4 {{'obsoletedInAnyAppleOS26()' is unavailable in tvOS}}
60+
// expected-visionos-error@-5 {{'obsoletedInAnyAppleOS26()' is unavailable in visionOS}}
61+
62+
availableInAnyAppleOS26AndMacOS26_1()
63+
// expected-macos-error@-1 {{'availableInAnyAppleOS26AndMacOS26_1()' is only available in macOS 26.1 or newer}}
64+
// expected-macos-note@-2 {{add 'if #available' version check}}{{3-40=if #available(macOS 26.1, *) {\n availableInAnyAppleOS26AndMacOS26_1()\n \} else {\n // Fallback on earlier versions\n \}}}
65+
availableInMacOS26_1AndAnyAppleOS26()
66+
// expected-macos-error@-1 {{'availableInMacOS26_1AndAnyAppleOS26()' is only available in macOS 26.1 or newer}}
67+
// expected-macos-note@-2 {{add 'if #available' version check}}{{3-40=if #available(macOS 26.1, *) {\n availableInMacOS26_1AndAnyAppleOS26()\n \} else {\n // Fallback on earlier versions\n \}}}
68+
69+
unavailableInAnyAppleOS()
70+
// expected-macos-error@-1 {{'unavailableInAnyAppleOS()' is unavailable in macOS}}
71+
// expected-ios-error@-2 {{'unavailableInAnyAppleOS()' is unavailable in iOS}}
72+
// expected-watchos-error@-3 {{'unavailableInAnyAppleOS()' is unavailable in watchOS}}
73+
// expected-tvos-error@-4 {{'unavailableInAnyAppleOS()' is unavailable in tvOS}}
74+
// expected-visionos-error@-5 {{'unavailableInAnyAppleOS()' is unavailable in visionOS}}
75+
76+
if #available(anyAppleOS 25, *) { } // expected-warning {{'25' is not a valid version number for any Apple OS}}
77+
78+
if #available(anyAppleOS 26.1, *) {
79+
availableInAnyAppleOS26_1()
80+
availableInEveryAppleOS26_1()
81+
availableInAnyAppleOS26AndMacOS26_1()
82+
availableInMacOS26_1AndAnyAppleOS26()
83+
}
84+
if #available(macOS 26.1, *) {
85+
availableInAnyAppleOS26AndMacOS26_1()
86+
availableInMacOS26_1AndAnyAppleOS26()
87+
}
88+
if #available(macOS 26.1, iOS 26.1, watchOS 26.1, tvOS 26.1, visionOS 26.1, *) {
89+
availableInAnyAppleOS26_1()
90+
availableInEveryAppleOS26_1()
91+
}
92+
}
93+
94+
@available(anyAppleOS 26.1, *)
95+
struct AvailableInAnyAppleOS26_1 {
96+
func method() {
97+
availableInAnyAppleOS26_1()
98+
}
99+
}
100+
101+
@available(anyAppleOS, unavailable)
102+
func alsoUnavailableInAnyAppleOS() {
103+
unavailableInAnyAppleOS()
104+
}

test/attr/attr_availability.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ func incorrect_platform_not_similar() {}
3939
@available(HAL9000, unavailable) // expected-warning {{unrecognized platform name 'HAL9000'}}
4040
func availabilityUnknownPlatform() {}
4141

42+
@available(Swift 6.2, *) // expected-error {{Swift requires '-enable-experimental-feature SwiftRuntimeAvailability'}}
43+
func swift6_2() {}
44+
45+
@available(SwiftLanguageMode 6.0, *) // expected-error {{Swift requires '-enable-experimental-feature SwiftRuntimeAvailability'}}
46+
func swiftLanguageMode6_0() {}
47+
48+
@available(anyAppleOS 26, *) // expected-error {{any Apple OS requires '-enable-experimental-feature AnyAppleOSAvailability'}}
49+
func anyAppleOS26() {}
50+
4251
// <rdar://problem/17669805> Availability can't appear on a typealias
4352
@available(*, unavailable, message: "oh no you don't")
4453
typealias int = Int // expected-note {{'int' has been explicitly marked unavailable here}}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 5 -parse-as-library -enable-experimental-feature AnyAppleOSAvailability
2+
3+
// REQUIRES: swift_feature_AnyAppleOSAvailability
4+
5+
@available(anyAppleOS 26, *)
6+
func availableIn26Short() { }
7+
8+
@available(anyAppleOS 26.0, *)
9+
func availableIn26_0Short() { }
10+
11+
@available(AnyAppleOS 26, *) // expected-warning {{unrecognized platform name 'AnyAppleOS'; did you mean 'anyAppleOS'}}
12+
func miscapitalized() { }
13+
14+
@available(anyAppleOS 25, *) // expected-warning {{'25' is not a valid version number for any Apple OS}}
15+
func availableIn25Short() { }
16+
17+
@available(anyAppleOS 26, macOS 26, iOS 26, watchOS 26, tvOS 26, visionOS 26, *)
18+
func availableIn26ShortWithPlatforms() { }
19+
20+
@available(anyAppleOS, introduced: 26)
21+
func availableIn26() { }
22+
23+
@available(anyAppleOS, introduced: 26.0)
24+
func availableIn26_0() { }
25+
26+
@available(anyAppleOS, obsoleted: 26)
27+
func obsoletedIn26() { }
28+
29+
@available(anyAppleOS, deprecated: 26)
30+
func deprecatedIn26() { }
31+
32+
@available(anyAppleOS, deprecated)
33+
func deprecated() { }
34+
35+
@available(anyAppleOS, unavailable)
36+
func unavailable() { }

0 commit comments

Comments
 (0)