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
33 changes: 24 additions & 9 deletions include/swift/AST/AvailabilityDomain.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -131,6 +140,9 @@ class AvailabilityDomain final {
: std::nullopt;
}

std::optional<AvailabilityDomain>
getRemappedDomainOrNull(const ASTContext &ctx) const;

public:
AvailabilityDomain() {}

Expand Down Expand Up @@ -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
Expand Down
34 changes: 2 additions & 32 deletions include/swift/AST/AvailabilityInference.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
namespace swift {
class ASTContext;
class AvailabilityDomain;
class BackDeployedAttr;
class Decl;
class SemanticAvailableAttr;

Expand All @@ -49,37 +48,8 @@ class AvailabilityInference {
static std::optional<AvailabilityRange>
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
Expand Down
3 changes: 1 addition & 2 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1134,8 +1134,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl>, public Swi
/// Returns the active `@backDeployed` attribute and the `AvailabilityRange`
/// in which the decl is available as ABI.
std::optional<std::pair<const BackDeployedAttr *, AvailabilityRange>>
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;
Expand Down
1 change: 0 additions & 1 deletion lib/AST/Attr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
225 changes: 37 additions & 188 deletions lib/AST/Availability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<clang::VersionTuple>
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<clang::VersionTuple>
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<llvm::VersionTuple> 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<llvm::VersionTuple> 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<llvm::VersionTuple> 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<SemanticAvailableAttr>
getDeclAvailableAttrForPlatformIntroduction(const Decl *D) {
std::optional<SemanticAvailableAttr> bestAvailAttr;
Expand Down Expand Up @@ -917,40 +795,31 @@ std::optional<llvm::VersionTuple> SemanticAvailableAttr::getIntroduced() const {
std::optional<AvailabilityDomainAndRange>
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<llvm::VersionTuple> SemanticAvailableAttr::getDeprecated() const {
Expand All @@ -962,28 +831,18 @@ std::optional<llvm::VersionTuple> SemanticAvailableAttr::getDeprecated() const {
std::optional<AvailabilityDomainAndRange>
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<llvm::VersionTuple> SemanticAvailableAttr::getObsoleted() const {
Expand All @@ -994,26 +853,16 @@ std::optional<llvm::VersionTuple> SemanticAvailableAttr::getObsoleted() const {

std::optional<AvailabilityDomainAndRange>
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 {
Expand Down
Loading