Skip to content

Commit c8da158

Browse files
committed
IRGen: Correctly use formal type for class downcasts
Fixes <https://bugs.swift.org/browse/SR-11818>, <rdar://problem/57369535>.
1 parent efe4883 commit c8da158

File tree

5 files changed

+227
-39
lines changed

5 files changed

+227
-39
lines changed

lib/IRGen/GenCast.cpp

Lines changed: 43 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ getDynamicCastArguments(IRGenFunction &IGF,
164164

165165
/// Emit a checked unconditional downcast of a class value.
166166
llvm::Value *irgen::emitClassDowncast(IRGenFunction &IGF, llvm::Value *from,
167-
SILType toType, CheckedCastMode mode) {
167+
CanType toType, CheckedCastMode mode) {
168168
// Emit the value we're casting from.
169169
if (from->getType() != IGF.IGM.Int8PtrTy)
170170
from = IGF.Builder.CreateBitOrPointerCast(from, IGF.IGM.Int8PtrTy);
@@ -174,11 +174,19 @@ llvm::Value *irgen::emitClassDowncast(IRGenFunction &IGF, llvm::Value *from,
174174
llvm::Value *metadataRef;
175175
llvm::Constant *castFn;
176176

177+
// If true, the target class is not known at compile time because it is a
178+
// class-bounded archetype or the dynamic Self type.
179+
bool nonSpecificClass = false;
180+
177181
// Get the best known type information about the destination type.
178182
ClassDecl *destClass = nullptr;
179-
if (auto archetypeTy = toType.getAs<ArchetypeType>()) {
183+
if (auto archetypeTy = dyn_cast<ArchetypeType>(toType)) {
184+
nonSpecificClass = true;
180185
if (auto superclassTy = archetypeTy->getSuperclass())
181186
destClass = superclassTy->getClassOrBoundGenericClass();
187+
} else if (auto selfTy = dyn_cast<DynamicSelfType>(toType)) {
188+
nonSpecificClass = true;
189+
destClass = selfTy->getSelfType()->getClassOrBoundGenericClass();
182190
} else {
183191
destClass = toType.getClassOrBoundGenericClass();
184192
assert(destClass != nullptr);
@@ -187,7 +195,7 @@ llvm::Value *irgen::emitClassDowncast(IRGenFunction &IGF, llvm::Value *from,
187195
// If the destination type is known to have a Swift-compatible
188196
// implementation, use the most specific entrypoint.
189197
if (destClass && destClass->hasKnownSwiftImplementation()) {
190-
metadataRef = IGF.emitTypeMetadataRef(toType.getASTType());
198+
metadataRef = IGF.emitTypeMetadataRef(toType);
191199

192200
switch (mode) {
193201
case CheckedCastMode::Unconditional:
@@ -200,9 +208,9 @@ llvm::Value *irgen::emitClassDowncast(IRGenFunction &IGF, llvm::Value *from,
200208

201209
// If the destination type is a CF type or a non-specific
202210
// class-bounded archetype, use the most general cast entrypoint.
203-
} else if (toType.is<ArchetypeType>() ||
211+
} else if (nonSpecificClass ||
204212
destClass->getForeignClassKind()==ClassDecl::ForeignKind::CFType) {
205-
metadataRef = IGF.emitTypeMetadataRef(toType.getASTType());
213+
metadataRef = IGF.emitTypeMetadataRef(toType);
206214

207215
switch (mode) {
208216
case CheckedCastMode::Unconditional:
@@ -249,7 +257,7 @@ llvm::Value *irgen::emitClassDowncast(IRGenFunction &IGF, llvm::Value *from,
249257
call->setCallingConv(cc);
250258
call->setDoesNotThrow();
251259

252-
llvm::Type *subTy = IGF.getTypeInfo(toType).getStorageType();
260+
llvm::Type *subTy = IGF.getTypeInfoForUnlowered(toType).getStorageType();
253261
return IGF.Builder.CreateBitCast(call, subTy);
254262
}
255263

@@ -871,12 +879,14 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
871879
/// isn't exposed.
872880
void irgen::emitScalarCheckedCast(IRGenFunction &IGF,
873881
Explosion &value,
874-
SILType sourceType,
875-
SILType targetType,
882+
SILType sourceLoweredType,
883+
CanType sourceFormalType,
884+
SILType targetLoweredType,
885+
CanType targetFormalType,
876886
CheckedCastMode mode,
877887
Explosion &out) {
878-
assert(sourceType.isObject());
879-
assert(targetType.isObject());
888+
assert(sourceLoweredType.isObject());
889+
assert(targetLoweredType.isObject());
880890

881891
llvm::BasicBlock *nilCheckBB = nullptr;
882892
llvm::BasicBlock *nilMergeBB = nullptr;
@@ -906,22 +916,23 @@ void irgen::emitScalarCheckedCast(IRGenFunction &IGF,
906916
}
907917
};
908918

909-
if (auto sourceOptObjectType = sourceType.getOptionalObjectType()) {
919+
if (auto sourceOptObjectType = sourceLoweredType.getOptionalObjectType()) {
910920
// Translate the value from an enum representation to a possibly-null
911921
// representation. Note that we assume that this projection is safe
912922
// for the particular case of an optional class-reference or metatype
913923
// value.
914924
Explosion optValue;
915925
auto someDecl = IGF.IGM.Context.getOptionalSomeDecl();
916-
emitProjectLoadableEnum(IGF, sourceType, value, someDecl, optValue);
926+
emitProjectLoadableEnum(IGF, sourceLoweredType, value, someDecl, optValue);
917927

918928
assert(value.empty());
919929
value = std::move(optValue);
920-
sourceType = sourceOptObjectType;
930+
sourceLoweredType = sourceOptObjectType;
931+
sourceFormalType = sourceFormalType.getOptionalObjectType();
921932

922933
// We need a null-check because the runtime function can't handle null in
923934
// some of the cases.
924-
if (targetType.isExistentialType()) {
935+
if (targetLoweredType.isExistentialType()) {
925936
auto &Builder = IGF.Builder;
926937
auto val = value.getAll()[0];
927938
auto isNotNil = Builder.CreateICmpNE(
@@ -938,7 +949,7 @@ void irgen::emitScalarCheckedCast(IRGenFunction &IGF,
938949

939950
// If the source value is a metatype, either do a metatype-to-metatype
940951
// cast or cast it to an object instance and continue.
941-
if (auto sourceMetatype = sourceType.getAs<AnyMetatypeType>()) {
952+
if (auto sourceMetatype = sourceLoweredType.getAs<AnyMetatypeType>()) {
942953
llvm::Value *metatypeVal = nullptr;
943954
if (sourceMetatype->getRepresentation() != MetatypeRepresentation::Thin)
944955
metatypeVal = value.claimNext();
@@ -952,60 +963,61 @@ void irgen::emitScalarCheckedCast(IRGenFunction &IGF,
952963
SmallVector<ProtocolDecl*, 1> protocols;
953964

954965
// Casts to existential metatypes.
955-
if (auto existential = targetType.getAs<ExistentialMetatypeType>()) {
956-
emitScalarExistentialDowncast(IGF, metatypeVal, sourceType, targetType,
957-
mode, existential->getRepresentation(),
966+
if (auto existential = targetLoweredType.getAs<ExistentialMetatypeType>()) {
967+
emitScalarExistentialDowncast(IGF, metatypeVal, sourceLoweredType,
968+
targetLoweredType, mode,
969+
existential->getRepresentation(),
958970
out);
959971
return;
960972

961973
// Casts to concrete metatypes.
962-
} else if (auto destMetaType = targetType.getAs<MetatypeType>()) {
974+
} else if (auto destMetaType = targetLoweredType.getAs<MetatypeType>()) {
963975
emitMetatypeDowncast(IGF, metatypeVal, destMetaType, mode, out);
964976
return;
965977
}
966978

967979
// Otherwise, this is a metatype-to-object cast.
968-
assert(targetType.isAnyClassReferenceType());
980+
assert(targetLoweredType.isAnyClassReferenceType());
969981

970982
// Convert the metatype value to AnyObject.
971983
llvm::Value *object =
972984
emitMetatypeToAnyObjectDowncast(IGF, metatypeVal, sourceMetatype, mode);
973985

974-
SILType anyObjectType =
975-
SILType::getPrimitiveObjectType(
976-
IGF.IGM.Context.getAnyObjectType());
986+
sourceFormalType = IGF.IGM.Context.getAnyObjectType();
987+
sourceLoweredType = SILType::getPrimitiveObjectType(sourceFormalType);
977988

978989
// Continue, pretending that the source value was an (optional) value.
979990
Explosion newValue;
980991
newValue.add(object);
981992
value = std::move(newValue);
982-
sourceType = anyObjectType;
983993
}
984994

985-
assert(!targetType.is<AnyMetatypeType>() &&
995+
assert(!targetLoweredType.is<AnyMetatypeType>() &&
986996
"scalar cast of class reference to metatype is unimplemented");
987997

988998
// If the source type is existential, project out the class pointer.
989999
//
9901000
// TODO: if we're casting to an existential type, don't throw away the
9911001
// protocol conformance information we already have.
9921002
llvm::Value *instance;
993-
if (sourceType.isExistentialType()) {
994-
instance = emitClassExistentialProjection(IGF, value, sourceType,
1003+
if (sourceLoweredType.isExistentialType()) {
1004+
instance = emitClassExistentialProjection(IGF, value, sourceLoweredType,
9951005
CanArchetypeType());
9961006
} else {
9971007
instance = value.claimNext();
9981008
}
9991009

1000-
if (targetType.isExistentialType()) {
1010+
if (targetFormalType.isExistentialType()) {
10011011
Explosion outRes;
1002-
emitScalarExistentialDowncast(IGF, instance, sourceType, targetType,
1003-
mode, /*not a metatype*/ None, outRes);
1012+
emitScalarExistentialDowncast(IGF, instance, sourceLoweredType,
1013+
targetLoweredType, mode,
1014+
/*not a metatype*/ None, outRes);
10041015
returnNilCheckedResult(IGF.Builder, outRes);
10051016
return;
10061017
}
10071018

10081019
Explosion outRes;
1009-
llvm::Value *result = emitClassDowncast(IGF, instance, targetType, mode);
1020+
llvm::Value *result = emitClassDowncast(IGF, instance, targetFormalType,
1021+
mode);
10101022
out.add(result);
10111023
}

lib/IRGen/GenCast.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,17 @@ namespace irgen {
4848
CheckedCastMode mode);
4949

5050
void emitScalarCheckedCast(IRGenFunction &IGF, Explosion &value,
51-
SILType valueType, SILType loweredTargetType,
51+
SILType sourceLoweredType,
52+
CanType sourceFormalType,
53+
SILType targetLoweredType,
54+
CanType targetFormalType,
5255
CheckedCastMode mode, Explosion &out);
5356

5457
/// Convert a class object to the given destination type,
5558
/// using a runtime-checked cast.
56-
///
57-
/// FIXME: toType should be an AST CanType.
5859
llvm::Value *emitClassDowncast(IRGenFunction &IGF,
5960
llvm::Value *from,
60-
SILType toType,
61+
CanType toType,
6162
CheckedCastMode mode);
6263

6364
/// A result of a cast generation function.

lib/IRGen/IRGenSIL.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4834,7 +4834,11 @@ void IRGenSILFunction::visitUnconditionalCheckedCastInst(
48344834
swift::UnconditionalCheckedCastInst *i) {
48354835
Explosion value = getLoweredExplosion(i->getOperand());
48364836
Explosion ex;
4837-
emitScalarCheckedCast(*this, value, i->getOperand()->getType(), i->getType(),
4837+
emitScalarCheckedCast(*this, value,
4838+
i->getSourceLoweredType(),
4839+
i->getSourceFormalType(),
4840+
i->getTargetLoweredType(),
4841+
i->getTargetFormalType(),
48384842
CheckedCastMode::Unconditional, ex);
48394843
setLoweredExplosion(i, ex);
48404844
}
@@ -5038,20 +5042,21 @@ void IRGenSILFunction::visitCheckedCastValueBranchInst(
50385042

50395043
void IRGenSILFunction::visitCheckedCastBranchInst(
50405044
swift::CheckedCastBranchInst *i) {
5041-
SILType destTy = i->getTargetLoweredType();
50425045
FailableCastResult castResult;
50435046
Explosion ex;
50445047
if (i->isExact()) {
50455048
auto operand = i->getOperand();
50465049
Explosion source = getLoweredExplosion(operand);
50475050
castResult = emitClassIdenticalCast(*this, source.claimNext(),
50485051
i->getSourceLoweredType(),
5049-
destTy);
5052+
i->getTargetLoweredType());
50505053
} else {
50515054
Explosion value = getLoweredExplosion(i->getOperand());
50525055
emitScalarCheckedCast(*this, value,
50535056
i->getSourceLoweredType(),
5057+
i->getSourceFormalType(),
50545058
i->getTargetLoweredType(),
5059+
i->getTargetFormalType(),
50555060
CheckedCastMode::Conditional, ex);
50565061
auto val = ex.claimNext();
50575062
castResult.casted = val;
@@ -5065,7 +5070,7 @@ void IRGenSILFunction::visitCheckedCastBranchInst(
50655070

50665071

50675072
auto &successBB = getLoweredBB(i->getSuccessBB());
5068-
llvm::Type *toTy = IGM.getTypeInfo(destTy).getStorageType();
5073+
llvm::Type *toTy = IGM.getTypeInfo(i->getTargetLoweredType()).getStorageType();
50695074
if (toTy->isPointerTy())
50705075
castResult.casted = Builder.CreateBitCast(castResult.casted, toTy);
50715076

test/IRGen/dynamic_self_cast.swift

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// RUN: %target-swift-frontend -emit-ir -disable-objc-interop %s | %FileCheck %s
2+
3+
// Note: -disable-objc-interop is used to give consistent results on Darwin
4+
// and Linux, avoiding differences like %swift.refcounted -vs- %objc_object,
5+
// etc.
6+
7+
public class SelfCasts {
8+
// CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc %T17dynamic_self_cast9SelfCastsC* @"$s17dynamic_self_cast9SelfCastsC02toD0yACXDACFZ"(%T17dynamic_self_cast9SelfCastsC*, %swift.type* swiftself)
9+
// CHECK: [[ARG:%.*]] = bitcast %T17dynamic_self_cast9SelfCastsC* %0 to i8*
10+
// CHECK: [[METATYPE:%.*]] = bitcast %swift.type* %1 to i8*
11+
// CHECK: call i8* @swift_dynamicCastClassUnconditional(i8* [[ARG]], i8* [[METATYPE]], i8* null, i32 0, i32 0)
12+
// CHECK: ret
13+
public static func toSelf(_ s: SelfCasts) -> Self {
14+
return s as! Self
15+
}
16+
17+
// CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc %T17dynamic_self_cast9SelfCastsC* @"$s17dynamic_self_cast9SelfCastsC09genericToD0yACXDxlFZ"(%swift.opaque* noalias nocapture, %swift.type* %T, %swift.type* swiftself)
18+
// CHECK: call i1 @swift_dynamicCast(%swift.opaque* {{%.*}}, %swift.opaque* {{%.*}}, %swift.type* %T, %swift.type* %1, {{.*}})
19+
// CHECK: ret
20+
public static func genericToSelf<T>(_ s: T) -> Self {
21+
return s as! Self
22+
}
23+
24+
// CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc %T17dynamic_self_cast9SelfCastsC* @"$s17dynamic_self_cast9SelfCastsC014classGenericToD0yACXDxRlzClFZ"(%swift.refcounted*, %swift.type* %T, %swift.type* swiftself)
25+
// CHECK: [[ARG:%.*]] = bitcast %swift.refcounted* %0 to i8*
26+
// CHECK: [[METATYPE:%.*]] = bitcast %swift.type* %1 to i8*
27+
// CHECK: call i8* @swift_dynamicCastClassUnconditional(i8* [[ARG]], i8* [[METATYPE]], i8* null, i32 0, i32 0)
28+
// CHECK: ret
29+
public static func classGenericToSelf<T : AnyObject>(_ s: T) -> Self {
30+
return s as! Self
31+
}
32+
33+
// CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc void @"$s17dynamic_self_cast9SelfCastsC011genericFromD0xylFZ"(%swift.opaque* noalias nocapture sret, %swift.type* %T, %swift.type* swiftself)
34+
// CHECK: call i1 @swift_dynamicCast(%swift.opaque* {{%.*}}, %swift.opaque* {{%.*}}, %swift.type* %1, %swift.type* %T, {{.*}})
35+
// CHECK: ret
36+
public static func genericFromSelf<T>() -> T {
37+
let s = Self()
38+
return s as! T
39+
}
40+
41+
// CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc %swift.refcounted* @"$s17dynamic_self_cast9SelfCastsC016classGenericFromD0xyRlzClFZ"(%swift.type* %T, %swift.type* swiftself)
42+
// CHECK: call i1 @swift_dynamicCast(%swift.opaque* {{%.*}}, %swift.opaque* {{%.*}}, %swift.type* %0, %swift.type* %T, {{.*}})
43+
// CHECK: ret
44+
public static func classGenericFromSelf<T : AnyObject>() -> T {
45+
let s = Self()
46+
return s as! T
47+
}
48+
49+
// CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc {{i32|i64}} @"$s17dynamic_self_cast9SelfCastsC02toD11ConditionalyACXDSgACFZ"(%T17dynamic_self_cast9SelfCastsC*, %swift.type* swiftself)
50+
// CHECK: [[ARG:%.*]] = bitcast %T17dynamic_self_cast9SelfCastsC* %0 to i8*
51+
// CHECK: [[METATYPE:%.*]] = bitcast %swift.type* %1 to i8*
52+
// CHECK: call i8* @swift_dynamicCastClass(i8* [[ARG]], i8* [[METATYPE]])
53+
// CHECK: ret
54+
public static func toSelfConditional(_ s: SelfCasts) -> Self? {
55+
return s as? Self
56+
}
57+
58+
// CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc {{i32|i64}} @"$s17dynamic_self_cast9SelfCastsC09genericToD11ConditionalyACXDSgxlFZ"(%swift.opaque* noalias nocapture, %swift.type* %T, %swift.type* swiftself)
59+
// CHECK: call i1 @swift_dynamicCast(%swift.opaque* {{%.*}}, %swift.opaque* {{%.*}}, %swift.type* %T, %swift.type* %1, {{.*}})
60+
// CHECK: ret
61+
public static func genericToSelfConditional<T>(_ s: T) -> Self? {
62+
return s as? Self
63+
}
64+
65+
// CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc {{i32|i64}} @"$s17dynamic_self_cast9SelfCastsC014classGenericToD11ConditionalyACXDSgxRlzClFZ"(%swift.refcounted*, %swift.type* %T, %swift.type* swiftself)
66+
// CHECK: [[ARG:%.*]] = bitcast %swift.refcounted* %0 to i8*
67+
// CHECK: [[METATYPE:%.*]] = bitcast %swift.type* %1 to i8*
68+
// CHECK: call i8* @swift_dynamicCastClass(i8* [[ARG]], i8* [[METATYPE]])
69+
// CHECK: ret
70+
public static func classGenericToSelfConditional<T : AnyObject>(_ s: T) -> Self? {
71+
return s as? Self
72+
}
73+
74+
// CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc void @"$s17dynamic_self_cast9SelfCastsC011genericFromD11ConditionalxSgylFZ"(%TSq* noalias nocapture sret, %swift.type* %T, %swift.type* swiftself)
75+
// CHECK: call i1 @swift_dynamicCast(%swift.opaque* {{%.*}}, %swift.opaque* {{%.*}}, %swift.type* %1, %swift.type* %T, {{.*}})
76+
// CHECK: ret
77+
public static func genericFromSelfConditional<T>() -> T? {
78+
let s = Self()
79+
return s as? T
80+
}
81+
82+
// CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc {{i32|i64}} @"$s17dynamic_self_cast9SelfCastsC016classGenericFromD11ConditionalxSgyRlzClFZ"(%swift.type* %T, %swift.type* swiftself)
83+
// CHECK: call i1 @swift_dynamicCast(%swift.opaque* {{%.*}}, %swift.opaque* {{%.*}}, %swift.type* %0, %swift.type* %T, {{.*}})
84+
// CHECK: ret
85+
public static func classGenericFromSelfConditional<T : AnyObject>() -> T? {
86+
let s = Self()
87+
return s as? T
88+
}
89+
90+
public required init() {}
91+
}

0 commit comments

Comments
 (0)