diff --git a/include/swift/AST/ASTDemangler.h b/include/swift/AST/ASTDemangler.h index 28f8f7d080c..0e82d714a9a 100644 --- a/include/swift/AST/ASTDemangler.h +++ b/include/swift/AST/ASTDemangler.h @@ -82,6 +82,13 @@ public: Type createFunctionType(ArrayRef> params, Type output, FunctionTypeFlags flags); + Type createImplFunctionType( + Demangle::ImplParameterConvention calleeConvention, + ArrayRef> params, + ArrayRef> results, + Optional> errorResult, + ImplFunctionTypeFlags flags); + Type createProtocolCompositionType(ArrayRef protocols, Type superclass, bool isClassBound); diff --git a/include/swift/Demangling/TypeDecoder.h b/include/swift/Demangling/TypeDecoder.h index 03cc53abcbf..85107cfc7b9 100644 --- a/include/swift/Demangling/TypeDecoder.h +++ b/include/swift/Demangling/TypeDecoder.h @@ -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 +class ImplFunctionParam { + ImplParameterConvention Convention; + BuiltType Type; + +public: + using ConventionType = ImplParameterConvention; + + static Optional + 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 +class ImplFunctionResult { + ImplResultConvention Convention; + BuiltType Type; + +public: + using ConventionType = ImplResultConvention; + + static Optional + 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> parameters; + std::vector> results; + std::vector> 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> parameters; + Optional> errorResult; + switch (errorResults.size()) { + case 0: + break; + case 1: + errorResult = errorResults.front(); + break; + default: + return BuiltType(); + } - std::vector 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 + bool decodeImplFunctionPart(Demangle::NodePointer node, + std::vector &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 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, diff --git a/include/swift/Reflection/TypeRefBuilder.h b/include/swift/Reflection/TypeRefBuilder.h index 5dfb8d55be2..572e6faba61 100644 --- a/include/swift/Reflection/TypeRefBuilder.h +++ b/include/swift/Reflection/TypeRefBuilder.h @@ -275,6 +275,39 @@ public: return FunctionTypeRef::create(*this, params, result, flags); } + const FunctionTypeRef *createImplFunctionType( + Demangle::ImplParameterConvention calleeConvention, + ArrayRef> params, + ArrayRef> results, + Optional> 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 protocols, BuiltType superclass, diff --git a/lib/AST/ASTDemangler.cpp b/lib/AST/ASTDemangler.cpp index 610f8effbec..3f33a8bb95d 100644 --- a/lib/AST/ASTDemangler.cpp +++ b/lib/AST/ASTDemangler.cpp @@ -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> params, + ArrayRef> results, + Optional> 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 funcParams; + llvm::SmallVector funcYields; + llvm::SmallVector funcResults; + Optional funcErrorResult; + + for (const auto ¶m : 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 protocols, Type superclass, diff --git a/stdlib/public/runtime/MetadataLookup.cpp b/stdlib/public/runtime/MetadataLookup.cpp index 393c58f1bb6..a5780c623e9 100644 --- a/stdlib/public/runtime/MetadataLookup.cpp +++ b/stdlib/public/runtime/MetadataLookup.cpp @@ -1167,6 +1167,16 @@ public: result); } + BuiltType createImplFunctionType( + Demangle::ImplParameterConvention calleeConvention, + ArrayRef> params, + ArrayRef> results, + Optional> errorResult, + ImplFunctionTypeFlags flags) { + // We can't realize the metadata for a SILFunctionType. + return BuiltType(); + } + BuiltType createTupleType(ArrayRef elements, std::string labels, bool variadic) const { diff --git a/test/TypeDecoder/lowered_function_types.swift b/test/TypeDecoder/lowered_function_types.swift new file mode 100644 index 00000000000..697c6dad137 --- /dev/null +++ b/test/TypeDecoder/lowered_function_types.swift @@ -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 \ No newline at end of file