Skip to content

Commit 454495f

Browse files
committed
IRGen: Subclass existential scalar downcasts
This handles the case where the left hand side of the cast is known to be class-like, and the right hand side is known at compile time to be a protocol composition type. Note that this change results in a small optimization -- a checked cast of a metatype known to be a class metatype to a class-constrained existential metatype no longer has to emit an explicit check that the source is class-constrained. Fully dynamic casts are coming up next.
1 parent a2c1548 commit 454495f

File tree

3 files changed

+250
-24
lines changed

3 files changed

+250
-24
lines changed

lib/IRGen/GenCast.cpp

Lines changed: 88 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -291,21 +291,34 @@ llvm::Value *irgen::emitReferenceToObjCProtocol(IRGenFunction &IGF,
291291
/// Emit a helper function to look up \c numProtocols witness tables given
292292
/// a value and a type metadata reference.
293293
///
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...)
295302
/// The function's output type is (value, witnessTable...)
296303
///
297304
/// 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+
302313
// Build the function name.
303314
llvm::SmallString<32> name;
304315
{
305316
llvm::raw_svector_ostream os(name);
306317
os << "dynamic_cast_existential_";
307318
os << numProtocols;
308-
if (checkClassConstraint)
319+
if (checkSuperclassConstraint)
320+
os << "_superclass";
321+
else if (checkClassConstraint)
309322
os << "_class";
310323
switch (mode) {
311324
case CheckedCastMode::Unconditional:
@@ -329,6 +342,8 @@ static llvm::Function *emitExistentialScalarCastFn(IRGenModule &IGM,
329342
argTys.push_back(IGM.Int8PtrTy);
330343
argTys.push_back(IGM.TypeMetadataPtrTy);
331344
returnTys.push_back(IGM.Int8PtrTy);
345+
if (checkSuperclassConstraint)
346+
argTys.push_back(IGM.TypeMetadataPtrTy);
332347
for (unsigned i = 0; i < numProtocols; ++i) {
333348
argTys.push_back(IGM.ProtocolDescriptorPtrTy);
334349
returnTys.push_back(IGM.WitnessTablePtrTy);
@@ -355,7 +370,26 @@ static llvm::Function *emitExistentialScalarCastFn(IRGenModule &IGM,
355370
rets.add(value);
356371

357372
// 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) {
359393
auto isClass = IGF.Builder.CreateCall(IGM.getIsClassTypeFn(), ref);
360394
auto contBB = IGF.createBasicBlock("cont");
361395
IGF.Builder.CreateCondBr(isClass, contBB, failBB);
@@ -374,7 +408,7 @@ static llvm::Function *emitExistentialScalarCastFn(IRGenModule &IGM,
374408
IGF.Builder.emitBlock(contBB);
375409
rets.add(witness);
376410
}
377-
411+
378412
// If we succeeded, return the witnesses.
379413
IGF.emitScalarReturn(returnTy, rets);
380414

@@ -472,11 +506,15 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
472506
CheckedCastMode mode,
473507
Optional<MetatypeRepresentation> metatypeKind,
474508
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+
}
478516

479-
auto layout = instanceType.getExistentialLayout();
517+
auto layout = destInstanceType.getExistentialLayout();
480518

481519
// Look up witness tables for the protocols that need them and get
482520
// references to the ObjC Protocol* values for the objc protocols.
@@ -486,7 +524,7 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
486524
bool hasClassConstraint = layout.requiresClass;
487525
bool hasClassConstraintByProtocol = false;
488526

489-
assert(!layout.superclass && "Subclass existentials not supported yet");
527+
bool hasSuperclassConstraint = bool(layout.superclass);
490528

491529
for (auto protoTy : layout.getProtocols()) {
492530
auto *protoDecl = protoTy->getDecl();
@@ -532,10 +570,34 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
532570
auto schema = IGF.getTypeInfo(destType).getSchema();
533571
resultType = schema[0].getScalarType();
534572
}
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;
539601

540602
llvm::Value *resultValue = value;
541603

@@ -671,8 +733,11 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
671733
}
672734

673735
// 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);
676741

677742
llvm::SmallVector<llvm::Value *, 4> args;
678743

@@ -681,6 +746,10 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
681746
args.push_back(resultValue);
682747

683748
args.push_back(metadataValue);
749+
750+
if (checkSuperclassConstraint)
751+
args.push_back(IGF.emitTypeMetadataRef(CanType(layout.superclass)));
752+
684753
for (auto proto : witnessTableProtos)
685754
args.push_back(proto);
686755

test/IRGen/metatype_casts.sil

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,24 @@ end:
8888
}
8989

9090
// CHECK-LABEL: define{{( protected)?}} swiftcc %swift.type* @checked_cast_to_anyobject_type
91-
// CHECK: [[METATYPE:%.*]] = call %swift.type* @_T014metatype_casts9SomeClassCMa()
92-
// CHECK: [[T0:%.*]] = bitcast %swift.type* [[METATYPE]] to i8*
93-
// CHECK: [[CAST:%.*]] = call { i8* } @dynamic_cast_existential_0_class_unconditional(i8* [[T0]], %swift.type* [[METATYPE]])
91+
// CHECK: [[T0:%.*]] = bitcast %swift.type* %0 to i8*
92+
// CHECK: [[CAST:%.*]] = call { i8* } @dynamic_cast_existential_0_class_unconditional(i8* [[T0]], %swift.type* %0)
9493
// CHECK: [[RESULT0:%.*]] = extractvalue { i8* } [[CAST]], 0
9594
// CHECK: [[RESULT:%.*]] = bitcast i8* [[RESULT0]] to %swift.type*
9695
// CHECK: ret %swift.type* [[RESULT]]
97-
sil @checked_cast_to_anyobject_type : $@convention(thin) () -> @thick AnyObject.Type {
96+
sil @checked_cast_to_anyobject_type : $@convention(thin) (@thick Any.Type) -> @thick AnyObject.Type {
97+
entry(%0 : $@thick Any.Type):
98+
%2 = unconditional_checked_cast %0 : $@thick Any.Type to $@thick AnyObject.Type
99+
return %2 : $@thick AnyObject.Type
100+
}
101+
102+
// Trivial case -- we know the source is a class metatype, so there's nothing
103+
// to check.
104+
105+
// CHECK-LABEL: define{{( protected)?}} swiftcc %swift.type* @checked_cast_class_to_anyobject_type
106+
// CHECK: [[METATYPE:%.*]] = call %swift.type* @_T014metatype_casts9SomeClassCMa()
107+
// CHECK: ret %swift.type* [[METATYPE]]
108+
sil @checked_cast_class_to_anyobject_type : $@convention(thin) () -> @thick AnyObject.Type {
98109
entry:
99110
%1 = metatype $@thick SomeClass.Type
100111
%2 = unconditional_checked_cast %1 : $@thick SomeClass.Type to $@thick AnyObject.Type

test/IRGen/subclass_existentials.sil

Lines changed: 147 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,14 @@ import Builtin
66
import Swift
77

88
class C {}
9+
class G<T> {}
10+
911
protocol P {}
1012

1113
class D : C, P {}
14+
class E<T> : G<T>, P {}
15+
16+
protocol R {}
1217

1318
// Make sure we use native reference counting when the existential has a Swift
1419
// class bound.
@@ -61,11 +66,152 @@ bb0:
6166
return %4 : $()
6267
}
6368

69+
// CHECK-LABEL: define{{( protected)?}} swiftcc { %T21subclass_existentials1CC*, i8** } @eraseToExistential(%T21subclass_existentials1DC*)
70+
// CHECK: [[INSTANCE:%.*]] = bitcast %T21subclass_existentials1DC* %0 to %T21subclass_existentials1CC*
71+
// CHECK-NEXT: [[RESULT1:%.*]] = insertvalue { %T21subclass_existentials1CC*, i8** } undef, %T21subclass_existentials1CC* [[INSTANCE]], 0
72+
// CHECK-NEXT: [[RESULT2:%.*]] = insertvalue { %T21subclass_existentials1CC*, i8** } [[RESULT1]], i8** @_T021subclass_existentials1DCAA1PAAWP, 1
73+
// CHECK-NEXT: ret { %T21subclass_existentials1CC*, i8** } [[RESULT2]]
6474
sil @eraseToExistential : $@convention(thin) (@owned D) -> @owned C & P {
6575
bb0(%0 : $D):
6676
%2 = init_existential_ref %0 : $D : $D, $C & P
6777
return %2 : $C & P
6878
}
6979

80+
// CHECK-LABEL: define{{( protected)?}} swiftcc { %T21subclass_existentials1GC*, i8** } @eraseToExistentialWithGeneric(%T21subclass_existentials1EC*)
81+
// CHECK: [[INSTANCE:%.*]] = bitcast %T21subclass_existentials1EC* %0 to %T21subclass_existentials1GC*
82+
// CHECK-NEXT: [[RESULT1:%.*]] = insertvalue { %T21subclass_existentials1GC*, i8** } undef, %T21subclass_existentials1GC* [[INSTANCE]], 0
83+
// CHECK-NEXT: [[RESULT2:%.*]] = insertvalue { %T21subclass_existentials1GC*, i8** } [[RESULT1]], i8** @_T021subclass_existentials1ECyxGAA1PAAlWP, 1
84+
// CHECK-NEXT: ret { %T21subclass_existentials1GC*, i8** } [[RESULT2]]
85+
sil @eraseToExistentialWithGeneric : $@convention(thin) <τ_0_0> (@owned E<τ_0_0>) -> @owned G<τ_0_0> & P {
86+
bb0(%0 : $E<τ_0_0>):
87+
%2 = init_existential_ref %0 : $E<τ_0_0> : $E<τ_0_0>, $G<τ_0_0> & P
88+
return %2 : $G<τ_0_0> & P
89+
}
90+
91+
// CHECK-LABEL: define{{( protected)?}} swiftcc void @checkExistentialDowncast(%T21subclass_existentials1CC*, %T21subclass_existentials1CC*, i8**)
92+
sil @checkExistentialDowncast : $@convention(thin) (@owned C, @owned C & P) -> () {
93+
bb0(%0 : $C, %1 : $C & P):
94+
95+
// CHECK: [[METATYPE_PTR:%.*]] = bitcast %T21subclass_existentials1CC* %0 to %swift.type**
96+
// CHECK-NEXT: [[METATYPE:%.*]] = load %swift.type*, %swift.type** [[METATYPE_PTR]], align 8
97+
// CHECK-NEXT: [[VALUE:%.*]] = bitcast %T21subclass_existentials1CC* %0 to i8*
98+
// CHECK-NEXT: [[SUPERCLASS:%.*]] = call %swift.type* @_T021subclass_existentials1DCMa()
99+
// CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_superclass_unconditional(i8* [[VALUE]], %swift.type* [[METATYPE]], %swift.type* [[SUPERCLASS]], %swift.protocol* @_T021subclass_existentials1RMp)
100+
// CHECK-NEXT: [[VALUE_ADDR:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0
101+
// CHECK-NEXT: [[VALUE:%.*]] = bitcast i8* [[VALUE_ADDR]] to %T21subclass_existentials1DC*
102+
// CHECK-NEXT: [[WTABLE:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 1
103+
%2 = unconditional_checked_cast %0 : $C to $D & R
104+
105+
// CHECK-NEXT: call void bitcast (void (%swift.refcounted*)* @swift_rt_swift_release to void (%T21subclass_existentials1DC*)*)(%T21subclass_existentials1DC* [[VALUE]])
106+
destroy_value %2 : $D & R
107+
108+
// CHECK-NEXT: [[VALUE:%.*]] = bitcast %T21subclass_existentials1CC* %1 to i8*
109+
// CHECK-NEXT: [[CLASS_ADDR:%.*]] = bitcast %swift.type* [[SUPERCLASS]] to i8*
110+
// CHECK-NEXT: [[RESULT:%.*]] = call i8* @swift_dynamicCastClassUnconditional(i8* [[VALUE]], i8* [[CLASS_ADDR]])
111+
// CHECK-NEXT: [[VALUE:%.*]] = bitcast i8* [[RESULT]] to %T21subclass_existentials1DC*
112+
%3 = unconditional_checked_cast %1 : $C & P to $D
113+
114+
// CHECK-NEXT: call void bitcast (void (%swift.refcounted*)* @swift_rt_swift_release to void (%T21subclass_existentials1DC*)*)(%T21subclass_existentials1DC* [[VALUE]])
115+
destroy_value %3 : $D
116+
117+
// CHECK-NEXT: ret void
118+
%result = tuple ()
119+
return %result : $()
120+
}
121+
122+
// CHECK-LABEL: define private { i8*, i8** } @dynamic_cast_existential_1_superclass_unconditional(i8*, %swift.type*, %swift.type*, %swift.protocol*)
123+
// CHECK: entry:
124+
// CHECK-NEXT: [[RESULT:%.*]] = call %swift.type* @swift_dynamicCastMetatype(%swift.type* %1, %swift.type* %2)
125+
// CHECK-NEXT: [[IS_SUBCLASS:%.*]] = icmp ne %swift.type* [[RESULT]], null
126+
// CHECK-NEXT: br i1 [[IS_SUBCLASS]], label %cont, label %fail
127+
128+
// CHECK: cont:
129+
// CHECK-NEXT: [[WTABLE:%.*]] = call i8** @swift_conformsToProtocol(%swift.type* %1, %swift.protocol* %3)
130+
// CHECK-NEXT: [[IS_NOT_CONFORMING:%.*]] = icmp eq i8** [[WTABLE]], null
131+
// CHECK-NEXT: br i1 [[IS_NOT_CONFORMING]], label %fail, label %cont1
132+
133+
// CHECK: cont1:
134+
// CHECK-NEXT: [[RESULT1:%.*]] = insertvalue { i8*, i8** } undef, i8* %0, 0
135+
// CHECK-NEXT: [[RESULT2:%.*]] = insertvalue { i8*, i8** } [[RESULT1]], i8** [[WTABLE]], 1
136+
// CHECK-NEXT: ret { i8*, i8** } [[RESULT2]]
137+
138+
// CHECK: fail:
139+
// CHECK-NEXT: call void @llvm.trap()
140+
// CHECK-NEXT: unreachable
141+
142+
// CHECK-LABEL: define{{( protected)?}} swiftcc void @checkExistentialSameClassDowncast(%T21subclass_existentials1CC*)
143+
sil @checkExistentialSameClassDowncast : $@convention(thin) (@owned C) -> () {
144+
bb0(%0 : $C):
145+
146+
// CHECK: [[METATYPE_PTR:%.*]] = bitcast %T21subclass_existentials1CC* %0 to %swift.type**
147+
// CHECK-NEXT: [[METATYPE:%.*]] = load %swift.type*, %swift.type** [[METATYPE_PTR]], align 8
148+
// CHECK-NEXT: [[VALUE:%.*]] = bitcast %T21subclass_existentials1CC* %0 to i8*
149+
// CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[VALUE]], %swift.type* [[METATYPE]], %swift.protocol* @_T021subclass_existentials1PMp)
150+
// CHECK-NEXT: [[VALUE_ADDR:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0
151+
// CHECK-NEXT: [[VALUE:%.*]] = bitcast i8* [[VALUE_ADDR]] to %T21subclass_existentials1CC*
152+
// CHECK-NEXT: [[WTABLE:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 1
153+
%2 = unconditional_checked_cast %0 : $C to $C & P
154+
155+
// CHECK-NEXT: call void bitcast (void (%swift.refcounted*)* @swift_rt_swift_release to void (%T21subclass_existentials1CC*)*)(%T21subclass_existentials1CC* [[VALUE]])
156+
destroy_value %2 : $C & P
157+
158+
// CHECK-NEXT: ret void
159+
%result = tuple ()
160+
return %result : $()
161+
}
162+
163+
// CHECK-LABEL: define private { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8*, %swift.type*, %swift.protocol*)
164+
// CHECK: entry:
165+
// CHECK-NEXT: [[WTABLE:%.*]] = call i8** @swift_conformsToProtocol(%swift.type* %1, %swift.protocol* %2)
166+
// CHECK-NEXT: [[IS_NOT_CONFORMING:%.*]] = icmp eq i8** [[WTABLE]], null
167+
// CHECK-NEXT: br i1 [[IS_NOT_CONFORMING]], label %fail, label %cont
168+
169+
// CHECK: cont:
170+
// CHECK-NEXT: [[RESULT1:%.*]] = insertvalue { i8*, i8** } undef, i8* %0, 0
171+
// CHECK-NEXT: [[RESULT2:%.*]] = insertvalue { i8*, i8** } [[RESULT1]], i8** [[WTABLE]], 1
172+
// CHECK-NEXT: ret { i8*, i8** } [[RESULT2]]
173+
174+
// CHECK: fail:
175+
// CHECK-NEXT: call void @llvm.trap()
176+
// CHECK-NEXT: unreachable
177+
178+
// CHECK-LABEL: define{{( protected)?}} swiftcc void @checkExistentialMetatypeDowncast(%swift.type*, %swift.type*, i8**)
179+
sil @checkExistentialMetatypeDowncast : $@convention(thin) (@owned @thick C.Type, @owned @thick (C & P).Type) -> () {
180+
bb0(%0 : $@thick C.Type, %1 : $@thick (C & P).Type):
181+
182+
// CHECK: [[METATYPE:%.*]] = bitcast %swift.type* %0 to i8*
183+
// CHECK-NEXT: [[SUPERCLASS:%.*]] = call %swift.type* @_T021subclass_existentials1DCMa()
184+
// CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_superclass_unconditional(i8* [[METATYPE]], %swift.type* %0, %swift.type* [[SUPERCLASS]], %swift.protocol* @_T021subclass_existentials1RMp)
185+
// CHECK-NEXT: [[VALUE_ADDR:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0
186+
// CHECK-NEXT: [[VALUE:%.*]] = bitcast i8* [[VALUE_ADDR]] to %swift.type*
187+
// CHECK-NEXT: [[WTABLE:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 1
188+
%2 = unconditional_checked_cast %0 : $@thick C.Type to $@thick (D & R).Type
189+
190+
// CHECK-NEXT: [[RESULT:%.*]] = call %swift.type* @swift_dynamicCastMetatypeUnconditional(%swift.type* %1, %swift.type* [[SUPERCLASS]])
191+
%3 = unconditional_checked_cast %1 : $@thick (C & P).Type to $@thick D.Type
192+
193+
// CHECK-NEXT: ret void
194+
%result = tuple ()
195+
return %result : $()
196+
}
197+
198+
// CHECK-LABEL: define{{( protected)?}} swiftcc void @checkExistentialSameClassMetatypeDowncast(%swift.type*)
199+
sil @checkExistentialSameClassMetatypeDowncast : $@convention(thin) (@owned @thick C.Type) -> () {
200+
bb0(%0 : $@thick C.Type):
201+
202+
// CHECK: [[METATYPE:%.*]] = bitcast %swift.type* %0 to i8*
203+
// CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[METATYPE]], %swift.type* %0, %swift.protocol* @_T021subclass_existentials1PMp)
204+
// CHECK-NEXT: [[VALUE_ADDR:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0
205+
// CHECK-NEXT: [[VALUE:%.*]] = bitcast i8* [[VALUE_ADDR]] to %swift.type*
206+
// CHECK-NEXT: [[WTABLE:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 1
207+
%2 = unconditional_checked_cast %0 : $@thick C.Type to $@thick (C & P).Type
208+
209+
// CHECK-NEXT: ret void
210+
%result = tuple ()
211+
return %result : $()
212+
}
213+
70214
sil_vtable C {}
71-
sil_vtable D {}
215+
sil_vtable D {}
216+
sil_vtable G {}
217+
sil_vtable E {}

0 commit comments

Comments
 (0)