[ConstraintSystem] Introduce a fix for assignment type mismatch

Introduce a fix/diagnostic when there is a contextual mismatch
between source and destination types of the assignment e.g.:

```swift
var x: Int = 0
x = 4.0 // destination expects an `Int`, but source is a `Double`
```
This commit is contained in:
Pavel Yaskevich
2019-09-19 22:02:33 -07:00
parent 81c7be86db
commit 8eaeb51fe5
6 changed files with 79 additions and 14 deletions

View File

@@ -759,6 +759,26 @@ IgnoreContextualType *IgnoreContextualType::create(ConstraintSystem &cs,
IgnoreContextualType(cs, resultTy, specifiedTy, locator); IgnoreContextualType(cs, resultTy, specifiedTy, locator);
} }
bool IgnoreAssignmentDestinationType::diagnose(Expr *root, bool asNote) const {
auto &cs = getConstraintSystem();
auto *AE = cast<AssignExpr>(getAnchor());
auto CTP = isa<SubscriptExpr>(AE->getDest()) ? CTP_SubscriptAssignSource
: CTP_AssignSource;
ContextualFailure failure(
root, cs, CTP, getFromType(), getToType(),
cs.getConstraintLocator(AE->getSrc(), LocatorPathElt::ContextualType()));
return failure.diagnose(asNote);
}
IgnoreAssignmentDestinationType *
IgnoreAssignmentDestinationType::create(ConstraintSystem &cs, Type sourceTy,
Type destTy,
ConstraintLocator *locator) {
return new (cs.getAllocator())
IgnoreAssignmentDestinationType(cs, sourceTy, destTy, locator);
}
bool AllowInOutConversion::diagnose(Expr *root, bool asNote) const { bool AllowInOutConversion::diagnose(Expr *root, bool asNote) const {
auto &cs = getConstraintSystem(); auto &cs = getConstraintSystem();
InOutConversionFailure failure(root, cs, getFromType(), getToType(), InOutConversionFailure failure(root, cs, getFromType(), getToType(),

View File

@@ -1299,6 +1299,23 @@ public:
ConstraintLocator *locator); ConstraintLocator *locator);
}; };
class IgnoreAssignmentDestinationType final : public ContextualMismatch {
IgnoreAssignmentDestinationType(ConstraintSystem &cs, Type sourceTy,
Type destTy, ConstraintLocator *locator)
: ContextualMismatch(cs, sourceTy, destTy, locator) {}
public:
std::string getName() const override {
return "ignore type of the assignment destination";
}
bool diagnose(Expr *root, bool asNote = false) const override;
static IgnoreAssignmentDestinationType *create(ConstraintSystem &cs,
Type sourceTy, Type destTy,
ConstraintLocator *locator);
};
/// If this is an argument-to-parameter conversion which is associated with /// If this is an argument-to-parameter conversion which is associated with
/// `inout` parameter, subtyping is not permitted, types have to /// `inout` parameter, subtyping is not permitted, types have to
/// be identical. /// be identical.

View File

