Skip to content

Commit 2cfcdf4

Browse files
authored
Merge pull request #26391 from sl/port-tuple-assignment-diagnostic
[Diagnostics] Ported tuple mismatch diagnostic to new diagnostic framework
2 parents c4c240d + 0fa9ec3 commit 2cfcdf4

File tree

9 files changed

+131
-16
lines changed

9 files changed

+131
-16
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,10 +1146,9 @@ bool FailureDiagnosis::diagnoseGeneralConversionFailure(Constraint *constraint){
11461146
if (auto fromTT = fromType->getAs<TupleType>())
11471147
if (auto toTT = toType->getAs<TupleType>()) {
11481148
if (fromTT->getNumElements() != toTT->getNumElements()) {
1149-
diagnose(anchor->getLoc(), diag::tuple_types_not_convertible_nelts,
1150-
fromTT, toTT)
1151-
.highlight(anchor->getSourceRange());
1152-
return true;
1149+
auto failure = TupleContextualFailure(anchor, CS, fromTT, toTT,
1150+
CS.getConstraintLocator(expr));
1151+
return failure.diagnoseAsError();
11531152
}
11541153

11551154
SmallVector<TupleTypeElt, 4> FromElts;
@@ -1165,10 +1164,9 @@ bool FailureDiagnosis::diagnoseGeneralConversionFailure(Constraint *constraint){
11651164
// then we have a type error.
11661165
if (computeTupleShuffle(TEType->castTo<TupleType>()->getElements(),
11671166
toTT->getElements(), sources)) {
1168-
diagnose(anchor->getLoc(), diag::tuple_types_not_convertible,
1169-
fromTT, toTT)
1170-
.highlight(anchor->getSourceRange());
1171-
return true;
1167+
auto failure = TupleContextualFailure(anchor, CS, fromTT, toTT,
1168+
CS.getConstraintLocator(expr));
1169+
return failure.diagnoseAsError();
11721170
}
11731171
}
11741172

lib/Sema/CSDiagnostics.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1954,6 +1954,14 @@ void ContextualFailure::tryComputedPropertyFixIts(Expr *expr) const {
19541954
}
19551955
}
19561956

1957+
bool TupleContextualFailure::diagnoseAsError() {
1958+
auto diagnostic = isNumElementsMismatch()
1959+
? diag::tuple_types_not_convertible_nelts
1960+
: diag::tuple_types_not_convertible;
1961+
emitDiagnostic(getAnchor()->getLoc(), diagnostic, getFromType(), getToType());
1962+
return true;
1963+
}
1964+
19571965
bool AutoClosureForwardingFailure::diagnoseAsError() {
19581966
auto path = getLocator()->getPath();
19591967
assert(!path.empty());

lib/Sema/CSDiagnostics.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,23 @@ class ContextualFailure : public FailureDiagnostic {
750750
void tryComputedPropertyFixIts(Expr *expr) const;
751751
};
752752

753+
/// Diagnose mismatches relating to tuple destructuring.
754+
class TupleContextualFailure final : public ContextualFailure {
755+
public:
756+
TupleContextualFailure(Expr *root, ConstraintSystem &cs, Type lhs, Type rhs,
757+
ConstraintLocator *locator)
758+
: ContextualFailure(root, cs, lhs, rhs, locator) {}
759+
760+
bool diagnoseAsError() override;
761+
762+
bool isNumElementsMismatch() const {
763+
auto lhsTy = getFromType()->castTo<TupleType>();
764+
auto rhsTy = getToType()->castTo<TupleType>();
765+
assert(lhsTy && rhsTy);
766+
return lhsTy->getNumElements() != rhsTy->getNumElements();
767+
}
768+
};
769+
753770
/// Diagnose situations when @autoclosure argument is passed to @autoclosure
754771
/// parameter directly without calling it first.
755772
class AutoClosureForwardingFailure final : public FailureDiagnostic {

lib/Sema/CSFix.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,20 @@ ContextualMismatch *ContextualMismatch::create(ConstraintSystem &cs, Type lhs,
227227
return new (cs.getAllocator()) ContextualMismatch(cs, lhs, rhs, locator);
228228
}
229229

230+
bool AllowTupleTypeMismatch::diagnose(Expr *root, bool asNote) const {
231+
auto failure = TupleContextualFailure(
232+
root, getConstraintSystem(), getFromType(), getToType(), getLocator());
233+
return failure.diagnose(asNote);
234+
}
235+
236+
AllowTupleTypeMismatch *
237+
AllowTupleTypeMismatch::create(ConstraintSystem &cs, Type lhs, Type rhs,
238+
ConstraintLocator *locator) {
239+
assert(lhs->is<TupleType>() && rhs->is<TupleType>() &&
240+
"lhs and rhs must be tuple types");
241+
return new (cs.getAllocator()) AllowTupleTypeMismatch(cs, lhs, rhs, locator);
242+
}
243+
230244
bool GenericArgumentsMismatch::diagnose(Expr *root, bool asNote) const {
231245
auto failure = GenericArgumentsMismatchFailure(root, getConstraintSystem(),
232246
getActual(), getRequired(),

lib/Sema/CSFix.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@ enum class FixKind : uint8_t {
137137
/// referenced constructor must be required.
138138
AllowInvalidInitRef,
139139

140+
/// Allow a tuple to be destructured with mismatched arity, or mismatched
141+
/// types.
142+
AllowTupleTypeMismatch,
143+
140144
/// Allow an invalid member access on a value of protocol type as if
141145
/// that protocol type were a generic constraint requiring conformance
142146
/// to that protocol.
@@ -499,6 +503,9 @@ class ContextualMismatch : public ConstraintFix {
499503
ConstraintLocator *locator)
500504
: ConstraintFix(cs, FixKind::ContextualMismatch, locator), LHS(lhs),
501505
RHS(rhs) {}
506+
ContextualMismatch(ConstraintSystem &cs, FixKind kind, Type lhs, Type rhs,
507+
ConstraintLocator *locator)
508+
: ConstraintFix(cs, kind, locator), LHS(lhs), RHS(rhs) {}
502509

503510
public:
504511
std::string getName() const override { return "fix contextual mismatch"; }
@@ -867,6 +874,23 @@ class AllowInvalidInitRef final : public ConstraintFix {
867874
ConstraintLocator *locator);
868875
};
869876

877+
class AllowTupleTypeMismatch final : public ContextualMismatch {
878+
AllowTupleTypeMismatch(ConstraintSystem &cs, Type lhs, Type rhs,
879+
ConstraintLocator *locator)
880+
: ContextualMismatch(cs, FixKind::AllowTupleTypeMismatch, lhs, rhs,
881+
locator) {}
882+
883+
public:
884+
static AllowTupleTypeMismatch *create(ConstraintSystem &cs, Type lhs,
885+
Type rhs, ConstraintLocator *locator);
886+
887+
std::string getName() const override {
888+
return "fix tuple mismatches in type and arity";
889+
}
890+
891+
bool diagnose(Expr *root, bool asNote = false) const override;
892+
};
893+
870894
class AllowMutatingMemberOnRValueBase final : public AllowInvalidMemberRef {
871895
AllowMutatingMemberOnRValueBase(ConstraintSystem &cs, Type baseType,
872896
ValueDecl *member, DeclName name,

lib/Sema/CSSimplify.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2417,6 +2417,13 @@ bool ConstraintSystem::repairFailures(
24172417
getConstraintLocator(locator));
24182418
conversionsOrFixes.push_back(fix);
24192419
}
2420+
2421+
if (purpose == CTP_Initialization && lhs->is<TupleType>() &&
2422+
rhs->is<TupleType>()) {
2423+
auto *fix = AllowTupleTypeMismatch::create(*this, lhs, rhs,
2424+
getConstraintLocator(locator));
2425+
conversionsOrFixes.push_back(fix);
2426+
}
24202427
break;
24212428
}
24222429

@@ -2448,6 +2455,11 @@ bool ConstraintSystem::repairFailures(
24482455
conversionsOrFixes.push_back(CollectionElementContextualMismatch::create(
24492456
*this, lhs, rhs, getConstraintLocator(locator)));
24502457
}
2458+
if (lhs->is<TupleType>() && rhs->is<TupleType>()) {
2459+
auto *fix = AllowTupleTypeMismatch::create(*this, lhs, rhs,
2460+
getConstraintLocator(locator));
2461+
conversionsOrFixes.push_back(fix);
2462+
}
24512463
break;
24522464
}
24532465

@@ -6915,6 +6927,45 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
69156927
return matchTypes(type1, type2, matchKind, subflags, locator);
69166928
}
69176929

6930+
case FixKind::AllowTupleTypeMismatch: {
6931+
auto lhs = type1->castTo<TupleType>();
6932+
auto rhs = type2->castTo<TupleType>();
6933+
// Create a new tuple type the size of the smaller tuple with elements
6934+
// from the larger tuple whenever either side contains a type variable.
6935+
// For example (A, $0, B, $2) and (X, Y, $1) produces: (X, $0, B).
6936+
// This allows us to guarentee that the types will match, and all
6937+
// type variables will get bound to something as long as we default
6938+
// excess types in the larger tuple to Any. In the prior example,
6939+
// when the tuples (X, Y, $1) and (X, $0, B) get matched, $0 is equated
6940+
// to Y, $1 is equated to B, and $2 is defaulted to Any.
6941+
auto lhsLarger = lhs->getNumElements() >= rhs->getNumElements();
6942+
auto larger = lhsLarger ? lhs : rhs;
6943+
auto smaller = lhsLarger ? rhs : lhs;
6944+
llvm::SmallVector<TupleTypeElt, 4> newTupleTypes;
6945+
6946+
for (unsigned i = 0; i < larger->getNumElements(); ++i) {
6947+
auto largerElt = larger->getElement(i);
6948+
if (i < smaller->getNumElements()) {
6949+
auto smallerElt = smaller->getElement(i);
6950+
if (largerElt.getType()->isTypeVariableOrMember() ||
6951+
smallerElt.getType()->isTypeVariableOrMember())
6952+
newTupleTypes.push_back(largerElt);
6953+
else
6954+
newTupleTypes.push_back(smallerElt);
6955+
} else {
6956+
if (largerElt.getType()->isTypeVariableOrMember())
6957+
addConstraint(ConstraintKind::Defaultable, largerElt.getType(),
6958+
getASTContext().TheAnyType,
6959+
getConstraintLocator(locator));
6960+
}
6961+
}
6962+
auto matchingType =
6963+
TupleType::get(newTupleTypes, getASTContext())->castTo<TupleType>();
6964+
if (recordFix(fix))
6965+
return SolutionKind::Error;
6966+
return matchTupleTypes(matchingType, smaller, matchKind, subflags, locator);
6967+
}
6968+
69186969
case FixKind::InsertCall:
69196970
case FixKind::RemoveReturn:
69206971
case FixKind::AddConformance:

test/Constraints/sr10728.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ struct S: P {
1919
typealias R = T3
2020

2121
static let foo: (T1, (R) -> T2) = bind()
22-
// expected-error@-1 {{cannot convert value of type '(T1, (S.R) -> T3)' (aka '(Int, (Bool) -> Bool)') to specified type '(T1, (S.R) -> T2)' (aka '(Int, (Bool) -> Float)')}}
22+
// expected-error@-1 {{tuple type '(T1, (S.R) -> T3)' (aka '(Int, (Bool) -> Bool)') is not convertible to tuple '(T1, (S.R) -> T2)' (aka '(Int, (Bool) -> Float)')}}
2323
}

test/NameBinding/name-binding.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ func test_varname_binding() {
5555
var (d, e) = (c.1, c.0)
5656
var ((), (g1, g2), h) = ((), (e, d), e)
5757
var (j, k, l) = callee1()
58-
var (m, n) = callee1() // expected-error{{'(Int, Int, Int)' is not convertible to '(_, _)', tuples have a different number of elements}}
59-
var (o, p, q, r) = callee1() // expected-error{{'(Int, Int, Int)' is not convertible to '(_, _, _, _)', tuples have a different number of elements}}
58+
var (m, n) = callee1() // expected-error{{'(Int, Int, Int)' is not convertible to '(Int, Int)', tuples have a different number of elements}}
59+
var (o, p, q, r) = callee1() // expected-error{{'(Int, Int, Int)' is not convertible to '(Int, Int, Int, Any)', tuples have a different number of elements}}
6060
}
6161

6262
//===----------------------------------------------------------------------===//

test/expr/expressions.swift

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ func funcdecl7(_ a: Int, b: (c: Int, d: Int), third: (c: Int, d: Int)) -> Int {
139139

140140
// Error recovery.
141141
func testfunc2 (_: ((), Int) -> Int) -> Int {}
142+
func makeTuple() -> (String, Int) { return ("foo", 42) }
142143
func errorRecovery() {
143144
testfunc2({ $0 + 1 }) // expected-error {{contextual closure type '((), Int) -> Int' expects 2 arguments, but 1 was used in closure body}}
144145

@@ -149,12 +150,14 @@ func errorRecovery() {
149150
var a: Int = .hello // expected-error {{type 'Int' has no member 'hello'}}
150151
var b: union1 = .bar // ok
151152
var c: union1 = .xyz // expected-error {{type 'union1' has no member 'xyz'}}
152-
var d: (Int,Int,Int) = (1,2) // expected-error {{cannot convert value of type '(Int, Int)' to specified type '(Int, Int, Int)'}}
153-
var e: (Int,Int) = (1, 2, 3) // expected-error {{cannot convert value of type '(Int, Int, Int)' to specified type '(Int, Int)'}}
154-
var f: (Int,Int) = (1, 2, f : 3) // expected-error {{cannot convert value of type '(Int, Int, f: Int)' to specified type '(Int, Int)'}}
153+
var d: (Int,Int,Int) = (1,2) // expected-error {{'(Int, Int)' is not convertible to '(Int, Int, Int)', tuples have a different number of elements}}
154+
var e: (Int,Int) = (1, 2, 3) // expected-error {{'(Int, Int, Int)' is not convertible to '(Int, Int)', tuples have a different number of elements}}
155+
var f: (Int,Int) = (1, 2, f : 3) // expected-error {{'(Int, Int, f: Int)' is not convertible to '(Int, Int)', tuples have a different number of elements}}
155156

156157
// <rdar://problem/22426860> CrashTracer: [USER] swift at …mous_namespace::ConstraintGenerator::getTypeForPattern + 698
157-
var (g1, g2, g3) = (1, 2) // expected-error {{'(Int, Int)' is not convertible to '(_, _, _)', tuples have a different number of elements}}
158+
var (g1, g2, g3) = (1, 2) // expected-error {{'(Int, Int)' is not convertible to '(Int, Int, Any)', tuples have a different number of elements}}
159+
var (h1, h2) = (1, 2, 3) // expected-error {{'(Int, Int, Int)' is not convertible to '(Int, Int)', tuples have a different number of elements}}
160+
var i: (Bool, Bool) = makeTuple() // expected-error {{tuple type '(String, Int)' is not convertible to tuple '(Bool, Bool)'}}
158161
}
159162

160163
func acceptsInt(_ x: Int) {}
@@ -185,7 +188,7 @@ func test4() -> ((_ arg1: Int, _ arg2: Int) -> Int) {
185188
func test5() {
186189
let a: (Int, Int) = (1,2)
187190
var
188-
_: ((Int) -> Int, Int) = a // expected-error {{cannot convert value of type '(Int, Int)' to specified type '((Int) -> Int, Int)'}}
191+
_: ((Int) -> Int, Int) = a // expected-error {{tuple type '(Int, Int)' is not convertible to tuple '((Int) -> Int, Int)'}}
189192

190193

191194
let c: (a: Int, b: Int) = (1,2)

0 commit comments

Comments
 (0)