@@ -225,14 +225,103 @@ static CanType getHashableExistentialType(ModuleDecl *M) {
225
225
return hashable->getDeclaredInterfaceType ()->getCanonicalType ();
226
226
}
227
227
228
- static bool canBeExistential (CanType ty) {
229
- // If ty is an archetype, conservatively assume it's an existential.
230
- return ty.isAnyExistentialType () || ty->is <ArchetypeType>();
231
- }
228
+ // Returns true if casting \p sourceFormalType to \p targetFormalType preserves
229
+ // ownership.
230
+ //
231
+ // Casting preserves ownership when all references from the source value are
232
+ // forwarded into the result value (without unbalanced retains or releases).
233
+ //
234
+ // When both the source and target types of checked-cast preserve ownership,
235
+ // then the cast is compatible with guaranteed ownership. A guaranteed
236
+ // compatible cast cannot release any references within its operand's value
237
+ // and cannot retain any references owned by its result.
238
+ //
239
+ // A type's ownership might not be preserved by a dynamic cast if it is either
240
+ // (A) a potentially bridged value
241
+ // or
242
+ // (B) potentially wrapped in a transparent type, which is equivalent to
243
+ // isPotentiallyAnyObject()
244
+ //
245
+ // Given:
246
+ // let source: sourceType
247
+ // let dest = source as! targetType
248
+ //
249
+ // Ownership conversion happens when
250
+ //
251
+ // (A) one type is a bridged value and the other is an object:
252
+ //
253
+ // (A1) Boxing: <trivial> as! Object instantiates references in Object
254
+ // Presumably, Object's type must be class-bound, but this is not
255
+ // currently checked.
256
+ //
257
+ // (A2) Unboxing: Object as! <trivial> destroys references in Object
258
+ // Object may be any type that can hold an object, including
259
+ // non-class-bound archetypes and existentials.
260
+ //
261
+ // (B) one type is transparently wrapped in __SwiftValue, while the other is
262
+ // unwrapped. Given:
263
+ //
264
+ // class C : Hashable {}
265
+ // let a = AnyHashable(C())
266
+ //
267
+ // (B1) When the substituted source type is AnyHashable and the
268
+ // substituted destination type is AnyObject, the cast
269
+ // instantiates an owned __SwiftValue:
270
+ //
271
+ // // instantiates __SwiftValue
272
+ // let b = a as! AnyObject
273
+ // or
274
+ // let b = a as! T where T.self == AnyObject.self
275
+ //
276
+ // (B2) When the substituted source type is Any or AnyObject, and the
277
+ // substituted destination type is not Any or AnyObject, the cast
278
+ // releases the owned __SwiftValue:
279
+ //
280
+ // let c = b as! C // releases __SwiftValue
281
+ //
282
+ // After unwrapping Optional, the type may fall into one one of
283
+ // the following categories that are relevant for cast ownership:
284
+ //
285
+ // Class-bound types (hasReferenceSemantics() && !isPotentiallyAnyObject())
286
+ // - includes classes, class-bound existentials other than AnyObject,
287
+ // class-bound archetypes with a superclass or protocol constraint,
288
+ // objc types, blocks, Builtin.NativeObject, etc.
289
+ // - excludes any type that are potentially AnyObject after substitution
290
+ // - the value is a single reference
291
+ // - the single reference is "known unwrapped". It never transparently wraps the
292
+ // underlying dynamically typed value in another type, such as __SwiftValue
293
+ // - casting directly forwards the reference
294
+ //
295
+ // Potentially bridged values:
296
+ // - includes struct, enum, non-class archetype, non-class existential,
297
+ // and non-objc-metatype
298
+ // - these types are potentially trivial after subsitution. If so, then they
299
+ // convert to a reference when casting to AnyObject or certain classes
300
+ //
301
+ // Any and AnyObject existentials:
302
+ // - although called existentials, their type is a protocol composition
303
+ // - these do not include existentials with constraints
304
+ // - these are very special types, unlike normal existentials...
305
+ // - the immediately erased value may itself be an existential
306
+ // (an AnyObject existential can be wrapped within an Any existential!)
307
+ // - the underlying dynamically typed value may be transparently wrapped in
308
+ // __SwiftValue
309
+ //
310
+ // These type categories are disjoint, except that a non-class archetype is both
311
+ // potentially bridged and potentially Any or AnyObject after substitution.
312
+ //
313
+ // TODO: In the future, when the runtime stops wrapping nontrivial types inside
314
+ // __SwiftValue, cases (B1) and (B2) above will no longer apply. At that time,
315
+ // expand ownership preserving cast types to AnyObject. Then remove the
316
+ // isPotentiallyAnyObject() check.
317
+ bool swift::doesCastPreserveOwnershipForTypes (SILModule &module ,
318
+ CanType sourceType,
319
+ CanType targetType) {
320
+ if (!canIRGenUseScalarCheckedCastInstructions (module , sourceType, targetType))
321
+ return false ;
232
322
233
- static bool canBeClass (CanType ty) {
234
- // If ty is an archetype, conservatively assume it's an existential.
235
- return ty.getClassOrBoundGenericClass () || ty->is <ArchetypeType>();
323
+ return !sourceType->isPotentiallyAnyObject ()
324
+ && !targetType->isPotentiallyAnyObject ();
236
325
}
237
326
238
327
bool SILDynamicCastInst::isRCIdentityPreserving () const {
@@ -247,26 +336,12 @@ bool SILDynamicCastInst::isRCIdentityPreserving() const {
247
336
// would get confused and might eliminate a retain of such an object
248
337
// completely.
249
338
SILFunction &f = *getFunction ();
250
- if (getSourceLoweredType ().isTrivial (f) != getTargetLoweredType ().isTrivial (f))
251
- return false ;
252
-
253
- CanType source = getSourceFormalType ();
254
- CanType target = getTargetFormalType ();
255
-
256
- // An existential may be holding a reference to a bridgeable struct.
257
- // In this case, ARC on the existential affects the refcount of the container
258
- // holding the struct, not the class to which the struct is bridged.
259
- // Therefore, don't assume RC identity when casting between existentials and
260
- // classes (and also between two existentials).
261
- if (canBeExistential (source) &&
262
- (canBeClass (target) || canBeExistential (target)))
263
- return false ;
264
-
265
- // And vice versa.
266
- if (canBeClass (source) && canBeExistential (target))
267
- return false ;
268
-
269
- return true ;
339
+ if (getSourceLoweredType ().isTrivial (f)
340
+ && getTargetLoweredType ().isTrivial (f)) {
341
+ return true ;
342
+ }
343
+ return doesCastPreserveOwnershipForTypes (f.getModule (), getSourceFormalType (),
344
+ getTargetFormalType ());
270
345
}
271
346
272
347
// / Check if a given type conforms to _BridgedToObjectiveC protocol.
@@ -1204,15 +1279,28 @@ bool swift::emitSuccessfulIndirectUnconditionalCast(
1204
1279
}
1205
1280
1206
1281
// / Can the given cast be performed by the scalar checked-cast
1207
- // / instructions?
1282
+ // / instructions at the current SIL stage ?
1208
1283
// /
1209
- // / TODO: in OSSA-with-opaque-values SIL, all casts could be modeled using
1210
- // / scalar casts by setting 'OwnershipForwardingMixin::directlyForwards =
1211
- // / false'. This would simplify SIL analysis. Temporaries would be emitted
1212
- // / during address lowering.
1213
- bool swift::canUseScalarCheckedCastInstructions (SILModule &M,
1214
- CanType sourceFormalType,
1215
- CanType targetFormalType) {
1284
+ // / Always returns true for !useLoweredAddresses. Scalar casts are always
1285
+ // / valid for owned values. If the operand is +1, the case will always destroy
1286
+ // / or forward it. The result is always either +1 or trivial. The cast never
1287
+ // / hides a copy. doesCastPreserveOwnershipForTypes determines whether the
1288
+ // / scalar cast is also compatible with guaranteed values.
1289
+ bool swift::canSILUseScalarCheckedCastInstructions (SILModule &M,
1290
+ CanType sourceFormalType,
1291
+ CanType targetFormalType) {
1292
+ if (!M.useLoweredAddresses ())
1293
+ return true ;
1294
+
1295
+ return canIRGenUseScalarCheckedCastInstructions (M, sourceFormalType,
1296
+ targetFormalType);
1297
+ }
1298
+
1299
+ // / Can the given cast be performed by the scalar checked-cast
1300
+ // / instructions?
1301
+ bool swift::canIRGenUseScalarCheckedCastInstructions (SILModule &M,
1302
+ CanType sourceFormalType,
1303
+ CanType targetFormalType) {
1216
1304
// Look through one level of optionality on the source.
1217
1305
auto objectType = sourceFormalType;
1218
1306
if (auto type = objectType.getOptionalObjectType ())
@@ -1278,8 +1366,9 @@ void swift::emitIndirectConditionalCastWithScalar(
1278
1366
SILValue destAddr, CanType targetFormalType,
1279
1367
SILBasicBlock *indirectSuccBB, SILBasicBlock *indirectFailBB,
1280
1368
ProfileCounter TrueCount, ProfileCounter FalseCount) {
1281
- assert (canUseScalarCheckedCastInstructions (B.getModule (),
1282
- sourceFormalType, targetFormalType));
1369
+ assert (canSILUseScalarCheckedCastInstructions (B.getModule (),
1370
+ sourceFormalType,
1371
+ targetFormalType));
1283
1372
1284
1373
// Create our successor and fail blocks.
1285
1374
SILBasicBlock *scalarFailBB = B.splitBlockForFallthrough ();
0 commit comments