Skip to content

Commit a888518

Browse files
committed
[ConstraintSystem] Encapsulate a way to diagnose ambiguity for aggregate fixes
1 parent 6b52016 commit a888518

File tree

1 file changed

+124
-0
lines changed

1 file changed

+124
-0
lines changed

lib/Sema/ConstraintSystem.cpp

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3062,6 +3062,130 @@ diagnoseAmbiguityWithEphemeralPointers(ConstraintSystem &cs,
30623062
return cs.diagnoseAmbiguity(solutions);
30633063
}
30643064

3065+
static bool diagnoseAmbiguity(
3066+
ConstraintSystem &cs,
3067+
ArrayRef<std::pair<const Solution *, const ConstraintFix *>> aggregateFix,
3068+
SolutionDiff &solutionDiff, ArrayRef<Solution> solutions) {
3069+
auto anchor = aggregateFix.front().second->getAnchor();
3070+
3071+
// Assignment failures are all about the source expression, because
3072+
// they treat destination as a contextual type.
3073+
if (auto *assignExpr = getAsExpr<AssignExpr>(anchor))
3074+
anchor = assignExpr->getSrc();
3075+
3076+
if (auto *callExpr = getAsExpr<CallExpr>(anchor)) {
3077+
if (!isa<TypeExpr>(callExpr->getDirectCallee()))
3078+
anchor = callExpr->getDirectCallee();
3079+
} else if (auto *applyExpr = getAsExpr<ApplyExpr>(anchor)) {
3080+
anchor = applyExpr->getFn();
3081+
}
3082+
3083+
auto ambiguity =
3084+
llvm::find_if(solutionDiff.overloads,
3085+
[&anchor](const SolutionDiff::OverloadDiff &diff) -> bool {
3086+
return diff.locator->getAnchor() == anchor;
3087+
});
3088+
3089+
if (ambiguity == solutionDiff.overloads.end()) {
3090+
auto &fix = aggregateFix.front();
3091+
return aggregateFix.size() == 1
3092+
? fix.second->diagnose(*fix.first, /*asNote=*/false)
3093+
: fix.second->diagnoseForAmbiguity(aggregateFix);
3094+
}
3095+
3096+
auto &DE = cs.getASTContext().Diags;
3097+
3098+
auto *decl = ambiguity->choices.front().getDeclOrNull();
3099+
if (!decl)
3100+
return false;
3101+
3102+
auto *commonCalleeLocator = ambiguity->locator;
3103+
3104+
bool diagnosed = true;
3105+
{
3106+
DiagnosticTransaction transaction(DE);
3107+
3108+
auto commonAnchor = commonCalleeLocator->getAnchor();
3109+
if (auto *callExpr = getAsExpr<CallExpr>(commonAnchor))
3110+
commonAnchor = callExpr->getDirectCallee();
3111+
3112+
const auto name = decl->getName();
3113+
3114+
// Emit an error message for the ambiguity.
3115+
if (name.isOperator()) {
3116+
auto *anchor = castToExpr(commonCalleeLocator->getAnchor());
3117+
3118+
// If operator is "applied" e.g. `1 + 2` there are tailored
3119+
// diagnostics in case of ambiguity, but if it's referenced
3120+
// e.g. `arr.sort(by: <)` it's better to produce generic error
3121+
// and a note per candidate.
3122+
if (auto *parentExpr = cs.getParentExpr(anchor)) {
3123+
if (isa<ApplyExpr>(parentExpr)) {
3124+
diagnoseOperatorAmbiguity(cs, name.getBaseIdentifier(), solutions,
3125+
commonCalleeLocator);
3126+
return true;
3127+
}
3128+
}
3129+
3130+
DE.diagnose(anchor->getLoc(), diag::no_overloads_match_exactly_in_call,
3131+
/*isApplication=*/false, decl->getDescriptiveKind(),
3132+
name.isSpecial(), name.getBaseName());
3133+
} else {
3134+
bool isApplication =
3135+
llvm::any_of(cs.ArgumentInfos, [&](const auto &argInfo) {
3136+
return argInfo.first->getAnchor() == commonAnchor;
3137+
});
3138+
3139+
DE.diagnose(getLoc(commonAnchor),
3140+
diag::no_overloads_match_exactly_in_call, isApplication,
3141+
decl->getDescriptiveKind(), name.isSpecial(),
3142+
name.getBaseName());
3143+
}
3144+
3145+
// Produce candidate notes
3146+
SmallPtrSet<ValueDecl *, 4> distinctChoices;
3147+
llvm::SmallSet<CanType, 4> candidateTypes;
3148+
for (const auto &solution : solutions) {
3149+
auto overload = solution.getOverloadChoice(commonCalleeLocator);
3150+
auto *decl = overload.choice.getDecl();
3151+
auto type = solution.simplifyType(overload.openedType);
3152+
// Skip if we've already produced a note for this overload
3153+
if (!distinctChoices.insert(decl).second)
3154+
continue;
3155+
3156+
if (solution.Fixes.size() == 1) {
3157+
diagnosed &=
3158+
solution.Fixes.front()->diagnose(solution, /*asNote*/ true);
3159+
} else if (llvm::all_of(solution.Fixes, [&](ConstraintFix *fix) {
3160+
return fix->getLocator()
3161+
->findLast<LocatorPathElt::ApplyArgument>()
3162+
.hasValue();
3163+
})) {
3164+
// All fixes have to do with arguments, so let's show the parameter
3165+
// lists.
3166+
auto *fn = type->getAs<AnyFunctionType>();
3167+
assert(fn);
3168+
DE.diagnose(decl->getLoc(), diag::candidate_partial_match,
3169+
fn->getParamListAsString(fn->getParams()));
3170+
} else {
3171+
// Emit a general "found candidate" note
3172+
if (decl->getLoc().isInvalid()) {
3173+
if (candidateTypes.insert(type->getCanonicalType()).second)
3174+
DE.diagnose(getLoc(commonAnchor), diag::found_candidate_type, type);
3175+
} else {
3176+
DE.diagnose(decl->getLoc(), diag::found_candidate);
3177+
}
3178+
}
3179+
}
3180+
3181+
// If not all of the fixes produced a note, we can't diagnose this.
3182+
if (!diagnosed)
3183+
transaction.abort();
3184+
}
3185+
3186+
return diagnosed;
3187+
}
3188+
30653189
bool ConstraintSystem::diagnoseAmbiguityWithFixes(
30663190
SmallVectorImpl<Solution> &solutions) {
30673191
if (solutions.empty())

0 commit comments

Comments
 (0)