//===--- 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 using namespace swift; AvailabilityContext AvailabilityContext::forDeploymentTarget(ASTContext &Ctx) { return AvailabilityContext( VersionRange::allGTE(Ctx.LangOpts.getMinPlatformVersion())); } AvailabilityContext AvailabilityContext::forInliningTarget(ASTContext &Ctx) { return AvailabilityContext( VersionRange::allGTE(Ctx.LangOpts.MinimumInliningTargetVersion)); } namespace { /// The inferred availability required to access a group of declarations /// on a single platform. struct InferredAvailability { PlatformAgnosticAvailabilityKind PlatformAgnostic = PlatformAgnosticAvailabilityKind::None; Optional Introduced; Optional Deprecated; Optional Obsoleted; 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 Optional &Version, Optional &Inferred, MergeFunction Merge) { if (Version.hasValue()) { if (Inferred.hasValue()) { Inferred = Merge(Inferred.getValue(), Version.getValue()); 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(const AvailableAttr *Attr, InferredAvailability &Inferred) { Inferred.PlatformAgnostic = static_cast( std::max(static_cast(Inferred.PlatformAgnostic), static_cast(Attr->getPlatformAgnosticAvailability()))); // The merge of two introduction versions is the maximum of the two versions. if (mergeIntoInferredVersion(Attr->Introduced, Inferred.Introduced, std::max)) { Inferred.IsSPI = Attr->IsSPI; } // 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(), /*RenameDecl=*/nullptr, Introduced, /*IntroducedRange=*/SourceRange(), Deprecated, /*DeprecatedRange=*/SourceRange(), Obsoleted, /*ObsoletedRange=*/SourceRange(), Inferred.PlatformAgnostic, /*Implicit=*/true, Inferred.IsSPI); } void AvailabilityInference::applyInferredAvailableAttrs( Decl *ToDecl, ArrayRef InferredFromDecls, ASTContext &Context) { // Iterate over the declarations and infer required availability on // a per-platform basis. std::map Inferred; for (const Decl *D : InferredFromDecls) { for (const DeclAttribute *Attr : D->getAttrs()) { auto *AvAttr = dyn_cast(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 AvailabilityInference::annotatedAvailableRange(const Decl *D, ASTContext &Ctx) { const AvailableAttr *bestAvailAttr = nullptr; for (auto Attr : D->getAttrs()) { auto *AvailAttr = dyn_cast(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()), bestAvailAttr->IsSPI}; } bool Decl::isAvailableAsSPI() const { return AvailabilityInference::availableRange(this, getASTContext()) .isAvailableAsSPI(); } AvailabilityContext AvailabilityInference::annotatedAvailableRangeForAttr(const SpecializeAttr* attr, ASTContext &ctx) { const AvailableAttr *bestAvailAttr = nullptr; for (auto *availAttr : attr->getAvailableAttrs()) { if (availAttr == nullptr || !availAttr->Introduced.hasValue() || !availAttr->isActivePlatform(ctx) || availAttr->isLanguageVersionSpecific() || availAttr->isPackageDescriptionVersionSpecific()) { continue; } if (isBetterThan(availAttr, bestAvailAttr)) bestAvailAttr = availAttr; } if (bestAvailAttr) { return AvailabilityContext{ VersionRange::allGTE(bestAvailAttr->Introduced.getValue()) }; } return AvailabilityContext::alwaysAvailable(); } AvailabilityContext AvailabilityInference::availableRange(const Decl *D, ASTContext &Ctx) { Optional 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(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()) { if (target.isArch64Bit()) return AvailabilityContext::alwaysAvailable(); 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()) { if (target.isArch64Bit()) return AvailabilityContext::alwaysAvailable(); 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 getSwift55Availability(); } AvailabilityContext ASTContext::getBackDeployedConcurrencyAvailability() { return getSwift51Availability(); } AvailabilityContext ASTContext::getDifferentiationAvailability() { return getSwiftFutureAvailability(); } AvailabilityContext ASTContext::getMultiPayloadEnumTagSinglePayload() { return getSwift56Availability(); } AvailabilityContext ASTContext::getObjCIsUniquelyReferencedAvailability() { return getSwift56Availability(); } AvailabilityContext ASTContext::getParameterizedExistentialRuntimeAvailability() { return getSwift57Availability(); } AvailabilityContext ASTContext::getImmortalRefCountSymbolsAvailability() { // TODO: replace this with a concrete swift version once we have it. // rdar://94185998 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() { auto target = LangOpts.Target; if (target.isMacOSX()) { return AvailabilityContext( VersionRange::allGTE(llvm::VersionTuple(11, 3, 0))); } else if (target.isiOS()) { return AvailabilityContext( VersionRange::allGTE(llvm::VersionTuple(14, 5, 0))); } else if (target.isWatchOS()) { return AvailabilityContext( VersionRange::allGTE(llvm::VersionTuple(7, 4, 0))); } else { return AvailabilityContext::alwaysAvailable(); } } AvailabilityContext ASTContext::getSwift55Availability() { auto target = LangOpts.Target; if (target.isMacOSX() ) { return AvailabilityContext( VersionRange::allGTE(llvm::VersionTuple(12, 0, 0))); } else if (target.isiOS()) { return AvailabilityContext( VersionRange::allGTE(llvm::VersionTuple(15, 0, 0))); } else if (target.isWatchOS()) { return AvailabilityContext( VersionRange::allGTE(llvm::VersionTuple(8, 0, 0))); } else { return AvailabilityContext::alwaysAvailable(); } } AvailabilityContext ASTContext::getSwift56Availability() { auto target = LangOpts.Target; if (target.isMacOSX() ) { return AvailabilityContext( VersionRange::allGTE(llvm::VersionTuple(12, 3, 0))); } else if (target.isiOS()) { return AvailabilityContext( VersionRange::allGTE(llvm::VersionTuple(15, 4, 0))); } else if (target.isWatchOS()) { return AvailabilityContext( VersionRange::allGTE(llvm::VersionTuple(8, 5, 0))); } else { return AvailabilityContext::alwaysAvailable(); } } AvailabilityContext ASTContext::getSwift57Availability() { auto target = LangOpts.Target; if (target.isMacOSX()) { return AvailabilityContext( VersionRange::allGTE(llvm::VersionTuple(13, 0, 0))); } else if (target.isiOS()) { return AvailabilityContext( VersionRange::allGTE(llvm::VersionTuple(16, 0, 0))); } else if (target.isWatchOS()) { return AvailabilityContext( VersionRange::allGTE(llvm::VersionTuple(9, 0, 0))); } else { return AvailabilityContext::alwaysAvailable(); } } 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(); } } AvailabilityContext ASTContext::getSwift5PlusAvailability(llvm::VersionTuple swiftVersion) { if (swiftVersion.getMajor() == 5) { switch (*swiftVersion.getMinor()) { case 0: return getSwift50Availability(); case 1: return getSwift51Availability(); case 2: return getSwift52Availability(); case 3: return getSwift53Availability(); case 4: return getSwift54Availability(); case 5: return getSwift55Availability(); case 6: return getSwift56Availability(); case 7: return getSwift57Availability(); default: break; } } llvm::report_fatal_error( Twine("Missing call to getSwiftXYAvailability for Swift ") + swiftVersion.getAsString()); }