blob: 28d9cdf19cf3b3306792b576ad211d5bc05aa351 [file] [log] [blame]
use fallible::Fallible;
use hh::HhGoal;
use {DelayedLiteral, ExClause, SimplifiedAnswer};
use std::fmt::Debug;
use std::hash::Hash;
pub(crate) mod prelude;
/// The "context" in which the SLG solver operates. It defines all the
/// types that the SLG solver may need to refer to, as well as a few
/// very simple interconversion methods.
///
/// At any given time, the SLG solver may have more than one context
/// active. First, there is always the *global* context, but when we
/// are in the midst of pursuing some particular strand, we will
/// instantiate a second context just for that work, via the
/// `instantiate_ucanonical_goal` and `instantiate_ex_clause` methods.
///
/// In the chalk implementation, these two contexts are mapped to the
/// same type. But in the rustc implementation, this second context
/// corresponds to a fresh arena, and data allocated in that second
/// context will be freed once the work is done. (The "canonicalizing"
/// steps offer a way to convert data from the inference context back
/// into the global context.)
///
/// FIXME: Clone and Debug bounds are just for easy derive, they are
/// not actually necessary. But dang are they convenient.
pub trait Context: Clone + Debug {
type CanonicalExClause: Debug;
/// A map between universes. These are produced when
/// u-canonicalizing something; they map canonical results back to
/// the universes from the original.
type UniverseMap: Clone + Debug;
/// Extracted from a canonicalized substitution or canonicalized ex clause, this is the type of
/// substitution that is fully normalized with respect to inference variables.
type InferenceNormalizedSubst: Debug;
/// A canonicalized `GoalInEnvironment` -- that is, one where all
/// free inference variables have been bound into the canonical
/// binder. See [the rustc-guide] for more information.
///
/// [the rustc-guide]: https://rust-lang-nursery.github.io/rustc-guide/traits-canonicalization.html
type CanonicalGoalInEnvironment: Debug;
/// A u-canonicalized `GoalInEnvironment` -- this is one where the
/// free universes are renumbered to consecutive integers starting
/// from U1 (but preserving their relative order).
type UCanonicalGoalInEnvironment: Debug + Clone + Eq + Hash;
/// A final solution that is passed back to the user. This is
/// completely opaque to the SLG solver; it is produced by
/// `make_solution`.
type Solution;
/// Part of an answer: represents a canonicalized substitution,
/// combined with region constraints. See [the rustc-guide] for more information.
///
/// [the rustc-guide]: https://rust-lang-nursery.github.io/rustc-guide/traits-canonicalization.html#canonicalizing-the-query-result
type CanonicalConstrainedSubst: Clone + Debug + Eq + Hash;
/// Represents a substitution from the "canonical variables" found
/// in a canonical goal to specific values.
type Substitution: Debug;
/// Represents a region constraint that will be propagated back
/// (but not verified).
type RegionConstraint: Debug;
/// Represents a goal along with an environment.
type GoalInEnvironment: Debug + Clone + Eq + Hash;
/// Represents a set of hypotheses that are assumed to be true.
type Environment: Debug + Clone;
/// Goals correspond to things we can prove.
type Goal: Clone + Debug + Eq;
/// A goal that can be targeted by a program clause. The SLG
/// solver treats these opaquely; in contrast, it understands
/// "meta" goals like `G1 && G2` and so forth natively.
type DomainGoal: Debug;
/// A "higher-order" goal, quantified over some types and/or
/// lifetimes. When you have a quantification, like `forall<T> { G
/// }` or `exists<T> { G }`, this represents the `<T> { G }` part.
///
/// (In Lambda Prolog, this would be a "lambda predicate", like `T
/// \ Goal`).
type BindersGoal: Debug;
/// A term that can be quantified over and unified -- in current
/// Chalk, either a type or lifetime.
type Parameter: Debug;
/// A rule like `DomainGoal :- Goal`.
///
/// `resolvent_clause` combines a program-clause and a concrete
/// goal we are trying to solve to produce an ex-clause.
type ProgramClause: Debug;
/// A vector of program clauses.
type ProgramClauses: Debug;
/// How to relate two kinds when unifying: for example in rustc, we
/// may want to unify parameters either for the sub-typing relation or for
/// the equality relation.
type Variance;
/// The successful result from unification: contains new subgoals
/// and things that can be attached to an ex-clause.
type UnificationResult;
/// Given an environment and a goal, glue them together to create
/// a `GoalInEnvironment`.
fn goal_in_environment(
environment: &Self::Environment,
goal: Self::Goal,
) -> Self::GoalInEnvironment;
}
pub trait ContextOps<C: Context>: Sized + Clone + Debug + AggregateOps<C> {
/// True if this is a coinductive goal -- e.g., proving an auto trait.
fn is_coinductive(&self, goal: &C::UCanonicalGoalInEnvironment) -> bool;
/// Create an inference table for processing a new goal and instantiate that goal
/// in that context, returning "all the pieces".
///
/// More specifically: given a u-canonical goal `arg`, creates a
/// new inference table `T` and populates it with the universes
/// found in `arg`. Then, creates a substitution `S` that maps
/// each bound variable in `arg` to a fresh inference variable
/// from T. Returns:
///
/// - the table `T`
/// - the substitution `S`
/// - the environment and goal found by substitution `S` into `arg`
fn instantiate_ucanonical_goal<R>(
&self,
arg: &C::UCanonicalGoalInEnvironment,
op: impl WithInstantiatedUCanonicalGoal<C, Output = R>,
) -> R;
fn instantiate_ex_clause<R>(
&self,
num_universes: usize,
canonical_ex_clause: &C::CanonicalExClause,
op: impl WithInstantiatedExClause<C, Output = R>,
) -> R;
/// Extracts the inner normalized substitution from a canonical ex-clause.
fn inference_normalized_subst_from_ex_clause(
canon_ex_clause: &C::CanonicalExClause,
) -> &C::InferenceNormalizedSubst;
/// Extracts the inner normalized substitution from a canonical constraint subst.
fn inference_normalized_subst_from_subst(
canon_ex_clause: &C::CanonicalConstrainedSubst,
) -> &C::InferenceNormalizedSubst;
/// True if this solution has no region constraints.
fn empty_constraints(ccs: &C::CanonicalConstrainedSubst) -> bool;
fn canonical(u_canon: &C::UCanonicalGoalInEnvironment) -> &C::CanonicalGoalInEnvironment;
fn is_trivial_substitution(u_canon: &C::UCanonicalGoalInEnvironment,
canonical_subst: &C::CanonicalConstrainedSubst) -> bool;
fn num_universes(&C::UCanonicalGoalInEnvironment) -> usize;
/// Convert a goal G *from* the canonical universes *into* our
/// local universes. This will yield a goal G' that is the same
/// but for the universes of universally quantified names.
fn map_goal_from_canonical(
&C::UniverseMap,
value: &C::CanonicalGoalInEnvironment,
) -> C::CanonicalGoalInEnvironment;
/// Convert a substitution *from* the canonical universes *into*
/// our local universes. This will yield a substitution S' that is
/// the same but for the universes of universally quantified
/// names.
fn map_subst_from_canonical(
&C::UniverseMap,
value: &C::CanonicalConstrainedSubst,
) -> C::CanonicalConstrainedSubst;
}
/// Callback trait for `instantiate_ucanonical_goal`. Unlike the other
/// traits in this file, this is not implemented by the context crate, but rather
/// by code in this crate.
///
/// This basically plays the role of an `FnOnce` -- but unlike an
/// `FnOnce`, the `with` method is generic.
pub trait WithInstantiatedUCanonicalGoal<C: Context> {
type Output;
fn with<I: Context>(
self,
infer: &mut dyn InferenceTable<C, I>,
subst: I::Substitution,
environment: I::Environment,
goal: I::Goal,
) -> Self::Output;
}
/// Callback trait for `instantiate_ex_clause`. Unlike the other
/// traits in this file, this is not implemented by the context crate,
/// but rather by code in this crate.
///
/// This basically plays the role of an `FnOnce` -- but unlike an
/// `FnOnce`, the `with` method is generic.
pub trait WithInstantiatedExClause<C: Context> {
type Output;
fn with<I: Context>(
self,
infer: &mut dyn InferenceTable<C, I>,
ex_clause: ExClause<I>,
) -> Self::Output;
}
/// Methods for combining solutions to yield an aggregate solution.
pub trait AggregateOps<C: Context> {
fn make_solution(
&self,
root_goal: &C::CanonicalGoalInEnvironment,
simplified_answers: impl AnswerStream<C>,
) -> Option<C::Solution>;
}
/// An "inference table" contains the state to support unification and
/// other operations on terms.
pub trait InferenceTable<C: Context, I: Context>:
ResolventOps<C, I> + TruncateOps<C, I> + UnificationOps<C, I>
{
/// Convert the context's goal type into the `HhGoal` type that
/// the SLG solver understands. The expectation is that the
/// context's goal type has the same set of variants, but with
/// different names and a different setup. If you inspect
/// `HhGoal`, you will see that this is a "shallow" or "lazy"
/// conversion -- that is, we convert the outermost goal into an
/// `HhGoal`, but the goals contained within are left as context
/// goals.
fn into_hh_goal(&mut self, goal: I::Goal) -> HhGoal<I>;
// Used by: simplify
fn add_clauses(
&mut self,
env: &I::Environment,
clauses: I::ProgramClauses,
) -> I::Environment;
/// Upcast this domain goal into a more general goal.
fn into_goal(&self, domain_goal: I::DomainGoal) -> I::Goal;
/// Create a "cannot prove" goal (see `HhGoal::CannotProve`).
fn cannot_prove(&self) -> I::Goal;
}
/// Methods for unifying and manipulating terms and binders.
pub trait UnificationOps<C: Context, I: Context> {
/// Returns the set of program clauses that might apply to
/// `goal`. (This set can be over-approximated, naturally.)
fn program_clauses(
&self,
environment: &I::Environment,
goal: &I::DomainGoal,
) -> Vec<I::ProgramClause>;
// Used by: simplify
fn instantiate_binders_universally(&mut self, arg: &I::BindersGoal) -> I::Goal;
// Used by: simplify
fn instantiate_binders_existentially(&mut self, arg: &I::BindersGoal) -> I::Goal;
// Used by: logic (but for debugging only)
fn debug_ex_clause(&mut self, value: &'v ExClause<I>) -> Box<dyn Debug + 'v>;
// Used by: logic
fn canonicalize_goal(&mut self, value: &I::GoalInEnvironment) -> C::CanonicalGoalInEnvironment;
// Used by: logic
fn canonicalize_ex_clause(&mut self, value: &ExClause<I>) -> C::CanonicalExClause;
// Used by: logic
fn canonicalize_constrained_subst(
&mut self,
subst: I::Substitution,
constraints: Vec<I::RegionConstraint>,
) -> C::CanonicalConstrainedSubst;
// Used by: logic
fn u_canonicalize_goal(
&mut self,
value: &C::CanonicalGoalInEnvironment,
) -> (C::UCanonicalGoalInEnvironment, C::UniverseMap);
fn sink_answer_subset(
&self,
value: &C::CanonicalConstrainedSubst,
) -> I::CanonicalConstrainedSubst;
fn lift_delayed_literal(
&self,
value: DelayedLiteral<I>,
) -> DelayedLiteral<C>;
// Used by: logic
fn invert_goal(&mut self, value: &I::GoalInEnvironment) -> Option<I::GoalInEnvironment>;
// Used by: simplify
fn unify_parameters(
&mut self,
environment: &I::Environment,
variance: I::Variance,
a: &I::Parameter,
b: &I::Parameter,
) -> Fallible<I::UnificationResult>;
/// Add the residual subgoals as new subgoals of the ex-clause.
/// Also add region constraints.
fn into_ex_clause(&mut self, result: I::UnificationResult, ex_clause: &mut ExClause<I>);
}
/// "Truncation" (called "abstraction" in the papers referenced below)
/// refers to the act of modifying a goal or answer that has become
/// too large in order to guarantee termination. The SLG solver
/// doesn't care about the precise truncation function, so long as
/// it's deterministic and so forth.
///
/// Citations:
///
/// - Terminating Evaluation of Logic Programs with Finite Three-Valued Models
/// - Riguzzi and Swift; ACM Transactions on Computational Logic 2013
/// - Radial Restraint
/// - Grosof and Swift; 2013
pub trait TruncateOps<C: Context, I: Context> {
/// If `subgoal` is too large, return a truncated variant (else
/// return `None`).
fn truncate_goal(&mut self, subgoal: &I::GoalInEnvironment) -> Option<I::GoalInEnvironment>;
/// If `subst` is too large, return a truncated variant (else
/// return `None`).
fn truncate_answer(&mut self, subst: &I::Substitution) -> Option<I::Substitution>;
}
pub trait ResolventOps<C: Context, I: Context> {
/// Combines the `goal` (instantiated within `infer`) with the
/// given program clause to yield the start of a new strand (a
/// canonical ex-clause).
///
/// The bindings in `infer` are unaffected by this operation.
fn resolvent_clause(
&mut self,
environment: &I::Environment,
goal: &I::DomainGoal,
subst: &I::Substitution,
clause: &I::ProgramClause,
) -> Fallible<C::CanonicalExClause>;
fn apply_answer_subst(
&mut self,
ex_clause: ExClause<I>,
selected_goal: &I::GoalInEnvironment,
answer_table_goal: &C::CanonicalGoalInEnvironment,
canonical_answer_subst: &C::CanonicalConstrainedSubst,
) -> Fallible<ExClause<I>>;
}
pub trait AnswerStream<C: Context> {
fn peek_answer(&mut self) -> Option<SimplifiedAnswer<C>>;
fn next_answer(&mut self) -> Option<SimplifiedAnswer<C>>;
/// Invokes `test` with each possible future answer, returning true immediately
/// if we find any answer for which `test` returns true.
fn any_future_answer(&mut self, test: impl FnMut(&C::InferenceNormalizedSubst) -> bool)
-> bool;
}