mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
https://github.com/swiftlang/swift/pull/79807 caused a regression in which `AvailabilityContext` stopped tracking the available version range for the active platform domain for certain platforms. Fix this by reverting to checking `AvailabilityDomain::isActive()` to determine when a given platform `AvailabilityDomain` represents the target platform. The compiler's existing mapping from target triple to platform domain is incomplete and it's not clear to me whether fixing that could cause other regressions. Resolves rdar://147413616.
471 lines
15 KiB
C++
471 lines
15 KiB
C++
//===--- AvailabilityDomain.h - Swift Availability Domains ------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2024 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 the AvailabilityDomain class, which represents a domain
|
|
// for which availability can be checked.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_AST_AVAILABILITY_DOMAIN_H
|
|
#define SWIFT_AST_AVAILABILITY_DOMAIN_H
|
|
|
|
#include "swift/AST/ASTAllocated.h"
|
|
#include "swift/AST/AvailabilityRange.h"
|
|
#include "swift/AST/Identifier.h"
|
|
#include "swift/AST/PlatformKind.h"
|
|
#include "swift/Basic/Assertions.h"
|
|
#include "swift/Basic/LLVM.h"
|
|
#include "swift/Basic/SourceLoc.h"
|
|
#include "llvm/ADT/FoldingSet.h"
|
|
#include "llvm/ADT/PointerEmbeddedInt.h"
|
|
#include "llvm/ADT/PointerUnion.h"
|
|
|
|
namespace swift {
|
|
class ASTContext;
|
|
class CustomAvailabilityDomain;
|
|
class DeclContext;
|
|
class ModuleDecl;
|
|
|
|
/// Represents a dimension of availability (e.g. macOS platform or Swift
|
|
/// language mode).
|
|
class AvailabilityDomain final {
|
|
public:
|
|
enum class Kind : uint8_t {
|
|
/// The root availability domain. This is used for declarations that are
|
|
/// universally unavailable or deprecated, for example.
|
|
Universal,
|
|
|
|
/// Represents availability with respect to Swift language mode.
|
|
SwiftLanguage,
|
|
|
|
/// Represents PackageDescription availability.
|
|
PackageDescription,
|
|
|
|
/// Represents Embedded Swift availability.
|
|
Embedded,
|
|
|
|
/// Represents availability for a specific operating system platform.
|
|
Platform,
|
|
|
|
/// Represents availability for an arbitrary domain that is defined at
|
|
/// compile time by a module.
|
|
Custom,
|
|
};
|
|
|
|
private:
|
|
friend struct llvm::PointerLikeTypeTraits<AvailabilityDomain>;
|
|
friend struct llvm::DenseMapInfo<AvailabilityDomain>;
|
|
|
|
/// For a subset of domain kinds, all the information needed to represent the
|
|
/// domain can be encapsulated inline in this class.
|
|
class InlineDomain {
|
|
Kind kind;
|
|
PlatformKind platform;
|
|
|
|
public:
|
|
using IntReprType = uint32_t;
|
|
enum : uintptr_t {
|
|
SpareBits = 8,
|
|
ReprBits = sizeof(IntReprType) * CHAR_BIT - SpareBits,
|
|
KindShift = ReprBits - sizeof(Kind) * CHAR_BIT,
|
|
PlatformShift = KindShift - sizeof(PlatformKind) * CHAR_BIT,
|
|
};
|
|
|
|
InlineDomain(Kind kind, PlatformKind platform)
|
|
: kind(kind), platform(platform) {};
|
|
InlineDomain(IntReprType value)
|
|
: kind(static_cast<Kind>(value >> KindShift)),
|
|
platform(static_cast<PlatformKind>(value >> PlatformShift)) {}
|
|
|
|
/// Serializes the domain into an integer value that must be smaller than a
|
|
/// a pointer.
|
|
IntReprType asInteger() const {
|
|
return static_cast<IntReprType>(kind) << KindShift |
|
|
static_cast<IntReprType>(platform) << PlatformShift;
|
|
}
|
|
|
|
Kind getKind() const { return kind; }
|
|
PlatformKind getPlatform() { return platform; }
|
|
};
|
|
|
|
using InlineDomainPtr =
|
|
llvm::PointerEmbeddedInt<uint32_t, InlineDomain::ReprBits>;
|
|
using Storage =
|
|
llvm::PointerUnion<const CustomAvailabilityDomain *, InlineDomainPtr>;
|
|
Storage storage;
|
|
|
|
AvailabilityDomain(Kind kind)
|
|
: storage(InlineDomain(kind, PlatformKind::none).asInteger()) {
|
|
DEBUG_ASSERT(kind != Kind::Platform);
|
|
};
|
|
|
|
AvailabilityDomain(PlatformKind platform)
|
|
: storage(InlineDomain(Kind::Platform, platform).asInteger()) {};
|
|
|
|
AvailabilityDomain(const CustomAvailabilityDomain *domain)
|
|
: storage(domain) {};
|
|
|
|
AvailabilityDomain(Storage storage) : storage(storage) {};
|
|
|
|
std::optional<InlineDomain> getInlineDomain() const {
|
|
return storage.is<InlineDomainPtr>()
|
|
? static_cast<std::optional<InlineDomain>>(
|
|
storage.get<InlineDomainPtr>())
|
|
: std::nullopt;
|
|
}
|
|
|
|
public:
|
|
AvailabilityDomain() {}
|
|
|
|
static AvailabilityDomain forUniversal() {
|
|
return AvailabilityDomain(Kind::Universal);
|
|
}
|
|
|
|
static AvailabilityDomain forPlatform(PlatformKind platformKind) {
|
|
bool isPlatform = platformKind != PlatformKind::none;
|
|
ASSERT(isPlatform);
|
|
return AvailabilityDomain(platformKind);
|
|
}
|
|
|
|
static AvailabilityDomain forSwiftLanguage() {
|
|
return AvailabilityDomain(Kind::SwiftLanguage);
|
|
}
|
|
|
|
static AvailabilityDomain forPackageDescription() {
|
|
return AvailabilityDomain(Kind::PackageDescription);
|
|
}
|
|
|
|
static AvailabilityDomain forEmbedded() {
|
|
return AvailabilityDomain(Kind::Embedded);
|
|
}
|
|
|
|
static AvailabilityDomain forCustom(const CustomAvailabilityDomain *domain) {
|
|
return AvailabilityDomain(domain);
|
|
}
|
|
|
|
/// Returns the built-in availability domain identified by the given string.
|
|
static std::optional<AvailabilityDomain>
|
|
builtinDomainForString(StringRef string, const DeclContext *declContext);
|
|
|
|
static AvailabilityDomain fromOpaque(void *opaque) {
|
|
return AvailabilityDomain(Storage::getFromOpaqueValue(opaque));
|
|
}
|
|
|
|
void *getOpaqueValue() const { return storage.getOpaqueValue(); }
|
|
|
|
Kind getKind() const {
|
|
if (auto inlineDomain = getInlineDomain())
|
|
return inlineDomain->getKind();
|
|
|
|
return Kind::Custom;
|
|
}
|
|
|
|
bool isUniversal() const { return getKind() == Kind::Universal; }
|
|
|
|
bool isPlatform() const { return getKind() == Kind::Platform; }
|
|
|
|
bool isSwiftLanguage() const { return getKind() == Kind::SwiftLanguage; }
|
|
|
|
bool isPackageDescription() const {
|
|
return getKind() == Kind::PackageDescription;
|
|
}
|
|
|
|
bool isEmbedded() const { return getKind() == Kind::Embedded; }
|
|
|
|
bool isCustom() const { return getKind() == Kind::Custom; }
|
|
|
|
/// Returns the platform kind for this domain if applicable.
|
|
PlatformKind getPlatformKind() const {
|
|
if (auto inlineDomain = getInlineDomain())
|
|
return inlineDomain->getPlatform();
|
|
|
|
return PlatformKind::none;
|
|
}
|
|
|
|
/// If the domain represents a user-defined domain, returns the metadata for
|
|
/// the domain. Returns `nullptr` otherwise.
|
|
const CustomAvailabilityDomain *getCustomDomain() const {
|
|
if (isCustom())
|
|
return storage.get<const CustomAvailabilityDomain *>();
|
|
return nullptr;
|
|
}
|
|
|
|
/// Returns true if availability for this domain can be specified in terms of
|
|
/// version ranges.
|
|
bool isVersioned() const;
|
|
|
|
/// Returns true if availability of the domain can be refined using
|
|
/// `@available` attributes and `if #available` queries. If not, then the
|
|
/// domain's availability is fixed by compilation settings. For example,
|
|
/// macOS platform availability supports contextual refinement, whereas Swift
|
|
/// language availability does not.
|
|
bool supportsContextRefinement() const;
|
|
|
|
/// Returns true if the domain supports `#available`/`#unavailable` queries.
|
|
bool supportsQueries() const;
|
|
|
|
/// Returns true if this domain is considered active in the current
|
|
/// compilation context.
|
|
bool isActive(const ASTContext &ctx) const;
|
|
|
|
/// Returns the domain's minimum available range for type checking. For
|
|
/// example, for the domain of the platform that compilation is targeting,
|
|
/// this version is specified with the `-target` option. For the Swift
|
|
/// language domain, it is specified with the `-swift-version` option. Returns
|
|
/// `std::nullopt` for domains which have don't have a "deployment target".
|
|
std::optional<AvailabilityRange>
|
|
getDeploymentRange(const ASTContext &ctx) const;
|
|
|
|
/// Returns the string to use in diagnostics to identify the domain. May
|
|
/// return an empty string.
|
|
llvm::StringRef getNameForDiagnostics() const;
|
|
|
|
/// Returns the string to use when printing an `@available` attribute.
|
|
llvm::StringRef getNameForAttributePrinting() const;
|
|
|
|
/// Returns the module that the domain belongs to, if it is a custom domain.
|
|
ModuleDecl *getModule() const;
|
|
|
|
/// Returns true if availability in `other` is a subset of availability in
|
|
/// this domain. The set of all availability domains form a lattice where the
|
|
/// universal domain (`*`) is the bottom element.
|
|
bool contains(const AvailabilityDomain &other) const;
|
|
|
|
/// Returns true for domains that are not contained by any domain other than
|
|
/// the universal domain.
|
|
bool isRoot() const;
|
|
|
|
/// Returns the root availability domain that contains this domain (see
|
|
/// `isRoot()`). For example, macCatalyst and visionOS are both ultimately
|
|
/// descendants of the iOS domain.
|
|
AvailabilityDomain getRootDomain() const;
|
|
|
|
bool operator==(const AvailabilityDomain &other) const {
|
|
return storage.getOpaqueValue() == other.storage.getOpaqueValue();
|
|
}
|
|
|
|
bool operator!=(const AvailabilityDomain &other) const {
|
|
return !(*this == other);
|
|
}
|
|
|
|
friend bool operator<(const AvailabilityDomain &lhs,
|
|
const AvailabilityDomain &rhs) {
|
|
return lhs.storage.getOpaqueValue() < rhs.storage.getOpaqueValue();
|
|
}
|
|
|
|
void Profile(llvm::FoldingSetNodeID &ID) const {
|
|
ID.AddPointer(getOpaqueValue());
|
|
}
|
|
|
|
void print(llvm::raw_ostream &os) const;
|
|
|
|
private:
|
|
friend class AvailabilityDomainOrIdentifier;
|
|
|
|
AvailabilityDomain copy(ASTContext &ctx) const;
|
|
};
|
|
|
|
inline void simple_display(llvm::raw_ostream &os,
|
|
const AvailabilityDomain &domain) {
|
|
domain.print(os);
|
|
}
|
|
|
|
/// A comparator that implements a stable, total ordering on
|
|
/// `AvailabilityDomain` that can be used for sorting in contexts where the
|
|
/// result must be stable and deterministic across compilations.
|
|
struct StableAvailabilityDomainComparator {
|
|
bool operator()(const AvailabilityDomain &lhs,
|
|
const AvailabilityDomain &rhs) const;
|
|
};
|
|
|
|
/// Represents an availability domain that has been defined in a module.
|
|
class CustomAvailabilityDomain : public llvm::FoldingSetNode {
|
|
public:
|
|
enum class Kind {
|
|
/// A domain that is known to be enabled at compile time.
|
|
Enabled,
|
|
/// A domain that is known to be disabled at compile time.
|
|
Disabled,
|
|
/// A domain with an enablement state that must be queried at runtime.
|
|
Dynamic,
|
|
};
|
|
|
|
private:
|
|
Identifier name;
|
|
Kind kind;
|
|
ModuleDecl *mod;
|
|
|
|
CustomAvailabilityDomain(Identifier name, ModuleDecl *mod, Kind kind);
|
|
|
|
public:
|
|
static const CustomAvailabilityDomain *get(StringRef name, ModuleDecl *mod,
|
|
Kind kind, const ASTContext &ctx);
|
|
|
|
Identifier getName() const { return name; }
|
|
Kind getKind() const { return kind; }
|
|
ModuleDecl *getModule() const { return mod; }
|
|
|
|
/// Uniquing for `ASTContext`.
|
|
static void Profile(llvm::FoldingSetNodeID &ID, Identifier name,
|
|
ModuleDecl *mod, Kind kind);
|
|
|
|
void Profile(llvm::FoldingSetNodeID &ID) const {
|
|
Profile(ID, name, mod, kind);
|
|
}
|
|
};
|
|
|
|
/// Represents either a resolved availability domain or an identifier written
|
|
/// in source that has not yet been resolved to a domain.
|
|
class AvailabilityDomainOrIdentifier {
|
|
friend struct llvm::PointerLikeTypeTraits<AvailabilityDomainOrIdentifier>;
|
|
|
|
using DomainOrIdentifier = llvm::PointerUnion<AvailabilityDomain, Identifier>;
|
|
|
|
/// Stores an extra bit representing whether the domain has been resolved.
|
|
using Storage = llvm::PointerIntPair<DomainOrIdentifier, 1, bool>;
|
|
Storage storage;
|
|
|
|
AvailabilityDomainOrIdentifier(Storage storage) : storage(storage) {}
|
|
|
|
std::optional<AvailabilityDomain>
|
|
lookUpInDeclContext(SourceLoc loc, const DeclContext *declContext) const;
|
|
|
|
void setResolved(std::optional<AvailabilityDomain> domain) {
|
|
if (domain)
|
|
storage.setPointer(*domain);
|
|
storage.setInt(true);
|
|
}
|
|
|
|
public:
|
|
AvailabilityDomainOrIdentifier(Identifier identifier)
|
|
: storage(identifier) {};
|
|
AvailabilityDomainOrIdentifier(AvailabilityDomain domain)
|
|
: storage(domain) {};
|
|
|
|
bool isDomain() const {
|
|
return storage.getPointer().is<AvailabilityDomain>();
|
|
}
|
|
bool isIdentifier() const { return storage.getPointer().is<Identifier>(); }
|
|
|
|
/// Overwrites the existing domain or identifier with the given domain.
|
|
void setDomain(AvailabilityDomain domain) { storage = Storage(domain); }
|
|
|
|
/// Returns the resolved domain, or `std::nullopt` if there isn't one.
|
|
std::optional<AvailabilityDomain> getAsDomain() const {
|
|
if (isDomain())
|
|
return storage.getPointer().get<AvailabilityDomain>();
|
|
return std::nullopt;
|
|
}
|
|
|
|
/// Returns the unresolved identifier, or `std::nullopt` if the domain has
|
|
/// been resolved.
|
|
std::optional<Identifier> getAsIdentifier() const {
|
|
if (isIdentifier())
|
|
return storage.getPointer().get<Identifier>();
|
|
return std::nullopt;
|
|
}
|
|
|
|
/// Returns true if either a resolved domain is available or if the attempt
|
|
/// to look up the domain from the identifier was unsuccessful.
|
|
bool isResolved() const { return storage.getInt() || isDomain(); }
|
|
|
|
std::optional<AvailabilityDomain>
|
|
resolveInDeclContext(SourceLoc loc, const DeclContext *declContext) {
|
|
// Return the domain directly if already resolved.
|
|
if (isResolved())
|
|
return getAsDomain();
|
|
|
|
// Look up the domain and cache the result.
|
|
auto result = lookUpInDeclContext(loc, declContext);
|
|
setResolved(result);
|
|
return result;
|
|
}
|
|
|
|
/// Creates a new `AvailabilityDomainOrIdentifier`, defensively copying
|
|
/// members of the original into the given `ASTContext` in case it is
|
|
/// different than the context that the original was created for.
|
|
AvailabilityDomainOrIdentifier copy(ASTContext &ctx) const;
|
|
|
|
static AvailabilityDomainOrIdentifier fromOpaque(void *opaque) {
|
|
return AvailabilityDomainOrIdentifier(Storage::getFromOpaqueValue(opaque));
|
|
}
|
|
|
|
void *getOpaqueValue() const { return storage.getOpaqueValue(); }
|
|
|
|
void print(llvm::raw_ostream &os) const;
|
|
};
|
|
|
|
} // end namespace swift
|
|
|
|
namespace llvm {
|
|
using swift::AvailabilityDomain;
|
|
using swift::AvailabilityDomainOrIdentifier;
|
|
|
|
// An AvailabilityDomain is "pointer like".
|
|
template <typename T>
|
|
struct PointerLikeTypeTraits;
|
|
template <>
|
|
struct PointerLikeTypeTraits<swift::AvailabilityDomain> {
|
|
public:
|
|
static inline void *getAsVoidPointer(AvailabilityDomain domain) {
|
|
return domain.storage.getOpaqueValue();
|
|
}
|
|
static inline swift::AvailabilityDomain getFromVoidPointer(void *P) {
|
|
return AvailabilityDomain::fromOpaque(P);
|
|
}
|
|
enum {
|
|
NumLowBitsAvailable =
|
|
PointerLikeTypeTraits<AvailabilityDomain::Storage>::NumLowBitsAvailable
|
|
};
|
|
};
|
|
|
|
template <>
|
|
struct DenseMapInfo<AvailabilityDomain> {
|
|
static inline AvailabilityDomain getEmptyKey() {
|
|
return DenseMapInfo<AvailabilityDomain::Storage>::getEmptyKey();
|
|
}
|
|
static inline AvailabilityDomain getTombstoneKey() {
|
|
return DenseMapInfo<AvailabilityDomain::Storage>::getTombstoneKey();
|
|
}
|
|
static inline unsigned getHashValue(AvailabilityDomain domain) {
|
|
return DenseMapInfo<AvailabilityDomain::Storage>::getHashValue(
|
|
domain.storage);
|
|
}
|
|
static bool isEqual(const AvailabilityDomain LHS,
|
|
const AvailabilityDomain RHS) {
|
|
return LHS == RHS;
|
|
}
|
|
};
|
|
|
|
// An AvailabilityDomainOrIdentifier is "pointer like".
|
|
template <typename T>
|
|
struct PointerLikeTypeTraits;
|
|
template <>
|
|
struct PointerLikeTypeTraits<swift::AvailabilityDomainOrIdentifier> {
|
|
public:
|
|
static inline void *getAsVoidPointer(AvailabilityDomainOrIdentifier value) {
|
|
return value.storage.getOpaqueValue();
|
|
}
|
|
static inline swift::AvailabilityDomainOrIdentifier
|
|
getFromVoidPointer(void *P) {
|
|
return AvailabilityDomainOrIdentifier::fromOpaque(P);
|
|
}
|
|
enum {
|
|
NumLowBitsAvailable = PointerLikeTypeTraits<
|
|
AvailabilityDomainOrIdentifier::Storage>::NumLowBitsAvailable
|
|
};
|
|
};
|
|
|
|
} // end namespace llvm
|
|
|
|
#endif
|