Skip to content

[ConstraintSystem] Detect and diagnose conversion failures related to… #24791

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 15, 2019
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
43 changes: 43 additions & 0 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2844,3 +2844,46 @@ bool ExtraneousReturnFailure::diagnoseAsError() {
emitDiagnostic(anchor->getLoc(), diag::cannot_return_value_from_void_func);
return true;
}

bool CollectionElementContextualFailure::diagnoseAsError() {
auto *anchor = getAnchor();

auto eltType = getFromType();
auto contextualType = getToType();

Optional<InFlightDiagnostic> diagnostic;
if (isa<ArrayExpr>(getRawAnchor())) {
diagnostic.emplace(emitDiagnostic(anchor->getLoc(),
diag::cannot_convert_array_element,
eltType, contextualType));
}

if (isa<DictionaryExpr>(getRawAnchor())) {
auto *locator = getLocator();
const auto eltLoc = locator->getPath().back();

switch (eltLoc.getValue()) {
case 0: // key
diagnostic.emplace(emitDiagnostic(anchor->getLoc(),
diag::cannot_convert_dict_key, eltType,
contextualType));
break;

case 1: // value
diagnostic.emplace(emitDiagnostic(anchor->getLoc(),
diag::cannot_convert_dict_value,
eltType, contextualType));
break;

default:
break;
}
}

if (!diagnostic)
return false;

(void)trySequenceSubsequenceFixIts(*diagnostic, getConstraintSystem(),
eltType, contextualType, anchor);
return true;
}
23 changes: 22 additions & 1 deletion lib/Sema/CSDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ class AssignmentFailure final : public FailureDiagnostic {

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

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

Type getFromType() const { return resolveType(FromType); }

Type getToType() const { return resolveType(ToType); }

bool diagnoseAsError() override;

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

/// 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"]
/// ```
class CollectionElementContextualFailure final : public ContextualFailure {
public:
CollectionElementContextualFailure(Expr *root, ConstraintSystem &cs,
Type eltType, Type contextualType,
ConstraintLocator *locator)
: ContextualFailure(root, cs, eltType, contextualType, locator) {}

bool diagnoseAsError() override;
};

} // end namespace constraints
} // end namespace swift

Expand Down
15 changes: 15 additions & 0 deletions lib/Sema/CSFix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -504,3 +504,18 @@ RemoveReturn *RemoveReturn::create(ConstraintSystem &cs,
ConstraintLocator *locator) {
return new (cs.getAllocator()) RemoveReturn(cs, locator);
}

bool CollectionElementContextualMismatch::diagnose(Expr *root,
bool asNote) const {
CollectionElementContextualFailure failure(
root, getConstraintSystem(), getFromType(), getToType(), getLocator());
return failure.diagnose(asNote);
}

CollectionElementContextualMismatch *
CollectionElementContextualMismatch::create(ConstraintSystem &cs, Type srcType,
Type dstType,
ConstraintLocator *locator) {
return new (cs.getAllocator())
CollectionElementContextualMismatch(cs, srcType, dstType, locator);
}
17 changes: 17 additions & 0 deletions lib/Sema/CSFix.h
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,23 @@ class RemoveReturn final : public ConstraintFix {
static RemoveReturn *create(ConstraintSystem &cs, ConstraintLocator *locator);
};

class CollectionElementContextualMismatch final : public ContextualMismatch {
CollectionElementContextualMismatch(ConstraintSystem &cs, Type srcType,
Type dstType, ConstraintLocator *locator)
: ContextualMismatch(cs, srcType, dstType, locator) {}

public:
std::string getName() const override {
return "fix collection element contextual mismatch";
}

bool diagnose(Expr *root, bool asNote = false) const override;

static CollectionElementContextualMismatch *
create(ConstraintSystem &cs, Type srcType, Type dstType,
ConstraintLocator *locator);
};

} // end namespace constraints
} // end namespace swift

Expand Down
8 changes: 8 additions & 0 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2185,6 +2185,14 @@ bool ConstraintSystem::repairFailures(
break;
}

case ConstraintLocator::TupleElement: {
if (anchor && (isa<ArrayExpr>(anchor) || isa<DictionaryExpr>(anchor))) {
conversionsOrFixes.push_back(CollectionElementContextualMismatch::create(
*this, lhs, rhs, getConstraintLocator(locator)));
}
break;
}

