mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
960 lines
33 KiB
C++
960 lines
33 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/ClangModuleLoader.h"
|
|
|
|
#include "llvm/ADT/Optional.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
#include <utility>
|
|
|
|
namespace clang {
|
|
class Type;
|
|
class ASTContext;
|
|
} // namespace clang
|
|
|
|
namespace swift {
|
|
class AnyFunctionType;
|
|
class ASTExtInfo;
|
|
class ASTExtInfoBuilder;
|
|
class FunctionType;
|
|
class SILExtInfo;
|
|
class SILExtInfoBuilder;
|
|
class SILFunctionType;
|
|
enum class SILFunctionTypeRepresentation : uint8_t;
|
|
} // namespace swift
|
|
|
|
namespace swift {
|
|
|
|
// 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 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,
|
|
};
|
|
|
|
/// 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:
|
|
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);
|
|
}
|
|
|
|
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 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:
|
|
return None;
|
|
}
|
|
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:
|
|
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:
|
|
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|differentiability|
|
|
// | 0 .. 3 | 4 | 5 | 6 | 7 | 8 .. 10 |
|
|
//
|
|
enum : unsigned {
|
|
RepresentationMask = 0xF << 0,
|
|
NoEscapeMask = 1 << 4,
|
|
SendableMask = 1 << 5,
|
|
AsyncMask = 1 << 6,
|
|
ThrowsMask = 1 << 7,
|
|
DifferentiabilityMaskOffset = 8,
|
|
DifferentiabilityMask = 0x7 << DifferentiabilityMaskOffset,
|
|
NumMaskBits = 11
|
|
};
|
|
|
|
unsigned bits; // Naturally sized for speed.
|
|
|
|
ClangTypeInfo clangTypeInfo;
|
|
Type globalActor;
|
|
|
|
using Representation = FunctionTypeRepresentation;
|
|
|
|
ASTExtInfoBuilder(
|
|
unsigned bits, ClangTypeInfo clangTypeInfo, Type globalActor
|
|
) : bits(bits), clangTypeInfo(clangTypeInfo), globalActor(globalActor) {}
|
|
|
|
public:
|
|
/// An ExtInfoBuilder for a typical Swift function: @convention(swift),
|
|
/// @escaping, non-throwing, non-differentiable.
|
|
ASTExtInfoBuilder()
|
|
: ASTExtInfoBuilder(Representation::Swift, false, false,
|
|
DifferentiabilityKind::NonDifferentiable, nullptr,
|
|
Type()) {}
|
|
|
|
// Constructor for polymorphic type.
|
|
ASTExtInfoBuilder(Representation rep, bool throws)
|
|
: ASTExtInfoBuilder(rep, false, throws,
|
|
DifferentiabilityKind::NonDifferentiable, nullptr,
|
|
Type()) {}
|
|
|
|
// Constructor with no defaults.
|
|
ASTExtInfoBuilder(Representation rep, bool isNoEscape, bool throws,
|
|
DifferentiabilityKind diffKind, const clang::Type *type,
|
|
Type globalActor)
|
|
: ASTExtInfoBuilder(
|
|
((unsigned)rep) | (isNoEscape ? NoEscapeMask : 0) |
|
|
(throws ? ThrowsMask : 0) |
|
|
(((unsigned)diffKind << DifferentiabilityMaskOffset) &
|
|
DifferentiabilityMask),
|
|
ClangTypeInfo(type), globalActor) {}
|
|
|
|
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 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; }
|
|
|
|
constexpr bool hasSelfParam() const {
|
|
switch (getSILRepresentation()) {
|
|
case SILFunctionTypeRepresentation::Thick:
|
|
case SILFunctionTypeRepresentation::Block:
|
|
case SILFunctionTypeRepresentation::Thin:
|
|
case SILFunctionTypeRepresentation::CFunctionPointer:
|
|
case SILFunctionTypeRepresentation::Closure:
|
|
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.
|
|
LLVM_NODISCARD
|
|
ASTExtInfoBuilder withRepresentation(Representation rep) const {
|
|
return ASTExtInfoBuilder((bits & ~RepresentationMask) | (unsigned)rep,
|
|
shouldStoreClangType(rep) ? clangTypeInfo
|
|
: ClangTypeInfo(),
|
|
globalActor);
|
|
}
|
|
LLVM_NODISCARD
|
|
ASTExtInfoBuilder withNoEscape(bool noEscape = true) const {
|
|
return ASTExtInfoBuilder(noEscape ? (bits | NoEscapeMask)
|
|
: (bits & ~NoEscapeMask),
|
|
clangTypeInfo, globalActor);
|
|
}
|
|
LLVM_NODISCARD
|
|
ASTExtInfoBuilder withConcurrent(bool concurrent = true) const {
|
|
return ASTExtInfoBuilder(concurrent ? (bits | SendableMask)
|
|
: (bits & ~SendableMask),
|
|
clangTypeInfo, globalActor);
|
|
}
|
|
LLVM_NODISCARD
|
|
ASTExtInfoBuilder withAsync(bool async = true) const {
|
|
return ASTExtInfoBuilder(async ? (bits | AsyncMask)
|
|
: (bits & ~AsyncMask),
|
|
clangTypeInfo, globalActor);
|
|
}
|
|
LLVM_NODISCARD
|
|
ASTExtInfoBuilder withThrows(bool throws = true) const {
|
|
return ASTExtInfoBuilder(
|
|
throws ? (bits | ThrowsMask) : (bits & ~ThrowsMask), clangTypeInfo,
|
|
globalActor);
|
|
}
|
|
LLVM_NODISCARD
|
|
ASTExtInfoBuilder
|
|
withDifferentiabilityKind(DifferentiabilityKind differentiability) const {
|
|
return ASTExtInfoBuilder(
|
|
(bits & ~DifferentiabilityMask) |
|
|
((unsigned)differentiability << DifferentiabilityMaskOffset),
|
|
clangTypeInfo, globalActor);
|
|
}
|
|
LLVM_NODISCARD
|
|
ASTExtInfoBuilder withClangFunctionType(const clang::Type *type) const {
|
|
return ASTExtInfoBuilder(bits, ClangTypeInfo(type), globalActor);
|
|
}
|
|
|
|
/// 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.
|
|
LLVM_NODISCARD
|
|
ASTExtInfoBuilder
|
|
withSILRepresentation(SILFunctionTypeRepresentation rep) const {
|
|
return ASTExtInfoBuilder((bits & ~RepresentationMask) | (unsigned)rep,
|
|
shouldStoreClangType(rep) ? clangTypeInfo
|
|
: ClangTypeInfo(),
|
|
globalActor);
|
|
}
|
|
|
|
LLVM_NODISCARD
|
|
ASTExtInfoBuilder withGlobalActor(Type globalActor) const {
|
|
return ASTExtInfoBuilder(bits, clangTypeInfo, globalActor);
|
|
}
|
|
|
|
bool isEqualTo(ASTExtInfoBuilder other, bool useClangTypes) const {
|
|
return bits == other.bits &&
|
|
(useClangTypes ? (clangTypeInfo == other.clangTypeInfo) : true) &&
|
|
globalActor.getPointer() == other.globalActor.getPointer();
|
|
}
|
|
|
|
constexpr std::tuple<unsigned, const void *, const void *>
|
|
getFuncAttrKey() const {
|
|
return std::make_tuple(
|
|
bits, clangTypeInfo.getType(), globalActor.getPointer());
|
|
}
|
|
}; // 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)
|
|
: builder(bits, clangTypeInfo, globalActor) {
|
|
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 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(); }
|
|
|
|
/// Helper method for changing the representation.
|
|
///
|
|
/// Prefer using \c ASTExtInfoBuilder::withRepresentation for chaining.
|
|
LLVM_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.
|
|
LLVM_NODISCARD
|
|
ASTExtInfo withNoEscape(bool noEscape = true) const {
|
|
return builder.withNoEscape(noEscape).build();
|
|
}
|
|
|
|
/// Helper method for changing only the concurrent field.
|
|
///
|
|
/// Prefer using \c ASTExtInfoBuilder::withConcurrent for chaining.
|
|
LLVM_NODISCARD
|
|
ASTExtInfo withConcurrent(bool concurrent = true) const {
|
|
return builder.withConcurrent(concurrent).build();
|
|
}
|
|
|
|
/// Helper method for changing only the throws field.
|
|
///
|
|
/// Prefer using \c ASTExtInfoBuilder::withThrows for chaining.
|
|
LLVM_NODISCARD
|
|
ASTExtInfo withThrows(bool throws = true) const {
|
|
return builder.withThrows(throws).build();
|
|
}
|
|
|
|
/// Helper method for changing only the async field.
|
|
///
|
|
/// Prefer using \c ASTExtInfoBuilder::withAsync for chaining.
|
|
LLVM_NODISCARD
|
|
ASTExtInfo withAsync(bool async = true) const {
|
|
return builder.withAsync(async).build();
|
|
}
|
|
|
|
LLVM_NODISCARD
|
|
ASTExtInfo withGlobalActor(Type globalActor) const {
|
|
return builder.withGlobalActor(globalActor).build();
|
|
}
|
|
|
|
bool isEqualTo(ASTExtInfo other, bool useClangTypes) const {
|
|
return builder.isEqualTo(other.builder, useClangTypes);
|
|
}
|
|
|
|
constexpr std::tuple<unsigned, const void *, const void *>
|
|
getFuncAttrKey() const {
|
|
return builder.getFuncAttrKey();
|
|
}
|
|
}; // 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:
|
|
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 |differentiability|
|
|
// | 0 .. 3 | 4 | 5 | 6 | 7 | 8 .. 10 |
|
|
//
|
|
enum : unsigned {
|
|
RepresentationMask = 0xF << 0,
|
|
PseudogenericMask = 1 << 4,
|
|
NoEscapeMask = 1 << 5,
|
|
SendableMask = 1 << 6,
|
|
AsyncMask = 1 << 7,
|
|
DifferentiabilityMaskOffset = 8,
|
|
DifferentiabilityMask = 0x7 << DifferentiabilityMaskOffset,
|
|
NumMaskBits = 11
|
|
};
|
|
|
|
unsigned bits; // Naturally sized for speed.
|
|
|
|
ClangTypeInfo clangTypeInfo;
|
|
|
|
using Language = SILFunctionLanguage;
|
|
using Representation = SILFunctionTypeRepresentation;
|
|
|
|
SILExtInfoBuilder(unsigned bits, ClangTypeInfo clangTypeInfo)
|
|
: bits(bits), clangTypeInfo(clangTypeInfo.getCanonical()) {}
|
|
|
|
static constexpr unsigned makeBits(Representation rep, bool isPseudogeneric,
|
|
bool isNoEscape, bool isSendable,
|
|
bool isAsync,
|
|
DifferentiabilityKind diffKind) {
|
|
return ((unsigned)rep) | (isPseudogeneric ? PseudogenericMask : 0) |
|
|
(isNoEscape ? NoEscapeMask : 0) |
|
|
(isSendable ? SendableMask : 0) |
|
|
(isAsync ? AsyncMask : 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,
|
|
DifferentiabilityKind::NonDifferentiable),
|
|
ClangTypeInfo(nullptr)) {}
|
|
|
|
SILExtInfoBuilder(Representation rep, bool isPseudogeneric, bool isNoEscape,
|
|
bool isSendable, bool isAsync,
|
|
DifferentiabilityKind diffKind, const clang::Type *type)
|
|
: SILExtInfoBuilder(makeBits(rep, isPseudogeneric, isNoEscape,
|
|
isSendable, isAsync, diffKind),
|
|
ClangTypeInfo(type)) {}
|
|
|
|
// Constructor for polymorphic type.
|
|
SILExtInfoBuilder(ASTExtInfoBuilder info, bool isPseudogeneric)
|
|
: SILExtInfoBuilder(makeBits(info.getSILRepresentation(), isPseudogeneric,
|
|
info.isNoEscape(), info.isSendable(),
|
|
info.isAsync(),
|
|
info.getDifferentiabilityKind()),
|
|
info.getClangTypeInfo()) {}
|
|
|
|
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;
|
|
}
|
|
|
|
/// Get the underlying ClangTypeInfo value.
|
|
ClangTypeInfo getClangTypeInfo() const { return clangTypeInfo; }
|
|
|
|
constexpr bool hasSelfParam() const {
|
|
switch (getRepresentation()) {
|
|
case Representation::Thick:
|
|
case Representation::Block:
|
|
case Representation::Thin:
|
|
case Representation::CFunctionPointer:
|
|
case Representation::Closure:
|
|
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:
|
|
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.
|
|
LLVM_NODISCARD
|
|
SILExtInfoBuilder withRepresentation(Representation rep) const {
|
|
return SILExtInfoBuilder((bits & ~RepresentationMask) | (unsigned)rep,
|
|
shouldStoreClangType(rep) ? clangTypeInfo
|
|
: ClangTypeInfo());
|
|
}
|
|
LLVM_NODISCARD
|
|
SILExtInfoBuilder withIsPseudogeneric(bool isPseudogeneric = true) const {
|
|
return SILExtInfoBuilder(isPseudogeneric ? (bits | PseudogenericMask)
|
|
: (bits & ~PseudogenericMask),
|
|
clangTypeInfo);
|
|
}
|
|
LLVM_NODISCARD
|
|
SILExtInfoBuilder withNoEscape(bool noEscape = true) const {
|
|
return SILExtInfoBuilder(noEscape ? (bits | NoEscapeMask)
|
|
: (bits & ~NoEscapeMask),
|
|
clangTypeInfo);
|
|
}
|
|
LLVM_NODISCARD
|
|
SILExtInfoBuilder withConcurrent(bool isSendable = true) const {
|
|
return SILExtInfoBuilder(isSendable ? (bits | SendableMask)
|
|
: (bits & ~SendableMask),
|
|
clangTypeInfo);
|
|
}
|
|
LLVM_NODISCARD
|
|
SILExtInfoBuilder withAsync(bool isAsync = true) const {
|
|
return SILExtInfoBuilder(isAsync ? (bits | AsyncMask) : (bits & ~AsyncMask),
|
|
clangTypeInfo);
|
|
}
|
|
LLVM_NODISCARD
|
|
SILExtInfoBuilder
|
|
withDifferentiabilityKind(DifferentiabilityKind differentiability) const {
|
|
return SILExtInfoBuilder(
|
|
(bits & ~DifferentiabilityMask) |
|
|
((unsigned)differentiability << DifferentiabilityMaskOffset),
|
|
clangTypeInfo);
|
|
}
|
|
LLVM_NODISCARD
|
|
SILExtInfoBuilder withClangFunctionType(const clang::Type *type) const {
|
|
return SILExtInfoBuilder(bits, ClangTypeInfo(type).getCanonical());
|
|
}
|
|
|
|
bool isEqualTo(SILExtInfoBuilder other, bool useClangTypes) const {
|
|
return bits == other.bits &&
|
|
(useClangTypes ? (clangTypeInfo == other.clangTypeInfo) : true);
|
|
}
|
|
|
|
constexpr std::pair<unsigned, const void *> getFuncAttrKey() const {
|
|
return std::make_pair(bits, clangTypeInfo.getType());
|
|
}
|
|
}; // 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)
|
|
: builder(bits, clangTypeInfo) {
|
|
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,
|
|
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 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(); }
|
|
|
|
/// 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 withConcurrent(bool isSendable = true) const {
|
|
return builder.withConcurrent(isSendable).build();
|
|
}
|
|
|
|
SILExtInfo withAsync(bool isAsync = true) const {
|
|
return builder.withAsync(isAsync).build();
|
|
}
|
|
|
|
bool isEqualTo(SILExtInfo other, bool useClangTypes) const {
|
|
return builder.isEqualTo(other.builder, useClangTypes);
|
|
}
|
|
|
|
constexpr std::pair<unsigned, const void *> getFuncAttrKey() const {
|
|
return builder.getFuncAttrKey();
|
|
}
|
|
|
|
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
|