mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Most of the compiler should use SemanticAvailableAttr instead. In contexts like ASTDumper where a semantic attribute is unavailable use accessors on AvailableAttr. NFC.
944 lines
33 KiB
C++
944 lines
33 KiB
C++
//===--- Availability.cpp - Swift Availability Structures -----------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines data structures for API availability.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/AST/ASTContext.h"
|
|
#include "swift/AST/Attr.h"
|
|
#include "swift/AST/AvailabilityConstraint.h"
|
|
#include "swift/AST/AvailabilityDomain.h"
|
|
#include "swift/AST/AvailabilityInference.h"
|
|
#include "swift/AST/AvailabilityRange.h"
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/AST/PlatformKind.h"
|
|
#include "swift/AST/TypeCheckRequests.h"
|
|
#include "swift/AST/TypeWalker.h"
|
|
#include "swift/AST/Types.h"
|
|
#include "swift/Basic/Assertions.h"
|
|
#include "swift/Basic/Platform.h"
|
|
#include "swift/ClangImporter/ClangModule.h"
|
|
#include <map>
|
|
|
|
using namespace swift;
|
|
|
|
void VersionRange::Profile(llvm::FoldingSetNodeID &id) const {
|
|
id.AddBoolean(hasLowerEndpoint());
|
|
if (!hasLowerEndpoint()) {
|
|
id.AddBoolean(isAll());
|
|
return;
|
|
}
|
|
|
|
auto profileVersionComponent = [&id](std::optional<unsigned> component) {
|
|
id.AddBoolean(component.has_value());
|
|
if (component)
|
|
id.AddInteger(*component);
|
|
};
|
|
|
|
auto lowerEndpoint = getLowerEndpoint();
|
|
id.AddInteger(lowerEndpoint.getMajor());
|
|
profileVersionComponent(lowerEndpoint.getMinor());
|
|
profileVersionComponent(lowerEndpoint.getSubminor());
|
|
profileVersionComponent(lowerEndpoint.getBuild());
|
|
}
|
|
|
|
AvailabilityRange
|
|
AvailabilityRange::forDeploymentTarget(const ASTContext &Ctx) {
|
|
return AvailabilityRange(
|
|
VersionRange::allGTE(Ctx.LangOpts.getMinPlatformVersion()));
|
|
}
|
|
|
|
AvailabilityRange AvailabilityRange::forInliningTarget(const ASTContext &Ctx) {
|
|
return AvailabilityRange(
|
|
VersionRange::allGTE(Ctx.LangOpts.MinimumInliningTargetVersion));
|
|
}
|
|
|
|
AvailabilityRange AvailabilityRange::forRuntimeTarget(const ASTContext &Ctx) {
|
|
return AvailabilityRange(VersionRange::allGTE(Ctx.LangOpts.RuntimeVersion));
|
|
}
|
|
|
|
PlatformKind AvailabilityConstraint::getPlatform() const {
|
|
return getAttr().getPlatform();
|
|
}
|
|
|
|
std::optional<AvailabilityRange>
|
|
AvailabilityConstraint::getRequiredNewerAvailabilityRange(
|
|
ASTContext &ctx) const {
|
|
switch (getKind()) {
|
|
case Kind::AlwaysUnavailable:
|
|
case Kind::RequiresVersion:
|
|
case Kind::Obsoleted:
|
|
return std::nullopt;
|
|
case Kind::IntroducedInNewerVersion:
|
|
return getAttr().getIntroducedRange(ctx);
|
|
}
|
|
}
|
|
|
|
bool AvailabilityConstraint::isConditionallySatisfiable() const {
|
|
switch (getKind()) {
|
|
case Kind::AlwaysUnavailable:
|
|
case Kind::RequiresVersion:
|
|
case Kind::Obsoleted:
|
|
return false;
|
|
case Kind::IntroducedInNewerVersion:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool AvailabilityConstraint::isActiveForRuntimeQueries(ASTContext &ctx) const {
|
|
if (getAttr().getPlatform() == PlatformKind::none)
|
|
return true;
|
|
|
|
return swift::isPlatformActive(getAttr().getPlatform(), ctx.LangOpts,
|
|
/*forTargetVariant=*/false,
|
|
/*forRuntimeQuery=*/true);
|
|
}
|
|
|
|
namespace {
|
|
|
|
/// The inferred availability required to access a group of declarations
|
|
/// on a single platform.
|
|
struct InferredAvailability {
|
|
AvailableAttr::Kind Kind = AvailableAttr::Kind::Default;
|
|
|
|
std::optional<llvm::VersionTuple> Introduced;
|
|
std::optional<llvm::VersionTuple> Deprecated;
|
|
std::optional<llvm::VersionTuple> Obsoleted;
|
|
|
|
StringRef Message;
|
|
StringRef Rename;
|
|
bool IsSPI = false;
|
|
};
|
|
|
|
/// The type of a function that merges two version tuples.
|
|
typedef const llvm::VersionTuple &(*MergeFunction)(
|
|
const llvm::VersionTuple &, const llvm::VersionTuple &);
|
|
|
|
} // end anonymous namespace
|
|
|
|
/// Apply a merge function to two optional versions, returning the result
|
|
/// in Inferred.
|
|
static bool
|
|
mergeIntoInferredVersion(const std::optional<llvm::VersionTuple> &Version,
|
|
std::optional<llvm::VersionTuple> &Inferred,
|
|
MergeFunction Merge) {
|
|
if (Version.has_value()) {
|
|
if (Inferred.has_value()) {
|
|
Inferred = Merge(Inferred.value(), Version.value());
|
|
return *Inferred == *Version;
|
|
} else {
|
|
Inferred = Version;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Merge an attribute's availability with an existing inferred availability
|
|
/// so that the new inferred availability is at least as available as
|
|
/// the attribute requires.
|
|
static void mergeWithInferredAvailability(SemanticAvailableAttr Attr,
|
|
InferredAvailability &Inferred) {
|
|
auto *ParsedAttr = Attr.getParsedAttr();
|
|
Inferred.Kind = static_cast<AvailableAttr::Kind>(
|
|
std::max(static_cast<unsigned>(Inferred.Kind),
|
|
static_cast<unsigned>(ParsedAttr->getKind())));
|
|
|
|
// The merge of two introduction versions is the maximum of the two versions.
|
|
if (mergeIntoInferredVersion(Attr.getIntroduced(), Inferred.Introduced,
|
|
std::max)) {
|
|
Inferred.IsSPI = Attr.isSPI();
|
|
}
|
|
|
|
// The merge of deprecated and obsoleted versions takes the minimum.
|
|
mergeIntoInferredVersion(Attr.getDeprecated(), Inferred.Deprecated, std::min);
|
|
mergeIntoInferredVersion(Attr.getObsoleted(), Inferred.Obsoleted, std::min);
|
|
|
|
if (Inferred.Message.empty() && !Attr.getMessage().empty())
|
|
Inferred.Message = Attr.getMessage();
|
|
|
|
if (Inferred.Rename.empty() && !Attr.getRename().empty())
|
|
Inferred.Rename = Attr.getRename();
|
|
}
|
|
|
|
/// Create an implicit availability attribute for the given domain
|
|
/// and with the inferred availability.
|
|
static AvailableAttr *createAvailableAttr(AvailabilityDomain Domain,
|
|
const InferredAvailability &Inferred,
|
|
ASTContext &Context) {
|
|
// If there is no information that would go into the availability attribute,
|
|
// don't create one.
|
|
if (Inferred.Kind == AvailableAttr::Kind::Default && !Inferred.Introduced &&
|
|
!Inferred.Deprecated && !Inferred.Obsoleted && Inferred.Message.empty() &&
|
|
Inferred.Rename.empty())
|
|
return nullptr;
|
|
|
|
llvm::VersionTuple Introduced =
|
|
Inferred.Introduced.value_or(llvm::VersionTuple());
|
|
llvm::VersionTuple Deprecated =
|
|
Inferred.Deprecated.value_or(llvm::VersionTuple());
|
|
llvm::VersionTuple Obsoleted =
|
|
Inferred.Obsoleted.value_or(llvm::VersionTuple());
|
|
|
|
return new (Context) AvailableAttr(
|
|
SourceLoc(), SourceRange(), Domain, Inferred.Kind, Inferred.Message,
|
|
Inferred.Rename, Introduced, SourceRange(), Deprecated, SourceRange(),
|
|
Obsoleted, SourceRange(), /*Implicit=*/true, Inferred.IsSPI);
|
|
}
|
|
|
|
void AvailabilityInference::applyInferredAvailableAttrs(
|
|
Decl *ToDecl, ArrayRef<const Decl *> InferredFromDecls) {
|
|
auto &Context = ToDecl->getASTContext();
|
|
|
|
// Iterate over the declarations and infer required availability on
|
|
// a per-platform basis.
|
|
std::map<AvailabilityDomain, InferredAvailability> Inferred;
|
|
for (const Decl *D : InferredFromDecls) {
|
|
llvm::SmallVector<SemanticAvailableAttr, 8> MergedAttrs;
|
|
|
|
do {
|
|
llvm::SmallVector<SemanticAvailableAttr, 8> PendingAttrs;
|
|
|
|
for (auto AvAttr : D->getSemanticAvailableAttrs()) {
|
|
// Skip an attribute from an outer declaration if it is for a platform
|
|
// that was already handled implicitly by an attribute from an inner
|
|
// declaration.
|
|
if (llvm::any_of(MergedAttrs,
|
|
[&AvAttr](SemanticAvailableAttr MergedAttr) {
|
|
return inheritsAvailabilityFromPlatform(
|
|
AvAttr.getPlatform(), MergedAttr.getPlatform());
|
|
}))
|
|
continue;
|
|
|
|
mergeWithInferredAvailability(AvAttr, Inferred[AvAttr.getDomain()]);
|
|
PendingAttrs.push_back(AvAttr);
|
|
}
|
|
|
|
MergedAttrs.append(PendingAttrs);
|
|
|
|
// Walk up the enclosing declaration hierarchy to make sure we aren't
|
|
// missing any inherited attributes.
|
|
D = AvailabilityInference::parentDeclForInferredAvailability(D);
|
|
} while (D);
|
|
}
|
|
|
|
DeclAttributes &Attrs = ToDecl->getAttrs();
|
|
|
|
// Create an availability attribute for each observed platform and add
|
|
// to ToDecl.
|
|
for (auto &Pair : Inferred) {
|
|
if (auto Attr = createAvailableAttr(Pair.first, Pair.second, Context))
|
|
Attrs.add(Attr);
|
|
}
|
|
}
|
|
|
|
/// Returns the decl that should be considered the parent decl of the given decl
|
|
/// when looking for inherited availability annotations.
|
|
const Decl *
|
|
AvailabilityInference::parentDeclForInferredAvailability(const Decl *D) {
|
|
if (auto *AD = dyn_cast<AccessorDecl>(D))
|
|
return AD->getStorage();
|
|
|
|
if (auto *ED = dyn_cast<ExtensionDecl>(D)) {
|
|
if (auto *NTD = ED->getExtendedNominal())
|
|
return NTD;
|
|
}
|
|
|
|
if (auto *PBD = dyn_cast<PatternBindingDecl>(D)) {
|
|
if (PBD->getNumPatternEntries() < 1)
|
|
return nullptr;
|
|
|
|
return PBD->getAnchoringVarDecl(0);
|
|
}
|
|
|
|
if (auto *OTD = dyn_cast<OpaqueTypeDecl>(D))
|
|
return OTD->getNamingDecl();
|
|
|
|
// Clang decls may be inaccurately parented rdar://53956555
|
|
if (D->hasClangNode())
|
|
return nullptr;
|
|
|
|
// Availability is inherited from the enclosing context.
|
|
return D->getDeclContext()->getInnermostDeclarationDeclContext();
|
|
}
|
|
|
|
/// Returns true if the introduced version in \p newAttr should be used instead
|
|
/// of the introduced version in \p prevAttr when both are attached to the same
|
|
/// declaration and refer to the active platform.
|
|
static bool isBetterThan(const SemanticAvailableAttr &newAttr,
|
|
const std::optional<SemanticAvailableAttr> &prevAttr) {
|
|
// If there is no prevAttr, newAttr of course wins.
|
|
if (!prevAttr)
|
|
return true;
|
|
|
|
// If they belong to the same platform, the one that introduces later wins.
|
|
if (prevAttr->getPlatform() == newAttr.getPlatform())
|
|
return prevAttr->getIntroduced().value() < newAttr.getIntroduced().value();
|
|
|
|
// If the new attribute's platform inherits from the old one, it wins.
|
|
return inheritsAvailabilityFromPlatform(newAttr.getPlatform(),
|
|
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::updateIntroducedPlatformForFallback(
|
|
const SemanticAvailableAttr &attr, const ASTContext &Ctx,
|
|
llvm::StringRef &Platform, llvm::VersionTuple &PlatformVer) {
|
|
std::optional<llvm::VersionTuple> IntroducedVersion = attr.getIntroduced();
|
|
if (attr.getPlatform() == PlatformKind::iOS &&
|
|
IntroducedVersion.has_value() &&
|
|
isPlatformActive(PlatformKind::visionOS, Ctx.LangOpts)) {
|
|
// We re-map the iOS introduced version to the corresponding visionOS version
|
|
auto PotentiallyRemappedIntroducedVersion =
|
|
getRemappedIntroducedVersionForFallbackPlatform(Ctx,
|
|
*IntroducedVersion);
|
|
if (PotentiallyRemappedIntroducedVersion.has_value()) {
|
|
Platform = swift::prettyPlatformString(PlatformKind::visionOS);
|
|
PlatformVer = PotentiallyRemappedIntroducedVersion.value();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AvailabilityInference::updateDeprecatedPlatformForFallback(
|
|
const SemanticAvailableAttr &attr, const ASTContext &Ctx,
|
|
llvm::StringRef &Platform, llvm::VersionTuple &PlatformVer) {
|
|
std::optional<llvm::VersionTuple> DeprecatedVersion = attr.getDeprecated();
|
|
if (attr.getPlatform() == PlatformKind::iOS &&
|
|
DeprecatedVersion.has_value() &&
|
|
isPlatformActive(PlatformKind::visionOS, Ctx.LangOpts)) {
|
|
// We re-map the iOS deprecated version to the corresponding visionOS version
|
|
auto PotentiallyRemappedDeprecatedVersion =
|
|
getRemappedDeprecatedObsoletedVersionForFallbackPlatform(
|
|
Ctx, *DeprecatedVersion);
|
|
if (PotentiallyRemappedDeprecatedVersion.has_value()) {
|
|
Platform = swift::prettyPlatformString(PlatformKind::visionOS);
|
|
PlatformVer = PotentiallyRemappedDeprecatedVersion.value();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AvailabilityInference::updateObsoletedPlatformForFallback(
|
|
const SemanticAvailableAttr &attr, const ASTContext &Ctx,
|
|
llvm::StringRef &Platform, llvm::VersionTuple &PlatformVer) {
|
|
std::optional<llvm::VersionTuple> ObsoletedVersion = attr.getObsoleted();
|
|
if (attr.getPlatform() == PlatformKind::iOS && ObsoletedVersion.has_value() &&
|
|
isPlatformActive(PlatformKind::visionOS, Ctx.LangOpts)) {
|
|
// We re-map the iOS obsoleted version to the corresponding visionOS version
|
|
auto PotentiallyRemappedObsoletedVersion =
|
|
getRemappedDeprecatedObsoletedVersionForFallbackPlatform(
|
|
Ctx, *ObsoletedVersion);
|
|
if (PotentiallyRemappedObsoletedVersion.has_value()) {
|
|
Platform = swift::prettyPlatformString(PlatformKind::visionOS);
|
|
PlatformVer = PotentiallyRemappedObsoletedVersion.value();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void AvailabilityInference::updatePlatformStringForFallback(
|
|
const SemanticAvailableAttr &attr, const ASTContext &Ctx,
|
|
llvm::StringRef &Platform) {
|
|
if (attr.getPlatform() == PlatformKind::iOS &&
|
|
isPlatformActive(PlatformKind::visionOS, Ctx.LangOpts)) {
|
|
Platform = swift::prettyPlatformString(PlatformKind::visionOS);
|
|
}
|
|
}
|
|
|
|
bool AvailabilityInference::updateBeforePlatformForFallback(
|
|
const BackDeployedAttr *attr, const ASTContext &Ctx,
|
|
llvm::StringRef &Platform, llvm::VersionTuple &PlatformVer) {
|
|
auto BeforeVersion = attr->Version;
|
|
if (attr->Platform == PlatformKind::iOS &&
|
|
isPlatformActive(PlatformKind::visionOS, Ctx.LangOpts)) {
|
|
// We re-map the iOS before version to the corresponding visionOS version
|
|
auto PotentiallyRemappedIntroducedVersion =
|
|
getRemappedIntroducedVersionForFallbackPlatform(Ctx, BeforeVersion);
|
|
if (PotentiallyRemappedIntroducedVersion.has_value()) {
|
|
Platform = swift::prettyPlatformString(PlatformKind::visionOS);
|
|
PlatformVer = PotentiallyRemappedIntroducedVersion.value();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static std::optional<SemanticAvailableAttr>
|
|
getDeclAvailableAttrForPlatformIntroduction(const Decl *D) {
|
|
std::optional<SemanticAvailableAttr> bestAvailAttr;
|
|
|
|
D = abstractSyntaxDeclForAvailableAttribute(D);
|
|
|
|
for (auto attr : D->getSemanticAvailableAttrs(/*includingInactive=*/false)) {
|
|
if (!attr.isPlatformSpecific() || !attr.getIntroduced())
|
|
continue;
|
|
|
|
if (isBetterThan(attr, bestAvailAttr))
|
|
bestAvailAttr.emplace(attr);
|
|
}
|
|
|
|
return bestAvailAttr;
|
|
}
|
|
|
|
std::optional<AvailabilityRange>
|
|
AvailabilityInference::annotatedAvailableRange(const Decl *D) {
|
|
auto bestAvailAttr = D->getAvailableAttrForPlatformIntroduction();
|
|
if (!bestAvailAttr)
|
|
return std::nullopt;
|
|
|
|
return bestAvailAttr->getIntroducedRange(D->getASTContext());
|
|
}
|
|
|
|
bool Decl::isAvailableAsSPI() const {
|
|
return AvailabilityInference::isAvailableAsSPI(this);
|
|
}
|
|
|
|
SemanticAvailableAttributes
|
|
Decl::getSemanticAvailableAttrs(bool includeInactive) const {
|
|
return SemanticAvailableAttributes(getAttrs(), this, includeInactive);
|
|
}
|
|
|
|
std::optional<SemanticAvailableAttr>
|
|
Decl::getSemanticAvailableAttr(const AvailableAttr *attr) const {
|
|
return SemanticAvailableAttr(attr);
|
|
}
|
|
|
|
std::optional<SemanticAvailableAttr>
|
|
Decl::getActiveAvailableAttrForCurrentPlatform(bool ignoreAppExtensions) const {
|
|
std::optional<SemanticAvailableAttr> bestAttr;
|
|
|
|
for (auto attr : getSemanticAvailableAttrs(/*includingInactive=*/false)) {
|
|
if (!attr.isPlatformSpecific())
|
|
continue;
|
|
|
|
if (ignoreAppExtensions &&
|
|
isApplicationExtensionPlatform(attr.getPlatform()))
|
|
continue;
|
|
|
|
// We have an attribute that is active for the platform, but is it more
|
|
// specific than our current best?
|
|
if (!bestAttr || inheritsAvailabilityFromPlatform(
|
|
attr.getPlatform(), bestAttr->getPlatform())) {
|
|
bestAttr.emplace(attr);
|
|
}
|
|
}
|
|
|
|
return bestAttr;
|
|
}
|
|
|
|
std::optional<SemanticAvailableAttr> Decl::getDeprecatedAttr() const {
|
|
auto &ctx = getASTContext();
|
|
std::optional<SemanticAvailableAttr> result;
|
|
auto bestActive = getActiveAvailableAttrForCurrentPlatform();
|
|
|
|
for (auto attr : getSemanticAvailableAttrs(/*includingInactive=*/false)) {
|
|
if (attr.isPlatformSpecific() && (!bestActive || attr != bestActive))
|
|
continue;
|
|
|
|
// Unconditional deprecated.
|
|
if (attr.isUnconditionallyDeprecated())
|
|
return attr;
|
|
|
|
auto deprecatedVersion = attr.getDeprecated();
|
|
|
|
StringRef deprecatedPlatform;
|
|
llvm::VersionTuple remappedDeprecatedVersion;
|
|
if (AvailabilityInference::updateDeprecatedPlatformForFallback(
|
|
attr, ctx, deprecatedPlatform, remappedDeprecatedVersion))
|
|
deprecatedVersion = remappedDeprecatedVersion;
|
|
|
|
if (!deprecatedVersion.has_value())
|
|
continue;
|
|
|
|
llvm::VersionTuple minVersion = attr.getActiveVersion(ctx);
|
|
|
|
// We treat the declaration as deprecated if it is deprecated on
|
|
// all deployment targets.
|
|
if (deprecatedVersion.value() <= minVersion) {
|
|
result.emplace(attr);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::optional<SemanticAvailableAttr> Decl::getSoftDeprecatedAttr() const {
|
|
auto &ctx = getASTContext();
|
|
std::optional<SemanticAvailableAttr> result;
|
|
auto bestActive = getActiveAvailableAttrForCurrentPlatform();
|
|
|
|
for (auto attr : getSemanticAvailableAttrs(/*includingInactive=*/false)) {
|
|
if (attr.isPlatformSpecific() && (!bestActive || attr != bestActive))
|
|
continue;
|
|
|
|
// FIXME: This needs to do a version remap.
|
|
auto deprecatedVersion = attr.getDeprecated();
|
|
if (!deprecatedVersion.has_value())
|
|
continue;
|
|
|
|
llvm::VersionTuple activeVersion = attr.getActiveVersion(ctx);
|
|
|
|
if (deprecatedVersion.value() > activeVersion)
|
|
result.emplace(attr);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::optional<SemanticAvailableAttr> Decl::getNoAsyncAttr() const {
|
|
std::optional<SemanticAvailableAttr> bestAttr;
|
|
|
|
for (auto attr : getSemanticAvailableAttrs(/*includingInactive=*/false)) {
|
|
if (!attr.isNoAsync())
|
|
continue;
|
|
|
|
if (!bestAttr) {
|
|
// If there is no best attr selected and the attr either has an active
|
|
// platform, or doesn't have one at all, select it.
|
|
bestAttr.emplace(attr);
|
|
} else if (bestAttr && attr.isPlatformSpecific() &&
|
|
bestAttr->isPlatformSpecific() &&
|
|
inheritsAvailabilityFromPlatform(attr.getPlatform(),
|
|
bestAttr->getPlatform())) {
|
|
// if they both have a viable platform, use the better one
|
|
bestAttr.emplace(attr);
|
|
} else if (attr.isPlatformSpecific() && !bestAttr->isPlatformSpecific()) {
|
|
// Use the one more specific
|
|
bestAttr.emplace(attr);
|
|
}
|
|
}
|
|
return bestAttr;
|
|
}
|
|
|
|
bool Decl::isUnavailableInCurrentSwiftVersion() const {
|
|
llvm::VersionTuple vers = getASTContext().LangOpts.EffectiveLanguageVersion;
|
|
for (auto attr : getSemanticAvailableAttrs(/*includingInactive=*/false)) {
|
|
if (attr.isSwiftLanguageModeSpecific()) {
|
|
auto introduced = attr.getIntroduced();
|
|
if (introduced && *introduced > vers)
|
|
return true;
|
|
|
|
auto obsoleted = attr.getObsoleted();
|
|
if (obsoleted && *obsoleted <= vers)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
std::optional<SemanticAvailableAttr>
|
|
getDeclUnavailableAttr(const Decl *D, bool ignoreAppExtensions) {
|
|
auto &ctx = D->getASTContext();
|
|
std::optional<SemanticAvailableAttr> result;
|
|
auto bestActive =
|
|
D->getActiveAvailableAttrForCurrentPlatform(ignoreAppExtensions);
|
|
|
|
for (auto attr : D->getSemanticAvailableAttrs(/*includingInactive=*/false)) {
|
|
// If this is a platform-specific attribute and it isn't the most
|
|
// specific attribute for the current platform, we're done.
|
|
if (attr.isPlatformSpecific() && (!bestActive || attr != bestActive))
|
|
continue;
|
|
|
|
if (ignoreAppExtensions &&
|
|
isApplicationExtensionPlatform(attr.getPlatform()))
|
|
continue;
|
|
|
|
// Unconditional unavailable.
|
|
if (attr.isUnconditionallyUnavailable())
|
|
return attr;
|
|
|
|
switch (attr.getVersionAvailability(ctx)) {
|
|
case AvailableVersionComparison::Available:
|
|
case AvailableVersionComparison::PotentiallyUnavailable:
|
|
break;
|
|
|
|
case AvailableVersionComparison::Obsoleted:
|
|
case AvailableVersionComparison::Unavailable:
|
|
result.emplace(attr);
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::optional<SemanticAvailableAttr>
|
|
Decl::getUnavailableAttr(bool ignoreAppExtensions) const {
|
|
if (auto attr = getDeclUnavailableAttr(this, ignoreAppExtensions))
|
|
return attr;
|
|
|
|
// If D is an extension member, check if the extension is unavailable.
|
|
//
|
|
// Skip decls imported from Clang, they could be associated to the wrong
|
|
// extension and inherit undesired unavailability. The ClangImporter
|
|
// associates Objective-C protocol members to the first category where the
|
|
// protocol is directly or indirectly adopted, no matter its availability
|
|
// and the availability of other categories. rdar://problem/53956555
|
|
if (!getClangNode())
|
|
if (auto ext = dyn_cast<ExtensionDecl>(getDeclContext()))
|
|
return ext->getUnavailableAttr(ignoreAppExtensions);
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
static bool isDeclCompletelyUnavailable(const Decl *decl) {
|
|
// Don't trust unavailability on declarations from clang modules.
|
|
if (isa<ClangModuleUnit>(decl->getDeclContext()->getModuleScopeContext()))
|
|
return false;
|
|
|
|
auto unavailableAttr = decl->getUnavailableAttr(/*ignoreAppExtensions=*/true);
|
|
if (!unavailableAttr)
|
|
return false;
|
|
|
|
// getUnavailableAttr() can return an @available attribute that is
|
|
// obsoleted for certain deployment targets or language modes. These decls
|
|
// can still be reached by code in other modules that is compiled with
|
|
// a different deployment target or language mode.
|
|
if (!unavailableAttr->isUnconditionallyUnavailable())
|
|
return false;
|
|
|
|
// Universally unavailable declarations are always completely unavailable.
|
|
if (unavailableAttr->getPlatform() == PlatformKind::none)
|
|
return true;
|
|
|
|
// FIXME: Support zippered frameworks (rdar://125371621)
|
|
// If we have a target variant (e.g. we're building a zippered macOS
|
|
// framework) then the decl is only unreachable if it is unavailable for both
|
|
// the primary target and the target variant.
|
|
if (decl->getASTContext().LangOpts.TargetVariant.has_value())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
SemanticDeclAvailability
|
|
SemanticDeclAvailabilityRequest::evaluate(Evaluator &evaluator,
|
|
const Decl *decl) const {
|
|
auto inherited = SemanticDeclAvailability::PotentiallyAvailable;
|
|
if (auto *parent =
|
|
AvailabilityInference::parentDeclForInferredAvailability(decl)) {
|
|
inherited = evaluateOrDefault(
|
|
evaluator, SemanticDeclAvailabilityRequest{parent}, inherited);
|
|
}
|
|
|
|
if (inherited == SemanticDeclAvailability::CompletelyUnavailable ||
|
|
isDeclCompletelyUnavailable(decl))
|
|
return SemanticDeclAvailability::CompletelyUnavailable;
|
|
|
|
if (inherited == SemanticDeclAvailability::ConditionallyUnavailable ||
|
|
decl->isUnavailable())
|
|
return SemanticDeclAvailability::ConditionallyUnavailable;
|
|
|
|
return SemanticDeclAvailability::PotentiallyAvailable;
|
|
}
|
|
|
|
bool Decl::isSemanticallyUnavailable() const {
|
|
auto availability = evaluateOrDefault(
|
|
getASTContext().evaluator, SemanticDeclAvailabilityRequest{this},
|
|
SemanticDeclAvailability::PotentiallyAvailable);
|
|
return availability != SemanticDeclAvailability::PotentiallyAvailable;
|
|
}
|
|
|
|
bool Decl::isUnreachableAtRuntime() const {
|
|
auto availability = evaluateOrDefault(
|
|
getASTContext().evaluator, SemanticDeclAvailabilityRequest{this},
|
|
SemanticDeclAvailability::PotentiallyAvailable);
|
|
return availability == SemanticDeclAvailability::CompletelyUnavailable;
|
|
}
|
|
|
|
static UnavailableDeclOptimization
|
|
getEffectiveUnavailableDeclOptimization(ASTContext &ctx) {
|
|
if (ctx.LangOpts.UnavailableDeclOptimizationMode.has_value())
|
|
return *ctx.LangOpts.UnavailableDeclOptimizationMode;
|
|
|
|
// FIXME: Allow unavailable decl optimization on visionOS.
|
|
// visionOS must be ABI compatible with iOS. Enabling unavailable declaration
|
|
// optimizations naively would break compatibility since declarations marked
|
|
// unavailable on visionOS would be optimized regardless of whether they are
|
|
// available on iOS. rdar://116742214
|
|
if (ctx.LangOpts.Target.isXROS())
|
|
return UnavailableDeclOptimization::None;
|
|
|
|
return UnavailableDeclOptimization::None;
|
|
}
|
|
|
|
bool Decl::isAvailableDuringLowering() const {
|
|
// Unconditionally unavailable declarations should be skipped during lowering
|
|
// when -unavailable-decl-optimization=complete is specified.
|
|
if (getEffectiveUnavailableDeclOptimization(getASTContext()) !=
|
|
UnavailableDeclOptimization::Complete)
|
|
return true;
|
|
|
|
return !isUnreachableAtRuntime();
|
|
}
|
|
|
|
bool Decl::requiresUnavailableDeclABICompatibilityStubs() const {
|
|
// Code associated with unavailable declarations should trap at runtime if
|
|
// -unavailable-decl-optimization=stub is specified.
|
|
if (getEffectiveUnavailableDeclOptimization(getASTContext()) !=
|
|
UnavailableDeclOptimization::Stub)
|
|
return false;
|
|
|
|
return isUnreachableAtRuntime();
|
|
}
|
|
|
|
AvailabilityRange AvailabilityInference::annotatedAvailableRangeForAttr(
|
|
const Decl *D, const SpecializeAttr *attr, ASTContext &ctx) {
|
|
std::optional<SemanticAvailableAttr> bestAvailAttr;
|
|
|
|
for (auto *availAttr : attr->getAvailableAttrs()) {
|
|
auto semanticAttr = D->getSemanticAvailableAttr(availAttr);
|
|
if (!semanticAttr)
|
|
continue;
|
|
|
|
if (!semanticAttr->getIntroduced() || !semanticAttr->isActive(ctx) ||
|
|
!semanticAttr->isPlatformSpecific()) {
|
|
continue;
|
|
}
|
|
|
|
if (isBetterThan(*semanticAttr, bestAvailAttr))
|
|
bestAvailAttr.emplace(*semanticAttr);
|
|
}
|
|
|
|
if (bestAvailAttr)
|
|
return bestAvailAttr->getIntroducedRange(ctx);
|
|
|
|
return AvailabilityRange::alwaysAvailable();
|
|
}
|
|
|
|
std::optional<SemanticAvailableAttr>
|
|
Decl::getAvailableAttrForPlatformIntroduction() const {
|
|
if (auto attr = getDeclAvailableAttrForPlatformIntroduction(this))
|
|
return attr;
|
|
|
|
// Unlike other declarations, extensions can be used without referring to them
|
|
// by name (they don't have one) in the source. For this reason, when checking
|
|
// the available range of a declaration we also need to check to see if it is
|
|
// immediately contained in an extension and use the extension's availability
|
|
// if the declaration does not have an explicit @available attribute
|
|
// itself. This check relies on the fact that we cannot have nested
|
|
// extensions.
|
|
|
|
DeclContext *DC = getDeclContext();
|
|
if (auto *ED = dyn_cast<ExtensionDecl>(DC)) {
|
|
if (auto attr = getDeclAvailableAttrForPlatformIntroduction(ED))
|
|
return attr;
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
AvailabilityRange AvailabilityInference::availableRange(const Decl *D) {
|
|
if (auto attr = D->getAvailableAttrForPlatformIntroduction())
|
|
return attr->getIntroducedRange(D->getASTContext());
|
|
|
|
return AvailabilityRange::alwaysAvailable();
|
|
}
|
|
|
|
bool AvailabilityInference::isAvailableAsSPI(const Decl *D) {
|
|
if (auto attr = D->getAvailableAttrForPlatformIntroduction())
|
|
return attr->isSPI();
|
|
|
|
return false;
|
|
}
|
|
|
|
AvailabilityRange
|
|
SemanticAvailableAttr::getIntroducedRange(const ASTContext &Ctx) const {
|
|
assert(getDomain().isActive(Ctx));
|
|
|
|
auto *attr = getParsedAttr();
|
|
if (!attr->Introduced.has_value())
|
|
return AvailabilityRange::alwaysAvailable();
|
|
|
|
llvm::VersionTuple IntroducedVersion = attr->Introduced.value();
|
|
StringRef Platform;
|
|
llvm::VersionTuple RemappedIntroducedVersion;
|
|
if (AvailabilityInference::updateIntroducedPlatformForFallback(
|
|
*this, Ctx, Platform, RemappedIntroducedVersion))
|
|
IntroducedVersion = RemappedIntroducedVersion;
|
|
|
|
return AvailabilityRange{VersionRange::allGTE(IntroducedVersion)};
|
|
}
|
|
|
|
namespace {
|
|
/// Infers the availability required to access a type.
|
|
class AvailabilityInferenceTypeWalker : public TypeWalker {
|
|
public:
|
|
AvailabilityRange AvailabilityInfo = AvailabilityRange::alwaysAvailable();
|
|
|
|
Action walkToTypePre(Type ty) override {
|
|
if (auto *nominalDecl = ty->getAnyNominal()) {
|
|
AvailabilityInfo.intersectWith(
|
|
AvailabilityInference::availableRange(nominalDecl));
|
|
}
|
|
|
|
return Action::Continue;
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
AvailabilityRange AvailabilityInference::inferForType(Type t) {
|
|
AvailabilityInferenceTypeWalker walker;
|
|
t.walk(walker);
|
|
return walker.AvailabilityInfo;
|
|
}
|
|
|
|
AvailabilityRange ASTContext::getSwiftFutureAvailability() const {
|
|
auto target = LangOpts.Target;
|
|
|
|
auto getFutureAvailabilityRange = []() -> AvailabilityRange {
|
|
return AvailabilityRange(
|
|
VersionRange::allGTE(llvm::VersionTuple(99, 99, 0)));
|
|
};
|
|
|
|
if (target.isMacOSX()) {
|
|
return getFutureAvailabilityRange();
|
|
} else if (target.isiOS()) {
|
|
return getFutureAvailabilityRange();
|
|
} else if (target.isWatchOS()) {
|
|
return getFutureAvailabilityRange();
|
|
} else if (target.isXROS()) {
|
|
return getFutureAvailabilityRange();
|
|
} else {
|
|
return AvailabilityRange::alwaysAvailable();
|
|
}
|
|
}
|
|
|
|
AvailabilityRange ASTContext::getSwiftAvailability(unsigned major,
|
|
unsigned minor) const {
|
|
auto target = LangOpts.Target;
|
|
|
|
// Deal with special cases for Swift 5.3 and lower
|
|
if (major == 5 && minor <= 3) {
|
|
if (target.getArchName() == "arm64e")
|
|
return AvailabilityRange::alwaysAvailable();
|
|
if (target.isMacOSX() && target.isAArch64())
|
|
return AvailabilityRange::alwaysAvailable();
|
|
if (target.isiOS() && target.isAArch64()
|
|
&& (target.isSimulatorEnvironment()
|
|
|| target.isMacCatalystEnvironment()))
|
|
return AvailabilityRange::alwaysAvailable();
|
|
if (target.isWatchOS() && target.isArch64Bit())
|
|
return AvailabilityRange::alwaysAvailable();
|
|
}
|
|
|
|
switch (major) {
|
|
#define MAJOR_VERSION(V) case V: switch (minor) {
|
|
#define END_MAJOR_VERSION(V) } break;
|
|
#define PLATFORM(P, V) \
|
|
if (IS_PLATFORM(P)) \
|
|
return AvailabilityRange(VersionRange::allGTE(llvm::VersionTuple V));
|
|
#define IS_PLATFORM(P) PLATFORM_TEST_##P
|
|
#define FUTURE return getSwiftFutureAvailability();
|
|
#define PLATFORM_TEST_macOS target.isMacOSX()
|
|
#define PLATFORM_TEST_iOS target.isiOS()
|
|
#define PLATFORM_TEST_watchOS target.isWatchOS()
|
|
#define PLATFORM_TEST_xrOS target.isXROS()
|
|
|
|
#define _SECOND(A, B) B
|
|
#define SECOND(T) _SECOND T
|
|
|
|
#define RUNTIME_VERSION(V, PLATFORMS) \
|
|
case SECOND(V): \
|
|
PLATFORMS \
|
|
return AvailabilityRange::alwaysAvailable();
|
|
|
|
#include "swift/AST/RuntimeVersions.def"
|
|
|
|
#undef PLATFORM_TEST_macOS
|
|
#undef PLATFORM_TEST_iOS
|
|
#undef PLATFORM_TEST_watchOS
|
|
#undef PLATFORM_TEST_xrOS
|
|
#undef _SECOND
|
|
#undef SECOND
|
|
|
|
case 99:
|
|
if (minor == 99)
|
|
return getSwiftFutureAvailability();
|
|
break;
|
|
}
|
|
|
|
llvm::report_fatal_error(
|
|
Twine("Missing runtime version data for Swift ") +
|
|
Twine(major) + Twine('.') + Twine(minor));
|
|
}
|
|
|
|
bool ASTContext::supportsVersionedAvailability() const {
|
|
return minimumAvailableOSVersionForTriple(LangOpts.Target).has_value();
|
|
}
|
|
|
|
// FIXME: Rename abstractSyntaxDeclForAvailableAttribute since it's useful
|
|
// for more attributes than `@available`.
|
|
const Decl *
|
|
swift::abstractSyntaxDeclForAvailableAttribute(const Decl *ConcreteSyntaxDecl) {
|
|
// This function needs to be kept in sync with its counterpart,
|
|
// concreteSyntaxDeclForAvailableAttribute().
|
|
|
|
if (auto *PBD = dyn_cast<PatternBindingDecl>(ConcreteSyntaxDecl)) {
|
|
// Existing @available attributes in the AST are attached to VarDecls
|
|
// rather than PatternBindingDecls, so we return the first VarDecl for
|
|
// the pattern binding declaration.
|
|
// This is safe, even though there may be multiple VarDecls, because
|
|
// all parsed attribute that appear in the concrete syntax upon on the
|
|
// PatternBindingDecl are added to all of the VarDecls for the pattern
|
|
// binding.
|
|
for (auto index : range(PBD->getNumPatternEntries())) {
|
|
if (auto VD = PBD->getAnchoringVarDecl(index))
|
|
return VD;
|
|
}
|
|
} else if (auto *ECD = dyn_cast<EnumCaseDecl>(ConcreteSyntaxDecl)) {
|
|
// Similar to the PatternBindingDecl case above, we return the
|
|
// first EnumElementDecl.
|
|
if (auto *Elem = ECD->getFirstElement()) {
|
|
return Elem;
|
|
}
|
|
}
|
|
|
|
return ConcreteSyntaxDecl;
|
|
}
|