Skip to content

Commit 54e5205

Browse files
authored
Merge pull request #68600 from kavon/optional-try-coercion-bug-v2
[SILGen] fix SR-14882: Assertion failed: (!concreteBuffer && "concrete buffer already formed?!")
2 parents 1d64558 + 583f127 commit 54e5205

File tree

3 files changed

+132
-19
lines changed

3 files changed

+132
-19
lines changed

lib/SILGen/SILGenExpr.cpp

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,18 +1210,27 @@ RValue RValueEmitter::visitOptionalTryExpr(OptionalTryExpr *E, SGFContext C) {
12101210

12111211
// Form the optional using address operations if the type is address-only or
12121212
// if we already have an address to use.
1213-
bool isByAddress = usingProvidedContext || optTL.isAddressOnly();
1213+
bool isByAddress = ((usingProvidedContext || optTL.isAddressOnly()) &&
1214+
SGF.silConv.useLoweredAddresses());
12141215

12151216
std::unique_ptr<TemporaryInitialization> optTemp;
1216-
if (!usingProvidedContext && isByAddress) {
1217+
if (!isByAddress) {
1218+
// If the caller produced a context for us, but we're not going
1219+
// to use it, make sure we don't.
1220+
optInit = nullptr;
1221+
} else if (!usingProvidedContext) {
12171222
// Allocate the temporary for the Optional<T> if we didn't get one from the
1218-
// context.
1223+
// context. This needs to happen outside of the cleanups scope we're about
1224+
// to push.
12191225
optTemp = SGF.emitTemporary(E, optTL);
12201226
optInit = optTemp.get();
1221-
} else if (!usingProvidedContext) {
1222-
// If the caller produced a context for us, but we can't use it, then don't.
1223-
optInit = nullptr;
12241227
}
1228+
assert(isByAddress == (optInit != nullptr));
1229+
1230+
// Acquire the address to emit into outside of the cleanups scope.
1231+
SILValue optAddr;
1232+
if (isByAddress)
1233+
optAddr = optInit->getAddressForInPlaceInitialization(SGF, E);
12251234

12261235
FullExpr localCleanups(SGF.Cleanups, E);
12271236

@@ -1234,8 +1243,7 @@ RValue RValueEmitter::visitOptionalTryExpr(OptionalTryExpr *E, SGFContext C) {
12341243
SILValue branchArg;
12351244
if (shouldWrapInOptional) {
12361245
if (isByAddress) {
1237-
assert(optInit);
1238-
SILValue optAddr = optInit->getAddressForInPlaceInitialization(SGF, E);
1246+
assert(optAddr);
12391247
SGF.emitInjectOptionalValueInto(E, E->getSubExpr(), optAddr, optTL);
12401248
} else {
12411249
ManagedValue subExprValue = SGF.emitRValueAsSingleValue(E->getSubExpr());
@@ -1245,8 +1253,11 @@ RValue RValueEmitter::visitOptionalTryExpr(OptionalTryExpr *E, SGFContext C) {
12451253
}
12461254
else {
12471255
if (isByAddress) {
1248-
assert(optInit);
1249-
SGF.emitExprInto(E->getSubExpr(), optInit);
1256+
assert(optAddr);
1257+
// We've already computed the address where we want the result.
1258+
KnownAddressInitialization normalInit(optAddr);
1259+
SGF.emitExprInto(E->getSubExpr(), &normalInit);
1260+
normalInit.finishInitialization(SGF);
12501261
} else {
12511262
ManagedValue subExprValue = SGF.emitRValueAsSingleValue(E->getSubExpr());
12521263
branchArg = subExprValue.forward(SGF);
@@ -1264,10 +1275,8 @@ RValue RValueEmitter::visitOptionalTryExpr(OptionalTryExpr *E, SGFContext C) {
12641275
if (!isByAddress)
12651276
return RValue(SGF, E,
12661277
SGF.emitManagedRValueWithCleanup(branchArg, optTL));
1267-
1268-
if (shouldWrapInOptional) {
1269-
optInit->finishInitialization(SGF);
1270-
}
1278+
1279+
optInit->finishInitialization(SGF);
12711280

12721281
// If we emitted into the provided context, we're done.
12731282
if (usingProvidedContext)
@@ -1294,8 +1303,7 @@ RValue RValueEmitter::visitOptionalTryExpr(OptionalTryExpr *E, SGFContext C) {
12941303
catchCleanups.pop();
12951304

12961305
if (isByAddress) {
1297-
SGF.emitInjectOptionalNothingInto(E,
1298-
optInit->getAddressForInPlaceInitialization(SGF, E), optTL);
1306+
SGF.emitInjectOptionalNothingInto(E, optAddr, optTL);
12991307
SGF.B.createBranch(E, contBB);
13001308
} else {
13011309
auto branchArg = SGF.getOptionalNoneValue(E, optTL);
@@ -1313,9 +1321,7 @@ RValue RValueEmitter::visitOptionalTryExpr(OptionalTryExpr *E, SGFContext C) {
13131321
return RValue(SGF, E, SGF.emitManagedRValueWithCleanup(arg, optTL));
13141322
}
13151323

1316-
if (shouldWrapInOptional) {
1317-
optInit->finishInitialization(SGF);
1318-
}
1324+
optInit->finishInitialization(SGF);
13191325

13201326
// If we emitted into the provided context, we're done.
13211327
if (usingProvidedContext)

test/Runtime/optional_try.swift

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// RUN: %target-run-simple-swift -sil-verify-all | %FileCheck %s
2+
// REQUIRES: executable_test
3+
4+
enum Bad: Error {
5+
case err
6+
case custom(String)
7+
}
8+
9+
func erase<T>(_ val: T) -> Any {
10+
return val as Any
11+
}
12+
13+
class Klass {}
14+
15+
typealias MaybeString = Result<String, Error>
16+
typealias MaybeKlass = Result<Klass, Error>
17+
typealias MaybeInt = Result<Int, Error>
18+
typealias MaybeNumbers = Result<[Int], Error>
19+
20+
////////
21+
// NOTE: Do _not_ heed the warnings about implicit coercions to Any.
22+
// That's an important part of this test's coverage!
23+
////////
24+
// -- throwing --
25+
// CHECK: nil
26+
print( try? MaybeString.failure(Bad.err).get() )
27+
28+
// CHECK: nil
29+
print( try? MaybeKlass.failure(Bad.custom("doggo")).get() )
30+
31+
// CHECK: nil
32+
print( try? MaybeInt.failure(Bad.err).get() )
33+
34+
// CHECK: nil
35+
print( try? MaybeNumbers.failure(Bad.err).get() )
36+
37+
// CHECK: nil
38+
print(erase( try? MaybeNumbers.failure(Bad.err).get() ))
39+
40+
// -- normal --
41+
// CHECK: Optional("catto")
42+
print( try? MaybeString.success("catto").get() )
43+
44+
// CHECK: Optional(main.Klass)
45+
print( try? MaybeKlass.success(Klass()).get() )
46+
47+
// CHECK: Optional(3)
48+
print( try? MaybeInt.success(3).get() )
49+
50+
// CHECK: Optional([4, 8, 15, 16, 23, 42])
51+
print( try? MaybeNumbers.success([4, 8, 15, 16, 23, 42]).get() )
52+
53+
// CHECK: Optional([0, 1, 1, 2, 3])
54+
print(erase( try? MaybeNumbers.success([0, 1, 1, 2, 3]).get() ))

test/SILGen/optional.swift

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,56 @@ func implicit_iuo_unwrap_sourceLocation(_ value: Int!) {
161161
use_unwrapped(value)
162162
#sourceLocation() // reset
163163
}
164+
165+
166+
// CHECK-LABEL: sil hidden [ossa] @$s8optional0A20_to_existential_castyyF : $@convention(thin) () -> () {
167+
// CHECK: bb0:
168+
// CHECK: [[MEM:%.*]] = alloc_stack $Any
169+
// CHECK: [[ADDR:%.*]] = init_existential_addr [[MEM]] : $*Any, $Optional<Int>
170+
// CHECK: [[PAYLOAD_ADDR:%.*]] = init_enum_data_addr [[ADDR]] : $*Optional<Int>, #Optional.some!enumelt
171+
// CHECK: [[CLOSURE:%.*]] = function_ref @$s8optional0A20_to_existential_castyyFSiyKXEfU_ : $@convention(thin) () -> (Int, @error any Error)
172+
// CHECK: try_apply [[CLOSURE]]() : $@convention(thin) () -> (Int, @error any Error), normal bb1, error bb3
173+
174+
// CHECK: bb1([[RESULT:%.*]] : $Int):
175+
// CHECK: store [[RESULT]] to [trivial] [[PAYLOAD_ADDR]] : $*Int
176+
// CHECK: inject_enum_addr [[ADDR]] : $*Optional<Int>, #Optional.some!enumelt
177+
// CHECK: br bb2
178+
179+
// CHECK: bb2:
180+
// CHECK-NEXT: destroy_addr [[MEM]] : $*Any
181+
// CHECK-NEXT: dealloc_stack [[MEM]] : $*Any
182+
// CHECK: return
183+
184+
// CHECK: bb3([[ERR:%.*]] : @owned $any Error):
185+
// CHECK: destroy_value [[ERR]] : $any Error
186+
// CHECK: inject_enum_addr [[ADDR]] : $*Optional<Int>, #Optional.none!enumelt
187+
// CHECK: br bb2
188+
// CHECK: }
189+
func optional_to_existential_cast() {
190+
let _: Any = try? {() throws in 3 }()
191+
}
192+
193+
// CHECK-LABEL: sil hidden [ossa] @$s8optional0A29_to_existential_cast_RETURNEDypyF : $@convention(thin) () -> @out Any {
194+
// CHECK: bb0([[MEM:%.*]] : $*Any):
195+
// CHECK: [[ADDR:%.*]] = init_existential_addr [[MEM]] : $*Any, $Optional<Int>
196+
// CHECK: [[PAYLOAD_ADDR:%.*]] = init_enum_data_addr [[ADDR]] : $*Optional<Int>, #Optional.some!enumelt
197+
// CHECK: [[CLOSURE:%.*]] = function_ref @$s8optional0A29_to_existential_cast_RETURNEDypyFSiyKXEfU_ : $@convention(thin) () -> (Int, @error any Error)
198+
// CHECK: try_apply [[CLOSURE]]() : $@convention(thin) () -> (Int, @error any Error), normal bb1, error bb3
199+
200+
// CHECK: bb1([[RESULT:%.*]] : $Int):
201+
// CHECK: store [[RESULT]] to [trivial] [[PAYLOAD_ADDR]] : $*Int
202+
// CHECK: inject_enum_addr [[ADDR]] : $*Optional<Int>, #Optional.some!enumelt
203+
// CHECK: br bb2
204+
205+
// CHECK: bb2:
206+
// CHECK-NEXT: = tuple ()
207+
// CHECK-NEXT: return
208+
209+
// CHECK: bb3([[ERR:%.*]] : @owned $any Error):
210+
// CHECK: destroy_value [[ERR]] : $any Error
211+
// CHECK: inject_enum_addr [[ADDR]] : $*Optional<Int>, #Optional.none!enumelt
212+
// CHECK: br bb2
213+
// CHECK: }
214+
func optional_to_existential_cast_RETURNED() -> Any {
215+
return try? {() throws in 3 }()
216+
}

0 commit comments

Comments
 (0)