blob: 1fee0851ffcf6668315e4cb321ab52f4505d14d8 [file] [log] [blame]
//===--- 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/Decl.h"
#include "swift/AST/Types.h"
#include "swift/AST/Availability.h"
#include "swift/AST/PlatformKind.h"
#include "swift/AST/TypeWalker.h"
#include <map>
using namespace swift;
AvailabilityContext AvailabilityContext::forDeploymentTarget(ASTContext &Ctx) {
return AvailabilityContext(
VersionRange::allGTE(Ctx.LangOpts.getMinPlatformVersion()));
}
namespace {
/// The inferred availability required to access a group of declarations
/// on a single platform.
struct InferredAvailability {
PlatformAgnosticAvailabilityKind PlatformAgnostic
= PlatformAgnosticAvailabilityKind::None;
Optional<llvm::VersionTuple> Introduced;
Optional<llvm::VersionTuple> Deprecated;
Optional<llvm::VersionTuple> Obsoleted;
};
/// 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 void
mergeIntoInferredVersion(const Optional<llvm::VersionTuple> &Version,
Optional<llvm::VersionTuple> &Inferred,
MergeFunction Merge) {
if (Version.hasValue()) {
if (Inferred.hasValue()) {
Inferred = Merge(Inferred.getValue(), Version.getValue());
} else {
Inferred = Version;
}
}
}
/// 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(const AvailableAttr *Attr,
InferredAvailability &Inferred) {
Inferred.PlatformAgnostic
= static_cast<PlatformAgnosticAvailabilityKind>(
std::max(static_cast<unsigned>(Inferred.PlatformAgnostic),
static_cast<unsigned>(Attr->getPlatformAgnosticAvailability())));
// The merge of two introduction versions is the maximum of the two versions.
mergeIntoInferredVersion(Attr->Introduced, Inferred.Introduced, std::max);
// The merge of deprecated and obsoleted versions takes the minimum.
mergeIntoInferredVersion(Attr->Deprecated, Inferred.Deprecated, std::min);
mergeIntoInferredVersion(Attr->Obsoleted, Inferred.Obsoleted, std::min);
}
/// Create an implicit availability attribute for the given platform
/// and with the inferred availability.
static AvailableAttr *
createAvailableAttr(PlatformKind Platform,
const InferredAvailability &Inferred,
ASTContext &Context) {
llvm::VersionTuple Introduced =
Inferred.Introduced.getValueOr(llvm::VersionTuple());
llvm::VersionTuple Deprecated =
Inferred.Deprecated.getValueOr(llvm::VersionTuple());
llvm::VersionTuple Obsoleted =
Inferred.Obsoleted.getValueOr(llvm::VersionTuple());
return new (Context) AvailableAttr(
SourceLoc(), SourceRange(), Platform,
/*Message=*/StringRef(),
/*Rename=*/StringRef(),
Introduced, /*IntroducedRange=*/SourceRange(),
Deprecated, /*DeprecatedRange=*/SourceRange(),
Obsoleted, /*ObsoletedRange=*/SourceRange(),
Inferred.PlatformAgnostic, /*Implicit=*/true);
}
void AvailabilityInference::applyInferredAvailableAttrs(
Decl *ToDecl, ArrayRef<const Decl *> InferredFromDecls,
ASTContext &Context) {
// Iterate over the declarations and infer required availability on
// a per-platform basis.
std::map<PlatformKind, InferredAvailability> Inferred;
for (const Decl *D : InferredFromDecls) {
for (const DeclAttribute *Attr : D->getAttrs()) {
auto *AvAttr = dyn_cast<AvailableAttr>(Attr);
if (!AvAttr || AvAttr->isInvalid())
continue;
mergeWithInferredAvailability(AvAttr, Inferred[AvAttr->Platform]);
}
}
// Create an availability attribute for each observed platform and add
// to ToDecl.
DeclAttributes &Attrs = ToDecl->getAttrs();
for (auto &Pair : Inferred) {
auto *Attr = createAvailableAttr(Pair.first, Pair.second, Context);
Attrs.add(Attr);
}
}
/// 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 AvailableAttr *newAttr,
const AvailableAttr *prevAttr) {
assert(newAttr);
// 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->Platform == newAttr->Platform)
return prevAttr->Introduced.getValue() < newAttr->Introduced.getValue();
// If the new attribute's platform inherits from the old one, it wins.
return inheritsAvailabilityFromPlatform(newAttr->Platform,
prevAttr->Platform);
}
Optional<AvailabilityContext>
AvailabilityInference::annotatedAvailableRange(const Decl *D, ASTContext &Ctx) {
const AvailableAttr *bestAvailAttr = nullptr;
for (auto Attr : D->getAttrs()) {
auto *AvailAttr = dyn_cast<AvailableAttr>(Attr);
if (AvailAttr == nullptr || !AvailAttr->Introduced.hasValue() ||
!AvailAttr->isActivePlatform(Ctx) ||
AvailAttr->isLanguageVersionSpecific() ||
AvailAttr->isPackageDescriptionVersionSpecific()) {
continue;
}
if (isBetterThan(AvailAttr, bestAvailAttr))
bestAvailAttr = AvailAttr;
}
if (!bestAvailAttr)
return None;
return AvailabilityContext{
VersionRange::allGTE(bestAvailAttr->Introduced.getValue())};
}
AvailabilityContext AvailabilityInference::availableRange(const Decl *D,
ASTContext &Ctx) {
Optional<AvailabilityContext> AnnotatedRange =
annotatedAvailableRange(D, Ctx);
if (AnnotatedRange.hasValue()) {
return AnnotatedRange.getValue();
}
// 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 = D->getDeclContext();
if (auto *ED = dyn_cast<ExtensionDecl>(DC)) {
AnnotatedRange = annotatedAvailableRange(ED, Ctx);
if (AnnotatedRange.hasValue()) {
return AnnotatedRange.getValue();
}
}
// Treat unannotated declarations as always available.
return AvailabilityContext::alwaysAvailable();
}
namespace {
/// Infers the availability required to access a type.
class AvailabilityInferenceTypeWalker : public TypeWalker {
public:
ASTContext &AC;
AvailabilityContext AvailabilityInfo = AvailabilityContext::alwaysAvailable();
AvailabilityInferenceTypeWalker(ASTContext &AC) : AC(AC) {}
Action walkToTypePre(Type ty) override {
if (auto *nominalDecl = ty->getAnyNominal()) {
AvailabilityInfo.intersectWith(
AvailabilityInference::availableRange(nominalDecl, AC));
}
return Action::Continue;
}
};
} // end anonymous namespace
AvailabilityContext AvailabilityInference::inferForType(Type t) {
AvailabilityInferenceTypeWalker walker(t->getASTContext());
t.walk(walker);
return walker.AvailabilityInfo;
}
AvailabilityContext ASTContext::getObjCMetadataUpdateCallbackAvailability() {
return getSwift50Availability();
}
AvailabilityContext ASTContext::getObjCGetClassHookAvailability() {
return getSwift50Availability();
}
AvailabilityContext ASTContext::getSwift50Availability() {
auto target = LangOpts.Target;
if (target.getArchName() == "arm64e")
return AvailabilityContext::alwaysAvailable();
if (target.isMacOSX()) {
if (target.isAArch64())
return AvailabilityContext::alwaysAvailable();
return AvailabilityContext(
VersionRange::allGTE(llvm::VersionTuple(10,14,4)));
} else if (target.isiOS()) {
if (target.isAArch64() &&
(target.isSimulatorEnvironment() || target.isMacCatalystEnvironment()))
return AvailabilityContext::alwaysAvailable();
return AvailabilityContext(
VersionRange::allGTE(llvm::VersionTuple(12,2)));
} else if (target.isWatchOS()) {
return AvailabilityContext(
VersionRange::allGTE(llvm::VersionTuple(5,2)));
} else {
return AvailabilityContext::alwaysAvailable();
}
}
AvailabilityContext ASTContext::getOpaqueTypeAvailability() {
return getSwift51Availability();
}
AvailabilityContext ASTContext::getObjCClassStubsAvailability() {
return getSwift51Availability();
}
AvailabilityContext ASTContext::getSwift51Availability() {
auto target = LangOpts.Target;
if (target.getArchName() == "arm64e")
return AvailabilityContext::alwaysAvailable();
if (target.isMacOSX()) {
if (target.isAArch64())
return AvailabilityContext::alwaysAvailable();
return AvailabilityContext(
VersionRange::allGTE(llvm::VersionTuple(10,15,0)));
} else if (target.isiOS()) {
if (target.isAArch64() &&
(target.isSimulatorEnvironment() || target.isMacCatalystEnvironment()))
return AvailabilityContext::alwaysAvailable();
return AvailabilityContext(
VersionRange::allGTE(llvm::VersionTuple(13,0,0)));
} else if (target.isWatchOS()) {
return AvailabilityContext(
VersionRange::allGTE(llvm::VersionTuple(6,0,0)));
} else {
return AvailabilityContext::alwaysAvailable();
}
}
AvailabilityContext ASTContext::getTypesInAbstractMetadataStateAvailability() {
return getSwift52Availability();
}
AvailabilityContext ASTContext::getPrespecializedGenericMetadataAvailability() {
return getSwift54Availability();
}
AvailabilityContext ASTContext::getCompareTypeContextDescriptorsAvailability() {
return getSwift54Availability();
}
AvailabilityContext
ASTContext::getCompareProtocolConformanceDescriptorsAvailability() {
return getSwift54Availability();
}
AvailabilityContext
ASTContext::getIntermodulePrespecializedGenericMetadataAvailability() {
return getSwift54Availability();
}
AvailabilityContext ASTContext::getConcurrencyAvailability() {
return getSwiftFutureAvailability();
}
AvailabilityContext ASTContext::getDifferentiationAvailability() {
return getSwiftFutureAvailability();
}
AvailabilityContext ASTContext::getSwift52Availability() {
auto target = LangOpts.Target;
if (target.getArchName() == "arm64e")
return AvailabilityContext::alwaysAvailable();
if (target.isMacOSX()) {
if (target.isAArch64())
return AvailabilityContext::alwaysAvailable();
return AvailabilityContext(
VersionRange::allGTE(llvm::VersionTuple(10, 15, 4)));
} else if (target.isiOS()) {
if (target.isAArch64() &&
(target.isSimulatorEnvironment() || target.isMacCatalystEnvironment()))
return AvailabilityContext::alwaysAvailable();
return AvailabilityContext(
VersionRange::allGTE(llvm::VersionTuple(13, 4, 0)));
} else if (target.isWatchOS()) {
if (target.isArch64Bit())
return AvailabilityContext::alwaysAvailable();
return AvailabilityContext(
VersionRange::allGTE(llvm::VersionTuple(6, 2, 0)));
}
return AvailabilityContext::alwaysAvailable();
}
AvailabilityContext ASTContext::getSwift53Availability() {
auto target = LangOpts.Target;
if (target.getArchName() == "arm64e")
return AvailabilityContext::alwaysAvailable();
if (target.isMacOSX() ) {
if (target.isAArch64())
return AvailabilityContext::alwaysAvailable();
llvm::VersionTuple macOVersion53(10, 16, 0);
macOVersion53 = canonicalizePlatformVersion(PlatformKind::macOS, macOVersion53);
return AvailabilityContext(
VersionRange::allGTE(macOVersion53));
} else if (target.isiOS()) {
if (target.isAArch64() &&
(target.isSimulatorEnvironment() || target.isMacCatalystEnvironment()))
return AvailabilityContext::alwaysAvailable();
return AvailabilityContext(
VersionRange::allGTE(llvm::VersionTuple(14, 0, 0)));
} else if (target.isWatchOS()) {
if (target.isArch64Bit())
return AvailabilityContext::alwaysAvailable();
return AvailabilityContext(
VersionRange::allGTE(llvm::VersionTuple(7, 0, 0)));
} else {
return AvailabilityContext::alwaysAvailable();
}
}
AvailabilityContext ASTContext::getSwift54Availability() {
return getSwiftFutureAvailability();
}
AvailabilityContext ASTContext::getSwiftFutureAvailability() {
auto target = LangOpts.Target;
if (target.isMacOSX() ) {
return AvailabilityContext(
VersionRange::allGTE(llvm::VersionTuple(99, 99, 0)));
} else if (target.isiOS()) {
return AvailabilityContext(
VersionRange::allGTE(llvm::VersionTuple(99, 0, 0)));
} else if (target.isWatchOS()) {
return AvailabilityContext(
VersionRange::allGTE(llvm::VersionTuple(9, 99, 0)));
} else {
return AvailabilityContext::alwaysAvailable();
}
}