@@ -291,21 +291,34 @@ llvm::Value *irgen::emitReferenceToObjCProtocol(IRGenFunction &IGF,
291
291
// / Emit a helper function to look up \c numProtocols witness tables given
292
292
// / a value and a type metadata reference.
293
293
// /
294
- // / The function's input type is (value, metadataValue, protocol...)
294
+ // / If \p checkClassConstraint is true, we must emit an explicit check that the
295
+ // / instance is a class.
296
+ // /
297
+ // / If \p checkSuperclassConstraint is true, we are given an additional parameter
298
+ // / with a superclass type in it, and must emit a check that the instance is a
299
+ // / subclass of the given class.
300
+ // /
301
+ // / The function's input type is (value, metadataValue, superclass?, protocol...)
295
302
// / The function's output type is (value, witnessTable...)
296
303
// /
297
304
// / The value is NULL if the cast failed.
298
- static llvm::Function *emitExistentialScalarCastFn (IRGenModule &IGM,
299
- unsigned numProtocols,
300
- CheckedCastMode mode,
301
- bool checkClassConstraint) {
305
+ static llvm::Function *
306
+ emitExistentialScalarCastFn (IRGenModule &IGM,
307
+ unsigned numProtocols,
308
+ CheckedCastMode mode,
309
+ bool checkClassConstraint,
310
+ bool checkSuperclassConstraint) {
311
+ assert (!checkSuperclassConstraint || checkClassConstraint);
312
+
302
313
// Build the function name.
303
314
llvm::SmallString<32 > name;
304
315
{
305
316
llvm::raw_svector_ostream os (name);
306
317
os << " dynamic_cast_existential_" ;
307
318
os << numProtocols;
308
- if (checkClassConstraint)
319
+ if (checkSuperclassConstraint)
320
+ os << " _superclass" ;
321
+ else if (checkClassConstraint)
309
322
os << " _class" ;
310
323
switch (mode) {
311
324
case CheckedCastMode::Unconditional:
@@ -329,6 +342,8 @@ static llvm::Function *emitExistentialScalarCastFn(IRGenModule &IGM,
329
342
argTys.push_back (IGM.Int8PtrTy );
330
343
argTys.push_back (IGM.TypeMetadataPtrTy );
331
344
returnTys.push_back (IGM.Int8PtrTy );
345
+ if (checkSuperclassConstraint)
346
+ argTys.push_back (IGM.TypeMetadataPtrTy );
332
347
for (unsigned i = 0 ; i < numProtocols; ++i) {
333
348
argTys.push_back (IGM.ProtocolDescriptorPtrTy );
334
349
returnTys.push_back (IGM.WitnessTablePtrTy );
@@ -355,7 +370,26 @@ static llvm::Function *emitExistentialScalarCastFn(IRGenModule &IGM,
355
370
rets.add (value);
356
371
357
372
// Check the class constraint if necessary.
358
- if (checkClassConstraint) {
373
+ if (checkSuperclassConstraint) {
374
+ auto superclassMetadata = args.claimNext ();
375
+ auto castFn = IGF.IGM .getDynamicCastMetatypeFn ();
376
+ auto castResult = IGF.Builder .CreateCall (castFn, {ref,
377
+ superclassMetadata});
378
+
379
+ auto cc = cast<llvm::Function>(castFn)->getCallingConv ();
380
+
381
+ // FIXME: Eventually, we may want to throw.
382
+ castResult->setCallingConv (cc);
383
+ castResult->setDoesNotThrow ();
384
+
385
+ auto isClass = IGF.Builder .CreateICmpNE (
386
+ castResult,
387
+ llvm::ConstantPointerNull::get (IGF.IGM .TypeMetadataPtrTy ));
388
+
389
+ auto contBB = IGF.createBasicBlock (" cont" );
390
+ IGF.Builder .CreateCondBr (isClass, contBB, failBB);
391
+ IGF.Builder .emitBlock (contBB);
392
+ } else if (checkClassConstraint) {
359
393
auto isClass = IGF.Builder .CreateCall (IGM.getIsClassTypeFn (), ref);
360
394
auto contBB = IGF.createBasicBlock (" cont" );
361
395
IGF.Builder .CreateCondBr (isClass, contBB, failBB);
@@ -374,7 +408,7 @@ static llvm::Function *emitExistentialScalarCastFn(IRGenModule &IGM,
374
408
IGF.Builder .emitBlock (contBB);
375
409
rets.add (witness);
376
410
}
377
-
411
+
378
412
// If we succeeded, return the witnesses.
379
413
IGF.emitScalarReturn (returnTy, rets);
380
414
@@ -472,11 +506,15 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
472
506
CheckedCastMode mode,
473
507
Optional<MetatypeRepresentation> metatypeKind,
474
508
Explosion &ex) {
475
- auto instanceType = destType.getSwiftRValueType ();
476
- while (auto metatypeType = dyn_cast<ExistentialMetatypeType>(instanceType))
477
- instanceType = metatypeType.getInstanceType ();
509
+ auto srcInstanceType = srcType.getSwiftRValueType ();
510
+ auto destInstanceType = destType.getSwiftRValueType ();
511
+ while (auto metatypeType = dyn_cast<ExistentialMetatypeType>(
512
+ destInstanceType)) {
513
+ destInstanceType = metatypeType.getInstanceType ();
514
+ srcInstanceType = cast<AnyMetatypeType>(srcInstanceType).getInstanceType ();
515
+ }
478
516
479
- auto layout = instanceType .getExistentialLayout ();
517
+ auto layout = destInstanceType .getExistentialLayout ();
480
518
481
519
// Look up witness tables for the protocols that need them and get
482
520
// references to the ObjC Protocol* values for the objc protocols.
@@ -486,7 +524,7 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
486
524
bool hasClassConstraint = layout.requiresClass ;
487
525
bool hasClassConstraintByProtocol = false ;
488
526
489
- assert (! layout.superclass && " Subclass existentials not supported yet " );
527
+ bool hasSuperclassConstraint = bool ( layout.superclass );
490
528
491
529
for (auto protoTy : layout.getProtocols ()) {
492
530
auto *protoDecl = protoTy->getDecl ();
@@ -532,10 +570,34 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
532
570
auto schema = IGF.getTypeInfo (destType).getSchema ();
533
571
resultType = schema[0 ].getScalarType ();
534
572
}
535
- // We only need to check the class constraint for metatype casts where
536
- // no protocol conformance indirectly requires the constraint for us.
537
- bool checkClassConstraint =
538
- (bool )metatypeKind && hasClassConstraint && !hasClassConstraintByProtocol;
573
+
574
+ // The source of a scalar cast is statically known to be a class or a
575
+ // metatype, so we only have to check the class constraint in two cases:
576
+ //
577
+ // 1) The destination type has an explicit superclass constraint that is
578
+ // more derived than what the source type is known to be.
579
+ //
580
+ // 2) We are casting between metatypes, in which case the source might
581
+ // be a non-class metatype.
582
+ bool checkClassConstraint = false ;
583
+ if ((bool )metatypeKind &&
584
+ hasClassConstraint &&
585
+ !hasClassConstraintByProtocol &&
586
+ !srcInstanceType->mayHaveSuperclass ())
587
+ checkClassConstraint = true ;
588
+
589
+ // If the source has an equal or more derived superclass constraint than
590
+ // the destination, we can elide the superclass check.
591
+ //
592
+ // Note that destInstanceType is always an existential type, so calling
593
+ // getSuperclass() returns the superclass constraint of the existential,
594
+ // not the superclass of some concrete class.
595
+ bool checkSuperclassConstraint =
596
+ hasSuperclassConstraint &&
597
+ !destInstanceType->getSuperclass ()->isExactSuperclassOf (srcInstanceType);
598
+
599
+ if (checkSuperclassConstraint)
600
+ checkClassConstraint = true ;
539
601
540
602
llvm::Value *resultValue = value;
541
603
@@ -671,8 +733,11 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
671
733
}
672
734
673
735
// Look up witness tables for the protocols that need them.
674
- auto fn = emitExistentialScalarCastFn (IGF.IGM , witnessTableProtos.size (),
675
- mode, checkClassConstraint);
736
+ auto fn = emitExistentialScalarCastFn (IGF.IGM ,
737
+ witnessTableProtos.size (),
738
+ mode,
739
+ checkClassConstraint,
740
+ checkSuperclassConstraint);
676
741
677
742
llvm::SmallVector<llvm::Value *, 4 > args;
678
743
@@ -681,6 +746,10 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
681
746
args.push_back (resultValue);
682
747
683
748
args.push_back (metadataValue);
749
+
750
+ if (checkSuperclassConstraint)
751
+ args.push_back (IGF.emitTypeMetadataRef (CanType (layout.superclass )));
752
+
684
753
for (auto proto : witnessTableProtos)
685
754
args.push_back (proto);
686
755
0 commit comments