Skip to content

Commit 927e242

Browse files
committed
[Typed throws] Handle closure reabstraction
This peephole optimization in SILGen requires us to use the thrown error for the context of a closure type rather than the thrown error for the closure AST node itself.
1 parent 251bbbb commit 927e242

File tree

5 files changed

+131
-61
lines changed

5 files changed

+131
-61
lines changed

lib/SIL/IR/AbstractionPattern.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,8 +1279,15 @@ AbstractionPattern::getFunctionThrownErrorType(
12791279
(*optErrorType)->getCanonicalType());
12801280
}
12811281

1282-
if (!optErrorType)
1283-
optErrorType = ctx.getErrorExistentialType();
1282+
if (!optErrorType) {
1283+
Type origErrorSubstType =
1284+
optOrigErrorType->getType()
1285+
.subst(optOrigErrorType->getGenericSubstitutions());
1286+
if (origErrorSubstType->isNever())
1287+
optErrorType = ctx.getNeverType();
1288+
else
1289+
optErrorType = ctx.getErrorExistentialType();
1290+
}
12841291

12851292
return std::make_pair(*optOrigErrorType,
12861293
(*optErrorType)->getCanonicalType());

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4948,5 +4948,5 @@ bool SILFunctionConventions::isTypedError() const {
49484948
return !funcTy->getErrorResult()
49494949
.getInterfaceType()->isEqual(
49504950
funcTy->getASTContext().getErrorExistentialType()) ||
4951-
getNumIndirectSILResults() > 0;
4951+
hasIndirectSILErrorResults();
49524952
}

