Skip to content

[IRGen] Check for nil in final class cast opt. #65636

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 26 additions & 7 deletions lib/IRGen/GenCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,8 @@ void irgen::emitScalarCheckedCast(IRGenFunction &IGF,
}
};

bool sourceWrappedInOptional = false;

if (auto sourceOptObjectType = sourceLoweredType.getOptionalObjectType()) {
// Translate the value from an enum representation to a possibly-null
// representation. Note that we assume that this projection is safe
Expand All @@ -898,6 +900,7 @@ void irgen::emitScalarCheckedCast(IRGenFunction &IGF,
value = std::move(optValue);
sourceLoweredType = sourceOptObjectType;
sourceFormalType = sourceFormalType.getOptionalObjectType();
sourceWrappedInOptional = true;

// We need a null-check because the runtime function can't handle null in
// some of the cases.
Expand Down Expand Up @@ -1021,9 +1024,12 @@ void irgen::emitScalarCheckedCast(IRGenFunction &IGF,
return;
}

if (llvm::Value *fastResult = emitFastClassCastIfPossible(IGF, instance,
sourceFormalType, targetFormalType)) {
out.add(fastResult);
if (llvm::Value *fastResult = emitFastClassCastIfPossible(
IGF, instance, sourceFormalType, targetFormalType,
sourceWrappedInOptional, nilCheckBB, nilMergeBB)) {
Explosion fastExplosion;
fastExplosion.add(fastResult);
returnNilCheckedResult(IGF.Builder, fastExplosion);
return;
}

Expand All @@ -1039,10 +1045,10 @@ void irgen::emitScalarCheckedCast(IRGenFunction &IGF,
/// It also avoids a call to the metadata accessor of the class (which calls
/// `swift_getInitializedObjCClass`). For comparing the metadata pointers it's
/// not required that the metadata is fully initialized.
llvm::Value *irgen::emitFastClassCastIfPossible(IRGenFunction &IGF,
llvm::Value *instance,
CanType sourceFormalType,
CanType targetFormalType) {
llvm::Value *irgen::emitFastClassCastIfPossible(
IRGenFunction &IGF, llvm::Value *instance, CanType sourceFormalType,
CanType targetFormalType, bool sourceWrappedInOptional,
llvm::BasicBlock *&nilCheckBB, llvm::BasicBlock *&nilMergeBB) {
if (!doesCastPreserveOwnershipForTypes(IGF.IGM.getSILModule(),
sourceFormalType, targetFormalType)) {
return nullptr;
Expand Down Expand Up @@ -1074,6 +1080,19 @@ llvm::Value *irgen::emitFastClassCastIfPossible(IRGenFunction &IGF,
if (toClass->checkAncestry() & forbidden)
return nullptr;

// If the source was originally wrapped in an Optional, check it for nil now.
if (sourceWrappedInOptional) {
auto isNotNil = IGF.Builder.CreateICmpNE(
instance, llvm::ConstantPointerNull::get(
cast<llvm::PointerType>(instance->getType())));
auto *isNotNilContBB = llvm::BasicBlock::Create(IGF.IGM.getLLVMContext());
nilMergeBB = llvm::BasicBlock::Create(IGF.IGM.getLLVMContext());
nilCheckBB = IGF.Builder.GetInsertBlock();
IGF.Builder.CreateCondBr(isNotNil, isNotNilContBB, nilMergeBB);

IGF.Builder.emitBlock(isNotNilContBB);
}

// Get the metadata pointer of the destination class type.
llvm::Value *destMetadata = IGF.IGM.getAddrOfTypeMetadata(targetFormalType);
if (IGF.IGM.IRGen.Opts.LazyInitializeClassMetadata) {
Expand Down
9 changes: 5 additions & 4 deletions lib/IRGen/GenCast.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

namespace llvm {
class Value;
class BasicBlock;
}

namespace swift {
Expand Down Expand Up @@ -56,10 +57,10 @@ namespace irgen {
GenericSignature fnSig,
Explosion &out);

llvm::Value *emitFastClassCastIfPossible(IRGenFunction &IGF,
llvm::Value *instance,
CanType sourceFormalType,
CanType targetFormalType);
llvm::Value *emitFastClassCastIfPossible(
IRGenFunction &IGF, llvm::Value *instance, CanType sourceFormalType,
CanType targetFormalType, bool sourceWrappedInOptional,
llvm::BasicBlock *&nilCheckBB, llvm::BasicBlock *&nilMergeBB);

/// Convert a class object to the given destination type,
/// using a runtime-checked cast.
Expand Down
24 changes: 24 additions & 0 deletions validation-test/IRGen/rdar108614878.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// RUN: %target-run-simple-swift | %FileCheck %s
// REQUIRES: executable_test

final class C {}

struct S {
@inline(never)
func g1<T>(_ componentType: T.Type) -> T? {
return nil
}
@inline(never)
func g2<T>(_ type: T.Type) -> T? {
g1(T.self) as? T
}
}

func run() -> C? {
var e = S()
let r = e.g2(C.self)
return r
}

// CHECK: nil
print(run())