Skip to content

Commit d752f41

Browse files
committed
[Constraint solver] Rework solveImpl(Expr*) to return SolutionResult.
SolutionResult better captures what can happen when solving a constraint system for the given expression occurs than the ad hoc SolutionKind return & small vector of Solution results. Also, try to make this logic less convoluted.
1 parent 5063cb5 commit d752f41

File tree

7 files changed

+102
-75
lines changed

7 files changed

+102
-75
lines changed

lib/Sema/CSApply.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7507,6 +7507,11 @@ ArrayRef<Solution> SolutionResult::getAmbiguousSolutions() const {
75077507
return makeArrayRef(solutions, numSolutions);
75087508
}
75097509

7510+
MutableArrayRef<Solution> SolutionResult::takeAmbiguousSolutions() && {
7511+
assert(getKind() == Ambiguous);
7512+
return MutableArrayRef<Solution>(solutions, numSolutions);
7513+
}
7514+
75107515
llvm::PointerUnion<Expr *, Stmt *> SolutionApplicationTarget::walk(
75117516
ASTWalker &walker) {
75127517
switch (kind) {

lib/Sema/CSSimplify.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9076,8 +9076,9 @@ void ConstraintSystem::addContextualConversionConstraint(
90769076
case CTP_ReturnStmt:
90779077
case CTP_ReturnSingleExpr:
90789078
case CTP_Initialization:
9079+
// FIXME: This option should not be global!
90799080
if (Options.contains(
9080-
ConstraintSystemFlags::UnderlyingTypeForOpaqueReturnType))
9081+
ConstraintSystemFlags::UnderlyingTypeForOpaqueReturnType))
90819082
constraintKind = ConstraintKind::OpaqueUnderlyingType;
90829083
break;
90839084

lib/Sema/CSSolver.cpp

Lines changed: 84 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,80 +1115,93 @@ bool ConstraintSystem::solve(Expr *&expr,
11151115
getASTContext().TypeCheckerOpts.DebugConstraintSolver,
11161116
debugConstraintSolverForExpr(getASTContext(), expr));
11171117

1118-
// Attempt to solve the constraint system.
1119-
auto solution = solveImpl(expr,
1120-
convertType,
1121-
listener,
1122-
solutions,
1123-
allowFreeTypeVariables);
1124-
1125-
// The constraint system has failed
1126-
if (solution == SolutionKind::Error)
1127-
return true;
1128-
1129-
// If the system is unsolved or there are multiple solutions present but
1130-
// type checker options do not allow unresolved types, let's try to salvage
1131-
if (solution == SolutionKind::Unsolved ||
1132-
(solutions.size() != 1 &&
1133-
!Options.contains(
1134-
ConstraintSystemFlags::AllowUnresolvedTypeVariables))) {
1135-
if (shouldSuppressDiagnostics())
1136-
return true;
1118+
/// Dump solutions for debugging purposes.
1119+
auto dumpSolutions = [&] {
1120+
// Debug-print the set of solutions.
1121+
if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) {
1122+
auto &log = getASTContext().TypeCheckerDebug->getStream();
1123+
if (solutions.size() == 1) {
1124+
log << "---Solution---\n";
1125+
solutions[0].dump(log);
1126+
} else {
1127+
for (unsigned i = 0, e = solutions.size(); i != e; ++i) {
1128+
log << "--- Solution #" << i << " ---\n";
1129+
solutions[i].dump(log);
1130+
}
1131+
}
1132+
}
1133+
};
11371134

1138-
// Try to fix the system or provide a decent diagnostic.
1139-
auto salvagedResult = salvage();
1140-
switch (salvagedResult.getKind()) {
1141-
case SolutionResult::Kind::Success:
1135+
// Take up to two attempts at solving the system. The first attempts to
1136+
// solve a system that is expected to be well-formed, the second kicks in
1137+
// when there is an error and attempts to salvage an ill-formed expression.
1138+
for (unsigned stage = 0; stage != 2; ++stage) {
1139+
auto solution = (stage == 0)
1140+
? solveImpl(expr, convertType, listener, allowFreeTypeVariables)
1141+
: salvage();
1142+
1143+
switch (solution.getKind()) {
1144+
case SolutionResult::Success:
1145+
// Return the successful solution.
11421146
solutions.clear();
1143-
solutions.push_back(std::move(salvagedResult).takeSolution());
1144-
break;
1145-
1146-
case SolutionResult::Kind::Error:
1147-
case SolutionResult::Kind::Ambiguous:
1148-
return true;
1147+
solutions.push_back(std::move(solution).takeSolution());
1148+
dumpSolutions();
1149+
return false;
11491150

1150-
case SolutionResult::Kind::UndiagnosedError:
1151-
diagnoseFailureFor(expr);
1152-
salvagedResult.markAsDiagnosed();
1151+
case SolutionResult::Error:
11531152
return true;
11541153

1155-
case SolutionResult::Kind::TooComplex:
1154+
case SolutionResult::TooComplex:
11561155
getASTContext().Diags.diagnose(expr->getLoc(), diag::expression_too_complex)
1157-
.highlight(expr->getSourceRange());
1158-
salvagedResult.markAsDiagnosed();
1156+
.highlight(expr->getSourceRange());
1157+
solution.markAsDiagnosed();
11591158
return true;
1160-
}
11611159

1162-
// The system was salvaged; continue on as if nothing happened.
1163-
}
1160+
case SolutionResult::Ambiguous:
1161+
// If salvaging produced an ambiguous result, it has already been
1162+
// diagnosed.
1163+
if (stage == 1) {
1164+
solution.markAsDiagnosed();
1165+
return true;
1166+
}
11641167

1165-
if (getExpressionTooComplex(solutions)) {
1166-
getASTContext().Diags.diagnose(expr->getLoc(), diag::expression_too_complex)
1167-
.highlight(expr->getSourceRange());
1168-
return true;
1169-
}
1168+
if (Options.contains(
1169+
ConstraintSystemFlags::AllowUnresolvedTypeVariables)) {
1170+
auto ambiguousSolutions = std::move(solution).takeAmbiguousSolutions();
1171+
solutions.assign(std::make_move_iterator(ambiguousSolutions.begin()),
1172+
std::make_move_iterator(ambiguousSolutions.end()));
1173+
dumpSolutions();
1174+
solution.markAsDiagnosed();
1175+
return false;
1176+
}
11701177

1171-
if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) {
1172-
auto &log = getASTContext().TypeCheckerDebug->getStream();
1173-
if (solutions.size() == 1) {
1174-
log << "---Solution---\n";
1175-
solutions[0].dump(log);
1176-
} else {
1177-
for (unsigned i = 0, e = solutions.size(); i != e; ++i) {
1178-
log << "--- Solution #" << i << " ---\n";
1179-
solutions[i].dump(log);
1178+
LLVM_FALLTHROUGH;
1179+
1180+
case SolutionResult::UndiagnosedError:
1181+
if (shouldSuppressDiagnostics()) {
1182+
solution.markAsDiagnosed();
1183+
return true;
1184+
}
1185+
1186+
if (stage == 1) {
1187+
diagnoseFailureFor(expr);
1188+
solution.markAsDiagnosed();
1189+
return true;
11801190
}
1191+
1192+
// Loop again to try to salvage.
1193+
solution.markAsDiagnosed();
1194+
continue;
11811195
}
11821196
}
11831197

1184-
return false;
1198+
llvm_unreachable("Loop always returns");
11851199
}
11861200

1187-
ConstraintSystem::SolutionKind
1201+
SolutionResult
11881202
ConstraintSystem::solveImpl(Expr *&expr,
11891203
Type convertType,
11901204
ExprTypeCheckListener *listener,
1191-
SmallVectorImpl<Solution> &solutions,
11921205
FreeTypeVariableBinding allowFreeTypeVariables) {
11931206
if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) {
11941207
auto &log = getASTContext().TypeCheckerDebug->getStream();
@@ -1220,7 +1233,7 @@ ConstraintSystem::solveImpl(Expr *&expr,
12201233
else {
12211234
if (listener)
12221235
listener->constraintGenerationFailed(expr);
1223-
return SolutionKind::Error;
1236+
return SolutionResult::forError();
12241237
}
12251238

12261239
// If there is a type that we're expected to convert to, add the conversion
@@ -1247,7 +1260,7 @@ ConstraintSystem::solveImpl(Expr *&expr,
12471260

12481261
// Notify the listener that we've built the constraint system.
12491262
if (listener && listener->builtConstraints(*this, expr)) {
1250-
return SolutionKind::Error;
1263+
return SolutionResult::forError();
12511264
}
12521265

12531266
if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) {
@@ -1259,11 +1272,22 @@ ConstraintSystem::solveImpl(Expr *&expr,
12591272
}
12601273

12611274
// Try to solve the constraint system using computed suggestions.
1275+
SmallVector<Solution, 4> solutions;
12621276
solve(solutions, allowFreeTypeVariables);
12631277

1264-
// If there are no solutions let's mark system as unsolved,
1265-
// and solved otherwise even if there are multiple solutions still present.
1266-
return solutions.empty() ? SolutionKind::Unsolved : SolutionKind::Solved;
1278+
if (getExpressionTooComplex(solutions))
1279+
return SolutionResult::forTooComplex();
1280+
1281+
switch (solutions.size()) {
1282+
case 0:
1283+
return SolutionResult::forUndiagnosedError();
1284+
1285+
case 1:
1286+
return SolutionResult::forSolved(std::move(solutions.front()));
1287+
1288+
default:
1289+
return SolutionResult::forAmbiguous(solutions);
1290+
}
12671291
}
12681292

12691293
bool ConstraintSystem::solve(SmallVectorImpl<Solution> &solutions,

lib/Sema/ConstraintSystem.h

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4040,18 +4040,13 @@ class ConstraintSystem {
40404040
/// \param expr The expression to generate constraints from.
40414041
/// \param convertType The expected type of the expression.
40424042
/// \param listener The callback to check solving progress.
4043-
/// \param solutions The set of solutions to the system of constraints.
40444043
/// \param allowFreeTypeVariables How to bind free type variables in
40454044
/// the solution.
4046-
///
4047-
/// \returns Error is an error occurred, Solved is system is consistent
4048-
/// and solutions were found, Unsolved otherwise.
4049-
SolutionKind solveImpl(Expr *&expr,
4050-
Type convertType,
4051-
ExprTypeCheckListener *listener,
4052-
SmallVectorImpl<Solution> &solutions,
4053-
FreeTypeVariableBinding allowFreeTypeVariables
4054-
= FreeTypeVariableBinding::Disallow);
4045+
SolutionResult solveImpl(Expr *&expr,
4046+
Type convertType,
4047+
ExprTypeCheckListener *listener,
4048+
FreeTypeVariableBinding allowFreeTypeVariables
4049+
= FreeTypeVariableBinding::Disallow);
40554050

40564051
public:
40574052
/// Pre-check the expression, validating any types that occur in the

lib/Sema/SolutionResult.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ class SolutionResult {
120120
/// Retrieve the set of solutions when there is an ambiguity.
121121
ArrayRef<Solution> getAmbiguousSolutions() const;
122122

123+
/// Take the set of solutions when there is an ambiguity.
124+
MutableArrayRef<Solution> takeAmbiguousSolutions() &&;
125+
123126
/// Whether this solution requires the client to produce a diagnostic.
124127
bool requiresDiagnostic() const {
125128
switch (kind) {

lib/Sema/TypeChecker.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ enum ContextualTypePurpose {
153153

154154

155155

156-
/// Flags that can be used to control name lookup.
156+
/// Flags that can be used to control type checking.
157157
enum class TypeCheckExprFlags {
158158
/// Whether we know that the result of the expression is discarded. This
159159
/// disables constraints forcing an lvalue result to be loadable.

test/Constraints/keypath.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,9 @@ func testFunc() {
4141
let _: (S) -> Int = \.i
4242
_ = ([S]()).map(\.i)
4343

44-
// FIXME: A terrible error, but the same as the pre-existing key path
45-
// error in the similar situation: 'let _ = \S.init'.
4644
_ = ([S]()).map(\.init)
47-
// expected-error@-1 {{type of expression is ambiguous without more context}}
45+
// expected-error@-1 {{key path cannot refer to static member 'init()'}}
46+
// expected-error@-2 {{key path value type '() -> S' cannot be converted to contextual type '_'}}
4847

4948
let kp = \S.i
5049
let _: KeyPath<S, Int> = kp // works, because type defaults to KeyPath nominal

0 commit comments

Comments
 (0)