default:
break;
}
Expand Down
3 changes: 1 addition & 2 deletions test/Constraints/array_literal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,7 @@ func longArray() {

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

func rdar25563498_ok<T : ExpressibleByArrayLiteral>(t: T) -> T
Expand Down
4 changes: 2 additions & 2 deletions test/Constraints/diagnostics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@ func r22470302(_ c: r22470302Class) {
// <rdar://problem/21928143> QoI: Pointfree reference to generic initializer in generic context does not compile
extension String {
@available(*, unavailable, message: "calling this is unwise")
func unavail<T : Sequence> // expected-note 2 {{'unavail' has been explicitly marked unavailable here}}
func unavail<T : Sequence> // expected-note {{'unavail' has been explicitly marked unavailable here}}
(_ a : T) -> String where T.Iterator.Element == String {}
}
extension Array {
Expand All @@ -612,7 +612,7 @@ extension Array {
}

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

Expand Down
6 changes: 3 additions & 3 deletions test/Constraints/dictionary_literal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ func useDict<K, V>(_ d: MyDictionary<K,V>) {}
useDictStringInt(["Hello" : 1])
useDictStringInt(["Hello" : 1, "World" : 2])
useDictStringInt(["Hello" : 1, "World" : 2.5])
// expected-error@-1 {{cannot convert value of type 'Double' to expected dictionary value type 'Int'}}
// expected-error@-1 {{cannot convert value of type 'Double' to expected dictionary value type 'DictStringInt.Value' (aka 'Int')}}
useDictStringInt([4.5 : 2])
// expected-error@-1 {{cannot convert value of type 'Double' to expected dictionary key type 'String'}}
// expected-error@-1 {{cannot convert value of type 'Double' to expected dictionary key type 'DictStringInt.Key' (aka 'String')}}
useDictStringInt([nil : 2])
// expected-error@-1 {{'nil' is not compatible with expected dictionary key type 'String'}}
useDictStringInt([7 : 1, "World" : 2])
// expected-error@-1 {{cannot convert value of type 'Int' to expected dictionary key type 'String'}}
// expected-error@-1 {{cannot convert value of type 'Int' to expected dictionary key type 'DictStringInt.Key' (aka 'String')}}
useDictStringInt(["Hello" : nil])
// expected-error@-1 {{'nil' is not compatible with expected dictionary value type 'Int'}}

Expand Down
3 changes: 1 addition & 2 deletions test/Constraints/operator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,5 @@ func rdar46459603() {
// expected-error@-1 {{binary operator '==' cannot be applied to operands of type 'Dictionary<String, E>.Values' and '[E]'}}
// expected-note@-2 {{expected an argument list of type '(Self, Self)'}}
_ = [arr.values] == [[e]]
// expected-error@-1 {{binary operator '==' cannot be applied to operands of type '[Dictionary<String, E>.Values]' and '[[E]]'}}
// expected-note@-2 {{expected an argument list of type '(Self, Self)'}}
// expected-error@-1 {{protocol type 'Any' cannot conform to 'Equatable' because only concrete types can conform to protocols}}
}
4 changes: 2 additions & 2 deletions test/Generics/conditional_conformances_literals.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func arraySameType() {

let _: SameType = [works]
let _: SameType = [fails]
// expected-error@-1 {{value of type '[Fails]' does not conform to specified type 'SameType'}}
// expected-error@-1 {{cannot convert value of type 'Fails' to expected element type 'Works'}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much nicer!


let _: SameType = arrayWorks
let _: SameType = arrayFails
Expand All @@ -51,7 +51,7 @@ func dictionarySameType() {

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

let _: SameType = dictWorks
let _: SameType = dictFails
Expand Down
3 changes: 2 additions & 1 deletion test/Parse/pointer_conversion.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ func constPointerArguments(_ p: UnsafeMutablePointer<Int>,
takesConstPointer(ff) // expected-error{{cannot convert value of type '[Float]' to expected argument type 'UnsafePointer<Int>${diag_suffix}'}}
takesConstPointer([0, 1, 2])
// <rdar://problem/22308330> QoI: CSDiags doesn't handle array -> pointer impl conversions well
takesConstPointer([0.0, 1.0, 2.0]) // expected-error{{cannot convert value of type '[Double]' to expected argument type 'UnsafePointer<Int>}}
takesConstPointer([0.0, 1.0, 2.0])
// expected-error@-1 3 {{annot convert value of type 'Double' to expected element type 'Int'}}

// We don't allow these conversions outside of function arguments.
var x: UnsafePointer<Int> = &i // expected-error {{use of extraneous '&'}}
Expand Down
7 changes: 5 additions & 2 deletions test/expr/cast/as_coerce.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,14 @@ c3 as C4 // expected-error {{'C3' is not convertible to 'C4'; did you mean to us
1 as Int as String // expected-error{{cannot convert value of type 'Int' to type 'String' in coercion}}
Double(1) as Double as String // expected-error{{cannot convert value of type 'Double' to type 'String' in coercion}}
["awd"] as [Int] // expected-error{{cannot convert value of type 'String' to expected element type 'Int'}}
([1, 2, 1.0], 1) as ([String], Int) // expected-error{{cannot convert value of type 'Int' to expected element type 'String'}}
([1, 2, 1.0], 1) as ([String], Int)
// expected-error@-1 2 {{cannot convert value of type 'Int' to expected element type 'String'}}
// expected-error@-2 {{cannot convert value of type 'Double' to expected element type 'String'}}
[[1]] as [[String]] // expected-error{{cannot convert value of type 'Int' to expected element type 'String'}}
(1, 1.0) as (Int, Int) // expected-error{{cannot convert value of type 'Double' to type 'Int' in coercion}}
(1.0, 1, "asd") as (String, Int, Float) // expected-error{{cannot convert value of type 'Double' to type 'String' in coercion}}
(1, 1.0, "a", [1, 23]) as (Int, Double, String, [String]) // expected-error{{cannot convert value of type 'Int' to expected element type 'String'}}
(1, 1.0, "a", [1, 23]) as (Int, Double, String, [String])
// expected-error@-1 2 {{cannot convert value of type 'Int' to expected element type 'String'}}

_ = [1] as! [String] // expected-warning{{cast from '[Int]' to unrelated type '[String]' always fails}}
_ = [(1, (1, 1))] as! [(Int, (String, Int))] // expected-warning{{cast from '[(Int, (Int, Int))]' to unrelated type '[(Int, (String, Int))]' always fails}}
Expand Down
4 changes: 2 additions & 2 deletions test/expr/expressions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -739,8 +739,8 @@ func invalidDictionaryLiteral() {
}


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

//===----------------------------------------------------------------------===//
// nil/metatype comparisons
Expand Down