mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
1455 lines
52 KiB
C++
1455 lines
52 KiB
C++
//===--- ExtInfo.h - Extended information for function types ----*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2020 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Defines the ASTExtInfo and SILExtInfo classes, which are used to store
|
|
// the calling convention and related information for function types in the AST
|
|
// and SIL respectively. These types are lightweight and immutable; they are
|
|
// constructed using builder-pattern style APIs to enforce invariants.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_EXTINFO_H
|
|
#define SWIFT_EXTINFO_H
|
|
|
|
#include "swift/AST/AutoDiff.h"
|
|
#include "swift/AST/LifetimeDependence.h"
|
|
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <optional>
|
|
|
|
#include <utility>
|
|
|
|
namespace clang {
|
|
class Type;
|
|
class ASTContext;
|
|
} // namespace clang
|
|
|
|
namespace swift {
|
|
class AnyFunctionType;
|
|
class ASTExtInfo;
|
|
class ASTExtInfoBuilder;
|
|
class ClangModuleLoader;
|
|
class FunctionType;
|
|
class SILExtInfo;
|
|
class SILExtInfoBuilder;
|
|
class SILFunctionType;
|
|
enum class SILFunctionTypeRepresentation : uint8_t;
|
|
} // namespace swift
|
|
|
|
namespace swift {
|
|
|
|
/// The formal isolation of a function type.
|
|
class FunctionTypeIsolation {
|
|
public:
|
|
enum class Kind : uint8_t {
|
|
/// The function is not isolated.
|
|
NonIsolated,
|
|
|
|
/// The function is isolated to a global actor.
|
|
GlobalActor,
|
|
|
|
/// The function has an isolated parameter; which one is indicated in
|
|
/// the parameter list.
|
|
Parameter,
|
|
|
|
/// The function's isolation is statically erased with @isolated(any).
|
|
Erased,
|
|
|
|
/// Inherits isolation from the caller. This is only applicable
|
|
/// to asynchronous function types.
|
|
///
|
|
/// NOTE: The difference in between NonIsolatedNonsending and
|
|
/// NonIsolated is that NonIsolatedNonsending is a strictly
|
|
/// weaker form of nonisolation. While both in their bodies cannot
|
|
/// access isolated state directly, NonIsolatedNonsending functions
|
|
/// /are/ allowed to access state isolated to their caller via
|
|
/// function arguments since we know that the callee will stay
|
|
/// in the caller's isolation domain. In contrast, NonIsolated
|
|
/// is strongly nonisolated and is not allowed to access /any/
|
|
/// isolated state (even via function parameters) since it is
|
|
/// considered safe to run on /any/ actor.
|
|
NonIsolatedNonsending,
|
|
};
|
|
|
|
static constexpr size_t NumBits = 3; // future-proof this slightly
|
|
static constexpr size_t Mask = (1 << NumBits) - 1;
|
|
|
|
private:
|
|
llvm::PointerIntPair<Type, NumBits, Kind> value;
|
|
|
|
FunctionTypeIsolation(Kind kind, Type type = Type()) : value(type, kind) {}
|
|
|
|
public:
|
|
static FunctionTypeIsolation forNonIsolated() {
|
|
return { Kind::NonIsolated };
|
|
}
|
|
static FunctionTypeIsolation forGlobalActor(Type type) {
|
|
assert(type && "creating global actor isolation without an actor type");
|
|
return { Kind::GlobalActor, type };
|
|
}
|
|
static FunctionTypeIsolation forParameter() {
|
|
return { Kind::Parameter };
|
|
}
|
|
static FunctionTypeIsolation forErased() {
|
|
return { Kind::Erased };
|
|
}
|
|
static FunctionTypeIsolation forNonIsolatedCaller() {
|
|
return { Kind::NonIsolatedNonsending };
|
|
}
|
|
|
|
Kind getKind() const { return value.getInt(); }
|
|
bool isNonIsolated() const {
|
|
return getKind() == Kind::NonIsolated;
|
|
}
|
|
bool isGlobalActor() const {
|
|
return getKind() == Kind::GlobalActor;
|
|
}
|
|
Type getGlobalActorType() const {
|
|
assert(getKind() == Kind::GlobalActor);
|
|
return value.getPointer();
|
|
}
|
|
bool isParameter() const {
|
|
return getKind() == Kind::Parameter;
|
|
}
|
|
bool isErased() const {
|
|
return getKind() == Kind::Erased;
|
|
}
|
|
bool isNonIsolatedCaller() const {
|
|
return getKind() == Kind::NonIsolatedNonsending;
|
|
}
|
|
|
|
/// Two function type isolations are equal if they have the same kind and
|
|
/// (when applicable) the same global actor types.
|
|
///
|
|
/// Exact equality is the right thing to ask about when deciding whether
|
|
/// two isolations are the same statically, because we have to treat
|
|
/// different specializations of the same generic global actor type
|
|
/// as potentially different isolations. (Of course, you must be comparing
|
|
/// types that have been mapped into the same context.)
|
|
///
|
|
/// Exact equality is *not* the right thing to ask about when deciding
|
|
/// whether two isolations might be the same dynamically, because two
|
|
/// different specializations of the same generic global actor type
|
|
/// could absolutely end up being the same in concrete specialization.
|
|
bool operator==(FunctionTypeIsolation other) const {
|
|
return value == other.value;
|
|
}
|
|
bool operator!=(FunctionTypeIsolation other) const {
|
|
return value != other.value;
|
|
}
|
|
|
|
// The opaque accessors below are just for the benefit of ExtInfoBuilder,
|
|
// which finds it convenient to break down the type separately. Normal
|
|
// clients should use the accessors above.
|
|
|
|
Type getOpaqueType() const {
|
|
return value.getPointer();
|
|
}
|
|
|
|
static FunctionTypeIsolation fromOpaqueValues(Kind kind, Type type) {
|
|
return FunctionTypeIsolation(kind, type);
|
|
}
|
|
};
|
|
|
|
/// For now, the kinds of isolation we carry on SIL function types
|
|
/// are significantly reduced compared to AST function types.
|
|
/// Isolation is not part of the SIL function model after the
|
|
/// early portion of the pipeline.
|
|
class SILFunctionTypeIsolation {
|
|
public:
|
|
enum Kind : uint8_t {
|
|
/// We don't normally record isolation in SIL function types,
|
|
/// so the empty case here is "unknown".
|
|
Unknown,
|
|
|
|
/// The isolation of the function has been statically erased.
|
|
/// This corresponds to @isolated(any).
|
|
Erased,
|
|
};
|
|
|
|
static constexpr size_t NumBits = 3; // future-proof this slightly
|
|
static constexpr uintptr_t Mask = (uintptr_t(1) << NumBits) - 1;
|
|
|
|
private:
|
|
// We do not use a pointer int pair, since it is not a literal type.
|
|
llvm::PointerIntPair<CanType, NumBits, Kind> value;
|
|
|
|
SILFunctionTypeIsolation(Kind kind, CanType type = CanType())
|
|
: value(type, kind) {}
|
|
|
|
public:
|
|
static SILFunctionTypeIsolation forUnknown() { return {Kind::Unknown}; }
|
|
|
|
static SILFunctionTypeIsolation forErased() { return {Kind::Erased}; }
|
|
|
|
bool operator==(const SILFunctionTypeIsolation &other) const {
|
|
if (getKind() != other.getKind())
|
|
return false;
|
|
|
|
switch (getKind()) {
|
|
case Kind::Unknown:
|
|
case Kind::Erased:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
Kind getKind() const { return value.getInt(); }
|
|
|
|
bool isUnknown() const { return getKind() == Kind::Unknown; }
|
|
bool isErased() const { return getKind() == Kind::Erased; }
|
|
|
|
// The opaque accessors below are just for the benefit of SILExtInfoBuilder,
|
|
// which finds it convenient to break down the type separately. Normal
|
|
// clients should use the accessors above.
|
|
|
|
CanType getOpaqueType() const { return value.getPointer(); }
|
|
|
|
static SILFunctionTypeIsolation fromOpaqueValues(Kind kind, CanType type) {
|
|
return SILFunctionTypeIsolation(kind, type);
|
|
}
|
|
};
|
|
|
|
// MARK: - ClangTypeInfo
|
|
/// Wrapper class for storing a clang::Type in an (AST|SIL)ExtInfo.
|
|
class ClangTypeInfo {
|
|
friend AnyFunctionType;
|
|
friend FunctionType;
|
|
friend SILFunctionType;
|
|
friend ASTExtInfoBuilder;
|
|
friend SILExtInfoBuilder;
|
|
|
|
// [NOTE: ClangTypeInfo-contents]
|
|
// We preserve a full clang::Type *, not a clang::FunctionType * as:
|
|
// 1. We need to keep sugar in case we need to present an error to the user
|
|
// (for AnyFunctionType).
|
|
// 2. The actual type being stored is [ignoring sugar] either a
|
|
// clang::PointerType, a clang::BlockPointerType, or a
|
|
// clang::ReferenceType which points to a clang::FunctionType.
|
|
//
|
|
// When used as a part of SILFunctionType, the type is canonical.
|
|
const clang::Type *type;
|
|
|
|
constexpr ClangTypeInfo() : type(nullptr) {}
|
|
constexpr ClangTypeInfo(const clang::Type *type) : type(type) {}
|
|
|
|
friend bool operator==(ClangTypeInfo lhs, ClangTypeInfo rhs);
|
|
friend bool operator!=(ClangTypeInfo lhs, ClangTypeInfo rhs);
|
|
ClangTypeInfo getCanonical() const;
|
|
|
|
public:
|
|
constexpr const clang::Type *getType() const { return type; }
|
|
|
|
constexpr bool empty() const { return !type; }
|
|
|
|
/// Use the ClangModuleLoader to print the Clang type as a string.
|
|
void printType(ClangModuleLoader *cml, llvm::raw_ostream &os) const;
|
|
|
|
void dump(llvm::raw_ostream &os, const clang::ASTContext &ctx) const;
|
|
};
|
|
|
|
// MARK: - UnexpectedClangTypeError
|
|
/// Potential errors when trying to store a Clang type in an ExtInfo.
|
|
struct UnexpectedClangTypeError {
|
|
enum class Kind {
|
|
NullForCOrBlock,
|
|
NonnullForNonCOrBlock,
|
|
NotBlockPointer,
|
|
NotFunctionPointerOrReference,
|
|
NonCanonical,
|
|
};
|
|
const Kind errorKind;
|
|
const clang::Type *type;
|
|
|
|
static std::optional<UnexpectedClangTypeError>
|
|
checkClangType(SILFunctionTypeRepresentation fnRep, const clang::Type *type,
|
|
bool expectNonnullForCOrBlock, bool expectCanonical);
|
|
|
|
void dump();
|
|
};
|
|
|
|
// MARK: - FunctionTypeRepresentation
|
|
/// The representation form of a function.
|
|
enum class FunctionTypeRepresentation : uint8_t {
|
|
/// A "thick" function that carries a context pointer to reference captured
|
|
/// state. The default native function representation.
|
|
Swift = 0,
|
|
|
|
/// A thick function that is represented as an Objective-C block.
|
|
Block,
|
|
|
|
/// A "thin" function that needs no context.
|
|
Thin,
|
|
|
|
/// A C function pointer (or reference), which is thin and also uses the C
|
|
/// calling convention.
|
|
CFunctionPointer,
|
|
|
|
/// The value of the greatest AST function representation.
|
|
Last = CFunctionPointer,
|
|
};
|
|
|
|
// MARK: - SILFunctionTypeRepresentation
|
|
|
|
/// The representation form of a SIL function.
|
|
///
|
|
/// This is a superset of FunctionTypeRepresentation. The common representations
|
|
/// must share an enum value.
|
|
///
|
|
/// TODO: The overlap of SILFunctionTypeRepresentation and
|
|
/// FunctionTypeRepresentation is a total hack necessitated by the way SIL
|
|
/// TypeLowering is currently written. We ought to refactor TypeLowering so that
|
|
/// it is not necessary to distinguish these cases.
|
|
enum class SILFunctionTypeRepresentation : uint8_t {
|
|
/// A freestanding thick function.
|
|
Thick = uint8_t(FunctionTypeRepresentation::Swift),
|
|
|
|
/// A thick function that is represented as an Objective-C block.
|
|
Block = uint8_t(FunctionTypeRepresentation::Block),
|
|
|
|
/// A freestanding thin function that needs no context.
|
|
Thin = uint8_t(FunctionTypeRepresentation::Thin),
|
|
|
|
/// A C function pointer, which is thin and also uses the C calling
|
|
/// convention.
|
|
CFunctionPointer = uint8_t(FunctionTypeRepresentation::CFunctionPointer),
|
|
|
|
/// The value of the greatest AST function representation.
|
|
LastAST = CFunctionPointer,
|
|
|
|
/// The value of the least SIL-only function representation.
|
|
FirstSIL = 8,
|
|
|
|
/// A Swift instance method.
|
|
Method = FirstSIL,
|
|
|
|
/// An Objective-C method.
|
|
ObjCMethod,
|
|
|
|
/// A Swift protocol witness.
|
|
WitnessMethod,
|
|
|
|
/// A closure invocation function that has not been bound to a context.
|
|
Closure,
|
|
|
|
/// A C++ method that takes a "this" argument (not a static C++ method or
|
|
/// constructor). Except for
|
|
/// handling the "this" argument, has the same behavior as "CFunctionPointer".
|
|
CXXMethod,
|
|
|
|
/// A KeyPath accessor function, which is thin and also uses the variadic
|
|
/// length generic components serialization in trailing buffer.
|
|
/// Each representation has a different convention for which parameters
|
|
/// have serialized generic type info.
|
|
KeyPathAccessorGetter,
|
|
KeyPathAccessorSetter,
|
|
KeyPathAccessorEquals,
|
|
KeyPathAccessorHash,
|
|
};
|
|
|
|
/// Returns true if the function with this convention doesn't carry a context.
|
|
constexpr bool
|
|
isThinRepresentation(FunctionTypeRepresentation rep) {
|
|
switch (rep) {
|
|
case FunctionTypeRepresentation::Swift:
|
|
case FunctionTypeRepresentation::Block:
|
|
return false;
|
|
case FunctionTypeRepresentation::Thin:
|
|
case FunctionTypeRepresentation::CFunctionPointer:
|
|
return true;
|
|
}
|
|
llvm_unreachable("Unhandled FunctionTypeRepresentation in switch.");
|
|
}
|
|
|
|
/// Returns true if the function with this convention doesn't carry a context.
|
|
constexpr bool
|
|
isThinRepresentation(SILFunctionTypeRepresentation rep) {
|
|
switch (rep) {
|
|
case SILFunctionTypeRepresentation::Thick:
|
|
case SILFunctionTypeRepresentation::Block:
|
|
return false;
|
|
case SILFunctionTypeRepresentation::Thin:
|
|
case SILFunctionTypeRepresentation::Method:
|
|
case SILFunctionTypeRepresentation::ObjCMethod:
|
|
case SILFunctionTypeRepresentation::WitnessMethod:
|
|
case SILFunctionTypeRepresentation::CFunctionPointer:
|
|
case SILFunctionTypeRepresentation::Closure:
|
|
case SILFunctionTypeRepresentation::CXXMethod:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorGetter:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorSetter:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorEquals:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorHash:
|
|
return true;
|
|
}
|
|
llvm_unreachable("Unhandled SILFunctionTypeRepresentation in switch.");
|
|
}
|
|
|
|
/// Returns true if the function with this convention carries a context.
|
|
template <typename Repr>
|
|
constexpr bool
|
|
isThickRepresentation(Repr repr) {
|
|
return !isThinRepresentation(repr);
|
|
}
|
|
|
|
/// Returns true if the function with this convention receives generic arguments
|
|
/// from KeyPath argument buffer.
|
|
constexpr bool
|
|
isKeyPathAccessorRepresentation(SILFunctionTypeRepresentation rep) {
|
|
switch (rep) {
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorGetter:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorSetter:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorEquals:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorHash:
|
|
return true;
|
|
case SILFunctionTypeRepresentation::Thick:
|
|
case SILFunctionTypeRepresentation::Block:
|
|
case SILFunctionTypeRepresentation::Thin:
|
|
case SILFunctionTypeRepresentation::Method:
|
|
case SILFunctionTypeRepresentation::ObjCMethod:
|
|
case SILFunctionTypeRepresentation::WitnessMethod:
|
|
case SILFunctionTypeRepresentation::CFunctionPointer:
|
|
case SILFunctionTypeRepresentation::Closure:
|
|
case SILFunctionTypeRepresentation::CXXMethod:
|
|
return false;
|
|
}
|
|
llvm_unreachable("Unhandled SILFunctionTypeRepresentation in switch.");
|
|
}
|
|
|
|
|
|
constexpr SILFunctionTypeRepresentation
|
|
convertRepresentation(FunctionTypeRepresentation rep) {
|
|
switch (rep) {
|
|
case FunctionTypeRepresentation::Swift:
|
|
return SILFunctionTypeRepresentation::Thick;
|
|
case FunctionTypeRepresentation::Block:
|
|
return SILFunctionTypeRepresentation::Block;
|
|
case FunctionTypeRepresentation::Thin:
|
|
return SILFunctionTypeRepresentation::Thin;
|
|
case FunctionTypeRepresentation::CFunctionPointer:
|
|
return SILFunctionTypeRepresentation::CFunctionPointer;
|
|
}
|
|
llvm_unreachable("Unhandled FunctionTypeRepresentation!");
|
|
}
|
|
|
|
inline std::optional<FunctionTypeRepresentation>
|
|
convertRepresentation(SILFunctionTypeRepresentation rep) {
|
|
switch (rep) {
|
|
case SILFunctionTypeRepresentation::Thick:
|
|
return {FunctionTypeRepresentation::Swift};
|
|
case SILFunctionTypeRepresentation::Block:
|
|
return {FunctionTypeRepresentation::Block};
|
|
case SILFunctionTypeRepresentation::Thin:
|
|
return {FunctionTypeRepresentation::Thin};
|
|
case SILFunctionTypeRepresentation::CXXMethod:
|
|
case SILFunctionTypeRepresentation::CFunctionPointer:
|
|
return {FunctionTypeRepresentation::CFunctionPointer};
|
|
case SILFunctionTypeRepresentation::Method:
|
|
case SILFunctionTypeRepresentation::ObjCMethod:
|
|
case SILFunctionTypeRepresentation::WitnessMethod:
|
|
case SILFunctionTypeRepresentation::Closure:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorGetter:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorSetter:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorEquals:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorHash:
|
|
return std::nullopt;
|
|
}
|
|
llvm_unreachable("Unhandled SILFunctionTypeRepresentation!");
|
|
}
|
|
|
|
/// Can this calling convention result in a function being called indirectly
|
|
/// through the runtime.
|
|
constexpr bool canBeCalledIndirectly(SILFunctionTypeRepresentation rep) {
|
|
switch (rep) {
|
|
case SILFunctionTypeRepresentation::Thick:
|
|
case SILFunctionTypeRepresentation::Thin:
|
|
case SILFunctionTypeRepresentation::CFunctionPointer:
|
|
case SILFunctionTypeRepresentation::Block:
|
|
case SILFunctionTypeRepresentation::Closure:
|
|
case SILFunctionTypeRepresentation::CXXMethod:
|
|
return false;
|
|
case SILFunctionTypeRepresentation::ObjCMethod:
|
|
case SILFunctionTypeRepresentation::Method:
|
|
case SILFunctionTypeRepresentation::WitnessMethod:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorGetter:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorSetter:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorEquals:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorHash:
|
|
return true;
|
|
}
|
|
|
|
llvm_unreachable("Unhandled SILFunctionTypeRepresentation in switch.");
|
|
}
|
|
|
|
template <typename Repr> constexpr bool shouldStoreClangType(Repr repr) {
|
|
static_assert(std::is_same<Repr, FunctionTypeRepresentation>::value ||
|
|
std::is_same<Repr, SILFunctionTypeRepresentation>::value,
|
|
"Expected a Representation type as the argument type.");
|
|
switch (static_cast<SILFunctionTypeRepresentation>(repr)) {
|
|
case SILFunctionTypeRepresentation::CFunctionPointer:
|
|
case SILFunctionTypeRepresentation::Block:
|
|
case SILFunctionTypeRepresentation::CXXMethod:
|
|
return true;
|
|
case SILFunctionTypeRepresentation::ObjCMethod:
|
|
case SILFunctionTypeRepresentation::Thick:
|
|
case SILFunctionTypeRepresentation::Thin:
|
|
case SILFunctionTypeRepresentation::Method:
|
|
case SILFunctionTypeRepresentation::WitnessMethod:
|
|
case SILFunctionTypeRepresentation::Closure:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorGetter:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorSetter:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorEquals:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorHash:
|
|
return false;
|
|
}
|
|
llvm_unreachable("Unhandled SILFunctionTypeRepresentation.");
|
|
}
|
|
|
|
// MARK: - ASTExtInfoBuilder
|
|
/// A builder type for creating an \c ASTExtInfo.
|
|
///
|
|
/// The main API public includes the \c withXYZ and \p build() methods.
|
|
class ASTExtInfoBuilder {
|
|
friend AnyFunctionType;
|
|
friend ASTExtInfo;
|
|
|
|
// If bits are added or removed, then TypeBase::NumAFTExtInfoBits
|
|
// and NumMaskBits must be updated, and they must match.
|
|
//
|
|
// |representation|noEscape|concurrent|async|throws|isolation|differentiability| SendingResult |
|
|
// | 0 .. 3 | 4 | 5 | 6 | 7 | 8 .. 10 | 11 .. 13 | 14 |
|
|
//
|
|
enum : unsigned {
|
|
RepresentationMask = 0xF << 0,
|
|
NoEscapeMask = 1 << 4,
|
|
SendableMask = 1 << 5,
|
|
AsyncMask = 1 << 6,
|
|
ThrowsMask = 1 << 7,
|
|
IsolationMaskOffset = 8,
|
|
IsolationMask = 0x7 << IsolationMaskOffset,
|
|
DifferentiabilityMaskOffset = 11,
|
|
DifferentiabilityMask = 0x7 << DifferentiabilityMaskOffset,
|
|
SendingResultMask = 1 << 14,
|
|
InOutResultMask = 1 << 15,
|
|
NumMaskBits = 16
|
|
};
|
|
|
|
static_assert(FunctionTypeIsolation::Mask == 0x7, "update mask manually");
|
|
|
|
unsigned bits; // Naturally sized for speed.
|
|
|
|
ClangTypeInfo clangTypeInfo;
|
|
|
|
Type globalActor;
|
|
Type thrownError;
|
|
|
|
ArrayRef<LifetimeDependenceInfo> lifetimeDependencies;
|
|
|
|
using Representation = FunctionTypeRepresentation;
|
|
|
|
ASTExtInfoBuilder(unsigned bits, ClangTypeInfo clangTypeInfo,
|
|
Type globalActor, Type thrownError,
|
|
ArrayRef<LifetimeDependenceInfo> lifetimeDependencies)
|
|
: bits(bits), clangTypeInfo(clangTypeInfo), globalActor(globalActor),
|
|
thrownError(thrownError), lifetimeDependencies(lifetimeDependencies) {
|
|
assert(isThrowing() || !thrownError);
|
|
assert(hasGlobalActorFromBits(bits) == !globalActor.isNull());
|
|
}
|
|
|
|
public:
|
|
/// An ExtInfoBuilder for a typical Swift function: @convention(swift),
|
|
/// @escaping, non-throwing, non-differentiable.
|
|
ASTExtInfoBuilder()
|
|
: ASTExtInfoBuilder(Representation::Swift, false, false, Type(),
|
|
DifferentiabilityKind::NonDifferentiable, nullptr,
|
|
FunctionTypeIsolation::forNonIsolated(),
|
|
{} /* LifetimeDependenceInfo */,
|
|
false /*sendingResult*/) {}
|
|
|
|
// Constructor for polymorphic type.
|
|
ASTExtInfoBuilder(Representation rep, bool throws, Type thrownError)
|
|
: ASTExtInfoBuilder(rep, false, throws, thrownError,
|
|
DifferentiabilityKind::NonDifferentiable, nullptr,
|
|
FunctionTypeIsolation::forNonIsolated(),
|
|
{} /* LifetimeDependenceInfo */,
|
|
false /*sendingResult*/) {}
|
|
|
|
// Constructor with no defaults.
|
|
ASTExtInfoBuilder(Representation rep, bool isNoEscape, bool throws,
|
|
Type thrownError, DifferentiabilityKind diffKind,
|
|
const clang::Type *type, FunctionTypeIsolation isolation,
|
|
ArrayRef<LifetimeDependenceInfo> lifetimeDependencies,
|
|
bool sendingResult)
|
|
: ASTExtInfoBuilder(
|
|
((unsigned)rep) | (isNoEscape ? NoEscapeMask : 0) |
|
|
(throws ? ThrowsMask : 0) |
|
|
(((unsigned)diffKind << DifferentiabilityMaskOffset) &
|
|
DifferentiabilityMask) |
|
|
(unsigned(isolation.getKind()) << IsolationMaskOffset) |
|
|
(sendingResult ? SendingResultMask : 0),
|
|
ClangTypeInfo(type), isolation.getOpaqueType(), thrownError,
|
|
lifetimeDependencies) {}
|
|
|
|
void checkInvariants() const;
|
|
|
|
/// Check if \c this is well-formed and create an ExtInfo.
|
|
ASTExtInfo build() const;
|
|
|
|
constexpr Representation getRepresentation() const {
|
|
unsigned rawRep = bits & RepresentationMask;
|
|
return Representation(rawRep);
|
|
}
|
|
|
|
constexpr bool isNoEscape() const { return bits & NoEscapeMask; }
|
|
|
|
constexpr bool isSendable() const { return bits & SendableMask; }
|
|
|
|
constexpr bool isAsync() const { return bits & AsyncMask; }
|
|
|
|
constexpr bool isThrowing() const { return bits & ThrowsMask; }
|
|
|
|
constexpr bool hasSendingResult() const { return bits & SendingResultMask; }
|
|
|
|
constexpr DifferentiabilityKind getDifferentiabilityKind() const {
|
|
return DifferentiabilityKind((bits & DifferentiabilityMask) >>
|
|
DifferentiabilityMaskOffset);
|
|
}
|
|
|
|
constexpr bool isDifferentiable() const {
|
|
return getDifferentiabilityKind() >
|
|
DifferentiabilityKind::NonDifferentiable;
|
|
}
|
|
|
|
ClangTypeInfo getClangTypeInfo() const { return clangTypeInfo; }
|
|
|
|
constexpr SILFunctionTypeRepresentation getSILRepresentation() const {
|
|
unsigned rawRep = bits & RepresentationMask;
|
|
return SILFunctionTypeRepresentation(rawRep);
|
|
}
|
|
|
|
Type getGlobalActor() const { return globalActor; }
|
|
Type getThrownError() const { return thrownError; }
|
|
|
|
ArrayRef<LifetimeDependenceInfo> getLifetimeDependencies() const {
|
|
return lifetimeDependencies;
|
|
}
|
|
|
|
FunctionTypeIsolation::Kind getIsolationKind() const {
|
|
return getIsolationKindFromBits(bits);
|
|
}
|
|
static FunctionTypeIsolation::Kind getIsolationKindFromBits(unsigned bits) {
|
|
return FunctionTypeIsolation::Kind(
|
|
(bits & IsolationMask) >> IsolationMaskOffset);
|
|
}
|
|
bool isIsolationStaticallyErased() const {
|
|
return getIsolationKind() == FunctionTypeIsolation::Kind::Erased;
|
|
}
|
|
static bool hasGlobalActorFromBits(unsigned bits) {
|
|
return getIsolationKindFromBits(bits)
|
|
== FunctionTypeIsolation::Kind::GlobalActor;
|
|
}
|
|
|
|
FunctionTypeIsolation getIsolation() const {
|
|
return FunctionTypeIsolation::fromOpaqueValues(getIsolationKind(),
|
|
globalActor);
|
|
}
|
|
|
|
constexpr bool hasInOutResult() const { return bits & InOutResultMask; }
|
|
|
|
constexpr bool hasSelfParam() const {
|
|
switch (getSILRepresentation()) {
|
|
case SILFunctionTypeRepresentation::Thick:
|
|
case SILFunctionTypeRepresentation::Block:
|
|
case SILFunctionTypeRepresentation::Thin:
|
|
case SILFunctionTypeRepresentation::CFunctionPointer:
|
|
case SILFunctionTypeRepresentation::Closure:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorGetter:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorSetter:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorEquals:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorHash:
|
|
return false;
|
|
case SILFunctionTypeRepresentation::ObjCMethod:
|
|
case SILFunctionTypeRepresentation::Method:
|
|
case SILFunctionTypeRepresentation::WitnessMethod:
|
|
case SILFunctionTypeRepresentation::CXXMethod:
|
|
return true;
|
|
}
|
|
llvm_unreachable("Unhandled SILFunctionTypeRepresentation in switch.");
|
|
}
|
|
|
|
/// True if the function representation carries context.
|
|
constexpr bool hasContext() const {
|
|
return isThickRepresentation(getSILRepresentation());
|
|
}
|
|
|
|
// Note that we don't have setters. That is by design, use
|
|
// the following with methods instead of mutating these objects.
|
|
[[nodiscard]]
|
|
ASTExtInfoBuilder withRepresentation(Representation rep) const {
|
|
return ASTExtInfoBuilder((bits & ~RepresentationMask) | (unsigned)rep,
|
|
shouldStoreClangType(rep) ? clangTypeInfo
|
|
: ClangTypeInfo(),
|
|
globalActor, thrownError, lifetimeDependencies);
|
|
}
|
|
[[nodiscard]]
|
|
ASTExtInfoBuilder withNoEscape(bool noEscape = true) const {
|
|
return ASTExtInfoBuilder(
|
|
noEscape ? (bits | NoEscapeMask) : (bits & ~NoEscapeMask),
|
|
clangTypeInfo, globalActor, thrownError, lifetimeDependencies);
|
|
}
|
|
[[nodiscard]]
|
|
ASTExtInfoBuilder withSendable(bool concurrent = true) const {
|
|
return ASTExtInfoBuilder(
|
|
concurrent ? (bits | SendableMask) : (bits & ~SendableMask),
|
|
clangTypeInfo, globalActor, thrownError, lifetimeDependencies);
|
|
}
|
|
[[nodiscard]]
|
|
ASTExtInfoBuilder withAsync(bool async = true) const {
|
|
return ASTExtInfoBuilder(async ? (bits | AsyncMask) : (bits & ~AsyncMask),
|
|
clangTypeInfo, globalActor, thrownError,
|
|
lifetimeDependencies);
|
|
}
|
|
[[nodiscard]]
|
|
ASTExtInfoBuilder withThrows(bool throws, Type thrownError) const {
|
|
assert(throws || !thrownError);
|
|
return ASTExtInfoBuilder(
|
|
throws ? (bits | ThrowsMask) : (bits & ~ThrowsMask), clangTypeInfo,
|
|
globalActor, thrownError, lifetimeDependencies);
|
|
}
|
|
|
|
[[nodiscard]]
|
|
ASTExtInfoBuilder withThrows() const {
|
|
return withThrows(true, Type());
|
|
}
|
|
|
|
[[nodiscard]] ASTExtInfoBuilder withSendingResult(bool sending = true) const {
|
|
return ASTExtInfoBuilder(
|
|
sending ? (bits | SendingResultMask) : (bits & ~SendingResultMask),
|
|
clangTypeInfo, globalActor, thrownError, lifetimeDependencies);
|
|
}
|
|
|
|
[[nodiscard]]
|
|
ASTExtInfoBuilder
|
|
withDifferentiabilityKind(DifferentiabilityKind differentiability) const {
|
|
return ASTExtInfoBuilder(
|
|
(bits & ~DifferentiabilityMask) |
|
|
((unsigned)differentiability << DifferentiabilityMaskOffset),
|
|
clangTypeInfo, globalActor, thrownError, lifetimeDependencies);
|
|
}
|
|
[[nodiscard]]
|
|
ASTExtInfoBuilder withClangFunctionType(const clang::Type *type) const {
|
|
return ASTExtInfoBuilder(bits, ClangTypeInfo(type), globalActor,
|
|
thrownError, lifetimeDependencies);
|
|
}
|
|
|
|
/// Put a SIL representation in the ExtInfo.
|
|
///
|
|
/// SIL type lowering transiently generates AST function types with SIL
|
|
/// representations. However, they shouldn't persist in the AST, and
|
|
/// don't need to be parsed, printed, or serialized.
|
|
[[nodiscard]]
|
|
ASTExtInfoBuilder
|
|
withSILRepresentation(SILFunctionTypeRepresentation rep) const {
|
|
return ASTExtInfoBuilder((bits & ~RepresentationMask) | (unsigned)rep,
|
|
shouldStoreClangType(rep) ? clangTypeInfo
|
|
: ClangTypeInfo(),
|
|
globalActor, thrownError, lifetimeDependencies);
|
|
}
|
|
|
|
/// \p lifetimeDependencies should be arena allocated and not a temporary
|
|
/// Function types are allocated on the are arena and their ExtInfo should be
|
|
/// valid throughout their lifetime.
|
|
[[nodiscard]] ASTExtInfoBuilder withLifetimeDependencies(
|
|
llvm::ArrayRef<LifetimeDependenceInfo> lifetimeDependencies) const {
|
|
return ASTExtInfoBuilder(bits, clangTypeInfo, globalActor, thrownError,
|
|
lifetimeDependencies);
|
|
}
|
|
|
|
[[nodiscard]] ASTExtInfoBuilder withLifetimeDependencies(
|
|
SmallVectorImpl<LifetimeDependenceInfo> lifetimeDependencies) const =
|
|
delete;
|
|
|
|
[[nodiscard]]
|
|
ASTExtInfoBuilder withIsolation(FunctionTypeIsolation isolation) const {
|
|
return ASTExtInfoBuilder(
|
|
(bits & ~IsolationMask) |
|
|
(unsigned(isolation.getKind()) << IsolationMaskOffset),
|
|
clangTypeInfo, isolation.getOpaqueType(), thrownError,
|
|
lifetimeDependencies);
|
|
}
|
|
|
|
[[nodiscard]] ASTExtInfoBuilder withHasInOutResult() const {
|
|
return ASTExtInfoBuilder((bits | InOutResultMask), clangTypeInfo,
|
|
globalActor, thrownError, lifetimeDependencies);
|
|
}
|
|
|
|
void Profile(llvm::FoldingSetNodeID &ID) const {
|
|
ID.AddInteger(bits);
|
|
ID.AddPointer(clangTypeInfo.getType());
|
|
ID.AddPointer(globalActor.getPointer());
|
|
ID.AddPointer(thrownError.getPointer());
|
|
for (auto info : lifetimeDependencies) {
|
|
info.Profile(ID);
|
|
}
|
|
}
|
|
|
|
bool isEqualTo(ASTExtInfoBuilder other, bool useClangTypes) const {
|
|
return bits == other.bits &&
|
|
(useClangTypes ? (clangTypeInfo == other.clangTypeInfo) : true) &&
|
|
globalActor.getPointer() == other.globalActor.getPointer() &&
|
|
thrownError.getPointer() == other.thrownError.getPointer() &&
|
|
lifetimeDependencies == other.lifetimeDependencies;
|
|
}
|
|
}; // end ASTExtInfoBuilder
|
|
|
|
// MARK: - ASTExtInfo
|
|
/// Calling convention and related information for AnyFunctionType + subclasses.
|
|
///
|
|
/// New instances can be made from existing instances via \c ASTExtInfoBuilder,
|
|
/// typically using a code pattern like:
|
|
/// \code
|
|
/// extInfo.intoBuilder().withX(x).withY(y).build()
|
|
/// \endcode
|
|
class ASTExtInfo {
|
|
friend ASTExtInfoBuilder;
|
|
friend AnyFunctionType;
|
|
|
|
ASTExtInfoBuilder builder;
|
|
|
|
// Only for use by ASTExtInfoBuilder::build. Don't use it elsewhere!
|
|
ASTExtInfo(ASTExtInfoBuilder builder) : builder(builder) {}
|
|
|
|
ASTExtInfo(unsigned bits, ClangTypeInfo clangTypeInfo, Type globalActor,
|
|
Type thrownError,
|
|
llvm::ArrayRef<LifetimeDependenceInfo> lifetimeDependenceInfo)
|
|
: builder(bits, clangTypeInfo, globalActor, thrownError,
|
|
lifetimeDependenceInfo) {
|
|
builder.checkInvariants();
|
|
};
|
|
|
|
public:
|
|
/// An ExtInfo for a typical Swift function: @convention(swift), @escaping,
|
|
/// non-throwing, non-differentiable.
|
|
ASTExtInfo() : builder() { builder.checkInvariants(); };
|
|
|
|
/// Create a builder with the same state as \c this.
|
|
ASTExtInfoBuilder intoBuilder() const { return builder; }
|
|
|
|
private:
|
|
constexpr unsigned getBits() const { return builder.bits; }
|
|
|
|
public:
|
|
constexpr FunctionTypeRepresentation getRepresentation() const {
|
|
return builder.getRepresentation();
|
|
}
|
|
|
|
constexpr SILFunctionTypeRepresentation getSILRepresentation() const {
|
|
return builder.getSILRepresentation();
|
|
}
|
|
|
|
constexpr bool isNoEscape() const { return builder.isNoEscape(); }
|
|
|
|
constexpr bool isSendable() const { return builder.isSendable(); }
|
|
|
|
constexpr bool isAsync() const { return builder.isAsync(); }
|
|
|
|
constexpr bool isThrowing() const { return builder.isThrowing(); }
|
|
|
|
constexpr bool hasSendingResult() const { return builder.hasSendingResult(); }
|
|
|
|
constexpr DifferentiabilityKind getDifferentiabilityKind() const {
|
|
return builder.getDifferentiabilityKind();
|
|
}
|
|
|
|
constexpr bool isDifferentiable() const { return builder.isDifferentiable(); }
|
|
|
|
ClangTypeInfo getClangTypeInfo() const { return builder.getClangTypeInfo(); }
|
|
|
|
constexpr bool hasSelfParam() const { return builder.hasSelfParam(); }
|
|
|
|
constexpr bool hasContext() const { return builder.hasContext(); }
|
|
|
|
Type getGlobalActor() const { return builder.getGlobalActor(); }
|
|
Type getThrownError() const { return builder.getThrownError(); }
|
|
|
|
ArrayRef<LifetimeDependenceInfo> getLifetimeDependencies() const {
|
|
return builder.getLifetimeDependencies();
|
|
}
|
|
|
|
FunctionTypeIsolation getIsolation() const { return builder.getIsolation(); }
|
|
|
|
constexpr bool hasInOutResult() const { return builder.hasInOutResult(); }
|
|
|
|
/// Helper method for changing the representation.
|
|
///
|
|
/// Prefer using \c ASTExtInfoBuilder::withRepresentation for chaining.
|
|
[[nodiscard]]
|
|
ASTExtInfo withRepresentation(ASTExtInfoBuilder::Representation rep) const {
|
|
return builder.withRepresentation(rep).build();
|
|
}
|
|
|
|
/// Helper method for changing only the noEscape field.
|
|
///
|
|
/// Prefer using \c ASTExtInfoBuilder::withNoEscape for chaining.
|
|
[[nodiscard]]
|
|
ASTExtInfo withNoEscape(bool noEscape = true) const {
|
|
return builder.withNoEscape(noEscape).build();
|
|
}
|
|
|
|
/// Helper method for changing only the concurrent field.
|
|
///
|
|
/// Prefer using \c ASTExtInfoBuilder::withSendable for chaining.
|
|
[[nodiscard]]
|
|
ASTExtInfo withSendable(bool isSendable = true) const {
|
|
return builder.withSendable(isSendable).build();
|
|
}
|
|
|
|
/// Helper method for changing only the throws field.
|
|
///
|
|
/// Prefer using \c ASTExtInfoBuilder::withThrows for chaining.
|
|
[[nodiscard]]
|
|
ASTExtInfo withThrows(bool throws, Type thrownError) const {
|
|
return builder.withThrows(throws, thrownError).build();
|
|
}
|
|
|
|
/// Helper method for changing only the throws field.
|
|
///
|
|
/// Prefer using \c ASTExtInfoBuilder::withThrows for chaining.
|
|
[[nodiscard]]
|
|
ASTExtInfo withThrows() const {
|
|
return builder.withThrows(true, Type()).build();
|
|
}
|
|
|
|
/// Helper method for changing only the async field.
|
|
///
|
|
/// Prefer using \c ASTExtInfoBuilder::withAsync for chaining.
|
|
[[nodiscard]]
|
|
ASTExtInfo withAsync(bool async = true) const {
|
|
return builder.withAsync(async).build();
|
|
}
|
|
|
|
[[nodiscard]] ASTExtInfo withSendingResult(bool sending = true) const {
|
|
return builder.withSendingResult(sending).build();
|
|
}
|
|
|
|
[[nodiscard]]
|
|
ASTExtInfo withIsolation(FunctionTypeIsolation isolation) const {
|
|
return builder.withIsolation(isolation).build();
|
|
}
|
|
|
|
[[nodiscard]]
|
|
ASTExtInfo withoutIsolation() const {
|
|
return builder.withIsolation(FunctionTypeIsolation::forNonIsolated())
|
|
.build();
|
|
}
|
|
|
|
[[nodiscard]]
|
|
ASTExtInfo withGlobalActor(Type globalActor) const {
|
|
return builder.withIsolation(
|
|
FunctionTypeIsolation::forGlobalActor(globalActor))
|
|
.build();
|
|
}
|
|
|
|
/// \p lifetimeDependencies should be arena allocated and not a temporary
|
|
/// Function types are allocated on the are arena and their ExtInfo should be
|
|
/// valid throughout their lifetime.
|
|
[[nodiscard]] ASTExtInfo withLifetimeDependencies(
|
|
ArrayRef<LifetimeDependenceInfo> lifetimeDependencies) const {
|
|
return builder.withLifetimeDependencies(lifetimeDependencies).build();
|
|
}
|
|
|
|
[[nodiscard]] ASTExtInfo withLifetimeDependencies(
|
|
SmallVectorImpl<LifetimeDependenceInfo> lifetimeDependencies) const =
|
|
delete;
|
|
|
|
void Profile(llvm::FoldingSetNodeID &ID) const { builder.Profile(ID); }
|
|
|
|
bool isEqualTo(ASTExtInfo other, bool useClangTypes) const {
|
|
return builder.isEqualTo(other.builder, useClangTypes);
|
|
}
|
|
}; // end ASTExtInfo
|
|
|
|
// MARK: - SILFunctionLanguage
|
|
|
|
/// A language-level calling convention.
|
|
enum class SILFunctionLanguage : uint8_t {
|
|
/// A variation of the Swift calling convention.
|
|
Swift = 0,
|
|
|
|
/// A variation of the C calling convention.
|
|
C,
|
|
};
|
|
|
|
/// Map a SIL function representation to the base language calling convention
|
|
/// it uses.
|
|
constexpr
|
|
SILFunctionLanguage getSILFunctionLanguage(SILFunctionTypeRepresentation rep) {
|
|
switch (rep) {
|
|
case SILFunctionTypeRepresentation::ObjCMethod:
|
|
case SILFunctionTypeRepresentation::CFunctionPointer:
|
|
case SILFunctionTypeRepresentation::Block:
|
|
case SILFunctionTypeRepresentation::CXXMethod:
|
|
return SILFunctionLanguage::C;
|
|
case SILFunctionTypeRepresentation::Thick:
|
|
case SILFunctionTypeRepresentation::Thin:
|
|
case SILFunctionTypeRepresentation::Method:
|
|
case SILFunctionTypeRepresentation::WitnessMethod:
|
|
case SILFunctionTypeRepresentation::Closure:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorGetter:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorSetter:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorEquals:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorHash:
|
|
return SILFunctionLanguage::Swift;
|
|
}
|
|
|
|
llvm_unreachable("Unhandled SILFunctionTypeRepresentation in switch.");
|
|
}
|
|
|
|
// MARK: - SILExtInfoBuilder
|
|
/// A builder type for creating an \c SILExtInfo.
|
|
///
|
|
/// The main API public includes the \c withXYZ and \p build() methods.
|
|
class SILExtInfoBuilder {
|
|
friend SILExtInfo;
|
|
friend SILFunctionType;
|
|
|
|
// If bits are added or removed, then TypeBase::SILFunctionTypeBits
|
|
// and NumMaskBits must be updated, and they must match.
|
|
|
|
// |representation|pseudogeneric| noescape | concurrent | async
|
|
// | 0 .. 4 | 5 | 6 | 7 | 8
|
|
// |differentiability|unimplementable|
|
|
// | 9 .. 11 | 12 |
|
|
//
|
|
enum : unsigned {
|
|
RepresentationMask = 0x1F << 0,
|
|
PseudogenericMask = 1 << 5,
|
|
NoEscapeMask = 1 << 6,
|
|
SendableMask = 1 << 7,
|
|
AsyncMask = 1 << 8,
|
|
DifferentiabilityMaskOffset = 9,
|
|
DifferentiabilityMask = 0x7 << DifferentiabilityMaskOffset,
|
|
UnimplementableMask = 1 << 12,
|
|
ErasedIsolationMask = 1 << 13,
|
|
NumMaskBits = 14
|
|
};
|
|
|
|
unsigned bits; // Naturally sized for speed.
|
|
|
|
ClangTypeInfo clangTypeInfo;
|
|
|
|
ArrayRef<LifetimeDependenceInfo> lifetimeDependencies;
|
|
|
|
using Language = SILFunctionLanguage;
|
|
using Representation = SILFunctionTypeRepresentation;
|
|
|
|
SILExtInfoBuilder(unsigned bits, ClangTypeInfo clangTypeInfo,
|
|
ArrayRef<LifetimeDependenceInfo> lifetimeDependencies)
|
|
: bits(bits), clangTypeInfo(clangTypeInfo.getCanonical()),
|
|
lifetimeDependencies(lifetimeDependencies) {}
|
|
|
|
static unsigned makeBits(Representation rep, bool isPseudogeneric,
|
|
bool isNoEscape, bool isSendable, bool isAsync,
|
|
bool isUnimplementable,
|
|
SILFunctionTypeIsolation isolation,
|
|
DifferentiabilityKind diffKind) {
|
|
return ((unsigned)rep) | (isPseudogeneric ? PseudogenericMask : 0) |
|
|
(isNoEscape ? NoEscapeMask : 0) | (isSendable ? SendableMask : 0) |
|
|
(isAsync ? AsyncMask : 0) |
|
|
(isUnimplementable ? UnimplementableMask : 0) |
|
|
(isolation.isErased() ? ErasedIsolationMask : 0) |
|
|
(((unsigned)diffKind << DifferentiabilityMaskOffset) &
|
|
DifferentiabilityMask);
|
|
}
|
|
|
|
public:
|
|
/// An ExtInfoBuilder for a typical Swift function: thick, @escaping,
|
|
/// non-pseudogeneric, non-differentiable.
|
|
SILExtInfoBuilder()
|
|
: SILExtInfoBuilder(
|
|
makeBits(SILFunctionTypeRepresentation::Thick, false, false, false,
|
|
false, false, SILFunctionTypeIsolation::forUnknown(),
|
|
DifferentiabilityKind::NonDifferentiable),
|
|
ClangTypeInfo(nullptr), /*LifetimeDependenceInfo*/ {}) {}
|
|
|
|
SILExtInfoBuilder(Representation rep, bool isPseudogeneric, bool isNoEscape,
|
|
bool isSendable, bool isAsync, bool isUnimplementable,
|
|
SILFunctionTypeIsolation isolation,
|
|
DifferentiabilityKind diffKind, const clang::Type *type,
|
|
ArrayRef<LifetimeDependenceInfo> lifetimeDependenceInfo)
|
|
: SILExtInfoBuilder(makeBits(rep, isPseudogeneric, isNoEscape, isSendable,
|
|
isAsync, isUnimplementable, isolation,
|
|
diffKind),
|
|
ClangTypeInfo(type), lifetimeDependenceInfo) {}
|
|
|
|
// Constructor for polymorphic type.
|
|
SILExtInfoBuilder(ASTExtInfoBuilder info, bool isPseudogeneric)
|
|
: SILExtInfoBuilder(makeBits(info.getSILRepresentation(), isPseudogeneric,
|
|
info.isNoEscape(), info.isSendable(),
|
|
info.isAsync(), /*unimplementable*/ false,
|
|
info.getIsolation().isErased()
|
|
? SILFunctionTypeIsolation::forErased()
|
|
: SILFunctionTypeIsolation::forUnknown(),
|
|
info.getDifferentiabilityKind()),
|
|
info.getClangTypeInfo(),
|
|
info.getLifetimeDependencies()) {}
|
|
|
|
void checkInvariants() const;
|
|
|
|
/// Check if \c this is well-formed and create an ExtInfo.
|
|
SILExtInfo build() const;
|
|
|
|
/// What is the abstract representation of this function value?
|
|
constexpr Representation getRepresentation() const {
|
|
return Representation(bits & RepresentationMask);
|
|
}
|
|
|
|
constexpr Language getLanguage() const {
|
|
return getSILFunctionLanguage(getRepresentation());
|
|
}
|
|
|
|
/// Is this function pseudo-generic? A pseudo-generic function
|
|
/// is not permitted to dynamically depend on its type arguments.
|
|
constexpr bool isPseudogeneric() const { return bits & PseudogenericMask; }
|
|
|
|
// Is this function guaranteed to be no-escape by the type system?
|
|
constexpr bool isNoEscape() const { return bits & NoEscapeMask; }
|
|
|
|
constexpr bool isSendable() const { return bits & SendableMask; }
|
|
|
|
constexpr bool isAsync() const { return bits & AsyncMask; }
|
|
|
|
constexpr DifferentiabilityKind getDifferentiabilityKind() const {
|
|
return DifferentiabilityKind((bits & DifferentiabilityMask) >>
|
|
DifferentiabilityMaskOffset);
|
|
}
|
|
|
|
constexpr bool isDifferentiable() const {
|
|
return getDifferentiabilityKind() !=
|
|
DifferentiabilityKind::NonDifferentiable;
|
|
}
|
|
|
|
constexpr bool isUnimplementable() const {
|
|
return bits & UnimplementableMask;
|
|
}
|
|
|
|
/// Does this function type have erased isolation (i.e. is it the
|
|
/// lowering of an @isolated(any) function type)?
|
|
constexpr bool hasErasedIsolation() const {
|
|
return bits & ErasedIsolationMask;
|
|
}
|
|
|
|
SILFunctionTypeIsolation getIsolation() const {
|
|
return hasErasedIsolation() ? SILFunctionTypeIsolation::forErased()
|
|
: SILFunctionTypeIsolation::forUnknown();
|
|
}
|
|
|
|
/// Get the underlying ClangTypeInfo value.
|
|
ClangTypeInfo getClangTypeInfo() const { return clangTypeInfo; }
|
|
|
|
ArrayRef<LifetimeDependenceInfo> getLifetimeDependencies() const {
|
|
return lifetimeDependencies;
|
|
}
|
|
|
|
constexpr bool hasSelfParam() const {
|
|
switch (getRepresentation()) {
|
|
case Representation::Thick:
|
|
case Representation::Block:
|
|
case Representation::Thin:
|
|
case Representation::CFunctionPointer:
|
|
case Representation::Closure:
|
|
case Representation::KeyPathAccessorGetter:
|
|
case Representation::KeyPathAccessorSetter:
|
|
case Representation::KeyPathAccessorEquals:
|
|
case Representation::KeyPathAccessorHash:
|
|
return false;
|
|
case Representation::ObjCMethod:
|
|
case Representation::Method:
|
|
case Representation::WitnessMethod:
|
|
case SILFunctionTypeRepresentation::CXXMethod:
|
|
return true;
|
|
}
|
|
llvm_unreachable("Unhandled Representation in switch.");
|
|
}
|
|
|
|
/// True if the function representation carries context.
|
|
constexpr bool hasContext() const {
|
|
switch (getRepresentation()) {
|
|
case Representation::Thick:
|
|
case Representation::Block:
|
|
return true;
|
|
case Representation::Thin:
|
|
case Representation::CFunctionPointer:
|
|
case Representation::ObjCMethod:
|
|
case Representation::Method:
|
|
case Representation::WitnessMethod:
|
|
case Representation::Closure:
|
|
case SILFunctionTypeRepresentation::CXXMethod:
|
|
case Representation::KeyPathAccessorGetter:
|
|
case Representation::KeyPathAccessorSetter:
|
|
case Representation::KeyPathAccessorEquals:
|
|
case Representation::KeyPathAccessorHash:
|
|
return false;
|
|
}
|
|
llvm_unreachable("Unhandled Representation in switch.");
|
|
}
|
|
|
|
// Note that we don't have setters. That is by design, use
|
|
// the following with methods instead of mutating these objects.
|
|
[[nodiscard]]
|
|
SILExtInfoBuilder withRepresentation(Representation rep) const {
|
|
return SILExtInfoBuilder((bits & ~RepresentationMask) | (unsigned)rep,
|
|
shouldStoreClangType(rep) ? clangTypeInfo
|
|
: ClangTypeInfo(),
|
|
lifetimeDependencies);
|
|
}
|
|
[[nodiscard]]
|
|
SILExtInfoBuilder withIsPseudogeneric(bool isPseudogeneric = true) const {
|
|
return SILExtInfoBuilder(isPseudogeneric ? (bits | PseudogenericMask)
|
|
: (bits & ~PseudogenericMask),
|
|
clangTypeInfo, lifetimeDependencies);
|
|
}
|
|
[[nodiscard]]
|
|
SILExtInfoBuilder withNoEscape(bool noEscape = true) const {
|
|
return SILExtInfoBuilder(noEscape ? (bits | NoEscapeMask)
|
|
: (bits & ~NoEscapeMask),
|
|
clangTypeInfo, lifetimeDependencies);
|
|
}
|
|
[[nodiscard]]
|
|
SILExtInfoBuilder withSendable(bool isSendable = true) const {
|
|
return SILExtInfoBuilder(isSendable ? (bits | SendableMask)
|
|
: (bits & ~SendableMask),
|
|
clangTypeInfo, lifetimeDependencies);
|
|
}
|
|
|
|
[[nodiscard]]
|
|
SILExtInfoBuilder withAsync(bool isAsync = true) const {
|
|
return SILExtInfoBuilder(isAsync ? (bits | AsyncMask) : (bits & ~AsyncMask),
|
|
clangTypeInfo, lifetimeDependencies);
|
|
}
|
|
|
|
[[nodiscard]]
|
|
SILExtInfoBuilder withErasedIsolation(bool erased = true) const {
|
|
return SILExtInfoBuilder(erased ? (bits | ErasedIsolationMask)
|
|
: (bits & ~ErasedIsolationMask),
|
|
clangTypeInfo, lifetimeDependencies);
|
|
}
|
|
[[nodiscard]]
|
|
SILExtInfoBuilder withIsolation(SILFunctionTypeIsolation isolation) const {
|
|
switch (isolation.getKind()) {
|
|
case SILFunctionTypeIsolation::Unknown:
|
|
return *this;
|
|
case SILFunctionTypeIsolation::Erased:
|
|
return withErasedIsolation(true);
|
|
}
|
|
llvm_unreachable("bad kind");
|
|
}
|
|
[[nodiscard]]
|
|
SILExtInfoBuilder withUnimplementable(bool isUnimplementable = true) const {
|
|
return SILExtInfoBuilder(isUnimplementable ? (bits | UnimplementableMask)
|
|
: (bits & ~UnimplementableMask),
|
|
clangTypeInfo, lifetimeDependencies);
|
|
}
|
|
|
|
[[nodiscard]]
|
|
SILExtInfoBuilder
|
|
withDifferentiabilityKind(DifferentiabilityKind differentiability) const {
|
|
return SILExtInfoBuilder(
|
|
(bits & ~DifferentiabilityMask) |
|
|
((unsigned)differentiability << DifferentiabilityMaskOffset),
|
|
clangTypeInfo, lifetimeDependencies);
|
|
}
|
|
[[nodiscard]]
|
|
SILExtInfoBuilder withClangFunctionType(const clang::Type *type) const {
|
|
return SILExtInfoBuilder(bits, ClangTypeInfo(type).getCanonical(),
|
|
lifetimeDependencies);
|
|
}
|
|
|
|
/// \p lifetimeDependencies should be arena allocated and not a temporary
|
|
/// Function types are allocated on the are arena and their ExtInfo should be
|
|
/// valid throughout their lifetime.
|
|
[[nodiscard]] SILExtInfoBuilder withLifetimeDependencies(
|
|
ArrayRef<LifetimeDependenceInfo> lifetimeDependenceInfo) const {
|
|
return SILExtInfoBuilder(bits, clangTypeInfo, lifetimeDependenceInfo);
|
|
}
|
|
|
|
[[nodiscard]] ASTExtInfoBuilder withLifetimeDependencies(
|
|
SmallVectorImpl<LifetimeDependenceInfo> lifetimeDependencies) const =
|
|
delete;
|
|
|
|
void Profile(llvm::FoldingSetNodeID &ID) const {
|
|
ID.AddInteger(bits);
|
|
ID.AddPointer(clangTypeInfo.getType());
|
|
for (auto info : lifetimeDependencies) {
|
|
info.Profile(ID);
|
|
}
|
|
}
|
|
|
|
bool isEqualTo(SILExtInfoBuilder other, bool useClangTypes) const {
|
|
return bits == other.bits &&
|
|
(useClangTypes ? (clangTypeInfo == other.clangTypeInfo) : true);
|
|
}
|
|
}; // end SILExtInfoBuilder
|
|
|
|
// MARK: - SILExtInfo
|
|
/// Calling convention information for SILFunctionType.
|
|
///
|
|
/// New instances can be made from existing instances via \c SILExtInfoBuilder,
|
|
/// typically using a code pattern like:
|
|
/// \code
|
|
/// extInfo.intoBuilder().withX(x).withY(y).build()
|
|
/// \endcode
|
|
class SILExtInfo {
|
|
friend SILExtInfoBuilder;
|
|
friend SILFunctionType;
|
|
|
|
SILExtInfoBuilder builder;
|
|
|
|
// Only for use by SILExtInfoBuilder::build. Don't use it elsewhere!
|
|
SILExtInfo(SILExtInfoBuilder builder) : builder(builder) {}
|
|
|
|
SILExtInfo(unsigned bits, ClangTypeInfo clangTypeInfo,
|
|
llvm::ArrayRef<LifetimeDependenceInfo> lifetimeDependencies)
|
|
: builder(bits, clangTypeInfo, lifetimeDependencies) {
|
|
builder.checkInvariants();
|
|
};
|
|
|
|
public:
|
|
/// An ExtInfo for a typical Swift function: thick, @escaping,
|
|
/// non-pseudogeneric, non-differentiable.
|
|
SILExtInfo() : builder() { builder.checkInvariants(); };
|
|
|
|
SILExtInfo(ASTExtInfo info, bool isPseudogeneric)
|
|
: builder(info.intoBuilder(), isPseudogeneric) {
|
|
builder.checkInvariants();
|
|
}
|
|
|
|
/// A default ExtInfo but with a Thin convention.
|
|
static SILExtInfo getThin() {
|
|
return SILExtInfoBuilder(
|
|
SILExtInfoBuilder::Representation::Thin, false, false, false,
|
|
false, false, SILFunctionTypeIsolation::forUnknown(),
|
|
DifferentiabilityKind::NonDifferentiable, nullptr, {})
|
|
.build();
|
|
}
|
|
|
|
/// Create a builder with the same state as \c this.
|
|
SILExtInfoBuilder intoBuilder() const { return builder; }
|
|
|
|
private:
|
|
constexpr unsigned getBits() const { return builder.bits; }
|
|
|
|
public:
|
|
constexpr SILFunctionTypeRepresentation getRepresentation() const {
|
|
return builder.getRepresentation();
|
|
}
|
|
|
|
constexpr SILFunctionLanguage getLanguage() const {
|
|
return builder.getLanguage();
|
|
}
|
|
|
|
constexpr bool isPseudogeneric() const { return builder.isPseudogeneric(); }
|
|
|
|
constexpr bool isNoEscape() const { return builder.isNoEscape(); }
|
|
|
|
constexpr bool isSendable() const { return builder.isSendable(); }
|
|
|
|
constexpr bool isAsync() const { return builder.isAsync(); }
|
|
|
|
constexpr bool isUnimplementable() const {
|
|
return builder.isUnimplementable();
|
|
}
|
|
|
|
constexpr bool hasErasedIsolation() const {
|
|
return builder.hasErasedIsolation();
|
|
}
|
|
SILFunctionTypeIsolation getIsolation() const {
|
|
return builder.getIsolation();
|
|
}
|
|
|
|
constexpr DifferentiabilityKind getDifferentiabilityKind() const {
|
|
return builder.getDifferentiabilityKind();
|
|
}
|
|
|
|
constexpr bool isDifferentiable() const { return builder.isDifferentiable(); }
|
|
|
|
ClangTypeInfo getClangTypeInfo() const { return builder.getClangTypeInfo(); }
|
|
|
|
ArrayRef<LifetimeDependenceInfo> getLifetimeDependencies() const {
|
|
return builder.getLifetimeDependencies();
|
|
}
|
|
|
|
constexpr bool hasSelfParam() const { return builder.hasSelfParam(); }
|
|
|
|
constexpr bool hasContext() const { return builder.hasContext(); }
|
|
|
|
/// Helper method for changing the Representation.
|
|
///
|
|
/// Prefer using \c SILExtInfoBuilder::withRepresentation for chaining.
|
|
SILExtInfo withRepresentation(SILExtInfoBuilder::Representation rep) const {
|
|
return builder.withRepresentation(rep).build();
|
|
}
|
|
|
|
/// Helper method for changing only the NoEscape field.
|
|
///
|
|
/// Prefer using \c SILExtInfoBuilder::withNoEscape for chaining.
|
|
SILExtInfo withNoEscape(bool noEscape = true) const {
|
|
return builder.withNoEscape(noEscape).build();
|
|
}
|
|
|
|
SILExtInfo withSendable(bool isSendable = true) const {
|
|
return builder.withSendable(isSendable).build();
|
|
}
|
|
|
|
SILExtInfo withAsync(bool isAsync = true) const {
|
|
return builder.withAsync(isAsync).build();
|
|
}
|
|
|
|
SILExtInfo withErasedIsolation(bool erased = true) const {
|
|
return builder.withErasedIsolation(erased).build();
|
|
}
|
|
|
|
SILExtInfo withUnimplementable(bool isUnimplementable = true) const {
|
|
return builder.withUnimplementable(isUnimplementable).build();
|
|
}
|
|
|
|
/// \p lifetimeDependencies should be arena allocated and not a temporary
|
|
/// Function types are allocated on the are arena and their ExtInfo should be
|
|
/// valid throughout their lifetime.
|
|
SILExtInfo withLifetimeDependencies(
|
|
ArrayRef<LifetimeDependenceInfo> lifetimeDependencies) const {
|
|
return builder.withLifetimeDependencies(lifetimeDependencies);
|
|
}
|
|
|
|
SILExtInfo withLifetimeDependencies(SmallVectorImpl<LifetimeDependenceInfo>
|
|
lifetimeDependencies) const = delete;
|
|
|
|
void Profile(llvm::FoldingSetNodeID &ID) const { builder.Profile(ID); }
|
|
|
|
bool isEqualTo(SILExtInfo other, bool useClangTypes) const {
|
|
return builder.isEqualTo(other.builder, useClangTypes);
|
|
}
|
|
|
|
std::optional<UnexpectedClangTypeError> checkClangType() const;
|
|
};
|
|
|
|
/// Helper function to obtain the useClangTypes parameter for checking equality
|
|
/// of ExtInfos.
|
|
///
|
|
/// Typically, the argument will be a function type which was used to obtain one
|
|
/// of the ExtInfos.
|
|
template <typename HasContext> bool useClangTypes(HasContext hasContext) {
|
|
return hasContext->getASTContext().LangOpts.UseClangFunctionTypes;
|
|
}
|
|
|
|
} // end namespace swift
|
|
|
|
#endif // SWIFT_EXTINFO_H
|