Skip to content

Commit 607c298

Browse files
hborlaxedin
authored andcommitted
[Diagnostics] Refactor diagnoseAmbigutiyWithFixes to use the differences between
solutions in order to figure out the source of ambiguity.
1 parent d40d47f commit 607c298

File tree

4 files changed

+85
-100
lines changed

4 files changed

+85
-100
lines changed

lib/Sema/ConstraintSystem.cpp

Lines changed: 70 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -2749,13 +2749,66 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes(
27492749
return false;
27502750
}
27512751

2752-
// Problems related to fixes forming ambiguous solution set
2753-
// could only be diagnosed (at the moment), if all of the fixes
2754-
// have the same callee locator, which means they fix different
2755-
// overloads of the same declaration.
2756-
ConstraintLocator *commonCalleeLocator = nullptr;
2752+
// Collect aggregated fixes from all solutions
2753+
llvm::SmallMapVector<std::pair<ConstraintLocator *, FixKind>,
2754+
llvm::SmallVector<ConstraintFix *, 4>, 4> aggregatedFixes;
2755+
for (const auto &solution: solutions) {
2756+
for (auto fixesPerLocator: solution.getAggregatedFixes()) {
2757+
for (auto kindAndFixes: fixesPerLocator.second) {
2758+
aggregatedFixes[{fixesPerLocator.first, kindAndFixes.first}]
2759+
.push_back(kindAndFixes.second.front());
2760+
}
2761+
}
2762+
}
2763+
2764+
// If there are no overload differences, diagnose the common fixes.
2765+
SolutionDiff solutionDiff(solutions);
2766+
if (solutionDiff.overloads.size() == 0) {
2767+
bool diagnosed = false;
2768+
ConstraintSystem::SolverScope scope(*this);
2769+
applySolution(solutions.front());
2770+
for (auto fixes: aggregatedFixes) {
2771+
// A common fix must appear in all solutions
2772+
if (fixes.second.size() < solutions.size()) continue;
2773+
diagnosed |= fixes.second.front()->diagnose();
2774+
}
2775+
return diagnosed;
2776+
}
2777+
2778+
// If there is an overload difference, let's see if there's a common callee
2779+
// locator for all of the fixes.
2780+
auto ambiguousOverload = llvm::find_if(solutionDiff.overloads,
2781+
[&](const auto &overloadDiff) {
2782+
return llvm::all_of(aggregatedFixes, [&](const auto &aggregatedFix) {
2783+
auto *locator = aggregatedFix.first.first;
2784+
auto *anchor = locator->getAnchor();
2785+
// Assignment failures are all about the source expression, because
2786+
// they treat destination as a contextual type.
2787+
if (auto *assignExpr = dyn_cast<AssignExpr>(anchor))
2788+
anchor = assignExpr->getSrc();
2789+
2790+
if (auto *callExpr = dyn_cast<CallExpr>(anchor)) {
2791+
if (!isa<TypeExpr>(callExpr->getDirectCallee()))
2792+
anchor = callExpr->getDirectCallee();
2793+
} else if (auto *applyExpr = dyn_cast<ApplyExpr>(anchor)) {
2794+
anchor = applyExpr->getFn();
2795+
}
2796+
2797+
return overloadDiff.locator->getAnchor() == anchor;
2798+
});
2799+
});
2800+
2801+
if (ambiguousOverload == solutionDiff.overloads.end())
2802+
return false;
2803+
2804+
ConstraintLocator* commonCalleeLocator = ambiguousOverload->locator;
27572805
SmallPtrSet<ValueDecl *, 4> distinctChoices;
27582806
SmallVector<const Solution *, 4> viableSolutions;
2807+
assert(ambiguousOverload->choices.size() == solutions.size());
2808+
for (unsigned i = 0; i < solutions.size(); ++i) {
2809+
if (distinctChoices.insert(ambiguousOverload->choices[i].getDecl()).second)
2810+
viableSolutions.push_back(&solutions[i]);
2811+
}
27592812

