Skip to content

Commit b6b999a

Browse files
committed
[Typed throws] Basic SIL lowering and SIL generation for typed throws
Lower the thrown error type into the SIL function type. This requires very little code because the thrown error type was already modeled as a SILResultInfo, which carries type information. Note that this lowering does not yet account for error types that need to passed indirectly, but we will need to do so for (e.g.) using resilient error types. Teach a few places in SIL generation not to assume that thrown types are always the existential error type, which primarily comes down to ensuring that rethrow epilogues have the thrown type of the corresponding function or closure. Teach throw emission to implicitly box concrete thrown errors in the error existential when needed to satisfy the throw destination. This is a temporary solution that helps translate typed throws into untyped throws, but it should be replaced by a better modeling within the AST of the points at which thrown errors are converted.
1 parent 31b8811 commit b6b999a

18 files changed

+232
-34
lines changed

include/swift/AST/Decl.h

+7
Original file line numberDiff line numberDiff line change
@@ -7066,6 +7066,13 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
70667066
/// Retrieves the thrown interface type.
70677067
Type getThrownInterfaceType() const;
70687068

7069+
/// Retrieve the "effective" thrown interface type, or llvm::None if
7070+
/// this function cannot throw.
7071+
///
7072+
/// Functions with untyped throws will produce "any Error", functions that
7073+
/// cannot throw or are specified to throw "Never" will return llvm::None.
7074+
llvm::Optional<Type> getEffectiveThrownInterfaceType() const;
7075+
70697076
/// Returns if the function throws or is async.
70707077
bool hasEffect(EffectKind kind) const;
70717078

include/swift/AST/Expr.h

+7
Original file line numberDiff line numberDiff line change
@@ -3847,6 +3847,13 @@ class AbstractClosureExpr : public DeclContext, public Expr {
38473847
/// Return whether this closure is throwing when fully applied.
38483848
bool isBodyThrowing() const;
38493849

3850+
/// Retrieve the "effective" thrown interface type, or llvm::None if
3851+
/// this closure cannot throw.
3852+
///
3853+
/// Closures with untyped throws will produce "any Error", functions that
3854+
/// cannot throw or are specified to throw "Never" will return llvm::None.
3855+
llvm::Optional<Type> getEffectiveThrownType() const;
3856+
38503857
/// \brief Return whether this closure is async when fully applied.
38513858
bool isBodyAsync() const;
38523859

include/swift/AST/Types.h

+9-1
Original file line numberDiff line numberDiff line change
@@ -3386,6 +3386,13 @@ class AnyFunctionType : public TypeBase {
33863386
Type getGlobalActor() const;
33873387
Type getThrownError() const;
33883388

3389+
/// Retrieve the "effective" thrown interface type, or llvm::None if
3390+
/// this function cannot throw.
3391+
///
3392+
/// Functions with untyped throws will produce "any Error", functions that
3393+
/// cannot throw or are specified to throw "Never" will return llvm::None.
3394+
llvm::Optional<Type> getEffectiveThrownInterfaceType() const;
3395+
33893396
/// Returns true if the function type stores a Clang type that cannot
33903397
/// be derived from its Swift type. Returns false otherwise, including if
33913398
/// the function type is not @convention(c) or @convention(block).
@@ -3612,7 +3619,8 @@ BEGIN_CAN_TYPE_WRAPPER(AnyFunctionType, Type)
36123619
}
36133620

36143621
PROXY_CAN_TYPE_SIMPLE_GETTER(getResult)
3615-
3622+
PROXY_CAN_TYPE_SIMPLE_GETTER(getThrownError)
3623+
36163624
CanAnyFunctionType withExtInfo(ExtInfo info) const {
36173625
return CanAnyFunctionType(getPointer()->withExtInfo(info));
36183626
}

lib/AST/ASTVerifier.cpp

+9-3
Original file line numberDiff line numberDiff line change
@@ -1014,9 +1014,15 @@ class Verifier : public ASTWalker {
10141014
}
10151015

10161016
void verifyChecked(ThrowStmt *S) {
1017-
checkSameType(S->getSubExpr()->getType(),
1018-
checkExceptionTypeExists("throw expression"),
1019-
"throw operand");
1017+
Type thrownError;
1018+
if (!Functions.empty()) {
1019+
if (auto fn = AnyFunctionRef::fromDeclContext(Functions.back()))
1020+
thrownError = fn->getThrownErrorType();
1021+
}
1022+
1023+
if (!thrownError)
1024+
thrownError = checkExceptionTypeExists("throw expression");
1025+
checkSameType(S->getSubExpr()->getType(), thrownError, "throw operand");
10201026
verifyCheckedBase(S);
10211027
}
10221028

lib/AST/Decl.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,18 @@ Type AbstractFunctionDecl::getThrownInterfaceType() const {
952952
Type());
953953
}
954954

