|
| 1 | +//===--- AvailabilityQuery.cpp - Swift Availability Queries ---------------===// |
| 2 | +// |
| 3 | +// This source file is part of the Swift.org open source project |
| 4 | +// |
| 5 | +// Copyright (c) 2025 Apple Inc. and the Swift project authors |
| 6 | +// Licensed under Apache License v2.0 with Runtime Library Exception |
| 7 | +// |
| 8 | +// See https://swift.org/LICENSE.txt for license information |
| 9 | +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| 10 | +// |
| 11 | +//===----------------------------------------------------------------------===// |
| 12 | + |
| 13 | +#include "swift/AST/AvailabilityQuery.h" |
| 14 | +#include "swift/AST/ASTContext.h" |
| 15 | +#include "swift/AST/Decl.h" |
| 16 | +#include "swift/Basic/Platform.h" |
| 17 | + |
| 18 | +using namespace swift; |
| 19 | + |
| 20 | +static void unpackVersion(const llvm::VersionTuple &version, |
| 21 | + llvm::SmallVectorImpl<unsigned> &arguments) { |
| 22 | + arguments.push_back(version.getMajor()); |
| 23 | + arguments.push_back(version.getMinor().value_or(0)); |
| 24 | + arguments.push_back(version.getSubminor().value_or(0)); |
| 25 | +} |
| 26 | + |
| 27 | +static FuncDecl * |
| 28 | +getOSVersionRangeCheck(const llvm::VersionTuple &version, |
| 29 | + llvm::SmallVectorImpl<unsigned> &arguments, |
| 30 | + ASTContext &ctx, bool forTargetVariant) { |
| 31 | + unpackVersion(version, arguments); |
| 32 | + return forTargetVariant ? ctx.getIsVariantOSVersionAtLeastDecl() |
| 33 | + : ctx.getIsOSVersionAtLeastDecl(); |
| 34 | +} |
| 35 | + |
| 36 | +static FuncDecl *getOSVersionOrVariantVersionRangeCheck( |
| 37 | + const llvm::VersionTuple &targetVersion, |
| 38 | + const llvm::VersionTuple &variantVersion, |
| 39 | + llvm::SmallVectorImpl<unsigned> &arguments, ASTContext &ctx) { |
| 40 | + unpackVersion(targetVersion, arguments); |
| 41 | + unpackVersion(variantVersion, arguments); |
| 42 | + return ctx.getIsOSVersionAtLeastOrVariantVersionAtLeast(); |
| 43 | +} |
| 44 | + |
| 45 | +static FuncDecl * |
| 46 | +getZipperedOSVersionRangeCheck(const AvailabilityQuery &query, |
| 47 | + llvm::SmallVectorImpl<unsigned> &arguments, |
| 48 | + ASTContext &ctx) { |
| 49 | + |
| 50 | + auto targetVersion = query.getPrimaryArgument(); |
| 51 | + auto variantVersion = query.getVariantArgument(); |
| 52 | + DEBUG_ASSERT(targetVersion || variantVersion); |
| 53 | + |
| 54 | + // We're building zippered, so we need to pass both macOS and iOS versions to |
| 55 | + // the runtime version range check. At run time that check will determine what |
| 56 | + // kind of process this code is loaded into. In a macOS process it will use |
| 57 | + // the macOS version; in an macCatalyst process it will use the iOS version. |
| 58 | + llvm::Triple targetTriple = ctx.LangOpts.Target; |
| 59 | + llvm::Triple variantTriple = *ctx.LangOpts.TargetVariant; |
| 60 | + |
| 61 | + // From perspective of the driver and most of the frontend, -target and |
| 62 | + // -target-variant are symmetric. That is, the user can pass either: |
| 63 | + // -target x86_64-apple-macosx10.15 \ |
| 64 | + // -target-variant x86_64-apple-ios13.1-macabi |
| 65 | + // or: |
| 66 | + // -target x86_64-apple-ios13.1-macabi \ |
| 67 | + // -target-variant x86_64-apple-macosx10.15 |
| 68 | + // |
| 69 | + // However, the runtime availability-checking entry points need to compare |
| 70 | + // against an actual running OS version and so can't be symmetric. Here we |
| 71 | + // standardize on "target" means macOS version and "targetVariant" means iOS |
| 72 | + // version. |
| 73 | + if (tripleIsMacCatalystEnvironment(targetTriple)) { |
| 74 | + DEBUG_ASSERT(variantTriple.isMacOSX()); |
| 75 | + // Normalize so that "variant" always means iOS version. |
| 76 | + std::swap(targetVersion, variantVersion); |
| 77 | + std::swap(targetTriple, variantTriple); |
| 78 | + } |
| 79 | + |
| 80 | + // The variant-only availability-checking entrypoint is not part of the |
| 81 | + // Swift 5.0 ABI. It is only available in macOS 10.15 and above. |
| 82 | + bool isVariantEntrypointAvailable = !targetTriple.isMacOSXVersionLT(10, 15); |
| 83 | + |
| 84 | + // If there is no check for the target but there is for the variant, then we |
| 85 | + // only need to emit code for the variant check. |
| 86 | + if (isVariantEntrypointAvailable && !targetVersion && variantVersion) |
| 87 | + return getOSVersionRangeCheck(*variantVersion, arguments, ctx, |
| 88 | + /*forVariant=*/true); |
| 89 | + |
| 90 | + // Similarly, if there is a check for the target but not for the target |
| 91 | + // variant then we only to emit code for the target check. |
| 92 | + if (targetVersion && !variantVersion) |
| 93 | + return getOSVersionRangeCheck(*targetVersion, arguments, ctx, |
| 94 | + /*forTargetVariant=*/false); |
| 95 | + |
| 96 | + if (!isVariantEntrypointAvailable || (targetVersion && variantVersion)) { |
| 97 | + |
| 98 | + // If the variant-only entrypoint isn't available (as is the case |
| 99 | + // pre-macOS 10.15) we need to use the zippered entrypoint (which is part of |
| 100 | + // the Swift 5.0 ABI) even when the macOS version is '*' (all). In this |
| 101 | + // case, use the minimum macOS deployment version from the target triple. |
| 102 | + // This ensures the check always passes on macOS. |
| 103 | + if (!isVariantEntrypointAvailable && !targetVersion) { |
| 104 | + DEBUG_ASSERT(targetTriple.isMacOSX()); |
| 105 | + |
| 106 | + llvm::VersionTuple macosVersion; |
| 107 | + targetTriple.getMacOSXVersion(macosVersion); |
| 108 | + targetVersion = macosVersion; |
| 109 | + } |
| 110 | + |
| 111 | + return getOSVersionOrVariantVersionRangeCheck( |
| 112 | + *targetVersion, *variantVersion, arguments, ctx); |
| 113 | + } |
| 114 | + |
| 115 | + llvm_unreachable("Unhandled zippered configuration"); |
| 116 | +} |
| 117 | + |
| 118 | +static FuncDecl * |
| 119 | +getOSAvailabilityDeclAndArguments(const AvailabilityQuery &query, |
| 120 | + llvm::SmallVectorImpl<unsigned> &arguments, |
| 121 | + ASTContext &ctx) { |
| 122 | + if (ctx.LangOpts.TargetVariant) |
| 123 | + return getZipperedOSVersionRangeCheck(query, arguments, ctx); |
| 124 | + |
| 125 | + bool isMacCatalyst = tripleIsMacCatalystEnvironment(ctx.LangOpts.Target); |
| 126 | + return getOSVersionRangeCheck(query.getPrimaryArgument().value(), arguments, |
| 127 | + ctx, isMacCatalyst); |
| 128 | +} |
| 129 | + |
| 130 | +FuncDecl *AvailabilityQuery::getDynamicQueryDeclAndArguments( |
| 131 | + llvm::SmallVectorImpl<unsigned> &arguments, ASTContext &ctx) const { |
| 132 | + switch (getDomain().getKind()) { |
| 133 | + case AvailabilityDomain::Kind::Universal: |
| 134 | + case AvailabilityDomain::Kind::SwiftLanguage: |
| 135 | + case AvailabilityDomain::Kind::PackageDescription: |
| 136 | + case AvailabilityDomain::Kind::Embedded: |
| 137 | + return nullptr; |
| 138 | + case AvailabilityDomain::Kind::Platform: |
| 139 | + return getOSAvailabilityDeclAndArguments(*this, arguments, ctx); |
| 140 | + case AvailabilityDomain::Kind::Custom: |
| 141 | + // FIXME: [availability] Support custom domains. |
| 142 | + return nullptr; |
| 143 | + } |
| 144 | +} |
0 commit comments