summaryrefslogtreecommitdiffstats
path: root/vendor/chalk-solve-0.87.0/src/solve.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/chalk-solve-0.87.0/src/solve.rs')
-rw-r--r--vendor/chalk-solve-0.87.0/src/solve.rs350
1 files changed, 350 insertions, 0 deletions
diff --git a/vendor/chalk-solve-0.87.0/src/solve.rs b/vendor/chalk-solve-0.87.0/src/solve.rs
new file mode 100644
index 000000000..0734fc53e
--- /dev/null
+++ b/vendor/chalk-solve-0.87.0/src/solve.rs
@@ -0,0 +1,350 @@
+use crate::RustIrDatabase;
+use chalk_derive::HasInterner;
+use chalk_ir::interner::Interner;
+use chalk_ir::*;
+use std::fmt;
+use tracing::debug;
+
+pub mod truncate;
+
+/// A (possible) solution for a proposed goal.
+#[derive(Clone, Debug, PartialEq, Eq, HasInterner)]
+pub enum Solution<I: Interner> {
+ /// The goal indeed holds, and there is a unique value for all existential
+ /// variables. In this case, we also record a set of lifetime constraints
+ /// which must also hold for the goal to be valid.
+ Unique(Canonical<ConstrainedSubst<I>>),
+
+ /// The goal may be provable in multiple ways, but regardless we may have some guidance
+ /// for type inference. In this case, we don't return any lifetime
+ /// constraints, since we have not "committed" to any particular solution
+ /// yet.
+ Ambig(Guidance<I>),
+}
+
+/// When a goal holds ambiguously (e.g., because there are multiple possible
+/// solutions), we issue a set of *guidance* back to type inference.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum Guidance<I: Interner> {
+ /// The existential variables *must* have the given values if the goal is
+ /// ever to hold, but that alone isn't enough to guarantee the goal will
+ /// actually hold.
+ Definite(Canonical<Substitution<I>>),
+
+ /// There are multiple plausible values for the existentials, but the ones
+ /// here are suggested as the preferred choice heuristically. These should
+ /// be used for inference fallback only.
+ Suggested(Canonical<Substitution<I>>),
+
+ /// There's no useful information to feed back to type inference
+ Unknown,
+}
+
+impl<I: Interner> Solution<I> {
+ /// There are multiple candidate solutions, which may or may not agree on
+ /// the values for existential variables; attempt to combine them. This
+ /// operation does not depend on the order of its arguments.
+ ///
+ /// This actually isn't as precise as it could be, in two ways:
+ ///
+ /// a. It might be that while there are multiple distinct candidates, they
+ /// all agree about *some things*. To be maximally precise, we would
+ /// compute the intersection of what they agree on. It's not clear though
+ /// that this is actually what we want Rust's inference to do, and it's
+ /// certainly not what it does today.
+ ///
+ /// b. There might also be an ambiguous candidate and a successful candidate,
+ /// both with the same refined-goal. In that case, we could probably claim
+ /// success, since if the conditions of the ambiguous candidate were met,
+ /// we know the success would apply. Example: `?0: Clone` yields ambiguous
+ /// candidate `Option<?0>: Clone` and successful candidate `Option<?0>:
+ /// Clone`.
+ ///
+ /// But you get the idea.
+ pub fn combine(self, other: Solution<I>, interner: I) -> Solution<I> {
+ use self::Guidance::*;
+
+ if self == other {
+ return self;
+ }
+
+ // Special case hack: if one solution is "true" without any constraints,
+ // that is always the combined result.
+ //
+ // This is not as general as it could be: ideally, if we had one solution
+ // that is Unique with a simpler substitution than the other one, or region constraints
+ // which are a subset, we'd combine them.
+ if self.is_trivial_and_always_true(interner) {
+ return self;
+ }
+ if other.is_trivial_and_always_true(interner) {
+ return other;
+ }
+
+ debug!(
+ "combine {} with {}",
+ self.display(interner),
+ other.display(interner)
+ );
+
+ // Otherwise, always downgrade to Ambig:
+
+ let guidance = match (self.into_guidance(), other.into_guidance()) {
+ (Definite(ref subst1), Definite(ref subst2)) if subst1 == subst2 => {
+ Definite(subst1.clone())
+ }
+ (Suggested(ref subst1), Suggested(ref subst2)) if subst1 == subst2 => {
+ Suggested(subst1.clone())
+ }
+ _ => Unknown,
+ };
+ Solution::Ambig(guidance)
+ }
+
+ pub fn is_trivial_and_always_true(&self, interner: I) -> bool {
+ match self {
+ Solution::Unique(constrained_subst) => {
+ constrained_subst.value.subst.is_identity_subst(interner)
+ && constrained_subst.value.constraints.is_empty(interner)
+ }
+ Solution::Ambig(_) => false,
+ }
+ }
+
+ /// View this solution purely in terms of type inference guidance
+ pub fn into_guidance(self) -> Guidance<I> {
+ match self {
+ Solution::Unique(constrained) => Guidance::Definite(Canonical {
+ value: constrained.value.subst,
+ binders: constrained.binders,
+ }),
+ Solution::Ambig(guidance) => guidance,
+ }
+ }
+
+ /// Extract a constrained substitution from this solution, even if ambiguous.
+ pub fn constrained_subst(&self, interner: I) -> Option<Canonical<ConstrainedSubst<I>>> {
+ match *self {
+ Solution::Unique(ref constrained) => Some(constrained.clone()),
+ Solution::Ambig(Guidance::Definite(ref canonical))
+ | Solution::Ambig(Guidance::Suggested(ref canonical)) => {
+ let value = ConstrainedSubst {
+ subst: canonical.value.clone(),
+ constraints: Constraints::empty(interner),
+ };
+ Some(Canonical {
+ value,
+ binders: canonical.binders.clone(),
+ })
+ }
+ Solution::Ambig(_) => None,
+ }
+ }
+
+ /// Determine whether this solution contains type information that *must*
+ /// hold, and returns the subst in that case.
+ pub fn definite_subst(&self, interner: I) -> Option<Canonical<ConstrainedSubst<I>>> {
+ match self {
+ Solution::Unique(constrained) => Some(constrained.clone()),
+ Solution::Ambig(Guidance::Definite(canonical)) => {
+ let value = ConstrainedSubst {
+ subst: canonical.value.clone(),
+ constraints: Constraints::empty(interner),
+ };
+ Some(Canonical {
+ value,
+ binders: canonical.binders.clone(),
+ })
+ }
+ _ => None,
+ }
+ }
+
+ pub fn is_unique(&self) -> bool {
+ matches!(*self, Solution::Unique(..))
+ }
+
+ pub fn is_ambig(&self) -> bool {
+ matches!(*self, Solution::Ambig(_))
+ }
+
+ pub fn display(&self, interner: I) -> SolutionDisplay<'_, I> {
+ SolutionDisplay {
+ solution: self,
+ interner,
+ }
+ }
+}
+
+pub struct SolutionDisplay<'a, I: Interner> {
+ solution: &'a Solution<I>,
+ interner: I,
+}
+
+impl<'a, I: Interner> fmt::Display for SolutionDisplay<'a, I> {
+ #[rustfmt::skip]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ let SolutionDisplay { solution, interner } = self;
+ match solution {
+ // If a `Unique` solution has no associated data, omit the trailing semicolon.
+ // This makes blessed test output nicer to read.
+ Solution::Unique(Canonical { binders, value: ConstrainedSubst { subst, constraints } } )
+ if interner.constraints_data(constraints.interned()).is_empty()
+ && interner.substitution_data(subst.interned()).is_empty()
+ && interner.canonical_var_kinds_data(binders.interned()).is_empty()
+ => write!(f, "Unique"),
+
+ Solution::Unique(constrained) => write!(f, "Unique; {}", constrained.display(*interner)),
+
+ Solution::Ambig(Guidance::Definite(subst)) => write!(
+ f,
+ "Ambiguous; definite substitution {}",
+ subst.display(*interner)
+ ),
+ Solution::Ambig(Guidance::Suggested(subst)) => write!(
+ f,
+ "Ambiguous; suggested substitution {}",
+ subst.display(*interner)
+ ),
+ Solution::Ambig(Guidance::Unknown) => write!(f, "Ambiguous; no inference guidance"),
+ }
+ }
+}
+
+#[derive(Debug)]
+pub enum SubstitutionResult<S> {
+ Definite(S),
+ Ambiguous(S),
+ Floundered,
+}
+
+impl<S> SubstitutionResult<S> {
+ pub fn as_ref(&self) -> SubstitutionResult<&S> {
+ match self {
+ SubstitutionResult::Definite(subst) => SubstitutionResult::Definite(subst),
+ SubstitutionResult::Ambiguous(subst) => SubstitutionResult::Ambiguous(subst),
+ SubstitutionResult::Floundered => SubstitutionResult::Floundered,
+ }
+ }
+ pub fn map<U, F: FnOnce(S) -> U>(self, f: F) -> SubstitutionResult<U> {
+ match self {
+ SubstitutionResult::Definite(subst) => SubstitutionResult::Definite(f(subst)),
+ SubstitutionResult::Ambiguous(subst) => SubstitutionResult::Ambiguous(f(subst)),
+ SubstitutionResult::Floundered => SubstitutionResult::Floundered,
+ }
+ }
+}
+
+impl<S: fmt::Display> fmt::Display for SubstitutionResult<S> {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ SubstitutionResult::Definite(subst) => write!(fmt, "{}", subst),
+ SubstitutionResult::Ambiguous(subst) => write!(fmt, "Ambiguous({})", subst),
+ SubstitutionResult::Floundered => write!(fmt, "Floundered"),
+ }
+ }
+}
+
+/// Finds the solution to "goals", or trait queries -- i.e., figures
+/// out what sets of types implement which traits. Also, between
+/// queries, this struct stores the cached state from previous solver
+/// attempts, which can then be re-used later.
+pub trait Solver<I: Interner>
+where
+ Self: fmt::Debug,
+{
+ /// Attempts to solve the given goal, which must be in canonical
+ /// form. Returns a unique solution (if one exists). This will do
+ /// only as much work towards `goal` as it has to (and that work
+ /// is cached for future attempts).
+ ///
+ /// # Parameters
+ ///
+ /// - `program` -- defines the program clauses in scope.
+ /// - **Important:** You must supply the same set of program clauses
+ /// each time you invoke `solve`, as otherwise the cached data may be
+ /// invalid.
+ /// - `goal` the goal to solve
+ ///
+ /// # Returns
+ ///
+ /// - `None` is the goal cannot be proven.
+ /// - `Some(solution)` if we succeeded in finding *some* answers,
+ /// although `solution` may reflect ambiguity and unknowns.
+ fn solve(
+ &mut self,
+ program: &dyn RustIrDatabase<I>,
+ goal: &UCanonical<InEnvironment<Goal<I>>>,
+ ) -> Option<Solution<I>>;
+
+ /// Attempts to solve the given goal, which must be in canonical
+ /// form. Returns a unique solution (if one exists). This will do
+ /// only as much work towards `goal` as it has to (and that work
+ /// is cached for future attempts). In addition, the solving of the
+ /// goal can be limited by returning `false` from `should_continue`.
+ ///
+ /// # Parameters
+ ///
+ /// - `program` -- defines the program clauses in scope.
+ /// - **Important:** You must supply the same set of program clauses
+ /// each time you invoke `solve`, as otherwise the cached data may be
+ /// invalid.
+ /// - `goal` the goal to solve
+ /// - `should_continue` if `false` is returned, the no further solving
+ /// will be done. A `Guidance(Suggested(...))` will be returned a
+ /// `Solution`, using any answers that were generated up to that point.
+ ///
+ /// # Returns
+ ///
+ /// - `None` is the goal cannot be proven.
+ /// - `Some(solution)` if we succeeded in finding *some* answers,
+ /// although `solution` may reflect ambiguity and unknowns.
+ fn solve_limited(
+ &mut self,
+ program: &dyn RustIrDatabase<I>,
+ goal: &UCanonical<InEnvironment<Goal<I>>>,
+ should_continue: &dyn std::ops::Fn() -> bool,
+ ) -> Option<Solution<I>>;
+
+ /// Attempts to solve the given goal, which must be in canonical
+ /// form. Provides multiple solutions to function `f`. This will do
+ /// only as much work towards `goal` as it has to (and that work
+ /// is cached for future attempts).
+ ///
+ /// # Parameters
+ ///
+ /// - `program` -- defines the program clauses in scope.
+ /// - **Important:** You must supply the same set of program clauses
+ /// each time you invoke `solve`, as otherwise the cached data may be
+ /// invalid.
+ /// - `goal` the goal to solve
+ /// - `f` -- function to proceed solution. New solutions will be generated
+ /// while function returns `true`.
+ /// - first argument is solution found
+ /// - second argument is the next solution present
+ /// - returns true if next solution should be handled
+ ///
+ /// # Returns
+ ///
+ /// - `true` all solutions were processed with the function.
+ /// - `false` the function returned `false` and solutions were interrupted.
+ fn solve_multiple(
+ &mut self,
+ program: &dyn RustIrDatabase<I>,
+ goal: &UCanonical<InEnvironment<Goal<I>>>,
+ f: &mut dyn FnMut(SubstitutionResult<Canonical<ConstrainedSubst<I>>>, bool) -> bool,
+ ) -> bool;
+
+ /// A convenience method for when one doesn't need the actual solution,
+ /// only whether or not one exists.
+ fn has_unique_solution(
+ &mut self,
+ program: &dyn RustIrDatabase<I>,
+ goal: &UCanonical<InEnvironment<Goal<I>>>,
+ ) -> bool {
+ match self.solve(program, goal) {
+ Some(sol) => sol.is_unique(),
+ None => false,
+ }
+ }
+}