From 9b7c531fd89d9a7b2fcf58252fa9ab1eb865c6d3 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Fri, 24 Oct 2025 16:14:20 -0700 Subject: [PATCH 1/3] AST/IDE/Sema: Remove unnecessary AvailabilityInference.h includes. NFC. --- lib/AST/Attr.cpp | 1 - lib/AST/AvailabilityContext.cpp | 1 - lib/AST/AvailabilityScope.cpp | 1 - lib/IDE/CodeCompletionDiagnostics.cpp | 1 - lib/Sema/TypeCheckDecl.cpp | 1 - 5 files changed, 5 deletions(-) diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 3d220566ba5ae..e9b7986842c15 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -18,7 +18,6 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/ASTPrinter.h" #include "swift/AST/AvailabilityDomain.h" -#include "swift/AST/AvailabilityInference.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" #include "swift/AST/GenericEnvironment.h" diff --git a/lib/AST/AvailabilityContext.cpp b/lib/AST/AvailabilityContext.cpp index 327c74e420b2e..cc059f555d7ef 100644 --- a/lib/AST/AvailabilityContext.cpp +++ b/lib/AST/AvailabilityContext.cpp @@ -14,7 +14,6 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/AvailabilityConstraint.h" #include "swift/AST/AvailabilityContextStorage.h" -#include "swift/AST/AvailabilityInference.h" #include "swift/AST/AvailabilityScope.h" #include "swift/AST/Decl.h" #include "swift/AST/Module.h" diff --git a/lib/AST/AvailabilityScope.cpp b/lib/AST/AvailabilityScope.cpp index 81558b9ca272a..911230b0dbdf1 100644 --- a/lib/AST/AvailabilityScope.cpp +++ b/lib/AST/AvailabilityScope.cpp @@ -18,7 +18,6 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/AvailabilityConstraint.h" -#include "swift/AST/AvailabilityInference.h" #include "swift/AST/AvailabilitySpec.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" diff --git a/lib/IDE/CodeCompletionDiagnostics.cpp b/lib/IDE/CodeCompletionDiagnostics.cpp index cb54dc0238ea3..da6762debcf9d 100644 --- a/lib/IDE/CodeCompletionDiagnostics.cpp +++ b/lib/IDE/CodeCompletionDiagnostics.cpp @@ -13,7 +13,6 @@ #include "CodeCompletionDiagnostics.h" #include "swift/AST/ASTContext.h" -#include "swift/AST/AvailabilityInference.h" #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsIDE.h" diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 661428cd08083..94c9a98273b42 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -32,7 +32,6 @@ #include "swift/AST/ASTWalker.h" #include "swift/AST/AccessScope.h" #include "swift/AST/Attr.h" -#include "swift/AST/AvailabilityInference.h" #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/ConformanceLookup.h" #include "swift/AST/Decl.h" From 88bc79f6afed7411131f9ef5ac47b907335dcd64 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Fri, 24 Oct 2025 16:17:21 -0700 Subject: [PATCH 2/3] AST: Remove unnecessary ASTContext argument. There's no need to take one as input to a method on `Decl`. NFC. --- include/swift/AST/Decl.h | 3 +-- lib/AST/Decl.cpp | 20 +++++++++----------- lib/SILGen/SILGenAvailability.cpp | 8 ++++---- lib/Sema/TypeCheckAttr.cpp | 2 +- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 026dce1948458..7c41541229f61 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -1134,8 +1134,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated, public Swi /// Returns the active `@backDeployed` attribute and the `AvailabilityRange` /// in which the decl is available as ABI. std::optional> - getBackDeployedAttrAndRange(ASTContext &Ctx, - bool forTargetVariant = false) const; + getBackDeployedAttrAndRange(bool forTargetVariant = false) const; /// Returns true if the decl has a valid and active `@backDeployed` attribute. bool isBackDeployed() const; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index a6c01aa21d307..74e1c601fc618 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -597,13 +597,13 @@ Decl::getIntroducedOSVersion(PlatformKind Kind) const { } std::optional> -Decl::getBackDeployedAttrAndRange(ASTContext &Ctx, - bool forTargetVariant) const { - if (auto *attr = getAttrs().getBackDeployed(Ctx, forTargetVariant)) { +Decl::getBackDeployedAttrAndRange(bool forTargetVariant) const { + auto &ctx = getASTContext(); + if (auto *attr = getAttrs().getBackDeployed(ctx, forTargetVariant)) { auto version = attr->getVersion(); AvailabilityDomain ignoredDomain; AvailabilityInference::updateBeforeAvailabilityDomainForFallback( - attr, getASTContext(), ignoredDomain, version); + attr, ctx, ignoredDomain, version); // If the remap for fallback resulted in 1.0, then the // backdeployment prior to that is not meaningful. @@ -615,14 +615,12 @@ Decl::getBackDeployedAttrAndRange(ASTContext &Ctx, // Accessors may inherit `@backDeployed`. if (auto *AD = dyn_cast(this)) - return AD->getStorage()->getBackDeployedAttrAndRange(Ctx, forTargetVariant); + return AD->getStorage()->getBackDeployedAttrAndRange(forTargetVariant); return std::nullopt; } bool Decl::isBackDeployed() const { - auto &Ctx = getASTContext(); - // A function declared in a local context can never be back-deployed. if (getDeclContext()->isLocalContext()) return false; @@ -636,11 +634,11 @@ bool Decl::isBackDeployed() const { return false; } - if (getBackDeployedAttrAndRange(Ctx)) + if (getBackDeployedAttrAndRange()) return true; - if (Ctx.LangOpts.TargetVariant) { - if (getBackDeployedAttrAndRange(Ctx, /*forTargetVariant=*/true)) + if (getASTContext().LangOpts.TargetVariant) { + if (getBackDeployedAttrAndRange(/*forTargetVariant=*/true)) return true; } @@ -1482,7 +1480,7 @@ AvailabilityRange Decl::getAvailabilityForLinkage() const { // When computing availability for linkage, use the "before" version from // the @backDeployed attribute, if present. - if (auto backDeployedAttrAndRange = getBackDeployedAttrAndRange(ctx)) + if (auto backDeployedAttrAndRange = getBackDeployedAttrAndRange()) return backDeployedAttrAndRange->second; auto containingContext = AvailabilityInference::annotatedAvailableRange(this); diff --git a/lib/SILGen/SILGenAvailability.cpp b/lib/SILGen/SILGenAvailability.cpp index e566259790f71..f289c3a44da5b 100644 --- a/lib/SILGen/SILGenAvailability.cpp +++ b/lib/SILGen/SILGenAvailability.cpp @@ -113,9 +113,9 @@ static std::optional getAvailabilityQueryForBackDeployment(AbstractFunctionDecl *AFD) { auto &ctx = AFD->getASTContext(); if (ctx.LangOpts.TargetVariant) { - auto primaryAttrAndRange = AFD->getBackDeployedAttrAndRange(ctx); + auto primaryAttrAndRange = AFD->getBackDeployedAttrAndRange(); auto variantAttrAndRange = - AFD->getBackDeployedAttrAndRange(ctx, /*forTargetVariant=*/true); + AFD->getBackDeployedAttrAndRange(/*forTargetVariant=*/true); if (!primaryAttrAndRange && !variantAttrAndRange) return std::nullopt; @@ -134,7 +134,7 @@ getAvailabilityQueryForBackDeployment(AbstractFunctionDecl *AFD) { primaryRange, variantRange); } - if (auto primaryAttrAndRange = AFD->getBackDeployedAttrAndRange(ctx)) + if (auto primaryAttrAndRange = AFD->getBackDeployedAttrAndRange()) return AvailabilityQuery::dynamic( primaryAttrAndRange->first->getAvailabilityDomain(), primaryAttrAndRange->second, std::nullopt); @@ -284,7 +284,7 @@ SILGenFunction::emitIfAvailableQuery(SILLocation loc, bool SILGenModule::requiresBackDeploymentThunk(ValueDecl *decl, ResilienceExpansion expansion) { auto &ctx = getASTContext(); - auto attrAndRange = decl->getBackDeployedAttrAndRange(ctx); + auto attrAndRange = decl->getBackDeployedAttrAndRange(); if (!attrAndRange) return false; diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index f9ff76cd06d94..97cee7cb0eab3 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -5266,7 +5266,7 @@ void AttributeChecker::checkBackDeployedAttrs( std::map seenPlatforms; const BackDeployedAttr *ActiveAttr = nullptr; - if (auto AttrAndRange = D->getBackDeployedAttrAndRange(Ctx)) + if (auto AttrAndRange = D->getBackDeployedAttrAndRange()) ActiveAttr = AttrAndRange->first; for (auto *Attr : Attrs) { From 05f7cb7017284de5cb9a656ee028917d76b322ce Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Fri, 24 Oct 2025 17:13:40 -0700 Subject: [PATCH 3/3] AST: Refactor availability version remapping. Availability version remapping currently only applies to code built for visionOS. We plan to introduce more platform kinds and standalone availability domains that will require version remapping, though, so it's time to rearchitect and simplify the code to make it easier to generalize. `AvailabilityDomain` is now responsible for version remapping and much of the previously duplicated utilities have been consolidated. --- include/swift/AST/AvailabilityDomain.h | 33 +++- include/swift/AST/AvailabilityInference.h | 34 +--- lib/AST/Availability.cpp | 225 ++++------------------ lib/AST/AvailabilityDomain.cpp | 69 ++++++- lib/AST/Decl.cpp | 19 +- lib/Sema/TypeCheckAttr.cpp | 38 ++-- 6 files changed, 151 insertions(+), 267 deletions(-) diff --git a/include/swift/AST/AvailabilityDomain.h b/include/swift/AST/AvailabilityDomain.h index ff1fe134168f3..eea232ecc4454 100644 --- a/include/swift/AST/AvailabilityDomain.h +++ b/include/swift/AST/AvailabilityDomain.h @@ -33,12 +33,21 @@ namespace swift { class ASTContext; +class AvailabilityDomainAndRange; class CustomAvailabilityDomain; class DeclContext; class FuncDecl; class ModuleDecl; class ValueDecl; +/// Discriminates whether a version tuple represents the `introduced:`, +/// `deprecated:`, or `obsoleted:` version of an `@available` attribute. +enum class AvailabilityVersionKind { + Introduced, + Deprecated, + Obsoleted, +}; + /// Represents a dimension of availability (e.g. macOS platform or Swift /// language mode). class AvailabilityDomain final { @@ -131,6 +140,9 @@ class AvailabilityDomain final { : std::nullopt; } + std::optional + getRemappedDomainOrNull(const ASTContext &ctx) const; + public: AvailabilityDomain() {} @@ -294,19 +306,22 @@ class AvailabilityDomain final { /// descendants of the iOS domain. AvailabilityDomain getRootDomain() const; - /// Returns the canonical domain that versions in this domain must be remapped - /// to before making availability comparisons in the current compilation - /// context. Sets \p didRemap to `true` if a remap was required. - const AvailabilityDomain getRemappedDomain(const ASTContext &ctx, - bool &didRemap) const; - /// Returns the canonical domain that versions in this domain must be remapped /// to before making availability comparisons in the current compilation /// context. const AvailabilityDomain getRemappedDomain(const ASTContext &ctx) const { - bool unused; - return getRemappedDomain(ctx, unused); - } + auto remappedDomain = getRemappedDomainOrNull(ctx); + return remappedDomain ? *remappedDomain : *this; + } + + /// Converts the domain and the given version into a canonical domain and + /// range that can be used for availability comparisons in the current current + /// compilation context. If no conversion is necessary or possible, the domain + /// and range are returned unmodified. + AvailabilityDomainAndRange + getRemappedDomainAndRange(const llvm::VersionTuple &version, + AvailabilityVersionKind versionKind, + const ASTContext &ctx) const; /// Returns true for a domain that is permanently always available, and /// therefore availability constraints in the domain are effectively the same diff --git a/include/swift/AST/AvailabilityInference.h b/include/swift/AST/AvailabilityInference.h index efb9fd7290fa2..b8e1d51a4ac9f 100644 --- a/include/swift/AST/AvailabilityInference.h +++ b/include/swift/AST/AvailabilityInference.h @@ -25,7 +25,6 @@ namespace swift { class ASTContext; class AvailabilityDomain; -class BackDeployedAttr; class Decl; class SemanticAvailableAttr; @@ -49,37 +48,8 @@ class AvailabilityInference { static std::optional annotatedAvailableRange(const Decl *D); - static AvailabilityRange - annotatedAvailableRangeForAttr(const Decl *D, const AbstractSpecializeAttr *attr, - ASTContext &ctx); - - /// For the attribute's introduction version, update the platform and version - /// values to the re-mapped platform's, if using a fallback platform. - /// Returns `true` if a remap occured. - static bool updateIntroducedAvailabilityDomainForFallback( - const SemanticAvailableAttr &attr, const ASTContext &ctx, - AvailabilityDomain &domain, llvm::VersionTuple &platformVer); - - /// For the attribute's deprecation version, update the platform and version - /// values to the re-mapped platform's, if using a fallback platform. - /// Returns `true` if a remap occured. - static bool updateDeprecatedAvailabilityDomainForFallback( - const SemanticAvailableAttr &attr, const ASTContext &ctx, - AvailabilityDomain &domain, llvm::VersionTuple &platformVer); - - /// For the attribute's obsoletion version, update the platform and version - /// values to the re-mapped platform's, if using a fallback platform. - /// Returns `true` if a remap occured. - static bool updateObsoletedAvailabilityDomainForFallback( - const SemanticAvailableAttr &attr, const ASTContext &ctx, - AvailabilityDomain &domain, llvm::VersionTuple &platformVer); - - /// For the attribute's before version, update the platform and version - /// values to the re-mapped platform's, if using a fallback platform. - /// Returns `true` if a remap occured. - static bool updateBeforeAvailabilityDomainForFallback( - const BackDeployedAttr *attr, const ASTContext &ctx, - AvailabilityDomain &domain, llvm::VersionTuple &platformVer); + static AvailabilityRange annotatedAvailableRangeForAttr( + const Decl *D, const AbstractSpecializeAttr *attr, ASTContext &ctx); }; } // end namespace swift diff --git a/lib/AST/Availability.cpp b/lib/AST/Availability.cpp index 6d9072a84ebf4..e9237a6c5d505 100644 --- a/lib/AST/Availability.cpp +++ b/lib/AST/Availability.cpp @@ -256,128 +256,6 @@ static bool isBetterThan(const SemanticAvailableAttr &newAttr, prevAttr->getPlatform()); } -static const clang::DarwinSDKInfo::RelatedTargetVersionMapping * -getFallbackVersionMapping(const ASTContext &Ctx, - clang::DarwinSDKInfo::OSEnvPair Kind) { - auto *SDKInfo = Ctx.getDarwinSDKInfo(); - if (SDKInfo) - return SDKInfo->getVersionMapping(Kind); - - return Ctx.getAuxiliaryDarwinPlatformRemapInfo(Kind); -} - -static std::optional -getRemappedIntroducedVersionForFallbackPlatform( - const ASTContext &Ctx, const llvm::VersionTuple &Version) { - const auto *Mapping = getFallbackVersionMapping( - Ctx, clang::DarwinSDKInfo::OSEnvPair( - llvm::Triple::IOS, llvm::Triple::UnknownEnvironment, - llvm::Triple::XROS, llvm::Triple::UnknownEnvironment)); - if (!Mapping) - return std::nullopt; - return Mapping->mapIntroducedAvailabilityVersion(Version); -} - -static std::optional -getRemappedDeprecatedObsoletedVersionForFallbackPlatform( - const ASTContext &Ctx, const llvm::VersionTuple &Version) { - const auto *Mapping = getFallbackVersionMapping( - Ctx, clang::DarwinSDKInfo::OSEnvPair( - llvm::Triple::IOS, llvm::Triple::UnknownEnvironment, - llvm::Triple::XROS, llvm::Triple::UnknownEnvironment)); - if (!Mapping) - return std::nullopt; - return Mapping->mapDeprecatedObsoletedAvailabilityVersion(Version); -} - -bool AvailabilityInference::updateIntroducedAvailabilityDomainForFallback( - const SemanticAvailableAttr &attr, const ASTContext &ctx, - AvailabilityDomain &domain, llvm::VersionTuple &platformVer) { - std::optional introducedVersion = attr.getIntroduced(); - if (!introducedVersion.has_value()) - return false; - - bool hasRemap = false; - auto remappedDomain = attr.getDomain().getRemappedDomain(ctx, hasRemap); - if (!hasRemap) - return false; - - auto potentiallyRemappedIntroducedVersion = - getRemappedIntroducedVersionForFallbackPlatform(ctx, *introducedVersion); - if (potentiallyRemappedIntroducedVersion.has_value()) { - domain = remappedDomain; - platformVer = potentiallyRemappedIntroducedVersion.value(); - return true; - } - return false; -} - -bool AvailabilityInference::updateDeprecatedAvailabilityDomainForFallback( - const SemanticAvailableAttr &attr, const ASTContext &ctx, - AvailabilityDomain &domain, llvm::VersionTuple &platformVer) { - std::optional deprecatedVersion = attr.getDeprecated(); - if (!deprecatedVersion.has_value()) - return false; - - bool hasRemap = false; - auto remappedDomain = attr.getDomain().getRemappedDomain(ctx, hasRemap); - if (!hasRemap) - return false; - - auto potentiallyRemappedDeprecatedVersion = - getRemappedDeprecatedObsoletedVersionForFallbackPlatform( - ctx, *deprecatedVersion); - if (potentiallyRemappedDeprecatedVersion.has_value()) { - domain = remappedDomain; - platformVer = potentiallyRemappedDeprecatedVersion.value(); - return true; - } - return false; -} - -bool AvailabilityInference::updateObsoletedAvailabilityDomainForFallback( - const SemanticAvailableAttr &attr, const ASTContext &ctx, - AvailabilityDomain &domain, llvm::VersionTuple &platformVer) { - std::optional obsoletedVersion = attr.getObsoleted(); - if (!obsoletedVersion.has_value()) - return false; - - bool hasRemap = false; - auto remappedDomain = attr.getDomain().getRemappedDomain(ctx, hasRemap); - if (!hasRemap) - return false; - - auto potentiallyRemappedObsoletedVersion = - getRemappedDeprecatedObsoletedVersionForFallbackPlatform( - ctx, *obsoletedVersion); - if (potentiallyRemappedObsoletedVersion.has_value()) { - domain = remappedDomain; - platformVer = potentiallyRemappedObsoletedVersion.value(); - return true; - } - return false; -} - -bool AvailabilityInference::updateBeforeAvailabilityDomainForFallback( - const BackDeployedAttr *attr, const ASTContext &ctx, - AvailabilityDomain &domain, llvm::VersionTuple &platformVer) { - bool hasRemap = false; - auto remappedDomain = - attr->getAvailabilityDomain().getRemappedDomain(ctx, hasRemap); - if (!hasRemap) - return false; - - auto beforeVersion = attr->getVersion(); - auto potentiallyRemappedIntroducedVersion = - getRemappedIntroducedVersionForFallbackPlatform(ctx, beforeVersion); - if (potentiallyRemappedIntroducedVersion.has_value()) { - domain = remappedDomain; - platformVer = potentiallyRemappedIntroducedVersion.value(); - return true; - } - return false; -} - static std::optional getDeclAvailableAttrForPlatformIntroduction(const Decl *D) { std::optional bestAvailAttr; @@ -917,40 +795,31 @@ std::optional SemanticAvailableAttr::getIntroduced() const { std::optional SemanticAvailableAttr::getIntroducedDomainAndRange( const ASTContext &Ctx) const { - auto *attr = getParsedAttr(); auto domain = getDomain(); - if (domain.isUniversal()) return std::nullopt; - if (!attr->getRawIntroduced().has_value()) { - // For versioned domains, an "introduced:" version is always required to - // indicate introduction. - if (domain.isVersioned()) - return std::nullopt; + if (auto introduced = getIntroduced()) + return domain.getRemappedDomainAndRange( + *introduced, AvailabilityVersionKind::Introduced, Ctx); - // For version-less domains, an attribute that does not indicate some other - // kind of unconditional availability constraint implicitly specifies that - // the decl is available in all versions of the domain. - switch (attr->getKind()) { - case AvailableAttr::Kind::Default: - return AvailabilityDomainAndRange(domain.getRemappedDomain(Ctx), - AvailabilityRange::alwaysAvailable()); - case AvailableAttr::Kind::Deprecated: - case AvailableAttr::Kind::Unavailable: - case AvailableAttr::Kind::NoAsync: - return std::nullopt; - } - } - - llvm::VersionTuple introducedVersion = getIntroduced().value(); - llvm::VersionTuple remappedVersion; - if (AvailabilityInference::updateIntroducedAvailabilityDomainForFallback( - *this, Ctx, domain, remappedVersion)) - introducedVersion = remappedVersion; + // For versioned domains, an "introduced:" version is always required to + // indicate introduction. + if (domain.isVersioned()) + return std::nullopt; - return AvailabilityDomainAndRange(domain, - AvailabilityRange{introducedVersion}); + // For version-less domains, an attribute that does not indicate some other + // kind of unconditional availability constraint implicitly specifies that + // the decl is available in all versions of the domain. + switch (attr->getKind()) { + case AvailableAttr::Kind::Default: + return AvailabilityDomainAndRange(domain.getRemappedDomain(Ctx), + AvailabilityRange::alwaysAvailable()); + case AvailableAttr::Kind::Deprecated: + case AvailableAttr::Kind::Unavailable: + case AvailableAttr::Kind::NoAsync: + return std::nullopt; + } } std::optional SemanticAvailableAttr::getDeprecated() const { @@ -962,28 +831,18 @@ std::optional SemanticAvailableAttr::getDeprecated() const { std::optional SemanticAvailableAttr::getDeprecatedDomainAndRange( const ASTContext &Ctx) const { - auto *attr = getParsedAttr(); - AvailabilityDomain domain = getDomain(); - - if (!attr->getRawDeprecated().has_value()) { - // Regardless of the whether the domain supports versions or not, an - // unconditional deprecation attribute indicates the decl is always - // deprecated. - if (isUnconditionallyDeprecated()) - return AvailabilityDomainAndRange(domain.getRemappedDomain(Ctx), - AvailabilityRange::alwaysAvailable()); - - return std::nullopt; - } + if (auto deprecated = getDeprecated()) + return getDomain().getRemappedDomainAndRange( + *deprecated, AvailabilityVersionKind::Deprecated, Ctx); - llvm::VersionTuple deprecatedVersion = getDeprecated().value(); - llvm::VersionTuple remappedVersion; - if (AvailabilityInference::updateDeprecatedAvailabilityDomainForFallback( - *this, Ctx, domain, remappedVersion)) - deprecatedVersion = remappedVersion; + // Regardless of the whether the domain supports versions or not, an + // unconditional deprecation attribute indicates the decl is always + // deprecated. + if (isUnconditionallyDeprecated()) + return AvailabilityDomainAndRange(getDomain().getRemappedDomain(Ctx), + AvailabilityRange::alwaysAvailable()); - return AvailabilityDomainAndRange(domain, - AvailabilityRange{deprecatedVersion}); + return std::nullopt; } std::optional SemanticAvailableAttr::getObsoleted() const { @@ -994,26 +853,16 @@ std::optional SemanticAvailableAttr::getObsoleted() const { std::optional SemanticAvailableAttr::getObsoletedDomainAndRange(const ASTContext &Ctx) const { - auto *attr = getParsedAttr(); - AvailabilityDomain domain = getDomain(); - - if (!attr->getRawObsoleted().has_value()) { - // An "unavailable" attribute effectively means obsolete in all versions. - if (attr->isUnconditionallyUnavailable()) - return AvailabilityDomainAndRange(domain.getRemappedDomain(Ctx), - AvailabilityRange::alwaysAvailable()); + if (auto obsoleted = getObsoleted()) + return getDomain().getRemappedDomainAndRange( + *obsoleted, AvailabilityVersionKind::Obsoleted, Ctx); - return std::nullopt; - } + // An "unavailable" attribute effectively means obsolete in all versions. + if (isUnconditionallyUnavailable()) + return AvailabilityDomainAndRange(getDomain().getRemappedDomain(Ctx), + AvailabilityRange::alwaysAvailable()); - llvm::VersionTuple obsoletedVersion = getObsoleted().value(); - llvm::VersionTuple remappedVersion; - if (AvailabilityInference::updateObsoletedAvailabilityDomainForFallback( - *this, Ctx, domain, remappedVersion)) - obsoletedVersion = remappedVersion; - - return AvailabilityDomainAndRange(domain, - AvailabilityRange{obsoletedVersion}); + return std::nullopt; } namespace { diff --git a/lib/AST/AvailabilityDomain.cpp b/lib/AST/AvailabilityDomain.cpp index 769a2ae9b4f0d..5ee3a7fcb4ea1 100644 --- a/lib/AST/AvailabilityDomain.cpp +++ b/lib/AST/AvailabilityDomain.cpp @@ -400,16 +400,73 @@ AvailabilityDomain AvailabilityDomain::getRootDomain() const { return *this; } -const AvailabilityDomain -AvailabilityDomain::getRemappedDomain(const ASTContext &ctx, - bool &didRemap) const { +std::optional +AvailabilityDomain::getRemappedDomainOrNull(const ASTContext &ctx) const { if (getPlatformKind() == PlatformKind::iOS && - isPlatformActive(PlatformKind::visionOS, ctx.LangOpts)) { - didRemap = true; + isPlatformActive(PlatformKind::visionOS, ctx.LangOpts)) return AvailabilityDomain::forPlatform(PlatformKind::visionOS); + + return std::nullopt; +} + +static const clang::DarwinSDKInfo::RelatedTargetVersionMapping * +getFallbackVersionMapping(const ASTContext &Ctx, + clang::DarwinSDKInfo::OSEnvPair Kind) { + auto *SDKInfo = Ctx.getDarwinSDKInfo(); + if (SDKInfo) + return SDKInfo->getVersionMapping(Kind); + + return Ctx.getAuxiliaryDarwinPlatformRemapInfo(Kind); +} + +static std::optional +getRemappedIntroducedVersionForFallbackPlatform( + const ASTContext &Ctx, const llvm::VersionTuple &Version) { + const auto *Mapping = getFallbackVersionMapping( + Ctx, clang::DarwinSDKInfo::OSEnvPair( + llvm::Triple::IOS, llvm::Triple::UnknownEnvironment, + llvm::Triple::XROS, llvm::Triple::UnknownEnvironment)); + if (!Mapping) + return std::nullopt; + return Mapping->mapIntroducedAvailabilityVersion(Version); +} + +static std::optional +getRemappedDeprecatedObsoletedVersionForFallbackPlatform( + const ASTContext &Ctx, const llvm::VersionTuple &Version) { + const auto *Mapping = getFallbackVersionMapping( + Ctx, clang::DarwinSDKInfo::OSEnvPair( + llvm::Triple::IOS, llvm::Triple::UnknownEnvironment, + llvm::Triple::XROS, llvm::Triple::UnknownEnvironment)); + if (!Mapping) + return std::nullopt; + return Mapping->mapDeprecatedObsoletedAvailabilityVersion(Version); +} + +AvailabilityDomainAndRange AvailabilityDomain::getRemappedDomainAndRange( + const llvm::VersionTuple &version, AvailabilityVersionKind versionKind, + const ASTContext &ctx) const { + auto remappedDomain = getRemappedDomainOrNull(ctx); + if (!remappedDomain) + return {*this, AvailabilityRange{version}}; + + std::optional remappedVersion; + switch (versionKind) { + case AvailabilityVersionKind::Introduced: + remappedVersion = + getRemappedIntroducedVersionForFallbackPlatform(ctx, version); + break; + case AvailabilityVersionKind::Deprecated: + case AvailabilityVersionKind::Obsoleted: + remappedVersion = + getRemappedDeprecatedObsoletedVersionForFallbackPlatform(ctx, version); + break; } - return *this; + if (!remappedVersion) + return {*this, AvailabilityRange{version}}; + + return {*remappedDomain, AvailabilityRange{*remappedVersion}}; } bool IsCustomAvailabilityDomainPermanentlyEnabled::evaluate( diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 74e1c601fc618..02d99e4152c25 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -600,17 +600,18 @@ std::optional> Decl::getBackDeployedAttrAndRange(bool forTargetVariant) const { auto &ctx = getASTContext(); if (auto *attr = getAttrs().getBackDeployed(ctx, forTargetVariant)) { - auto version = attr->getVersion(); - AvailabilityDomain ignoredDomain; - AvailabilityInference::updateBeforeAvailabilityDomainForFallback( - attr, ctx, ignoredDomain, version); - - // If the remap for fallback resulted in 1.0, then the - // backdeployment prior to that is not meaningful. - if (version == llvm::VersionTuple(1, 0, 0, 0)) + auto remappedDomainAndRange = + attr->getAvailabilityDomain().getRemappedDomainAndRange( + attr->getVersion(), AvailabilityVersionKind::Introduced, ctx); + + // If the before version was remapped to '1.0', then this decl has + // effectively always been available on the current platform and does not + // qualify as back deployed. + if (remappedDomainAndRange.getRange().getRawMinimumVersion() == + llvm::VersionTuple(1, 0, 0, 0)) return std::nullopt; - return std::make_pair(attr, AvailabilityRange(version)); + return std::make_pair(attr, remappedDomainAndRange.getRange()); } // Accessors may inherit `@backDeployed`. diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 97cee7cb0eab3..527ccffd74f1e 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -27,7 +27,6 @@ #include "swift/AST/ASTVisitor.h" #include "swift/AST/Attr.h" #include "swift/AST/AttrKind.h" -#include "swift/AST/AvailabilityInference.h" #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/ConformanceLookup.h" #include "swift/AST/Decl.h" @@ -5366,14 +5365,8 @@ void AttributeChecker::checkBackDeployedAttrs( // Unavailable decls cannot be back deployed. if (availability.containsUnavailableDomain(Domain)) { - auto domainForDiagnostics = Domain; - llvm::VersionTuple ignoredVersion; - - AvailabilityInference::updateBeforeAvailabilityDomainForFallback( - Attr, Ctx, domainForDiagnostics, ignoredVersion); - diagnose(AtLoc, diag::attr_has_no_effect_on_unavailable_decl, Attr, VD, - domainForDiagnostics); + Domain.getRemappedDomain(Ctx)); // Find the attribute that makes the declaration unavailable. const Decl *attrDecl = D; @@ -5396,23 +5389,22 @@ void AttributeChecker::checkBackDeployedAttrs( // fallback could never be executed at runtime. if (auto availableRangeAttrPair = getSemanticAvailableRangeDeclAndAttr(VD, Domain)) { - auto beforeDomain = Domain; - auto beforeVersion = Attr->getVersion(); - auto availableAttr = availableRangeAttrPair.value().first; - auto introVersion = availableAttr.getIntroduced().value(); - AvailabilityDomain introDomain = availableAttr.getDomain(); - - AvailabilityInference::updateBeforeAvailabilityDomainForFallback( - Attr, Ctx, beforeDomain, beforeVersion); - AvailabilityInference::updateIntroducedAvailabilityDomainForFallback( - availableAttr, Ctx, introDomain, introVersion); - - if (beforeVersion <= introVersion) { + auto availableAttr = availableRangeAttrPair->first; + auto introDomainAndRange = availableAttr.getIntroducedDomainAndRange(Ctx); + if (!introDomainAndRange) + continue; + + auto beforeDomainAndRange = Domain.getRemappedDomainAndRange( + Attr->getVersion(), AvailabilityVersionKind::Introduced, Ctx); + + auto introRange = introDomainAndRange->getRange(); + auto beforeRange = beforeDomainAndRange.getRange(); + if (introRange.isContainedIn(beforeRange)) { diagnose(AtLoc, diag::attr_has_no_effect_decl_not_available_before, - Attr, VD, beforeDomain, AvailabilityRange(beforeVersion)); + Attr, VD, beforeDomainAndRange.getDomain(), beforeRange); diagnose(availableAttr.getParsedAttr()->AtLoc, - diag::availability_introduced_in_version, VD, introDomain, - AvailabilityRange(introVersion)) + diag::availability_introduced_in_version, VD, + introDomainAndRange->getDomain(), introRange) .highlight(availableAttr.getParsedAttr()->getRange()); continue; }