Skip to content

Commit 3edbb06

Browse files
committed
[SILGen] Don't peephole closures with abstraction differences in the thrown error
The prolog and epilog code in SILGen is not set up to deal with abstraction differences in the thrown error type of closures, so disable the peephole optimization for closure literals. Fixes #71401 / rdar://122366566, which I've stared at for waaaaay too many hours.
1 parent 5cb418a commit 3edbb06

File tree

3 files changed

+53
-6
lines changed

3 files changed

+53
-6
lines changed

lib/SIL/IR/TypeLowering.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3246,6 +3246,13 @@ TypeConverter::computeLoweredRValueType(TypeExpansionContext forExpansion,
32463246
AnyFunctionType::ExtInfo baseExtInfo;
32473247
if (auto origFnType = origType.getAs<AnyFunctionType>()) {
32483248
baseExtInfo = origFnType->getExtInfo();
3249+
3250+
if (baseExtInfo.getThrownError()) {
3251+
if (auto substThrownError = substFnType->getEffectiveThrownErrorType())
3252+
baseExtInfo = baseExtInfo.withThrows(true, *substThrownError);
3253+
else
3254+
baseExtInfo = baseExtInfo.withThrows(false, Type());
3255+
}
32493256
} else {
32503257
baseExtInfo = substFnType->getExtInfo();
32513258
}

lib/SILGen/SILGenExpr.cpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1903,12 +1903,21 @@ static ManagedValue convertFunctionRepresentation(SILGenFunction &SGF,
19031903
llvm_unreachable("bad representation");
19041904
}
19051905

1906+
/// Whether the given abstraction pattern as an opaque thrown error.
1907+
static bool hasOpaqueThrownError(const AbstractionPattern &pattern) {
1908+
if (auto thrownPattern = pattern.getFunctionThrownErrorType())
1909+
return thrownPattern->isTypeParameterOrOpaqueArchetype();
1910+
1911+
return false;
1912+
}
1913+
19061914
// Ideally our prolog/epilog emission would be able to handle all possible
19071915
// reabstractions and conversions. Until then, this returns true if a closure
19081916
// literal of type `literalType` can be directly emitted by SILGen as
19091917
// `convertedType`.
1910-
static bool canPeepholeLiteralClosureConversion(Type literalType,
1911-
Type convertedType) {
1918+
static bool canPeepholeLiteralClosureConversion(
1919+
Type literalType, Type convertedType,
1920+
const std::optional<AbstractionPattern> &closurePattern) {
19121921
auto literalFnType = literalType->getAs<FunctionType>();
19131922
auto convertedFnType = convertedType->getAs<FunctionType>();
19141923

@@ -1919,22 +1928,28 @@ static bool canPeepholeLiteralClosureConversion(Type literalType,
19191928
if (literalFnType->isEqual(convertedFnType)) {
19201929
return true;
19211930
}
1922-
1931+
19231932
// Are the types equivalent aside from effects (throws) or coeffects
19241933
// (escaping)? Then we should emit the literal as having the destination type
19251934
// (co)effects, even if it doesn't exercise them.
19261935
//
19271936
// TODO: We could also in principle let `async` through here, but that
19281937
// interferes with the implementation of `reasync`.
19291938
auto literalWithoutEffects = literalFnType->getExtInfo().intoBuilder()
1930-
.withThrows(false, Type())
19311939
.withNoEscape(false)
19321940
.build();
19331941

19341942
auto convertedWithoutEffects = convertedFnType->getExtInfo().intoBuilder()
1935-
.withThrows(false, Type())
19361943
.withNoEscape(false)
19371944
.build();
1945+
1946+
// If the closure pattern has an abstract thrown error, we are unable to
1947+
// emit the literal with a difference in the thrown error type.
1948+
if (!(closurePattern && hasOpaqueThrownError(*closurePattern))) {
1949+
literalWithoutEffects = literalWithoutEffects.withThrows(false, Type());
1950+
convertedWithoutEffects = convertedWithoutEffects.withThrows(false, Type());
1951+
}
1952+
19381953
if (literalFnType->withExtInfo(literalWithoutEffects)
19391954
->isEqual(convertedFnType->withExtInfo(convertedWithoutEffects))) {
19401955
return true;
@@ -2000,7 +2015,8 @@ RValue RValueEmitter::visitFunctionConversionExpr(FunctionConversionExpr *e,
20002015

20012016
if ((isa<AbstractClosureExpr>(subExpr) || isa<CaptureListExpr>(subExpr))
20022017
&& canPeepholeLiteralClosureConversion(subExpr->getType(),
2003-
e->getType())) {
2018+
e->getType(),
2019+
C.getAbstractionPattern())) {
20042020
// If we're emitting into a context with a preferred abstraction pattern
20052021
// already, carry that along.
20062022
auto origType = C.getAbstractionPattern();

test/SILGen/typed_throws.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,30 @@ func throwAndPatternMatchCatch<E: Error>(_ body: () throws(E) -> Void) throws(E)
272272
}
273273
}
274274

275+
enum MyResult<Success, Failure: Error> {
276+
case success(Success)
277+
case failure(Failure)
278+
279+
@inlinable
280+
init(catching body: () throws(Failure) -> Success) {
281+
do {
282+
self = .success(try body())
283+
} catch {
284+
self = .failure(error)
285+
}
286+
}
287+
}
288+
289+
func formerReabstractionCrash() {
290+
// CHECK-LABEL: sil private [ossa] @$s12typed_throws24formerReabstractionCrashyyFAA8MyResultOySSs5Error_pGyXEfU_ : $@convention(thin) () -> @owned MyResult<String, any Error> {
291+
// CHECK: function_ref @$s12typed_throws24formerReabstractionCrashyyFAA8MyResultOySSs5Error_pGyXEfU_SSyXEfU_ : $@convention(thin) () -> @owned String
292+
// CHECK-NEXT: thin_to_thick_function
293+
// CHECK-NEXT: convert_function {{%.*}} : $@noescape @callee_guaranteed () -> @owned String to $@noescape @callee_guaranteed () -> (@owned String, @error any Error)
294+
let _: MyResult<String, Error>? = {
295+
return MyResult{"hello"}
296+
}()
297+
}
298+
275299
// CHECK-LABEL: sil_vtable MySubclass {
276300
// CHECK-NEXT: #MyClass.init!allocator: <E where E : Error> (MyClass.Type) -> (() throws(E) -> ()) throws(E) -> MyClass : @$s12typed_throws10MySubclassC4bodyACyyxYKXE_txYKcs5ErrorRzlufC [override]
277301
// CHECK-NEXT: #MyClass.f: (MyClass) -> () throws -> () : @$s12typed_throws10MySubclassC1fyyAA0C5ErrorOYKFAA0C5ClassCADyyKFTV [override]

0 commit comments

Comments
 (0)