Skip to content

Commit e06fb4b

Browse files
committed
[Constraint system] Capture "salvage" result in a self-contained data structure
Rework the interface to ConstraintSystem::salvage() to (a) not require an existing set of solutions, which it overwrites anyway, (b) not depend on having a single expression as input, and (c) be clear with its client about whether the operation has already emitted a diagnostic vs. the client being expected to produce a diagnostic.
1 parent 78e360a commit e06fb4b

File tree

7 files changed

+253
-31
lines changed

7 files changed

+253
-31
lines changed

lib/Sema/CSApply.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "CodeSynthesis.h"
2121
#include "CSDiagnostics.h"
2222
#include "MiscDiagnostics.h"
23+
#include "SolutionResult.h"
2324
#include "TypeCheckProtocol.h"
2425
#include "swift/AST/ASTVisitor.h"
2526
#include "swift/AST/ASTWalker.h"
@@ -7531,3 +7532,50 @@ void Solution::setExprTypes(Expr *expr) const {
75317532
SetExprTypes SET(*this);
75327533
expr->walk(SET);
75337534
}
7535+
7536+
/// MARK: SolutionResult implementation.
7537+
7538+
SolutionResult SolutionResult::forSolved(Solution &&solution) {
7539+
SolutionResult result(Kind::Success);
7540+
result.solutions = new Solution(std::move(solution));
7541+
result.numSolutions = 1;
7542+
return result;
7543+
}
7544+
7545+
SolutionResult SolutionResult::forAmbiguous(
7546+
MutableArrayRef<Solution> solutions) {
7547+
assert(solutions.size() > 1 && "Not actually ambiguous");
7548+
SolutionResult result(Kind::Ambiguous);
7549+
result.solutions =
7550+
(Solution *)malloc(sizeof(Solution) * solutions.size());
7551+
result.numSolutions = solutions.size();
7552+
std::uninitialized_copy(std::make_move_iterator(solutions.begin()),
7553+
std::make_move_iterator(solutions.end()),
7554+
result.solutions);
7555+
return result;
7556+
}
7557+
7558+
SolutionResult::~SolutionResult() {
7559+
assert((!requiresDiagnostic() || emittedDiagnostic) &&
7560+
"SolutionResult was destroyed without emitting a diagnostic");
7561+
7562+
for (unsigned i : range(numSolutions)) {
7563+
solutions[i].~Solution();
7564+
}
7565+
free(solutions);
7566+
}
7567+
7568+
const Solution &SolutionResult::getSolution() const {
7569+
assert(numSolutions == 1 && "Wrong number of solutions");
7570+
return solutions[0];
7571+
}
7572+
7573+
Solution &&SolutionResult::takeSolution() && {
7574+
assert(numSolutions == 1 && "Wrong number of solutions");
7575+
return std::move(solutions[0]);
7576+
}
7577+
7578+
ArrayRef<Solution> SolutionResult::getAmbiguousSolutions() const {
7579+
assert(getKind() == Ambiguous);
7580+
return makeArrayRef(solutions, numSolutions);
7581+
}

