Skip to content

Commit 5ca7700

Browse files
authored
Merge pull request #18935 from xedin/properly-diagnose-ambiguities-with-fixes
[ConstraintSystem] Diagnose ambiguities related to solutions with fixes
2 parents e661702 + 294c05b commit 5ca7700

File tree

9 files changed

+233
-71
lines changed

9 files changed

+233
-71
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ NOTE(extended_type_declared_here,none,
5858

5959
ERROR(ambiguous_member_overload_set,none,
6060
"ambiguous reference to member %0", (DeclName))
61+
ERROR(ambiguous_reference_to_decl,none,
62+
"ambiguous reference to %0 %1", (DescriptiveDeclKind, DeclName))
6163

6264
ERROR(ambiguous_subscript,none,
6365
"ambiguous subscript with base type %0 and index type %1",
@@ -1539,6 +1541,10 @@ NOTE(where_requirement_failure_both_subst,none,
15391541
"where %0 = %1, %2 = %3", (Type, Type, Type, Type))
15401542
NOTE(requirement_implied_by_conditional_conformance,none,
15411543
"requirement from conditional conformance of %0 to %1", (Type, Type))
1544+
NOTE(candidate_types_conformance_requirement,none,
1545+
"candidate requires that %0 conform to %1 "
1546+
"(requirement specified as %2 == %3%4)",
1547+
(Type, Type, Type, Type, StringRef))
15421548
NOTE(candidate_types_equal_requirement,none,
15431549
"candidate requires that the types %0 and %1 be equivalent "
15441550
"(requirement specified as %2 == %3%4)",

lib/Sema/CSDiag.cpp

Lines changed: 98 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,20 @@
1414
//
1515
//===----------------------------------------------------------------------===//
1616

17-
#include "ConstraintSystem.h"
1817
#include "CSDiag.h"
1918
#include "CalleeCandidateInfo.h"
19+
#include "ConstraintSystem.h"
2020
#include "MiscDiagnostics.h"
2121
#include "TypeCheckAvailability.h"
2222
#include "TypoCorrection.h"
2323
#include "swift/AST/ASTWalker.h"
24+
#include "swift/AST/DiagnosticEngine.h"
2425
#include "swift/AST/GenericEnvironment.h"
2526
#include "swift/AST/Initializer.h"
2627
#include "swift/AST/ParameterList.h"
2728
#include "swift/AST/ProtocolConformance.h"
28-
#include "swift/AST/TypeWalker.h"
2929
#include "swift/AST/TypeMatcher.h"
30+
#include "swift/AST/TypeWalker.h"
3031
#include "swift/Basic/Defer.h"
3132
#include "swift/Basic/StringExtras.h"
3233
#include "llvm/ADT/DenseSet.h"
@@ -455,9 +456,91 @@ tryDiagnoseTrailingClosureAmbiguity(TypeChecker &tc,
455456
return true;
456457
}
457458

458-
static bool diagnoseAmbiguity(ConstraintSystem &cs,
459-
ArrayRef<Solution> solutions,
460-
Expr *expr) {
459+
bool ConstraintSystem::diagnoseAmbiguityWithFixes(
460+
Expr *expr, ArrayRef<Solution> solutions) {
461+
if (solutions.empty())
462+
return false;
463+
464+
auto getOverloadDecl = [&](SelectedOverload &overload) -> ValueDecl * {
465+
auto &choice = overload.choice;
466+
return choice.isDecl() ? choice.getDecl() : nullptr;
467+
};
468+
469+
// Problems related to fixes forming ambiguous solution set
470+
// could only be diagnosed (at the moment), if all of the fixes
471+
// are attached to the same anchor, which means they fix
472+
// different overloads of the same declaration.
473+
Expr *commonAnchor = nullptr;
474+
SmallPtrSet<ValueDecl *, 4> distinctChoices;
475+
SmallVector<std::pair<const Solution *, const ConstraintFix *>, 4>
476+
viableSolutions;
477+
478+
bool diagnosable = llvm::all_of(solutions, [&](const Solution &solution) {
479+
ArrayRef<ConstraintFix *> fixes = solution.Fixes;
480+
481+
// Currently only support a single fix in a solution,
482+
// but ultimately should be able to deal with multiple.
483+
if (fixes.size() != 1)
484+
return false;
485+
486+
const auto *fix = fixes.front();
487+
if (commonAnchor && commonAnchor != fix->getAnchor())
488+
return false;
489+
490+
commonAnchor = fix->getAnchor();
491+
492+
SmallVector<SelectedOverload, 2> overloads;
493+
solution.getOverloadChoices(commonAnchor, overloads);
494+
// There is unfortunately no way, at the moment, to figure out
495+
// what declaration the fix is attached to, so we have to make
496+
// sure that there is only one declaration associated with common
497+
// anchor to be sure that the right problem is being diagnosed.
498+
if (overloads.size() != 1)
499+
return false;
500+
501+
auto *decl = getOverloadDecl(overloads.front());
502+
if (!decl)
503+
return false;
504+
505+
// If this declaration is distinct, let's record this solution
506+
// as viable, otherwise we'd produce the same diagnostic multiple
507+
// times, which means that actual problem is elsewhere.
508+
if (distinctChoices.insert(decl).second)
509+
viableSolutions.push_back({&solution, fix});
510+
return true;
511+
});
512+
513+
if (!diagnosable || viableSolutions.size() < 2)
514+
return false;
515+
516+
auto *decl = *distinctChoices.begin();
517+
assert(solverState);
518+
519+
bool diagnosed = true;
520+
{
521+
DiagnosticTransaction transaction(TC.Diags);
522+
523+
TC.diagnose(commonAnchor->getLoc(), diag::ambiguous_reference_to_decl,
524+
decl->getDescriptiveKind(), decl->getFullName());
525+
526+
for (const auto &viable : viableSolutions) {
527+
// Create scope so each applied solution is rolled back.
528+
ConstraintSystem::SolverScope scope(*this);
529+
applySolution(*viable.first);
530+
// All of the solutions supposed to produce a "candidate" note.
531+
diagnosed &= viable.second->diagnose(expr, /*asNote*/ true);
532+
}
533+
534+
// If not all of the fixes produced a note, we can't diagnose this.
535+
if (!diagnosed)
536+
transaction.abort();
537+
}
538+
539+
return diagnosed;
540+
}
541+
542+
bool ConstraintSystem::diagnoseAmbiguity(Expr *expr,
543+
ArrayRef<Solution> solutions) {
461544
// Produce a diff of the solutions.
462545
SolutionDiff diff(solutions);
463546

@@ -484,7 +567,7 @@ static bool diagnoseAmbiguity(ConstraintSystem &cs,
484567

485568
// If we can't resolve the locator to an anchor expression with no path,
486569
// we can't diagnose this well.
487-
auto *anchor = simplifyLocatorToAnchor(cs, overload.locator);
570+
auto *anchor = simplifyLocatorToAnchor(*this, overload.locator);
488571
if (!anchor)
489572
continue;
490573
auto it = indexMap.find(anchor);
@@ -524,10 +607,10 @@ static bool diagnoseAmbiguity(ConstraintSystem &cs,
524607
if (bestOverload) {
525608
auto &overload = diff.overloads[*bestOverload];
526609
auto name = getOverloadChoiceName(overload.choices);
527-
auto anchor = simplifyLocatorToAnchor(cs, overload.locator);
610+
auto anchor = simplifyLocatorToAnchor(*this, overload.locator);
528611

529612
// Emit the ambiguity diagnostic.
530-
auto &tc = cs.getTypeChecker();
613+
auto &tc = getTypeChecker();
531614
tc.diagnose(anchor->getLoc(),
532615
name.isOperator() ? diag::ambiguous_operator_ref
533616
: diag::ambiguous_decl_ref,
@@ -9021,6 +9104,11 @@ bool ConstraintSystem::salvage(SmallVectorImpl<Solution> &viable, Expr *expr) {
90219104
// FIXME: If we were able to actually fix things along the way,
90229105
// we may have to hunt for the best solution. For now, we don't care.
90239106

9107+
// Before removing any "fixed" solutions, let's check
9108+
// if ambiguity is caused by fixes and diagnose if possible.
9109+
if (diagnoseAmbiguityWithFixes(expr, viable))
9110+
return true;
9111+
90249112
// Remove solutions that require fixes; the fixes in those systems should
90259113
// be diagnosed rather than any ambiguity.
90269114
auto hasFixes = [](const Solution &sol) { return !sol.Fixes.empty(); };
@@ -9039,9 +9127,9 @@ bool ConstraintSystem::salvage(SmallVectorImpl<Solution> &viable, Expr *expr) {
90399127
solution.dump(log);
90409128
log << "\n";
90419129
}
9042-
}
9130+
}
90439131

9044-
if (diagnoseAmbiguity(*this, viable, expr)) {
9132+
if (diagnoseAmbiguity(expr, viable)) {
90459133
return true;
90469134
}
90479135
}

lib/Sema/CSDiag.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,16 @@
1818
#ifndef SWIFT_SEMA_CSDIAG_H
1919
#define SWIFT_SEMA_CSDIAG_H
2020

21+
#include "ConstraintSystem.h"
22+
#include "swift/AST/DiagnosticEngine.h"
23+
#include "llvm/ADT/StringRef.h"
24+
#include <string>
25+
2126
namespace swift {
22-
27+
class Expr;
28+
class Type;
29+
class SourceLoc;
30+
2331
std::string getTypeListString(Type type);
2432

2533
/// Rewrite any type variables & archetypes in the specified type with

lib/Sema/CSDiagnostics.cpp

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ using namespace constraints;
2929

3030
FailureDiagnostic::~FailureDiagnostic() {}
3131

32+
bool FailureDiagnostic::diagnose(bool asNote) {
33+
return asNote ? diagnoseAsNote() : diagnoseAsError();
34+
}
35+
36+
bool FailureDiagnostic::diagnoseAsNote() {
37+
return false;
38+
}
39+
3240
std::pair<Expr *, bool> FailureDiagnostic::computeAnchor() const {
3341
auto &cs = getConstraintSystem();
3442

@@ -116,7 +124,7 @@ const DeclContext *RequirementFailure::getRequirementDC() const {
116124
return AffectedDecl->getAsGenericContext();
117125
}
118126

119-
bool RequirementFailure::diagnose() {
127+
bool RequirementFailure::diagnoseAsError() {
120128
if (!canDiagnoseFailure())
121129
return false;
122130

@@ -140,6 +148,15 @@ bool RequirementFailure::diagnose() {
140148
return true;
141149
}
142150

151+
bool RequirementFailure::diagnoseAsNote() {
152+
const auto &req = getRequirement();
153+
const auto *reqDC = getRequirementDC();
154+
155+
emitDiagnostic(reqDC->getAsDecl(), getDiagnosticAsNote(), getLHS(), getRHS(),
156+
req.getFirstType(), req.getSecondType(), "");
157+
return true;
158+
}
159+
143160
void RequirementFailure::emitRequirementNote(const Decl *anchor) const {
144161
auto &req = getRequirement();
145162

@@ -159,7 +176,7 @@ void RequirementFailure::emitRequirementNote(const Decl *anchor) const {
159176
req.getFirstType(), getLHS(), req.getSecondType(), getRHS());
160177
}
161178

162-
bool MissingConformanceFailure::diagnose() {
179+
bool MissingConformanceFailure::diagnoseAsError() {
163180
if (!canDiagnoseFailure())
164181
return false;
165182

@@ -220,18 +237,18 @@ bool MissingConformanceFailure::diagnose() {
220237

221238
// If none of the special cases could be diagnosed,
222239
// let's fallback to the most general diagnostic.
223-
return RequirementFailure::diagnose();
240+
return RequirementFailure::diagnoseAsError();
224241
}
225242

226-
bool LabelingFailure::diagnose() {
243+
bool LabelingFailure::diagnoseAsError() {
227244
auto &cs = getConstraintSystem();
228245
auto *call = cast<CallExpr>(getAnchor());
229246
return diagnoseArgumentLabelError(cs.getASTContext(), call->getArg(),
230247
CorrectLabels,
231248
isa<SubscriptExpr>(call->getFn()));
232249
}
233250

234-
bool NoEscapeFuncToTypeConversionFailure::diagnose() {
251+
bool NoEscapeFuncToTypeConversionFailure::diagnoseAsError() {
235252
auto *anchor = getAnchor();
236253

237254
if (ConvertTo) {
@@ -254,7 +271,7 @@ bool NoEscapeFuncToTypeConversionFailure::diagnose() {
254271
return true;
255272
}
256273

257-
bool MissingForcedDowncastFailure::diagnose() {
274+
bool MissingForcedDowncastFailure::diagnoseAsError() {
258275
if (hasComplexLocator())
259276
return false;
260277

@@ -295,7 +312,7 @@ bool MissingForcedDowncastFailure::diagnose() {
295312
}
296313
}
297314

298-
bool MissingAddressOfFailure::diagnose() {
315+
bool MissingAddressOfFailure::diagnoseAsError() {
299316
if (hasComplexLocator())
300317
return false;
301318

@@ -306,7 +323,7 @@ bool MissingAddressOfFailure::diagnose() {
306323
return true;
307324
}
308325

309-
bool MissingExplicitConversionFailure::diagnose() {
326+
bool MissingExplicitConversionFailure::diagnoseAsError() {
310327
if (hasComplexLocator())
311328
return false;
312329

@@ -363,7 +380,7 @@ bool MissingExplicitConversionFailure::diagnose() {
363380
return true;
364381
}
365382

366-
bool MemberAccessOnOptionalBaseFailure::diagnose() {
383+
bool MemberAccessOnOptionalBaseFailure::diagnoseAsError() {
367384
if (hasComplexLocator())
368385
return false;
369386

@@ -516,7 +533,7 @@ static bool diagnoseUnwrap(ConstraintSystem &CS, Expr *expr, Type type) {
516533
return true;
517534
}
518535

519-
bool MissingOptionalUnwrapFailure::diagnose() {
536+
bool MissingOptionalUnwrapFailure::diagnoseAsError() {
520537
if (hasComplexLocator())
521538
return false;
522539

@@ -533,7 +550,7 @@ bool MissingOptionalUnwrapFailure::diagnose() {
533550
return true;
534551
}
535552

536-
bool RValueTreatedAsLValueFailure::diagnose() {
553+
bool RValueTreatedAsLValueFailure::diagnoseAsError() {
537554
Diag<StringRef> subElementDiagID;
538555
Diag<Type> rvalueDiagID;
539556
Expr *diagExpr = getLocator()->getAnchor();

0 commit comments

Comments
 (0)