Skip to content

Commit 043b192

Browse files
committed
[Sema] Diagnose duplicate pattern vars in pattern resolution
Unify the duplicate pattern var checking in pattern resolution such that it is always called no matter how we end up type-checking the pattern. This avoids incorrectly allowing duplicate pattern vars to compile for patterns as a part of multi-statement closures.
1 parent 6a7878b commit 043b192

File tree

6 files changed

+62
-19
lines changed

6 files changed

+62
-19
lines changed

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ static void checkForEmptyOptionSet(const VarDecl *VD) {
438438
}
439439

440440
template<typename T>
441-
static void diagnoseDuplicateDecls(const T &decls) {
441+
static void diagnoseDuplicateDecls(T &&decls) {
442442
llvm::SmallDenseMap<DeclBaseName, const ValueDecl *> names;
443443
for (auto *current : decls) {
444444
if (!current->hasName() || current->isImplicit())
@@ -453,6 +453,14 @@ static void diagnoseDuplicateDecls(const T &decls) {
453453
current->getName()), [&]() {
454454
other->diagnose(diag::invalid_redecl_prev, other->getName());
455455
});
456+
457+
// Mark the decl as invalid, unless it's a GenericTypeParamDecl, which is
458+
// expected to maintain its type of GenericTypeParamType.
459+
// This is needed to avoid emitting a duplicate diagnostic when running
460+
// redeclaration checking in the case where the VarDecl is part of the
461+
// enclosing context, e.g `let (x, x) = (0, 0)`.
462+
if (!isa<GenericTypeParamDecl>(current))
463+
current->setInvalid();
456464
}
457465
}
458466
}

lib/Sema/TypeCheckPattern.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,8 @@ Pattern *TypeChecker::resolvePattern(Pattern *P, DeclContext *DC,
662662
bool isStmtCondition) {
663663
P = ResolvePattern(DC).visit(P);
664664

665+
TypeChecker::diagnoseDuplicateBoundVars(P);
666+
665667
// If the entire pattern is "(pattern_expr (type_expr SomeType))", then this
666668
// is an invalid pattern. If it were actually a value comparison (with ~=)
667669
// then the metatype would have had to be spelled with "SomeType.self". What

lib/Sema/TypeCheckStmt.cpp

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -837,8 +837,6 @@ bool TypeChecker::typeCheckStmtConditionElement(StmtConditionElement &elt,
837837
}
838838
elt.setPattern(pattern);
839839

840-
TypeChecker::diagnoseDuplicateBoundVars(pattern);
841-
842840
// Check the pattern, it allows unspecified types because the pattern can
843841
// provide type information.
844842
auto contextualPattern = ContextualPattern::forRawPattern(pattern, dc);
@@ -1271,8 +1269,6 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
12711269
if (TypeChecker::typeCheckForEachBinding(DC, S))
12721270
return nullptr;
12731271

1274-
TypeChecker::diagnoseDuplicateBoundVars(S->getPattern());
1275-
12761272
// Type-check the body of the loop.
12771273
auto sourceFile = DC->getParentSourceFile();
12781274
checkLabeledStmtShadowing(getASTContext(), sourceFile, S);
@@ -1339,13 +1335,11 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
13391335
}
13401336
labelItem.setPattern(pattern, /*resolved=*/true);
13411337

