Skip to content

Commit a3d7942

Browse files
Merge pull request #71958 from nate-chandler/noncopyable-bugs/20240228/1
Allow partial consumption of self in deinit.
2 parents 9200364 + 6100a1b commit a3d7942

13 files changed

+400
-78
lines changed

include/swift/SIL/AddressWalker.h

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@
3030

3131
namespace swift {
3232

33+
enum class TransitiveAddressWalkerTransitiveUseVisitation : uint8_t {
34+
OnlyUses,
35+
OnlyUser,
36+
BothUserAndUses,
37+
};
38+
3339
/// A state structure for findTransitiveUsesForAddress. Intended to be only used
3440
/// a single time. Please always use a new one for every call to
3541
/// findTransitiveUsesForAddress.
@@ -61,11 +67,16 @@ class TransitiveAddressWalker {
6167
/// understand. These cause us to return AddressUseKind::Unknown.
6268
void onError(Operand *use) {}
6369

70+
using TransitiveUseVisitation =
71+
TransitiveAddressWalkerTransitiveUseVisitation;
72+
6473
/// Customization point that causes the walker to treat a specific transitive
6574
/// use as an end point use.
6675
///
6776
/// Example: Visiting a mutable or immutable open_existential_addr.
68-
bool visitTransitiveUseAsEndPointUse(Operand *use) { return false; }
77+
TransitiveUseVisitation visitTransitiveUseAsEndPointUse(Operand *use) {
78+
return TransitiveUseVisitation::OnlyUses;
79+
}
6980

7081
void meet(AddressUseKind other) {
7182
assert(!didInvalidate);
@@ -112,8 +123,12 @@ TransitiveAddressWalker<Impl>::walk(SILValue projectedAddress) && {
112123
return callVisitUse(use);
113124
}
114125

115-
if (asImpl().visitTransitiveUseAsEndPointUse(use))
116-
return callVisitUse(use);
126+
auto visitation = asImpl().visitTransitiveUseAsEndPointUse(use);
127+
if (visitation != TransitiveUseVisitation::OnlyUses)
128+
callVisitUse(use);
129+
130+
if (visitation == TransitiveUseVisitation::OnlyUser)
131+
return;
117132

118133
for (auto *use : svi->getUses())
119134
addToWorklist(use);

include/swift/SIL/FieldSensitivePrunedLiveness.h

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "swift/Basic/Debug.h"
2525
#include "swift/Basic/FrozenMultiMap.h"
2626
#include "swift/Basic/STLExtras.h"
27+
#include "swift/SIL/ApplySite.h"
2728
#include "swift/SIL/BasicBlockDatastructures.h"
2829
#include "swift/SIL/SILFunction.h"
2930
#include "swift/SIL/SILInstruction.h"
@@ -254,9 +255,7 @@ struct TypeSubElementCount {
254255
TypeSubElementCount(SILType type, SILFunction *fn)
255256
: TypeSubElementCount(type, fn->getModule(), TypeExpansionContext(*fn)) {}
256257

257-
TypeSubElementCount(SILValue value)
258-
: TypeSubElementCount(value->getType(), *value->getModule(),
259-
TypeExpansionContext(*value->getFunction())) {}
258+
TypeSubElementCount(SILValue value);
260259

261260
operator unsigned() const { return number; }
262261

@@ -304,6 +303,39 @@ struct TypeTreeLeafTypeRange {
304303
*startEltOffset + TypeSubElementCount(projectedValue)}};
305304
}
306305

306+
/// Which bits of \p rootValue are involved in \p op.
307+
///
308+
/// This is a subset of (usually equal to) the bits of op->getType() in \p
309+
/// rootValue.
310+
static std::optional<TypeTreeLeafTypeRange> get(Operand *op,
311+
SILValue rootValue) {
312+
auto projectedValue = op->get();
313+
auto startEltOffset = SubElementOffset::compute(projectedValue, rootValue);
314+
if (!startEltOffset)
315+
return std::nullopt;
316+
317+
// A drop_deinit only consumes the deinit bit of its operand.
318+
auto *ddi = dyn_cast<DropDeinitInst>(op->getUser());
319+
if (ddi) {
320+
auto upperBound = *startEltOffset + TypeSubElementCount(projectedValue);
321+
return {{upperBound - 1, upperBound}};
322+
}
323+
324+
// Uses that borrow a value do not involve the deinit bit.
325+
//
326+
// FIXME: This shouldn't be limited to applies.
327+
unsigned deinitBitOffset = 0;
328+
if (op->get()->getType().isValueTypeWithDeinit() &&
329+
op->getOperandOwnership() == OperandOwnership::Borrow &&
330+
ApplySite::isa(op->getUser())) {
331+
deinitBitOffset = 1;
332+
}
333+
334+
return {{*startEltOffset, *startEltOffset +
335+
TypeSubElementCount(projectedValue) -
336+
deinitBitOffset}};
337+
}
338+
307339
/// Given a type \p rootType and a set of needed elements specified by the bit
308340
/// vector \p neededElements, place into \p foundContiguousTypeRanges a set of
309341
/// TypeTreeLeafTypeRanges that are associated with the bit vectors

lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,18 @@ TypeSubElementCount::TypeSubElementCount(SILType type, SILModule &mod,
104104
// our default value, so we can just return.
105105
}
106106

107+
TypeSubElementCount::TypeSubElementCount(SILValue value) : number(1) {
108+
auto whole = TypeSubElementCount(value->getType(), *value->getModule(),
109+
TypeExpansionContext(*value->getFunction()));
110+
// The value produced by a drop_deinit has one fewer subelement than that of
111+
// its type--the deinit bit is not included.
112+
if (isa<DropDeinitInst>(value)) {
113+
assert(value->getType().isValueTypeWithDeinit());
114+
whole = whole - 1;
115+
}
116+
number = whole;
117+
}
118+
107119
//===----------------------------------------------------------------------===//
108120
// MARK: SubElementNumber
109121
//===----------------------------------------------------------------------===//
@@ -201,7 +213,14 @@ SubElementOffset::computeForAddress(SILValue projectionDerivedFromRoot,
201213
projectionDerivedFromRoot = initData->getOperand();
202214
continue;
203215
}
204-
216+
217+
// A drop_deinit consumes the "self" bit at the end of its type. The offset
218+
// is still to the beginning.
219+
if (auto dd = dyn_cast<DropDeinitInst>(projectionDerivedFromRoot)) {
220+
projectionDerivedFromRoot = dd->getOperand();
221+
continue;
222+
}
223+
205224
// Look through wrappers.
206225
if (auto c2m = dyn_cast<CopyableToMoveOnlyWrapperAddrInst>(projectionDerivedFromRoot)) {
207226
projectionDerivedFromRoot = c2m->getOperand();

lib/SILGen/SILGenDestructor.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,18 @@ void SILGenFunction::emitDeallocatingMoveOnlyDestructor(DestructorDecl *dd) {
264264
dd->getDeclContext()->getSelfNominalTypeDecl(),
265265
cleanupLoc);
266266

267+
if (getASTContext().LangOpts.hasFeature(
268+
Feature::MoveOnlyPartialConsumption)) {
269+
if (auto *ddi = dyn_cast<DropDeinitInst>(selfValue)) {
270+
if (auto *mu =
271+
dyn_cast<MarkUnresolvedNonCopyableValueInst>(ddi->getOperand())) {
272+
if (auto *asi = dyn_cast<AllocStackInst>(mu->getOperand())) {
273+
B.createDeallocStack(loc, asi);
274+
}
275+
}
276+
}
277+
}
278+
267279
// Return.
268280
B.createReturn(loc, emitEmptyTuple(loc));
269281
}
@@ -506,15 +518,19 @@ void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue,
506518
void SILGenFunction::emitMoveOnlyMemberDestruction(SILValue selfValue,
507519
NominalTypeDecl *nom,
508520
CleanupLocation cleanupLoc) {
509-
// drop_deinit invalidates any user-defined struct/enum deinit
510-
// before the individual members are destroyed.
511-
selfValue = B.createDropDeinit(cleanupLoc, selfValue);
521+
if (!isa<DropDeinitInst>(selfValue)) {
522+
// drop_deinit invalidates any user-defined struct/enum deinit
523+
// before the individual members are destroyed.
524+
selfValue = B.createDropDeinit(cleanupLoc, selfValue);
525+
}
512526
if (selfValue->getType().isObject()) {
513527
// A destroy value that uses the result of a drop_deinit implicitly performs
514528
// memberwise destruction.
515529
B.emitDestroyValueOperation(cleanupLoc, selfValue);
516530
return;
517531
}
532+
// self has been stored into a temporary
533+
assert(!selfValue->getType().isObject());
518534
if (auto *structDecl = dyn_cast<StructDecl>(nom)) {
519535
for (VarDecl *vd : nom->getStoredProperties()) {
520536
const TypeLowering &ti = getTypeLowering(vd->getTypeInContext());

lib/SILGen/SILGenLValue.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3509,13 +3509,26 @@ RValue SILGenFunction::emitRValueForNonMemberVarDecl(SILLocation loc,
35093509
SILValue accessAddr = UnenforcedFormalAccess::enter(*this, loc, destAddr,
35103510
SILAccessKind::Read);
35113511

3512+
auto isEffectivelyMarkUnresolvedInst = [](auto *inst) -> bool {
3513+
if (!inst)
3514+
return false;
3515+
if (isa<MarkUnresolvedNonCopyableValueInst>(inst))
3516+
return true;
3517+
auto *ddi = dyn_cast<DropDeinitInst>(inst);
3518+
if (!ddi)
3519+
return false;
3520+
return isa<MarkUnresolvedNonCopyableValueInst>(ddi->getOperand());
3521+
};
3522+
35123523
if (accessAddr->getType().isMoveOnly() &&
3513-
!isa<MarkUnresolvedNonCopyableValueInst>(accessAddr)) {
3524+
!isEffectivelyMarkUnresolvedInst(
3525+
accessAddr->getDefiningInstruction())) {
3526+
auto kind =
3527+
MarkUnresolvedNonCopyableValueInst::CheckKind::NoConsumeOrAssign;
35143528
// When loading an rvalue, we should never need to modify the place
35153529
// we're loading from.
3516-
accessAddr = B.createMarkUnresolvedNonCopyableValueInst(
3517-
loc, accessAddr,
3518-
MarkUnresolvedNonCopyableValueInst::CheckKind::NoConsumeOrAssign);
3530+
accessAddr =
3531+
B.createMarkUnresolvedNonCopyableValueInst(loc, accessAddr, kind);
35193532
}
35203533

35213534
auto propagateRValuePastAccess = [&](RValue &&rvalue) {

lib/SILGen/SILGenProlog.cpp

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,28 +47,48 @@ SILValue SILGenFunction::emitSelfDeclForDestructor(VarDecl *selfDecl) {
4747
selfType = F.mapTypeIntoContext(selfType);
4848
SILValue selfValue = F.begin()->createFunctionArgument(selfType, selfDecl);
4949

50+
uint16_t ArgNo = 1; // Hardcoded for destructors.
51+
auto dv = SILDebugVariable(selfDecl->isLet(), ArgNo);
52+
5053
// If we have a move only type, then mark it with
5154
// mark_unresolved_non_copyable_value so we can't escape it.
52-
if (selfType.isMoveOnly()) {
53-
// For now, we do not handle move only class deinits. This is because we
54-
// need to do a bit more refactoring to handle the weird way that it deals
55-
// with ownership. But for simple move only deinits (like struct/enum), that
56-
// are owned, lets mark them as needing to be no implicit copy checked so
57-
// they cannot escape.
58-
if (selfValue->getOwnershipKind() == OwnershipKind::Owned) {
59-
selfValue = B.createMarkUnresolvedNonCopyableValueInst(
60-
selfDecl, selfValue,
55+
//
56+
// For now, we do not handle move only class deinits. This is because we need
57+
// to do a bit more refactoring to handle the weird way that it deals with
58+
// ownership. But for simple move only deinits (like struct/enum), that are
59+
// owned, lets mark them as needing to be no implicit copy checked so they
60+
// cannot escape.
61+
if (selfType.isMoveOnly() && !selfType.isAnyClassReferenceType()) {
62+
if (getASTContext().LangOpts.hasFeature(
63+
Feature::MoveOnlyPartialConsumption)) {
64+
SILValue addr = B.createAllocStack(selfDecl, selfValue->getType(), dv);
65+
addr = B.createMarkUnresolvedNonCopyableValueInst(
66+
selfDecl, addr,
6167
MarkUnresolvedNonCopyableValueInst::CheckKind::
6268
ConsumableAndAssignable);
69+
if (selfValue->getType().isObject()) {
70+
B.createStore(selfDecl, selfValue, addr, StoreOwnershipQualifier::Init);
71+
} else {
72+
B.createCopyAddr(selfDecl, selfValue, addr, IsTake, IsInitialization);
73+
}
74+
// drop_deinit invalidates any user-defined struct/enum deinit
75+
// before the individual members are destroyed.
76+
addr = B.createDropDeinit(selfDecl, addr);
77+
selfValue = addr;
78+
} else {
79+
if (selfValue->getOwnershipKind() == OwnershipKind::Owned) {
80+
selfValue = B.createMarkUnresolvedNonCopyableValueInst(
81+
selfDecl, selfValue,
82+
MarkUnresolvedNonCopyableValueInst::CheckKind::
83+
ConsumableAndAssignable);
84+
}
6385
}
6486
}
6587

6688
VarLocs[selfDecl] = VarLoc::get(selfValue);
6789
SILLocation PrologueLoc(selfDecl);
6890
PrologueLoc.markAsPrologue();
69-
uint16_t ArgNo = 1; // Hardcoded for destructors.
70-
B.createDebugValue(PrologueLoc, selfValue,
71-
SILDebugVariable(selfDecl->isLet(), ArgNo));
91+
B.createDebugValue(PrologueLoc, selfValue, dv);
7292
return selfValue;
7393
}
7494

lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,10 @@ SourceAccess AccessEnforcementSelection::getSourceAccess(SILValue address) {
711711
if (auto *m = dyn_cast<MoveOnlyWrapperToCopyableAddrInst>(address))
712712
return getSourceAccess(m->getOperand());
713713

714+
// Recurse through drop_deinit.
715+
if (auto *ddi = dyn_cast<DropDeinitInst>(address))
716+
return getSourceAccess(ddi->getOperand());
717+
714718
// Recurse through moveonlywrapper_to_copyable_box.
715719
if (auto *m = dyn_cast<MoveOnlyWrapperToCopyableBoxInst>(address))
716720
return getAccessKindForBox(m->getOperand());

0 commit comments

Comments
 (0)