Skip to content

Commit 9e73bb3

Browse files
committed
[ConstraintSystem] Start to allow diagnosing ambiguity with fixes
for solution sets with more than one fix.
1 parent 8aa1627 commit 9e73bb3

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
@@ -2070,21 +2070,6 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
20702070
calleeInfo, TCC_ForceRecheck);
20712071
if (!argExpr)
20722072
return true; // already diagnosed.
2073-
2074-
// Handle argument label mismatches when we have multiple candidates.
2075-
if (calleeInfo.closeness == CC_ArgumentLabelMismatch) {
2076-
auto args = decomposeArgType(CS.getType(argExpr), argLabels);
2077-
2078-
// If we have multiple candidates that we fail to match, just say we have
2079-
// the wrong labels and list the candidates out.
2080-
diagnose(callExpr->getLoc(), diag::wrong_argument_labels_overload,
2081-
getParamListAsString(args))
2082-
.highlight(argExpr->getSourceRange());
2083-
2084-
// Did the user intend on invoking a different overload?
2085-
calleeInfo.suggestPotentialOverloads(fnExpr->getLoc());
2086-
return true;
2087-
}
20882073

20892074
auto overloadName = calleeInfo.declName;
20902075

lib/Sema/ConstraintSystem.cpp

Lines changed: 88 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2734,34 +2734,52 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes(
27342734
// overloads of the same declaration.
27352735
ConstraintLocator *commonCalleeLocator = nullptr;
27362736
SmallPtrSet<ValueDecl *, 4> distinctChoices;
2737-
SmallVector<std::pair<const Solution *, const ConstraintFix *>, 4>
2738-
viableSolutions;
2737+
SmallVector<const Solution *, 4> viableSolutions;
2738+
2739+
enum class AmbiguityKind {
2740+
/// There is exactly one fix associated with each candidate.
2741+
CloseMatch,
2742+
/// Solution is ambiguous because all candidates had partially matching
2743+
/// parameter lists.
2744+
ParameterList,
2745+
/// General ambiguity failure.
2746+
General,
2747+
};
2748+
auto ambiguityKind = AmbiguityKind::CloseMatch;
27392749

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

2743-
// Currently only support a single fix in a solution,
2744-
// but ultimately should be able to deal with multiple.
2745-
if (fixes.size() != 1)
2753+
if (fixes.empty())
27462754
return false;
27472755

2748-
const auto *fix = fixes.front();
2749-
auto *locator = fix->getLocator();
2756+
if (fixes.size() > 1) {
2757+
ambiguityKind = (ambiguityKind == AmbiguityKind::CloseMatch ||
2758+
ambiguityKind == AmbiguityKind::ParameterList) &&
2759+
llvm::all_of(fixes, [](const ConstraintFix *fix) -> bool {
2760+
auto *locator = fix->getLocator();
2761+
return locator->findLast<LocatorPathElt::ApplyArgument>().hasValue();
2762+
}) ? AmbiguityKind::ParameterList
2763+
: AmbiguityKind::General;
2764+
}
2765+
2766+
for (const auto *fix: fixes) {
2767+
auto *locator = fix->getLocator();
2768+
// Assignment failures are all about the source expression,
2769+
// because they treat destination as a contextual type.
2770+
if (auto *anchor = locator->getAnchor()) {
2771+
if (auto *assignExpr = dyn_cast<AssignExpr>(anchor))
2772+
locator = getConstraintLocator(assignExpr->getSrc());
2773+
}
27502774

2751-
// Assignment failures are all about the source expression,
2752-
// because they treat destination as a contextual type.
2753-
if (auto *anchor = locator->getAnchor()) {
2754-
if (auto *assignExpr = dyn_cast<AssignExpr>(anchor))
2755-
locator = getConstraintLocator(assignExpr->getSrc());
2775+
auto *calleeLocator = getCalleeLocator(locator);
2776+
if (!commonCalleeLocator)
2777+
commonCalleeLocator = calleeLocator;
2778+
else if (commonCalleeLocator != calleeLocator)
2779+
return false;
27562780
}
27572781

2758-
auto *calleeLocator = getCalleeLocator(locator);
2759-
if (commonCalleeLocator && commonCalleeLocator != calleeLocator)
2760-
return false;
2761-
2762-
commonCalleeLocator = calleeLocator;
2763-
2764-
auto overload = solution.getOverloadChoiceIfAvailable(calleeLocator);
2782+
auto overload = solution.getOverloadChoiceIfAvailable(commonCalleeLocator);
27652783
if (!overload)
27662784
return false;
27672785

@@ -2773,7 +2791,7 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes(
27732791
// as viable, otherwise we'd produce the same diagnostic multiple
27742792
// times, which means that actual problem is elsewhere.
27752793
if (distinctChoices.insert(decl).second)
2776-
viableSolutions.push_back({&solution, fix});
2794+
viableSolutions.push_back(&solution);
27772795
return true;
27782796
});
27792797

@@ -2787,32 +2805,11 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes(
27872805
{
27882806
DiagnosticTransaction transaction(getASTContext().Diags);
27892807

2790-
const auto *fix = viableSolutions.front().second;
27912808
auto *commonAnchor = commonCalleeLocator->getAnchor();
27922809
auto &DE = getASTContext().Diags;
27932810
auto name = decl->getFullName();
27942811

2795-
if (fix->getKind() == FixKind::UseSubscriptOperator) {
2796-
auto *UDE = cast<UnresolvedDotExpr>(commonAnchor);
2797-
DE.diagnose(commonAnchor->getLoc(),
2798-
diag::could_not_find_subscript_member_did_you_mean,
2799-
getType(UDE->getBase()));
2800-
} else if (fix->getKind() == FixKind::TreatRValueAsLValue) {
2801-
DE.diagnose(commonAnchor->getLoc(),
2802-
diag::no_overloads_match_exactly_in_assignment,
2803-
decl->getBaseName());
2804-
} else if (llvm::all_of(
2805-
viableSolutions,
2806-
[](const std::pair<const Solution *, const ConstraintFix *>
2807-
&fix) {
2808-
auto *locator = fix.second->getLocator();
2809-
return locator
2810-
->isLastElement<LocatorPathElt::ContextualType>();
2811-
})) {
2812-
auto baseName = name.getBaseName();
2813-
DE.diagnose(commonAnchor->getLoc(), diag::no_candidates_match_result_type,
2814-
baseName.userFacingName(), getContextualType());
2815-
} else {
2812+
auto emitGeneralAmbiguityFailure = [&]() {
28162813
// Three choices here:
28172814
// 1. If this is a special name avoid printing it because
28182815
// printing kind is sufficient;
@@ -2839,14 +2836,61 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes(
28392836
diag::no_overloads_match_exactly_in_call_no_labels,
28402837
decl->getDescriptiveKind(), name.getBaseName());
28412838
}
2839+
};
2840+
2841+
switch (ambiguityKind) {
2842+
case AmbiguityKind::CloseMatch:
2843+
// Handled below
2844+
break;
2845+
case AmbiguityKind::ParameterList: {
2846+
emitGeneralAmbiguityFailure();
2847+
2848+
for (const auto &viable: viableSolutions) {
2849+
auto overload = viable->getOverloadChoice(commonCalleeLocator);
2850+
auto *fn = overload.openedType->getAs<AnyFunctionType>();
2851+
assert(fn);
2852+
DE.diagnose(overload.choice.getDecl()->getLoc(),
2853+
diag::candidate_partial_match,
2854+
fn->getParamListAsString(fn->getParams()));
2855+
}
2856+
2857+
return true;
2858+
}
2859+
case AmbiguityKind::General:
2860+
// TODO: Handle general ambiguity here.
2861+
return false;
2862+
}
2863+
2864+
auto *fix = viableSolutions.front()->Fixes.front();
2865+
if (fix->getKind() == FixKind::UseSubscriptOperator) {
2866+
auto *UDE = cast<UnresolvedDotExpr>(commonAnchor);
2867+
DE.diagnose(commonAnchor->getLoc(),
2868+
diag::could_not_find_subscript_member_did_you_mean,
2869+
getType(UDE->getBase()));
2870+
} else if (fix->getKind() == FixKind::TreatRValueAsLValue) {
2871+
DE.diagnose(commonAnchor->getLoc(),
2872+
diag::no_overloads_match_exactly_in_assignment,
2873+
decl->getBaseName());
2874+
} else if (llvm::all_of(
2875+
viableSolutions,
2876+
[](const Solution *viable) {
2877+
auto *locator = viable->Fixes.front()->getLocator();
2878+
return locator
2879+
->isLastElement<LocatorPathElt::ContextualType>();
2880+
})) {
2881+
auto baseName = name.getBaseName();
2882+
DE.diagnose(commonAnchor->getLoc(), diag::no_candidates_match_result_type,
2883+
baseName.userFacingName(), getContextualType());
2884+
} else {
2885+
emitGeneralAmbiguityFailure();
28422886
}
28432887

28442888
for (const auto &viable : viableSolutions) {
28452889
// Create scope so each applied solution is rolled back.
28462890
ConstraintSystem::SolverScope scope(*this);
2847-
applySolution(*viable.first);
2891+
applySolution(*viable);
28482892
// All of the solutions supposed to produce a "candidate" note.
2849-
diagnosed &= viable.second->diagnose(/*asNote*/ true);
2893+
diagnosed &= viable->Fixes.front()->diagnose(/*asNote*/ true);
28502894
}
28512895

28522896
// 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)