Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,9 @@ SUPPRESSIBLE_EXPERIMENTAL_FEATURE(TildeSendable, false)
/// Allow use of protocol typed values in Embedded mode (`Any` and friends)
EXPERIMENTAL_FEATURE(EmbeddedExistentials, false)

/// Allow use of the 'anyAppleOS' availability domain.
EXPERIMENTAL_FEATURE(AnyAppleOSAvailability, true)

#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
#undef EXPERIMENTAL_FEATURE
#undef UPCOMING_FEATURE
Expand Down
5 changes: 3 additions & 2 deletions lib/AST/Availability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -684,9 +684,10 @@ SemanticAvailableAttrRequest::evaluate(swift::Evaluator &evaluator,
if (!version)
return false;

auto diagLoc = sourceRange.isValid() ? sourceRange.Start : attrLoc;
if (!VersionRange::isValidVersion(*version)) {
diags
.diagnose(attrLoc, diag::availability_unsupported_version_number,
.diagnose(diagLoc, diag::availability_unsupported_version_number,
*version)
.highlight(sourceRange);
return true;
Expand All @@ -696,7 +697,7 @@ SemanticAvailableAttrRequest::evaluate(swift::Evaluator &evaluator,
// 17 will never exist.
if (domain->isVersioned() && !domain->isVersionValid(*version)) {
diags
.diagnose(attrLoc,
.diagnose(diagLoc,
diag::availability_invalid_version_number_for_domain,
*version, *domain)
.highlight(sourceRange);
Expand Down
75 changes: 60 additions & 15 deletions lib/AST/AvailabilityDomain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,17 @@ bool AvailabilityDomain::isVersionValid(
return true;

case Kind::Platform:
// If the platform kind corresponds to a specific OS, LLVM is the source of
// truth for version validity.
if (auto osType = tripleOSTypeForPlatform(getPlatformKind()))
return llvm::Triple::isValidVersionForOS(*osType, version);

// Unified versioning for Apple's operating systems starts at 26.0.
if (getPlatformKind() == PlatformKind::anyAppleOS)
return (version.getMajor() >= 26);

return true;

case Kind::Custom:
return true;
}
Expand Down Expand Up @@ -400,12 +408,36 @@ AvailabilityDomain AvailabilityDomain::getRootDomain() const {
return *this;
}

static std::optional<PlatformKind>
getApplePlatformKindForTarget(const llvm::Triple &target) {
if (target.isMacOSX())
return PlatformKind::macOS;
if (target.isTvOS()) // Must be checked before isiOS.
return PlatformKind::tvOS;
if (target.isiOS())
return PlatformKind::iOS;
if (target.isWatchOS())
return PlatformKind::watchOS;
if (target.isXROS())
return PlatformKind::visionOS;

return std::nullopt;
}

std::optional<AvailabilityDomain>
AvailabilityDomain::getRemappedDomainOrNull(const ASTContext &ctx) const {
if (getPlatformKind() == PlatformKind::iOS &&
isPlatformActive(PlatformKind::visionOS, ctx.LangOpts))
return AvailabilityDomain::forPlatform(PlatformKind::visionOS);

if (getPlatformKind() == PlatformKind::anyAppleOS) {
if (auto applePlatformKind =
getApplePlatformKindForTarget(ctx.LangOpts.Target))
return AvailabilityDomain::forPlatform(*applePlatformKind);

return std::nullopt;
}

return std::nullopt;
}

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

std::optional<clang::VersionTuple> remappedVersion;
switch (versionKind) {
case AvailabilityVersionKind::Introduced:
remappedVersion =
getRemappedIntroducedVersionForFallbackPlatform(ctx, version);
break;
case AvailabilityVersionKind::Deprecated:
case AvailabilityVersionKind::Obsoleted:
remappedVersion =
getRemappedDeprecatedObsoletedVersionForFallbackPlatform(ctx, version);
break;
}
if (getPlatformKind() == PlatformKind::anyAppleOS)
return {*remappedDomain, AvailabilityRange{version}};

if (getPlatformKind() == PlatformKind::iOS) {
std::optional<clang::VersionTuple> remappedVersion;
switch (versionKind) {
case AvailabilityVersionKind::Introduced:
remappedVersion =
getRemappedIntroducedVersionForFallbackPlatform(ctx, version);
break;
case AvailabilityVersionKind::Deprecated:
case AvailabilityVersionKind::Obsoleted:
remappedVersion =
getRemappedDeprecatedObsoletedVersionForFallbackPlatform(ctx,
version);
break;
}

if (!remappedVersion)
return {*this, AvailabilityRange{version}};
if (remappedVersion)
return {*remappedDomain, AvailabilityRange{*remappedVersion}};
}

return {*remappedDomain, AvailabilityRange{*remappedVersion}};
return {*this, AvailabilityRange{version}};
}

bool IsCustomAvailabilityDomainPermanentlyEnabled::evaluate(
Expand Down Expand Up @@ -627,6 +665,13 @@ AvailabilityDomainOrIdentifier::lookUpInDeclContext(
return std::nullopt;
}

if (domain->getPlatformKind() == PlatformKind::anyAppleOS &&
!ctx.LangOpts.hasFeature(Feature::AnyAppleOSAvailability)) {
diags.diagnose(loc, diag::availability_domain_requires_feature, *domain,
"AnyAppleOSAvailability");
return std::nullopt;
}

// Use of the 'Swift' domain requires the 'SwiftRuntimeAvailability' feature.
if (!hasSwiftRuntimeAvailability && domain->isStandaloneSwiftRuntime()) {
diags.diagnose(loc, diag::availability_domain_requires_feature, *domain,
Expand Down
1 change: 1 addition & 0 deletions lib/AST/FeatureSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ static bool usesFeatureTildeSendable(Decl *decl) {
});
}

UNINTERESTING_FEATURE(AnyAppleOSAvailability)

// ----------------------------------------------------------------------------
// MARK: - FeatureSet
Expand Down
42 changes: 36 additions & 6 deletions lib/AST/PlatformKindUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,12 @@ swift::basePlatformForExtensionPlatform(PlatformKind Platform) {

static bool isPlatformActiveForTarget(PlatformKind Platform,
const llvm::Triple &Target,
bool EnableAppExtensionRestrictions,
const LangOptions &LangOpts,
bool ForRuntimeQuery) {
if (Platform == PlatformKind::none)
return true;

if (!EnableAppExtensionRestrictions &&
if (!LangOpts.EnableAppExtensionRestrictions &&
isApplicationExtensionPlatform(Platform))
return false;

Expand Down Expand Up @@ -186,12 +186,11 @@ bool swift::isPlatformActive(PlatformKind Platform, const LangOptions &LangOpts,
if (ForTargetVariant) {
assert(LangOpts.TargetVariant && "Must have target variant triple");
return isPlatformActiveForTarget(Platform, *LangOpts.TargetVariant,
LangOpts.EnableAppExtensionRestrictions,
ForRuntimeQuery);
LangOpts, ForRuntimeQuery);
}

return isPlatformActiveForTarget(Platform, LangOpts.Target,
LangOpts.EnableAppExtensionRestrictions, ForRuntimeQuery);
return isPlatformActiveForTarget(Platform, LangOpts.Target, LangOpts,
ForRuntimeQuery);
}

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

static bool inheritsAvailabilityFromAnyAppleOS(PlatformKind platform) {
switch (platform) {
case PlatformKind::macOSApplicationExtension:
case PlatformKind::iOSApplicationExtension:
case PlatformKind::macCatalystApplicationExtension:
case PlatformKind::tvOSApplicationExtension:
case PlatformKind::watchOSApplicationExtension:
case PlatformKind::visionOSApplicationExtension:
case PlatformKind::macOS:
case PlatformKind::iOS:
case PlatformKind::macCatalyst:
case PlatformKind::tvOS:
case PlatformKind::watchOS:
case PlatformKind::visionOS:
return true;
case PlatformKind::DriverKit:
case PlatformKind::anyAppleOS:
case PlatformKind::Swift:
case PlatformKind::FreeBSD:
case PlatformKind::OpenBSD:
case PlatformKind::Windows:
case PlatformKind::Android:
case PlatformKind::none:
return false;
}
}

bool swift::inheritsAvailabilityFromPlatform(PlatformKind Child,
PlatformKind Parent) {
if (auto ChildPlatformBase = basePlatformForExtensionPlatform(Child)) {
Expand Down Expand Up @@ -277,6 +303,10 @@ bool swift::inheritsAvailabilityFromPlatform(PlatformKind Child,
}
}

if (Parent == PlatformKind::anyAppleOS &&
inheritsAvailabilityFromAnyAppleOS(Child))
return true;

return false;
}

Expand Down
104 changes: 104 additions & 0 deletions test/Availability/availability_any_apple_os.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// RUN: %target-typecheck-verify-swift -parse-stdlib -enable-experimental-feature AnyAppleOSAvailability -target %target-cpu-apple-macos26 -verify-additional-prefix apple- -verify-additional-prefix macos-
// RUN: %target-typecheck-verify-swift -parse-stdlib -enable-experimental-feature AnyAppleOSAvailability -target %target-cpu-apple-ios26 -verify-additional-prefix apple- -verify-additional-prefix ios-
// RUN: %target-typecheck-verify-swift -parse-stdlib -enable-experimental-feature AnyAppleOSAvailability -target %target-cpu-apple-watchos26 -verify-additional-prefix apple- -verify-additional-prefix watchos-
// RUN: %target-typecheck-verify-swift -parse-stdlib -enable-experimental-feature AnyAppleOSAvailability -target %target-cpu-apple-tvos26 -verify-additional-prefix apple- -verify-additional-prefix tvos-
// RUN: %target-typecheck-verify-swift -parse-stdlib -enable-experimental-feature AnyAppleOSAvailability -target %target-cpu-apple-visionos26 -verify-additional-prefix apple- -verify-additional-prefix visionos-
// RUN: %target-typecheck-verify-swift -parse-stdlib -enable-experimental-feature AnyAppleOSAvailability -target x86_64-unknown-linux-gnu
// RUN: %target-typecheck-verify-swift -parse-stdlib -enable-experimental-feature AnyAppleOSAvailability -target x86_64-unknown-windows-msvc

// REQUIRES: swift_feature_AnyAppleOSAvailability

@available(anyAppleOS 26.1, *)
func availableInAnyAppleOS26_1() { }

@available(anyAppleOS, deprecated: 26)
func deprecatedInAnyAppleOS26() { }

@available(anyAppleOS, obsoleted: 26)
func obsoletedInAnyAppleOS26() { }
// expected-macos-note@-1 {{'obsoletedInAnyAppleOS26()' was obsoleted in macOS 26}}
// expected-ios-note@-2 {{'obsoletedInAnyAppleOS26()' was obsoleted in iOS 26}}
// expected-watchos-note@-3 {{'obsoletedInAnyAppleOS26()' was obsoleted in watchOS 26}}
// expected-tvos-note@-4 {{'obsoletedInAnyAppleOS26()' was obsoleted in tvOS 26}}
// expected-visionos-note@-5 {{'obsoletedInAnyAppleOS26()' was obsoleted in visionOS 26}}

@available(anyAppleOS 26, macOS 26.1, *)
func availableInAnyAppleOS26AndMacOS26_1() { }

@available(macOS 26.1, anyAppleOS 26, *)
func availableInMacOS26_1AndAnyAppleOS26() { }

@available(macOS 26.1, iOS 26.1, watchOS 26.1, tvOS 26.1, visionOS 26.1, *)
func availableInEveryAppleOS26_1() { }

@available(anyAppleOS, unavailable)
func unavailableInAnyAppleOS() { } // expected-apple-note {{'unavailableInAnyAppleOS()' has been explicitly marked unavailable here}}

// FIXME: [availability] Ensure the fix-it suggests @available(anyAppleOS ...) rdar://163819878
func availableAtDeploymentTarget() {
// expected-apple-note@-1 {{add '@available' attribute to enclosing global function}}
// expected-macos-note@-2 2 {{add '@available' attribute to enclosing global function}}

// FIXME: [availability] Ensure the fix-it suggests if #available(anyAppleOS ...) rdar://163819878
availableInAnyAppleOS26_1()
// expected-macos-error@-1 {{'availableInAnyAppleOS26_1()' is only available in macOS 26.1 or newer}}
// expected-ios-error@-2 {{'availableInAnyAppleOS26_1()' is only available in iOS 26.1 or newer}}
// expected-watchos-error@-3 {{'availableInAnyAppleOS26_1()' is only available in watchOS 26.1 or newer}}
// expected-tvos-error@-4 {{'availableInAnyAppleOS26_1()' is only available in tvOS 26.1 or newer}}
// expected-visionos-error@-5 {{'availableInAnyAppleOS26_1()' is only available in visionOS 26.1 or newer}}
// expected-apple-note@-6 {{add 'if #available' version check}}

// FIXME: [availability] Remap domain/version in deprecation diagnostics
deprecatedInAnyAppleOS26()
// expected-apple-warning@-1 {{'deprecatedInAnyAppleOS26()' was deprecated in any Apple OS 26}}

obsoletedInAnyAppleOS26()
// expected-macos-error@-1 {{'obsoletedInAnyAppleOS26()' is unavailable in macOS}}
// expected-ios-error@-2 {{'obsoletedInAnyAppleOS26()' is unavailable in iOS}}
// expected-watchos-error@-3 {{'obsoletedInAnyAppleOS26()' is unavailable in watchOS}}
// expected-tvos-error@-4 {{'obsoletedInAnyAppleOS26()' is unavailable in tvOS}}
// expected-visionos-error@-5 {{'obsoletedInAnyAppleOS26()' is unavailable in visionOS}}

availableInAnyAppleOS26AndMacOS26_1()
// expected-macos-error@-1 {{'availableInAnyAppleOS26AndMacOS26_1()' is only available in macOS 26.1 or newer}}
// 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 \}}}
availableInMacOS26_1AndAnyAppleOS26()
// expected-macos-error@-1 {{'availableInMacOS26_1AndAnyAppleOS26()' is only available in macOS 26.1 or newer}}
// 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 \}}}

unavailableInAnyAppleOS()
// expected-macos-error@-1 {{'unavailableInAnyAppleOS()' is unavailable in macOS}}
// expected-ios-error@-2 {{'unavailableInAnyAppleOS()' is unavailable in iOS}}
// expected-watchos-error@-3 {{'unavailableInAnyAppleOS()' is unavailable in watchOS}}
// expected-tvos-error@-4 {{'unavailableInAnyAppleOS()' is unavailable in tvOS}}
// expected-visionos-error@-5 {{'unavailableInAnyAppleOS()' is unavailable in visionOS}}

if #available(anyAppleOS 25, *) { } // expected-warning {{'25' is not a valid version number for any Apple OS}}

if #available(anyAppleOS 26.1, *) {
availableInAnyAppleOS26_1()
availableInEveryAppleOS26_1()
availableInAnyAppleOS26AndMacOS26_1()
availableInMacOS26_1AndAnyAppleOS26()
}
if #available(macOS 26.1, *) {
availableInAnyAppleOS26AndMacOS26_1()
availableInMacOS26_1AndAnyAppleOS26()
}
if #available(macOS 26.1, iOS 26.1, watchOS 26.1, tvOS 26.1, visionOS 26.1, *) {
availableInAnyAppleOS26_1()
availableInEveryAppleOS26_1()
}
}

@available(anyAppleOS 26.1, *)
struct AvailableInAnyAppleOS26_1 {
func method() {
availableInAnyAppleOS26_1()
}
}

@available(anyAppleOS, unavailable)
func alsoUnavailableInAnyAppleOS() {
unavailableInAnyAppleOS()
}
9 changes: 9 additions & 0 deletions test/attr/attr_availability.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ func incorrect_platform_not_similar() {}
@available(HAL9000, unavailable) // expected-warning {{unrecognized platform name 'HAL9000'}}
func availabilityUnknownPlatform() {}

@available(Swift 6.2, *) // expected-error {{Swift requires '-enable-experimental-feature SwiftRuntimeAvailability'}}
func swift6_2() {}

@available(SwiftLanguageMode 6.0, *) // expected-error {{Swift requires '-enable-experimental-feature SwiftRuntimeAvailability'}}
func swiftLanguageMode6_0() {}

@available(anyAppleOS 26, *) // expected-error {{any Apple OS requires '-enable-experimental-feature AnyAppleOSAvailability'}}
func anyAppleOS26() {}

// <rdar://problem/17669805> Availability can't appear on a typealias
@available(*, unavailable, message: "oh no you don't")
typealias int = Int // expected-note {{'int' has been explicitly marked unavailable here}}
Expand Down
36 changes: 36 additions & 0 deletions test/attr/attr_availability_any_apple_os.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// RUN: %target-typecheck-verify-swift -swift-version 5 -parse-as-library -enable-experimental-feature AnyAppleOSAvailability

// REQUIRES: swift_feature_AnyAppleOSAvailability

@available(anyAppleOS 26, *)
func availableIn26Short() { }

@available(anyAppleOS 26.0, *)
func availableIn26_0Short() { }

@available(AnyAppleOS 26, *) // expected-warning {{unrecognized platform name 'AnyAppleOS'; did you mean 'anyAppleOS'}}
func miscapitalized() { }

@available(anyAppleOS 25, *) // expected-warning {{'25' is not a valid version number for any Apple OS}}
func availableIn25Short() { }

@available(anyAppleOS 26, macOS 26, iOS 26, watchOS 26, tvOS 26, visionOS 26, *)
func availableIn26ShortWithPlatforms() { }

@available(anyAppleOS, introduced: 26)
func availableIn26() { }

@available(anyAppleOS, introduced: 26.0)
func availableIn26_0() { }

@available(anyAppleOS, obsoleted: 26)
func obsoletedIn26() { }

@available(anyAppleOS, deprecated: 26)
func deprecatedIn26() { }

@available(anyAppleOS, deprecated)
func deprecated() { }

@available(anyAppleOS, unavailable)
func unavailable() { }