From 736d0da1ca96fbeb966cab11def4fefd54ebbe8d Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 26 Oct 2023 12:11:37 -0400 Subject: [PATCH 1/6] ASTDumper: Better dumping of the thrown error type of a function type --- lib/AST/ASTDumper.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 1d2b1bc1e0623..9c507613f1476 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -4224,9 +4224,6 @@ namespace { printFlag(T->isAsync(), "async"); printFlag(T->isThrowing(), "throws"); } - if (Type thrownError = T->getThrownError()) { - printFieldQuoted(thrownError.getString(), "thrown_error"); - } if (Type globalActor = T->getGlobalActor()) { printFieldQuoted(globalActor.getString(), "global_actor"); } @@ -4234,6 +4231,9 @@ namespace { printClangTypeRec(T->getClangTypeInfo(), T->getASTContext()); printAnyFunctionParamsRec(T->getParams(), "input"); printRec(T->getResult(), "output"); + if (Type thrownError = T->getThrownError()) { + printRec(thrownError, "thrown_error"); + } } void visitFunctionType(FunctionType *T, StringRef label) { From 1d05c356c4c95e9594e2fdbd0bc576b50effc42c Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 26 Oct 2023 22:40:04 -0400 Subject: [PATCH 2/6] SIL: AbstractionPattern support for typed throws --- include/swift/SIL/AbstractionPattern.h | 26 ++++- include/swift/SIL/SILFunctionConventions.h | 2 + include/swift/SIL/SILType.h | 7 ++ lib/SIL/IR/AbstractionPattern.cpp | 125 ++++++++++++++++++++- lib/SIL/IR/SILType.cpp | 9 ++ 5 files changed, 160 insertions(+), 9 deletions(-) diff --git a/include/swift/SIL/AbstractionPattern.h b/include/swift/SIL/AbstractionPattern.h index 89daad2af85dc..e3692cbbf4d55 100644 --- a/include/swift/SIL/AbstractionPattern.h +++ b/include/swift/SIL/AbstractionPattern.h @@ -1521,6 +1521,22 @@ class AbstractionPattern { /// abstraction pattern for its result type. AbstractionPattern getFunctionResultType() const; + /// Given that the value being abstracted is a function, return the + /// abstraction pattern for its thrown error type. + llvm::Optional getFunctionThrownErrorType() const; + + /// Utility method to adjust a thrown error pattern and thrown error type + /// to account for some quirks in type lowering. + /// + /// When lowered with an opaque pattern, + /// + /// - () -> () becomes () -> (), + /// - () throws(any Error) -> () becomes () -> (@error any Error), + /// + /// *not* () -> (@error_indirect Never) or () -> (@error_indirect any Error). + llvm::Optional> + getFunctionThrownErrorType(CanAnyFunctionType substFnInterfaceType) const; + /// Given that the value being abstracted is a function type, return /// the abstraction pattern for one of its parameter types. AbstractionPattern getFunctionParamType(unsigned index) const; @@ -1611,15 +1627,19 @@ class AbstractionPattern { /// Given that this is a pack expansion, do the pack elements need to be /// passed indirectly? bool arePackElementsPassedIndirectly(TypeConverter &TC) const; - + /// If this abstraction pattern appears in function return position, how is /// the corresponding value returned? CallingConventionKind getResultConvention(TypeConverter &TC) const; - + /// If this abstraction pattern appears in function parameter position, how /// is the corresponding value passed? CallingConventionKind getParameterConvention(TypeConverter &TC) const; - + + /// If this abstraction pattern appears in function thrown error position, how + /// is the corresponding value passed? + CallingConventionKind getErrorConvention(TypeConverter &TC) const; + /// Generate the abstraction pattern for lowering the substituted SIL /// function type for a function type matching this abstraction pattern. /// diff --git a/include/swift/SIL/SILFunctionConventions.h b/include/swift/SIL/SILFunctionConventions.h index 28bb137740aec..50f4d94ca2c50 100644 --- a/include/swift/SIL/SILFunctionConventions.h +++ b/include/swift/SIL/SILFunctionConventions.h @@ -78,6 +78,8 @@ class SILModuleConventions { public: static bool isPassedIndirectlyInSIL(SILType type, SILModule &M); + static bool isThrownIndirectlyInSIL(SILType type, SILModule &M); + static bool isReturnedIndirectlyInSIL(SILType type, SILModule &M); static SILModuleConventions getLoweredAddressConventions(SILModule &M) { diff --git a/include/swift/SIL/SILType.h b/include/swift/SIL/SILType.h index fc56000e80363..75de16f90b36a 100644 --- a/include/swift/SIL/SILType.h +++ b/include/swift/SIL/SILType.h @@ -340,6 +340,13 @@ class SILType { return isAddressOnly(type, tc, sig, TypeExpansionContext::minimal()); } + /// Return true if this type must be thrown indirectly. + static bool isFormallyThrownIndirectly(CanType type, + Lowering::TypeConverter &tc, + CanGenericSignature sig) { + return isAddressOnly(type, tc, sig, TypeExpansionContext::minimal()); + } + /// True if the type, or the referenced type of an address type, is loadable. /// This is the opposite of isAddressOnly. bool isLoadable(const SILFunction &F) const { diff --git a/lib/SIL/IR/AbstractionPattern.cpp b/lib/SIL/IR/AbstractionPattern.cpp index ee4d4c7a40b1a..7a1c7ec4b49dc 100644 --- a/lib/SIL/IR/AbstractionPattern.cpp +++ b/lib/SIL/IR/AbstractionPattern.cpp @@ -1214,6 +1214,78 @@ AbstractionPattern AbstractionPattern::getFunctionResultType() const { llvm_unreachable("bad kind"); } +llvm::Optional +AbstractionPattern::getFunctionThrownErrorType() const { + switch (getKind()) { + case Kind::Invalid: + llvm_unreachable("querying invalid abstraction pattern!"); + case Kind::ObjCCompletionHandlerArgumentsType: + case Kind::Tuple: + llvm_unreachable("abstraction pattern for tuple cannot be function"); + case Kind::Opaque: + return *this; + case Kind::Type: { + if (isTypeParameterOrOpaqueArchetype()) + return getOpaque(); + + if (auto errorType = cast(getType())->getEffectiveThrownErrorType()) { + return AbstractionPattern(getGenericSubstitutions(), + getGenericSignatureForFunctionComponent(), + (*errorType)->getCanonicalType()); + } + + return llvm::None; + } + case Kind::Discard: + llvm_unreachable("don't need to discard function abstractions yet"); + case Kind::ClangType: + case Kind::CFunctionAsMethodType: + case Kind::PartialCurriedCFunctionAsMethodType: + case Kind::CXXMethodType: + case Kind::PartialCurriedCXXMethodType: + case Kind::CurriedObjCMethodType: + case Kind::CurriedCFunctionAsMethodType: + case Kind::CurriedCXXMethodType: + case Kind::PartialCurriedObjCMethodType: + case Kind::ObjCMethodType: + llvm_unreachable("implement me"); + case Kind::OpaqueFunction: + case Kind::OpaqueDerivativeFunction: + return llvm::None; + } + llvm_unreachable("bad kind"); +} + +llvm::Optional> +AbstractionPattern::getFunctionThrownErrorType( + CanAnyFunctionType substFnInterfaceType) const { + auto optOrigErrorType = getFunctionThrownErrorType(); + if (!optOrigErrorType) + return llvm::None; + + auto &ctx = substFnInterfaceType->getASTContext(); + auto optErrorType = substFnInterfaceType->getEffectiveThrownErrorType(); + + if (isTypeParameterOrOpaqueArchetype()) { + if (!optErrorType) + return llvm::None; + + if (!(*optErrorType)->isEqual(ctx.getErrorExistentialType())) { + llvm::errs() << "unsupported reabstraction\n"; + abort(); + } + + return std::make_pair(AbstractionPattern(*optErrorType), + (*optErrorType)->getCanonicalType()); + } + + if (!optErrorType) + optErrorType = ctx.getErrorExistentialType(); + + return std::make_pair(*optOrigErrorType, + (*optErrorType)->getCanonicalType()); +} + AbstractionPattern AbstractionPattern::getObjCMethodAsyncCompletionHandlerType( CanType swiftCompletionHandlerType) const { @@ -1939,7 +2011,7 @@ AbstractionPattern::getParameterConvention(TypeConverter &TC) const { case Kind::Opaque: // Maximally abstracted values are always passed indirectly. return Indirect; - + case Kind::OpaqueFunction: case Kind::OpaqueDerivativeFunction: case Kind::PartialCurriedObjCMethodType: @@ -1953,16 +2025,57 @@ AbstractionPattern::getParameterConvention(TypeConverter &TC) const { case Kind::PartialCurriedCXXMethodType: // Function types are always passed directly return Direct; - + case Kind::ClangType: case Kind::Type: case Kind::Discard: // Pass according to the formal type. return SILType::isFormallyPassedIndirectly(getType(), - TC, - getGenericSignatureOrNull()) + TC, + getGenericSignatureOrNull()) ? Indirect : Direct; - + + case Kind::Invalid: + case Kind::Tuple: + case Kind::ObjCCompletionHandlerArgumentsType: + llvm_unreachable("should not get here"); + } +} + +AbstractionPattern::CallingConventionKind +AbstractionPattern::getErrorConvention(TypeConverter &TC) const { + // Tuples should be destructured. + if (isTuple()) { + return Destructured; + } + switch (getKind()) { + case Kind::Opaque: + // Maximally abstracted values are always thrown indirectly. + return Indirect; + + case Kind::OpaqueFunction: + case Kind::OpaqueDerivativeFunction: + case Kind::PartialCurriedObjCMethodType: + case Kind::CurriedObjCMethodType: + case Kind::PartialCurriedCFunctionAsMethodType: + case Kind::CurriedCFunctionAsMethodType: + case Kind::CFunctionAsMethodType: + case Kind::ObjCMethodType: + case Kind::CXXMethodType: + case Kind::CurriedCXXMethodType: + case Kind::PartialCurriedCXXMethodType: + // Function types are always thrown directly + return Direct; + + case Kind::ClangType: + case Kind::Type: + case Kind::Discard: + // Pass according to the formal type. + return SILType::isFormallyThrownIndirectly(getType(), + TC, + getGenericSignatureOrNull()) + ? Indirect : Direct; + case Kind::Invalid: case Kind::Tuple: case Kind::ObjCCompletionHandlerArgumentsType: @@ -2562,7 +2675,7 @@ const { auto substTy = visitor.handleUnabstractedFunctionType(substType, *this, substYieldType, origYieldType); - + auto substSig = buildGenericSignature(TC.Context, GenericSignature(), std::move(visitor.substGenericParams), std::move(visitor.substRequirements)) diff --git a/lib/SIL/IR/SILType.cpp b/lib/SIL/IR/SILType.cpp index b2b56cc66c741..3773e26a7d789 100644 --- a/lib/SIL/IR/SILType.cpp +++ b/lib/SIL/IR/SILType.cpp @@ -713,6 +713,15 @@ bool SILModuleConventions::isPassedIndirectlyInSIL(SILType type, SILModule &M) { return false; } +bool SILModuleConventions::isThrownIndirectlyInSIL(SILType type, SILModule &M) { + if (SILModuleConventions(M).loweredAddresses) { + return M.Types.getTypeLowering(type, TypeExpansionContext::minimal()) + .isAddressOnly(); + } + + return false; +} + bool SILFunctionType::isNoReturnFunction(SILModule &M, TypeExpansionContext context) const { for (unsigned i = 0, e = getNumResults(); i < e; ++i) { From b84df92b702ccdfc27bbc2ffdab4f79d79e1f337 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 26 Oct 2023 22:46:39 -0400 Subject: [PATCH 3/6] SIL: Type lowering support for indirect error results --- lib/SIL/IR/AbstractionPattern.cpp | 18 ++++++++-- lib/SIL/IR/SILFunctionType.cpp | 56 +++++++++++++++++-------------- 2 files changed, 46 insertions(+), 28 deletions(-) diff --git a/lib/SIL/IR/AbstractionPattern.cpp b/lib/SIL/IR/AbstractionPattern.cpp index 7a1c7ec4b49dc..aee5dbc08c1d2 100644 --- a/lib/SIL/IR/AbstractionPattern.cpp +++ b/lib/SIL/IR/AbstractionPattern.cpp @@ -2627,14 +2627,28 @@ class SubstFunctionTypePatternVisitor if (yieldType) { substYieldType = visit(yieldType, yieldPattern); } - + + CanType newErrorType; + + if (auto optPair = pattern.getFunctionThrownErrorType(func)) { + auto errorPattern = optPair->first; + auto errorType = optPair->second; + newErrorType = visit(errorType, errorPattern); + } + auto newResultTy = visit(func.getResult(), pattern.getFunctionResultType()); llvm::Optional extInfo; if (func->hasExtInfo()) extInfo = func->getExtInfo(); - + + if (newErrorType) { + if (!extInfo) + extInfo = FunctionType::ExtInfo(); + extInfo = extInfo->withThrows(true, newErrorType); + } + return CanFunctionType::get(FunctionType::CanParamArrayRef(newParams), newResultTy, extInfo); } diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index ca784bace843a..7fe91d0f66fe1 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -2190,32 +2190,6 @@ static CanSILFunctionType getSILFunctionType( isAsync = true; } - // Map 'throws' to the appropriate error convention. - // Give the type an error argument whether the substituted type semantically - // `throws` or if the abstraction pattern specifies a Swift function type - // that also throws. This prevents the need for a second possibly-thunking - // conversion when using a non-throwing function in more abstract throwing - // context. - bool isThrowing = substFnInterfaceType->getExtInfo().isThrowing(); - if (auto origFnType = origType.getAs()) { - isThrowing |= origFnType->getExtInfo().isThrowing(); - } - if (isThrowing && !foreignInfo.error && - !foreignInfo.async) { - assert(!origType.isForeign() - && "using native Swift error convention for foreign type!"); - SILType exnType; - if (CanType thrownError = substFnInterfaceType.getThrownError()) { - exnType = TC.getLoweredType(thrownError, expansionContext); - } else { - // Untyped error throws the exception type. - exnType = SILType::getExceptionType(TC.Context); - } - assert(exnType.isObject()); - errorResult = SILResultInfo(exnType.getASTType(), - ResultConvention::Owned); - } - // Get the yield type for an accessor coroutine. SILCoroutineKind coroutineKind = SILCoroutineKind::None; AbstractionPattern coroutineOrigYieldType = AbstractionPattern::getInvalid(); @@ -2308,6 +2282,36 @@ static CanSILFunctionType getSILFunctionType( } } + // Map 'throws' to the appropriate error convention. + // Give the type an error argument whether the substituted type semantically + // `throws` or if the abstraction pattern specifies a Swift function type + // that also throws. This prevents the need for a second possibly-thunking + // conversion when using a non-throwing function in more abstract throwing + // context. + bool isThrowing = substFnInterfaceType->getExtInfo().isThrowing(); + if (auto origFnType = origType.getAs()) { + isThrowing |= origFnType->getExtInfo().isThrowing(); + } + + if (isThrowing && !foreignInfo.error && + !foreignInfo.async) { + assert(!origType.isForeign() + && "using native Swift error convention for foreign type!"); + auto optPair = origType.getFunctionThrownErrorType(substFnInterfaceType); + assert(optPair && + "Lowering a throwing function type against non-throwing pattern"); + + auto origErrorType = optPair->first; + auto errorType = optPair->second; + auto &errorTLConv = TC.getTypeLowering(origErrorType, errorType, + TypeExpansionContext::minimal()); + + errorResult = SILResultInfo(errorTLConv.getLoweredType().getASTType(), + errorTLConv.isAddressOnly() + ? ResultConvention::Indirect + : ResultConvention::Owned); + } + // Lower the result type. AbstractionPattern origResultType = origType.getFunctionResultType(); CanType substFormalResultType = substFnInterfaceType.getResult(); From eac491ccd29ddce5b226de73b3c0a2af1c439d72 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 26 Oct 2023 12:14:15 -0400 Subject: [PATCH 4/6] SIL: Fix memory lifetime verifier check for the thrown error result --- include/swift/SIL/SILArgument.h | 2 ++ lib/SIL/IR/SILArgument.cpp | 9 +++++++++ lib/SIL/Verifier/MemoryLifetimeVerifier.cpp | 7 ++++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/include/swift/SIL/SILArgument.h b/include/swift/SIL/SILArgument.h index 62786af072672..941a9bc57b785 100644 --- a/include/swift/SIL/SILArgument.h +++ b/include/swift/SIL/SILArgument.h @@ -433,6 +433,8 @@ class SILFunctionArgument : public SILArgument { bool isIndirectResult() const; + bool isIndirectErrorResult() const; + SILArgumentConvention getArgumentConvention() const; /// Given that this is an entry block argument, and given that it does diff --git a/lib/SIL/IR/SILArgument.cpp b/lib/SIL/IR/SILArgument.cpp index 2b0429a95054e..30ec6a195c05a 100644 --- a/lib/SIL/IR/SILArgument.cpp +++ b/lib/SIL/IR/SILArgument.cpp @@ -64,6 +64,15 @@ bool SILFunctionArgument::isIndirectResult() const { return getIndex() < numIndirectResults; } +bool SILFunctionArgument::isIndirectErrorResult() const { + auto numIndirectResults = + getFunction()->getConventions().getNumIndirectSILResults(); + auto numIndirectErrorResults = + getFunction()->getConventions().getNumIndirectSILErrorResults(); + return ((getIndex() >= numIndirectResults) && + (getIndex() < numIndirectResults + numIndirectErrorResults)); +} + SILArgumentConvention SILFunctionArgument::getArgumentConvention() const { return getFunction()->getConventions().getSILArgumentConvention(getIndex()); } diff --git a/lib/SIL/Verifier/MemoryLifetimeVerifier.cpp b/lib/SIL/Verifier/MemoryLifetimeVerifier.cpp index b6244bde9c20e..11866ad54277c 100644 --- a/lib/SIL/Verifier/MemoryLifetimeVerifier.cpp +++ b/lib/SIL/Verifier/MemoryLifetimeVerifier.cpp @@ -563,7 +563,12 @@ void MemoryLifetimeVerifier::checkFunction(BitDataflow &dataFlow) { locations.setBits(expectedThrowBits, funcArg); break; case SILArgumentConvention::Indirect_Out: - locations.setBits(expectedReturnBits, funcArg); + if (funcArg->isIndirectErrorResult()) { + locations.setBits(expectedThrowBits, funcArg); + } else { + assert(funcArg->isIndirectResult()); + locations.setBits(expectedReturnBits, funcArg); + } break; default: break; From 4ed2dc44287d07e9b898881545650f150322185a Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 26 Oct 2023 12:16:42 -0400 Subject: [PATCH 5/6] SILGen: Don't use CanType() on a type that might be a sugared type --- lib/SILGen/SILGenProlog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SILGen/SILGenProlog.cpp b/lib/SILGen/SILGenProlog.cpp index bfaa7d6005ef0..d9dd8a02f9bac 100644 --- a/lib/SILGen/SILGenProlog.cpp +++ b/lib/SILGen/SILGenProlog.cpp @@ -1765,7 +1765,7 @@ uint16_t SILGenFunction::emitBasicProlog( AbstractionPattern origResultType = origClosureType ? origClosureType->getFunctionResultType() : AbstractionPattern(genericSig.getCanonicalSignature(), - CanType(resultType)); + resultType->getCanonicalType()); emitIndirectResultParameters(*this, resultType, origResultType, DC); From 581776b9975bb1b454f3fbc55326a081eb11f995 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 26 Oct 2023 12:19:50 -0400 Subject: [PATCH 6/6] SILGen: Emit indirect error result argument --- lib/SILGen/SILGenBackDeploy.cpp | 3 +- lib/SILGen/SILGenBridging.cpp | 5 +- lib/SILGen/SILGenConstructor.cpp | 36 +++++++---- lib/SILGen/SILGenDestructor.cpp | 8 +-- lib/SILGen/SILGenEpilog.cpp | 29 ++++++--- lib/SILGen/SILGenFunction.cpp | 49 ++++++++------ lib/SILGen/SILGenFunction.h | 33 ++++++---- lib/SILGen/SILGenProlog.cpp | 90 ++++++++++++++++++++------ lib/SILGen/SILGenTopLevel.cpp | 3 +- test/SILGen/typed_throws_generic.swift | 6 ++ 10 files changed, 182 insertions(+), 80 deletions(-) create mode 100644 test/SILGen/typed_throws_generic.swift diff --git a/lib/SILGen/SILGenBackDeploy.cpp b/lib/SILGen/SILGenBackDeploy.cpp index 5fd29d9553cb7..b8be13ecd16cd 100644 --- a/lib/SILGen/SILGenBackDeploy.cpp +++ b/lib/SILGen/SILGenBackDeploy.cpp @@ -237,7 +237,8 @@ void SILGenFunction::emitBackDeploymentThunk(SILDeclRef thunk) { paramsForForwarding.emplace_back(param.forward(*this)); } - prepareEpilog(getResultInterfaceType(AFD), + prepareEpilog(AFD, + getResultInterfaceType(AFD), AFD->getEffectiveThrownErrorType(), CleanupLocation(AFD)); diff --git a/lib/SILGen/SILGenBridging.cpp b/lib/SILGen/SILGenBridging.cpp index c413a0d56a190..340cb4035c025 100644 --- a/lib/SILGen/SILGenBridging.cpp +++ b/lib/SILGen/SILGenBridging.cpp @@ -2134,7 +2134,10 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) { // Set up the throw destination if necessary. CleanupLocation cleanupLoc(fd); if (thrownErrorType) { - prepareRethrowEpilog(*thrownErrorType, cleanupLoc); + prepareRethrowEpilog(fd, + AbstractionPattern(*thrownErrorType), + *thrownErrorType, + cleanupLoc); } SILValue result; diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 42c41acde35e9..456852eb49181 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -658,10 +658,11 @@ void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) { assert(selfLV); // Emit the prolog. - emitBasicProlog(ctor->getParameters(), + emitBasicProlog(ctor, + ctor->getParameters(), /*selfParam=*/nullptr, - ctor->getResultInterfaceType(), ctor, - ctor->hasThrows(), + ctor->getResultInterfaceType(), + ctor->getEffectiveThrownErrorType(), ctor->getThrowsLoc(), /*ignored parameters*/ 1); emitConstructorMetatypeArg(*this, ctor); @@ -680,7 +681,9 @@ void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) { // Create a basic block to jump to for the implicit 'self' return. // We won't emit this until after we've emitted the body. // The epilog takes a void return because the return of 'self' is implicit. - prepareEpilog(llvm::None, ctor->getEffectiveThrownErrorType(), + prepareEpilog(ctor, + llvm::None, + ctor->getEffectiveThrownErrorType(), CleanupLocation(ctor)); // If the constructor can fail, set up an alternative epilog for constructor @@ -1105,9 +1108,11 @@ void SILGenFunction::emitClassConstructorInitializer(ConstructorDecl *ctor) { // Emit the prolog for the non-self arguments. // FIXME: Handle self along with the other body patterns. - uint16_t ArgNo = emitBasicProlog(ctor->getParameters(), /*selfParam=*/nullptr, - TupleType::getEmpty(F.getASTContext()), ctor, - ctor->hasThrows(), ctor->getThrowsLoc(), + uint16_t ArgNo = emitBasicProlog(ctor, + ctor->getParameters(), /*selfParam=*/nullptr, + TupleType::getEmpty(F.getASTContext()), + ctor->getEffectiveThrownErrorType(), + ctor->getThrowsLoc(), /*ignored parameters*/ 1); SILType selfTy = getLoweredLoadableType(selfDecl->getTypeInContext()); @@ -1185,7 +1190,9 @@ void SILGenFunction::emitClassConstructorInitializer(ConstructorDecl *ctor) { // Create a basic block to jump to for the implicit 'self' return. // We won't emit the block until after we've emitted the body. - prepareEpilog(llvm::None, ctor->getEffectiveThrownErrorType(), + prepareEpilog(ctor, + llvm::None, + ctor->getEffectiveThrownErrorType(), CleanupLocation(endOfInitLoc)); auto resultType = ctor->mapTypeIntoContext(ctor->getResultInterfaceType()); @@ -1680,7 +1687,7 @@ void SILGenFunction::emitIVarInitializer(SILDeclRef ivarInitializer) { VarLocs[selfDecl] = VarLoc::get(selfArg); auto cleanupLoc = CleanupLocation(loc); - prepareEpilog(llvm::None, llvm::None, cleanupLoc); + prepareEpilog(cd, llvm::None, llvm::None, cleanupLoc); // Emit the initializers. emitMemberInitializers(cd, selfDecl, cd); @@ -1732,9 +1739,11 @@ void SILGenFunction::emitInitAccessor(AccessorDecl *accessor) { auto accessedProperties = accessor->getAccessedProperties(); // Emit `newValue` argument. - emitBasicProlog(accessor->getParameters(), /*selfParam=*/nullptr, - TupleType::getEmpty(F.getASTContext()), accessor, - /*throws=*/false, /*throwsLoc=*/SourceLoc(), + emitBasicProlog(accessor, + accessor->getParameters(), /*selfParam=*/nullptr, + TupleType::getEmpty(F.getASTContext()), + /*errorType=*/llvm::None, + /*throwsLoc=*/SourceLoc(), /*ignored parameters*/ accessedProperties.size()); @@ -1750,7 +1759,8 @@ void SILGenFunction::emitInitAccessor(AccessorDecl *accessor) { } } - prepareEpilog(accessor->getResultInterfaceType(), + prepareEpilog(accessor, + accessor->getResultInterfaceType(), accessor->getEffectiveThrownErrorType(), CleanupLocation(accessor)); diff --git a/lib/SILGen/SILGenDestructor.cpp b/lib/SILGen/SILGenDestructor.cpp index 2ecb757ecb02f..5f8c5e3f1d510 100644 --- a/lib/SILGen/SILGenDestructor.cpp +++ b/lib/SILGen/SILGenDestructor.cpp @@ -45,7 +45,7 @@ void SILGenFunction::emitDestroyingDestructor(DestructorDecl *dd) { // Create a basic block to jump to for the implicit destruction behavior // of releasing the elements and calling the superclass destructor. // We won't actually emit the block until we finish with the destructor body. - prepareEpilog(llvm::None, llvm::None, CleanupLocation(Loc)); + prepareEpilog(dd, llvm::None, llvm::None, CleanupLocation(Loc)); auto cleanupLoc = CleanupLocation(Loc); @@ -248,7 +248,7 @@ void SILGenFunction::emitDeallocatingMoveOnlyDestructor(DestructorDecl *dd) { // Create a basic block to jump to for the implicit destruction behavior // of releasing the elements and calling the superclass destructor. // We won't actually emit the block until we finish with the destructor body. - prepareEpilog(llvm::None, llvm::None, CleanupLocation(loc)); + prepareEpilog(dd, llvm::None, llvm::None, CleanupLocation(loc)); auto cleanupLoc = CleanupLocation(loc); @@ -286,7 +286,7 @@ void SILGenFunction::emitIVarDestroyer(SILDeclRef ivarDestroyer) { assert(selfValue); auto cleanupLoc = CleanupLocation(loc); - prepareEpilog(llvm::None, llvm::None, cleanupLoc); + prepareEpilog(cd, llvm::None, llvm::None, cleanupLoc); { Scope S(*this, cleanupLoc); // Self is effectively guaranteed for the duration of any destructor. For @@ -577,7 +577,7 @@ void SILGenFunction::emitObjCDestructor(SILDeclRef dtor) { // Create a basic block to jump to for the implicit destruction behavior // of releasing the elements and calling the superclass destructor. // We won't actually emit the block until we finish with the destructor body. - prepareEpilog(llvm::None, llvm::None, CleanupLocation(loc)); + prepareEpilog(dd, llvm::None, llvm::None, CleanupLocation(loc)); emitProfilerIncrement(dd->getTypecheckedBody()); // Emit the destructor body. diff --git a/lib/SILGen/SILGenEpilog.cpp b/lib/SILGen/SILGenEpilog.cpp index 4639bbf388ecf..0520c19af569a 100644 --- a/lib/SILGen/SILGenEpilog.cpp +++ b/lib/SILGen/SILGenEpilog.cpp @@ -20,9 +20,11 @@ using namespace swift; using namespace Lowering; -void SILGenFunction::prepareEpilog(llvm::Optional directResultType, - llvm::Optional exnType, - CleanupLocation CleanupL) { +void SILGenFunction::prepareEpilog(DeclContext *DC, + llvm::Optional directResultType, + llvm::Optional errorType, + CleanupLocation CleanupL, + llvm::Optional origClosureType) { auto *epilogBB = createBasicBlock(); // If we have any direct results, receive them via BB arguments. @@ -63,8 +65,14 @@ void SILGenFunction::prepareEpilog(llvm::Optional directResultType, ReturnDest = JumpDest(epilogBB, getCleanupsDepth(), CleanupL); - if (exnType) { - prepareRethrowEpilog(*exnType, CleanupL); + if (errorType) { + auto genericSig = DC->getGenericSignatureOfContext(); + AbstractionPattern origErrorType = origClosureType + ? *origClosureType->getFunctionThrownErrorType() + : AbstractionPattern(genericSig.getCanonicalSignature(), + (*errorType)->getCanonicalType()); + + prepareRethrowEpilog(DC, origErrorType, *errorType, CleanupL); } if (F.getLoweredFunctionType()->isCoroutine()) { @@ -73,10 +81,15 @@ void SILGenFunction::prepareEpilog(llvm::Optional directResultType, } void SILGenFunction::prepareRethrowEpilog( - Type exnType, CleanupLocation cleanupLoc) { - SILType loweredExnType = getLoweredType(exnType); + DeclContext *dc, AbstractionPattern origErrorType, Type errorType, + CleanupLocation cleanupLoc) { + SILBasicBlock *rethrowBB = createBasicBlock(FunctionSection::Postmatter); - rethrowBB->createPhiArgument(loweredExnType, OwnershipKind::Owned); + if (!IndirectErrorResult) { + SILType loweredErrorType = getLoweredType(origErrorType, errorType); + rethrowBB->createPhiArgument(loweredErrorType, OwnershipKind::Owned); + } + ThrowDest = JumpDest(rethrowBB, getCleanupsDepth(), cleanupLoc); } diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 0c8965d8307ee..1ae32f3216c45 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -1043,15 +1043,18 @@ void SILGenFunction::emitFunction(FuncDecl *fd) { MagicFunctionName = SILGenModule::getMagicFunctionName(fd); auto captureInfo = SGM.M.Types.getLoweredLocalCaptures(SILDeclRef(fd)); - emitProlog(captureInfo, fd->getParameters(), fd->getImplicitSelfDecl(), fd, - fd->getResultInterfaceType(), fd->hasThrows(), fd->getThrowsLoc()); + emitProlog(fd, captureInfo, fd->getParameters(), fd->getImplicitSelfDecl(), + fd->getResultInterfaceType(), fd->getEffectiveThrownErrorType(), + fd->getThrowsLoc()); if (fd->isDistributedActorFactory()) { // Synthesize the factory function body emitDistributedActorFactory(fd); } else { - prepareEpilog(fd->getResultInterfaceType(), - fd->getEffectiveThrownErrorType(), CleanupLocation(fd)); + prepareEpilog(fd, + fd->getResultInterfaceType(), + fd->getEffectiveThrownErrorType(), + CleanupLocation(fd)); if (fd->requiresUnavailableDeclABICompatibilityStubs()) emitApplyOfUnavailableCodeReached(); @@ -1071,15 +1074,17 @@ void SILGenFunction::emitFunction(FuncDecl *fd) { void SILGenFunction::emitClosure(AbstractClosureExpr *ace) { MagicFunctionName = SILGenModule::getMagicFunctionName(ace); OrigFnType = SGM.M.Types.getConstantAbstractionPattern(SILDeclRef(ace)); - + auto resultIfaceTy = ace->getResultType()->mapTypeOutOfContext(); + llvm::Optional errorIfaceTy; + if (auto optErrorTy = ace->getEffectiveThrownType()) + errorIfaceTy = (*optErrorTy)->mapTypeOutOfContext(); auto captureInfo = SGM.M.Types.getLoweredLocalCaptures( SILDeclRef(ace)); - emitProlog(captureInfo, ace->getParameters(), /*selfParam=*/nullptr, - ace, resultIfaceTy, ace->isBodyThrowing(), ace->getLoc(), - OrigFnType); - prepareEpilog(resultIfaceTy, ace->getEffectiveThrownType(), - CleanupLocation(ace)); + emitProlog(ace, captureInfo, ace->getParameters(), /*selfParam=*/nullptr, + resultIfaceTy, errorIfaceTy, ace->getLoc(), OrigFnType); + prepareEpilog(ace, resultIfaceTy, errorIfaceTy, + CleanupLocation(ace), OrigFnType); emitProfilerIncrement(ace); if (auto *ce = dyn_cast(ace)) { @@ -1553,15 +1558,15 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, Expr *value, auto captureInfo = SGM.M.Types.getLoweredLocalCaptures(function); auto interfaceType = value->getType()->mapTypeOutOfContext(); - emitProlog(captureInfo, params, /*selfParam=*/nullptr, - dc, interfaceType, /*throws=*/false, SourceLoc()); + emitProlog(dc, captureInfo, params, /*selfParam=*/nullptr, interfaceType, + /*errorType=*/llvm::None, SourceLoc()); if (EmitProfilerIncrement) { // Emit a profiler increment for the top-level value, not looking through // any function conversions. This is necessary as the counter would have // been recorded for this expression, not the sub-expression. emitProfilerIncrement(topLevelValue); } - prepareEpilog(interfaceType, llvm::None, CleanupLocation(Loc)); + prepareEpilog(dc, interfaceType, llvm::None, CleanupLocation(Loc)); { llvm::Optional opaqueValue; @@ -1621,10 +1626,14 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, VarDecl *var) { } } - emitBasicProlog(/*paramList*/ nullptr, /*selfParam*/ nullptr, - interfaceType, dc, /*throws=*/ false,SourceLoc(), + emitBasicProlog(dc, + /*paramList*/ nullptr, + /*selfParam*/ nullptr, + interfaceType, + /*errorType=*/llvm::None, + /*throwsLoc=*/SourceLoc(), /*ignored parameters*/ 0); - prepareEpilog(interfaceType, llvm::None, CleanupLocation(loc)); + prepareEpilog(dc, interfaceType, llvm::None, CleanupLocation(loc)); auto pbd = var->getParentPatternBinding(); const auto i = pbd->getPatternEntryIndexForVarDecl(var); @@ -1676,11 +1685,11 @@ void SILGenFunction::emitGeneratorFunction( auto *dc = function.getDecl()->getInnermostDeclContext(); auto captureInfo = SGM.M.Types.getLoweredLocalCaptures(function); - emitProlog(captureInfo, ParameterList::createEmpty(getASTContext()), - /*selfParam=*/nullptr, dc, resultInterfaceType, /*throws=*/false, - SourceLoc(), pattern); + emitProlog(dc, captureInfo, ParameterList::createEmpty(getASTContext()), + /*selfParam=*/nullptr, resultInterfaceType, + /*errorType=*/llvm::None, SourceLoc(), pattern); - prepareEpilog(resultInterfaceType, llvm::None, CleanupLocation(loc)); + prepareEpilog(dc, resultInterfaceType, llvm::None, CleanupLocation(loc)); emitStmt(body); diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index c4353dc9a2125..bd611b17b250d 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -392,6 +392,9 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// postmatter and takes a BB argument of the exception type. JumpDest ThrowDest = JumpDest::invalid(); + /// Support for typed throws. + SILArgument *IndirectErrorResult = nullptr; + /// The destination for coroutine unwinds. The block will always /// be in the postmatter. JumpDest CoroutineUnwindDest = JumpDest::invalid(); @@ -1144,15 +1147,15 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// emitProlog - Generates prolog code to allocate and clean up mutable /// storage for closure captures and local arguments. void - emitProlog(CaptureInfo captureInfo, ParameterList *paramList, - ParamDecl *selfParam, DeclContext *DC, Type resultType, - bool throws, SourceLoc throwsLoc, + emitProlog(DeclContext *DC, CaptureInfo captureInfo, ParameterList *paramList, + ParamDecl *selfParam, Type resultType, + llvm::Optional errorType, SourceLoc throwsLoc, llvm::Optional origClosureType = llvm::None); /// A simpler version of emitProlog /// \returns the number of variables in paramPatterns. uint16_t emitBasicProlog( - ParameterList *paramList, ParamDecl *selfParam, Type resultType, - DeclContext *DC, bool throws, SourceLoc throwsLoc, + DeclContext *DC, ParameterList *paramList, ParamDecl *selfParam, + Type resultType, llvm::Optional errorType, SourceLoc throwsLoc, unsigned numIgnoredTrailingParameters, llvm::Optional origClosureType = llvm::None); @@ -1169,17 +1172,25 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// Create (but do not emit) the epilog branch, and save the /// current cleanups depth as the destination for return statement branches. /// + /// \param dc The declaration context whose generic signature to use for + /// interpreting interface types. /// \param directResultType If given a value, the epilog block will be /// created with arguments for each direct result of this /// function, corresponding to the formal return type. - /// \param exnType If not None, create an error epilog block with the given - /// exception type. + /// \param errorType If not None, create an error epilog block with the given + /// thrown error type. /// \param L The SILLocation which should be associated with /// cleanup instructions. - void prepareEpilog(llvm::Optional directResultType, - llvm::Optional exnType, - CleanupLocation L); - void prepareRethrowEpilog(Type exnType, CleanupLocation l); + /// \param origClosureType Overrides the abstraction pattern for lowering the + /// error type. + void prepareEpilog(DeclContext *dc, + llvm::Optional directResultType, + llvm::Optional errorType, + CleanupLocation L, + llvm::Optional origClosureType = llvm::None); + void prepareRethrowEpilog(DeclContext *dc, + AbstractionPattern origErrorType, + Type errorType, CleanupLocation l); void prepareCoroutineUnwindEpilog(CleanupLocation l); /// Branch to and emit the epilog basic block. This will fuse diff --git a/lib/SILGen/SILGenProlog.cpp b/lib/SILGen/SILGenProlog.cpp index d9dd8a02f9bac..4476966faefca 100644 --- a/lib/SILGen/SILGenProlog.cpp +++ b/lib/SILGen/SILGenProlog.cpp @@ -1210,16 +1210,17 @@ static void emitCaptureArguments(SILGenFunction &SGF, } void SILGenFunction::emitProlog( - CaptureInfo captureInfo, ParameterList *paramList, ParamDecl *selfParam, - DeclContext *DC, Type resultType, bool throws, SourceLoc throwsLoc, + DeclContext *DC, CaptureInfo captureInfo, + ParameterList *paramList, ParamDecl *selfParam, Type resultType, + llvm::Optional errorType, SourceLoc throwsLoc, llvm::Optional origClosureType) { // Emit the capture argument variables. These are placed last because they // become the first curry level of the SIL function. assert(captureInfo.hasBeenComputed() && "can't emit prolog of function with uncomputed captures"); - uint16_t ArgNo = emitBasicProlog(paramList, selfParam, resultType, - DC, throws, throwsLoc, + uint16_t ArgNo = emitBasicProlog(DC, paramList, selfParam, resultType, + errorType, throwsLoc, /*ignored parameters*/ captureInfo.getCaptures().size(), origClosureType); @@ -1722,18 +1723,10 @@ static void emitIndirectResultParameters(SILGenFunction &SGF, assert(!resultType->is()); // If the return type is address-only, emit the indirect return argument. - auto &resultTI = - SGF.SGM.Types.getTypeLowering(origResultType, resultTypeInContext, - SGF.getTypeExpansionContext()); - + // The calling convention always uses minimal resilience expansion. - auto &resultTIConv = SGF.SGM.Types.getTypeLowering( + auto resultConvType = SGF.SGM.Types.getLoweredType( resultTypeInContext, TypeExpansionContext::minimal()); - auto resultConvType = resultTIConv.getLoweredType(); - - auto &ctx = SGF.getASTContext(); - - SILType resultSILType = resultTI.getLoweredType().getAddressType(); // And the abstraction pattern may force an indirect return even if the // concrete type wouldn't normally be returned indirectly. @@ -1743,19 +1736,63 @@ static void emitIndirectResultParameters(SILGenFunction &SGF, || origResultType.getResultConvention(SGF.SGM.Types) != AbstractionPattern::Indirect) return; } + + auto &ctx = SGF.getASTContext(); auto var = new (ctx) ParamDecl(SourceLoc(), SourceLoc(), ctx.getIdentifier("$return_value"), SourceLoc(), ctx.getIdentifier("$return_value"), DC); var->setSpecifier(ParamSpecifier::InOut); var->setInterfaceType(resultType); + auto &resultTI = + SGF.SGM.Types.getTypeLowering(origResultType, resultTypeInContext, + SGF.getTypeExpansionContext()); + SILType resultSILType = resultTI.getLoweredType().getAddressType(); auto *arg = SGF.F.begin()->createFunctionArgument(resultSILType, var); (void)arg; } +static void emitIndirectErrorParameter(SILGenFunction &SGF, + Type errorType, + AbstractionPattern origErrorType, + DeclContext *DC) { + CanType errorTypeInContext = + DC->mapTypeIntoContext(errorType)->getCanonicalType(); + + // If the error type is address-only, emit the indirect error argument. + + // The calling convention always uses minimal resilience expansion. + auto errorConvType = SGF.SGM.Types.getLoweredType( + errorTypeInContext, TypeExpansionContext::minimal()); + + // And the abstraction pattern may force an indirect return even if the + // concrete type wouldn't normally be returned indirectly. + if (!SILModuleConventions::isThrownIndirectlyInSIL(errorConvType, + SGF.SGM.M)) { + if (!SILModuleConventions(SGF.SGM.M).useLoweredAddresses() + || origErrorType.getErrorConvention(SGF.SGM.Types) != AbstractionPattern::Indirect) + return; + } + + auto &ctx = SGF.getASTContext(); + auto var = new (ctx) ParamDecl(SourceLoc(), SourceLoc(), + ctx.getIdentifier("$error"), SourceLoc(), + ctx.getIdentifier("$error"), + DC); + var->setSpecifier(ParamSpecifier::InOut); + var->setInterfaceType(errorType); + + auto &errorTI = + SGF.SGM.Types.getTypeLowering(origErrorType, errorTypeInContext, + SGF.getTypeExpansionContext()); + SILType errorSILType = errorTI.getLoweredType().getAddressType(); + assert(SGF.IndirectErrorResult == nullptr); + SGF.IndirectErrorResult = SGF.F.begin()->createFunctionArgument(errorSILType, var); +} + uint16_t SILGenFunction::emitBasicProlog( - ParameterList *paramList, ParamDecl *selfParam, Type resultType, - DeclContext *DC, bool throws, SourceLoc throwsLoc, + DeclContext *DC, ParameterList *paramList, ParamDecl *selfParam, + Type resultType, llvm::Optional errorType, SourceLoc throwsLoc, unsigned numIgnoredTrailingParameters, llvm::Optional origClosureType) { // Create the indirect result parameters. @@ -1769,20 +1806,31 @@ uint16_t SILGenFunction::emitBasicProlog( emitIndirectResultParameters(*this, resultType, origResultType, DC); + llvm::Optional origErrorType; + if (errorType) { + origErrorType = origClosureType + ? origClosureType->getFunctionThrownErrorType() + : AbstractionPattern(genericSig.getCanonicalSignature(), + (*errorType)->getCanonicalType()); + emitIndirectErrorParameter(*this, *errorType, *origErrorType, DC); + } + // Emit the argument variables in calling convention order. unsigned ArgNo = ArgumentInitHelper(*this, numIgnoredTrailingParameters) .emitParams(origClosureType, paramList, selfParam); // Record the ArgNo of the artificial $error inout argument. - if (throws) { - auto NativeErrorTy = SILType::getExceptionType(getASTContext()); - ManagedValue Undef = emitUndef(NativeErrorTy); - SILDebugVariable DbgVar("$error", /*Constant*/ false, ++ArgNo); + if (errorType && IndirectErrorResult == nullptr) { + CanType errorTypeInContext = + DC->mapTypeIntoContext(*errorType)->getCanonicalType(); + auto loweredErrorTy = getLoweredType(*origErrorType, errorTypeInContext); + ManagedValue undef = emitUndef(loweredErrorTy); + SILDebugVariable dbgVar("$error", /*Constant*/ false, ++ArgNo); RegularLocation loc = RegularLocation::getAutoGeneratedLocation(); if (throwsLoc.isValid()) loc = throwsLoc; - B.createDebugValue(loc, Undef.getValue(), DbgVar); + B.createDebugValue(loc, undef.getValue(), dbgVar); } return ArgNo; diff --git a/lib/SILGen/SILGenTopLevel.cpp b/lib/SILGen/SILGenTopLevel.cpp index a0e45652c8b77..9317bccdffe23 100644 --- a/lib/SILGen/SILGenTopLevel.cpp +++ b/lib/SILGen/SILGenTopLevel.cpp @@ -42,7 +42,8 @@ void SILGenModule::emitEntryPoint(SourceFile *SF, SILFunction *TopLevel) { auto moduleCleanupLoc = CleanupLocation::getModuleCleanupLocation(); TopLevelSGF.prepareEpilog( - llvm::None, getASTContext().getErrorExistentialType(), moduleCleanupLoc); + SF, llvm::None, getASTContext().getErrorExistentialType(), + moduleCleanupLoc); auto prologueLoc = RegularLocation::getModuleLocation(); prologueLoc.markAsPrologue(); diff --git a/test/SILGen/typed_throws_generic.swift b/test/SILGen/typed_throws_generic.swift new file mode 100644 index 0000000000000..ccb1720f83404 --- /dev/null +++ b/test/SILGen/typed_throws_generic.swift @@ -0,0 +1,6 @@ +// RUN: %target-swift-emit-silgen %s -enable-experimental-feature TypedThrows | %FileCheck %s + +public func genericThrows(_: () throws(E) -> ()) throws(E) -> () {} + +// CHECK-LABEL: sil [ossa] @$s20typed_throws_generic0C6ThrowsyyyyKXEKs5ErrorRzlF : $@convention(thin) (@guaranteed @noescape @callee_guaranteed @substituted <τ_0_0> () -> @error_indirect τ_0_0 for ) -> @error_indirect E { +// CHECK: bb0(%0 : $*E, %1 : @guaranteed $@noescape @callee_guaranteed @substituted <τ_0_0> () -> @error_indirect τ_0_0 for ): \ No newline at end of file