Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1586,9 +1586,9 @@ WARNING(coercion_may_fail_warning,none,
"coercion from %0 to %1 may fail; use 'as?' or 'as!' instead",
(Type, Type))

WARNING(tuple_label_mismatch_warning,none,
"tuple conversion from %0 to %1 mismatches labels",
(Type, Type))
ERROR(tuple_label_mismatch,none,
"tuple conversion from %0 to %1 mismatches labels",
(Type, Type))

ERROR(missing_explicit_conversion,none,
"%0 is not implicitly convertible to %1; "
Expand Down Expand Up @@ -7833,9 +7833,14 @@ ERROR(result_builder_buildpartialblock_accumulated_not_accessible,none,
// MARK: Tuple Shuffle Diagnostics
//------------------------------------------------------------------------------

WARNING(warn_reordering_tuple_shuffle_deprecated,Deprecation,
"expression shuffles the elements of this tuple; "
"this behavior is deprecated", ())
ERROR(reordering_tuple_shuffle,none,
"cannot implicitly reorder tuple elements from '%0' to '%1'",
(StringRef, StringRef))

WARNING(warn_reordering_tuple_shuffle_deprecated,Deprecation,
"implicit reordering of tuple elements from '%0' to '%1' is deprecated"
"; this will be an error in a future Swift language mode",
(StringRef, StringRef))

//------------------------------------------------------------------------------
// MARK: Implicit conversion diagnostics
Expand Down
3 changes: 2 additions & 1 deletion include/swift/Sema/CSFix.h
Original file line number Diff line number Diff line change
Expand Up @@ -3458,7 +3458,8 @@ class AllowInvalidStaticMemberRefOnProtocolMetatype final
}
};

