Skip to content

Commit a308915

Browse files
committed
[IRGen] Emit null check before swift_conformsToProtocol for nullable metatypes
rdar://149882902 swift_conformsToProtocol does not properly handle nullptr values, which can currently be passed if the source type is an optional metatype. This change adds emission of a null check before calling the runtime function in these cases.
1 parent 083cb11 commit a308915

File tree

3 files changed

+67
-12
lines changed

3 files changed

+67
-12
lines changed

lib/IRGen/GenCast.cpp

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -544,8 +544,8 @@ llvm::Value *irgen::emitMetatypeToAnyObjectDowncast(IRGenFunction &IGF,
544544
/// Emit a checked cast to a protocol or protocol composition.
545545
void irgen::emitScalarExistentialDowncast(
546546
IRGenFunction &IGF, llvm::Value *value, SILType srcType, SILType destType,
547-
CheckedCastMode mode, std::optional<MetatypeRepresentation> metatypeKind,
548-
Explosion &ex) {
547+
CheckedCastMode mode, bool sourceWrappedInOptional,
548+
std::optional<MetatypeRepresentation> metatypeKind, Explosion &ex) {
549549
auto srcInstanceType = srcType.getASTType();
550550
auto destInstanceType = destType.getASTType();
551551
while (auto metatypeType = dyn_cast<ExistentialMetatypeType>(
@@ -800,7 +800,40 @@ void irgen::emitScalarExistentialDowncast(
800800
for (auto proto : witnessTableProtos)
801801
args.push_back(proto);
802802

803-
auto valueAndWitnessTables = IGF.Builder.CreateCall(fn, args);
803+
llvm::BasicBlock *nilCheckedCont = nullptr;
804+
llvm::BasicBlock *nilBB = nullptr;
805+
llvm::BasicBlock *nonNilBB = nullptr;
806+
if (sourceWrappedInOptional) {
807+
nilBB = IGF.createBasicBlock("is-nil");
808+
nonNilBB = IGF.createBasicBlock("is-non-nil");
809+
nilCheckedCont = IGF.createBasicBlock("nil-checked-cont");
810+
811+
auto isNotNil = IGF.Builder.CreateICmpNE(
812+
metadataValue, llvm::ConstantPointerNull::get(
813+
cast<llvm::PointerType>(IGF.IGM.Int8PtrTy)));
814+
815+
IGF.Builder.CreateCondBr(isNotNil, nonNilBB, nilBB);
816+
IGF.Builder.emitBlock(nilBB);
817+
IGF.Builder.CreateBr(nilCheckedCont);
818+
IGF.Builder.emitBlock(nonNilBB);
819+
}
820+
821+
llvm::Value *valueAndWitnessTables = IGF.Builder.CreateCall(fn, args);
822+
823+
if (nilCheckedCont) {
824+
IGF.Builder.CreateBr(nilCheckedCont);
825+
IGF.Builder.emitBlock(nilCheckedCont);
826+
auto *returnTy = valueAndWitnessTables->getType();
827+
auto failureVal = llvm::Constant::getNullValue(returnTy);
828+
auto phi = IGF.Builder.CreatePHI(returnTy, 2);
829+
phi->addIncoming(valueAndWitnessTables, nonNilBB);
830+
phi->addIncoming(failureVal, nilBB);
831+
ex.add(phi);
832+
valueAndWitnessTables = ex.claimNext();
833+
ex.reset();
834+
}
835+
836+
assert(ex.empty());
804837

805838
resultValue = IGF.Builder.CreateExtractValue(valueAndWitnessTables, 0);
806839
if (resultValue->getType() != resultType)
@@ -945,10 +978,9 @@ void irgen::emitScalarCheckedCast(IRGenFunction &IGF,
945978

946979
// Casts to existential metatypes.
947980
if (auto existential = targetLoweredType.getAs<ExistentialMetatypeType>()) {
948-
emitScalarExistentialDowncast(IGF, metatypeVal, sourceLoweredType,
949-
targetLoweredType, mode,
950-
existential->getRepresentation(),
951-
out);
981+
emitScalarExistentialDowncast(
982+
IGF, metatypeVal, sourceLoweredType, targetLoweredType, mode,
983+
sourceWrappedInOptional, existential->getRepresentation(), out);
952984
return;
953985

954986
// Casts to concrete metatypes.
@@ -1023,9 +1055,10 @@ void irgen::emitScalarCheckedCast(IRGenFunction &IGF,
10231055

10241056
if (targetFormalType.isExistentialType()) {
10251057
Explosion outRes;
1026-
emitScalarExistentialDowncast(
1027-
IGF, instance, sourceLoweredType, targetLoweredType, mode,
1028-
/*not a metatype*/ std::nullopt, outRes);
1058+
emitScalarExistentialDowncast(IGF, instance, sourceLoweredType,
1059+
targetLoweredType, mode,
1060+
/*sourceWrappedInOptional*/ false,
1061+
/*not a metatype*/ std::nullopt, outRes);
10291062
returnNilCheckedResult(IGF.Builder, outRes);
10301063
return;
10311064
}

lib/IRGen/GenCast.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ namespace irgen {
9999
/// not, the cast is done as a class instance cast.
100100
void emitScalarExistentialDowncast(
101101
IRGenFunction &IGF, llvm::Value *orig, SILType srcType, SILType destType,
102-
CheckedCastMode mode, std::optional<MetatypeRepresentation> metatypeKind,
103-
Explosion &ex);
102+
CheckedCastMode mode, bool sourceWrappedInOptional,
103+
std::optional<MetatypeRepresentation> metatypeKind, Explosion &ex);
104104

105105
/// Emit a checked cast from a metatype to AnyObject.
106106
llvm::Value *emitMetatypeToAnyObjectDowncast(IRGenFunction &IGF,

test/IRGen/casts.sil

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ struct NotClass {}
1212
class A {}
1313
class B: A {}
1414
final class F: A {}
15+
protocol Q {}
1516

1617
sil_vtable A {}
1718
sil_vtable B {}
@@ -435,3 +436,24 @@ bb2:
435436
bb3(%11 : $Optional<C & PAnyObject>):
436437
return %11 : $Optional<C & PAnyObject>
437438
}
439+
440+
// CHECK-LABEL: define{{.*}} @checked_cast_optional_metatype
441+
// CHECK: [[COND:%.*]] = icmp ne ptr {{%.*}}, null
442+
// CHECK-NEXT: br i1 [[COND]], label %is-non-nil, label %is-nil
443+
// CHECK: nil-checked-cont:
444+
// CHECK: %4 = phi { ptr, ptr } [ {{%.*}}, %is-non-nil ], [ zeroinitializer, %is-nil ]
445+
sil @checked_cast_optional_metatype : $@convention(thin) (Optional<@thick any Any.Type>) -> Optional<@thick any Q.Type> {
446+
bb0(%0 : $Optional<@thick any Any.Type>):
447+
checked_cast_br Optional<any Any.Type> in %0 to any Q.Type, bb1, bb2
448+
449+
bb1(%3 : $@thick any Q.Type):
450+
%4 = enum $Optional<@thick any Q.Type>, #Optional.some!enumelt, %3
451+
br bb3(%4)
452+
453+
bb2:
454+
%6 = enum $Optional<@thick any Q.Type>, #Optional.none!enumelt
455+
br bb3(%6)
456+
457+
bb3(%8 : $Optional<@thick any Q.Type>):
458+
return %8
459+
}

0 commit comments

Comments
 (0)