Skip to content

Commit 31470bb

Browse files
Merge pull request #82732 from nickolas-pohilets/mpokhylets/weak-let-feature
Wrap SE-0481 into an upcoming feature until source incompatibilities are resolved
2 parents 75faafe + 0496ca4 commit 31470bb

28 files changed

+288
-116
lines changed

include/swift/AST/DiagnosticGroups.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ GROUP(TemporaryPointers, "temporary-pointers")
7373
GROUP(TrailingClosureMatching, "trailing-closure-matching")
7474
GROUP(UnknownWarningGroup, "unknown-warning-group")
7575
GROUP(CompilationCaching, "compilation-caching")
76+
GROUP(WeakMutability, "weak-mutability")
7677

7778
#define UNDEFINE_DIAGNOSTIC_GROUPS_MACROS
7879
#include "swift/AST/DefineDiagnosticGroupsMacros.h"

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7414,6 +7414,14 @@ WARNING(variable_tuple_elt_never_mutated, none,
74147414
"variable %0 was never mutated; "
74157415
"consider changing the pattern to 'case (..., let %1, ...)'",
74167416
(Identifier, StringRef))
7417+
GROUPED_WARNING(weak_variable_never_mutated, WeakMutability, none,
7418+
"weak variable %0 was never mutated; "
7419+
"consider %select{removing 'var' to make it|changing to 'let'}1 constant",
7420+
(Identifier, bool))
7421+
GROUPED_WARNING(weak_variable_tuple_elt_never_mutated, WeakMutability, none,
7422+
"weak variable %0 was never mutated; "
7423+
"consider changing the pattern to 'case (..., let %1, ...)'",
7424+
(Identifier, StringRef))
74177425
WARNING(variable_never_read, none,
74187426
"variable %0 was written to, but never read",
74197427
(Identifier))

