Skip to content

Commit 1f867ed

Browse files
authored
Merge pull request #29833 from CodaFi/do-the-cupid-shuffle
[Sema] Warn About Tuple Shuffles
2 parents 04d47dc + a9e871a commit 1f867ed

File tree

8 files changed

+61
-4
lines changed

8 files changed

+61
-4
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4900,6 +4900,14 @@ NOTE(function_builder_remove_attr, none,
49004900
NOTE(function_builder_remove_returns, none,
49014901
"remove 'return' statements to apply the function builder", ())
49024902

4903+
//------------------------------------------------------------------------------
4904+
// MARK: Tuple Shuffle Diagnostics
4905+
//------------------------------------------------------------------------------
4906+
4907+
WARNING(warn_reordering_tuple_shuffle_deprecated,none,
4908+
"expression shuffles the elements of this tuple; "
4909+
"this behavior is deprecated", ())
4910+
49034911
//------------------------------------------------------------------------------
49044912
// MARK: differentiable programming diagnostics
49054913
//------------------------------------------------------------------------------

lib/Sema/CSApply.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4736,6 +4736,7 @@ Expr *ExprRewriter::coerceTupleToTuple(Expr *expr,
47364736
SmallVector<Identifier, 4> labels;
47374737
SmallVector<TupleTypeElt, 4> convertedElts;
47384738

4739+
bool anythingShuffled = false;
47394740
for (unsigned i = 0, e = sources.size(); i != e; ++i) {
47404741
unsigned source = sources[i];
47414742
auto *fromElt = destructured[source];
@@ -4744,6 +4745,12 @@ Expr *ExprRewriter::coerceTupleToTuple(Expr *expr,
47444745
auto toEltType = toTuple->getElementType(i);
47454746
auto toLabel = toTuple->getElement(i).getName();
47464747

4748+
// If we're shuffling positions and labels, we have to warn about this
4749+
// conversion.
4750+
if (i != sources[i] &&
4751+
fromTuple->getElement(i).getName() != toLabel)
4752+
anythingShuffled = true;
4753+
47474754
auto *toElt
47484755
= coerceToType(fromElt, toEltType,
47494756
locator.withPathElement(
@@ -4756,6 +4763,14 @@ Expr *ExprRewriter::coerceTupleToTuple(Expr *expr,
47564763
convertedElts.emplace_back(toEltType, toLabel, ParameterTypeFlags());
47574764
}
47584765

4766+
// Shuffling tuple elements is an anti-pattern worthy of a diagnostic. We
4767+
// will form the shuffle for now, but a future compiler should decline to
4768+
// do so and begin the process of removing them altogether.
4769+
if (anythingShuffled) {
4770+
cs.getASTContext().Diags.diagnose(
4771+
expr->getLoc(), diag::warn_reordering_tuple_shuffle_deprecated);
4772+
}
4773+
47594774
// Create the result tuple, written in terms of the destructured
47604775
// OpaqueValueExprs.
47614776
auto *result = TupleExpr::createImplicit(ctx, converted, labels);

test/Constraints/tuple_shuffle.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 5
2+
3+
func consume<T>(_ x: T) {} // Suppress unused variable warnings
4+
5+
func shuffle_through_initialization() {
6+
let a = (x: 1, y: 2)
7+
let b: (y: Int, x: Int)
8+
b = a // expected-warning {{expression shuffles the elements of this tuple}}
9+
consume(b)
10+
}
11+
12+
func shuffle_through_destructuring() {
13+
let a = (x: 1, y: 2)
14+
let (y: b, x: c) = a // expected-warning {{expression shuffles the elements of this tuple}}
15+
consume((b, c))
16+
}
17+
18+
func shuffle_through_call() {
19+
func foo(_ : (x: Int, y: Int)) {}
20+
foo((y: 5, x: 10)) // expected-warning {{expression shuffles the elements of this tuple}}
21+
}
22+
23+
func shuffle_through_cast() {
24+
let x = ((a: Int(), b: Int()) as (b: Int, a: Int)).0 // expected-warning {{expression shuffles the elements of this tuple}}
25+
26+
// Ah, the famous double-shuffle
27+
let (c1, (c2, c3)): (c: Int, (b: Int, a: Int)) = ((a: Int(), b: Int()), c: Int())
28+
// expected-warning@-1 {{expression shuffles the elements of this tuple}}
29+
// expected-warning@-2 {{expression shuffles the elements of this tuple}}
30+
consume((x, c1, c2, c3))
31+
}

test/Parse/omit_return.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ func ff_implicitInjectIntoOptionalExpr(_ int: Int) -> Int? {
474474
}
475475

476476
func ff_implicitTupleShuffle(_ input: (one: Int, two: Int)) -> (two: Int, one: Int) {
477-
input
477+
input // expected-warning {{expression shuffles the elements of this tuple; this behavior is deprecated}}
478478
}
479479

480480
func ff_implicitCollectionUpcast(_ deriveds: [Derived]) -> [Base] {

test/Sema/diag_unowned_immediate_deallocation.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,10 @@ func testGenericWeakClassDiag() {
450450
// The diagnostic doesn't currently support tuple shuffles.
451451
func testDontDiagnoseThroughTupleShuffles() {
452452
unowned let (c1, (c2, c3)): (c: C, (b: C, a: C)) = ((a: D(), b: C()), c: D())
453+
// expected-warning@-1 {{expression shuffles the elements of this tuple; this behavior is deprecated}}
454+
// expected-warning@-2 {{expression shuffles the elements of this tuple; this behavior is deprecated}}
453455
unowned let c4 = ((a: C(), b: C()) as (b: C, a: C)).0
456+
// expected-warning@-1 {{expression shuffles the elements of this tuple; this behavior is deprecated}}
454457

455458
_ = c1; _ = c2; _ = c3; _ = c4
456459
}

test/decl/func/operator.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ var f2 : (Int) -> Int = (+-+)
132132
var f3 : (inout Int) -> Int = (-+-) // expected-error{{ambiguous use of operator '-+-'}}
133133
var f4 : (inout Int, Int) -> Int = (+-+=)
134134
var r5 : (a : (Int, Int) -> Int, b : (Int, Int) -> Int) = (+, -)
135-
var r6 : (a : (Int, Int) -> Int, b : (Int, Int) -> Int) = (b : +, a : -)
135+
var r6 : (a : (Int, Int) -> Int, b : (Int, Int) -> Int) = (b : +, a : -) // expected-warning {{expression shuffles the elements of this tuple; this behavior is deprecated}}
136136

137137
struct f6_S {
138138
subscript(op : (Int, Int) -> Int) -> Int {

test/expr/closure/closures.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func funcdecl5(_ a: Int, _ y: Int) {
5353
var b = a.1+a.f
5454

5555
// Tuple expressions with named elements.
56-
var i : (y : Int, x : Int) = (x : 42, y : 11)
56+
var i : (y : Int, x : Int) = (x : 42, y : 11) // expected-warning {{expression shuffles the elements of this tuple; this behavior is deprecated}}
5757
funcdecl1(123, 444)
5858

5959
// Calls.

test/expr/expressions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ func test5() {
193193

194194

195195
let c: (a: Int, b: Int) = (1,2)
196-
let _: (b: Int, a: Int) = c // Ok, reshuffle tuple.
196+
let _: (b: Int, a: Int) = c // expected-warning {{expression shuffles the elements of this tuple; this behavior is deprecated}}
197197
}
198198

199199

0 commit comments

Comments
 (0)