lib/SILGen/SILGenProlog.cpp

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1813,17 +1813,21 @@ uint16_t SILGenFunction::emitBasicProlog(
18131813
emitIndirectResultParameters(*this, resultType, origResultType, DC);
18141814

18151815
llvm::Optional<AbstractionPattern> origErrorType;
1816-
if (errorType) {
1817-
if (origClosureType &&
1818-
!origClosureType->isTypeParameterOrOpaqueArchetype()) {
1819-
origErrorType =
1820-
origClosureType->getFunctionThrownErrorType();
1821-
} else {
1822-
origErrorType =
1823-
AbstractionPattern(genericSig.getCanonicalSignature(),
1824-
(*errorType)->getCanonicalType());
1816+
if (origClosureType && !origClosureType->isTypeParameterOrOpaqueArchetype()) {
1817+
CanType substClosureType = origClosureType->getType()
1818+
.subst(origClosureType->getGenericSubstitutions())->getCanonicalType();
1819+
CanAnyFunctionType substClosureFnType =
1820+
cast<AnyFunctionType>(substClosureType);
1821+
if (auto optPair = origClosureType->getFunctionThrownErrorType(substClosureFnType)) {
1822+
origErrorType = optPair->first;
1823+
errorType = optPair->second;
18251824
}
1825+
} else if (errorType) {
1826+
origErrorType = AbstractionPattern(genericSig.getCanonicalSignature(),
1827+
(*errorType)->getCanonicalType());
1828+
}
18261829

1830+
if (origErrorType && errorType) {
18271831
emitIndirectErrorParameter(*this, *errorType, *origErrorType, DC);
18281832
}
18291833

lib/SILGen/SILGenStmt.cpp

Lines changed: 51 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1547,63 +1547,65 @@ void SILGenFunction::emitThrow(SILLocation loc, ManagedValue exnMV,
15471547
}
15481548
}
15491549

1550+
bool shouldDiscard = ThrowDest.getThrownError().Discard;
1551+
SILType exnType = exn->getType().getObjectType();
15501552
SILBasicBlock &throwBB = *ThrowDest.getBlock();
1551-
if (!throwBB.getArguments().empty()) {
1552-
assert(exn);
1553-
assert(!indirectErrorAddr);
1554-
1555-
auto errorArg = throwBB.getArguments()[0];
1556-
1557-
// If we don't have an existential box, create one to jump to the throw
1558-
// destination.
1559-
SILType errorArgType = errorArg->getType();
1560-
if (exn->getType() != errorArgType) {
1561-
SILType existentialBoxType = SILType::getExceptionType(getASTContext());
1562-
assert(errorArgType == existentialBoxType);
1563-
1564-
// FIXME: Callers should provide this conformance from places recorded in
1565-
// the AST.
1566-
ProtocolConformanceRef conformances[1] = {
1567-
getModule().getSwiftModule()->conformsToProtocol(
1568-
exn->getType().getASTType(), getASTContext().getErrorDecl())
1569-
};
1570-
1571-
exnMV = emitExistentialErasure(
1572-
loc,
1573-
exn->getType().getASTType(),
1574-
getTypeLowering(exn->getType().getObjectType()),
1575-
getTypeLowering(existentialBoxType),
1576-
getASTContext().AllocateCopy(conformances),
1577-
SGFContext(),
1578-
[&](SGFContext C) -> ManagedValue {
1579-
if (exn->getType().isAddress()) {
1580-
return emitLoad(loc, exn, getTypeLowering(exn->getType().getObjectType()), C, IsTake);
1581-
}
1553+
SILType destErrorType = indirectErrorAddr
1554+
? indirectErrorAddr->getType().getObjectType()
1555+
: !throwBB.getArguments().empty()
1556+
? throwBB.getArguments()[0]->getType().getObjectType()
1557+
: exnType;
1558+
1559+
// If the thrown error type differs from what the throw destination expects,
1560+
// perform the conversion.
1561+
// FIXME: Can the AST tell us what to do here?
1562+
if (exnType != destErrorType && !shouldDiscard) {
1563+
assert(destErrorType == SILType::getExceptionType(getASTContext()));
1564+
1565+
ProtocolConformanceRef conformances[1] = {
1566+
getModule().getSwiftModule()->conformsToProtocol(
1567+
exn->getType().getASTType(), getASTContext().getErrorDecl())
1568+
};
1569+
1570+
exn = emitExistentialErasure(
1571+
loc,
1572+
exnType.getASTType(),
1573+
getTypeLowering(exnType),
1574+
getTypeLowering(destErrorType),
1575+
getASTContext().AllocateCopy(conformances),
1576+
SGFContext(),
1577+
[&](SGFContext C) -> ManagedValue {
1578+
if (exn->getType().isAddress()) {
1579+
return emitLoad(loc, exn, getTypeLowering(exnType), SGFContext(),
1580+
IsTake);
1581+
}
15821582

1583-
return ManagedValue::forForwardedRValue(*this, exn);
1584-
});
1583+
return ManagedValue::forForwardedRValue(*this, exn);
1584+
}).forward(*this);
1585+
}
1586+
assert(exn->getType().getObjectType() == destErrorType);
15851587

1586-
exn = exnMV.forward(*this);
1588+
if (indirectErrorAddr) {
1589+
if (exn->getType().isAddress()) {
1590+
B.createCopyAddr(loc, exn, indirectErrorAddr,
1591+
IsTake, IsInitialization);
1592+
} else {
1593+
// An indirect error is written into the destination error address.
1594+
emitSemanticStore(loc, exn, indirectErrorAddr,
1595+
getTypeLowering(destErrorType), IsInitialization);
1596+
}
1597+
} else if (!throwBB.getArguments().empty()) {
1598+
// Load if we need to.
1599+
if (exn->getType().isAddress()) {
1600+
exn = emitLoad(loc, exn, getTypeLowering(exnType), SGFContext(), IsTake)
1601+
.forward(*this);
15871602
}
15881603

15891604
// A direct error value is passed to the epilog block as a BB argument.
15901605
args.push_back(exn);
1591-
} else if (ThrowDest.getThrownError().Discard) {
1592-
assert(!indirectErrorAddr);
1593-
if (exn)
1606+
} else if (shouldDiscard) {
1607+
if (exn && exn->getType().isAddress())
15941608
B.createDestroyAddr(loc, exn);
1595-
} else {
1596-
assert(indirectErrorAddr);
1597-
1598-
// FIXME: opaque values
1599-
1600-
// If an error value was provided by the caller, copy it into the
1601-
// indirect error result. Otherwise we assume the indirect error
1602-
// result has been initialized.
1603-
if (exn) {
1604-
B.createCopyAddr(loc, exn, indirectErrorAddr,
1605-
IsTake, IsInitialization);
1606-
}
16071609
}
16081610

16091611
// Branch to the cleanup destination.

test/SILGen/typed_throws_generic.swift

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,60 @@ func reabstractAsConcreteThrowing() throws -> Int {
180180
// CHECK: [[ERROR_BB]]([[INNER_ERROR:%.*]] : $MyError):
181181
// CHECK-NEXT: store [[INNER_ERROR]] to [trivial] [[OUTER_ERROR]] : $*MyError
182182
// CHECK-NEXT: throw_addr
183+
184+
185+
// CHECK-LABEL: sil hidden [ossa] @$s20typed_throws_generic30reabstractClosureAsNonthrowingSiyF : $@convention(thin) () -> Int
186+
func reabstractClosureAsNonthrowing() -> Int {
187+
// CHECK: [[INT_BOX:%.*]] = alloc_stack $Int
188+
// CHECK: [[CLOSURE:%.*]] = function_ref @$s20typed_throws_generic30reabstractClosureAsNonthrowingSiyFSiyXEfU_ : $@convention(thin) @substituted <τ_0_0, τ_0_1> () -> (@out τ_0_1, @error_indirect τ_0_0) for <Never, Int>
189+
// CHECK-NEXT: [[THICK_CLOSURE:%.*]] = thin_to_thick_function [[CLOSURE]] : $@convention(thin) @substituted <τ_0_0, τ_0_1> () -> (@out τ_0_1, @error_indirect τ_0_0) for <Never, Int> to $@noescape @callee_guaranteed @substituted <τ_0_0, τ_0_1> () -> (@out τ_0_1, @error_indirect τ_0_0) for <Never, Int>
190+
// CHECK: [[CALLEE:%.*]] = function_ref @$s20typed_throws_generic15passthroughCallyxxyq_YKXEq_YKs5ErrorR_r0_lF : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_1 : Error> (@guaranteed @noescape @callee_guaranteed @substituted <τ_0_0, τ_0_1> () -> (@out τ_0_1, @error_indirect τ_0_0) for <τ_0_1, τ_0_0>) -> (@out τ_0_0, @error_indirect τ_0_1)
191+
// CHECK-NEXT: [[NEVER_BOX:%.*]] = alloc_stack $Never
192+
// CHECK-NEXT: try_apply [[CALLEE]]<Int, Never>([[INT_BOX]], [[NEVER_BOX]], [[THICK_CLOSURE]]) : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_1 : Error> (@guaranteed @noescape @callee_guaranteed @substituted <τ_0_0, τ_0_1> () -> (@out τ_0_1, @error_indirect τ_0_0) for <τ_0_1, τ_0_0>) -> (@out τ_0_0, @error_indirect τ_0_1), normal [[NORMAL_BB:bb[0-9]+]], error [[ERROR_BB:bb[0-9]+]]
193+
passthroughCall { 5 }
194+
195+
// CHECK: [[NORMAL_BB]]
196+
// CHECK-NEXT: dealloc_stack [[NEVER_BOX]] : $*Never
197+
// CHECK-NEXT: [[RESULT:%.*]] = load [trivial] [[INT_BOX]] : $*Int
198+
// CHECK-NEXT: dealloc_stack [[INT_BOX]] : $*Int
199+
// CHECK-NEXT: return [[RESULT]] : $Int
200+
201+
// CHECK: [[ERROR_BB]]:
202+
// CHECK-NEXT: unreachable
203+
204+
// CHECK-LABEL: sil private [ossa] @$s20typed_throws_generic30reabstractClosureAsNonthrowingSiyFSiyXEfU_ : $@convention(thin) @substituted <τ_0_0, τ_0_1> () -> (@out τ_0_1, @error_indirect τ_0_0) for <Never, Int>
205+
}
206+
207+
func reabstractClosureAsThrowing(b: Bool) throws -> Int {
208+
try passthroughCall {
209+
if b {
210+
throw MyError.fail
211+
}
212+
213+
return 5
214+
}
215+
}
216+
217+
// CHECK-LABEL: sil hidden [ossa] @$s20typed_throws_generic32reabstractClosureAsTypedThrowing1bSiSb_tAA7MyErrorOYKF : $@convention(thin) (Bool) -> (Int, @error MyError)
218+
func reabstractClosureAsTypedThrowing(b: Bool) throws(MyError) -> Int {
219+
// CHECK: try_apply [[CALLEE:%.*]]<Int, MyError>([[INT_BOX:%.*]], [[ERROR_BOX:%.*]], [[THICK_CLOSURE:%.*]]) : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_1 : Error> (@guaranteed @noescape @callee_guaranteed @substituted <τ_0_0, τ_0_1> () -> (@out τ_0_1, @error_indirect τ_0_0) for <τ_0_1, τ_0_0>) -> (@out τ_0_0, @error_indirect τ_0_1), normal [[NORMAL_BB:bb[0-9]+]], error [[ERROR_BB:bb[0-9]+]]
220+
try passthroughCall { () throws(MyError) -> Int in
221+
if b {
222+
throw MyError.fail
223+
}
224+
225+
return 5
226+
}
227+
228+
// CHECK: [[NORMAL_BB]]
229+
// CHECK-NEXT: dealloc_stack [[ERROR_BOX]] : $*MyError
230+
// CHECK: [[RESULT:%.*]] = load [trivial] [[INT_BOX]] : $*Int
231+
// CHECK-NEXT: dealloc_stack [[INT_BOX]] : $*Int
232+
// CHECK-NEXT: return [[RESULT]] : $Int
233+
234+
// CHECK: [[ERROR_BB]]:
235+
// CHECK-NEXT: [[ERROR:%.*]] = load [trivial] [[ERROR_BOX]] : $*MyError
236+
// CHECK-NEXT: dealloc_stack [[ERROR_BOX]] : $*MyError
237+
// CHECK: dealloc_stack [[INT_BOX]] : $*Int
238+
// CHECK-NEXT: throw [[ERROR]] : $MyError
239+
}

0 commit comments

Comments
 (0)