Skip to content

Commit cfed6e5

Browse files
committed
[Typed throws] Implement reabstraction thunks that change the error
Introduce SILGen support for reabstractions thunks that change the error, between indirect and direct errors as well as conversions amongst error types (e.g., from concrete to `any Error`).
1 parent 78adeb6 commit cfed6e5

File tree

8 files changed

+192
-17
lines changed

8 files changed

+192
-17
lines changed

include/swift/SIL/SILFunctionConventions.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,10 +216,7 @@ class SILFunctionConventions {
216216
return getSILType(funcTy->getErrorResult(), context);
217217
}
218218

219-
bool isTypedError() const {
220-
return !funcTy->getErrorResult()
221-
.getInterfaceType()->isExistentialWithError();
222-
}
219+
bool isTypedError() const;
223220

224221
/// Returns an array of result info.
225222
/// Provides convenient access to the underlying SILFunctionType.

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4943,3 +4943,8 @@ CanSILFunctionType SILFunction::getLoweredFunctionTypeInContext(
49434943
auto funTy = M.Types.getLoweredType(origFunTy , context);
49444944
return cast<SILFunctionType>(funTy.getASTType());
49454945
}
4946+
4947+
bool SILFunctionConventions::isTypedError() const {
4948+
return !funcTy->getErrorResult()
4949+
.getInterfaceType()->isExistentialWithError();
4950+
}

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6067,7 +6067,6 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
60676067
}
60686068

60696069
if (fnConv.hasIndirectSILErrorResults()) {
6070-
assert(fnConv.isTypedError());
60716070
auto errorResult = fnConv.getSILErrorType(F.getTypeExpansionContext());
60726071
check("indirect error result", errorResult);
60736072
}

