Skip to content

Commit 475b559

Browse files
authored
Merge pull request #28979 from hborla/ambiguous-call-diagnostics
[ConstraintSystem] Start to allow diagnosing ambiguity with fixes for solution sets with more than one fix.
2 parents 1bebee1 + 9e73bb3 commit 475b559

File tree

6 files changed

+104
-75
lines changed

6 files changed

+104
-75
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ ERROR(no_overloads_match_exactly_in_assignment,none,
7373
"no exact matches in assignment to %0",
7474
(DeclBaseName))
7575

76+
NOTE(candidate_partial_match,none,
77+
"candidate has partially matching parameter list %0",
78+
(StringRef))
79+
7680
ERROR(ambiguous_subscript,none,
7781
"ambiguous subscript with base type %0 and index type %1",
7882
(Type, Type))
@@ -274,9 +278,6 @@ ERROR(cannot_call_with_params, none,
274278
ERROR(cannot_call_non_function_value,none,
275279
"cannot call value of non-function type %0", (Type))
276280

277-
ERROR(wrong_argument_labels_overload,none,
278-
"argument labels '%0' do not match any available overloads", (StringRef))
279-
280281
ERROR(no_candidates_match_result_type,none,
281282
"no '%0' candidates produce the expected contextual result type %1",
282283
(StringRef, Type))

lib/Sema/CSDiag.cpp

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1715,21 +1715,6 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
17151715
calleeInfo, TCC_ForceRecheck);
17161716
if (!argExpr)
17171717
return true; // already diagnosed.
1718-
1719-
// Handle argument label mismatches when we have multiple candidates.
1720-
if (calleeInfo.closeness == CC_ArgumentLabelMismatch) {
1721-
auto args = decomposeArgType(CS.getType(argExpr), argLabels);
1722-
1723-
// If we have multiple candidates that we fail to match, just say we have
1724-
// the wrong labels and list the candidates out.
1725-
diagnose(callExpr->getLoc(), diag::wrong_argument_labels_overload,
1726-
getParamListAsString(args))
1727-
.highlight(argExpr->getSourceRange());
1728-
1729-
// Did the user intend on invoking a different overload?
1730-
calleeInfo.suggestPotentialOverloads(fnExpr->getLoc());
1731-
return true;
1732-
}
17331718

17341719
auto overloadName = calleeInfo.declName;
17351720

lib/Sema/ConstraintSystem.cpp

