diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt index 5bc72b050d4..14513cc2d54 100644 --- a/lib/Sema/CMakeLists.txt +++ b/lib/Sema/CMakeLists.txt @@ -11,6 +11,7 @@ add_swift_library(swiftSema STATIC CSRanking.cpp CSSimplify.cpp CSSolver.cpp + CSStep.cpp CSFix.cpp CSDiagnostics.cpp CalleeCandidateInfo.cpp diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 06bbcc16eb6..530175d2c9e 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -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 +#include #include #include @@ -1556,6 +1558,68 @@ bool ConstraintSystem::solveRec(SmallVectorImpl &solutions) { return !anySolutions; } +bool ConstraintSystem::solveIteratively( + Expr *expr, SmallVectorImpl &solutions, + FreeTypeVariableBinding allowFreeTypeVariables) { + SolverState state(expr, *this, allowFreeTypeVariables); + + std::list 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 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, diff --git a/lib/Sema/CSStep.cpp b/lib/Sema/CSStep.cpp new file mode 100644 index 00000000000..31a7beb8979 --- /dev/null +++ b/lib/Sema/CSStep.cpp @@ -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 + +using namespace llvm; +using namespace swift; +using namespace constraints; + +SolverStep SolverStep::create(ConstraintSystem &cs, + ArrayRef typeVars, + ConstraintList &constraints, + SmallVectorImpl &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 nextSteps; + // Compute next steps based on that connected components + // algorithm tells us is splittable. + return {ConstraintSystem::SolutionKind::Unsolved, std::move(nextSteps)}; +} diff --git a/lib/Sema/CSStep.h b/lib/Sema/CSStep.h new file mode 100644 index 00000000000..fb8be280e5f --- /dev/null +++ b/lib/Sema/CSStep.h @@ -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 +#include + +using namespace llvm; + +namespace swift { +namespace constraints { + +class SolverStep { + struct Scope; + + ConstraintSystem &CS; + + // Type variables and constraints "in scope" of this step. + SmallVector TypeVars; + SmallVector 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 &Solutions; + +public: + using StepResult = + std::pair>; + + explicit SolverStep(ConstraintSystem &cs, + SmallVectorImpl &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 typeVars, + ConstraintList &constraints, + SmallVectorImpl &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 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[]> 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 diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index d1a5206bdb6..89c430784fa 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -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 &solutions); + bool solveIteratively(Expr *expr, SmallVectorImpl &solutions, + FreeTypeVariableBinding allowFreeTypeVariables); + /// \brief Solve the system of constraints. /// /// \param allowFreeTypeVariables How to bind free type variables in