Skip to content

Commit 6f7961b

Browse files
committed
[move-only] Mark self of move only struct/enum types in deinits as being move-only.
This ensures that if we try to escape self or assign it to a variable, we error since at the end of the deinit we always consume self and clean up its variables. I did not handle unique classes since it would have required a bit more surgery around how deinits are handled and we do not need unique classes for our MVP. rdar://102339259
1 parent 5fa31c6 commit 6f7961b

File tree

4 files changed

+64
-7
lines changed

4 files changed

+64
-7
lines changed

lib/SILGen/SILGenDestructor.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ void SILGenFunction::emitDestroyingDestructor(DestructorDecl *dd) {
3737

3838
auto cd = cast<ClassDecl>(dd->getDeclContext()->getSelfNominalTypeDecl());
3939
auto &C = cd->getASTContext();
40-
SILValue selfValue = emitSelfDecl(dd->getImplicitSelfDecl());
40+
SILValue selfValue = emitSelfDeclForDestructor(dd->getImplicitSelfDecl());
4141

4242
// Create a basic block to jump to for the implicit destruction behavior
4343
// of releasing the elements and calling the superclass destructor.
@@ -179,7 +179,8 @@ void SILGenFunction::emitDeallocatingClassDestructor(DestructorDecl *dd) {
179179
loc.markAutoGenerated();
180180

181181
// Emit the prolog.
182-
SILValue initialSelfValue = emitSelfDecl(dd->getImplicitSelfDecl());
182+
SILValue initialSelfValue =
183+
emitSelfDeclForDestructor(dd->getImplicitSelfDecl());
183184

184185
// Form a reference to the destroying destructor.
185186
SILDeclRef dtorConstant(dd, SILDeclRef::Kind::Destroyer);
@@ -233,7 +234,7 @@ void SILGenFunction::emitDeallocatingMoveOnlyDestructor(DestructorDecl *dd) {
233234
loc.markAutoGenerated();
234235

235236
// Emit the prolog.
236-
auto selfValue = emitSelfDecl(dd->getImplicitSelfDecl());
237+
auto selfValue = emitSelfDeclForDestructor(dd->getImplicitSelfDecl());
237238

238239
// Create a basic block to jump to for the implicit destruction behavior
239240
// of releasing the elements and calling the superclass destructor.
@@ -264,7 +265,7 @@ void SILGenFunction::emitIVarDestroyer(SILDeclRef ivarDestroyer) {
264265
loc.markAutoGenerated();
265266

266267
ManagedValue selfValue = ManagedValue::forUnmanaged(
267-
emitSelfDecl(cd->getDestructor()->getImplicitSelfDecl()));
268+
emitSelfDeclForDestructor(cd->getDestructor()->getImplicitSelfDecl()));
268269

269270
auto cleanupLoc = CleanupLocation(loc);
270271
prepareEpilog(None, false, cleanupLoc);
@@ -593,7 +594,7 @@ void SILGenFunction::emitObjCDestructor(SILDeclRef dtor) {
593594
if (dd->isImplicit())
594595
loc.markAutoGenerated();
595596

596-
SILValue selfValue = emitSelfDecl(dd->getImplicitSelfDecl());
597+
SILValue selfValue = emitSelfDeclForDestructor(dd->getImplicitSelfDecl());
597598

598599
// Create a basic block to jump to for the implicit destruction behavior
599600
// of releasing the elements and calling the superclass destructor.

lib/SILGen/SILGenFunction.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1054,7 +1054,9 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
10541054

10551055
/// emitSelfDecl - Emit a SILArgument for 'self', register it in varlocs, set
10561056
/// up debug info, etc. This returns the 'self' value.
1057-
SILValue emitSelfDecl(VarDecl *selfDecl);
1057+
///
1058+
/// This is intended to only be used for destructors.
1059+
SILValue emitSelfDeclForDestructor(VarDecl *selfDecl);
10581060

10591061
/// Emits a temporary allocation that will be deallocated automatically at the
10601062
/// end of the current scope. Returns the address of the allocation.

lib/SILGen/SILGenProlog.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,25 @@ static void diagnose(ASTContext &Context, SourceLoc loc, Diag<T...> diag,
3434
Context.Diags.diagnose(loc, diag, std::forward<U>(args)...);
3535
}
3636

37-
SILValue SILGenFunction::emitSelfDecl(VarDecl *selfDecl) {
37+
SILValue SILGenFunction::emitSelfDeclForDestructor(VarDecl *selfDecl) {
3838
// Emit the implicit 'self' argument.
3939
SILType selfType = getLoweredLoadableType(selfDecl->getType());
4040
SILValue selfValue = F.begin()->createFunctionArgument(selfType, selfDecl);
41+
42+
// If we have a move only type, then mark it with mark_must_check so we can't
43+
// escape it.
44+
if (selfType.isMoveOnly()) {
45+
// For now, we do not handle move only class deinits. This is because we
46+
// need to do a bit more refactoring to handle the weird way that it deals
47+
// with ownership. But for simple move only deinits (like struct/enum), that
48+
// are owned, lets mark them as needing to be no implicit copy checked so
49+
// they cannot escape.
50+
if (selfValue->getOwnershipKind() == OwnershipKind::Owned) {
51+
selfValue = B.createMarkMustCheckInst(
52+
selfDecl, selfValue, MarkMustCheckInst::CheckKind::NoImplicitCopy);
53+
}
54+
}
55+
4156
VarLocs[selfDecl] = VarLoc::get(selfValue);
4257
SILLocation PrologueLoc(selfDecl);
4358
PrologueLoc.markAsPrologue();
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: %target-swift-frontend -enable-experimental-move-only -verify -emit-sil %s
2+
3+
class Klass {}
4+
5+
var globalMoveOnlyStruct = MoveOnlyStruct()
6+
var globalMoveOnlyEnum = MoveOnlyEnum.lhs(Klass())
7+
8+
@_moveOnly
9+
struct MoveOnlyStruct {
10+
var k = Klass()
11+
12+
deinit { // expected-error {{'self' consumed more than once}}
13+
let x = self // expected-note {{consuming use}}
14+
_ = x
15+
var y = MoveOnlyStruct()
16+
y = self // expected-note {{consuming use}}
17+
// We get an infinite recursion since we are going to call our own
18+
// deinit here. We are just testing diagnostics here though.
19+
_ = y // expected-warning {{function call causes an infinite recursion}}
20+
globalMoveOnlyStruct = self // expected-note {{consuming use}}
21+
} // expected-note {{consuming use}}
22+
}
23+
24+
@_moveOnly
25+
enum MoveOnlyEnum {
26+
case lhs(Klass)
27+
case rhs(Klass)
28+
29+
deinit { // expected-error {{'self' consumed more than once}}
30+
let x = self // expected-note {{consuming use}}
31+
_ = x
32+
var y = MoveOnlyEnum.lhs(Klass())
33+
y = self // expected-note {{consuming use}}
34+
// We get an infinite recursion since we are going to call our own
35+
// deinit here. We are just testing diagnostics here though.
36+
_ = y // expected-warning {{function call causes an infinite recursion}}
37+
globalMoveOnlyEnum = self // expected-note {{consuming use}}
38+
} // expected-note {{consuming use}}
39+
}

0 commit comments

Comments
 (0)