Skip to content

Commit e0658a3

Browse files
authored
Merge pull request #24774 from theblixguy/fix/duplicate-tuple-labels
[Typechecker] Ban tuples with duplicate labels
2 parents d744ab0 + 8badb5f commit e0658a3

File tree

7 files changed

+118
-7
lines changed

7 files changed

+118
-7
lines changed

CHANGELOG.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,28 @@ CHANGELOG
2626
Swift Next
2727
----------
2828

29+
* [SR-8974][]:
30+
31+
Duplicate tuple element labels are no longer allowed, because it leads
32+
to incorrect behavior. For example:
33+
34+
```
35+
let dupLabels: (foo: Int, foo: Int) = (foo: 1, foo: 2)
36+
37+
enum Foo { case bar(x: Int, x: Int) }
38+
let f: Foo = .bar(x: 0, x: 1)
39+
```
40+
41+
will now be diagnosed as an error.
42+
43+
Note: You can still use duplicate labels when declaring functions and
44+
subscripts, as long as the internal labels are different. For example:
45+
46+
```
47+
func foo(bar x: Int, bar y: Int) {}
48+
subscript(a x: Int, a y: Int) -> Int {}
49+
```
50+
2951
* [SR-6118][]:
3052

3153
Subscripts can now declare default arguments:
@@ -7696,5 +7718,6 @@ Swift 1.0
76967718
[SR-7799]: <https://bugs.swift.org/browse/SR-7799>
76977719
[SR-8109]: <https://bugs.swift.org/browse/SR-8109>
76987720
[SR-8546]: <https://bugs.swift.org/browse/SR-8546>
7721+
[SR-8974]: <https://bugs.swift.org/browse/SR-8974>
76997722
[SR-9043]: <https://bugs.swift.org/browse/SR-9043>
77007723
[SR-9827]: <https://bugs.swift.org/browse/SR-9827>

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3523,6 +3523,8 @@ ERROR(tuple_single_element,none,
35233523
"cannot create a single-element tuple with an element label", ())
35243524
ERROR(tuple_ellipsis,none,
35253525
"cannot create a variadic tuple", ())
3526+
ERROR(tuple_duplicate_label,none,
3527+
"cannot create a tuple with a duplicate element label", ())
35263528
ERROR(enum_element_ellipsis,none,
35273529
"variadic enum cases are not supported", ())
35283530

lib/Sema/MiscDiagnostics.cpp

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E,
6464
class DiagnoseWalker : public ASTWalker {
6565
SmallPtrSet<Expr*, 4> AlreadyDiagnosedMetatypes;
6666
SmallPtrSet<DeclRefExpr*, 4> AlreadyDiagnosedBitCasts;
67-
68-
// Keep track of acceptable DiscardAssignmentExpr's.
67+
68+
/// Keep track of acceptable DiscardAssignmentExpr's.
6969
SmallPtrSet<DiscardAssignmentExpr*, 2> CorrectDiscardAssignmentExprs;
7070

7171
/// Keep track of the arguments to CallExprs.
@@ -245,6 +245,42 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E,
245245
}
246246
}
247247

248+
// Diagnose tuple expressions with duplicate element label
249+
if (auto *tupleExpr = dyn_cast<TupleExpr>(E)) {
250+
// FIXME: Duplicate labels on enum payloads should be diagnosed
251+
// when declared, not when called.
252+
bool isEnumCase = false;
253+
if (auto CE = dyn_cast_or_null<CallExpr>(Parent.getAsExpr())) {
254+
auto calledValue = CE->getCalledValue();
255+
if (calledValue) {
256+
isEnumCase = isa<EnumElementDecl>(calledValue);
257+
}
258+
}
259+
260+
if ((!CallArgs.count(tupleExpr)) || isEnumCase) {
261+
auto diagnose = false;
262+
263+
llvm::SmallDenseSet<Identifier> names;
264+
names.reserve(tupleExpr->getNumElements());
265+
266+
for (auto name : tupleExpr->getElementNames()) {
267+
if (name.empty())
268+
continue;
269+
270+
if (names.count(name) == 1) {
271+
diagnose = true;
272+
break;
273+
}
274+
275+
names.insert(name);
276+
}
277+
278+
if (diagnose) {
279+
TC.diagnose(tupleExpr->getLoc(), diag::tuple_duplicate_label);
280+
}
281+
}
282+
}
283+
248284
return { true, E };
249285
}
250286