Lines changed: 88 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2750,34 +2750,52 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes(
27502750
// overloads of the same declaration.
27512751
ConstraintLocator *commonCalleeLocator = nullptr;
27522752
SmallPtrSet<ValueDecl *, 4> distinctChoices;
2753-
SmallVector<std::pair<const Solution *, const ConstraintFix *>, 4>
2754-
viableSolutions;
2753+
SmallVector<const Solution *, 4> viableSolutions;
2754+
2755+
enum class AmbiguityKind {
2756+
/// There is exactly one fix associated with each candidate.
2757+
CloseMatch,
2758+
/// Solution is ambiguous because all candidates had partially matching
2759+
/// parameter lists.
2760+
ParameterList,
2761+
/// General ambiguity failure.
2762+
General,
2763+
};
2764+
auto ambiguityKind = AmbiguityKind::CloseMatch;
27552765

27562766
bool diagnosable = llvm::all_of(solutions, [&](const Solution &solution) {
27572767
ArrayRef<ConstraintFix *> fixes = solution.Fixes;
27582768

2759-
// Currently only support a single fix in a solution,
2760-
// but ultimately should be able to deal with multiple.
2761-
if (fixes.size() != 1)
2769+
if (fixes.empty())
27622770
return false;
27632771

2764-
const auto *fix = fixes.front();
2765-
auto *locator = fix->getLocator();
2772+
if (fixes.size() > 1) {
2773+
ambiguityKind = (ambiguityKind == AmbiguityKind::CloseMatch ||
2774+
ambiguityKind == AmbiguityKind::ParameterList) &&
2775+
llvm::all_of(fixes, [](const ConstraintFix *fix) -> bool {
2776+
auto *locator = fix->getLocator();
2777+
return locator->findLast<LocatorPathElt::ApplyArgument>().hasValue();
2778+
}) ? AmbiguityKind::ParameterList
2779+
: AmbiguityKind::General;
2780+
}
2781+
2782+
for (const auto *fix: fixes) {
2783+
auto *locator = fix->getLocator();
2784+
// Assignment failures are all about the source expression,
2785+
// because they treat destination as a contextual type.
2786+
if (auto *anchor = locator->getAnchor()) {
2787+
if (auto *assignExpr = dyn_cast<AssignExpr>(anchor))
2788+
locator = getConstraintLocator(assignExpr->getSrc());
2789+
}
27662790

2767-
// Assignment failures are all about the source expression,
2768-
// because they treat destination as a contextual type.
2769-
if (auto *anchor = locator->getAnchor()) {
2770-
if (auto *assignExpr = dyn_cast<AssignExpr>(anchor))
2771-
locator = getConstraintLocator(assignExpr->getSrc());
2791+
auto *calleeLocator = getCalleeLocator(locator);
2792+
if (!commonCalleeLocator)
2793+
commonCalleeLocator = calleeLocator;
2794+
else if (commonCalleeLocator != calleeLocator)
2795+
return false;
27722796
}
27732797

2774-
auto *calleeLocator = getCalleeLocator(locator);
2775-
if (commonCalleeLocator && commonCalleeLocator != calleeLocator)
2776-
return false;
2777-
2778-
commonCalleeLocator = calleeLocator;
2779-
2780-
auto overload = solution.getOverloadChoiceIfAvailable(calleeLocator);
2798+
auto overload = solution.getOverloadChoiceIfAvailable(commonCalleeLocator);
27812799
if (!overload)
27822800
return false;
27832801

@@ -2789,7 +2807,7 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes(
27892807
// as viable, otherwise we'd produce the same diagnostic multiple
27902808
// times, which means that actual problem is elsewhere.
27912809
if (distinctChoices.insert(decl).second)
2792-
viableSolutions.push_back({&solution, fix});
2810+
viableSolutions.push_back(&solution);
27932811
return true;
27942812
});
27952813

@@ -2803,32 +2821,11 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes(
28032821
{
28042822
DiagnosticTransaction transaction(getASTContext().Diags);
28052823

2806-
const auto *fix = viableSolutions.front().second;
28072824
auto *commonAnchor = commonCalleeLocator->getAnchor();
28082825
auto &DE = getASTContext().Diags;
28092826
auto name = decl->getFullName();
28102827

2811-
if (fix->getKind() == FixKind::UseSubscriptOperator) {
2812-
auto *UDE = cast<UnresolvedDotExpr>(commonAnchor);
2813-
DE.diagnose(commonAnchor->getLoc(),
2814-
diag::could_not_find_subscript_member_did_you_mean,
2815-
getType(UDE->getBase()));
2816-
} else if (fix->getKind() == FixKind::TreatRValueAsLValue) {
2817-
DE.diagnose(commonAnchor->getLoc(),
2818-
diag::no_overloads_match_exactly_in_assignment,
2819-
decl->getBaseName());
2820-
} else if (llvm::all_of(
2821-
viableSolutions,
2822-
[](const std::pair<const Solution *, const ConstraintFix *>
2823-
&fix) {
2824-
auto *locator = fix.second->getLocator();
2825-
return locator
2826-
->isLastElement<LocatorPathElt::ContextualType>();
2827-
})) {
2828-
auto baseName = name.getBaseName();
2829-
DE.diagnose(commonAnchor->getLoc(), diag::no_candidates_match_result_type,
2830-
baseName.userFacingName(), getContextualType());
2831-
} else {
2828+
auto emitGeneralAmbiguityFailure = [&]() {
28322829
// Three choices here:
28332830
// 1. If this is a special name avoid printing it because
28342831
// printing kind is sufficient;
@@ -2855,14 +2852,61 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes(
28552852
diag::no_overloads_match_exactly_in_call_no_labels,
28562853
decl->getDescriptiveKind(), name.getBaseName());
28572854
}
2855+
};
2856+
2857+
switch (ambiguityKind) {
2858+
case AmbiguityKind::CloseMatch:
2859+
// Handled below
2860+
break;
2861+
case AmbiguityKind::ParameterList: {
2862+
emitGeneralAmbiguityFailure();
2863+
2864+
for (const auto &viable: viableSolutions) {
2865+
auto overload = viable->getOverloadChoice(commonCalleeLocator);
2866+
auto *fn = overload.openedType->getAs<AnyFunctionType>();
2867+
assert(fn);
2868+
DE.diagnose(overload.choice.getDecl()->getLoc(),
2869+
diag::candidate_partial_match,
2870+
fn->getParamListAsString(fn->getParams()));
2871+
}
2872+
2873+
return true;
2874+
}
2875+
case AmbiguityKind::General:
2876+
// TODO: Handle general ambiguity here.
2877+
return false;
2878+
}
2879+
2880+
auto *fix = viableSolutions.front()->Fixes.front();
2881+
if (fix->getKind() == FixKind::UseSubscriptOperator) {
2882+
auto *UDE = cast<UnresolvedDotExpr>(commonAnchor);
2883+
DE.diagnose(commonAnchor->getLoc(),
2884+
diag::could_not_find_subscript_member_did_you_mean,
2885+
getType(UDE->getBase()));
2886+
} else if (fix->getKind() == FixKind::TreatRValueAsLValue) {
2887+
DE.diagnose(commonAnchor->getLoc(),
2888+
diag::no_overloads_match_exactly_in_assignment,
2889+
decl->getBaseName());
2890+
} else if (llvm::all_of(
2891+
viableSolutions,
2892+
[](const Solution *viable) {
2893+
auto *locator = viable->Fixes.front()->getLocator();
2894+
return locator
2895+
->isLastElement<LocatorPathElt::ContextualType>();
2896+
})) {
2897+
auto baseName = name.getBaseName();
2898+
DE.diagnose(commonAnchor->getLoc(), diag::no_candidates_match_result_type,
2899+
baseName.userFacingName(), getContextualType());
2900+
} else {
2901+
emitGeneralAmbiguityFailure();
28582902
}
28592903

28602904
for (const auto &viable : viableSolutions) {
28612905
// Create scope so each applied solution is rolled back.
28622906
ConstraintSystem::SolverScope scope(*this);
2863-
applySolution(*viable.first);
2907+
applySolution(*viable);
28642908
// All of the solutions supposed to produce a "candidate" note.
2865-
diagnosed &= viable.second->diagnose(/*asNote*/ true);
2909+
diagnosed &= viable->Fixes.front()->diagnose(/*asNote*/ true);
28662910
}
28672911

28682912
// If not all of the fixes produced a note, we can't diagnose this.

test/Constraints/diagnostics.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,9 @@ enum Color {
492492
static func rainbow() -> Color {}
493493

494494
static func overload(a : Int) -> Color {} // expected-note {{incorrect labels for candidate (have: '(_:)', expected: '(a:)')}}
495+
// expected-note@-1 {{candidate has partially matching parameter list (a: Int)}}
495496
static func overload(b : Int) -> Color {} // expected-note {{incorrect labels for candidate (have: '(_:)', expected: '(b:)')}}
497+
// expected-note@-1 {{candidate has partially matching parameter list (b: Int)}}
496498

497499
static func frob(_ a : Int, b : inout Int) -> Color {}
498500
static var svar: Color { return .Red }
@@ -517,8 +519,7 @@ let _ : (Int, Float) = (42.0, 12) // expected-error {{cannot convert value of t
517519
let _ : Color = .rainbow // expected-error {{member 'rainbow()' is a function; did you mean to call it?}} {{25-25=()}}
518520

519521
let _: Color = .overload(a : 1.0) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}}
520-
let _: Color = .overload(1.0) // expected-error {{ambiguous reference to member 'overload'}}
521-
// expected-note @-1 {{overloads for 'overload' exist with these partially matching parameter lists: (a: Int), (b: Int)}}
522+
let _: Color = .overload(1.0) // expected-error {{no exact matches in call to static method 'overload'}}
522523
let _: Color = .overload(1) // expected-error {{no exact matches in call to static method 'overload'}}
523524
let _: Color = .frob(1.0, &i) // expected-error {{missing argument label 'b:' in call}}
524525
// expected-error@-1 {{cannot convert value of type 'Double' to expected argument type 'Int'}}

test/Constraints/super_constructor.swift

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ class D : B {
2020
}
2121

2222
init(g:Int) {
23-
super.init("aoeu") // expected-error{{argument labels '(_:)' do not match any available overloads}}
24-
// expected-note @-1 {{overloads for 'B.init' exist with these partially matching parameter lists: (a: UnicodeScalar), (b: UnicodeScalar), (x: Int), (z: Float)}}
23+
super.init("aoeu") // expected-error{{no exact matches in call to initializer}}
2524
}
2625

2726
init(h:Int) {
@@ -40,15 +39,15 @@ class B {
4039
init() {
4140
}
4241

43-
init(x:Int) {
42+
init(x:Int) { // expected-note{{candidate has partially matching parameter list (x: Int)}}
4443
}
4544

46-
init(a:UnicodeScalar) {
45+
init(a:UnicodeScalar) { // expected-note {{candidate has partially matching parameter list (a: UnicodeScalar)}}
4746
}
48-
init(b:UnicodeScalar) {
47+
init(b:UnicodeScalar) { // expected-note{{candidate has partially matching parameter list (b: UnicodeScalar)}}
4948
}
5049

51-
init(z:Float) {
50+
init(z:Float) { // expected-note{{candidate has partially matching parameter list (z: Float)}}
5251
super.init() // expected-error{{'super' members cannot be referenced in a root class}}
5352
}
5453
}

test/expr/postfix/dot/init_ref_delegation.swift

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -323,13 +323,12 @@ class TestOverloadSets {
323323
self.init(5, 5) // expected-error{{extra argument in call}}
324324
}
325325

326-
convenience init(a : Z0) {
327-
self.init(42 as Int8) // expected-error{{argument labels '(_:)' do not match any available overloads}}
328-
// expected-note @-1 {{overloads for 'TestOverloadSets.init' exist with these partially matching parameter lists: (a: Z0), (value: Double), (value: Int)}}
326+
convenience init(a : Z0) { // expected-note{{candidate has partially matching parameter list (a: Z0)}}
327+
self.init(42 as Int8) // expected-error{{no exact matches in call to initializer}}
329328
}
330329

331-
init(value: Int) { /* ... */ }
332-
init(value: Double) { /* ... */ }
330+
init(value: Int) { /* ... */ } // expected-note{{candidate has partially matching parameter list (value: Int)}}
331+
init(value: Double) { /* ... */ } // expected-note{{candidate has partially matching parameter list (value: Double)}}
333332
}
334333

335334
class TestNestedExpr {

0 commit comments

Comments
 (0)