/// Emit a warning for mismatched tuple labels.
/// Emit a warning for mismatched tuple labels, which is upgraded to an error
/// for a future language mode.
class AllowTupleLabelMismatch final : public ContextualMismatch {
AllowTupleLabelMismatch(ConstraintSystem &cs, Type fromType, Type toType,
ConstraintLocator *locator)
Expand Down
30 changes: 26 additions & 4 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5818,22 +5818,23 @@ Expr *ExprRewriter::coerceTupleToTuple(Expr *expr,

// Convert each OpaqueValueExpr to the correct type.
SmallVector<Expr *, 4> converted;
SmallVector<Identifier, 4> origLabels;
SmallVector<Identifier, 4> labels;
SmallVector<TupleTypeElt, 4> convertedElts;

bool anythingShuffled = false;
for (unsigned i = 0, e = sources.size(); i != e; ++i) {
unsigned source = sources[i];
auto *fromElt = destructured[source];
auto fromLabel = fromTuple->getElement(i).getName();

// Actually convert the source element.
auto toEltType = toTuple->getElementType(i);
auto toLabel = toTuple->getElement(i).getName();

// If we're shuffling positions and labels, we have to warn about this
// conversion.
if (i != sources[i] &&
fromTuple->getElement(i).getName() != toLabel)
if (i != sources[i] && fromLabel != toLabel)
anythingShuffled = true;

auto *toElt
Expand All @@ -5845,15 +5846,36 @@ Expr *ExprRewriter::coerceTupleToTuple(Expr *expr,

converted.push_back(toElt);
labels.push_back(toLabel);
origLabels.push_back(fromLabel);
convertedElts.emplace_back(toEltType, toLabel);
}

// Shuffling tuple elements is an anti-pattern worthy of a diagnostic. We
// will form the shuffle for now, but a future compiler should decline to
// do so and begin the process of removing them altogether.
if (anythingShuffled) {
ctx.Diags.diagnose(
expr->getLoc(), diag::warn_reordering_tuple_shuffle_deprecated);
auto concatLabels = [](SmallVectorImpl<Identifier> &labels,
SmallVectorImpl<char> &out) {
llvm::raw_svector_ostream OS(out);
for (auto label : labels) {
DeclName(label).print(OS, /*skipEmpty*/ false, /*escapeIfNeeded*/ true);
OS << ':';
}
};
SmallString<16> fromLabelStr;
concatLabels(origLabels, fromLabelStr);
SmallString<16> toLabelStr;
concatLabels(labels, toLabelStr);

using namespace version;
if (ctx.isSwiftVersionAtLeast(Version::getFutureMajorLanguageVersion())) {
ctx.Diags.diagnose(expr->getLoc(), diag::reordering_tuple_shuffle,
fromLabelStr, toLabelStr);
} else {
ctx.Diags.diagnose(expr->getLoc(),
diag::warn_reordering_tuple_shuffle_deprecated,
fromLabelStr, toLabelStr);
}
}

// Create the result tuple, written in terms of the destructured
Expand Down
5 changes: 3 additions & 2 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9419,8 +9419,9 @@ bool InvalidWeakAttributeUse::diagnoseAsError() {
}

bool TupleLabelMismatchWarning::diagnoseAsError() {
emitDiagnostic(diag::tuple_label_mismatch_warning, getFromType(), getToType())
.highlight(getSourceRange());
emitDiagnostic(diag::tuple_label_mismatch, getFromType(), getToType())
.highlight(getSourceRange())
.warnUntilFutureSwiftVersion();
return true;
}

Expand Down
3 changes: 2 additions & 1 deletion lib/Sema/CSDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -3009,7 +3009,8 @@ class InvalidWeakAttributeUse final : public FailureDiagnostic {
bool diagnoseAsError() override;
};

/// Emit a warning for mismatched tuple labels.
/// Emit a warning for mismatched tuple labels, which is upgraded to an error
/// for a future language mode.
class TupleLabelMismatchWarning final : public ContextualFailure {
public:
TupleLabelMismatchWarning(const Solution &solution, Type fromType,
Expand Down
21 changes: 0 additions & 21 deletions test/Constraints/tuple.swift
Original file line number Diff line number Diff line change
Expand Up @@ -342,27 +342,6 @@ optionalTuple = (bignum, 1) // expected-error {{cannot assign value of type '(In
optionalTuple = optionalTuple2 // expected-error {{cannot assign value of type '(Int64, Int)?' to type '(Int, Int)?'}}
// expected-note@-1 {{arguments to generic parameter 'Wrapped' ('(Int64, Int)' and '(Int, Int)') are expected to be equal}}

func testTupleLabelMismatchFuncConversion(fn1: @escaping ((x: Int, y: Int)) -> Void,
fn2: @escaping () -> (x: Int, Int)) {
// Warn on mismatches
let _: ((a: Int, b: Int)) -> Void = fn1 // expected-warning {{tuple conversion from '(a: Int, b: Int)' to '(x: Int, y: Int)' mismatches labels}}
let _: ((x: Int, b: Int)) -> Void = fn1 // expected-warning {{tuple conversion from '(x: Int, b: Int)' to '(x: Int, y: Int)' mismatches labels}}

let _: () -> (y: Int, Int) = fn2 // expected-warning {{tuple conversion from '(x: Int, Int)' to '(y: Int, Int)' mismatches labels}}
let _: () -> (y: Int, k: Int) = fn2 // expected-warning {{tuple conversion from '(x: Int, Int)' to '(y: Int, k: Int)' mismatches labels}}

// Attempting to shuffle has always been illegal here
let _: () -> (y: Int, x: Int) = fn2 // expected-error {{cannot convert value of type '() -> (x: Int, Int)' to specified type '() -> (y: Int, x: Int)'}}

// Losing labels is okay though.
let _: () -> (Int, Int) = fn2

// Gaining labels also okay.
let _: ((x: Int, Int)) -> Void = fn1
let _: () -> (x: Int, y: Int) = fn2
let _: () -> (Int, y: Int) = fn2
}

func testTupleLabelMismatchKeyPath() {
// FIXME: The warning should be upgraded to an error for key paths.
let _: KeyPath<(x: Int, y: Int), Int> = \(a: Int, b: Int).x
Expand Down
81 changes: 50 additions & 31 deletions test/Constraints/tuple_shuffle.swift
Original file line number Diff line number Diff line change
@@ -1,31 +1,50 @@
// RUN: %target-typecheck-verify-swift -swift-version 5

func consume<T>(_ x: T) {} // Suppress unused variable warnings

func shuffle_through_initialization() {
let a = (x: 1, y: 2)
let b: (y: Int, x: Int)
b = a // expected-warning {{expression shuffles the elements of this tuple}}
consume(b)
}

func shuffle_through_destructuring() {
let a = (x: 1, y: 2)
let (y: b, x: c) = a // expected-warning {{expression shuffles the elements of this tuple}}
consume((b, c))
}

func shuffle_through_call() {
func foo(_ : (x: Int, y: Int)) {}
foo((y: 5, x: 10)) // expected-warning {{expression shuffles the elements of this tuple}}
}

func shuffle_through_cast() {
let x = ((a: Int(), b: Int()) as (b: Int, a: Int)).0 // expected-warning {{expression shuffles the elements of this tuple}}

// Ah, the famous double-shuffle
let (c1, (c2, c3)): (c: Int, (b: Int, a: Int)) = ((a: Int(), b: Int()), c: Int())
// expected-warning@-1 {{expression shuffles the elements of this tuple}}
// expected-warning@-2 {{expression shuffles the elements of this tuple}}
consume((x, c1, c2, c3))
}
// RUN: %target-typecheck-verify-swift -swift-version 6 -verify-additional-prefix swift6-
// RUN: %target-typecheck-verify-swift -swift-version 7 -verify-additional-prefix swift7-

// REQUIRES: swift7

func consume<T>(_ x: T) {} // Suppress unused variable warnings

func shuffle_through_initialization() {
let a = (x: 1, y: 2)
let b: (y: Int, x: Int)
b = a
// expected-swift6-warning@-1 {{implicit reordering of tuple elements from 'x:y:' to 'y:x:' is deprecated; this will be an error in a future Swift language mode}}
// expected-swift7-error@-2 {{cannot implicitly reorder tuple elements from 'x:y:' to 'y:x:'}}
consume(b)
}

func shuffle_raw_label(_ t: (`a b`: Int, `c d`: Int)) {
let _: (`c d`: Int, `a b`: Int) = t
// expected-swift6-warning@-1 {{implicit reordering of tuple elements from '`a b`:`c d`:' to '`c d`:`a b`:' is deprecated; this will be an error in a future Swift language mode}}
// expected-swift7-error@-2 {{cannot implicitly reorder tuple elements from '`a b`:`c d`:' to '`c d`:`a b`:'}}
}

func shuffle_through_destructuring() {
let a = (x: 1, y: 2)
let (y: b, x: c) = a
// expected-swift6-warning@-1 {{implicit reordering of tuple elements from 'x:y:' to 'y:x:' is deprecated; this will be an error in a future Swift language mode}}
// expected-swift7-error@-2 {{cannot implicitly reorder tuple elements from 'x:y:' to 'y:x:'}}
consume((b, c))
}

func shuffle_through_call() {
func foo(_ : (x: Int, y: Int)) {}
foo((y: 5, x: 10))
// expected-swift6-warning@-1 {{implicit reordering of tuple elements from 'y:x:' to 'x:y:' is deprecated; this will be an error in a future Swift language mode}}
// expected-swift7-error@-2 {{cannot implicitly reorder tuple elements from 'y:x:' to 'x:y:'}}
}

func shuffle_through_cast() {
let x = ((a: Int(), b: Int()) as (b: Int, a: Int)).0
// expected-swift6-warning@-1 {{implicit reordering of tuple elements from 'a:b:' to 'b:a:' is deprecated; this will be an error in a future Swift language mode}}
// expected-swift7-error@-2 {{cannot implicitly reorder tuple elements from 'a:b:' to 'b:a:'}}

// Ah, the famous double-shuffle
let (c1, (c2, c3)): (c: Int, (b: Int, a: Int)) = ((a: Int(), b: Int()), c: Int())
// expected-swift6-warning@-1 {{implicit reordering of tuple elements from 'a:b:' to 'b:a:' is deprecated; this will be an error in a future Swift language mode}}
// expected-swift6-warning@-2 {{implicit reordering of tuple elements from '_:c:' to 'c:_:' is deprecated; this will be an error in a future Swift language mode}}
// expected-swift7-error@-3 {{cannot implicitly reorder tuple elements from 'a:b:' to 'b:a:'}}
// expected-swift7-error@-4 {{cannot implicitly reorder tuple elements from '_:c:' to 'c:_:'}}
consume((x, c1, c2, c3))
}
33 changes: 33 additions & 0 deletions test/Constraints/tuple_subtype_label_mismatch.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// RUN: %target-typecheck-verify-swift -language-mode 6 -verify-additional-prefix swift6-
// RUN: %target-typecheck-verify-swift -language-mode 7 -verify-additional-prefix swift7-
// REQUIRES: swift7

func testTupleLabelMismatchFuncConversion(fn1: @escaping ((x: Int, y: Int)) -> Void,
fn2: @escaping () -> (x: Int, Int)) {
// Warn on mismatches in Swift 6, upgrading to an error for Swift 7
let _: ((a: Int, b: Int)) -> Void = fn1
// expected-swift6-warning@-1 {{tuple conversion from '(a: Int, b: Int)' to '(x: Int, y: Int)' mismatches labels}}
// expected-swift7-error@-2 {{tuple conversion from '(a: Int, b: Int)' to '(x: Int, y: Int)' mismatches labels}}
let _: ((x: Int, b: Int)) -> Void = fn1
// expected-swift6-warning@-1 {{tuple conversion from '(x: Int, b: Int)' to '(x: Int, y: Int)' mismatches labels}}
// expected-swift7-error@-2 {{tuple conversion from '(x: Int, b: Int)' to '(x: Int, y: Int)' mismatches labels}}

let _: () -> (y: Int, Int) = fn2
// expected-swift6-warning@-1 {{tuple conversion from '(x: Int, Int)' to '(y: Int, Int)' mismatches labels}}
// expected-swift7-error@-2 {{tuple conversion from '(x: Int, Int)' to '(y: Int, Int)' mismatches labels}}
let _: () -> (y: Int, k: Int) = fn2
// expected-swift6-warning@-1 {{tuple conversion from '(x: Int, Int)' to '(y: Int, k: Int)' mismatches labels}}
// expected-swift7-error@-2 {{tuple conversion from '(x: Int, Int)' to '(y: Int, k: Int)' mismatches labels}}

// Attempting to shuffle has always been illegal here
let _: () -> (y: Int, x: Int) = fn2
// expected-error@-1 {{cannot convert value of type '() -> (x: Int, Int)' to specified type '() -> (y: Int, x: Int)'}}

// Losing labels is okay though.
let _: () -> (Int, Int) = fn2

// Gaining labels also okay.
let _: ((x: Int, Int)) -> Void = fn1
let _: () -> (x: Int, y: Int) = fn2
let _: () -> (Int, y: Int) = fn2
}
2 changes: 1 addition & 1 deletion test/Parse/omit_return.swift
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ func ff_implicitInjectIntoOptionalExpr(_ int: Int) -> Int? {
}

func ff_implicitTupleShuffle(_ input: (one: Int, two: Int)) -> (two: Int, one: Int) {
input // expected-warning {{expression shuffles the elements of this tuple; this behavior is deprecated}}
input // expected-warning {{implicit reordering of tuple elements from 'one:two:' to 'two:one:' is deprecated; this will be an error in a future Swift language mode}}
}

func ff_implicitCollectionUpcast(_ derived: [Derived]) -> [Base] {
Expand Down
2 changes: 1 addition & 1 deletion test/Parse/omit_return_ifdecl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,7 @@ func ff_implicitInjectIntoOptionalExpr(_ int: Int) -> Int? {

func ff_implicitTupleShuffle(_ input: (one: Int, two: Int)) -> (two: Int, one: Int) {
#if true
input // expected-warning {{expression shuffles the elements of this tuple; this behavior is deprecated}}
input // expected-warning {{implicit reordering of tuple elements from 'one:two:' to 'two:one:' is deprecated; this will be an error in a future Swift language mode}}
#endif
}

Expand Down
6 changes: 3 additions & 3 deletions test/Sema/diag_unowned_immediate_deallocation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -452,10 +452,10 @@ func testGenericWeakClassDiag() {
// The diagnostic doesn't currently support tuple shuffles.
func testDontDiagnoseThroughTupleShuffles() {
unowned let (c1, (c2, c3)): (c: C, (b: C, a: C)) = ((a: D(), b: C()), c: D())
// expected-warning@-1 {{expression shuffles the elements of this tuple; this behavior is deprecated}}
// expected-warning@-2 {{expression shuffles the elements of this tuple; this behavior is deprecated}}
// expected-warning@-1 {{implicit reordering of tuple elements from 'a:b:' to 'b:a:' is deprecated; this will be an error in a future Swift language mode}}
// expected-warning@-2 {{implicit reordering of tuple elements from '_:c:' to 'c:_:' is deprecated; this will be an error in a future Swift language mode}}
unowned let c4 = ((a: C(), b: C()) as (b: C, a: C)).0
// expected-warning@-1 {{expression shuffles the elements of this tuple; this behavior is deprecated}}
// expected-warning@-1 {{implicit reordering of tuple elements from 'a:b:' to 'b:a:' is deprecated; this will be an error in a future Swift language mode}}

_ = c1; _ = c2; _ = c3; _ = c4
}
Expand Down
2 changes: 1 addition & 1 deletion test/decl/func/operator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ var f2 : (Int) -> Int = (+-+)
var f3 : (inout Int) -> Int = (-+-) // expected-error{{ambiguous use of operator '-+-'}}
var f4 : (inout Int, Int) -> Int = (+-+=)
var r5 : (a : (Int, Int) -> Int, b : (Int, Int) -> Int) = (+, -)
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}}
var r6 : (a : (Int, Int) -> Int, b : (Int, Int) -> Int) = (b : +, a : -) // expected-warning {{implicit reordering of tuple elements from 'b:a:' to 'a:b:' is deprecated; this will be an error in a future Swift language mode}}

struct f6_S {
subscript(op : (Int, Int) -> Int) -> Int {
Expand Down
2 changes: 1 addition & 1 deletion test/expr/closure/closures.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func funcdecl5(_ a: Int, _ y: Int) {
var b = a.1+a.f

// Tuple expressions with named elements.
var i : (y : Int, x : Int) = (x : 42, y : 11) // expected-warning {{expression shuffles the elements of this tuple; this behavior is deprecated}}
var i : (y : Int, x : Int) = (x : 42, y : 11) // expected-warning {{implicit reordering of tuple elements from 'x:y:' to 'y:x:' is deprecated; this will be an error in a future Swift language mode}}
funcdecl1(123, 444)

// Calls.
Expand Down
2 changes: 1 addition & 1 deletion test/expr/expressions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ func test5() {


let c: (a: Int, b: Int) = (1,2)
let _: (b: Int, a: Int) = c // expected-warning {{expression shuffles the elements of this tuple; this behavior is deprecated}}
let _: (b: Int, a: Int) = c // expected-warning {{implicit reordering of tuple elements from 'a:b:' to 'b:a:' is deprecated; this will be an error in a future Swift language mode}}
}


Expand Down