Skip to content

Commit eeeb8c3

Browse files
authored
Merge pull request #71577 from DougGregor/silgen-no-peeping-typed-throws-closures
[SILGen] Don't peephole closures with abstraction differences in the thrown error
2 parents 2170b4e + 3edbb06 commit eeeb8c3

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)