Skip to content

Commit 69d0642

Browse files
[ConstraintSystem] Adjust diagnoseAmbiguityWithFixes to look for common fixes in all solutions first and try diagnose if not fall back to common anchor diagnostic
1 parent 9f18130 commit 69d0642

File tree

1 file changed

+149
-107
lines changed

1 file changed

+149
-107
lines changed

lib/Sema/ConstraintSystem.cpp

Lines changed: 149 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -2958,6 +2958,8 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes(
29582958
// Collect aggregated fixes from all solutions
29592959
using LocatorAndKind = std::pair<ConstraintLocator *, FixKind>;
29602960
using SolutionAndFix = std::pair<const Solution *, const ConstraintFix *>;
2961+
using AggregatedEntry = std::pair<LocatorAndKind,
2962+
llvm::SmallVector<SolutionAndFix, 4>>;
29612963
llvm::SmallMapVector<LocatorAndKind, llvm::SmallVector<SolutionAndFix, 4>, 4>
29622964
aggregatedFixes;
29632965
for (const auto &solution : solutions) {
@@ -2966,134 +2968,174 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes(
29662968
aggregatedFixes[key].emplace_back(&solution, fix);
29672969
}
29682970
}
2971+
2972+
auto getAggregatedFixAnchor = [](const AggregatedEntry &aggregatedFix) {
2973+
auto *locator = aggregatedFix.first.first;
2974+
auto anchor = locator->getAnchor();
2975+
// Assignment failures are all about the source expression, because
2976+
// they treat destination as a contextual type.
2977+
if (auto *assignExpr = getAsExpr<AssignExpr>(anchor))
2978+
anchor = assignExpr->getSrc();
2979+
2980+
if (auto *callExpr = getAsExpr<CallExpr>(anchor)) {
2981+
if (!isa<TypeExpr>(callExpr->getDirectCallee()))
2982+
anchor = callExpr->getDirectCallee();
2983+
} else if (auto *applyExpr = getAsExpr<ApplyExpr>(anchor)) {
2984+
anchor = applyExpr->getFn();
2985+
}
2986+
return anchor;
2987+
};
2988+
2989+
SmallPtrSet<SolutionDiff::OverloadDiff* , 4> ambiguosOverloads;
29692990

2970-
// If there is an overload difference, let's see if there's a common callee
2971-
// locator for all of the fixes.
2972-
auto ambiguousOverload = llvm::find_if(solutionDiff.overloads,
2973-
[&](const auto &overloadDiff) {
2974-
return llvm::all_of(aggregatedFixes, [&](const auto &aggregatedFix) {
2975-
auto *locator = aggregatedFix.first.first;
2976-
auto anchor = locator->getAnchor();
2977-
// Assignment failures are all about the source expression, because
2978-
// they treat destination as a contextual type.
2979-
if (auto *assignExpr = getAsExpr<AssignExpr>(anchor))
2980-
anchor = assignExpr->getSrc();
2981-
2982-
if (auto *callExpr = getAsExpr<CallExpr>(anchor)) {
2983-
if (!isa<TypeExpr>(callExpr->getDirectCallee()))
2984-
anchor = callExpr->getDirectCallee();
2985-
} else if (auto *applyExpr = getAsExpr<ApplyExpr>(anchor)) {
2986-
anchor = applyExpr->getFn();
2987-
}
2988-
2989-
return overloadDiff.locator->getAnchor() == anchor;
2991+
bool diagnosed = false;
2992+
for (const AggregatedEntry &aggregatedFix : aggregatedFixes) {
2993+
auto ambiguousOverload =
2994+
llvm::find_if(solutionDiff.overloads, [&](const auto &overloadDiff) {
2995+
return overloadDiff.locator->getAnchor() ==
2996+
getAggregatedFixAnchor(aggregatedFix);
29902997
});
2991-
});
29922998

2993-
// If we didn't find an ambiguous overload, diagnose the common fixes.
2994-
if (ambiguousOverload == solutionDiff.overloads.end()) {
2995-
bool diagnosed = false;
2996-
for (auto fixes: aggregatedFixes) {
2999+
if (ambiguousOverload == solutionDiff.overloads.end()) {
29973000
// A common fix must appear in all solutions
2998-
auto &commonFixes = fixes.second;
2999-
if (commonFixes.size() != solutions.size()) continue;
3001+
auto &commonFixes = aggregatedFix.second;
3002+
if (commonFixes.size() != solutions.size())
3003+
continue;
30003004

30013005
auto *firstFix = commonFixes.front().second;
30023006
diagnosed |= firstFix->diagnoseForAmbiguity(commonFixes);
3007+
} else {
3008+
ambiguosOverloads.insert(ambiguousOverload);
30033009
}
3004-
return diagnosed;
30053010
}
30063011

3007-
auto *commonCalleeLocator = ambiguousOverload->locator;
3008-
auto *decl = ambiguousOverload->choices.front().getDecl();
3009-
assert(solverState);
3012+
if (diagnosed)
3013+
return true;
3014+
3015+
// If all non-overloaded common fixes do not appear in all solutions
3016+
// nor are diagnosed for ambiguity, let's try to fall back to common
3017+
// anchor diagnostic.
30103018

3011-
bool diagnosed = true;
3012-
{
3013-
DiagnosticTransaction transaction(getASTContext().Diags);
3019+
if (ambiguosOverloads.empty())
3020+
return false;
30143021

3015-
auto commonAnchor = commonCalleeLocator->getAnchor();
3016-
if (auto *callExpr = getAsExpr<CallExpr>(commonAnchor))
3017-
commonAnchor = callExpr->getDirectCallee();
3018-
auto &DE = getASTContext().Diags;
3019-
const auto name = decl->getName();
3020-
3021-
// Emit an error message for the ambiguity.
3022-
if (aggregatedFixes.size() == 1 &&
3023-
aggregatedFixes.front().first.first->isForContextualType()) {
3024-
auto anchor = aggregatedFixes.front().first.first->getAnchor();
3025-
auto baseName = name.getBaseName();
3026-
DE.diagnose(getLoc(commonAnchor), diag::no_candidates_match_result_type,
3027-
baseName.userFacingName(), getContextualType(anchor));
3028-
} else if (name.isOperator()) {
3029-
auto *anchor = castToExpr(commonCalleeLocator->getAnchor());
3030-
3031-
// If operator is "applied" e.g. `1 + 2` there are tailored
3032-
// diagnostics in case of ambiguity, but if it's referenced
3033-
// e.g. `arr.sort(by: <)` it's better to produce generic error
3034-
// and a note per candidate.
3035-
if (auto *parentExpr = getParentExpr(anchor)) {
3036-
if (isa<ApplyExpr>(parentExpr)) {
3037-
diagnoseOperatorAmbiguity(*this, name.getBaseIdentifier(), solutions,
3038-
commonCalleeLocator);
3039-
return true;
3022+
diagnosed = true;
3023+
for (auto ambiguousOverload : ambiguosOverloads) {
3024+
auto *commonCalleeLocator = ambiguousOverload->locator;
3025+
auto *decl = ambiguousOverload->choices.front().getDecl();
3026+
assert(solverState);
3027+
3028+
{
3029+
DiagnosticTransaction transaction(getASTContext().Diags);
3030+
3031+
auto commonAnchor = commonCalleeLocator->getAnchor();
3032+
if (auto *callExpr = getAsExpr<CallExpr>(commonAnchor))
3033+
commonAnchor = callExpr->getDirectCallee();
3034+
auto &DE = getASTContext().Diags;
3035+
const auto name = decl->getName();
3036+
3037+
// Emit an error message for the ambiguity.
3038+
if (aggregatedFixes.size() == 1 &&
3039+
aggregatedFixes.front().first.first->isForContextualType()) {
3040+
auto anchor = aggregatedFixes.front().first.first->getAnchor();
3041+
auto baseName = name.getBaseName();
3042+
DE.diagnose(getLoc(commonAnchor), diag::no_candidates_match_result_type,
3043+
baseName.userFacingName(), getContextualType(anchor));
3044+
} else if (name.isOperator()) {
3045+
auto *anchor = castToExpr(commonCalleeLocator->getAnchor());
3046+
3047+
// If operator is "applied" e.g. `1 + 2` there are tailored
3048+
// diagnostics in case of ambiguity, but if it's referenced
3049+
// e.g. `arr.sort(by: <)` it's better to produce generic error
3050+
// and a note per candidate.
3051+
if (auto *parentExpr = getParentExpr(anchor)) {
3052+
if (isa<ApplyExpr>(parentExpr)) {
3053+
diagnoseOperatorAmbiguity(*this, name.getBaseIdentifier(),
3054+
solutions, commonCalleeLocator);
3055+
return true;
3056+
}
30403057
}
3041-
}
30423058

3043-
DE.diagnose(anchor->getLoc(), diag::no_overloads_match_exactly_in_call,
3044-
/*isApplication=*/false, decl->getDescriptiveKind(),
3045-
name.isSpecial(), name.getBaseName());
3046-
} else {
3047-
bool isApplication =
3048-
llvm::any_of(ArgumentInfos, [&](const auto &argInfo) {
3049-
return argInfo.first->getAnchor() == commonAnchor;
3050-
});
3059+
DE.diagnose(anchor->getLoc(), diag::no_overloads_match_exactly_in_call,
3060+
/*isApplication=*/false, decl->getDescriptiveKind(),
3061+
name.isSpecial(), name.getBaseName());
3062+
} else {
3063+
bool isApplication =
3064+
llvm::any_of(ArgumentInfos, [&](const auto &argInfo) {
3065+
return argInfo.first->getAnchor() == commonAnchor;
3066+
});
30513067

3052-
DE.diagnose(getLoc(commonAnchor),
3053-
diag::no_overloads_match_exactly_in_call, isApplication,
3054-
decl->getDescriptiveKind(), name.isSpecial(),
3055-
name.getBaseName());
3056-
}
3068+
DE.diagnose(getLoc(commonAnchor),
3069+
diag::no_overloads_match_exactly_in_call, isApplication,
3070+
decl->getDescriptiveKind(), name.isSpecial(),
3071+
name.getBaseName());
3072+
}
30573073

3058-
// Produce candidate notes
3059-
SmallPtrSet<ValueDecl *, 4> distinctChoices;
3060-
llvm::SmallSet<CanType, 4> candidateTypes;
3061-
for (const auto &solution: solutions) {
3062-
auto overload = solution.getOverloadChoice(commonCalleeLocator);
3063-
auto *decl = overload.choice.getDecl();
3064-
auto type = solution.simplifyType(overload.openedType);
3065-
// Skip if we've already produced a note for this overload
3066-
if (!distinctChoices.insert(decl).second)
3067-
continue;
3074+
// Produce candidate notes
3075+
SmallPtrSet<ValueDecl *, 4> distinctChoices;
3076+
llvm::SmallSet<CanType, 4> candidateTypes;
3077+
for (const auto &solution : solutions) {
3078+
auto overload = solution.getOverloadChoice(commonCalleeLocator);
3079+
auto *decl = overload.choice.getDecl();
3080+
auto type = solution.simplifyType(overload.openedType);
3081+
// Skip if we've already produced a note for this overload
3082+
if (!distinctChoices.insert(decl).second)
3083+
continue;
30683084

3069-
if (solution.Fixes.size() == 1) {
3070-
diagnosed &=
3071-
solution.Fixes.front()->diagnose(solution, /*asNote*/ true);
3072-
} else if (llvm::all_of(solution.Fixes,
3073-
[&](ConstraintFix *fix) {
3074-
return fix->getLocator()
3075-
->findLast<LocatorPathElt::ApplyArgument>().hasValue();
3076-
})) {
3077-
// All fixes have to do with arguments, so let's show the parameter lists.
3078-
auto *fn = type->getAs<AnyFunctionType>();
3079-
assert(fn);
3080-
DE.diagnose(decl->getLoc(),
3081-
diag::candidate_partial_match,
3082-
fn->getParamListAsString(fn->getParams()));
3083-
} else {
3084-
// Emit a general "found candidate" note
3085-
if (decl->getLoc().isInvalid()) {
3086-
if (candidateTypes.insert(type->getCanonicalType()).second)
3087-
DE.diagnose(getLoc(commonAnchor), diag::found_candidate_type, type);
3085+
if (solution.Fixes.size() == 1) {
3086+
diagnosed &=
3087+
solution.Fixes.front()->diagnose(solution, /*asNote*/ true);
30883088
} else {
3089-
DE.diagnose(decl->getLoc(), diag::found_candidate);
3089+
auto emitGeneralFoundCandidate = [&]() {
3090+
// Emit a general "found candidate" note
3091+
if (decl->getLoc().isInvalid()) {
3092+
if (candidateTypes.insert(type->getCanonicalType()).second)
3093+
DE.diagnose(getLoc(commonAnchor), diag::found_candidate_type,
3094+
type);
3095+
} else {
3096+
DE.diagnose(decl->getLoc(), diag::found_candidate);
3097+
}
3098+
};
3099+
3100+
if (llvm::all_of(solution.Fixes, [](ConstraintFix *fix) {
3101+
return fix->getLocator()
3102+
->findLast<LocatorPathElt::ApplyArgument>()
3103+
.hasValue();
3104+
})) {
3105+
3106+
// All fixes have to do with arguments, so let's show the parameter
3107+
// lists.
3108+
auto *fn = type->getAs<AnyFunctionType>();
3109+
assert(fn);
3110+
3111+
if (fn->getNumParams() == 1) {
3112+
// For single param functions if we have a re-label fix,
3113+
// let's prefer note that instead of a general mismatch.
3114+
auto relabelFix =
3115+
llvm::find_if(solution.Fixes, [](ConstraintFix *fix) {
3116+
return fix->getKind() == FixKind::RelabelArguments;
3117+
});
3118+
if (relabelFix != solution.Fixes.end()) {
3119+
(*relabelFix)->diagnose(solution, /*asNote*/ true);
3120+
} else {
3121+
emitGeneralFoundCandidate();
3122+
}
3123+
continue;
3124+
}
3125+
3126+
DE.diagnose(decl->getLoc(), diag::candidate_partial_match,
3127+
fn->getParamListAsString(fn->getParams()));
3128+
3129+
} else {
3130+
emitGeneralFoundCandidate();
3131+
}
30903132
}
30913133
}
3092-
}
30933134

3094-
// If not all of the fixes produced a note, we can't diagnose this.
3095-
if (!diagnosed)
3096-
transaction.abort();
3135+
// If not all of the fixes produced a note, we can't diagnose this.
3136+
if (!diagnosed)
3137+
transaction.abort();
3138+
}
30973139
}
30983140

30993141
return diagnosed;

0 commit comments

Comments
 (0)