lib/SILGen/SILGenApply.cpp

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5715,7 +5715,6 @@ SILValue SILGenFunction::emitApplyWithRethrow(SILLocation loc, SILValue fn,
57155715
CanSILFunctionType silFnType = substFnType.castTo<SILFunctionType>();
57165716
SILFunctionConventions fnConv(silFnType, SGM.M);
57175717
SILType resultType = fnConv.getSILResultType(getTypeExpansionContext());
5718-
57195718
if (!silFnType->hasErrorResult()) {
57205719
return B.createApply(loc, fn, subs, args);
57215720
}
@@ -5728,19 +5727,64 @@ SILValue SILGenFunction::emitApplyWithRethrow(SILLocation loc, SILValue fn,
57285727
{
57295728
B.emitBlock(errorBB);
57305729

5731-
SILValue error;
5732-
bool indirectError = fnConv.hasIndirectSILErrorResults();
5733-
5734-
if (!indirectError) {
5735-
error = errorBB->createPhiArgument(
5730+
// Grab the inner error.
5731+
SILValue innerError;
5732+
bool hasInnerIndirectError = fnConv.hasIndirectSILErrorResults();
5733+
if (!hasInnerIndirectError) {
5734+
innerError = errorBB->createPhiArgument(
57365735
fnConv.getSILErrorType(getTypeExpansionContext()),
57375736
OwnershipKind::Owned);
5737+
} else {
5738+
// FIXME: This probably belongs on SILFunctionConventions.
5739+
innerError = args[fnConv.getNumIndirectSILResults()];
5740+
}
5741+
5742+
// Convert to the outer error, if we need to.
5743+
SILValue outerError;
5744+
SILType innerErrorType = innerError->getType().getObjectType();
5745+
SILType outerErrorType = F.mapTypeIntoContext(
5746+
F.getConventions().getSILErrorType(getTypeExpansionContext()));
5747+
if (IndirectErrorResult && IndirectErrorResult == innerError) {
5748+
// Fast path: we aliased the indirect error result slot because both are
5749+
// indirect and the types matched, so we are done.
5750+
} else if (!IndirectErrorResult && !hasInnerIndirectError &&
5751+
innerErrorType == outerErrorType) {
5752+
// Fast path: both have a direct error result and the types line up, so
5753+
// rethrow the inner error.
5754+
outerError = innerError;
5755+
} else {
5756+
// The error requires some kind of translation.
5757+
5758+
// Load the inner error, if it was returned indirectly.
5759+
if (innerError->getType().isAddress()) {
5760+
innerError = emitLoad(loc, innerError, getTypeLowering(innerErrorType),
5761+
SGFContext(), IsTake).forward(*this);
5762+
}
5763+
5764+
// If we need to convert the error type, do so now.
5765+
if (innerErrorType != outerErrorType) {
5766+
auto conversion = Conversion::getOrigToSubst(
5767+
AbstractionPattern(innerErrorType.getASTType()),
5768+
outerErrorType.getASTType(),
5769+
outerErrorType);
5770+
outerError = emitConvertedRValue(loc, conversion, SGFContext(),
5771+
[innerError](SILGenFunction &SGF, SILLocation loc, SGFContext C) {
5772+
return ManagedValue::forForwardedRValue(SGF, innerError);
5773+
}).forward(*this);
5774+
}
5775+
5776+
// If the outer error is returned indirectly, copy from the converted
5777+
// inner error to the outer error slot.
5778+
if (IndirectErrorResult) {
5779+
emitSemanticStore(loc, outerError, IndirectErrorResult,
5780+
getTypeLowering(outerErrorType), IsInitialization);
5781+
}
57385782
}
57395783

57405784
Cleanups.emitCleanupsForReturn(CleanupLocation(loc), IsForUnwind);
57415785

5742-
if (!indirectError)
5743-
B.createThrow(loc, error);
5786+
if (!IndirectErrorResult)
5787+
B.createThrow(loc, outerError);
57445788
else
57455789
B.createThrowAddr(loc);
57465790
}

lib/SILGen/SILGenFunction.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2273,7 +2273,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
22732273
/// Used for emitting SILArguments of bare functions, such as thunks.
22742274
void collectThunkParams(
22752275
SILLocation loc, SmallVectorImpl<ManagedValue> &params,
2276-
SmallVectorImpl<ManagedValue> *indirectResultParams = nullptr);
2276+
SmallVectorImpl<ManagedValue> *indirectResultParams = nullptr,
2277+
SmallVectorImpl<ManagedValue> *indirectErrorParams = nullptr);
22772278

22782279
/// Build the type of a function transformation thunk.
22792280
CanSILFunctionType buildThunkType(CanSILFunctionType &sourceType,

lib/SILGen/SILGenPoly.cpp

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -850,7 +850,8 @@ ManagedValue Transform::transformTuple(ManagedValue inputTuple,
850850

851851
void SILGenFunction::collectThunkParams(
852852
SILLocation loc, SmallVectorImpl<ManagedValue> &params,
853-
SmallVectorImpl<ManagedValue> *indirectResults) {
853+
SmallVectorImpl<ManagedValue> *indirectResults,
854+
SmallVectorImpl<ManagedValue> *indirectErrors) {
854855
// Add the indirect results.
855856
for (auto resultTy : F.getConventions().getIndirectSILResultTypes(
856857
getTypeExpansionContext())) {
@@ -864,6 +865,19 @@ void SILGenFunction::collectThunkParams(
864865
indirectResults->push_back(ManagedValue::forLValue(arg));
865866
}
866867

868+
if (F.getConventions().hasIndirectSILErrorResults()) {
869+
assert(F.getConventions().getNumIndirectSILErrorResults() == 1);
870+
auto paramTy = F.mapTypeIntoContext(
871+
F.getConventions().getSILErrorType(getTypeExpansionContext()));
872+
auto inContextParamTy = F.getLoweredType(paramTy.getASTType())
873+
.getCategoryType(paramTy.getCategory());
874+
SILArgument *arg = F.begin()->createFunctionArgument(inContextParamTy);
875+
if (indirectErrors)
876+
indirectErrors->push_back(ManagedValue::forLValue(arg));
877+
878+
IndirectErrorResult = arg;
879+
}
880+
867881
// Add the parameters.
868882
auto paramTypes = F.getLoweredFunctionType()->getParameters();
869883
for (auto param : paramTypes) {
@@ -4831,6 +4845,31 @@ static void buildThunkBody(SILGenFunction &SGF, SILLocation loc,
48314845
outputSubstType.getResult(),
48324846
fnType, thunkType);
48334847

4848+
// If the function we're calling has as indirect error result, create an
4849+
// argument for it.
4850+
SILValue innerIndirectErrorAddr;
4851+
if (auto innerError = fnType->getOptionalErrorResult()) {
4852+
if (innerError->getConvention() == ResultConvention::Indirect) {
4853+
auto loweredErrorResultType = SGF.getSILType(*innerError, fnType);
4854+
if (SGF.IndirectErrorResult &&
4855+
SGF.IndirectErrorResult->getType().getObjectType()
4856+
== loweredErrorResultType) {
4857+
// The type of the indirect error is the same for both the inner
4858+
// function and the thunk, so we can re-use the indirect error slot.
4859+
innerIndirectErrorAddr = SGF.IndirectErrorResult;
4860+
} else {
4861+
// The type of the indirect error in the inner function differs from
4862+
// that of the thunk, or the thunk has a direct error, so allocate a
4863+
// stack location for the inner indirect error.
4864+
innerIndirectErrorAddr =
4865+
SGF.B.createAllocStack(loc, loweredErrorResultType);
4866+
SGF.enterDeallocStackCleanup(innerIndirectErrorAddr);
4867+
}
4868+
4869+
argValues.push_back(innerIndirectErrorAddr);
4870+
}
4871+
}
4872+
48344873
// Add the rest of the arguments.
48354874
forwardFunctionArguments(SGF, loc, fnType, args, argValues);
48364875

lib/SILGen/SILGenStmt.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1567,14 +1567,19 @@ void SILGenFunction::emitThrow(SILLocation loc, ManagedValue exnMV,
15671567
getModule().getSwiftModule()->conformsToProtocol(
15681568
exn->getType().getASTType(), getASTContext().getErrorDecl())
15691569
};
1570+
15701571
exnMV = emitExistentialErasure(
15711572
loc,
15721573
exn->getType().getASTType(),
1573-
getTypeLowering(exn->getType()),
1574+
getTypeLowering(exn->getType().getObjectType()),
15741575
getTypeLowering(existentialBoxType),
15751576
getASTContext().AllocateCopy(conformances),
15761577
SGFContext(),
15771578
[&](SGFContext C) -> ManagedValue {
1579+
if (exn->getType().isAddress()) {
1580+
return emitLoad(loc, exn, getTypeLowering(exn->getType().getObjectType()), C, IsTake);
1581+
}
1582+
15781583
return ManagedValue::forForwardedRValue(*this, exn);
15791584
});
15801585

test/SILGen/typed_throws_generic.swift

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,89 @@ public func genericForceTry<E>(fn: () throws(E) -> ()) {
9494
// CHECK: end_borrow [[FN_BORROW]] : $@noescape @callee_guaranteed @substituted <τ_0_0> () -> @error_indirect τ_0_0 for <E>
9595
// CHECK: dealloc_stack [[ERROR]] : $*E
9696
// CHECK: destroy_value [[FN]] : $@noescape @callee_guaranteed @substituted <τ_0_0> () -> @error_indirect τ_0_0 for <E>
97-
// CHECK: unreachable
97+
// CHECK: unreachable
98+
99+
enum MyError: Error {
100+
case fail
101+
}
102+
103+
func passthroughCall<T, E>(_ body: () throws(E) -> T) throws(E) -> T {
104+
try body()
105+
}
106+
107+
func five() -> Int { 5 }
108+
109+
func fiveOrBust() throws -> Int { 5 }
110+
111+
func fiveOrTypedBust() throws(MyError) -> Int { 5 }
112+
113+
// CHECK-LABEL: sil hidden [ossa] @$s20typed_throws_generic23reabstractAsNonthrowingSiyF
114+
func reabstractAsNonthrowing() -> Int {
115+
// CHECK: [[FN:%.*]] = convert_escape_to_noescape [not_guaranteed] [[PRIOR_FN:%.*]] : $@callee_guaranteed @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>
116+
// 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)
117+
// CHECK: [[ERROR_SLOT:%.*]] = alloc_stack $Never
118+
// CHECK: try_apply [[CALLEE]]<Int, Never>(%0, [[ERROR_SLOT]], [[FN]]) : $@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]+]]
119+
passthroughCall(five)
120+
121+
// CHECK: [[NORMAL_BB]]
122+
// CHECK: dealloc_stack [[ERROR_SLOT]] : $*Never
123+
124+
// CHECK: [[ERROR_BB]]:
125+
// CHECK-NEXT: unreachable
126+
}
127+
128+
// CHECK-LABEL: sil hidden [ossa] @$s20typed_throws_generic20reabstractAsThrowingSiyKF
129+
func reabstractAsThrowing() throws -> Int {
130+
// CHECK: [[FN:%.*]] = convert_escape_to_noescape [not_guaranteed] [[PRIOR_FN:%.*]] : $@callee_guaranteed @substituted <τ_0_0, τ_0_1> () -> (@out τ_0_1, @error_indirect τ_0_0) for <any Error, Int> to $@noescape @callee_guaranteed @substituted <τ_0_0, τ_0_1> () -> (@out τ_0_1, @error_indirect τ_0_0) for <any Error, Int>
131+
// 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)
132+
// CHECK: [[ERROR_SLOT:%.*]] = alloc_stack $any Error
133+
// CHECK: try_apply [[CALLEE]]<Int, any Error>(%1, [[ERROR_SLOT]], [[FN]]) : $@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]+]]
134+
try passthroughCall(fiveOrBust)
135+
136+
// CHECK: [[NORMAL_BB]]
137+
// CHECK: dealloc_stack [[ERROR_SLOT]] : $*any Error
138+
139+
// CHECK: [[ERROR_BB]]:
140+
// CHECK: throw [[ERR:%.*]] : $any Error
141+
}
142+
143+
// CHECK-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sSis5Error_pIgdzo_SisAA_pIegrzr_TR : $@convention(thin) (@guaranteed @noescape @callee_guaranteed () -> (Int, @error any Error)) -> (@out Int, @error_indirect any Error)
144+
// CHECK: bb0([[ARG:%.*]] : $*Int, [[OUTER_ERROR_SLOT:%.*]] : $*any Error, [[INNER_FN:%.*]] : @guaranteed $@noescape @callee_guaranteed () -> (Int, @error any Error)):
145+
// CHECK: try_apply [[INNER_FN]]() : $@noescape @callee_guaranteed () -> (Int, @error any Error), normal [[NORMAL_BB:bb[0-9]+]], error [[ERROR_BB:bb[0-9]+]]
146+
147+
// CHECK: [[NORMAL_BB]]([[RESULT:%.*]] : $Int):
148+
// CHECK-NEXT: store [[RESULT]] to [trivial] [[ARG]] : $*Int
149+
// CHECK-NEXT: [[VOID_RESULT:%.*]] = tuple ()
150+
// CHECK-NEXT: return [[VOID_RESULT]] : $()
151+
152+
// CHECK: [[ERROR_BB]]([[INNER_ERROR:%.*]] : @owned $any Error):
153+
// CHECK-NEXT: store [[INNER_ERROR]] to [init] [[OUTER_ERROR_SLOT]] : $*any Error
154+
// CHECK-NEXT: throw_addr
155+
156+
// CHECK-LABEL: sil hidden [ossa] @$s20typed_throws_generic28reabstractAsConcreteThrowingSiyKF : $@convention(thin) () -> (Int, @error any Error)
157+
func reabstractAsConcreteThrowing() throws -> Int {
158+
// CHECK: [[FN:%.*]] = convert_escape_to_noescape [not_guaranteed] [[PRIOR_FN:%.*]] : $@callee_guaranteed @substituted <τ_0_0, τ_0_1> () -> (@out τ_0_1, @error_indirect τ_0_0) for <MyError, Int> to $@noescape @callee_guaranteed @substituted <τ_0_0, τ_0_1> () -> (@out τ_0_1, @error_indirect τ_0_0) for <MyError, Int>
159+
// 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)
160+
// CHECK: [[ERROR_SLOT:%.*]] = alloc_stack $MyError
161+
// CHECK: try_apply [[CALLEE]]<Int, MyError>(%1, [[ERROR_SLOT]], [[FN]]) : $@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]+]]
162+
try passthroughCall(fiveOrTypedBust)
163+
164+
// CHECK: [[NORMAL_BB]]
165+
// CHECK-NEXT: dealloc_stack [[ERROR_SLOT]] : $*MyError
166+
167+
// CHECK: [[ERROR_BB]]:
168+
// CHECK: alloc_existential_box $any Error, $MyError
169+
// CHECK-NEXT: project_existential_box $MyError in
170+
// CHECK: throw [[ERR:%.*]] : $any Error
171+
}
172+
173+
// CHECK-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sSi20typed_throws_generic7MyErrorOIgdzo_SiACIegrzr_TR : $@convention(thin) (@guaranteed @noescape @callee_guaranteed () -> (Int, @error MyError)) -> (@out Int, @error_indirect MyError) {
174+
// CHECK: bb0([[RESULT_ADDR:%.*]] : $*Int, [[OUTER_ERROR:%.*]] : $*MyError, [[FN:%.*]] : @guaranteed $@noescape @callee_guaranteed () -> (Int, @error MyError)):
175+
// CHECK-NEXT: try_apply [[FN]]() : $@noescape @callee_guaranteed () -> (Int, @error MyError), normal [[NORMAL_BB:bb[0-9]+]], error [[ERROR_BB:bb[0-9]+]]
176+
177+
// CHECK: [[NORMAL_BB]]([[INNER_RESULT:%.*]] : $Int):
178+
// CHECK-NEXT: store [[INNER_RESULT]] to [trivial] [[RESULT_ADDR]] : $*Int
179+
180+
// CHECK: [[ERROR_BB]]([[INNER_ERROR:%.*]] : $MyError):
181+
// CHECK-NEXT: store [[INNER_ERROR]] to [trivial] [[OUTER_ERROR]] : $*MyError
182+
// CHECK-NEXT: throw_addr

0 commit comments

Comments
 (0)