[CSSolver] Add skeleton of iterative solve

The idea so to split solving into non-recursive steps,
represented by `SolverStep`, each of the steps is resposible
for a unit of work e.g. attempting type variable or
disjunction bindings/choices.

Each step could produce more work via "follow-up" steps,
complete "partial" solution when it's done, or error which
terminates solver loop.
This commit is contained in:
Pavel Yaskevich
2018-09-03 00:12:32 -07:00
parent 9bff72aa8d
commit daf4c2f3b5
5 changed files with 278 additions and 0 deletions

View File

@@ -11,6 +11,7 @@ add_swift_library(swiftSema STATIC
CSRanking.cpp
CSSimplify.cpp
CSSolver.cpp
CSStep.cpp
CSFix.cpp
CSDiagnostics.cpp
CalleeCandidateInfo.cpp

View File

@@ -13,6 +13,7 @@
// This file implements the constraint solver used in the type checker.
//
//===----------------------------------------------------------------------===//
#include "CSStep.h"
#include "ConstraintGraph.h"
#include "ConstraintSystem.h"
#include "TypeCheckType.h"
@@ -24,6 +25,7 @@
#include "llvm/Support/SaveAndRestore.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <list>
#include <memory>
#include <tuple>
@@ -1556,6 +1558,68 @@ bool ConstraintSystem::solveRec(SmallVectorImpl<Solution> &solutions) {
return !anySolutions;
}
bool ConstraintSystem::solveIteratively(
Expr *expr, SmallVectorImpl<Solution> &solutions,
FreeTypeVariableBinding allowFreeTypeVariables) {
SolverState state(expr, *this, allowFreeTypeVariables);
std::list<SolverStep> workList;
// First step is always wraps whole constraint system.
workList.push_back(
SolverStep::create(*this, TypeVariables, InactiveConstraints, solutions));
// Execute steps in LIFO order, which means that
// each individual step would either end up producing
// a solution, or producing another set of mergeable
// steps to take before arriving to solution.
while (!workList.empty()) {
auto &step = workList.back();
// Now let's try to advance to the next step,
// which should produce another steps to follow,
// or error, which means that we'll have to stop.
{
SolutionKind result;
std::list<SolverStep> followupSteps;
std::tie(result, followupSteps) = step.advance();
switch (result) {
// Step has been solved successfully by either
// producing a partial solution, or more steps
// toward that solution.
case SolutionKind::Solved: {
workList.pop_back();
break;
}
// Keep this step in the work list to return to it
// once all other steps are done, this could be a
// disjunction which has to peek a new choice until
// it completely runs out of choices, or type variable
// binding.
case SolutionKind::Unsolved:
break;
// It was impossible to produce a solution for this step,
// let's terminate the solver loop.
case SolutionKind::Error:
return true;
}
workList.splice(workList.end(), followupSteps);
}
}
// Filter deduced solutions, try to figure out if there is
// a single best solution to use, if not explicitly disabled
// by constraint system options.
if (!retainAllSolutions())
filterSolutions(solutions, state.ExprWeights);
// We fail if there is no solution or the expression was too complex.
return solutions.empty() || getExpressionTooComplex(solutions);
}
/// Whether we should short-circuit a disjunction that already has a
/// solution when we encounter the given constraint.
static bool shortCircuitDisjunctionAt(Constraint *constraint,

71
lib/Sema/CSStep.cpp Normal file
View File

@@ -0,0 +1,71 @@
#include "CSStep.h"
#include "ConstraintSystem.h"
#include "swift/AST/Types.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include <list>
using namespace llvm;
using namespace swift;
using namespace constraints;
SolverStep SolverStep::create(ConstraintSystem &cs,
ArrayRef<TypeVariableType *> typeVars,
ConstraintList &constraints,
SmallVectorImpl<Solution> &solutions) {
SolverStep step(cs, solutions);
for (auto *typeVar : typeVars)
step.record(typeVar);
for (auto &constraint : constraints)
step.record(&constraint);
return step;
}
SolverStep::StepResult SolverStep::advance() {
if (!ActiveScope)
ActiveScope = new (CS.getAllocator()) Scope(*this);
auto *disjunction = CS.selectDisjunction();
auto bestBindings = CS.determineBestBindings();
// TODO: Add expression too complex into the mix
// TODO: This is where the generator comes in.
if (bestBindings && (!disjunction || (!bestBindings->InvolvesTypeVariables &&
!bestBindings->FullyBound))) {
// Attempt given "best" binding and record the current scope,
// we'll have to come back to this step later on when follow-up
// steps are solved.
return computeFollowupSteps();
}
// For disjunctions we need to figure out what all of the already
// attempted choices are, and try the next one.
auto choices = disjunction->getNestedConstraints();
for (unsigned i = ActiveScope->CurrChoice, n = choices.size(); i != n; ++i) {
auto *choice = choices[i];
if (choice->isDisabled())
continue;
// Attempt the choice and record it in the scope.
ActiveScope->CurrChoice = i;
return computeFollowupSteps();
}
// Since we are done with this step, it's a good time to
// roll-up all of the solutions produced by follow-up steps.
auto result = mergePartialSolutions() ? ConstraintSystem::SolutionKind::Solved
: ConstraintSystem::SolutionKind::Error;
return {result, {}};
}
SolverStep::StepResult SolverStep::computeFollowupSteps() const {
std::list<SolverStep> nextSteps;
// Compute next steps based on that connected components
// algorithm tells us is splittable.
return {ConstraintSystem::SolutionKind::Unsolved, std::move(nextSteps)};
}

137
lib/Sema/CSStep.h Normal file
View File

@@ -0,0 +1,137 @@
//===--- CSFix.cpp - Constraint Fixes -------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file implements the \c SolverStep class and its related types,
// which is used by constraint solver to do iterative constraint solving.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SEMA_CSSTEP_H
#define SWIFT_SEMA_CSSTEP_H
#include "ConstraintSystem.h"
#include "swift/AST/Types.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include <list>
#include <memory>
using namespace llvm;
namespace swift {
namespace constraints {
class SolverStep {
struct Scope;
ConstraintSystem &CS;
// Type variables and constraints "in scope" of this step.
SmallVector<TypeVariableType *, 16> TypeVars;
SmallVector<Constraint *, 16> Constraints;
// If this step depends on other smaller steps to be solved first
// we need to keep active scope until all of the work is done.
Scope *ActiveScope = nullptr;
/// Once step is complete this is a container to hold finalized solutions.
SmallVectorImpl<Solution> &Solutions;
public:
using StepResult =
std::pair<ConstraintSystem::SolutionKind, std::list<SolverStep>>;
explicit SolverStep(ConstraintSystem &cs,
SmallVectorImpl<Solution> &solutions)
: CS(cs), Solutions(solutions) {}
~SolverStep() {
if (!ActiveScope)
return;
// Since the step is no longer needed, it's same to rewind active scope.
delete ActiveScope;
ActiveScope = nullptr;
}
/// Record a type variable as associated with this step.
void record(TypeVariableType *typeVar) { TypeVars.push_back(typeVar); }
/// Record a constraint as associated with this step.
void record(Constraint *constraint) { Constraints.push_back(constraint); }
/// Try to move solver forward by simplifying constraints if possible.
/// Such simplication might lead to either producing a solution, or
/// creating a set of "follow-up" more granular steps to execute.
StepResult advance();
/// If current step needs follow-up steps to get completely solved,
/// let's compute them using connected components algorithm.
StepResult computeFollowupSteps() const;
static SolverStep create(ConstraintSystem &cs,
ArrayRef<TypeVariableType *> typeVars,
ConstraintList &constraints,
SmallVectorImpl<Solution> &solutions);
/// Once all of the follow-up steps are complete, let's try
/// to merge resulting solutions together, to form final solution(s)
/// for this step.
///
/// \returns true if there are any solutions, false otherwise.
bool mergePartialSolutions() const { return false; }
private:
struct Scope {
ConstraintSystem &CS;
ConstraintSystem::SolverScope *SolverScope;
SmallVector<TypeVariableType *, 16> TypeVars;
ConstraintList Constraints;
// Current disjunction choice index.
unsigned CurrChoice = 0;
// Partial solutions associated with given step, each element
// of the array presents a disjoint component (or follow-up step)
// that current step has been split into.
std::unique_ptr<SmallVector<Solution, 4>[]> PartialSolutions = nullptr;
Scope(SolverStep &step) : CS(step.CS) {
TypeVars = std::move(CS.TypeVariables);
for (auto *typeVar : step.TypeVars)
CS.TypeVariables.push_back(typeVar);
Constraints.splice(Constraints.end(), CS.InactiveConstraints);
for (auto *constraint : step.Constraints)
CS.InactiveConstraints.push_back(constraint);
SolverScope = new ConstraintSystem::SolverScope(CS);
}
~Scope() {
delete SolverScope; // rewind back all of the changes.
// return all of the saved type variables back to the system.
CS.TypeVariables = std::move(TypeVars);
// return all of the saved constraints back to the system.
CS.InactiveConstraints.splice(CS.InactiveConstraints.end(), Constraints);
}
};
};
} // end namespace constraints
} // end namespace swift
#endif // SWIFT_SEMA_CSSTEP_H

View File

@@ -57,6 +57,7 @@ class DisjunctionChoiceProducer;
class TypeBinding;
class TypeVariableBinding;
class TypeVarBindingProducer;
class SolverStep;
} // end namespace constraints
@@ -930,6 +931,7 @@ public:
friend class FailureDiagnostic;
friend class TypeVarBindingProducer;
friend class TypeVariableBinding;
friend class SolverStep;
class SolverScope;
@@ -3088,6 +3090,9 @@ public:
/// \returns true if there are no solutions
bool solveRec(SmallVectorImpl<Solution> &solutions);
bool solveIteratively(Expr *expr, SmallVectorImpl<Solution> &solutions,
FreeTypeVariableBinding allowFreeTypeVariables);
/// \brief Solve the system of constraints.
///
/// \param allowFreeTypeVariables How to bind free type variables in