Skip to content

Commit b9a0ca6

Browse files
committed
[ConstraintSystem] Detect and diagnose conversion failures related to collection element types
Detect and diagnose a contextual mismatch between expected collection element type and the one provided (e.g. source of the assignment or argument to a call) e.g.: ```swift let _: [Int] = ["hello"] func foo(_: [Int]) {} foo(["hello"]) ```
1 parent 2587df2 commit b9a0ca6

13 files changed

+123
-17
lines changed

lib/Sema/CSDiagnostics.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2844,3 +2844,46 @@ bool ExtraneousReturnFailure::diagnoseAsError() {
28442844
emitDiagnostic(anchor->getLoc(), diag::cannot_return_value_from_void_func);
28452845
return true;
28462846
}
2847+
2848+
bool CollectionElementContextualFailure::diagnoseAsError() {
2849+
auto *anchor = getAnchor();
2850+
2851+
auto eltType = getFromType();
2852+
auto contextualType = getToType();
2853+
2854+
Optional<InFlightDiagnostic> diagnostic;
2855+
if (isa<ArrayExpr>(getRawAnchor())) {
2856+
diagnostic.emplace(emitDiagnostic(anchor->getLoc(),
2857+
diag::cannot_convert_array_element,
2858+
eltType, contextualType));
2859+
}
2860+
2861+
if (isa<DictionaryExpr>(getRawAnchor())) {
2862+
auto *locator = getLocator();
2863+
const auto eltLoc = locator->getPath().back();
2864+
2865+
switch (eltLoc.getValue()) {
2866+
case 0: // key
2867+
diagnostic.emplace(emitDiagnostic(anchor->getLoc(),
2868+
diag::cannot_convert_dict_key, eltType,
2869+
contextualType));
2870+
break;
2871+
2872+
case 1: // value
2873+
diagnostic.emplace(emitDiagnostic(anchor->getLoc(),
2874+
diag::cannot_convert_dict_value,
2875+
eltType, contextualType));
2876+
break;
2877+
2878+
default:
2879+
break;
2880+
}
2881+
}
2882+
2883+
if (!diagnostic)
2884+
return false;
2885+
2886+
(void)trySequenceSubsequenceFixIts(*diagnostic, getConstraintSystem(),
2887+
eltType, contextualType, anchor);
2888+
return true;
2889+
}

lib/Sema/CSDiagnostics.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,7 @@ class AssignmentFailure final : public FailureDiagnostic {
669669

670670
/// Intended to diagnose any possible contextual failure
671671
/// e.g. argument/parameter, closure result, conversions etc.
672-
class ContextualFailure final : public FailureDiagnostic {
672+
class ContextualFailure : public FailureDiagnostic {
673673
Type FromType, ToType;
674674

675675
public:
@@ -678,6 +678,10 @@ class ContextualFailure final : public FailureDiagnostic {
678678
: FailureDiagnostic(root, cs, locator), FromType(resolve(lhs)),
679679
ToType(resolve(rhs)) {}
680680

681+
Type getFromType() const { return resolveType(FromType); }
682+
683+
Type getToType() const { return resolveType(ToType); }
684+
681685
bool diagnoseAsError() override;
682686

683687
// If we're trying to convert something of type "() -> T" to T,
@@ -1181,6 +1185,23 @@ class ExtraneousReturnFailure final : public FailureDiagnostic {
11811185
bool diagnoseAsError() override;
11821186
};
11831187

1188+
/// Diagnose a contextual mismatch between expected collection element type
1189+
/// and the one provided (e.g. source of the assignment or argument to a call)
1190+
/// e.g.:
1191+
///
1192+
/// ```swift
1193+
/// let _: [Int] = ["hello"]
1194+
/// ```
1195+
class CollectionElementContextualFailure final : public ContextualFailure {
1196+
public:
1197+
CollectionElementContextualFailure(Expr *root, ConstraintSystem &cs,
1198+
Type eltType, Type contextualType,
1199+
ConstraintLocator *locator)
1200+
: ContextualFailure(root, cs, eltType, contextualType, locator) {}
1201+
1202+
bool diagnoseAsError() override;
1203+
};
1204+
11841205
} // end namespace constraints
11851206
} // end namespace swift
11861207

lib/Sema/CSFix.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,3 +504,18 @@ RemoveReturn *RemoveReturn::create(ConstraintSystem &cs,
504504
ConstraintLocator *locator) {
505505
return new (cs.getAllocator()) RemoveReturn(cs, locator);
506506
}
507+
508+
bool CollectionElementContextualMismatch::diagnose(Expr *root,
509+
bool asNote) const {
510+
CollectionElementContextualFailure failure(
511+
root, getConstraintSystem(), getFromType(), getToType(), getLocator());
512+
return failure.diagnose(asNote);
513+
}
514+
515+
CollectionElementContextualMismatch *
516+
CollectionElementContextualMismatch::create(ConstraintSystem &cs, Type srcType,
517+
Type dstType,
518+
ConstraintLocator *locator) {
519+
return new (cs.getAllocator())
520+
CollectionElementContextualMismatch(cs, srcType, dstType, locator);
521+
}

lib/Sema/CSFix.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,23 @@ class RemoveReturn final : public ConstraintFix {
886886
static RemoveReturn *create(ConstraintSystem &cs, ConstraintLocator *locator);
887887
};
888888

889+
class CollectionElementContextualMismatch final : public ContextualMismatch {
890+
CollectionElementContextualMismatch(ConstraintSystem &cs, Type srcType,
891+
Type dstType, ConstraintLocator *locator)
892+
: ContextualMismatch(cs, srcType, dstType, locator) {}
893+
894+
public:
895+
std::string getName() const override {
896+
return "fix collection element contextual mismatch";
897+
}
898+
899+
bool diagnose(Expr *root, bool asNote = false) const override;
900+
901+
static CollectionElementContextualMismatch *
902+
create(ConstraintSystem &cs, Type srcType, Type dstType,
903+
ConstraintLocator *locator);
904+
};
905+
889906
} // end namespace constraints
890907
} // end namespace swift
891908