@@ -2353,6 +2353,15 @@ bool ConstraintSystem::repairFailures(
return false; return false;
}; };
auto hasConversionOrRestriction = [&](ConversionRestrictionKind kind) {
return llvm::any_of(conversionsOrFixes,
[kind](const RestrictionOrFix correction) {
if (auto restriction = correction.getRestriction())
return restriction == kind;
return false;
});
};
if (path.empty()) { if (path.empty()) {
if (!anchor) if (!anchor)
return false; return false;
@@ -2391,20 +2400,39 @@ bool ConstraintSystem::repairFailures(
if (repairViaBridgingCast(*this, lhs, rhs, conversionsOrFixes, locator)) if (repairViaBridgingCast(*this, lhs, rhs, conversionsOrFixes, locator))
return true; return true;
// If we are trying to assign e.g. `Array<Int>` to `Array<Float>` let's
// give solver a chance to determine which generic parameters are
// mismatched and produce a fix for that.
if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality))
return false;
// If the situation has to do with protocol composition types and
// destination doesn't have one of the conformances e.g. source is
// `X & Y` but destination is only `Y` or vice versa, there is a
// tailored "missing conformance" fix for that.
if (hasConversionOrRestriction(ConversionRestrictionKind::Existential))
return false;
// If this is an attempt to assign something to a value of optional type
// there is a possiblity that the problem is related to escapiness, so
// fix has to be delayed.
if (hasConversionOrRestriction(
ConversionRestrictionKind::ValueToOptional))
return false;
// If the destination of an assignment is l-value type
// it leaves only possible reason for failure - a type mismatch.
if (getType(AE->getDest())->is<LValueType>()) {
conversionsOrFixes.push_back(IgnoreAssignmentDestinationType::create(
*this, lhs, rhs, getConstraintLocator(locator)));
return true;
}
} }
return false; return false;
} }
auto hasConversionOrRestriction = [&](ConversionRestrictionKind kind) {
return llvm::any_of(conversionsOrFixes,
[kind](const RestrictionOrFix correction) {
if (auto restriction = correction.getRestriction())
return restriction == kind;
return false;
});
};
auto elt = path.back(); auto elt = path.back();
switch (elt.getKind()) { switch (elt.getKind()) {
case ConstraintLocator::LValueConversion: { case ConstraintLocator::LValueConversion: {

View File

@@ -394,7 +394,7 @@ func rdar20868864(_ s: String) {
func r22058555() { func r22058555() {
var firstChar: UInt8 = 0 var firstChar: UInt8 = 0
"abc".withCString { chars in "abc".withCString { chars in
firstChar = chars[0] // expected-error {{cannot assign value of type 'Int8' to type 'UInt8'}} firstChar = chars[0] // expected-error {{cannot assign value of type 'Int8' to type 'UInt8'}} {{17-17=UInt8(}} {{25-25=)}}
} }
} }

View File

@@ -22,7 +22,7 @@ func useIdentity(_ x: Int, y: Float, i32: Int32) {
// Deduction where the result type and input type can get different results // Deduction where the result type and input type can get different results
var xx : X, yy : Y var xx : X, yy : Y
xx = identity(yy) // expected-error{{cannot convert value of type 'Y' to expected argument type 'X'}} xx = identity(yy) // expected-error{{cannot assign value of type 'Y' to type 'X'}}
xx = identity2(yy) // expected-error{{cannot convert value of type 'Y' to expected argument type 'X'}} xx = identity2(yy) // expected-error{{cannot convert value of type 'Y' to expected argument type 'X'}}
} }

View File

@@ -17,14 +17,14 @@ struct YourFoo: Foo {}
func someTypeIsTheSame() { func someTypeIsTheSame() {
var a = foo(0) var a = foo(0)
a = foo(0) a = foo(0)
a = foo("") // expected-error{{cannot convert value of type 'String' to expected argument type 'Int'}} a = foo("") // expected-error{{cannot assign value of type 'some Foo' (result of 'foo') to type 'some Foo' (result of 'foo')}}
var b = foo("") var b = foo("")
b = foo(0) // expected-error{{cannot convert value of type 'Int' to expected argument type 'String'}} b = foo(0) // expected-error{{cannot assign value of type 'some Foo' (result of 'foo') to type 'some Foo' (result of 'foo')}}
b = foo("") b = foo("")
var c = foo(MyFoo()) var c = foo(MyFoo())
c = foo(0) // expected-error{{cannot convert value of type 'Int' to expected argument type 'MyFoo'}} c = foo(0) // expected-error{{cannot assign value of type 'some Foo' (result of 'foo') to type 'some Foo' (result of 'foo')}}
c = foo(MyFoo()) c = foo(MyFoo())
c = foo(YourFoo()) // expected-error{{cannot convert value of type 'YourFoo' to expected argument type 'MyFoo'}} c = foo(YourFoo()) // expected-error{{cannot convert value of type 'YourFoo' to expected argument type 'MyFoo'}}