Skip to content

Commit b4c13c2

Browse files
committed
[CS] Adjust assessRequirementFailureImpact
- In `simplifyConformsToConstraint`, pass the LHS type regardless of whether it is a type variable. - Add the `choiceImpact` onto the impact for adding a stdlib conformance. - Treat Any and AnyObject as standard library types.
1 parent dc4b089 commit b4c13c2

File tree

4 files changed

+71
-38
lines changed

4 files changed

+71
-38
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 47 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1369,37 +1369,12 @@ static ConstraintFix *fixRequirementFailure(ConstraintSystem &cs, Type type1,
13691369
static unsigned
13701370
assessRequirementFailureImpact(ConstraintSystem &cs, Type requirementType,
13711371
ConstraintLocatorBuilder locator) {
1372+
assert(requirementType);
1373+
1374+
unsigned impact = 1;
13721375
auto *anchor = locator.getAnchor();
13731376
if (!anchor)
1374-
return 1;
1375-
1376-
if (requirementType && cs.simplifyType(requirementType)->isStdlibType()) {
1377-
if (auto last = locator.last()) {
1378-
if (auto requirement = last->getAs<LocatorPathElt::AnyRequirement>()) {
1379-
auto kind = requirement->getRequirementKind();
1380-
if (kind == RequirementKind::Conformance)
1381-
return 3;
1382-
}
1383-
}
1384-
}
1385-
1386-
// If this requirement is associated with an overload choice let's
1387-
// tie impact to how many times this requirement type is mentioned.
1388-
if (auto *ODRE = dyn_cast<OverloadedDeclRefExpr>(anchor)) {
1389-
if (!(requirementType && requirementType->is<TypeVariableType>()))
1390-
return 1;
1391-
1392-
unsigned choiceImpact = 0;
1393-
if (auto choice = cs.findSelectedOverloadFor(ODRE)) {
1394-
auto *typeVar = requirementType->castTo<TypeVariableType>();
1395-
choice->openedType.visit([&](Type type) {
1396-
if (type->isEqual(typeVar))
1397-
++choiceImpact;
1398-
});
1399-
}
1400-
1401-
return choiceImpact == 0 ? 1 : choiceImpact;
1402-
}
1377+
return impact;
14031378

14041379
// If this requirement is associated with a member reference and it
14051380
// was possible to check it before overload choice is bound, that means
@@ -1417,13 +1392,53 @@ assessRequirementFailureImpact(ConstraintSystem &cs, Type requirementType,
14171392
// fix for same-type requirement higher impact vs. requirement associated
14181393
// with method itself e.g. `func foo<U>() -> U where U : P {}` because
14191394
// `foo` is accessible from any `S` regardless of what `T` is.
1395+
//
1396+
// Don't add this impact with the others, as we want to keep it consistent
1397+
// across requirement failures to present the user with a choice.
14201398
if (isa<UnresolvedDotExpr>(anchor) || isa<UnresolvedMemberExpr>(anchor)) {
14211399
auto *calleeLoc = cs.getCalleeLocator(cs.getConstraintLocator(locator));
14221400
if (!cs.findSelectedOverloadFor(calleeLoc))
14231401
return 10;
14241402
}
14251403

1426-
return 1;
1404+
// Increase the impact of a conformance fix for a standard library type,
1405+
// as it's unlikely to be a good suggestion. Also do the same for the builtin
1406+
// compiler types Any and AnyObject, which cannot conform to protocols.
1407+
// FIXME: We ought not to have the is<TypeVariableType>() condition here, but
1408+
// removing it currently regresses the diagnostic for the test case for
1409+
// rdar://60727310. Once we better handle the separation of conformance fixes
1410+
// from argument mismatches in cases like SR-12438, we should be able to
1411+
// remove it from the condition.
1412+
auto resolvedTy = cs.simplifyType(requirementType);
1413+
if ((requirementType->is<TypeVariableType>() && resolvedTy->isStdlibType()) ||
1414+
resolvedTy->isAny() || resolvedTy->isAnyObject()) {
1415+
if (auto last = locator.last()) {
1416+
if (auto requirement = last->getAs<LocatorPathElt::AnyRequirement>()) {
1417+
auto kind = requirement->getRequirementKind();
1418+
if (kind == RequirementKind::Conformance)
1419+
impact += 2;
1420+
}
1421+
}
1422+
}
1423+
1424+
// If this requirement is associated with an overload choice let's
1425+
// tie impact to how many times this requirement type is mentioned.
1426+
if (auto *ODRE = dyn_cast<OverloadedDeclRefExpr>(anchor)) {
1427+
if (auto *typeVar = requirementType->getAs<TypeVariableType>()) {
1428+
unsigned choiceImpact = 0;
1429+
if (auto choice = cs.findSelectedOverloadFor(ODRE)) {
1430+
choice->openedType.visit([&](Type type) {
1431+
if (type->isEqual(typeVar))
1432+
++choiceImpact;
1433+
});
1434+
}
1435+
// If the type is used multiple times in the signature, increase the
1436+
// impact for every additional use.
1437+
if (choiceImpact > 1)
1438+
impact += choiceImpact - 1;
1439+
}
1440+
}
1441+
return impact;
14271442
}
14281443

14291444
/// Attempt to fix missing arguments by introducing type variables
@@ -5072,6 +5087,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
50725087
ConstraintKind kind,
50735088
ConstraintLocatorBuilder locator,
50745089
TypeMatchOptions flags) {
5090+
const auto rawType = type;
50755091
auto *typeVar = type->getAs<TypeVariableType>();
50765092

50775093
// Dig out the fixed type to which this type refers.
@@ -5243,7 +5259,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
52435259

52445260
if (auto *fix =
52455261
fixRequirementFailure(*this, type, protocolTy, anchor, path)) {
5246-
auto impact = assessRequirementFailureImpact(*this, typeVar, locator);
5262+
auto impact = assessRequirementFailureImpact(*this, rawType, locator);
52475263
if (!recordFix(fix, impact)) {
52485264
// Record this conformance requirement as "fixed".
52495265
recordFixedRequirement(type, RequirementKind::Conformance,

test/Constraints/bridging.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ func rdar19831698() {
265265
var v71 = true + 1.0 // expected-error{{binary operator '+' cannot be applied to operands of type 'Bool' and 'Double'}}
266266
// expected-note@-1{{overloads for '+'}}
267267
var v72 = true + true // expected-error{{binary operator '+' cannot be applied to two 'Bool' operands}}
268+
// expected-note@-1{{overloads for '+'}}
268269
var v73 = true + [] // expected-error@:13 {{cannot convert value of type 'Bool' to expected argument type 'Array<Bool>'}}
269270
var v75 = true + "str" // expected-error@:13 {{cannot convert value of type 'Bool' to expected argument type 'String'}}
270271
}

test/Constraints/diagnostics.swift

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,11 @@ func f7() -> (c: Int, v: A) {
100100
}
101101

102102
func f8<T:P2>(_ n: T, _ f: @escaping (T) -> T) {} // expected-note {{where 'T' = 'Int'}}
103-
// expected-note@-1 {{required by global function 'f8' where 'T' = '(Int, Double)'}}
103+
// expected-note@-1 {{required by global function 'f8' where 'T' = 'Tup' (aka '(Int, Double)')}}
104104
f8(3, f4) // expected-error {{global function 'f8' requires that 'Int' conform to 'P2'}}
105105
typealias Tup = (Int, Double)
106106
func f9(_ x: Tup) -> Tup { return x }
107-
f8((1,2.0), f9) // expected-error {{type '(Int, Double)' cannot conform to 'P2'; only struct/enum/class types can conform to protocols}}
107+
f8((1,2.0), f9) // expected-error {{type 'Tup' (aka '(Int, Double)') cannot conform to 'P2'; only struct/enum/class types can conform to protocols}}
108108

109109
// <rdar://problem/19658691> QoI: Incorrect diagnostic for calling nonexistent members on literals
110110
1.doesntExist(0) // expected-error {{value of type 'Int' has no member 'doesntExist'}}
@@ -404,11 +404,11 @@ f7(1)(1.0) // expected-error {{cannot convert value of type 'Double' to ex
404404
f7(1)(b: 1.0) // expected-error{{extraneous argument label 'b:' in call}}
405405
// expected-error@-1 {{cannot convert value of type 'Double' to expected argument type 'Int'}}
406406

407-
let f8 = f7(2)
408-
_ = f8(1)
409-
f8(10) // expected-warning {{result of call to function returning 'Int' is unused}}
410-
f8(1.0) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}}
411-
f8(b: 1.0) // expected-error {{extraneous argument label 'b:' in call}}
407+
let f10 = f7(2)
408+
_ = f10(1)
409+
f10(10) // expected-warning {{result of call to function returning 'Int' is unused}}
410+
f10(1.0) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}}
411+
f10(b: 1.0) // expected-error {{extraneous argument label 'b:' in call}}
412412
// expected-error@-1 {{cannot convert value of type 'Double' to expected argument type 'Int'}}
413413

414414

@@ -1405,3 +1405,13 @@ func foo(frame: Frame) {
14051405
// expected-note@-1 {{overloads for '+' exist with these partially matching parameter lists: (Double, Double), (Int, Int)}}
14061406

14071407
}
1408+
1409+
// Make sure we prefer the conformance failure.
1410+
func f11(_ n: Int) {}
1411+
func f11<T : P2>(_ n: T, _ f: @escaping (T) -> T) {} // expected-note {{where 'T' = 'Int'}}
1412+
f11(3, f4) // expected-error {{global function 'f11' requires that 'Int' conform to 'P2'}}
1413+
1414+
// FIXME: Arguably we should also prefer the conformance failure in this case.
1415+
let f12: (Int) -> Void = { _ in }
1416+
func f12<T : P2>(_ n: T, _ f: @escaping (T) -> T) {}
1417+
f12(3, f4)// expected-error {{extra argument in call}}

test/Constraints/operator.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,3 +273,9 @@ func rdar60727310() {
273273
var e: Error? = nil
274274
myAssertion(e, ==, nil) // expected-error {{binary operator '==' cannot be applied to two 'Error?' operands}}
275275
}
276+
277+
// FIXME(SR-12438): Bad diagnostic.
278+
func sr12438(_ e: Error) {
279+
func foo<T>(_ a: T, _ op: ((T, T) -> Bool)) {}
280+
foo(e, ==) // expected-error {{type of expression is ambiguous without more context}}
281+
}

0 commit comments

Comments
 (0)