lib/Sema/TypeCheckPattern.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ class ResolvePattern : public ASTVisitor<ResolvePattern,
313313
Pattern *visitExprPattern(ExprPattern *P) {
314314
if (P->isResolved())
315315
return P;
316-
316+
317317
// Try to convert to a pattern.
318318
Pattern *exprAsPattern = visit(P->getSubExpr());
319319
// If we failed, keep the ExprPattern as is.

lib/Sema/TypeCheckType.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3145,7 +3145,10 @@ Type TypeResolver::resolveTupleType(TupleTypeRepr *repr,
31453145
TypeResolutionOptions options) {
31463146
SmallVector<TupleTypeElt, 8> elements;
31473147
elements.reserve(repr->getNumElements());
3148-
3148+
3149+
llvm::SmallDenseSet<Identifier> seenEltNames;
3150+
seenEltNames.reserve(repr->getNumElements());
3151+
31493152
auto elementOptions = options;
31503153
if (!repr->isParenType()) {
31513154
elementOptions = elementOptions.withoutContext(true);
@@ -3160,14 +3163,26 @@ Type TypeResolver::resolveTupleType(TupleTypeRepr *repr,
31603163
}
31613164

31623165
bool hadError = false;
3166+
bool foundDupLabel = false;
31633167
for (unsigned i = 0, end = repr->getNumElements(); i != end; ++i) {
31643168
auto *tyR = repr->getElementType(i);
31653169

31663170
Type ty = resolveType(tyR, elementOptions);
31673171
if (!ty || ty->hasError())
31683172
hadError = true;
31693173

3170-
elements.emplace_back(ty, repr->getElementName(i), ParameterTypeFlags());
3174+
auto eltName = repr->getElementName(i);
3175+
3176+
elements.emplace_back(ty, eltName, ParameterTypeFlags());
3177+
3178+
if (eltName.empty())
3179+
continue;
3180+
3181+
if (seenEltNames.count(eltName) == 1) {
3182+
foundDupLabel = true;
3183+
}
3184+
3185+
seenEltNames.insert(eltName);
31713186
}
31723187

31733188
if (hadError)
@@ -3186,6 +3201,11 @@ Type TypeResolver::resolveTupleType(TupleTypeRepr *repr,
31863201
elements[0] = TupleTypeElt(elements[0].getType());
31873202
}
31883203

3204+
// Tuples with duplicate element labels are not permitted
3205+
if (foundDupLabel) {
3206+
diagnose(repr->getLoc(), diag::tuple_duplicate_label);
3207+
}
3208+
31893209
return TupleType::get(elements, Context);
31903210
}
31913211

test/Constraints/tuple.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,3 +289,33 @@ func singleElementTuple() {
289289
let _ = ((label: 123)) // expected-error {{cannot create a single-element tuple with an element label}} {{13-20=}}
290290
let _ = ((label: 123)).label // expected-error {{cannot create a single-element tuple with an element label}} {{13-20=}}
291291
}
292+
293+
// Tuples with duplicate labels
294+
295+
let dupLabel1: (foo: Int, foo: Int) = (foo: 1, foo: 2) // expected-error 2{{cannot create a tuple with a duplicate element label}}
296+
297+
func dupLabel2(x a: Int, x b: Int) -> (y: Int, y: Int) { // expected-error {{cannot create a tuple with a duplicate element label}}
298+
return (a, b)
299+
}
300+
301+
let _ = (bar: 0, bar: "") // expected-error {{cannot create a tuple with a duplicate element label}}
302+
303+
let zeroTuple = (0,0)
304+
305+
if case (foo: let x, foo: let y) = zeroTuple { print(x+y) } // expected-error {{cannot create a tuple with a duplicate element label}}
306+
// expected-warning@-1 {{'if' condition is always true}}
307+
308+
enum BishBash { case bar(foo: Int, foo: String) }
309+
let enumLabelDup: BishBash = .bar(foo: 0, foo: "") // expected-error {{cannot create a tuple with a duplicate element label}}
310+
311+
func dupLabelClosure(_ fn: () -> Void) {}
312+
dupLabelClosure { print((bar: "", bar: 5).bar) } // expected-error {{cannot create a tuple with a duplicate element label}}
313+
314+
struct DupLabelSubscript {
315+
subscript(foo x: Int, foo y: Int) -> Int {
316+
return 0
317+
}
318+
}
319+
320+
let dupLabelSubscriptStruct = DupLabelSubscript()
321+
let _ = dupLabelSubscriptStruct[foo: 5, foo: 5] // ok

test/Parse/strange_interpolation.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ print("[\(x, foo: x)]")
3434
// expected-warning@-2{{interpolating multiple values will not form a tuple in Swift 5}}
3535
// expected-note@-3{{insert parentheses to keep current behavior}} {{11-11=(}} {{20-20=)}}
3636

37-
print("[\(foo: x, foo: x)]")
38-
// CHECK-NEXT: [(foo: 1, foo: 1)]
37+
print("[\(foo: x, bar: x)]")
38+
// CHECK-NEXT: [(foo: 1, bar: 1)]
3939
// expected-warning@-2{{interpolating multiple values will not form a tuple in Swift 5}}
4040
// expected-note@-3{{insert parentheses to keep current behavior}} {{11-11=(}} {{25-25=)}}
4141

0 commit comments

Comments
 (0)