Skip to content

5.7 fix cast ownership #42378

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 6 commits into from
Apr 19, 2022
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
4 changes: 4 additions & 0 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -1248,6 +1248,10 @@ class alignas(1 << TypeAlignInBits) TypeBase
/// Whether this is the AnyObject type.
bool isAnyObject();

/// Return true if this type is potentially an AnyObject existential after
/// substitution.
bool isPotentiallyAnyObject();

/// Whether this is an existential composition containing
/// Error.
bool isExistentialWithError();
Expand Down
22 changes: 17 additions & 5 deletions include/swift/SIL/DynamicCasts.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ class SILType;
enum class CastConsumptionKind : uint8_t;
struct SILDynamicCastInst;

/// Returns true if the ownership of all references in this type are preserved
/// (without unbalanced retains or releases) during dynamic casting.
bool doesCastPreserveOwnershipForTypes(SILModule &module, CanType sourceType,
CanType targetType);

enum class DynamicCastFeasibility {
/// The cast will always succeed.
WillSucceed,
Expand Down Expand Up @@ -82,10 +87,17 @@ bool emitSuccessfulIndirectUnconditionalCast(
bool emitSuccessfulIndirectUnconditionalCast(SILBuilder &B, SILLocation loc,
SILDynamicCastInst dynamicCast);

/// Can the given cast be performed by the scalar checked-cast instructions in
/// the current SIL stage, or do we need to use the indirect instructions?
bool canSILUseScalarCheckedCastInstructions(SILModule &M,
CanType sourceType,
CanType targetType);

/// Can the given cast be performed by the scalar checked-cast
/// instructions, or does we need to use the indirect instructions?
bool canUseScalarCheckedCastInstructions(SILModule &M,
CanType sourceType,CanType targetType);
/// instructions at IRGen, or do we need to use the indirect instructions?
bool canIRGenUseScalarCheckedCastInstructions(SILModule &M,
CanType sourceType,
CanType targetType);

/// Carry out the operations required for an indirect conditional cast
/// using a scalar cast operation.
Expand Down Expand Up @@ -435,8 +447,8 @@ struct SILDynamicCastInst {
llvm_unreachable("covered switch");
}

bool canUseScalarCheckedCastInstructions() const {
return swift::canUseScalarCheckedCastInstructions(
bool canSILUseScalarCheckedCastInstructions() const {
return swift::canSILUseScalarCheckedCastInstructions(
getModule(), getSourceFormalType(), getTargetFormalType());
}
};
Expand Down
4 changes: 2 additions & 2 deletions include/swift/SIL/OwnershipUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,9 @@ class ForwardingOperand {
return use->getOwnershipConstraint();
}

bool isDirectlyForwarding() const {
bool preservesOwnership() const {
auto &mixin = *OwnershipForwardingMixin::get(use->getUser());
return mixin.isDirectlyForwarding();
return mixin.preservesOwnership();
}

ValueOwnershipKind getForwardingOwnershipKind() const;
Expand Down
50 changes: 19 additions & 31 deletions include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -1112,39 +1112,31 @@ class CopyLikeInstruction {
/// initializes the kind field on our object is run before our constructor runs.
class OwnershipForwardingMixin {
ValueOwnershipKind ownershipKind;
bool directlyForwards;
bool preservesOwnershipFlag;

protected:
OwnershipForwardingMixin(SILInstructionKind kind,
ValueOwnershipKind ownershipKind,
bool isDirectlyForwarding = true)
: ownershipKind(ownershipKind), directlyForwards(isDirectlyForwarding) {
bool preservesOwnership = true)
: ownershipKind(ownershipKind),
preservesOwnershipFlag(preservesOwnership) {
assert(isa(kind) && "Invalid subclass?!");
assert(ownershipKind && "invalid forwarding ownership");
assert((directlyForwards || ownershipKind != OwnershipKind::Guaranteed) &&
assert((preservesOwnershipFlag
|| ownershipKind != OwnershipKind::Guaranteed) &&
"Non directly forwarding instructions can not forward guaranteed "
"ownership");
}

public:
/// If an instruction is directly forwarding, then any operand op whose
/// ownership it forwards into a result r must have the property that op and r
/// are "rc identical". This means that they are representing the same set of
/// underlying lifetimes (plural b/c of aggregates).
/// A forwarding instruction preserved ownership if it has a
/// dynamically non-trivial result in which all references are forwarded from
/// the operand.
///
/// An instruction that is not directly forwarding, can not have guaranteed
/// ownership since without direct forwarding, there isn't necessarily any
/// connection in between the operand's lifetime and the value's lifetime.
///
/// An example of this is checked_cast_br where when performing the following:
///
/// __SwiftValue(AnyHashable(Klass())) to OtherKlass()
///
/// we will look through the __SwiftValue(AnyHashable(X)) any just cast Klass
/// to OtherKlass. This means that the result argument would no longer be
/// rc-identical to the operand and default case and thus we can not propagate
/// forward any form of guaranteed ownership.
bool isDirectlyForwarding() const { return directlyForwards; }
/// A cast can only forward guaranteed values if it preserves ownership. Such
/// casts cannot release any references within their operand's value and
/// cannot retain any references owned by their result.
bool preservesOwnership() const { return preservesOwnershipFlag; }

/// Forwarding ownership is determined by the forwarding instruction's
/// constant ownership attribute. If forwarding ownership is owned, then the
Expand All @@ -1160,7 +1152,7 @@ class OwnershipForwardingMixin {
return ownershipKind;
}
void setForwardingOwnershipKind(ValueOwnershipKind newKind) {
assert((isDirectlyForwarding() || newKind != OwnershipKind::Guaranteed) &&
assert((preservesOwnership() || newKind != OwnershipKind::Guaranteed) &&
"Non directly forwarding instructions can not forward guaranteed "
"ownership");
ownershipKind = newKind;
Expand Down Expand Up @@ -8083,9 +8075,9 @@ class OwnershipForwardingTermInst : public TermInst,
OwnershipForwardingTermInst(SILInstructionKind kind,
SILDebugLocation debugLoc,
ValueOwnershipKind ownershipKind,
bool isDirectlyForwarding = true)
bool preservesOwnership = true)
: TermInst(kind, debugLoc),
OwnershipForwardingMixin(kind, ownershipKind, isDirectlyForwarding) {
OwnershipForwardingMixin(kind, ownershipKind, preservesOwnership) {
assert(classof(kind));
}

Expand Down Expand Up @@ -9062,16 +9054,12 @@ class CheckedCastBranchInst final
SILBasicBlock *SuccessBB, SILBasicBlock *FailureBB,
ProfileCounter Target1Count,
ProfileCounter Target2Count,
ValueOwnershipKind forwardingOwnershipKind)
ValueOwnershipKind forwardingOwnershipKind,
bool preservesOwnership)
: UnaryInstructionWithTypeDependentOperandsBase(
DebugLoc, Operand, TypeDependentOperands, SuccessBB, FailureBB,
Target1Count, Target2Count, forwardingOwnershipKind,
// We are always directly forwarding unless we are casting an
// AnyObject. This is b/c an AnyObject could contain a boxed
// AnyObject(Class()) that we unwrap as part of the cast. In such a
// case, we would return a different value and potentially end the
// lifetime of the operand value.
!Operand->getType().isAnyObject()),
preservesOwnership),
DestLoweredTy(DestLoweredTy), DestFormalTy(DestFormalTy),
IsExact(IsExact) {}

Expand Down
4 changes: 2 additions & 2 deletions include/swift/SIL/TypeSubstCloner.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,8 @@ class TypeSubstCloner : public SILClonerWithScopes<ImplClass> {
auto FalseCount = inst->getFalseBBCount();

// Try to use the scalar cast instruction.
if (canUseScalarCheckedCastInstructions(B.getModule(),
sourceType, targetType)) {
if (canSILUseScalarCheckedCastInstructions(B.getModule(),
sourceType, targetType)) {
emitIndirectConditionalCastWithScalar(
B, SwiftMod, loc, inst->getConsumptionKind(), src, sourceType, dest,
targetType, succBB, failBB, TrueCount, FalseCount);
Expand Down
14 changes: 14 additions & 0 deletions lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,20 @@ bool TypeBase::isAnyObject() {
return canTy.getExistentialLayout().isAnyObject();
}

// Distinguish between class-bound types that might be AnyObject vs other
// class-bound types. Only types that are potentially AnyObject might have a
// transparent runtime type wrapper like __SwiftValue. This must look through
// all optional types because dynamic casting sees through them.
bool TypeBase::isPotentiallyAnyObject() {
Type unwrappedTy = lookThroughAllOptionalTypes();
if (auto archetype = unwrappedTy->getAs<ArchetypeType>()) {
// Does archetype have any requirements that contradict AnyObject?
// 'T : AnyObject' requires a layout constraint, not a conformance.
return archetype->getConformsTo().empty() && !archetype->getSuperclass();
}
return unwrappedTy->isAnyObject();
}

bool ExistentialLayout::isErrorExistential() const {
auto protocols = getProtocols();
return (!hasExplicitAnyObject &&
Expand Down
9 changes: 6 additions & 3 deletions lib/SIL/IR/SILInstructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "swift/Basic/AssertImplements.h"
#include "swift/Basic/Unicode.h"
#include "swift/Basic/type_traits.h"
#include "swift/SIL/DynamicCasts.h"
#include "swift/SIL/FormalLinkage.h"
#include "swift/SIL/Projection.h"
#include "swift/SIL/SILBuilder.h"
Expand Down Expand Up @@ -2283,16 +2284,18 @@ CheckedCastBranchInst *CheckedCastBranchInst::create(
SILBasicBlock *FailureBB, SILFunction &F,
ProfileCounter Target1Count, ProfileCounter Target2Count,
ValueOwnershipKind forwardingOwnershipKind) {
SILModule &Mod = F.getModule();
SILModule &module = F.getModule();
bool preservesOwnership = doesCastPreserveOwnershipForTypes(
module, Operand->getType().getASTType(), DestFormalTy);
SmallVector<SILValue, 8> TypeDependentOperands;
collectTypeDependentOperands(TypeDependentOperands, F, DestFormalTy);
unsigned size =
totalSizeToAlloc<swift::Operand>(1 + TypeDependentOperands.size());
void *Buffer = Mod.allocateInst(size, alignof(CheckedCastBranchInst));
void *Buffer = module.allocateInst(size, alignof(CheckedCastBranchInst));
return ::new (Buffer) CheckedCastBranchInst(
DebugLoc, IsExact, Operand, TypeDependentOperands, DestLoweredTy,
DestFormalTy, SuccessBB, FailureBB, Target1Count, Target2Count,
forwardingOwnershipKind);
forwardingOwnershipKind, preservesOwnership);
}

MetatypeInst *MetatypeInst::create(SILDebugLocation Loc, SILType Ty,
Expand Down
Loading