27602813
enum class AmbiguityKind {
27612814
/// There is exactly one fix associated with each candidate.
@@ -2771,103 +2824,23 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes(
27712824
};
27722825
auto ambiguityKind = AmbiguityKind::CloseMatch;
27732826

2774-
bool diagnosable = llvm::all_of(solutions, [&](const Solution &solution) {
2775-
ArrayRef<ConstraintFix *> fixes = solution.Fixes;
2776-
2777-
if (fixes.empty())
2827+
// FIXME: Can this be re-written in a cleaner way?
2828+
for (const auto &solution: solutions) {
2829+
if (solution.Fixes.empty())
27782830
return false;
27792831

2780-
if (fixes.size() > 1) {
2781-
// Attempt to disambiguite in cases where the argument matches
2782-
// involves tuple mismatches. e.g.
2783-
// func t<T, U>(_: (T, U), _: (U, T)) {}
2784-
// func useTuples(_ x: Int, y: Float) {
2785-
// t((x, y), (x, y))
2786-
// }
2787-
// So fixes are ambiguous in all solutions.
2788-
if ((ambiguityKind == AmbiguityKind::CloseMatch ||
2789-
ambiguityKind == AmbiguityKind::ArgumentMismatch) &&
2790-
llvm::all_of(fixes, [](const ConstraintFix *fix) -> bool {
2791-
return fix->getKind() == FixKind::AllowTupleTypeMismatch;
2792-
})) {
2793-
ambiguityKind = AmbiguityKind::ArgumentMismatch;
2794-
} else {
2795-
ambiguityKind =
2796-
(ambiguityKind == AmbiguityKind::CloseMatch ||
2797-
ambiguityKind == AmbiguityKind::ArgumentMismatch ||
2798-
ambiguityKind == AmbiguityKind::ParameterList) &&
2799-
llvm::all_of(
2800-
fixes,
2801-
[](const ConstraintFix *fix) -> bool {
2802-
auto *locator = fix->getLocator();
2803-
return locator
2804-
->findLast<LocatorPathElt::ApplyArgument>()
2805-
.hasValue();
2806-
})
2807-
? AmbiguityKind::ParameterList
2808-
: AmbiguityKind::General;
2809-
}
2832+
if (solution.Fixes.size() > 1) {
2833+
ambiguityKind = (ambiguityKind == AmbiguityKind::CloseMatch ||
2834+
ambiguityKind == AmbiguityKind::ParameterList) &&
2835+
llvm::all_of(solution.Fixes, [](const ConstraintFix *fix) -> bool {
2836+
auto *locator = fix->getLocator();
2837+
return locator->findLast<LocatorPathElt::ApplyArgument>().hasValue();
2838+
}) ? AmbiguityKind::ParameterList
2839+
: AmbiguityKind::General;
28102840
}
2811-
2812-
if (fixes.size() == 1) {
2813-
// Attempt to disambiguite in cases where all the solutions
2814-
// produces the same fixes for different generic arguments e.g.
2815-
// func f<T>(_: T, _: T) {}
2816-
// f(Int(1), Float(1))
2817-
//
2818-
ambiguityKind =
2819-
((ambiguityKind == AmbiguityKind::CloseMatch ||
2820-
ambiguityKind == AmbiguityKind::ArgumentMismatch) &&
2821-
fixes.front()->getKind() == FixKind::AllowArgumentTypeMismatch)
2822-
? AmbiguityKind::ArgumentMismatch
2823-
: AmbiguityKind::CloseMatch;
2824-
}
2825-
2826-
for (const auto *fix : fixes) {
2827-
auto *locator = fix->getLocator();
2828-
// Assignment failures are all about the source expression,
2829-
// because they treat destination as a contextual type.
2830-
if (auto *anchor = locator->getAnchor()) {
2831-
if (auto *assignExpr = dyn_cast<AssignExpr>(anchor))
2832-
locator = getConstraintLocator(assignExpr->getSrc());
2833-
}
2834-
2835-
auto *calleeLocator = solution.getCalleeLocator(locator);
2836-
if (!commonCalleeLocator)
2837-
commonCalleeLocator = calleeLocator;
2838-
else if (commonCalleeLocator != calleeLocator)
2839-
return false;
2840-
}
2841-
2842-
auto overload = solution.getOverloadChoiceIfAvailable(commonCalleeLocator);
2843-
if (!overload)
2844-
return false;
2845-
2846-
auto *decl = overload->choice.getDeclOrNull();
2847-
if (!decl)
2848-
return false;
2849-
2850-
// If this declaration is distinct, let's record this solution
2851-
// as viable, otherwise we'd produce the same diagnostic multiple
2852-
// times, which means that actual problem is elsewhere.
2853-
if (distinctChoices.insert(decl).second)
2854-
viableSolutions.push_back(&solution);
2855-
return true;
2856-
});
2857-
2858-
if (ambiguityKind == AmbiguityKind::ArgumentMismatch &&
2859-
viableSolutions.size() == 1) {
2860-
// Let's apply the solution so the contextual generic types
2861-
// are available in the system for diagnostics.
2862-
applySolution(*viableSolutions[0]);
2863-
solutions.front().Fixes.front()->diagnose(/*asNote*/ false);
2864-
return true;
28652841
}
28662842

2867-
if (!diagnosable || viableSolutions.size() < 2)
2868-
return false;
2869-
2870-
auto *decl = *distinctChoices.begin();
2843+
auto *decl = ambiguousOverload->choices.front().getDecl();
28712844
assert(solverState);
28722845

28732846
bool diagnosed = true;

lib/Sema/ConstraintSystem.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -837,9 +837,21 @@ class Solution {
837837
llvm::DenseMap<std::pair<CanType, CanType>, ConversionRestrictionKind>
838838
ConstraintRestrictions;
839839

840+
using ConstraintFixVector = llvm::SmallVector<ConstraintFix *, 4>;
840841
/// The list of fixes that need to be applied to the initial expression
841842
/// to make the solution work.
842-
llvm::SmallVector<ConstraintFix *, 4> Fixes;
843+
ConstraintFixVector Fixes;
844+
845+
using AggregatedFixesMap = llvm::SmallMapVector<ConstraintLocator *,
846+
llvm::SmallMapVector<FixKind, ConstraintFixVector, 4>, 4>;
847+
848+
AggregatedFixesMap getAggregatedFixes() const {
849+
AggregatedFixesMap aggregatedFixes;
850+
for (auto *fix: Fixes) {
851+
aggregatedFixes[fix->getLocator()][fix->getKind()].push_back(fix);
852+
}
853+
return aggregatedFixes;
854+
}
843855

844856
/// The set of disjunction choices used to arrive at this solution,
845857
/// which informs constraint application.

test/Constraints/closures.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ func someGeneric19997471<T>(_ x: T) {
371371
func rdar21078316() {
372372
var foo : [String : String]?
373373
var bar : [(String, String)]?
374-
bar = foo.map { ($0, $1) } // expected-error {{contextual closure type '(Dictionary<String, String>) throws -> (Dictionary<String, String>, _)' expects 1 argument, but 2 were used in closure body}}
374+
bar = foo.map { ($0, $1) } // expected-error {{contextual closure type '([String : String]) throws -> [(String, String)]' expects 1 argument, but 2 were used in closure body}}
375375
}
376376

377377

test/Constraints/one_way_constraints.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ func testTernaryOneWay(b: Bool) {
1010
let _: Float = b ? 3.14159 : 17
1111

1212
// Errors due to one-way inference.
13-
let _: Float = b ? Builtin.one_way(3.14159) // expected-error{{result values in '? :' expression have mismatching types 'Double' and 'Int'}}
13+
let _: Float = b ? Builtin.one_way(3.14159) // expected-error{{cannot convert value of type 'Double' to specified type 'Float'}}
1414
: 17
1515
let _: Float = b ? 3.14159 // expected-error {{cannot convert value of type 'Int' to specified type 'Float'}}
1616
: Builtin.one_way(17)

0 commit comments

Comments
 (0)