lib/Sema/CSSimplify.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2185,6 +2185,14 @@ bool ConstraintSystem::repairFailures(
21852185
break;
21862186
}
21872187

2188+
case ConstraintLocator::TupleElement: {
2189+
if (anchor && (isa<ArrayExpr>(anchor) || isa<DictionaryExpr>(anchor))) {
2190+
conversionsOrFixes.push_back(CollectionElementContextualMismatch::create(
2191+
*this, lhs, rhs, getConstraintLocator(locator)));
2192+
}
2193+
break;
2194+
}
2195+
21882196
default:
21892197
break;
21902198
}

test/Constraints/array_literal.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,7 @@ func longArray() {
107107

108108
// <rdar://problem/25563498> Type checker crash assigning array literal to type conforming to ArrayProtocol
109109
func rdar25563498<T : ExpressibleByArrayLiteral>(t: T) {
110-
var x: T = [1] // expected-error {{cannot convert value of type '[Int]' to specified type 'T'}}
111-
// expected-warning@-1{{variable 'x' was never used; consider replacing with '_' or removing it}}
110+
var x: T = [1] // expected-error {{cannot convert value of type 'Int' to expected element type 'T.ArrayLiteralElement'}}
112111
}
113112

114113
func rdar25563498_ok<T : ExpressibleByArrayLiteral>(t: T) -> T

test/Constraints/diagnostics.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,7 @@ func r22470302(_ c: r22470302Class) {
603603
// <rdar://problem/21928143> QoI: Pointfree reference to generic initializer in generic context does not compile
604604
extension String {
605605
@available(*, unavailable, message: "calling this is unwise")
606-
func unavail<T : Sequence> // expected-note 2 {{'unavail' has been explicitly marked unavailable here}}
606+
func unavail<T : Sequence> // expected-note {{'unavail' has been explicitly marked unavailable here}}
607607
(_ a : T) -> String where T.Iterator.Element == String {}
608608
}
609609
extension Array {
@@ -612,7 +612,7 @@ extension Array {
612612
}
613613

614614
func h() -> String {
615-
return "foo".unavail([0]) // expected-error {{'unavail' is unavailable: calling this is unwise}}
615+
return "foo".unavail([0]) // expected-error {{cannot convert value of type 'Int' to expected element type 'String'}}
616616
}
617617
}
618618

test/Constraints/dictionary_literal.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ func useDict<K, V>(_ d: MyDictionary<K,V>) {}
1919
useDictStringInt(["Hello" : 1])
2020
useDictStringInt(["Hello" : 1, "World" : 2])
2121
useDictStringInt(["Hello" : 1, "World" : 2.5])
22-
// expected-error@-1 {{cannot convert value of type 'Double' to expected dictionary value type 'Int'}}
22+
// expected-error@-1 {{cannot convert value of type 'Double' to expected dictionary value type 'DictStringInt.Value' (aka 'Int')}}
2323
useDictStringInt([4.5 : 2])
24-
// expected-error@-1 {{cannot convert value of type 'Double' to expected dictionary key type 'String'}}
24+
// expected-error@-1 {{cannot convert value of type 'Double' to expected dictionary key type 'DictStringInt.Key' (aka 'String')}}
2525
useDictStringInt([nil : 2])
2626
// expected-error@-1 {{'nil' is not compatible with expected dictionary key type 'String'}}
2727
useDictStringInt([7 : 1, "World" : 2])
28-
// expected-error@-1 {{cannot convert value of type 'Int' to expected dictionary key type 'String'}}
28+
// expected-error@-1 {{cannot convert value of type 'Int' to expected dictionary key type 'DictStringInt.Key' (aka 'String')}}
2929
useDictStringInt(["Hello" : nil])
3030
// expected-error@-1 {{'nil' is not compatible with expected dictionary value type 'Int'}}
3131

test/Constraints/operator.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,5 @@ func rdar46459603() {
221221
// expected-error@-1 {{binary operator '==' cannot be applied to operands of type 'Dictionary<String, E>.Values' and '[E]'}}
222222
// expected-note@-2 {{expected an argument list of type '(Self, Self)'}}
223223
_ = [arr.values] == [[e]]
224-
// expected-error@-1 {{binary operator '==' cannot be applied to operands of type '[Dictionary<String, E>.Values]' and '[[E]]'}}
225-
// expected-note@-2 {{expected an argument list of type '(Self, Self)'}}
224+
// expected-error@-1 {{protocol type 'Any' cannot conform to 'Equatable' because only concrete types can conform to protocols}}
226225
}

test/Generics/conditional_conformances_literals.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func arraySameType() {
2626

2727
let _: SameType = [works]
2828
let _: SameType = [fails]
29-
// expected-error@-1 {{value of type '[Fails]' does not conform to specified type 'SameType'}}
29+
// expected-error@-1 {{cannot convert value of type 'Fails' to expected element type 'Works'}}
3030

3131
let _: SameType = arrayWorks
3232
let _: SameType = arrayFails
@@ -51,7 +51,7 @@ func dictionarySameType() {
5151

5252
let _: SameType = [0 : works]
5353
let _: SameType = [0 : fails]
54-
// expected-error@-1 {{contextual type 'SameType' cannot be used with dictionary literal}}
54+
// expected-error@-1 {{cannot convert value of type 'Fails' to expected dictionary value type 'Works'}}
5555

5656
let _: SameType = dictWorks
5757
let _: SameType = dictFails

test/Parse/pointer_conversion.swift.gyb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,8 @@ func constPointerArguments(_ p: UnsafeMutablePointer<Int>,
157157
takesConstPointer(ff) // expected-error{{cannot convert value of type '[Float]' to expected argument type 'UnsafePointer<Int>${diag_suffix}'}}
158158
takesConstPointer([0, 1, 2])
159159
// <rdar://problem/22308330> QoI: CSDiags doesn't handle array -> pointer impl conversions well
160-
takesConstPointer([0.0, 1.0, 2.0]) // expected-error{{cannot convert value of type '[Double]' to expected argument type 'UnsafePointer<Int>}}
160+
takesConstPointer([0.0, 1.0, 2.0])
161+
// expected-error@-1 3 {{annot convert value of type 'Double' to expected element type 'Int'}}
161162

162163
// We don't allow these conversions outside of function arguments.
163164
var x: UnsafePointer<Int> = &i // expected-error {{use of extraneous '&'}}

test/expr/cast/as_coerce.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,14 @@ c3 as C4 // expected-error {{'C3' is not convertible to 'C4'; did you mean to us
8383
1 as Int as String // expected-error{{cannot convert value of type 'Int' to type 'String' in coercion}}
8484
Double(1) as Double as String // expected-error{{cannot convert value of type 'Double' to type 'String' in coercion}}
8585
["awd"] as [Int] // expected-error{{cannot convert value of type 'String' to expected element type 'Int'}}
86-
([1, 2, 1.0], 1) as ([String], Int) // expected-error{{cannot convert value of type 'Int' to expected element type 'String'}}
86+
([1, 2, 1.0], 1) as ([String], Int)
87+
// expected-error@-1 2 {{cannot convert value of type 'Int' to expected element type 'String'}}
88+
// expected-error@-2 {{cannot convert value of type 'Double' to expected element type 'String'}}
8789
[[1]] as [[String]] // expected-error{{cannot convert value of type 'Int' to expected element type 'String'}}
8890
(1, 1.0) as (Int, Int) // expected-error{{cannot convert value of type 'Double' to type 'Int' in coercion}}
8991
(1.0, 1, "asd") as (String, Int, Float) // expected-error{{cannot convert value of type 'Double' to type 'String' in coercion}}
90-
(1, 1.0, "a", [1, 23]) as (Int, Double, String, [String]) // expected-error{{cannot convert value of type 'Int' to expected element type 'String'}}
92+
(1, 1.0, "a", [1, 23]) as (Int, Double, String, [String])
93+
// expected-error@-1 2 {{cannot convert value of type 'Int' to expected element type 'String'}}
9194

9295
_ = [1] as! [String] // expected-warning{{cast from '[Int]' to unrelated type '[String]' always fails}}
9396
_ = [(1, (1, 1))] as! [(Int, (String, Int))] // expected-warning{{cast from '[(Int, (Int, Int))]' to unrelated type '[(Int, (String, Int))]' always fails}}

test/expr/expressions.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -739,8 +739,8 @@ func invalidDictionaryLiteral() {
739739
}
740740

741741

742-
[4].joined(separator: [1]) // expected-error {{referencing instance method 'joined(separator:)' on 'Sequence' requires that 'Int' conform to 'StringProtocol'}}
743-
[4].joined(separator: [[[1]]]) // expected-error {{referencing instance method 'joined(separator:)' on 'Sequence' requires that 'Int' conform to 'StringProtocol'}}
742+
[4].joined(separator: [1]) // expected-error {{cannot convert value of type 'Int' to expected element type 'String'}}
743+
[4].joined(separator: [[[1]]]) // expected-error {{cannot convert value of type 'Int' to expected element type 'String'}}
744744

745745
//===----------------------------------------------------------------------===//
746746
// nil/metatype comparisons

0 commit comments

Comments
 (0)