1342-
TypeChecker::diagnoseDuplicateBoundVars(pattern);
1343-
13441338
// Otherwise for each variable in the pattern, make sure its type is
13451339
// identical to the initial case decl and stash the previous case decl as
13461340
// the parent of the decl.
13471341
pattern->forEachVariable([&](VarDecl *vd) {
1348-
if (!vd->hasName())
1342+
if (!vd->hasName() || vd->isInvalid())
13491343
return;
13501344

13511345
// We know that prev var decls matches the initial var decl. So if we can

test/Parse/matching_patterns.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ case 1 + (_): // expected-error{{'_' can only appear in a pattern or on the left
4747
}
4848

4949
switch (x,x) {
50-
case (var a, var a): // expected-error {{invalid redeclaration of 'a'}} expected-note {{'a' previously declared here}} expected-warning {{variable 'a' was never used; consider replacing with '_' or removing it}} expected-warning {{variable 'a' was never used; consider replacing with '_' or removing it}}
50+
case (var a, var a): // expected-error {{invalid redeclaration of 'a'}} expected-note {{'a' previously declared here}}
5151
fallthrough
5252
case _: // expected-warning {{case is already handled by previous patterns; consider removing it}}
5353
()

test/Sema/redeclaration-checking.swift

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,36 +63,30 @@ func stmtTest() {
6363
if case (let x, let x)? = n {}
6464
// expected-note@-1 {{'x' previously declared here}}
6565
// expected-error@-2 {{invalid redeclaration of 'x'}}
66-
// expected-warning@-3 2{{never used}}
6766

6867
for case (let x, let x) in [(Int, Int)]() {}
6968
// expected-note@-1 {{'x' previously declared here}}
7069
// expected-error@-2 {{invalid redeclaration of 'x'}}
71-
// expected-warning@-3 2{{never used}}
7270

7371
switch n {
7472
case (let x, let x)?: _ = ()
7573
// expected-note@-1 {{'x' previously declared here}}
7674
// expected-error@-2 {{invalid redeclaration of 'x'}}
77-
// expected-warning@-3 2{{never used}}
7875
case nil: _ = ()
7976
}
8077

8178
while case (let x, let x)? = n {}
8279
// expected-note@-1 {{'x' previously declared here}}
8380
// expected-error@-2 {{invalid redeclaration of 'x'}}
84-
// expected-warning@-3 2{{never used}}
8581

8682
guard case (let x, let x)? = n else {}
8783
// expected-note@-1 {{'x' previously declared here}}
8884
// expected-error@-2 {{invalid redeclaration of 'x'}}
89-
// expected-warning@-3 2{{never used}}
9085

9186
do {} catch MyError.error(let x, let x) {}
9287
// expected-note@-1 {{'x' previously declared here}}
9388
// expected-error@-2 {{invalid redeclaration of 'x'}}
94-
// expected-warning@-3 2{{never used}}
95-
// expected-warning@-4 {{unreachable}}
89+
// expected-warning@-3 {{unreachable}}
9690
}
9791

9892
func fullNameTest() {
@@ -111,4 +105,51 @@ protocol SillyProtocol {
111105

112106
subscript(x: Int, x: Int) -> Int { get }
113107
subscript(a x: Int, b x: Int) -> Int { get }
114-
}
108+
}
109+
110+
// https://github.com/apple/swift/issues/63750
111+
let issue63750 = {
112+
for (x,x) in [(0,0)] {}
113+
// expected-error@-1 {{invalid redeclaration of 'x'}}
114+
// expected-note@-2 {{'x' previously declared here}}
115+
116+
if case let (x,x) = (0,0) {}
117+
// expected-error@-1 {{invalid redeclaration of 'x'}}
118+
// expected-note@-2 {{'x' previously declared here}}
119+
120+
switch (0,0) {
121+
case let (x,x):
122+
// expected-error@-1 {{invalid redeclaration of 'x'}}
123+
// expected-note@-2 {{'x' previously declared here}}
124+
()
125+
}
126+
// FIXME: We should really say 'let' instead of 'var' in "'var' binding pattern cannot appear in an expression"
127+
// https://github.com/apple/swift/issues/63993
128+
func bar(_ x: Int) -> Int { x }
129+
if case (bar(let x), let x) = (0,0) {}
130+
// expected-error@-1 {{'var' binding pattern cannot appear in an expression}}
131+
// expected-error@-2 {{invalid redeclaration of 'x'}}
132+
// expected-note@-3 {{'x' previously declared here}}
133+
}
134+
func issue63750fn() {
135+
// Make sure the behavior is consistent with the multi-statement closure case.
136+
for (x,x) in [(0,0)] {}
137+
// expected-error@-1 {{invalid redeclaration of 'x'}}
138+
// expected-note@-2 {{'x' previously declared here}}
139+
140+
if case let (x,x) = (0,0) {} // expected-warning {{'if' condition is always true}}
141+
// expected-error@-1 {{invalid redeclaration of 'x'}}
142+
// expected-note@-2 {{'x' previously declared here}}
143+
144+
switch (0,0) {
145+
case let (x,x):
146+
// expected-error@-1 {{invalid redeclaration of 'x'}}
147+
// expected-note@-2 {{'x' previously declared here}}
148+
()
149+
}
150+
func bar(_ x: Int) -> Int { x }
151+
if case (bar(let x), let x) = (0,0) {}
152+
// expected-error@-1 {{'var' binding pattern cannot appear in an expression}}
153+
// expected-error@-2 {{invalid redeclaration of 'x'}}
154+
// expected-note@-3 {{'x' previously declared here}}
155+
}

test/expr/closure/closures.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,6 @@ func testCaptureBehavior(_ ptr : SomeClass) {
331331
let v2 : SomeClass = ptr
332332

333333
doStuff { [weak v1] in v1!.foo() }
334-
// expected-warning @+2 {{variable 'v1' was written to, but never read}}
335334
doStuff { [weak v1, // expected-note {{previous}}
336335
weak v1] in v1!.foo() } // expected-error {{invalid redeclaration of 'v1'}}
337336
doStuff { [unowned v2] in v2.foo() }
@@ -340,7 +339,6 @@ func testCaptureBehavior(_ ptr : SomeClass) {
340339
doStuff { [weak v1, weak v2] in v1!.foo() + v2!.foo() }
341340

342341
let i = 42
343-
// expected-warning @+1 {{variable 'i' was never mutated}}
344342
doStuff { [weak i] in i! } // expected-error {{'weak' may only be applied to class and class-bound protocol types, not 'Int'}}
345343
}
346344

0 commit comments

Comments
 (0)