Skip to content

Commit 1cc6f77

Browse files
committed
[ConstraintSystem] Improve contextual mismatch diagnostics for for ... in <expr> loop
1 parent e39d236 commit 1cc6f77

File tree

6 files changed

+39
-4
lines changed

6 files changed

+39
-4
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,13 @@ ERROR(cannot_convert_assign_protocol,none,
447447
ERROR(cannot_convert_assign_nil,none,
448448
"'nil' cannot be assigned to type %0", (Type))
449449

450+
// for ... in expression
451+
ERROR(cannot_convert_sequence_element_value,none,
452+
"cannot convert sequence element type %0 to expected type %1",
453+
(Type, Type))
454+
ERROR(cannot_convert_sequence_element_protocol,none,
455+
"sequence element type %0 does not conform to expected type %1",
456+
(Type, Type))
450457

451458
ERROR(throws_functiontype_mismatch,none,
452459
"invalid conversion from throwing function of type %0 to "

lib/Sema/CSDiagnostics.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2847,6 +2847,7 @@ bool ExtraneousReturnFailure::diagnoseAsError() {
28472847

28482848
bool CollectionElementContextualFailure::diagnoseAsError() {
28492849
auto *anchor = getAnchor();
2850+
auto *locator = getLocator();
28502851

28512852
auto eltType = getFromType();
28522853
auto contextualType = getToType();
@@ -2859,8 +2860,7 @@ bool CollectionElementContextualFailure::diagnoseAsError() {
28592860
}
28602861

28612862
if (isa<DictionaryExpr>(getRawAnchor())) {
2862-
auto *locator = getLocator();
2863-
const auto eltLoc = locator->getPath().back();
2863+
const auto &eltLoc = locator->getPath().back();
28642864

28652865
switch (eltLoc.getValue()) {
28662866
case 0: // key
@@ -2880,6 +2880,15 @@ bool CollectionElementContextualFailure::diagnoseAsError() {
28802880
}
28812881
}
28822882

2883+
if (locator->isForSequenceElementType()) {
2884+
diagnostic.emplace(
2885+
emitDiagnostic(anchor->getLoc(),
2886+
contextualType->isExistentialType()
2887+
? diag::cannot_convert_sequence_element_protocol
2888+
: diag::cannot_convert_sequence_element_value,
2889+
eltType, contextualType));
2890+
}
2891+
28832892
if (!diagnostic)
28842893
return false;
28852894

lib/Sema/CSSimplify.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2193,6 +2193,12 @@ bool ConstraintSystem::repairFailures(
21932193
break;
21942194
}
21952195

2196+
case ConstraintLocator::SequenceElementType: {
2197+
conversionsOrFixes.push_back(CollectionElementContextualMismatch::create(
2198+
*this, lhs, rhs, getConstraintLocator(locator)));
2199+
break;
2200+
}
2201+
21962202
default:
21972203
break;
21982204
}

lib/Sema/ConstraintLocator.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,12 @@ bool ConstraintLocator::isForGenericParameter() const {
175175
path.back().getKind() == ConstraintLocator::GenericParameter;
176176
}
177177

178+
bool ConstraintLocator::isForSequenceElementType() const {
179+
auto path = getPath();
180+
return !path.empty() &&
181+
path.back().getKind() == ConstraintLocator::SequenceElementType;
182+
}
183+
178184
void ConstraintLocator::dump(SourceManager *sm) {
179185
dump(sm, llvm::errs());
180186
llvm::errs() << "\n";

lib/Sema/ConstraintLocator.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,10 @@ class ConstraintLocator : public llvm::FoldingSetNode {
567567
/// Determine whether this locator points to the generic parameter.
568568
bool isForGenericParameter() const;
569569

570+
/// Determine whether this locator points to the element type of a
571+
/// sequence in a for ... in ... loop.
572+
bool isForSequenceElementType() const;
573+
570574
/// Produce a profile of this locator, for use in a folding set.
571575
static void Profile(llvm::FoldingSetNodeID &id, Expr *anchor,
572576
ArrayRef<PathElement> path);

test/stmt/foreach.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,15 @@ struct GoodTupleIterator: Sequence, IteratorProtocol {
5353
func makeIterator() -> GoodTupleIterator {}
5454
}
5555

56+
protocol ElementProtocol {}
57+
5658
func patterns(gir: GoodRange<Int>, gtr: GoodTupleIterator) {
5759
var sum : Int
5860
var sumf : Float
5961
for i : Int in gir { sum = sum + i }
6062
for i in gir { sum = sum + i }
61-
for f : Float in gir { sum = sum + f } // expected-error{{'Int' is not convertible to 'Float'}}
63+
for f : Float in gir { sum = sum + f } // expected-error{{cannot convert sequence element type 'Int' to expected type 'Float'}}
64+
for f : ElementProtocol in gir { } // expected-error {{sequence element type 'Int' does not conform to expected type 'ElementProtocol'}}
6265

6366
for (i, f) : (Int, Float) in gtr { sum = sum + i }
6467

@@ -70,7 +73,7 @@ func patterns(gir: GoodRange<Int>, gtr: GoodTupleIterator) {
7073

7174
for (i, _) : (Int, Float) in gtr { sum = sum + i }
7275

73-
for (i, _) : (Int, Int) in gtr { sum = sum + i } // expected-error{{'GoodTupleIterator.Element' (aka '(Int, Float)') is not convertible to '(Int, Int)'}}
76+
for (i, _) : (Int, Int) in gtr { sum = sum + i } // expected-error{{cannot convert sequence element type 'GoodTupleIterator.Element' (aka '(Int, Float)') to expected type '(Int, Int)'}}
7477

7578
for (i, f) in gtr {}
7679
}

0 commit comments

Comments
 (0)