lib/Sema/CSSolver.cpp

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "CSStep.h"
1717
#include "ConstraintGraph.h"
1818
#include "ConstraintSystem.h"
19+
#include "SolutionResult.h"
1920
#include "TypeCheckType.h"
2021
#include "swift/AST/ParameterList.h"
2122
#include "swift/AST/TypeWalker.h"
@@ -1121,10 +1122,27 @@ bool ConstraintSystem::solve(Expr *&expr,
11211122
if (shouldSuppressDiagnostics())
11221123
return true;
11231124

1124-
// Try to provide a decent diagnostic.
1125-
if (salvage(solutions, expr)) {
1126-
// If salvage produced an error message, then it failed to salvage the
1127-
// expression, just bail out having reported the error.
1125+
// Try to fix the system or provide a decent diagnostic.
1126+
auto salvagedResult = salvage();
1127+
switch (salvagedResult.getKind()) {
1128+
case SolutionResult::Kind::Success:
1129+
solutions.clear();
1130+
solutions.push_back(std::move(salvagedResult).takeSolution());
1131+
break;
1132+
1133+
case SolutionResult::Kind::Error:
1134+
case SolutionResult::Kind::Ambiguous:
1135+
return true;
1136+
1137+
case SolutionResult::Kind::UndiagnosedError:
1138+
diagnoseFailureForExpr(expr);
1139+
salvagedResult.markAsDiagnosed();
1140+
return true;
1141+
1142+
case SolutionResult::Kind::TooComplex:
1143+
getASTContext().Diags.diagnose(expr->getLoc(), diag::expression_too_complex)
1144+
.highlight(expr->getSourceRange());
1145+
salvagedResult.markAsDiagnosed();
11281146
return true;
11291147
}
11301148

lib/Sema/ConstraintSystem.cpp

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "ConstraintGraph.h"
2020
#include "CSDiagnostics.h"
2121
#include "CSFix.h"
22+
#include "SolutionResult.h"
2223
#include "TypeCheckType.h"
2324
#include "swift/AST/Initializer.h"
2425
#include "swift/AST/GenericEnvironment.h"
@@ -2444,7 +2445,7 @@ bool OverloadChoice::isImplicitlyUnwrappedValueOrReturnValue() const {
24442445
llvm_unreachable("unhandled kind");
24452446
}
24462447

2447-
bool ConstraintSystem::salvage(SmallVectorImpl<Solution> &viable, Expr *expr) {
2448+
SolutionResult ConstraintSystem::salvage() {
24482449
auto &ctx = getASTContext();
24492450
if (ctx.TypeCheckerOpts.DebugConstraintSolver) {
24502451
auto &log = ctx.TypeCheckerDebug->getStream();
@@ -2456,6 +2457,7 @@ bool ConstraintSystem::salvage(SmallVectorImpl<Solution> &viable, Expr *expr) {
24562457
//
24572458
// FIXME: can this be removed? We need to arrange for recordFixes to be
24582459
// eliminated.
2460+
SmallVector<Solution, 2> viable;
24592461
viable.clear();
24602462

24612463
{
@@ -2472,13 +2474,13 @@ bool ConstraintSystem::salvage(SmallVectorImpl<Solution> &viable, Expr *expr) {
24722474
if (*best != 0)
24732475
viable[0] = std::move(viable[*best]);
24742476
viable.erase(viable.begin() + 1, viable.end());
2475-
return false;
2477+
return SolutionResult::forSolved(std::move(viable[0]));
24762478
}
24772479

24782480
// Before removing any "fixed" solutions, let's check
24792481
// if ambiguity is caused by fixes and diagnose if possible.
24802482
if (diagnoseAmbiguityWithFixes(viable))
2481-
return true;
2483+
return SolutionResult::forAmbiguous(viable);
24822484

24832485
// FIXME: If we were able to actually fix things along the way,
24842486
// we may have to hunt for the best solution. For now, we don't care.
@@ -2504,23 +2506,18 @@ bool ConstraintSystem::salvage(SmallVectorImpl<Solution> &viable, Expr *expr) {
25042506
}
25052507

25062508
if (diagnoseAmbiguity(viable)) {
2507-
return true;
2509+
return SolutionResult::forAmbiguous(viable);
25082510
}
25092511
}
25102512

25112513
// Fall through to produce diagnostics.
25122514
}
25132515

2514-
if (getExpressionTooComplex(viable)) {
2515-
ctx.Diags.diagnose(expr->getLoc(), diag::expression_too_complex)
2516-
.highlight(expr->getSourceRange());
2517-
return true;
2518-
}
2516+
if (getExpressionTooComplex(viable))
2517+
return SolutionResult::forTooComplex();
25192518

2520-
// If all else fails, diagnose the failure by looking through the system's
2521-
// constraints.
2522-
diagnoseFailureForExpr(expr);
2523-
return true;
2519+
// Could not produce a specific diagnostic; punt to the client.
2520+
return SolutionResult::forUndiagnosedError();
25242521
}
25252522

25262523
static void diagnoseOperatorAmbiguity(ConstraintSystem &cs,

lib/Sema/ConstraintSystem.h

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2081,17 +2081,8 @@ class ConstraintSystem {
20812081
ValueDecl *findResolvedMemberRef(ConstraintLocator *locator);
20822082

20832083
/// Try to salvage the constraint system by applying (speculative)
2084-
/// fixes to the underlying expression.
2085-
///
2086-
/// \param viable the set of viable solutions produced by the initial
2087-
/// solution attempt.
2088-
///
2089-
/// \param expr the expression we're trying to salvage.
2090-
///
2091-
/// \returns false if we were able to salvage the system, in which case
2092-
/// \c viable[0] contains the resulting solution. Otherwise, emits a
2093-
/// diagnostic and returns true.
2094-
bool salvage(SmallVectorImpl<Solution> &viable, Expr *expr);
2084+
/// fixes.
2085+
SolutionResult salvage();
20952086

20962087
/// Mine the active and inactive constraints in the constraint
20972088
/// system to generate a plausible diagnosis of why the system could not be

lib/Sema/SolutionResult.h

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
//===--- SolutionResult.h - Constraint System Solution ----------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2019 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file defines the SolutionResult class.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#ifndef SWIFT_TYPECHECK_SOLUTION_RESULT_H
18+
#define SWIFT_TYPECHECK_SOLUTION_RESULT_H
19+
20+
#include "llvm/ADT/ArrayRef.h"
21+
22+
namespace swift {
23+
24+
using llvm::ArrayRef;
25+
using llvm::makeArrayRef;
26+
27+
namespace constraints {
28+
29+
class Solution;
30+
31+
/// Describes the result of solving a constraint system, after
32+
/// potentially taking various corrective actions.
33+
class SolutionResult {
34+
public:
35+
enum Kind : unsigned char {
36+
/// The constraint system was successfully solved, and one can
37+
/// retrieve the resulting solution.
38+
Success,
39+
/// The constraint system had multiple solutions, none of which
40+
/// was better than the others.
41+
Ambiguous,
42+
/// The constraint system had no solution, and a diagnostic has
43+
/// already been emitted.
44+
Error,
45+
/// The constraint system had no solution, but no diagnostic has
46+
/// been emitted yet.
47+
UndiagnosedError,
48+
/// The constraint system was too complex to solve, but no
49+
/// diagnostic has been emitted yet.
50+
TooComplex,
51+
};
52+
53+
private:
54+
/// The kind of solution result.
55+
Kind kind;
56+
57+
/// Whether the client has emitted a diagnostic.
58+
unsigned emittedDiagnostic : 1;
59+
60+
/// The number of solutions owned by this result.
61+
unsigned numSolutions = 0;
62+
63+
/// A pointer to the set of solutions, of which there are
64+
/// \c numSolutions entries.
65+
Solution *solutions = nullptr;
66+
67+
/// General constructor for the named constructors.
68+
SolutionResult(Kind kind) : kind(kind) {
69+
emittedDiagnostic = false;
70+
}
71+
72+
public:
73+
SolutionResult(const SolutionResult &other) = delete;
74+
75+
SolutionResult(SolutionResult &&other)
76+
: kind(other.kind), numSolutions(other.numSolutions),
77+
solutions(other.solutions) {
78+
emittedDiagnostic = false;
79+
other.kind = Error;
80+
other.numSolutions = 0;
81+
other.solutions = nullptr;
82+
}
83+
84+
SolutionResult &operator=(const SolutionResult &other) = delete;
85+
SolutionResult &operator=(SolutionResult &&other) = delete;
86+
87+
~SolutionResult();
88+
89+
/// Produce a "solved" result, embedding the given solution.
90+
static SolutionResult forSolved(Solution &&solution);
91+
92+
/// Produce an "ambiguous" result, providing the set of
93+
/// potential solutions.
94+
static SolutionResult forAmbiguous(MutableArrayRef<Solution> solutions);
95+
96+
/// Produce a "too complex" failure, which was not yet been
97+
/// diagnosed.
98+
static SolutionResult forTooComplex() {
99+
return SolutionResult(TooComplex);
100+
}
101+
102+
/// Produce a failure that has already been diagnosed.
103+
static SolutionResult forError() {
104+
return SolutionResult(Error);
105+
}
106+
107+
/// Produce a failure that has not yet been diagnosed.
108+
static SolutionResult forUndiagnosedError() {
109+
return SolutionResult(UndiagnosedError);
110+
}
111+
112+
Kind getKind() const{ return kind; }
113+
114+
/// Retrieve the solution, where there is one.
115+
const Solution &getSolution() const;
116+
117+
/// Retrieve the solution, where there is one.
118+
Solution &&takeSolution() &&;
119+
120+
/// Retrieve the set of solutions when there is an ambiguity.
121+
ArrayRef<Solution> getAmbiguousSolutions() const;
122+
123+
/// Whether this solution requires the client to produce a diagnostic.
124+
bool requiresDiagnostic() const {
125+
switch (kind) {
126+
case Success:
127+
case Ambiguous:
128+
case Error:
129+
return false;
130+
131+
case UndiagnosedError:
132+
case TooComplex:
133+
return true;
134+
}
135+
}
136+
137+
/// Note that the failure has been diagnosed.
138+
void markAsDiagnosed() {
139+
emittedDiagnostic = true;
140+
}
141+
};
142+
143+
} }
144+
145+
#endif /* SWIFT_TYPECHECK_SOLUTION_RESULT_H */

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "ConstraintSystem.h"
2020
#include "MiscDiagnostics.h"
21+
#include "SolutionResult.h"
2122
#include "TypeChecker.h"
2223
#include "TypeCheckType.h"
2324
#include "TypoCorrection.h"
@@ -3465,9 +3466,30 @@ bool TypeChecker::convertToType(Expr *&expr, Type type, DeclContext *dc,
34653466

34663467
// Attempt to solve the constraint system.
34673468
SmallVector<Solution, 4> viable;
3468-
if ((cs.solve(viable) || viable.size() != 1) &&
3469-
cs.salvage(viable, expr)) {
3470-
return true;
3469+
if ((cs.solve(viable) || viable.size() != 1)) {
3470+
// Try to fix the system or provide a decent diagnostic.
3471+
auto salvagedResult = cs.salvage();
3472+
switch (salvagedResult.getKind()) {
3473+
case SolutionResult::Kind::Success:
3474+
viable.clear();
3475+
viable.push_back(std::move(salvagedResult).takeSolution());
3476+
break;
3477+
3478+
case SolutionResult::Kind::Error:
3479+
case SolutionResult::Kind::Ambiguous:
3480+
return true;
3481+
3482+
case SolutionResult::Kind::UndiagnosedError:
3483+
cs.diagnoseFailureForExpr(expr);
3484+
salvagedResult.markAsDiagnosed();
3485+
return true;
3486+
3487+
case SolutionResult::Kind::TooComplex:
3488+
Context.Diags.diagnose(expr->getLoc(), diag::expression_too_complex)
3489+
.highlight(expr->getSourceRange());
3490+
salvagedResult.markAsDiagnosed();
3491+
return true;
3492+
}
34713493
}
34723494

34733495
auto &solution = viable[0];

lib/Sema/TypeChecker.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ namespace constraints {
5050
enum class SolutionKind : char;
5151
class ConstraintSystem;
5252
class Solution;
53+
class SolutionResult;
5354
}
5455

5556
/// A mapping from substitutable types to the protocol-conformance

0 commit comments

Comments
 (0)