mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
This name will distinguish the standalone Swift runtime availability domain from the domain for the built-in Swift runtime on supported targets.
198 lines
7.9 KiB
C++
198 lines
7.9 KiB
C++
//===--- AvailabilityQuery.cpp - Swift Availability Queries ---------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2025 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/AST/AvailabilityQuery.h"
|
|
#include "swift/AST/ASTContext.h"
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/Basic/Platform.h"
|
|
|
|
using namespace swift;
|
|
|
|
AvailabilityQuery::AvailabilityQuery(
|
|
AvailabilityDomain domain, ResultKind kind,
|
|
const std::optional<AvailabilityRange> &primaryRange,
|
|
const std::optional<AvailabilityRange> &variantRange)
|
|
: domain(domain), primaryRange(primaryRange), variantRange(variantRange),
|
|
kind(kind), unavailable(false) {
|
|
// Check invariants.
|
|
switch (domain.getKind()) {
|
|
case AvailabilityDomain::Kind::SwiftLanguageMode:
|
|
case AvailabilityDomain::Kind::PackageDescription:
|
|
case AvailabilityDomain::Kind::Embedded:
|
|
// These domains don't support queries at all.
|
|
DEBUG_ASSERT(false);
|
|
break;
|
|
|
|
case AvailabilityDomain::Kind::Universal:
|
|
// The universal domain can only support constant queries.
|
|
DEBUG_ASSERT(kind != ResultKind::Dynamic);
|
|
break;
|
|
|
|
case AvailabilityDomain::Kind::StandaloneSwiftRuntime:
|
|
// Dynamic Swift runtime queries take just a primary version argument.
|
|
if (kind == ResultKind::Dynamic) {
|
|
DEBUG_ASSERT(primaryRange);
|
|
DEBUG_ASSERT(!variantRange);
|
|
}
|
|
break;
|
|
|
|
case AvailabilityDomain::Kind::Platform:
|
|
// Dynamic platform version queries must have either a primary version
|
|
// argument or a variant version argument (or both).
|
|
if (kind == ResultKind::Dynamic) {
|
|
DEBUG_ASSERT(primaryRange || variantRange);
|
|
}
|
|
break;
|
|
|
|
case AvailabilityDomain::Kind::Custom:
|
|
// Custom availability domains do not support versioned queries at all yet.
|
|
DEBUG_ASSERT(!primaryRange);
|
|
DEBUG_ASSERT(!variantRange);
|
|
|
|
// A valid custom domain object is required.
|
|
auto customDomain = domain.getCustomDomain();
|
|
ASSERT(customDomain);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void unpackVersion(const llvm::VersionTuple &version,
|
|
llvm::SmallVectorImpl<unsigned> &arguments) {
|
|
arguments.push_back(version.getMajor());
|
|
arguments.push_back(version.getMinor().value_or(0));
|
|
arguments.push_back(version.getSubminor().value_or(0));
|
|
}
|
|
|
|
static FuncDecl *
|
|
getOSVersionRangeCheck(const llvm::VersionTuple &version,
|
|
llvm::SmallVectorImpl<unsigned> &arguments,
|
|
ASTContext &ctx, bool forTargetVariant) {
|
|
unpackVersion(version, arguments);
|
|
return forTargetVariant ? ctx.getIsVariantOSVersionAtLeastDecl()
|
|
: ctx.getIsOSVersionAtLeastDecl();
|
|
}
|
|
|
|
static FuncDecl *getOSVersionOrVariantVersionRangeCheck(
|
|
const llvm::VersionTuple &targetVersion,
|
|
const llvm::VersionTuple &variantVersion,
|
|
llvm::SmallVectorImpl<unsigned> &arguments, ASTContext &ctx) {
|
|
unpackVersion(targetVersion, arguments);
|
|
unpackVersion(variantVersion, arguments);
|
|
return ctx.getIsOSVersionAtLeastOrVariantVersionAtLeast();
|
|
}
|
|
|
|
static FuncDecl *
|
|
getZipperedOSVersionRangeCheck(const AvailabilityQuery &query,
|
|
llvm::SmallVectorImpl<unsigned> &arguments,
|
|
ASTContext &ctx) {
|
|
|
|
auto targetVersion = query.getPrimaryArgument();
|
|
auto variantVersion = query.getVariantArgument();
|
|
DEBUG_ASSERT(targetVersion || variantVersion);
|
|
|
|
// We're building zippered, so we need to pass both macOS and iOS versions to
|
|
// the runtime version range check. At run time that check will determine what
|
|
// kind of process this code is loaded into. In a macOS process it will use
|
|
// the macOS version; in an macCatalyst process it will use the iOS version.
|
|
llvm::Triple targetTriple = ctx.LangOpts.Target;
|
|
llvm::Triple variantTriple = *ctx.LangOpts.TargetVariant;
|
|
|
|
// From perspective of the driver and most of the frontend, -target and
|
|
// -target-variant are symmetric. That is, the user can pass either:
|
|
// -target x86_64-apple-macosx10.15 \
|
|
// -target-variant x86_64-apple-ios13.1-macabi
|
|
// or:
|
|
// -target x86_64-apple-ios13.1-macabi \
|
|
// -target-variant x86_64-apple-macosx10.15
|
|
//
|
|
// However, the runtime availability-checking entry points need to compare
|
|
// against an actual running OS version and so can't be symmetric. Here we
|
|
// standardize on "target" means macOS version and "targetVariant" means iOS
|
|
// version.
|
|
if (tripleIsMacCatalystEnvironment(targetTriple)) {
|
|
DEBUG_ASSERT(variantTriple.isMacOSX());
|
|
// Normalize so that "variant" always means iOS version.
|
|
std::swap(targetVersion, variantVersion);
|
|
std::swap(targetTriple, variantTriple);
|
|
}
|
|
|
|
// The variant-only availability-checking entrypoint is not part of the
|
|
// Swift 5.0 ABI. It is only available in macOS 10.15 and above.
|
|
bool isVariantEntrypointAvailable = !targetTriple.isMacOSXVersionLT(10, 15);
|
|
|
|
// If there is no check for the target but there is for the variant, then we
|
|
// only need to emit code for the variant check.
|
|
if (isVariantEntrypointAvailable && !targetVersion && variantVersion)
|
|
return getOSVersionRangeCheck(*variantVersion, arguments, ctx,
|
|
/*forVariant=*/true);
|
|
|
|
// Similarly, if there is a check for the target but not for the target
|
|
// variant then we only to emit code for the target check.
|
|
if (targetVersion && !variantVersion)
|
|
return getOSVersionRangeCheck(*targetVersion, arguments, ctx,
|
|
/*forTargetVariant=*/false);
|
|
|
|
if (!isVariantEntrypointAvailable || (targetVersion && variantVersion)) {
|
|
|
|
// If the variant-only entrypoint isn't available (as is the case
|
|
// pre-macOS 10.15) we need to use the zippered entrypoint (which is part of
|
|
// the Swift 5.0 ABI) even when the macOS version is '*' (all). In this
|
|
// case, use the minimum macOS deployment version from the target triple.
|
|
// This ensures the check always passes on macOS.
|
|
if (!isVariantEntrypointAvailable && !targetVersion) {
|
|
DEBUG_ASSERT(targetTriple.isMacOSX());
|
|
|
|
llvm::VersionTuple macosVersion;
|
|
targetTriple.getMacOSXVersion(macosVersion);
|
|
targetVersion = macosVersion;
|
|
}
|
|
|
|
return getOSVersionOrVariantVersionRangeCheck(
|
|
*targetVersion, *variantVersion, arguments, ctx);
|
|
}
|
|
|
|
llvm_unreachable("Unhandled zippered configuration");
|
|
}
|
|
|
|
static FuncDecl *
|
|
getOSAvailabilityDeclAndArguments(const AvailabilityQuery &query,
|
|
llvm::SmallVectorImpl<unsigned> &arguments,
|
|
ASTContext &ctx) {
|
|
if (ctx.LangOpts.TargetVariant)
|
|
return getZipperedOSVersionRangeCheck(query, arguments, ctx);
|
|
|
|
bool isMacCatalyst = tripleIsMacCatalystEnvironment(ctx.LangOpts.Target);
|
|
return getOSVersionRangeCheck(query.getPrimaryArgument().value(), arguments,
|
|
ctx, isMacCatalyst);
|
|
}
|
|
|
|
FuncDecl *AvailabilityQuery::getDynamicQueryDeclAndArguments(
|
|
llvm::SmallVectorImpl<unsigned> &arguments, ASTContext &ctx) const {
|
|
auto domain = getDomain();
|
|
switch (domain.getKind()) {
|
|
case AvailabilityDomain::Kind::Universal:
|
|
case AvailabilityDomain::Kind::SwiftLanguageMode:
|
|
case AvailabilityDomain::Kind::PackageDescription:
|
|
case AvailabilityDomain::Kind::Embedded:
|
|
// These domains don't support dynamic queries.
|
|
return nullptr;
|
|
|
|
case AvailabilityDomain::Kind::StandaloneSwiftRuntime:
|
|
unpackVersion(getPrimaryArgument().value(), arguments);
|
|
return ctx.getIsSwiftRuntimeVersionAtLeast();
|
|
case AvailabilityDomain::Kind::Platform:
|
|
return getOSAvailabilityDeclAndArguments(*this, arguments, ctx);
|
|
case AvailabilityDomain::Kind::Custom:
|
|
return domain.getCustomDomain()->getPredicateFunc();
|
|
}
|
|
}
|