Skip to content

Commit 1c3667b

Browse files
authored
Merge pull request swiftlang#65310 from calda/cal--SE-3065-bad-pattern-check
[SE-0365] Fix issue where boolean condition before `let self` condition would prevent using implicit self
2 parents 0be1ad1 + c029dd6 commit 1c3667b

File tree

6 files changed

+237
-56
lines changed

6 files changed

+237
-56
lines changed

include/swift/AST/Stmt.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,11 +556,17 @@ class alignas(1 << PatternAlignInBits) StmtConditionElement {
556556
assert(getKind() == CK_PatternBinding && "Not a pattern binding condition");
557557
ThePattern = P;
558558
}
559+
560+
/// Pattern Binding Accessors.
561+
Expr *getInitializerOrNull() const {
562+
return getKind() == CK_PatternBinding ? Condition.get<Expr *>() : nullptr;
563+
}
559564

560565
Expr *getInitializer() const {
561566
assert(getKind() == CK_PatternBinding && "Not a pattern binding condition");
562567
return Condition.get<Expr *>();
563568
}
569+
564570
void setInitializer(Expr *E) {
565571
assert(getKind() == CK_PatternBinding && "Not a pattern binding condition");
566572
Condition = E;
@@ -588,6 +594,11 @@ class alignas(1 << PatternAlignInBits) StmtConditionElement {
588594
Condition = Info;
589595
}
590596

597+
/// Whether or not this conditional stmt rebinds self with a `let self`
598+
/// or `let self = self` condition. If `requireLoadExpr` is `true`,
599+
/// additionally requires that the RHS of the self condition is a `LoadExpr`.
600+
bool rebindsSelf(ASTContext &Ctx, bool requireLoadExpr = false) const;
601+
591602
SourceLoc getStartLoc() const;
592603
SourceLoc getEndLoc() const;
593604
SourceRange getSourceRange() const;
@@ -691,6 +702,11 @@ class LabeledConditionalStmt : public LabeledStmt {
691702
/// stored in \c ASTNode.
692703
StmtCondition *getCondPointer() { return &Cond; }
693704

705+
/// Whether or not this conditional stmt rebinds self with a `let self`
706+
/// or `let self = self` condition. If `requireLoadExpr` is `true`,
707+
/// additionally requires that the RHS of the self condition is a `LoadExpr`.
708+
bool rebindsSelf(ASTContext &Ctx, bool requireLoadExpr = false) const;
709+
694710
static bool classof(const Stmt *S) {
695711
return S->getKind() >= StmtKind::First_LabeledConditionalStmt &&
696712
S->getKind() <= StmtKind::Last_LabeledConditionalStmt;

lib/AST/Stmt.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,66 @@ void LabeledConditionalStmt::setCond(StmtCondition e) {
466466
Cond = e;
467467
}
468468

469+
/// Whether or not this conditional stmt rebinds self with a `let self`
470+
/// or `let self = self` condition. If `requireLoadExpr` is `true`,
471+
/// additionally requires that the RHS of the self condition is a `LoadExpr`.
472+
bool LabeledConditionalStmt::rebindsSelf(ASTContext &Ctx,
473+
bool requireLoadExpr) const {
474+
return llvm::any_of(getCond(), [&Ctx, requireLoadExpr](const auto &cond) {
475+
return cond.rebindsSelf(Ctx, requireLoadExpr);
476+
});
477+
}
478+
479+
/// Whether or not this conditional stmt rebinds self with a `let self`
480+
/// or `let self = self` condition. If `requireLoadExpr` is `true`,
481+
/// additionally requires that the RHS of the self condition is a `LoadExpr`.
482+
bool StmtConditionElement::rebindsSelf(ASTContext &Ctx,
483+
bool requireLoadExpr) const {
484+
auto pattern = getPatternOrNull();
485+
if (!pattern) {
486+
return false;
487+
}
488+
489+
// Check whether or not this pattern defines a new `self` decl
490+
bool isSelfRebinding = false;
491+
if (pattern->getBoundName() == Ctx.Id_self) {
492+
isSelfRebinding = true;
493+
}
494+
495+
else if (auto OSP = dyn_cast<OptionalSomePattern>(pattern)) {
496+
if (auto subPattern = OSP->getSubPattern()) {
497+
isSelfRebinding = subPattern->getBoundName() == Ctx.Id_self;
498+
}
499+
}
500+
501+
if (!isSelfRebinding) {
502+
return false;
503+
}
504+
505+
// Check that the RHS expr is exactly `self` and not something else
506+
Expr *exprToCheckForDRE = getInitializerOrNull();
507+
if (!exprToCheckForDRE) {
508+
return false;
509+
}
510+
511+
if (requireLoadExpr && !isa<LoadExpr>(exprToCheckForDRE)) {
512+
return false;
513+
}
514+
515+
if (auto *load = dyn_cast<LoadExpr>(exprToCheckForDRE)) {
516+
exprToCheckForDRE = load->getSubExpr();
517+
}
518+
519+
if (auto *DRE = dyn_cast<DeclRefExpr>(
520+
exprToCheckForDRE->getSemanticsProvidingExpr())) {
521+
auto *decl = DRE->getDecl();
522+
return decl && decl->hasName() ? decl->getName().isSimpleName(Ctx.Id_self)
523+
: false;
524+
}
525+
526+
return false;
527+
}
528+
469529
PoundAvailableInfo *
470530
PoundAvailableInfo::create(ASTContext &ctx, SourceLoc PoundLoc,
471531
SourceLoc LParenLoc,

lib/AST/UnqualifiedLookup.cpp

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -397,45 +397,7 @@ bool implicitSelfReferenceIsUnwrapped(const ValueDecl *selfDecl,
397397
return false;
398398
}
399399

400-
// Find the condition that defined the self decl,
401-
// and check that both its LHS and RHS are 'self'
402-
for (auto cond : conditionalStmt->getCond()) {
403-
if (auto pattern = cond.getPattern()) {
404-
bool isSelfRebinding = false;
405-
406-
if (pattern->getBoundName() == Ctx.Id_self) {
407-
isSelfRebinding = true;
408-
}
409-
410-
else if (auto OSP = dyn_cast<OptionalSomePattern>(pattern)) {
411-
if (auto subPattern = OSP->getSubPattern()) {
412-
isSelfRebinding = subPattern->getBoundName() == Ctx.Id_self;
413-
}
414-
}
415-
416-
if (!isSelfRebinding) {
417-
continue;
418-
}
419-
}
420-
421-
Expr *exprToCheckForDRE = cond.getInitializer();
422-
if (auto LE = dyn_cast<LoadExpr>(exprToCheckForDRE)) {
423-
if (auto subexpr = LE->getSubExpr()) {
424-
exprToCheckForDRE = subexpr;
425-
}
426-
}
427-
428-
exprToCheckForDRE = exprToCheckForDRE->getSemanticsProvidingExpr();
429-
430-
DeclRefExpr *condDRE = dyn_cast<DeclRefExpr>(exprToCheckForDRE);
431-
if (!condDRE || !condDRE->getDecl()->hasName()) {
432-
return false;
433-
}
434-
435-
return condDRE->getDecl()->getName().isSimpleName(Ctx.Id_self);
436-
}
437-
438-
return false;
400+
return conditionalStmt->rebindsSelf(Ctx);
439401
}
440402

441403
// Finds the nearest parent closure, which would define the

lib/Sema/MiscDiagnostics.cpp

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,23 +1689,14 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
16891689
return false;
16901690
}
16911691

1692-
// Find the condition that defined the self decl,
1693-
// and check that both its LHS and RHS are 'self'
1694-
for (auto cond : conditionalStmt->getCond()) {
1695-
if (auto OSP = dyn_cast<OptionalSomePattern>(cond.getPattern())) {
1696-
if (OSP->getSubPattern()->getBoundName() != Ctx.Id_self) {
1697-
continue;
1698-
}
1699-
1700-
if (auto LE = dyn_cast<LoadExpr>(cond.getInitializer())) {
1701-
if (auto selfDRE = dyn_cast<DeclRefExpr>(LE->getSubExpr())) {
1702-
return (selfDRE->getDecl()->getName().isSimpleName(Ctx.Id_self));
1703-
}
1704-
}
1705-
}
1706-
}
1707-
1708-
return false;
1692+
// Require `LoadExpr`s when validating the self binding.
1693+
// This lets us reject invalid examples like:
1694+
//
1695+
// let `self` = self ?? .somethingElse
1696+
// guard let self = self else { return }
1697+
// method() // <- implicit self is not allowed
1698+
//
1699+
return conditionalStmt->rebindsSelf(Ctx, /*requireLoadExpr*/ true);
17091700
}
17101701

17111702
/// Return true if this is a closure expression that will require explicit

test/expr/closure/closures.swift

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,82 @@ class NoImplicitSelfInInnerClass {
983983
}
984984

985985
}
986+
987+
func foo(condition: Bool) {
988+
doVoidStuff { [weak self] in
989+
guard condition, let self else { return }
990+
method()
991+
}
992+
993+
doVoidStuff { [weak self] in
994+
guard let self, condition else { return }
995+
method()
996+
}
997+
998+
doVoidStuffNonEscaping { [weak self] in
999+
guard condition, let self else { return }
1000+
method()
1001+
}
1002+
1003+
doVoidStuffNonEscaping { [weak self] in
1004+
guard let self, condition else { return }
1005+
method()
1006+
}
1007+
}
1008+
1009+
func foo(optionalCondition: Bool?) {
1010+
doVoidStuff { [weak self] in
1011+
guard let optionalCondition, optionalCondition, let self else { return }
1012+
method()
1013+
}
1014+
1015+
doVoidStuff { [weak self] in
1016+
guard let self, let optionalCondition, optionalCondition else { return }
1017+
method()
1018+
}
1019+
1020+
doVoidStuff { [weak self] in
1021+
guard let optionalCondition, let self, optionalCondition else { return }
1022+
method()
1023+
}
1024+
1025+
doVoidStuffNonEscaping { [weak self] in
1026+
guard let optionalCondition, optionalCondition, let self else { return }
1027+
method()
1028+
}
1029+
1030+
doVoidStuffNonEscaping { [weak self] in
1031+
guard let self, let optionalCondition, optionalCondition else { return }
1032+
method()
1033+
}
1034+
1035+
doVoidStuffNonEscaping { [weak self] in
1036+
guard let optionalCondition, let self, optionalCondition else { return }
1037+
method()
1038+
}
1039+
}
1040+
1041+
func foo() {
1042+
doVoidStuff { [weak self] in
1043+
guard #available(SwiftStdlib 5.8, *), let self else { return }
1044+
method()
1045+
}
1046+
1047+
doVoidStuff { [weak self] in
1048+
guard let self, #available(SwiftStdlib 5.8, *) else { return }
1049+
method()
1050+
}
1051+
1052+
doVoidStuffNonEscaping { [weak self] in
1053+
guard #available(SwiftStdlib 5.8, *), let self else { return }
1054+
method()
1055+
}
1056+
1057+
doVoidStuffNonEscaping { [weak self] in
1058+
guard let self, #available(SwiftStdlib 5.8, *) else { return }
1059+
method()
1060+
}
1061+
}
9861062
}
9871063

