Skip to content

Commit 7d3f5e3

Browse files
committed
[ConstraintSystem] Overhaul ambiguity diagnostics
Refactor `diagnoseAmbiguityWithFixes` as follows: - Aggregate all of the available fixes based on callee locator; - For each ambiguous overload match aggregated fixes and diagnose; - Discard all of the fixes which have been already considered as part of overload diagnostics; - Diagnose remaining (uniqued based on kind + locator) fixes iff they appear in all of the solutions.
1 parent a888518 commit 7d3f5e3

File tree

1 file changed

+55
-154
lines changed

1 file changed

+55
-154
lines changed

lib/Sema/ConstraintSystem.cpp

Lines changed: 55 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -3063,43 +3063,19 @@ diagnoseAmbiguityWithEphemeralPointers(ConstraintSystem &cs,
30633063
}
30643064

30653065
static bool diagnoseAmbiguity(
3066-
ConstraintSystem &cs,
3066+
ConstraintSystem &cs, const SolutionDiff::OverloadDiff &ambiguity,
30673067
ArrayRef<std::pair<const Solution *, const ConstraintFix *>> aggregateFix,
3068-
SolutionDiff &solutionDiff, ArrayRef<Solution> solutions) {
3068+
ArrayRef<Solution> solutions) {
3069+
auto *locator = aggregateFix.front().second->getLocator();
30693070
auto anchor = aggregateFix.front().second->getAnchor();
30703071

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-
30963072
auto &DE = cs.getASTContext().Diags;
30973073

3098-
auto *decl = ambiguity->choices.front().getDeclOrNull();
3074+
auto *decl = ambiguity.choices.front().getDeclOrNull();
30993075
if (!decl)
31003076
return false;
31013077

3102-
auto *commonCalleeLocator = ambiguity->locator;
3078+
auto *commonCalleeLocator = ambiguity.locator;
31033079

31043080
bool diagnosed = true;
31053081
{
@@ -3112,7 +3088,11 @@ static bool diagnoseAmbiguity(
31123088
const auto name = decl->getName();
31133089

31143090
// Emit an error message for the ambiguity.
3115-
if (name.isOperator()) {
3091+
if (locator->isForContextualType()) {
3092+
auto baseName = name.getBaseName();
3093+
DE.diagnose(getLoc(commonAnchor), diag::no_candidates_match_result_type,
3094+
baseName.userFacingName(), cs.getContextualType(anchor));
3095+
} else if (name.isOperator()) {
31163096
auto *anchor = castToExpr(commonCalleeLocator->getAnchor());
31173097

31183098
// If operator is "applied" e.g. `1 + 2` there are tailored
@@ -3217,145 +3197,66 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes(
32173197
if (diagnoseAmbiguityWithEphemeralPointers(*this, solutions))
32183198
return true;
32193199

3220-
// Collect aggregated fixes from all solutions
3221-
using LocatorAndKind = std::pair<ConstraintLocator *, FixKind>;
3222-
using SolutionAndFix = std::pair<const Solution *, const ConstraintFix *>;
3223-
llvm::SmallMapVector<LocatorAndKind, llvm::SmallVector<SolutionAndFix, 4>, 4>
3224-
aggregatedFixes;
3225-
for (const auto &solution : solutions) {
3226-
for (const auto *fix : solution.Fixes) {
3227-
LocatorAndKind key(fix->getLocator(), fix->getKind());
3228-
aggregatedFixes[key].emplace_back(&solution, fix);
3229-
}
3230-
}
3200+
using Fix = std::pair<const Solution *, const ConstraintFix *>;
32313201

3232-
// If there is an overload difference, let's see if there's a common callee
3233-
// locator for all of the fixes.
3234-
auto ambiguousOverload = llvm::find_if(solutionDiff.overloads,
3235-
[&](const auto &overloadDiff) {
3236-
return llvm::all_of(aggregatedFixes, [&](const auto &aggregatedFix) {
3237-
auto *locator = aggregatedFix.first.first;
3238-
auto anchor = locator->getAnchor();
3239-
// Assignment failures are all about the source expression, because
3240-
// they treat destination as a contextual type.
3241-
if (auto *assignExpr = getAsExpr<AssignExpr>(anchor))
3242-
anchor = assignExpr->getSrc();
3243-
3244-
if (auto *callExpr = getAsExpr<CallExpr>(anchor)) {
3245-
if (!isa<TypeExpr>(callExpr->getDirectCallee()))
3246-
anchor = callExpr->getDirectCallee();
3247-
} else if (auto *applyExpr = getAsExpr<ApplyExpr>(anchor)) {
3248-
anchor = applyExpr->getFn();
3249-
}
3202+
llvm::SmallSetVector<Fix, 4> fixes;
3203+
for (auto &solution : solutions) {
3204+
for (auto *fix : solution.Fixes)
3205+
fixes.insert({&solution, fix});
3206+
}
32503207

3251-
return overloadDiff.locator->getAnchor() == anchor;
3252-
});
3253-
});
3208+
llvm::MapVector<ConstraintLocator *, SmallVector<Fix, 4>> fixesByCallee;
32543209

3255-
// If we didn't find an ambiguous overload, diagnose the common fixes.
3256-
if (ambiguousOverload == solutionDiff.overloads.end()) {
3257-
bool diagnosed = false;
3258-
for (auto fixes: aggregatedFixes) {
3259-
// A common fix must appear in all solutions
3260-
auto &commonFixes = fixes.second;
3261-
if (commonFixes.size() != solutions.size()) continue;
3210+
for (const auto &entry : fixes) {
3211+
const auto &solution = *entry.first;
3212+
const auto *fix = entry.second;
3213+
auto *calleeLocator = solution.getCalleeLocator(fix->getLocator());
32623214

3263-
auto *firstFix = commonFixes.front().second;
3264-
diagnosed |= firstFix->diagnoseForAmbiguity(commonFixes);
3265-
}
3266-
return diagnosed;
3215+
fixesByCallee[calleeLocator].push_back({&solution, fix});
32673216
}
32683217

3269-
auto *commonCalleeLocator = ambiguousOverload->locator;
3270-
auto *decl = ambiguousOverload->choices.front().getDecl();
3271-
assert(solverState);
3218+
bool diagnosed = false;
32723219

3273-
bool diagnosed = true;
3274-
{
3275-
DiagnosticTransaction transaction(getASTContext().Diags);
3220+
// All of the fixes which have been considered already.
3221+
llvm::SmallSetVector<Fix, 4> consideredFixes;
32763222

3277-
auto commonAnchor = commonCalleeLocator->getAnchor();
3278-
if (auto *callExpr = getAsExpr<CallExpr>(commonAnchor))
3279-
commonAnchor = callExpr->getDirectCallee();
3280-
auto &DE = getASTContext().Diags;
3281-
const auto name = decl->getName();
3223+
for (const auto &ambiguity : solutionDiff.overloads) {
3224+
auto fixes = fixesByCallee.find(ambiguity.locator);
3225+
if (fixes == fixesByCallee.end())
3226+
continue;
32823227

3283-
// Emit an error message for the ambiguity.
3284-
if (aggregatedFixes.size() == 1 &&
3285-
aggregatedFixes.front().first.first->isForContextualType()) {
3286-
auto anchor = aggregatedFixes.front().first.first->getAnchor();
3287-
auto baseName = name.getBaseName();
3288-
DE.diagnose(getLoc(commonAnchor), diag::no_candidates_match_result_type,
3289-
baseName.userFacingName(), getContextualType(anchor));
3290-
} else if (name.isOperator()) {
3291-
auto *anchor = castToExpr(commonCalleeLocator->getAnchor());
3228+
auto aggregate = fixes->second;
3229+
diagnosed |= ::diagnoseAmbiguity(*this, ambiguity, aggregate, solutions);
32923230

3293-
// If operator is "applied" e.g. `1 + 2` there are tailored
3294-
// diagnostics in case of ambiguity, but if it's referenced
3295-
// e.g. `arr.sort(by: <)` it's better to produce generic error
3296-
// and a note per candidate.
3297-
if (auto *parentExpr = getParentExpr(anchor)) {
3298-
if (isa<ApplyExpr>(parentExpr)) {
3299-
diagnoseOperatorAmbiguity(*this, name.getBaseIdentifier(), solutions,
3300-
commonCalleeLocator);
3301-
return true;
3302-
}
3303-
}
3231+
consideredFixes.insert(aggregate.begin(), aggregate.end());
3232+
}
33043233

3305-
DE.diagnose(anchor->getLoc(), diag::no_overloads_match_exactly_in_call,
3306-
/*isApplication=*/false, decl->getDescriptiveKind(),
3307-
name.isSpecial(), name.getBaseName());
3308-
} else {
3309-
bool isApplication =
3310-
llvm::any_of(ArgumentInfos, [&](const auto &argInfo) {
3311-
return argInfo.first->getAnchor() == commonAnchor;
3312-
});
3234+
// Remove all of the fixes which have been attached to ambiguous
3235+
// overload choices.
3236+
fixes.set_subtract(consideredFixes);
33133237

3314-
DE.diagnose(getLoc(commonAnchor),
3315-
diag::no_overloads_match_exactly_in_call, isApplication,
3316-
decl->getDescriptiveKind(), name.isSpecial(),
3317-
name.getBaseName());
3318-
}
3238+
llvm::MapVector<std::pair<FixKind, ConstraintLocator *>, SmallVector<Fix, 4>>
3239+
fixesByKind;
33193240

3320-
// Produce candidate notes
3321-
SmallPtrSet<ValueDecl *, 4> distinctChoices;
3322-
llvm::SmallSet<CanType, 4> candidateTypes;
3323-
for (const auto &solution: solutions) {
3324-
auto overload = solution.getOverloadChoice(commonCalleeLocator);
3325-
auto *decl = overload.choice.getDecl();
3326-
auto type = solution.simplifyType(overload.openedType);
3327-
// Skip if we've already produced a note for this overload
3328-
if (!distinctChoices.insert(decl).second)
3329-
continue;
3241+
for (const auto &entry : fixes) {
3242+
const auto *fix = entry.second;
3243+
fixesByKind[{fix->getKind(), fix->getLocator()}].push_back(
3244+
{entry.first, fix});
3245+
}
33303246

3331-
if (solution.Fixes.size() == 1) {
3332-
diagnosed &=
3333-
solution.Fixes.front()->diagnose(solution, /*asNote*/ true);
3334-
} else if (llvm::all_of(solution.Fixes,
3335-
[&](ConstraintFix *fix) {
3336-
return fix->getLocator()
3337-
->findLast<LocatorPathElt::ApplyArgument>().hasValue();
3338-
})) {
3339-
// All fixes have to do with arguments, so let's show the parameter lists.
3340-
auto *fn = type->getAs<AnyFunctionType>();
3341-
assert(fn);
3342-
DE.diagnose(decl->getLoc(),
3343-
diag::candidate_partial_match,
3344-
fn->getParamListAsString(fn->getParams()));
3345-
} else {
3346-
// Emit a general "found candidate" note
3347-
if (decl->getLoc().isInvalid()) {
3348-
if (candidateTypes.insert(type->getCanonicalType()).second)
3349-
DE.diagnose(getLoc(commonAnchor), diag::found_candidate_type, type);
3350-
} else {
3351-
DE.diagnose(decl->getLoc(), diag::found_candidate);
3352-
}
3353-
}
3247+
// If leftover fix is contained in all of the solutions let's
3248+
// diagnose it as ambiguity.
3249+
for (const auto &entry : fixesByKind) {
3250+
if (llvm::all_of(solutions, [&](const Solution &solution) -> bool {
3251+
return llvm::any_of(
3252+
solution.Fixes, [&](const ConstraintFix *fix) -> bool {
3253+
return std::make_pair(fix->getKind(), fix->getLocator()) ==
3254+
entry.first;
3255+
});
3256+
})) {
3257+
auto &aggregate = entry.second;
3258+
diagnosed |= aggregate.front().second->diagnoseForAmbiguity(aggregate);
33543259
}
3355-
3356-
// If not all of the fixes produced a note, we can't diagnose this.
3357-
if (!diagnosed)
3358-
transaction.abort();
33593260
}
33603261

33613262
return diagnosed;

0 commit comments

Comments
 (0)