mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[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:
@@ -11,6 +11,7 @@ add_swift_library(swiftSema STATIC
|
|||||||
CSRanking.cpp
|
CSRanking.cpp
|
||||||
CSSimplify.cpp
|
CSSimplify.cpp
|
||||||
CSSolver.cpp
|
CSSolver.cpp
|
||||||
|
CSStep.cpp
|
||||||
CSFix.cpp
|
CSFix.cpp
|
||||||
CSDiagnostics.cpp
|
CSDiagnostics.cpp
|
||||||
CalleeCandidateInfo.cpp
|
CalleeCandidateInfo.cpp
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
// This file implements the constraint solver used in the type checker.
|
// This file implements the constraint solver used in the type checker.
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
#include "CSStep.h"
|
||||||
#include "ConstraintGraph.h"
|
#include "ConstraintGraph.h"
|
||||||
#include "ConstraintSystem.h"
|
#include "ConstraintSystem.h"
|
||||||
#include "TypeCheckType.h"
|
#include "TypeCheckType.h"
|
||||||
@@ -24,6 +25,7 @@
|
|||||||
#include "llvm/Support/SaveAndRestore.h"
|
#include "llvm/Support/SaveAndRestore.h"
|
||||||
#include "llvm/Support/raw_ostream.h"
|
#include "llvm/Support/raw_ostream.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
@@ -1556,6 +1558,68 @@ bool ConstraintSystem::solveRec(SmallVectorImpl<Solution> &solutions) {
|
|||||||
return !anySolutions;
|
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
|
/// Whether we should short-circuit a disjunction that already has a
|
||||||
/// solution when we encounter the given constraint.
|
/// solution when we encounter the given constraint.
|
||||||
static bool shortCircuitDisjunctionAt(Constraint *constraint,
|
static bool shortCircuitDisjunctionAt(Constraint *constraint,
|
||||||
|
|||||||
71
lib/Sema/CSStep.cpp
Normal file
71
lib/Sema/CSStep.cpp
Normal 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
137
lib/Sema/CSStep.h
Normal 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
|
||||||
@@ -57,6 +57,7 @@ class DisjunctionChoiceProducer;
|
|||||||
class TypeBinding;
|
class TypeBinding;
|
||||||
class TypeVariableBinding;
|
class TypeVariableBinding;
|
||||||
class TypeVarBindingProducer;
|
class TypeVarBindingProducer;
|
||||||
|
class SolverStep;
|
||||||
|
|
||||||
} // end namespace constraints
|
} // end namespace constraints
|
||||||
|
|
||||||
@@ -930,6 +931,7 @@ public:
|
|||||||
friend class FailureDiagnostic;
|
friend class FailureDiagnostic;
|
||||||
friend class TypeVarBindingProducer;
|
friend class TypeVarBindingProducer;
|
||||||
friend class TypeVariableBinding;
|
friend class TypeVariableBinding;
|
||||||
|
friend class SolverStep;
|
||||||
|
|
||||||
class SolverScope;
|
class SolverScope;
|
||||||
|
|
||||||
@@ -3088,6 +3090,9 @@ public:
|
|||||||
/// \returns true if there are no solutions
|
/// \returns true if there are no solutions
|
||||||
bool solveRec(SmallVectorImpl<Solution> &solutions);
|
bool solveRec(SmallVectorImpl<Solution> &solutions);
|
||||||
|
|
||||||
|
bool solveIteratively(Expr *expr, SmallVectorImpl<Solution> &solutions,
|
||||||
|
FreeTypeVariableBinding allowFreeTypeVariables);
|
||||||
|
|
||||||
/// \brief Solve the system of constraints.
|
/// \brief Solve the system of constraints.
|
||||||
///
|
///
|
||||||
/// \param allowFreeTypeVariables How to bind free type variables in
|
/// \param allowFreeTypeVariables How to bind free type variables in
|
||||||
|
|||||||
Reference in New Issue
Block a user