include/swift/AST/Stmt.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,12 @@ class alignas(1 << PatternAlignInBits) StmtConditionElement {
713713
/// or `let self = self` condition.
714714
/// - If `requiresCaptureListRef` is `true`, additionally requires that the
715715
/// RHS of the self condition references a var defined in a capture list.
716-
bool rebindsSelf(ASTContext &Ctx, bool requiresCaptureListRef = false) const;
716+
/// - If `requireLoadExpr` is `true`, additionally requires that the RHS of
717+
/// the self condition is a `LoadExpr`.
718+
/// TODO: Remove `requireLoadExpr` after full-on of the ImmutableWeakCaptures
719+
/// feature
720+
bool rebindsSelf(ASTContext &Ctx, bool requiresCaptureListRef = false,
721+
bool requireLoadExpr = false) const;
717722

718723
SourceLoc getStartLoc() const;
719724
SourceLoc getEndLoc() const;
@@ -822,7 +827,8 @@ class LabeledConditionalStmt : public LabeledStmt {
822827
/// or `let self = self` condition.
823828
/// - If `requiresCaptureListRef` is `true`, additionally requires that the
824829
/// RHS of the self condition references a var defined in a capture list.
825-
bool rebindsSelf(ASTContext &Ctx, bool requiresCaptureListRef = false) const;
830+
bool rebindsSelf(ASTContext &Ctx, bool requiresCaptureListRef = false,
831+
bool requireLoadExpr = false) const;
826832

827833
static bool classof(const Stmt *S) {
828834
return S->getKind() >= StmtKind::First_LabeledConditionalStmt &&

include/swift/Basic/Features.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ UPCOMING_FEATURE(InternalImportsByDefault, 409, 7)
297297
MIGRATABLE_UPCOMING_FEATURE(MemberImportVisibility, 444, 7)
298298
MIGRATABLE_UPCOMING_FEATURE(InferIsolatedConformances, 470, 7)
299299
MIGRATABLE_UPCOMING_FEATURE(NonisolatedNonsendingByDefault, 461, 7)
300+
UPCOMING_FEATURE(ImmutableWeakCaptures, 481, 7)
300301

301302
// Optional language features / modes
302303

lib/AST/Expr.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1373,8 +1373,11 @@ CaptureListEntry CaptureListEntry::createParsed(
13731373
SourceRange ownershipRange, Identifier name, SourceLoc nameLoc,
13741374
SourceLoc equalLoc, Expr *initializer, DeclContext *DC) {
13751375

1376+
bool forceVar = ownershipKind == ReferenceOwnership::Weak &&
1377+
!Ctx.LangOpts.hasFeature(Feature::ImmutableWeakCaptures);
1378+
auto introducer = forceVar ? VarDecl::Introducer::Var : VarDecl::Introducer::Let;
13761379
auto *VD =
1377-
new (Ctx) VarDecl(/*isStatic==*/false, VarDecl::Introducer::Let, nameLoc, name, DC);
1380+
new (Ctx) VarDecl(/*isStatic==*/false, introducer, nameLoc, name, DC);
13781381

13791382
if (ownershipKind != ReferenceOwnership::Strong)
13801383
VD->getAttrs().add(

lib/AST/FeatureSet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,7 @@ UNINTERESTING_FEATURE(BuiltinSelect)
441441
UNINTERESTING_FEATURE(BuiltinInterleave)
442442
UNINTERESTING_FEATURE(BuiltinVectorsExternC)
443443
UNINTERESTING_FEATURE(AddressOfProperty2)
444+
UNINTERESTING_FEATURE(ImmutableWeakCaptures)
444445

445446
// ----------------------------------------------------------------------------
446447
// MARK: - FeatureSet

lib/AST/Stmt.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -515,9 +515,11 @@ void LabeledConditionalStmt::setCond(StmtCondition e) {
515515
/// - If `requiresCaptureListRef` is `true`, additionally requires that the
516516
/// RHS of the self condition references a var defined in a capture list.
517517
bool LabeledConditionalStmt::rebindsSelf(ASTContext &Ctx,
518-
bool requiresCaptureListRef) const {
519-
return llvm::any_of(getCond(), [&Ctx, requiresCaptureListRef](const auto &cond) {
520-
return cond.rebindsSelf(Ctx, requiresCaptureListRef);
518+
bool requiresCaptureListRef,
519+
bool requireLoadExpr) const {
520+
return llvm::any_of(getCond(), [&Ctx, requiresCaptureListRef,
521+
requireLoadExpr](const auto &cond) {
522+
return cond.rebindsSelf(Ctx, requiresCaptureListRef, requireLoadExpr);
521523
});
522524
}
523525

@@ -526,7 +528,8 @@ bool LabeledConditionalStmt::rebindsSelf(ASTContext &Ctx,
526528
/// - If `requiresCaptureListRef` is `true`, additionally requires that the
527529
/// RHS of the self condition references a var defined in a capture list.
528530
bool StmtConditionElement::rebindsSelf(ASTContext &Ctx,
529-
bool requiresCaptureListRef) const {
531+
bool requiresCaptureListRef,
532+
bool requireLoadExpr) const {
530533
auto pattern = getPatternOrNull();
531534
if (!pattern) {
532535
return false;
@@ -554,6 +557,10 @@ bool StmtConditionElement::rebindsSelf(ASTContext &Ctx,
554557
return false;
555558
}
556559

560+
if (requireLoadExpr && !isa<LoadExpr>(exprToCheckForDRE)) {
561+
return false;
562+
}
563+
557564
if (auto *load = dyn_cast<LoadExpr>(exprToCheckForDRE)) {
558565
exprToCheckForDRE = load->getSubExpr();
559566
}

lib/SIL/IR/TypeLowering.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,15 @@ CaptureKind TypeConverter::getDeclCaptureKind(CapturedValue capture,
170170
return CaptureKind::StorageAddress;
171171
}
172172

173+
// Reference storage types can appear in a capture list, which means
174+
// we might allocate boxes to store the captures. However, those boxes
175+
// have the same lifetime as the closure itself, so we must capture
176+
// the box itself and not the payload, even if the closure is noescape,
177+
// otherwise they will be destroyed when the closure is formed.
178+
if (var->getInterfaceType()->is<ReferenceStorageType>()) {
179+
return CaptureKind::Box;
180+
}
181+
173182
// For 'let' constants
174183
if (!var->supportsMutation()) {
175184
assert(getTypeProperties(

lib/Sema/MiscDiagnostics.cpp

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1781,23 +1781,34 @@ class ImplicitSelfUsageChecker : public BaseDiagnosticWalker {
17811781
return false;
17821782
}
17831783

1784-
// Require that the RHS of the `let self = self` condition
1785-
// refers to a variable defined in a capture list.
1786-
// This lets us reject invalid examples like:
1787-
//
1788-
// var `self` = self ?? .somethingElse
1789-
// guard let self = self else { return }
1790-
// method() // <- implicit self is not allowed
1791-
//
1792-
// In 5.10, instead of this check, compiler was checking that RHS of the
1793-
// self binding is loaded from a mutable variable. This is incorrect, but
1794-
// before SE-0481 compiler was trying to maintain this behavior in Swift 5
1795-
// mode for source compatibility. After SE-0481 this does not work
1796-
// anymore, because even in Swift 5 mode `weak self` capture is not mutable.
1797-
// So we have to introduce a breaking change as part of the SE-0481, and use
1798-
// proper check for capture list even in Swift 5 mode.
1799-
//
1800-
return conditionalStmt->rebindsSelf(Ctx, /*requiresCaptureListRef*/ true);
1784+
if (Ctx.LangOpts.hasFeature(Feature::ImmutableWeakCaptures)) {
1785+
// Require that the RHS of the `let self = self` condition
1786+
// refers to a variable defined in a capture list.
1787+
// This lets us reject invalid examples like:
1788+
//
1789+
// var `self` = self ?? .somethingElse
1790+
// guard let self = self else { return }
1791+
// method() // <- implicit self is not allowed
1792+
//
1793+
// In 5.10, instead of this check, compiler was checking that RHS of the
1794+
// self binding is loaded from a mutable variable. This is incorrect, but
1795+
// before immutable weak captures compiler was trying to maintain this
1796+
// behavior in Swift 5 mode for source compatibility. With immutable weak
1797+
// captures this does not work anymore, because even in Swift 5 mode there
1798+
// is no `LoadExpr` to use.
1799+
//
1800+
return conditionalStmt->rebindsSelf(Ctx, /*requiresCaptureListRef*/ true);
1801+
} else {
1802+
// Require `LoadExpr`s when validating the self binding.
1803+
// This lets us reject invalid examples like:
1804+
//
1805+
// let `self` = self ?? .somethingElse
1806+
// guard let self = self else { return }
1807+
// method() // <- implicit self is not allowed
1808+
//
1809+
return conditionalStmt->rebindsSelf(Ctx, /*requiresCaptureListRef*/ false,
1810+
/*requireLoadExpr*/ true);
1811+
}
18011812
}
18021813

18031814
static bool
@@ -4087,6 +4098,14 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
40874098
access &= ~RK_Written;
40884099
}
40894100

4101+
// If this variable has WeakStorageType, then it can be mutated in ways we
4102+
// don't know.
4103+
if (var->getInterfaceType()->is<WeakStorageType>() &&
4104+
(access & RK_CaptureList) &&
4105+
!DC->getASTContext().LangOpts.hasFeature(
4106+
Feature::ImmutableWeakCaptures))
4107+
access |= RK_Written;
4108+
40904109
// Diagnose variables that were never used (other than their
40914110
// initialization).
40924111
//
@@ -4257,6 +4276,8 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
42574276
if (isUsedInInactive(var))
42584277
continue;
42594278

4279+
bool isWeak = var->getInterfaceType()->is<WeakStorageType>();
4280+
42604281
// If this is a parameter explicitly marked 'var', remove it.
42614282
if (FixItLoc.isInvalid()) {
42624283
bool suggestCaseLet = false;
@@ -4267,10 +4288,14 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
42674288
suggestCaseLet = isa<ForEachStmt>(stmt);
42684289
}
42694290
if (suggestCaseLet)
4270-
Diags.diagnose(var->getLoc(), diag::variable_tuple_elt_never_mutated,
4291+
Diags.diagnose(var->getLoc(),
4292+
isWeak ? diag::weak_variable_tuple_elt_never_mutated
4293+
: diag::variable_tuple_elt_never_mutated,
42714294
var->getName(), var->getNameStr());
42724295
else
4273-
Diags.diagnose(var->getLoc(), diag::variable_never_mutated,
4296+
Diags.diagnose(var->getLoc(),
4297+
isWeak ? diag::weak_variable_never_mutated
4298+
: diag::variable_never_mutated,
42744299
var->getName(), true);
42754300

42764301
}
@@ -4283,7 +4308,9 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
42834308
suggestLet = !isa<ForEachStmt>(stmt);
42844309
}
42854310

4286-
auto diag = Diags.diagnose(var->getLoc(), diag::variable_never_mutated,
4311+
auto diag = Diags.diagnose(var->getLoc(),
4312+
isWeak ? diag::weak_variable_never_mutated
4313+
: diag::variable_never_mutated,
42874314
var->getName(), suggestLet);
42884315

42894316
if (suggestLet)

0 commit comments

Comments
 (0)