Skip to content

Fix casts involving the 'Self' type #28401

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 2 commits into from
Dec 3, 2019
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
74 changes: 43 additions & 31 deletions lib/IRGen/GenCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ getDynamicCastArguments(IRGenFunction &IGF,

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

// If true, the target class is not known at compile time because it is a
// class-bounded archetype or the dynamic Self type.
bool nonSpecificClass = false;

// Get the best known type information about the destination type.
ClassDecl *destClass = nullptr;
if (auto archetypeTy = toType.getAs<ArchetypeType>()) {
if (auto archetypeTy = dyn_cast<ArchetypeType>(toType)) {
nonSpecificClass = true;
if (auto superclassTy = archetypeTy->getSuperclass())
destClass = superclassTy->getClassOrBoundGenericClass();
} else if (auto selfTy = dyn_cast<DynamicSelfType>(toType)) {
nonSpecificClass = true;
destClass = selfTy->getSelfType()->getClassOrBoundGenericClass();
} else {
destClass = toType.getClassOrBoundGenericClass();
assert(destClass != nullptr);
Expand All @@ -187,7 +195,7 @@ llvm::Value *irgen::emitClassDowncast(IRGenFunction &IGF, llvm::Value *from,
// If the destination type is known to have a Swift-compatible
// implementation, use the most specific entrypoint.
if (destClass && destClass->hasKnownSwiftImplementation()) {
metadataRef = IGF.emitTypeMetadataRef(toType.getASTType());
metadataRef = IGF.emitTypeMetadataRef(toType);

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

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

switch (mode) {
case CheckedCastMode::Unconditional:
Expand Down Expand Up @@ -249,7 +257,7 @@ llvm::Value *irgen::emitClassDowncast(IRGenFunction &IGF, llvm::Value *from,
call->setCallingConv(cc);
call->setDoesNotThrow();

llvm::Type *subTy = IGF.getTypeInfo(toType).getStorageType();
llvm::Type *subTy = IGF.getTypeInfoForUnlowered(toType).getStorageType();
return IGF.Builder.CreateBitCast(call, subTy);
}

Expand Down Expand Up @@ -871,12 +879,14 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
/// isn't exposed.
void irgen::emitScalarCheckedCast(IRGenFunction &IGF,
Explosion &value,
SILType sourceType,
SILType targetType,
SILType sourceLoweredType,
CanType sourceFormalType,
SILType targetLoweredType,
CanType targetFormalType,
CheckedCastMode mode,
Explosion &out) {
assert(sourceType.isObject());
assert(targetType.isObject());
assert(sourceLoweredType.isObject());
assert(targetLoweredType.isObject());

llvm::BasicBlock *nilCheckBB = nullptr;
llvm::BasicBlock *nilMergeBB = nullptr;
Expand Down Expand Up @@ -906,22 +916,23 @@ void irgen::emitScalarCheckedCast(IRGenFunction &IGF,
}
};

if (auto sourceOptObjectType = sourceType.getOptionalObjectType()) {
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
// for the particular case of an optional class-reference or metatype
// value.
Explosion optValue;
auto someDecl = IGF.IGM.Context.getOptionalSomeDecl();
emitProjectLoadableEnum(IGF, sourceType, value, someDecl, optValue);
emitProjectLoadableEnum(IGF, sourceLoweredType, value, someDecl, optValue);

assert(value.empty());
value = std::move(optValue);
sourceType = sourceOptObjectType;
sourceLoweredType = sourceOptObjectType;
sourceFormalType = sourceFormalType.getOptionalObjectType();

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

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

// Casts to existential metatypes.
if (auto existential = targetType.getAs<ExistentialMetatypeType>()) {
emitScalarExistentialDowncast(IGF, metatypeVal, sourceType, targetType,
mode, existential->getRepresentation(),
if (auto existential = targetLoweredType.getAs<ExistentialMetatypeType>()) {
emitScalarExistentialDowncast(IGF, metatypeVal, sourceLoweredType,
targetLoweredType, mode,
existential->getRepresentation(),
out);
return;

// Casts to concrete metatypes.
} else if (auto destMetaType = targetType.getAs<MetatypeType>()) {
} else if (auto destMetaType = targetLoweredType.getAs<MetatypeType>()) {
emitMetatypeDowncast(IGF, metatypeVal, destMetaType, mode, out);
return;
}

// Otherwise, this is a metatype-to-object cast.
assert(targetType.isAnyClassReferenceType());
assert(targetLoweredType.isAnyClassReferenceType());

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

SILType anyObjectType =
SILType::getPrimitiveObjectType(
IGF.IGM.Context.getAnyObjectType());
sourceFormalType = IGF.IGM.Context.getAnyObjectType();
sourceLoweredType = SILType::getPrimitiveObjectType(sourceFormalType);

// Continue, pretending that the source value was an (optional) value.
Explosion newValue;
newValue.add(object);
value = std::move(newValue);
sourceType = anyObjectType;
}

assert(!targetType.is<AnyMetatypeType>() &&
assert(!targetLoweredType.is<AnyMetatypeType>() &&
"scalar cast of class reference to metatype is unimplemented");

// If the source type is existential, project out the class pointer.
//
// TODO: if we're casting to an existential type, don't throw away the
// protocol conformance information we already have.
llvm::Value *instance;
if (sourceType.isExistentialType()) {
instance = emitClassExistentialProjection(IGF, value, sourceType,
if (sourceLoweredType.isExistentialType()) {
instance = emitClassExistentialProjection(IGF, value, sourceLoweredType,
CanArchetypeType());
} else {
instance = value.claimNext();
}

if (targetType.isExistentialType()) {
if (targetFormalType.isExistentialType()) {
Explosion outRes;
emitScalarExistentialDowncast(IGF, instance, sourceType, targetType,
mode, /*not a metatype*/ None, outRes);
emitScalarExistentialDowncast(IGF, instance, sourceLoweredType,
targetLoweredType, mode,
/*not a metatype*/ None, outRes);
returnNilCheckedResult(IGF.Builder, outRes);
return;
}

Explosion outRes;
llvm::Value *result = emitClassDowncast(IGF, instance, targetType, mode);
llvm::Value *result = emitClassDowncast(IGF, instance, targetFormalType,
mode);
out.add(result);
}
9 changes: 5 additions & 4 deletions lib/IRGen/GenCast.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,17 @@ namespace irgen {
CheckedCastMode mode);

void emitScalarCheckedCast(IRGenFunction &IGF, Explosion &value,
SILType valueType, SILType loweredTargetType,
SILType sourceLoweredType,
CanType sourceFormalType,
SILType targetLoweredType,
CanType targetFormalType,
CheckedCastMode mode, Explosion &out);

/// Convert a class object to the given destination type,
/// using a runtime-checked cast.
///
/// FIXME: toType should be an AST CanType.
llvm::Value *emitClassDowncast(IRGenFunction &IGF,
llvm::Value *from,
SILType toType,
CanType toType,
CheckedCastMode mode);

/// A result of a cast generation function.
Expand Down
13 changes: 9 additions & 4 deletions lib/IRGen/IRGenSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4834,7 +4834,11 @@ void IRGenSILFunction::visitUnconditionalCheckedCastInst(
swift::UnconditionalCheckedCastInst *i) {
Explosion value = getLoweredExplosion(i->getOperand());
Explosion ex;
emitScalarCheckedCast(*this, value, i->getOperand()->getType(), i->getType(),
emitScalarCheckedCast(*this, value,
i->getSourceLoweredType(),
i->getSourceFormalType(),
i->getTargetLoweredType(),
i->getTargetFormalType(),
CheckedCastMode::Unconditional, ex);
setLoweredExplosion(i, ex);
}
Expand Down Expand Up @@ -5038,20 +5042,21 @@ void IRGenSILFunction::visitCheckedCastValueBranchInst(

void IRGenSILFunction::visitCheckedCastBranchInst(
swift::CheckedCastBranchInst *i) {
SILType destTy = i->getTargetLoweredType();
FailableCastResult castResult;
Explosion ex;
if (i->isExact()) {
auto operand = i->getOperand();
Explosion source = getLoweredExplosion(operand);
castResult = emitClassIdenticalCast(*this, source.claimNext(),
i->getSourceLoweredType(),
destTy);
i->getTargetLoweredType());
} else {
Explosion value = getLoweredExplosion(i->getOperand());
emitScalarCheckedCast(*this, value,
i->getSourceLoweredType(),
i->getSourceFormalType(),
i->getTargetLoweredType(),
i->getTargetFormalType(),
CheckedCastMode::Conditional, ex);
auto val = ex.claimNext();
castResult.casted = val;
Expand All @@ -5065,7 +5070,7 @@ void IRGenSILFunction::visitCheckedCastBranchInst(


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

Expand Down
10 changes: 3 additions & 7 deletions lib/SILOptimizer/Transforms/DeadCodeElimination.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,22 +400,18 @@ void DCE::propagateLiveness(SILInstruction *I) {
case TermKind::SwitchEnumInst:
case TermKind::SwitchEnumAddrInst:
case TermKind::DynamicMethodBranchInst:
case TermKind::CheckedCastBranchInst:
case TermKind::CheckedCastValueBranchInst:
markValueLive(I->getOperand(0));
return;

case TermKind::CheckedCastBranchInst:
case TermKind::CheckedCastValueBranchInst:
case TermKind::CheckedCastAddrBranchInst:
case TermKind::TryApplyInst:
case TermKind::SwitchValueInst:
case TermKind::YieldInst:
for (auto &O : I->getAllOperands())
markValueLive(O.get());
return;

case TermKind::CheckedCastAddrBranchInst:
markValueLive(I->getOperand(0));
markValueLive(I->getOperand(1));
return;
}
llvm_unreachable("corrupt instruction!");
}
Expand Down
13 changes: 5 additions & 8 deletions lib/SILOptimizer/Utils/CastOptimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,9 @@ CastOptimizer::simplifyCheckedCastBranchInst(CheckedCastBranchInst *Inst) {
// Check if we can statically predict the outcome of the cast.
auto Feasibility =
dynamicCast.classifyFeasibility(false /*allow whole module*/);
if (Feasibility == DynamicCastFeasibility::MaySucceed) {
return nullptr;
}

SILBuilderWithScope Builder(Inst, builderContext);
if (Feasibility == DynamicCastFeasibility::WillFail) {
Expand All @@ -1009,6 +1012,8 @@ CastOptimizer::simplifyCheckedCastBranchInst(CheckedCastBranchInst *Inst) {
return NewI;
}

assert(Feasibility == DynamicCastFeasibility::WillSucceed);

bool ResultNotUsed = SuccessBB->getArgument(0)->use_empty();
SILValue CastedValue;
if (Op->getType() != TargetLoweredType) {
Expand All @@ -1022,14 +1027,6 @@ CastOptimizer::simplifyCheckedCastBranchInst(CheckedCastBranchInst *Inst) {
llvm_unreachable(
"Bridged casts cannot be expressed by checked_cast_br yet");
} else {
// If the cast may succeed or fail and can't be turned into a bridging
// call, then let it be.
if (Feasibility == DynamicCastFeasibility::MaySucceed) {
return nullptr;
}

assert(Feasibility == DynamicCastFeasibility::WillSucceed);

// Replace by unconditional_cast, followed by a branch.
// The unconditional_cast can be skipped, if the result of a cast
// is not used afterwards.
Expand Down
Loading