Skip to content

Commit ee3e694

Browse files
authored
[CS] Explore additional bindings for fixes (#30686)
[CS] Explore additional bindings for fixes
2 parents 80e5a51 + b4c13c2 commit ee3e694

File tree

11 files changed

+124
-52
lines changed

11 files changed

+124
-52
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 58 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
@@ -3301,6 +3316,17 @@ bool ConstraintSystem::repairFailures(
33013316
if (hasFixFor(loc, FixKind::AllowInvalidUseOfTrailingClosure))
33023317
return true;
33033318

3319+
// Don't attempt to fix an argument being passed to a
3320+
// _OptionalNilComparisonType parameter. Such an overload should only take
3321+
// effect when a nil literal is used in valid code, and doesn't offer any
3322+
// useful fixes for invalid code.
3323+
if (auto *nominal = rhs->getAnyNominal()) {
3324+
if (nominal->isStdlibDecl() &&
3325+
nominal->getName() == getASTContext().Id_OptionalNilComparisonType) {
3326+
return false;
3327+
}
3328+
}
3329+
33043330
if (repairByInsertingExplicitCall(lhs, rhs))
33053331
break;
33063332

@@ -5061,6 +5087,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
50615087
ConstraintKind kind,
50625088
ConstraintLocatorBuilder locator,
50635089
TypeMatchOptions flags) {
5090+
const auto rawType = type;
50645091
auto *typeVar = type->getAs<TypeVariableType>();
50655092

50665093
// Dig out the fixed type to which this type refers.
@@ -5232,7 +5259,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
52325259

52335260
if (auto *fix =
52345261
fixRequirementFailure(*this, type, protocolTy, anchor, path)) {
5235-
auto impact = assessRequirementFailureImpact(*this, typeVar, locator);
5262+
auto impact = assessRequirementFailureImpact(*this, rawType, locator);
52365263
if (!recordFix(fix, impact)) {
52375264
// Record this conformance requirement as "fixed".
52385265
recordFixedRequirement(type, RequirementKind::Conformance,

lib/Sema/CSStep.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,13 +619,25 @@ class TypeVariableStep final : public BindingStep<TypeVarBindingProducer> {
619619
/// Check whether attempting type variable binding choices should
620620
/// be stopped, because optimal solution has already been found.
621621
bool shouldStopAt(const TypeVariableBinding &choice) const override {
622+
// Let's always attempt default types inferred from literals in diagnostic
623+
// mode because that could lead to better diagnostics if the problem is
624+
// contextual like argument/parameter conversion or collection element
625+
// mismatch.
626+
if (CS.shouldAttemptFixes())
627+
return false;
628+
622629
// If we were able to solve this without considering
623630
// default literals, don't bother looking at default literals.
624631
return AnySolved && choice.hasDefaultedProtocol() &&
625632
!SawFirstLiteralConstraint;
626633
}
627634

628635
bool shouldStopAfter(const TypeVariableBinding &choice) const override {
636+
// Let's always attempt additional bindings in diagnostic mode, as that
637+
// could lead to better diagnostic for e.g trying the unwrapped type.
638+
if (CS.shouldAttemptFixes())
639+
return false;
640+
629641
// If there has been at least one solution so far
630642
// at a current batch of bindings is done it's a
631643
// success because each new batch would be less

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/closures.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,17 +273,17 @@ func verify_NotAC_to_AC_failure(_ arg: () -> ()) {
273273
// SR-1069 - Error diagnostic refers to wrong argument
274274
class SR1069_W<T> {
275275
func append<Key: AnyObject>(value: T, forKey key: Key) where Key: Hashable {}
276-
// expected-note@-1 {{where 'Key' = 'Object?'}}
277276
}
278277
class SR1069_C<T> { let w: SR1069_W<(AnyObject, T) -> ()> = SR1069_W() }
279278
struct S<T> {
280279
let cs: [SR1069_C<T>] = []
281280

282281
func subscribe<Object: AnyObject>(object: Object?, method: (Object, T) -> ()) where Object: Hashable {
283282
let wrappedMethod = { (object: AnyObject, value: T) in }
284-
// expected-error @+2 {{instance method 'append(value:forKey:)' requires that 'Object?' be a class type}}
285-
// expected-note @+1 {{wrapped type 'Object' satisfies this requirement}}
286283
cs.forEach { $0.w.append(value: wrappedMethod, forKey: object) }
284+
// expected-error@-1 {{value of optional type 'Object?' must be unwrapped to a value of type 'Object'}}
285+
// expected-note@-2 {{coalesce using '??' to provide a default when the optional value contains 'nil'}}
286+
// expected-note@-3 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}}
287287
}
288288
}
289289

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/fixes.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,14 @@ var s1116 = Set(1...10).subtracting(a1116.map({ $0.s })) // expected-error {{val
182182
// expected-note@-1{{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{49-49=(}} {{53-53= ?? <#default value#>)}}
183183
// expected-note@-2{{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{53-53=!}}
184184

185+
func makeArray<T>(_ x: T) -> [T] { [x] }
186+
187+
func sr12399(_ x: Int?) {
188+
_ = Set(0...10).subtracting(makeArray(x)) // expected-error {{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}}
189+
// expected-note@-1{{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{42-42= ?? <#default value#>}}
190+
// expected-note@-2{{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{42-42=!}}
191+
}
192+
185193
func moreComplexUnwrapFixes() {
186194
struct S {
187195
let value: Int
@@ -348,3 +356,10 @@ func testKeyPathSubscriptArgFixes(_ fn: @escaping () -> Int) {
348356
_ = \S.[nil] // expected-error {{'nil' is not compatible with expected argument type 'Int'}}
349357
_ = \S.[fn] // expected-error {{function produces expected type 'Int'; did you mean to call it with '()'?}} {{13-13=()}}
350358
}
359+
360+
func sr12426(a: Any, _ str: String?) {
361+
a == str // expected-error {{cannot convert value of type 'Any' to expected argument type 'String'}}
362+
// expected-error@-1 {{value of optional type 'String?' must be unwrapped to a value of type 'String'}}
363+
// expected-note@-2 {{coalesce using '??' to provide a default when the optional value contains 'nil'}}
364+
// expected-note@-3 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}}
365+
}

test/Constraints/keyword_arguments.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -798,19 +798,15 @@ func trailingclosure4(f: () -> Int) {}
798798
trailingclosure4 { 5 }
799799

800800
func trailingClosure5<T>(_ file: String = #file, line: UInt = #line, expression: () -> T?) { }
801-
// expected-note@-1 {{in call to function 'trailingClosure5(_:line:expression:)'}}
802801
func trailingClosure6<T>(value: Int, expression: () -> T?) { }
803-
// expected-note@-1 {{in call to function 'trailingClosure6(value:expression:)'}}
804802

805803
trailingClosure5(file: "hello", line: 17) { // expected-error{{extraneous argument label 'file:' in call}}{{18-24=}}
806-
// expected-error@-1 {{generic parameter 'T' could not be inferred}}
807804
return Optional.Some(5)
808805
// expected-error@-1 {{enum type 'Optional<Wrapped>' has no case 'Some'; did you mean 'some'?}} {{19-23=some}}
809806
// expected-error@-2 {{generic parameter 'Wrapped' could not be inferred}}
810807
// expected-note@-3 {{explicitly specify the generic arguments to fix this issue}}
811808
}
812809
trailingClosure6(5) { // expected-error{{missing argument label 'value:' in call}}{{18-18=value: }}
813-
// expected-error@-1 {{generic parameter 'T' could not be inferred}}
814810
return Optional.Some(5)
815811
// expected-error@-1 {{enum type 'Optional<Wrapped>' has no case 'Some'; did you mean 'some'?}} {{19-23=some}}
816812
// expected-error@-2 {{generic parameter 'Wrapped' could not be inferred}}

test/Constraints/operator.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,10 +217,11 @@ func rdar46459603() {
217217
var arr = ["key": e]
218218

219219
_ = arr.values == [e]
220-
// expected-error@-1 {{binary operator '==' cannot be applied to operands of type 'Dictionary<String, E>.Values' and '[E]'}}
220+
// expected-error@-1 {{referencing operator function '==' on 'Equatable' requires that 'Dictionary<String, E>.Values' conform to 'Equatable'}}
221+
// expected-error@-2 {{cannot convert value of type '[E]' to expected argument type 'Dictionary<String, E>.Values'}}
221222
_ = [arr.values] == [[e]]
222-
// expected-error@-1 {{value of protocol type 'Any' cannot conform to 'Equatable'; only struct/enum/class types can conform to protocols}}
223-
// expected-note@-2 {{requirement from conditional conformance of '[Any]' to 'Equatable'}}
223+
// expected-error@-1 {{operator function '==' requires that 'Dictionary<String, E>.Values' conform to 'Equatable'}}
224+
// expected-error@-2 {{cannot convert value of type '[E]' to expected element type 'Dictionary<String, E>.Values'}}
224225
}
225226

226227
// SR-10843
@@ -272,3 +273,9 @@ func rdar60727310() {
272273
var e: Error? = nil
273274
myAssertion(e, ==, nil) // expected-error {{binary operator '==' cannot be applied to two 'Error?' operands}}
274275
}
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+
}

test/Generics/deduction.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -319,8 +319,9 @@ func foo() {
319319
let j = min(Int(3), Float(2.5)) // expected-error{{conflicting arguments to generic parameter 'T' ('Int' vs. 'Float')}}
320320
let k = min(A(), A()) // expected-error{{global function 'min' requires that 'A' conform to 'Comparable'}}
321321
let oi : Int? = 5
322-
let l = min(3, oi) // expected-error{{global function 'min' requires that 'Int?' conform to 'Comparable'}}
323-
// expected-note@-1{{wrapped type 'Int' satisfies this requirement}}
322+
let l = min(3, oi) // expected-error {{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}}
323+
// expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}}
324+
// expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}}
324325
}
325326

326327
infix operator +&

test/Misc/misc_diagnostics.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,10 @@ func test17875634() {
140140

141141
// <rdar://problem/20770032> Pattern matching ranges against tuples crashes the compiler
142142
func test20770032() {
143-
if case let 1...10 = (1, 1) { // expected-warning{{'let' pattern has no effect; sub-pattern didn't bind any variables}} {{11-15=}} expected-error{{expression pattern of type 'ClosedRange<Int>' cannot match values of type '(Int, Int)'}}
143+
if case let 1...10 = (1, 1) { // expected-warning{{'let' pattern has no effect; sub-pattern didn't bind any variables}} {{11-15=}}
144+
// expected-error@-1 {{expression pattern of type 'ClosedRange<Int>' cannot match values of type '(Int, Int)'}}
145+
// expected-error@-2 {{'(Int, Int)' cannot conform to 'Equatable'; only struct/enum/class types can conform to protocols}}
146+
// expected-note@-3 {{required by operator function '~=' where 'T' = '(Int, Int)'}}
144147
}
145148
}
146149

test/expr/delayed-ident/static_var.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,4 @@ var _: HasClosure = .factoryOpt(3)
5151
// expected-note@-2 {{coalesce}}
5252
// expected-note@-3 {{force-unwrap}}
5353
// FIXME: we should accept this
54-
var _: HasClosure = .factoryOpt!(4) // expected-error {{type 'Optional<_>' has no member 'factoryOpt'}}
54+
var _: HasClosure = .factoryOpt!(4) // expected-error {{cannot infer contextual base in reference to member 'factoryOpt'}}

0 commit comments

Comments
 (0)