955+
llvm::Optional<Type>
956+
AbstractFunctionDecl::getEffectiveThrownInterfaceType() const {
957+
Type interfaceType = getInterfaceType();
958+
if (hasImplicitSelfDecl()) {
959+
if (auto fnType = interfaceType->getAs<AnyFunctionType>())
960+
interfaceType = fnType->getResult();
961+
}
962+
963+
return interfaceType->castTo<AnyFunctionType>()
964+
->getEffectiveThrownInterfaceType();
965+
}
966+
955967
Expr *AbstractFunctionDecl::getSingleExpressionBody() const {
956968
assert(hasSingleExpressionBody() && "Not a single-expression body");
957969
auto braceStmt = getBody();

lib/AST/Expr.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -1930,6 +1930,11 @@ Type AbstractClosureExpr::getResultType(
19301930
return T->castTo<FunctionType>()->getResult();
19311931
}
19321932

1933+
llvm::Optional<Type> AbstractClosureExpr::getEffectiveThrownType() const {
1934+
return getType()->castTo<AnyFunctionType>()
1935+
->getEffectiveThrownInterfaceType();
1936+
}
1937+
19331938
bool AbstractClosureExpr::isBodyThrowing() const {
19341939
if (!getType() || getType()->hasError()) {
19351940
// Scan the closure body to infer effects.

lib/AST/Type.cpp

+18
Original file line numberDiff line numberDiff line change
@@ -5419,6 +5419,24 @@ AnyFunctionType *AnyFunctionType::getWithoutThrowing() const {
54195419
return withExtInfo(info);
54205420
}
54215421

5422+
llvm::Optional<Type> AnyFunctionType::getEffectiveThrownInterfaceType() const {
5423+
// A non-throwing function... has no thrown interface type.
5424+
if (!isThrowing())
5425+
return llvm::None;
5426+
5427+
// If there is no specified thrown error type, it throws "any Error".
5428+
Type thrownError = getThrownError();
5429+
if (!thrownError)
5430+
return getASTContext().getErrorExistentialType();
5431+
5432+
// If the thrown interface type is "Never", this function does not throw.
5433+
if (thrownError->isEqual(getASTContext().getNeverType()))
5434+
return llvm::None;
5435+
5436+
// Otherwise, return the typed error.
5437+
return thrownError;
5438+
}
5439+
54225440
llvm::Optional<TangentSpace>
54235441
TypeBase::getAutoDiffTangentSpace(LookupConformanceFn lookupConformance) {
54245442
assert(lookupConformance);

lib/SIL/IR/SILFunctionType.cpp

+7-1
Original file line numberDiff line numberDiff line change
@@ -2202,7 +2202,13 @@ static CanSILFunctionType getSILFunctionType(
22022202
!foreignInfo.async) {
22032203
assert(!origType.isForeign()
22042204
&& "using native Swift error convention for foreign type!");
2205-
SILType exnType = SILType::getExceptionType(TC.Context);
2205+
SILType exnType;
2206+
if (CanType thrownError = substFnInterfaceType.getThrownError()) {
2207+
exnType = TC.getLoweredType(thrownError, expansionContext);
2208+
} else {
2209+
// Untyped error throws the exception type.
2210+
exnType = SILType::getExceptionType(TC.Context);
2211+
}
22062212
assert(exnType.isObject());
22072213
errorResult = SILResultInfo(exnType.getASTType(),
22082214
ResultConvention::Owned);

lib/SILGen/SILGenBackDeploy.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,8 @@ void SILGenFunction::emitBackDeploymentThunk(SILDeclRef thunk) {
237237
paramsForForwarding.emplace_back(param.forward(*this));
238238
}
239239

240-
prepareEpilog(getResultInterfaceType(AFD), AFD->hasThrows(),
240+
prepareEpilog(getResultInterfaceType(AFD),
241+
AFD->getEffectiveThrownInterfaceType(),
241242
CleanupLocation(AFD));
242243

243244
SILBasicBlock *availableBB = createBasicBlock("availableBB");

lib/SILGen/SILGenBridging.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -2081,15 +2081,15 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) {
20812081
auto foreignFnTy = foreignCI.SILFnType;
20822082

20832083
// Find the foreign error/async convention and 'self' parameter index.
2084-
bool hasError = false;
2084+
llvm::Optional<Type> thrownErrorType;
20852085
llvm::Optional<ForeignAsyncConvention> foreignAsync;
20862086
if (nativeFnTy->isAsync()) {
20872087
foreignAsync = fd->getForeignAsyncConvention();
20882088
assert(foreignAsync && "couldn't find foreign async convention?!");
20892089
}
20902090
llvm::Optional<ForeignErrorConvention> foreignError;
20912091
if (nativeFnTy->hasErrorResult()) {
2092-
hasError = true;
2092+
thrownErrorType = nativeFnTy->getErrorResult().getInterfaceType();
20932093
foreignError = fd->getForeignErrorConvention();
20942094
assert((foreignError || foreignAsync)
20952095
&& "couldn't find foreign error or async convention for foreign error!");
@@ -2133,8 +2133,8 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) {
21332133

21342134
// Set up the throw destination if necessary.
21352135
CleanupLocation cleanupLoc(fd);
2136-
if (hasError) {
2137-
prepareRethrowEpilog(cleanupLoc);
2136+
if (thrownErrorType) {
2137+
prepareRethrowEpilog(*thrownErrorType, cleanupLoc);
21382138
}
21392139

21402140
SILValue result;

lib/SILGen/SILGenConstructor.cpp

+7-4
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,8 @@ void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) {
666666
// Create a basic block to jump to for the implicit 'self' return.
667667
// We won't emit this until after we've emitted the body.
668668
// The epilog takes a void return because the return of 'self' is implicit.
669-
prepareEpilog(llvm::None, ctor->hasThrows(), CleanupLocation(ctor));
669+
prepareEpilog(llvm::None, ctor->getEffectiveThrownInterfaceType(),
670+
CleanupLocation(ctor));
670671

671672
// If the constructor can fail, set up an alternative epilog for constructor
672673
// failure.
@@ -1170,7 +1171,8 @@ void SILGenFunction::emitClassConstructorInitializer(ConstructorDecl *ctor) {
11701171

11711172
// Create a basic block to jump to for the implicit 'self' return.
11721173
// We won't emit the block until after we've emitted the body.
1173-
prepareEpilog(llvm::None, ctor->hasThrows(), CleanupLocation(endOfInitLoc));
1174+
prepareEpilog(llvm::None, ctor->getEffectiveThrownInterfaceType(),
1175+
CleanupLocation(endOfInitLoc));
11741176

11751177
auto resultType = ctor->mapTypeIntoContext(ctor->getResultInterfaceType());
11761178

@@ -1643,7 +1645,7 @@ void SILGenFunction::emitIVarInitializer(SILDeclRef ivarInitializer) {
16431645
VarLocs[selfDecl] = VarLoc::get(selfArg);
16441646

16451647
auto cleanupLoc = CleanupLocation(loc);
1646-
prepareEpilog(llvm::None, false, cleanupLoc);
1648+
prepareEpilog(llvm::None, llvm::None, cleanupLoc);
16471649

16481650
// Emit the initializers.
16491651
emitMemberInitializers(cd, selfDecl, cd);
@@ -1713,7 +1715,8 @@ void SILGenFunction::emitInitAccessor(AccessorDecl *accessor) {
17131715
}
17141716
}
17151717

1716-
prepareEpilog(accessor->getResultInterfaceType(), accessor->hasThrows(),
1718+
prepareEpilog(accessor->getResultInterfaceType(),
1719+
accessor->getEffectiveThrownInterfaceType(),
17171720
CleanupLocation(accessor));
17181721

17191722
emitProfilerIncrement(accessor->getTypecheckedBody());

lib/SILGen/SILGenDestructor.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ void SILGenFunction::emitDestroyingDestructor(DestructorDecl *dd) {
4545
// Create a basic block to jump to for the implicit destruction behavior
4646
// of releasing the elements and calling the superclass destructor.
4747
// We won't actually emit the block until we finish with the destructor body.
48-
prepareEpilog(llvm::None, false, CleanupLocation(Loc));
48+
prepareEpilog(llvm::None, llvm::None, CleanupLocation(Loc));
4949

5050
auto cleanupLoc = CleanupLocation(Loc);
5151

@@ -248,7 +248,7 @@ void SILGenFunction::emitDeallocatingMoveOnlyDestructor(DestructorDecl *dd) {
248248
// Create a basic block to jump to for the implicit destruction behavior
249249
// of releasing the elements and calling the superclass destructor.
250250
// We won't actually emit the block until we finish with the destructor body.
251-
prepareEpilog(llvm::None, false, CleanupLocation(loc));
251+
prepareEpilog(llvm::None, llvm::None, CleanupLocation(loc));
252252

253253
auto cleanupLoc = CleanupLocation(loc);
254254

@@ -286,7 +286,7 @@ void SILGenFunction::emitIVarDestroyer(SILDeclRef ivarDestroyer) {
286286
assert(selfValue);
287287

288288
auto cleanupLoc = CleanupLocation(loc);
289-
prepareEpilog(llvm::None, false, cleanupLoc);
289+
prepareEpilog(llvm::None, llvm::None, cleanupLoc);
290290
{
291291
Scope S(*this, cleanupLoc);
292292
// Self is effectively guaranteed for the duration of any destructor. For
@@ -577,7 +577,7 @@ void SILGenFunction::emitObjCDestructor(SILDeclRef dtor) {
577577
// Create a basic block to jump to for the implicit destruction behavior
578578
// of releasing the elements and calling the superclass destructor.
579579
// We won't actually emit the block until we finish with the destructor body.
580-
prepareEpilog(llvm::None, false, CleanupLocation(loc));
580+
prepareEpilog(llvm::None, llvm::None, CleanupLocation(loc));
581581

582582
emitProfilerIncrement(dd->getTypecheckedBody());
583583
// Emit the destructor body.

lib/SILGen/SILGenEpilog.cpp

+8-6
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ using namespace swift;
2121
using namespace Lowering;
2222

2323
void SILGenFunction::prepareEpilog(llvm::Optional<Type> directResultType,
24-
bool isThrowing, CleanupLocation CleanupL) {
24+
llvm::Optional<Type> exnType,
25+
CleanupLocation CleanupL) {
2526
auto *epilogBB = createBasicBlock();
2627

2728
// If we have any direct results, receive them via BB arguments.
@@ -62,19 +63,20 @@ void SILGenFunction::prepareEpilog(llvm::Optional<Type> directResultType,
6263

6364
ReturnDest = JumpDest(epilogBB, getCleanupsDepth(), CleanupL);
6465

65-
if (isThrowing) {
66-
prepareRethrowEpilog(CleanupL);
66+
if (exnType) {
67+
prepareRethrowEpilog(*exnType, CleanupL);
6768
}
6869

6970
if (F.getLoweredFunctionType()->isCoroutine()) {
7071
prepareCoroutineUnwindEpilog(CleanupL);
7172
}
7273
}
7374

74-
void SILGenFunction::prepareRethrowEpilog(CleanupLocation cleanupLoc) {
75-
auto exnType = SILType::getExceptionType(getASTContext());
75+
void SILGenFunction::prepareRethrowEpilog(
76+
Type exnType, CleanupLocation cleanupLoc) {
77+
SILType loweredExnType = getLoweredType(exnType);
7678
SILBasicBlock *rethrowBB = createBasicBlock(FunctionSection::Postmatter);
77-
rethrowBB->createPhiArgument(exnType, OwnershipKind::Owned);
79+
rethrowBB->createPhiArgument(loweredExnType, OwnershipKind::Owned);
7880
ThrowDest = JumpDest(rethrowBB, getCleanupsDepth(), cleanupLoc);
7981
}
8082

lib/SILGen/SILGenFunction.cpp

+6-5
Original file line numberDiff line numberDiff line change
@@ -1051,7 +1051,7 @@ void SILGenFunction::emitFunction(FuncDecl *fd) {
10511051
emitDistributedActorFactory(fd);
10521052
} else {
10531053
prepareEpilog(fd->getResultInterfaceType(),
1054-
fd->hasThrows(), CleanupLocation(fd));
1054+
fd->getEffectiveThrownInterfaceType(), CleanupLocation(fd));
10551055

10561056
if (fd->requiresUnavailableDeclABICompatibilityStubs())
10571057
emitApplyOfUnavailableCodeReached();
@@ -1077,7 +1077,8 @@ void SILGenFunction::emitClosure(AbstractClosureExpr *ace) {
10771077
emitProlog(captureInfo, ace->getParameters(), /*selfParam=*/nullptr,
10781078
ace, resultIfaceTy, ace->isBodyThrowing(), ace->getLoc(),
10791079
OrigFnType);
1080-
prepareEpilog(resultIfaceTy, ace->isBodyThrowing(), CleanupLocation(ace));
1080+
prepareEpilog(resultIfaceTy, ace->getEffectiveThrownType(),
1081+
CleanupLocation(ace));
10811082

10821083
emitProfilerIncrement(ace);
10831084
if (auto *ce = dyn_cast<ClosureExpr>(ace)) {
@@ -1559,7 +1560,7 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, Expr *value,
15591560
// been recorded for this expression, not the sub-expression.
15601561
emitProfilerIncrement(topLevelValue);
15611562
}
1562-
prepareEpilog(interfaceType, false, CleanupLocation(Loc));
1563+
prepareEpilog(interfaceType, llvm::None, CleanupLocation(Loc));
15631564

15641565
{
15651566
llvm::Optional<SILGenFunction::OpaqueValueRAII> opaqueValue;
@@ -1622,7 +1623,7 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, VarDecl *var) {
16221623
emitBasicProlog(/*paramList*/ nullptr, /*selfParam*/ nullptr,
16231624
interfaceType, dc, /*throws=*/ false,SourceLoc(),
16241625
/*ignored parameters*/ 0);
1625-
prepareEpilog(interfaceType, false, CleanupLocation(loc));
1626+
prepareEpilog(interfaceType, llvm::None, CleanupLocation(loc));
16261627

16271628
auto pbd = var->getParentPatternBinding();
16281629
const auto i = pbd->getPatternEntryIndexForVarDecl(var);
@@ -1678,7 +1679,7 @@ void SILGenFunction::emitGeneratorFunction(
16781679
/*selfParam=*/nullptr, dc, resultInterfaceType, /*throws=*/false,
16791680
SourceLoc(), pattern);
16801681

1681-
prepareEpilog(resultInterfaceType, /*hasThrows=*/false, CleanupLocation(loc));
1682+
prepareEpilog(resultInterfaceType, llvm::None, CleanupLocation(loc));
16821683

16831684
emitStmt(body);
16841685

lib/SILGen/SILGenFunction.h

+5-3
Original file line numberDiff line numberDiff line change
@@ -1172,12 +1172,14 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
11721172
/// \param directResultType If given a value, the epilog block will be
11731173
/// created with arguments for each direct result of this
11741174
/// function, corresponding to the formal return type.
1175-
/// \param isThrowing If true, create an error epilog block.
1175+
/// \param exnType If not None, create an error epilog block with the given
1176+
/// exception type.
11761177
/// \param L The SILLocation which should be associated with
11771178
/// cleanup instructions.
1178-
void prepareEpilog(llvm::Optional<Type> directResultType, bool isThrowing,
1179+
void prepareEpilog(llvm::Optional<Type> directResultType,
1180+
llvm::Optional<Type> exnType,
11791181
CleanupLocation L);
1180-
void prepareRethrowEpilog(CleanupLocation l);
1182+
void prepareRethrowEpilog(Type exnType, CleanupLocation l);
11811183
void prepareCoroutineUnwindEpilog(CleanupLocation l);
11821184

11831185
/// Branch to and emit the epilog basic block. This will fuse

0 commit comments

Comments
 (0)