Skip to content

[Diagnostics] Improve error when type parameters aren't equal #17210

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
Jun 15, 2018
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
3 changes: 0 additions & 3 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1429,9 +1429,6 @@ NOTE(type_does_not_inherit_or_conform_requirement,none,
ERROR(types_not_equal,none,
"%0 requires the types %1 and %2 be equivalent",
(Type, Type, Type))
ERROR(types_not_equal_in_call,none,
"%0 requires the types %1 and %2 be equivalent to use %3",
(Type, Type, Type, DeclName))
ERROR(type_does_not_conform_owner,none,
"%0 requires that %1 conform to %2", (Type, Type, Type))
NOTE(requirement_implied_by_conditional_conformance,none,
Expand Down
81 changes: 75 additions & 6 deletions lib/Sema/CSDiag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1704,6 +1704,77 @@ bool FailureDiagnosis::diagnoseConversionToBool(Expr *expr, Type exprType) {
return false;
}

static bool
diagnoseUnresolvedDotExprTypeRequirementFailure(ConstraintSystem &cs,
Constraint *constraint) {
auto &TC = cs.TC;

auto *locator = constraint->getLocator();
if (!locator)
return false;

auto path = locator->getPath();
if (path.empty())
return false;

auto &last = path.back();
if (last.getKind() != ConstraintLocator::TypeParameterRequirement)
return false;

auto *anchor = locator->getAnchor();
if (!anchor)
return false;

auto *UDE = dyn_cast<UnresolvedDotExpr>(anchor);
if (!UDE)
return false;

auto ownerType = cs.getType(UDE->getBase());
if (!ownerType)
return false;

ownerType = cs.simplifyType(ownerType)->getWithoutSpecifierType();
if (ownerType->hasTypeVariable() || ownerType->hasUnresolvedType())
return false;

// If we actually resolved the member to use, use it.
auto loc = cs.getConstraintLocator(UDE, ConstraintLocator::Member);
auto *member = cs.findResolvedMemberRef(loc);
if (!member)
return false;

auto req = member->getAsGenericContext()
->getGenericSignature()
->getRequirements()[last.getValue()];

Diag<Type, Type, Type, Type, StringRef> note;
switch (req.getKind()) {
case RequirementKind::Conformance:
case RequirementKind::Layout:
return false;

case RequirementKind::Superclass:
note = diag::candidate_types_inheritance_requirement;
break;

case RequirementKind::SameType:
note = diag::candidate_types_equal_requirement;
break;
}

TC.diagnose(UDE->getLoc(), diag::could_not_find_value_member, ownerType,
UDE->getName());

auto first = cs.simplifyType(constraint->getFirstType());
auto second = cs.simplifyType(constraint->getSecondType());
auto rawFirstType = req.getFirstType();
auto rawSecondType = req.getSecondType();

TC.diagnose(member, note, first, second, rawFirstType, rawSecondType, "");

return true;
}

/// Diagnose problems related to failures in constraints
/// generated by `openGeneric` which represent different
/// kinds of type parameter requirements.
Expand Down Expand Up @@ -1741,6 +1812,9 @@ static bool diagnoseTypeRequirementFailure(ConstraintSystem &cs,
if (ownerType->hasTypeVariable() || ownerType->hasUnresolvedType())
return false;

if (diagnoseUnresolvedDotExprTypeRequirementFailure(cs, constraint))
return true;

auto lhs = cs.simplifyType(constraint->getFirstType());
auto rhs = cs.simplifyType(constraint->getSecondType());

Expand All @@ -1756,12 +1830,7 @@ static bool diagnoseTypeRequirementFailure(ConstraintSystem &cs,
return true;

case ConstraintKind::Equal: { // same type
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(anchor)) {
TC.diagnose(UDE->getLoc(), diag::types_not_equal_in_call, ownerType, lhs,
rhs, UDE->getName());
} else {
TC.diagnose(anchor->getLoc(), diag::types_not_equal, ownerType, lhs, rhs);
}
TC.diagnose(anchor->getLoc(), diag::types_not_equal, ownerType, lhs, rhs);
return true;
}

Expand Down
2 changes: 1 addition & 1 deletion test/Constraints/generics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ do {
}

func rdar35890334(_ arr: inout [Int]) {
_ = arr.popFirst() // expected-error {{'[Int]' requires the types '[Int]' and 'ArraySlice<Int>' be equivalent to use 'popFirst'}}
_ = arr.popFirst() // expected-error {{value of type '[Int]' has no member 'popFirst'}}
}

// rdar://problem/39616039
Expand Down
4 changes: 2 additions & 2 deletions test/decl/ext/protocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ struct S4d : P4 {
}

extension P4 where Self.AssocP4 == Int {
func extP4Int() { }
func extP4Int() { } // expected-note {{candidate requires that the types 'Bool' and 'Int' be equivalent (requirement specified as 'Self.AssocP4' == 'Int')}}
}

extension P4 where Self.AssocP4 == Bool {
Expand All @@ -221,7 +221,7 @@ func testP4(_ s4a: S4a, s4b: S4b, s4c: S4c, s4d: S4d) {
s4c.extP4Int() // okay
var b1 = s4d.extP4a() // okay, "Bool" version
b1 = true // checks type above
s4d.extP4Int() // expected-error{{'S4d' requires the types 'Bool' and 'Int' be equivalent to use 'extP4Int'}}
s4d.extP4Int() // expected-error{{value of type 'S4d' has no member 'extP4Int'}}
_ = b1
}

Expand Down
2 changes: 1 addition & 1 deletion test/decl/protocol/protocols.swift
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ struct WrongIsEqual : IsEqualComparable { // expected-error{{type 'WrongIsEqual'
//===----------------------------------------------------------------------===//

func existentialSequence(_ e: Sequence) { // expected-error{{has Self or associated type requirements}}
var x = e.makeIterator() // expected-error{{'Sequence' requires the types 'Sequence' and 'Sequence.Iterator' be equivalent to use 'makeIterator'}}
var x = e.makeIterator() // expected-error{{value of type 'Sequence' has no member 'makeIterator'}}
x.next()
x.nonexistent()
}
Expand Down