Skip to content

Commit 404267d

Browse files
committed
ensure forget cannot appear outside the type's module
We can't implement `forget` unless if we know all of the fields in the type, so it cannot be resilient at all. We go further and say that you can only `forget` from the same module, since that's what the proposal currently states. If needed we can loosen that in the future.
1 parent f41ed59 commit 404267d

File tree

5 files changed

+83
-5
lines changed

5 files changed

+83
-5
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4518,7 +4518,9 @@ ERROR(forget_wrong_context_nonconsuming,none,
45184518
(DescriptiveDeclKind))
45194519
ERROR(forget_wrong_not_self,none,
45204520
"you can only forget 'self'", ())
4521-
4521+
ERROR(forget_wrong_module,none,
4522+
"can only 'forget' from the same module defining type %0",
4523+
(Type))
45224524

45234525
//------------------------------------------------------------------------------
45244526
// MARK: Type Check Patterns

lib/Sema/TypeCheckStmt.cpp

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1256,14 +1256,33 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
12561256
if (!diagnosed) {
12571257
Type nominalType =
12581258
fn->getDeclContext()->getSelfNominalTypeDecl()->getDeclaredType();
1259-
if (nominalType->isPureMoveOnly()) {
1260-
// Set the contextual type for the sub-expression before we typecheck.
1261-
contextualInfo = {nominalType, CTP_ForgetStmt};
1262-
} else {
1259+
if (!nominalType->isPureMoveOnly()) {
12631260
ctx.Diags.diagnose(FS->getForgetLoc(),
12641261
diag::forget_wrong_context_copyable,
12651262
fn->getDescriptiveKind());
12661263
diagnosed = true;
1264+
} else {
1265+
// Set the contextual type for the sub-expression before we typecheck.
1266+
contextualInfo = {nominalType, CTP_ForgetStmt};
1267+
1268+
// Now verify that we're not forgetting a type from another module.
1269+
//
1270+
// NOTE: We could do a proper resilience check instead of just asking
1271+
// if the modules differ, so that you can forget a @frozen type from a
1272+
// resilient module. But for now the proposal simply says that it has to
1273+
// be the same module, which is probably better for everyone.
1274+
auto *typeDecl = nominalType->getAnyNominal();
1275+
auto *fnModule = fn->getModuleContext();
1276+
auto *typeModule = typeDecl->getModuleContext();
1277+
if (fnModule != typeModule) {
1278+
ctx.Diags.diagnose(FS->getForgetLoc(), diag::forget_wrong_module,
1279+
nominalType);
1280+
diagnosed = true;
1281+
} else {
1282+
assert(
1283+
!typeDecl->isResilient(fnModule, ResilienceExpansion::Maximal) &&
1284+
"trying to forget a type resilient to us!");
1285+
}
12671286
}
12681287
}
12691288

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
public extension Regular {
2+
__consuming func shutdownParty() {
3+
_forget self // ok; same module
4+
}
5+
}
6+
7+
public extension Frozen {
8+
__consuming func shutdownParty() {
9+
_forget self // ok; same module
10+
}
11+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
@_moveOnly
2+
public struct Regular {
3+
private let sorry = 0
4+
}
5+
6+
@_moveOnly
7+
@frozen public struct Frozen {
8+
private let lotfan = 0
9+
}
10+
11+
12+
public extension Regular {
13+
__consuming func endParty() {
14+
_forget self
15+
}
16+
}
17+
18+
public extension Frozen {
19+
__consuming func endParty() {
20+
_forget self
21+
}
22+
}

test/Sema/forget_module.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// >> first try when no library evolution is specified
4+
// RUN: %target-swift-frontend -enable-experimental-move-only -emit-module -o %t/SorryModule.swiftmodule %S/Inputs/forget_module_defining.swift %S/Inputs/forget_module_adjacent.swift
5+
// RUN: %target-typecheck-verify-swift -enable-experimental-move-only -I %t
6+
7+
// >> now again with library evolution; we expect the same result.
8+
// RUN: %target-swift-frontend -enable-library-evolution -enable-experimental-move-only -emit-module -o %t/SorryModule.swiftmodule %S/Inputs/forget_module_defining.swift %S/Inputs/forget_module_adjacent.swift
9+
// RUN: %target-typecheck-verify-swift -enable-experimental-move-only -I %t
10+
11+
// "Sorry" is meaningless
12+
import SorryModule
13+
14+
extension Regular {
15+
__consuming func delete() {
16+
_forget self // expected-error {{can only 'forget' from the same module defining type 'Regular'}}
17+
}
18+
}
19+
20+
extension Frozen {
21+
__consuming func delete() {
22+
_forget self // expected-error {{can only 'forget' from the same module defining type 'Frozen'}}
23+
}
24+
}

0 commit comments

Comments
 (0)