9881064
public class TestRebindingSelfIsDisallowed {

test/expr/closure/closures_swift6.swift

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,82 @@ class NoImplicitSelfInInnerClass {
210210
}
211211
}
212212
}
213+
214+
func foo(condition: Bool) {
215+
doVoidStuff { [weak self] in
216+
guard condition, let self else { return }
217+
method()
218+
}
219+
220+
doVoidStuff { [weak self] in
221+
guard let self, condition else { return }
222+
method()
223+
}
224+
225+
doVoidStuffNonEscaping { [weak self] in
226+
guard condition, let self else { return }
227+
method()
228+
}
229+
230+
doVoidStuffNonEscaping { [weak self] in
231+
guard let self, condition else { return }
232+
method()
233+
}
234+
}
235+
236+
func foo(optionalCondition: Bool?) {
237+
doVoidStuff { [weak self] in
238+
guard let optionalCondition, optionalCondition, let self else { return }
239+
method()
240+
}
241+
242+
doVoidStuff { [weak self] in
243+
guard let self, let optionalCondition, optionalCondition else { return }
244+
method()
245+
}
246+
247+
doVoidStuff { [weak self] in
248+
guard let optionalCondition, let self, optionalCondition else { return }
249+
method()
250+
}
251+
252+
doVoidStuffNonEscaping { [weak self] in
253+
guard let optionalCondition, optionalCondition, let self else { return }
254+
method()
255+
}
256+
257+
doVoidStuffNonEscaping { [weak self] in
258+
guard let self, let optionalCondition, optionalCondition else { return }
259+
method()
260+
}
261+
262+
doVoidStuffNonEscaping { [weak self] in
263+
guard let optionalCondition, let self, optionalCondition else { return }
264+
method()
265+
}
266+
}
267+
268+
func foo() {
269+
doVoidStuff { [weak self] in
270+
guard #available(SwiftStdlib 5.8, *), let self else { return }
271+
method()
272+
}
273+
274+
doVoidStuff { [weak self] in
275+
guard let self, #available(SwiftStdlib 5.8, *) else { return }
276+
method()
277+
}
278+
279+
doVoidStuffNonEscaping { [weak self] in
280+
guard #available(SwiftStdlib 5.8, *), let self else { return }
281+
method()
282+
}
283+
284+
doVoidStuffNonEscaping { [weak self] in
285+
guard let self, #available(SwiftStdlib 5.8, *) else { return }
286+
method()
287+
}
288+
}
213289
}
214290

215291
public class TestRebindingSelfIsDisallowed {

0 commit comments

Comments
 (0)