[silgen] Balance out the +1 from the self argument in deallocating deinits using end_lifetime.

The issue here is that:

1. Self is passed into deallocating deinits at +1.
2. Destroying deinits take in self as a +0 value that is then returned at +1.

This means that the lifetime of self can not be modeled statically in a
deallocating deinit without analyzing the body of the destroying deinit
(something that violates semantic sil). Thus we add an artifical destroy of
self before the actual destroy of self so that the verifier can understand
that self is being properly balanced.

rdar://29791263
This commit is contained in:
Michael Gottesman
2017-03-01 18:07:33 -08:00
parent 4bc12aedbd
commit f92693ab04
2 changed files with 25 additions and 8 deletions

View File

@@ -85,29 +85,45 @@ void SILGenFunction::emitDeallocatingDestructor(DestructorDecl *dd) {
loc.markAutoGenerated();
// Emit the prolog.
SILValue selfValue = emitSelfDecl(dd->getImplicitSelfDecl());
SILValue initialSelfValue = emitSelfDecl(dd->getImplicitSelfDecl());
// Form a reference to the destroying destructor.
SILDeclRef dtorConstant(dd, SILDeclRef::Kind::Destroyer);
auto classTy = selfValue->getType();
auto classTy = initialSelfValue->getType();
ManagedValue dtorValue;
SILType dtorTy;
SubstitutionList subs = classTy.gatherAllSubstitutions(SGM.M);
std::tie(dtorValue, dtorTy, subs)
= emitSiblingMethodRef(loc, selfValue, dtorConstant, subs);
= emitSiblingMethodRef(loc, initialSelfValue, dtorConstant, subs);
// Call the destroying destructor.
SILValue selfForDealloc;
{
FullExpr CleanupScope(Cleanups, CleanupLocation::get(loc));
ManagedValue borrowedSelf = emitManagedBeginBorrow(loc, selfValue);
ManagedValue borrowedSelf = emitManagedBeginBorrow(loc, initialSelfValue);
SILType objectPtrTy = SILType::getNativeObjectType(F.getASTContext());
selfValue = B.createApply(loc, dtorValue.forward(*this),
dtorTy, objectPtrTy, subs, borrowedSelf.getUnmanagedValue());
selfForDealloc = B.createApply(loc, dtorValue.forward(*this),
dtorTy, objectPtrTy, subs, borrowedSelf.getUnmanagedValue());
}
// Balance out the +1 from the self argument using end_lifetime.
//
// The issue here is that:
//
// 1. Self is passed into deallocating deinits at +1.
// 2. Destroying deinits take in self as a +0 value that is then returned at
// +1.
//
// This means that the lifetime of self can not be modeled statically in a
// deallocating deinit without analyzing the body of the destroying deinit
// (something that violates semantic sil). Thus we add an artifical destroy of
// self before the actual destroy of self so that the verifier can understand
// that self is being properly balanced.
B.createEndLifetime(loc, initialSelfValue);
// Deallocate the object.
selfValue = B.createUncheckedRefCast(loc, selfValue, classTy);
B.createDeallocRef(loc, selfValue, false);
selfForDealloc = B.createUncheckedRefCast(loc, selfForDealloc, classTy);
B.createDeallocRef(loc, selfForDealloc, false);
// Return.
B.createReturn(loc, emitEmptyTuple(loc));