ASTDemangler: Add support for lowered function types

This commit is contained in:
Slava Pestov
2019-01-29 17:10:02 -05:00
parent 13a50c2d2d
commit 4a74bf4ba1
6 changed files with 390 additions and 17 deletions

View File

@@ -82,6 +82,13 @@ public:
Type createFunctionType(ArrayRef<Demangle::FunctionParam<Type>> params,
Type output, FunctionTypeFlags flags);
Type createImplFunctionType(
Demangle::ImplParameterConvention calleeConvention,
ArrayRef<Demangle::ImplFunctionParam<Type>> params,
ArrayRef<Demangle::ImplFunctionResult<Type>> results,
Optional<Demangle::ImplFunctionResult<Type>> errorResult,
ImplFunctionTypeFlags flags);
Type createProtocolCompositionType(ArrayRef<ProtocolDecl *> protocols,
Type superclass,
bool isClassBound);

View File

@@ -77,6 +77,147 @@ public:
}
};
enum class ImplParameterConvention {
Indirect_In,
Indirect_In_Constant,
Indirect_In_Guaranteed,
Indirect_Inout,
Indirect_InoutAliasable,
Direct_Owned,
Direct_Unowned,
Direct_Guaranteed,
};
/// Describe a lowered function parameter, parameterized on the type
/// representation.
template <typename BuiltType>
class ImplFunctionParam {
ImplParameterConvention Convention;
BuiltType Type;
public:
using ConventionType = ImplParameterConvention;
static Optional<ConventionType>
getConventionFromString(StringRef conventionString) {
if (conventionString == "@in")
return ConventionType::Indirect_In;
if (conventionString == "@indirect_in_constant")
return ConventionType::Indirect_In_Constant;
if (conventionString == "@in_guaranteed")
return ConventionType::Indirect_In_Guaranteed;
if (conventionString == "@inout")
return ConventionType::Indirect_Inout;
if (conventionString == "@inout_aliasable")
return ConventionType::Indirect_InoutAliasable;
if (conventionString == "@owned")
return ConventionType::Direct_Owned;
if (conventionString == "@unowned")
return ConventionType::Direct_Unowned;
if (conventionString == "@guaranteed")
return ConventionType::Direct_Guaranteed;
return None;
}
ImplFunctionParam(ImplParameterConvention convention, BuiltType type)
: Convention(convention), Type(type) {}
ImplParameterConvention getConvention() const { return Convention; }
BuiltType getType() const { return Type; }
};
enum class ImplResultConvention {
Indirect,
Owned,
Unowned,
UnownedInnerPointer,
Autoreleased,
};
/// Describe a lowered function result, parameterized on the type
/// representation.
template <typename BuiltType>
class ImplFunctionResult {
ImplResultConvention Convention;
BuiltType Type;
public:
using ConventionType = ImplResultConvention;
static Optional<ConventionType>
getConventionFromString(StringRef conventionString) {
if (conventionString == "@out")
return ConventionType::Indirect;
if (conventionString == "@owned")
return ConventionType::Owned;
if (conventionString == "@unowned")
return ConventionType::Unowned;
if (conventionString == "@unowned_inner_pointer")
return ConventionType::UnownedInnerPointer;
if (conventionString == "@autoreleased")
return ConventionType::Autoreleased;
return None;
}
ImplFunctionResult(ImplResultConvention convention, BuiltType type)
: Convention(convention), Type(type) {}
ImplResultConvention getConvention() const { return Convention; }
BuiltType getType() const { return Type; }
};
enum class ImplFunctionRepresentation {
Thick,
Block,
Thin,
CFunctionPointer,
Method,
ObjCMethod,
WitnessMethod,
Closure
};
class ImplFunctionTypeFlags {
unsigned Rep : 3;
unsigned Pseudogeneric : 1;
unsigned Escaping : 1;
public:
ImplFunctionTypeFlags() : Rep(0), Pseudogeneric(0), Escaping(0) {}
ImplFunctionTypeFlags(ImplFunctionRepresentation rep,
bool pseudogeneric, bool noescape)
: Rep(unsigned(rep)), Pseudogeneric(pseudogeneric), Escaping(noescape) {}
ImplFunctionTypeFlags
withRepresentation(ImplFunctionRepresentation rep) const {
return ImplFunctionTypeFlags(rep, Pseudogeneric, Escaping);
}
ImplFunctionTypeFlags
withEscaping() const {
return ImplFunctionTypeFlags(ImplFunctionRepresentation(Rep),
Pseudogeneric, true);
}
ImplFunctionTypeFlags
withPseudogeneric() const {
return ImplFunctionTypeFlags(ImplFunctionRepresentation(Rep),
true, Escaping);
}
ImplFunctionRepresentation getRepresentation() const {
return ImplFunctionRepresentation(Rep);
}
bool isEscaping() const { return Escaping; }
bool isPseudogeneric() const { return Pseudogeneric; }
};
#if SWIFT_OBJC_INTEROP
/// For a mangled node that refers to an Objective-C class or protocol,
@@ -391,12 +532,11 @@ class TypeDecoder {
return Builder.createFunctionType(parameters, result, flags);
}
case NodeKind::ImplFunctionType: {
// Minimal support for lowered function types. These come up in
// reflection as capture types. For the reflection library's
// purposes, the only part that matters is the convention.
//
// TODO: Do we want to reflect @escaping?
FunctionTypeFlags flags;
auto calleeConvention = ImplParameterConvention::Direct_Unowned;
std::vector<ImplFunctionParam<BuiltType>> parameters;
std::vector<ImplFunctionResult<BuiltType>> results;
std::vector<ImplFunctionResult<BuiltType>> errorResults;
ImplFunctionTypeFlags flags;
for (unsigned i = 0; i < Node->getNumChildren(); i++) {
auto child = Node->getChild(i);
@@ -407,7 +547,9 @@ class TypeDecoder {
if (child->getText() == "@convention(thin)") {
flags =
flags.withConvention(FunctionMetadataConvention::Thin);
flags.withRepresentation(ImplFunctionRepresentation::Thin);
} else if (child->getText() == "@callee_guaranteed") {
calleeConvention = ImplParameterConvention::Direct_Guaranteed;
}
} else if (child->getKind() == NodeKind::ImplFunctionAttribute) {
if (!child->hasText())
@@ -416,24 +558,46 @@ class TypeDecoder {
StringRef text = child->getText();
if (text == "@convention(c)") {
flags =
flags.withConvention(FunctionMetadataConvention::CFunctionPointer);
flags.withRepresentation(ImplFunctionRepresentation::CFunctionPointer);
} else if (text == "@convention(block)") {
flags =
flags.withConvention(FunctionMetadataConvention::Block);
flags.withRepresentation(ImplFunctionRepresentation::Block);
}
} else if (child->getKind() == NodeKind::ImplEscaping) {
flags = flags.withEscaping(true);
flags = flags.withEscaping();
} else if (child->getKind() == NodeKind::ImplParameter) {
if (decodeImplFunctionPart(child, parameters))
return BuiltType();
} else if (child->getKind() == NodeKind::ImplResult) {
if (decodeImplFunctionPart(child, results))
return BuiltType();
} else if (child->getKind() == NodeKind::ImplErrorResult) {
if (decodeImplFunctionPart(child, errorResults))
return BuiltType();
} else {
return BuiltType();
}
}
// Completely punt on argument types and results.
std::vector<FunctionParam<BuiltType>> parameters;
Optional<ImplFunctionResult<BuiltType>> errorResult;
switch (errorResults.size()) {
case 0:
break;
case 1:
errorResult = errorResults.front();
break;
default:
return BuiltType();
}
std::vector<BuiltType> elements;
std::string labels;
auto result = Builder.createTupleType(elements, std::move(labels), false);
return Builder.createFunctionType(parameters, result, flags);
// TODO: Some cases not handled above, but *probably* they cannot
// appear as the types of values in SIL (yet?):
// - functions with yield returns
// - functions with generic signatures
// - foreign error conventions
return Builder.createImplFunctionType(calleeConvention,
parameters, results,
errorResult, flags);
}
case NodeKind::ArgumentTuple:
@@ -577,6 +741,29 @@ class TypeDecoder {
}
private:
template <typename T>
bool decodeImplFunctionPart(Demangle::NodePointer node,
std::vector<T> &results) {
if (node->getNumChildren() != 2)
return true;
if (node->getChild(0)->getKind() != Node::Kind::ImplConvention ||
node->getChild(1)->getKind() != Node::Kind::Type)
return true;
StringRef conventionString = node->getChild(0)->getText();
Optional<typename T::ConventionType> convention =
T::getConventionFromString(conventionString);
if (!convention)
return true;
BuiltType type = decodeMangledType(node->getChild(1));
if (!type)
return true;
results.emplace_back(*convention, type);
return false;
}
bool decodeMangledTypeDecl(Demangle::NodePointer node,
BuiltTypeDecl &typeDecl,
BuiltType &parent,

View File

@@ -275,6 +275,39 @@ public:
return FunctionTypeRef::create(*this, params, result, flags);
}
const FunctionTypeRef *createImplFunctionType(
Demangle::ImplParameterConvention calleeConvention,
ArrayRef<Demangle::ImplFunctionParam<const TypeRef *>> params,
ArrayRef<Demangle::ImplFunctionResult<const TypeRef *>> results,
Optional<Demangle::ImplFunctionResult<const TypeRef *>> errorResult,
ImplFunctionTypeFlags flags) {
// Minimal support for lowered function types. These come up in
// reflection as capture types. For the reflection library's
// purposes, the only part that matters is the convention.
FunctionTypeFlags funcFlags;
switch (flags.getRepresentation()) {
case Demangle::ImplFunctionRepresentation::Thick:
case Demangle::ImplFunctionRepresentation::Closure:
funcFlags = funcFlags.withConvention(FunctionMetadataConvention::Swift);
break;
case Demangle::ImplFunctionRepresentation::Thin:
case Demangle::ImplFunctionRepresentation::Method:
case Demangle::ImplFunctionRepresentation::ObjCMethod:
case Demangle::ImplFunctionRepresentation::WitnessMethod:
funcFlags = funcFlags.withConvention(FunctionMetadataConvention::Thin);
break;
case Demangle::ImplFunctionRepresentation::CFunctionPointer:
funcFlags = funcFlags.withConvention(FunctionMetadataConvention::CFunctionPointer);
break;
case Demangle::ImplFunctionRepresentation::Block:
funcFlags = funcFlags.withConvention(FunctionMetadataConvention::Block);
break;
}
auto result = createTupleType({}, "", false);
return FunctionTypeRef::create(*this, {}, result, funcFlags);
}
const ProtocolCompositionTypeRef *
createProtocolCompositionType(ArrayRef<BuiltProtocolDecl> protocols,
BuiltType superclass,

View File

@@ -313,6 +313,115 @@ Type ASTBuilder::createFunctionType(
return FunctionType::get(funcParams, output, einfo);
}
static ParameterConvention
getParameterConvention(ImplParameterConvention conv) {
switch (conv) {
case Demangle::ImplParameterConvention::Indirect_In:
return ParameterConvention::Indirect_In;
case Demangle::ImplParameterConvention::Indirect_In_Constant:
return ParameterConvention::Indirect_In_Constant;
case Demangle::ImplParameterConvention::Indirect_In_Guaranteed:
return ParameterConvention::Indirect_In_Guaranteed;
case Demangle::ImplParameterConvention::Indirect_Inout:
return ParameterConvention::Indirect_Inout;
case Demangle::ImplParameterConvention::Indirect_InoutAliasable:
return ParameterConvention::Indirect_InoutAliasable;
case Demangle::ImplParameterConvention::Direct_Owned:
return ParameterConvention::Direct_Owned;
case Demangle::ImplParameterConvention::Direct_Unowned:
return ParameterConvention::Direct_Unowned;
case Demangle::ImplParameterConvention::Direct_Guaranteed:
return ParameterConvention::Direct_Guaranteed;
}
}
static ResultConvention getResultConvention(ImplResultConvention conv) {
switch (conv) {
case Demangle::ImplResultConvention::Indirect:
return ResultConvention::Indirect;
case Demangle::ImplResultConvention::Owned:
return ResultConvention::Owned;
case Demangle::ImplResultConvention::Unowned:
return ResultConvention::Unowned;
case Demangle::ImplResultConvention::UnownedInnerPointer:
return ResultConvention::UnownedInnerPointer;
case Demangle::ImplResultConvention::Autoreleased:
return ResultConvention::Autoreleased;
}
}
Type ASTBuilder::createImplFunctionType(
Demangle::ImplParameterConvention calleeConvention,
ArrayRef<Demangle::ImplFunctionParam<Type>> params,
ArrayRef<Demangle::ImplFunctionResult<Type>> results,
Optional<Demangle::ImplFunctionResult<Type>> errorResult,
ImplFunctionTypeFlags flags) {
GenericSignature *genericSig = nullptr;
SILCoroutineKind funcCoroutineKind = SILCoroutineKind::None;
ParameterConvention funcCalleeConvention =
getParameterConvention(calleeConvention);
SILFunctionTypeRepresentation representation;
switch (flags.getRepresentation()) {
case ImplFunctionRepresentation::Thick:
representation = SILFunctionTypeRepresentation::Thick;
break;
case ImplFunctionRepresentation::Block:
representation = SILFunctionTypeRepresentation::Block;
break;
case ImplFunctionRepresentation::Thin:
representation = SILFunctionTypeRepresentation::Thin;
break;
case ImplFunctionRepresentation::CFunctionPointer:
representation = SILFunctionTypeRepresentation::CFunctionPointer;
break;
case ImplFunctionRepresentation::Method:
representation = SILFunctionTypeRepresentation::Method;
break;
case ImplFunctionRepresentation::ObjCMethod:
representation = SILFunctionTypeRepresentation::ObjCMethod;
break;
case ImplFunctionRepresentation::WitnessMethod:
representation = SILFunctionTypeRepresentation::WitnessMethod;
break;
case ImplFunctionRepresentation::Closure:
representation = SILFunctionTypeRepresentation::Closure;
break;
}
auto einfo = SILFunctionType::ExtInfo(representation,
flags.isPseudogeneric(),
!flags.isEscaping());
llvm::SmallVector<SILParameterInfo, 8> funcParams;
llvm::SmallVector<SILYieldInfo, 8> funcYields;
llvm::SmallVector<SILResultInfo, 8> funcResults;
Optional<SILResultInfo> funcErrorResult;
for (const auto &param : params) {
auto type = param.getType()->getCanonicalType();
auto conv = getParameterConvention(param.getConvention());
funcParams.emplace_back(type, conv);
}
for (const auto &result : results) {
auto type = result.getType()->getCanonicalType();
auto conv = getResultConvention(result.getConvention());
funcResults.emplace_back(type, conv);
}
if (errorResult) {
auto type = errorResult->getType()->getCanonicalType();
auto conv = getResultConvention(errorResult->getConvention());
funcErrorResult.emplace(type, conv);
}
return SILFunctionType::get(genericSig, einfo, funcCoroutineKind,
funcCalleeConvention, funcParams, funcYields,
funcResults, funcErrorResult, Ctx);
}
Type ASTBuilder::createProtocolCompositionType(
ArrayRef<ProtocolDecl *> protocols,
Type superclass,

View File

@@ -1167,6 +1167,16 @@ public:
result);
}
BuiltType createImplFunctionType(
Demangle::ImplParameterConvention calleeConvention,
ArrayRef<Demangle::ImplFunctionParam<BuiltType>> params,
ArrayRef<Demangle::ImplFunctionResult<BuiltType>> results,
Optional<Demangle::ImplFunctionResult<BuiltType>> errorResult,
ImplFunctionTypeFlags flags) {
// We can't realize the metadata for a SILFunctionType.
return BuiltType();
}
BuiltType createTupleType(ArrayRef<BuiltType> elements,
std::string labels,
bool variadic) const {

View File

@@ -0,0 +1,27 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift -emit-executable %s -g -o %t/lowered_function_types -emit-module
// RUN: sed -ne '/\/\/ *DEMANGLE: /s/\/\/ *DEMANGLE: *//p' < %s > %t/input
// RUN: %lldb-moduleimport-test %t/lowered_function_types -type-from-mangled=%t/input | %FileCheck %s
func blackHole(_: Any...) {}
class Class {}
do {
let fn: (Int, Class, __owned Class, Any, inout Int) -> (Int, Class, Any) = {
_, _, _, _, _ in fatalError()
}
blackHole(fn)
}
do {
let fn: () throws -> () = {}
blackHole(fn)
}
// DEMANGLE: $sSi22lowered_function_types5ClassCACypS2iACypIegygxnldor_D
// DEMANGLE: $ss5Error_pIegzo_D
// CHECK: @callee_guaranteed (Int, @guaranteed Class, @owned Class, @in_guaranteed Any, @inout Int) -> (Int, @owned Class, @out Any)
// CHECK: @callee_guaranteed () -> @error Error