blob: 2524ff795b8c67cefe3a5fb62b6e9abf7e3ed8cd [file] [log] [blame] [edit]
use crate::fixed_point::Minimums;
use crate::solve::SolveDatabase;
use chalk_ir::cast::Cast;
use chalk_ir::fold::TypeFoldable;
use chalk_ir::interner::{HasInterner, Interner};
use chalk_ir::visit::TypeVisitable;
use chalk_ir::zip::Zip;
use chalk_ir::{
Binders, BoundVar, Canonical, ConstrainedSubst, Constraint, Constraints, DomainGoal,
Environment, EqGoal, Fallible, GenericArg, GenericArgData, Goal, GoalData, InEnvironment,
NoSolution, ProgramClauseImplication, QuantifierKind, Substitution, SubtypeGoal, TyKind,
TyVariableKind, UCanonical, UnificationDatabase, UniverseMap, Variance,
};
use chalk_solve::debug_span;
use chalk_solve::infer::{InferenceTable, ParameterEnaVariableExt};
use chalk_solve::solve::truncate;
use chalk_solve::{Guidance, Solution};
use rustc_hash::FxHashSet;
use std::fmt::Debug;
use tracing::{debug, instrument};
enum Outcome {
Complete,
Incomplete,
}
impl Outcome {
fn is_complete(&self) -> bool {
matches!(self, Outcome::Complete)
}
}
/// A goal that must be resolved
#[derive(Clone, Debug, PartialEq, Eq)]
enum Obligation<I: Interner> {
/// For "positive" goals, we flatten all the way out to leafs within the
/// current `Fulfill`
Prove(InEnvironment<Goal<I>>),
/// For "negative" goals, we don't flatten in *this* `Fulfill`, which would
/// require having a logical "or" operator. Instead, we recursively solve in
/// a fresh `Fulfill`.
Refute(InEnvironment<Goal<I>>),
}
/// When proving a leaf goal, we record the free variables that appear within it
/// so that we can update inference state accordingly.
#[derive(Clone, Debug)]
struct PositiveSolution<I: Interner> {
free_vars: Vec<GenericArg<I>>,
universes: UniverseMap,
solution: Solution<I>,
}
/// When refuting a goal, there's no impact on inference state.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum NegativeSolution {
Refuted,
Ambiguous,
}
fn canonicalize<I: Interner, T>(
infer: &mut InferenceTable<I>,
interner: I,
value: T,
) -> (Canonical<T>, Vec<GenericArg<I>>)
where
T: TypeFoldable<I>,
T: HasInterner<Interner = I>,
{
let res = infer.canonicalize(interner, value);
let free_vars = res
.free_vars
.into_iter()
.map(|free_var| free_var.to_generic_arg(interner))
.collect();
(res.quantified, free_vars)
}
fn u_canonicalize<I: Interner, T>(
_infer: &mut InferenceTable<I>,
interner: I,
value0: &Canonical<T>,
) -> (UCanonical<T>, UniverseMap)
where
T: Clone + HasInterner<Interner = I> + TypeFoldable<I> + TypeVisitable<I>,
T: HasInterner<Interner = I>,
{
let res = InferenceTable::u_canonicalize(interner, value0);
(res.quantified, res.universes)
}
fn unify<I: Interner, T>(
infer: &mut InferenceTable<I>,
interner: I,
db: &dyn UnificationDatabase<I>,
environment: &Environment<I>,
variance: Variance,
a: &T,
b: &T,
) -> Fallible<Vec<InEnvironment<Goal<I>>>>
where
T: ?Sized + Zip<I>,
{
let res = infer.relate(interner, db, environment, variance, a, b)?;
Ok(res.goals)
}
/// A `Fulfill` is where we actually break down complex goals, instantiate
/// variables, and perform inference. It's highly stateful. It's generally used
/// in Chalk to try to solve a goal, and then package up what was learned in a
/// stateless, canonical way.
///
/// In rustc, you can think of there being an outermost `Fulfill` that's used when
/// type checking each function body, etc. There, the state reflects the state
/// of type inference in general. But when solving trait constraints, *fresh*
/// `Fulfill` instances will be created to solve canonicalized, free-standing
/// goals, and transport what was learned back to the outer context.
pub(super) struct Fulfill<'s, I: Interner, Solver: SolveDatabase<I>> {
solver: &'s mut Solver,
subst: Substitution<I>,
infer: InferenceTable<I>,
/// The remaining goals to prove or refute
obligations: Vec<Obligation<I>>,
/// Lifetime constraints that must be fulfilled for a solution to be fully
/// validated.
constraints: FxHashSet<InEnvironment<Constraint<I>>>,
/// Record that a goal has been processed that can neither be proved nor
/// refuted. In such a case the solution will be either `CannotProve`, or `Err`
/// in the case where some other goal leads to an error.
cannot_prove: bool,
}
impl<'s, I: Interner, Solver: SolveDatabase<I>> Fulfill<'s, I, Solver> {
#[instrument(level = "debug", skip(solver, infer))]
pub(super) fn new_with_clause(
solver: &'s mut Solver,
infer: InferenceTable<I>,
subst: Substitution<I>,
canonical_goal: InEnvironment<DomainGoal<I>>,
clause: &Binders<ProgramClauseImplication<I>>,
) -> Fallible<Self> {
let mut fulfill = Fulfill {
solver,
infer,
subst,
obligations: vec![],
constraints: FxHashSet::default(),
cannot_prove: false,
};
let ProgramClauseImplication {
consequence,
conditions,
constraints,
priority: _,
} = fulfill
.infer
.instantiate_binders_existentially(fulfill.solver.interner(), clause.clone());
debug!(?consequence, ?conditions, ?constraints);
fulfill
.constraints
.extend(constraints.as_slice(fulfill.interner()).to_owned());
debug!("the subst is {:?}", fulfill.subst);
if let Err(e) = fulfill.unify(
&canonical_goal.environment,
Variance::Invariant,
&canonical_goal.goal,
&consequence,
) {
return Err(e);
}
// if so, toss in all of its premises
for condition in conditions.as_slice(fulfill.solver.interner()) {
if let Err(e) = fulfill.push_goal(&canonical_goal.environment, condition.clone()) {
return Err(e);
}
}
Ok(fulfill)
}
pub(super) fn new_with_simplification(
solver: &'s mut Solver,
infer: InferenceTable<I>,
subst: Substitution<I>,
canonical_goal: InEnvironment<Goal<I>>,
) -> Fallible<Self> {
let mut fulfill = Fulfill {
solver,
infer,
subst,
obligations: vec![],
constraints: FxHashSet::default(),
cannot_prove: false,
};
if let Err(e) = fulfill.push_goal(&canonical_goal.environment, canonical_goal.goal.clone())
{
return Err(e);
}
Ok(fulfill)
}
fn push_obligation(&mut self, obligation: Obligation<I>) {
// truncate to avoid overflows
match &obligation {
Obligation::Prove(goal) => {
if truncate::needs_truncation(
self.solver.interner(),
&mut self.infer,
self.solver.max_size(),
goal,
) {
// the goal is too big. Record that we should return Ambiguous
self.cannot_prove = true;
return;
}
}
Obligation::Refute(goal) => {
if truncate::needs_truncation(
self.solver.interner(),
&mut self.infer,
self.solver.max_size(),
goal,
) {
// the goal is too big. Record that we should return Ambiguous
self.cannot_prove = true;
return;
}
}
};
self.obligations.push(obligation);
}
/// Unifies `a` and `b` in the given environment.
///
/// Wraps `InferenceTable::unify`; any resulting normalizations are added
/// into our list of pending obligations with the given environment.
pub(super) fn unify<T>(
&mut self,
environment: &Environment<I>,
variance: Variance,
a: &T,
b: &T,
) -> Fallible<()>
where
T: ?Sized + Zip<I> + Debug,
{
let goals = unify(
&mut self.infer,
self.solver.interner(),
self.solver.db().unification_database(),
environment,
variance,
a,
b,
)?;
debug!("unify({:?}, {:?}) succeeded", a, b);
debug!("unify: goals={:?}", goals);
for goal in goals {
let goal = goal.cast(self.solver.interner());
self.push_obligation(Obligation::Prove(goal));
}
Ok(())
}
/// Create obligations for the given goal in the given environment. This may
/// ultimately create any number of obligations.
#[instrument(level = "debug", skip(self))]
pub(super) fn push_goal(
&mut self,
environment: &Environment<I>,
goal: Goal<I>,
) -> Fallible<()> {
let interner = self.solver.interner();
match goal.data(interner) {
GoalData::Quantified(QuantifierKind::ForAll, subgoal) => {
let subgoal = self
.infer
.instantiate_binders_universally(self.solver.interner(), subgoal.clone());
self.push_goal(environment, subgoal)?;
}
GoalData::Quantified(QuantifierKind::Exists, subgoal) => {
let subgoal = self
.infer
.instantiate_binders_existentially(self.solver.interner(), subgoal.clone());
self.push_goal(environment, subgoal)?;
}
GoalData::Implies(wc, subgoal) => {
let new_environment =
&environment.add_clauses(interner, wc.iter(interner).cloned());
self.push_goal(new_environment, subgoal.clone())?;
}
GoalData::All(goals) => {
for subgoal in goals.as_slice(interner) {
self.push_goal(environment, subgoal.clone())?;
}
}
GoalData::Not(subgoal) => {
let in_env = InEnvironment::new(environment, subgoal.clone());
self.push_obligation(Obligation::Refute(in_env));
}
GoalData::DomainGoal(_) => {
let in_env = InEnvironment::new(environment, goal);
self.push_obligation(Obligation::Prove(in_env));
}
GoalData::EqGoal(EqGoal { a, b }) => {
self.unify(environment, Variance::Invariant, &a, &b)?;
}
GoalData::SubtypeGoal(SubtypeGoal { a, b }) => {
let a_norm = self.infer.normalize_ty_shallow(interner, a);
let a = a_norm.as_ref().unwrap_or(a);
let b_norm = self.infer.normalize_ty_shallow(interner, b);
let b = b_norm.as_ref().unwrap_or(b);
if matches!(
a.kind(interner),
TyKind::InferenceVar(_, TyVariableKind::General)
) && matches!(
b.kind(interner),
TyKind::InferenceVar(_, TyVariableKind::General)
) {
self.cannot_prove = true;
} else {
self.unify(environment, Variance::Covariant, &a, &b)?;
}
}
GoalData::CannotProve => {
debug!("Pushed a CannotProve goal, setting cannot_prove = true");
self.cannot_prove = true;
}
}
Ok(())
}
#[instrument(level = "debug", skip(self, minimums, should_continue))]
fn prove(
&mut self,
wc: InEnvironment<Goal<I>>,
minimums: &mut Minimums,
should_continue: impl std::ops::Fn() -> bool + Clone,
) -> Fallible<PositiveSolution<I>> {
let interner = self.solver.interner();
let (quantified, free_vars) = canonicalize(&mut self.infer, interner, wc);
let (quantified, universes) = u_canonicalize(&mut self.infer, interner, &quantified);
let result = self
.solver
.solve_goal(quantified, minimums, should_continue);
Ok(PositiveSolution {
free_vars,
universes,
solution: result?,
})
}
fn refute(
&mut self,
goal: InEnvironment<Goal<I>>,
should_continue: impl std::ops::Fn() -> bool + Clone,
) -> Fallible<NegativeSolution> {
let canonicalized = match self
.infer
.invert_then_canonicalize(self.solver.interner(), goal)
{
Some(v) => v,
None => {
// Treat non-ground negatives as ambiguous. Note that, as inference
// proceeds, we may wind up with more information here.
return Ok(NegativeSolution::Ambiguous);
}
};
// Negate the result
let (quantified, _) =
u_canonicalize(&mut self.infer, self.solver.interner(), &canonicalized);
let mut minimums = Minimums::new(); // FIXME -- minimums here seems wrong
if let Ok(solution) = self
.solver
.solve_goal(quantified, &mut minimums, should_continue)
{
if solution.is_unique() {
Err(NoSolution)
} else {
Ok(NegativeSolution::Ambiguous)
}
} else {
Ok(NegativeSolution::Refuted)
}
}
/// Trying to prove some goal led to a the substitution `subst`; we
/// wish to apply that substitution to our own inference variables
/// (and incorporate any region constraints). This substitution
/// requires some mapping to get it into our namespace -- first,
/// the universes it refers to have been canonicalized, and
/// `universes` stores the mapping back into our
/// universes. Second, the free variables that appear within can
/// be mapped into our variables with `free_vars`.
fn apply_solution(
&mut self,
free_vars: Vec<GenericArg<I>>,
universes: UniverseMap,
subst: Canonical<ConstrainedSubst<I>>,
) {
use chalk_solve::infer::ucanonicalize::UniverseMapExt;
let subst = universes.map_from_canonical(self.interner(), &subst);
let ConstrainedSubst { subst, constraints } = self
.infer
.instantiate_canonical(self.solver.interner(), subst);
debug!(
"fulfill::apply_solution: adding constraints {:?}",
constraints
);
self.constraints
.extend(constraints.as_slice(self.interner()).to_owned());
// We use the empty environment for unification here because we're
// really just doing a substitution on unconstrained variables, which is
// guaranteed to succeed without generating any new constraints.
let empty_env = &Environment::new(self.solver.interner());
for (i, free_var) in free_vars.into_iter().enumerate() {
let subst_value = subst.at(self.interner(), i);
self.unify(empty_env, Variance::Invariant, &free_var, subst_value)
.unwrap_or_else(|err| {
panic!(
"apply_solution failed with free_var={:?}, subst_value={:?}: {:?}",
free_var, subst_value, err
);
});
}
}
fn fulfill(
&mut self,
minimums: &mut Minimums,
should_continue: impl std::ops::Fn() -> bool + Clone,
) -> Fallible<Outcome> {
debug_span!("fulfill", obligations=?self.obligations);
// Try to solve all the obligations. We do this via a fixed-point
// iteration. We try to solve each obligation in turn. Anything which is
// successful, we drop; anything ambiguous, we retain in the
// `obligations` array. This process is repeated so long as we are
// learning new things about our inference state.
let mut obligations = Vec::with_capacity(self.obligations.len());
let mut progress = true;
while progress {
progress = false;
debug!("start of round, {} obligations", self.obligations.len());
// Take the list of `obligations` to solve this round and replace it
// with an empty vector. Iterate through each obligation to solve
// and solve it if we can. If not (because of ambiguity), then push
// it back onto `self.to_prove` for next round. Note that
// `solve_one` may also push onto the `self.to_prove` list
// directly.
assert!(obligations.is_empty());
while let Some(obligation) = self.obligations.pop() {
let ambiguous = match &obligation {
Obligation::Prove(wc) => {
let PositiveSolution {
free_vars,
universes,
solution,
} = self.prove(wc.clone(), minimums, should_continue.clone())?;
if let Some(constrained_subst) = solution.definite_subst(self.interner()) {
// If the substitution is trivial, we won't actually make any progress by applying it!
// So we need to check this to prevent endless loops.
let nontrivial_subst = !is_trivial_canonical_subst(
self.interner(),
&constrained_subst.value.subst,
);
let has_constraints = !constrained_subst
.value
.constraints
.is_empty(self.interner());
if nontrivial_subst || has_constraints {
self.apply_solution(free_vars, universes, constrained_subst);
progress = true;
}
}
solution.is_ambig()
}
Obligation::Refute(goal) => {
let answer = self.refute(goal.clone(), should_continue.clone())?;
answer == NegativeSolution::Ambiguous
}
};
if ambiguous {
debug!("ambiguous result: {:?}", obligation);
obligations.push(obligation);
}
}
self.obligations.append(&mut obligations);
debug!("end of round, {} obligations left", self.obligations.len());
}
// At the end of this process, `self.obligations` should have
// all of the ambiguous obligations, and `obligations` should
// be empty.
assert!(obligations.is_empty());
if self.obligations.is_empty() {
Ok(Outcome::Complete)
} else {
Ok(Outcome::Incomplete)
}
}
/// Try to fulfill all pending obligations and build the resulting
/// solution. The returned solution will transform `subst` substitution with
/// the outcome of type inference by updating the replacements it provides.
pub(super) fn solve(
mut self,
minimums: &mut Minimums,
should_continue: impl std::ops::Fn() -> bool + Clone,
) -> Fallible<Solution<I>> {
let outcome = match self.fulfill(minimums, should_continue.clone()) {
Ok(o) => o,
Err(e) => return Err(e),
};
if self.cannot_prove {
debug!("Goal cannot be proven (cannot_prove = true), returning ambiguous");
return Ok(Solution::Ambig(Guidance::Unknown));
}
if outcome.is_complete() {
// No obligations remain, so we have definitively solved our goals,
// and the current inference state is the unique way to solve them.
let constraints = Constraints::from_iter(self.interner(), self.constraints.clone());
let constrained = canonicalize(
&mut self.infer,
self.solver.interner(),
ConstrainedSubst {
subst: self.subst,
constraints,
},
);
return Ok(Solution::Unique(constrained.0));
}
// Otherwise, we have (positive or negative) obligations remaining, but
// haven't proved that it's *impossible* to satisfy out obligations. we
// need to determine how to package up what we learned about type
// inference as an ambiguous solution.
let canonical_subst =
canonicalize(&mut self.infer, self.solver.interner(), self.subst.clone());
if canonical_subst
.0
.value
.is_identity_subst(self.solver.interner())
{
// In this case, we didn't learn *anything* definitively. So now, we
// go one last time through the positive obligations, this time
// applying even *tentative* inference suggestions, so that we can
// yield these upwards as our own suggestions. There are no
// particular guarantees about *which* obligaiton we derive
// suggestions from.
while let Some(obligation) = self.obligations.pop() {
if let Obligation::Prove(goal) = obligation {
let PositiveSolution {
free_vars,
universes,
solution,
} = self.prove(goal, minimums, should_continue.clone()).unwrap();
if let Some(constrained_subst) =
solution.constrained_subst(self.solver.interner())
{
self.apply_solution(free_vars, universes, constrained_subst);
return Ok(Solution::Ambig(Guidance::Suggested(canonical_subst.0)));
}
}
}
Ok(Solution::Ambig(Guidance::Unknown))
} else {
// While we failed to prove the goal, we still learned that
// something had to hold. Here's an example where this happens:
//
// ```rust
// trait Display {}
// trait Debug {}
// struct Foo<T> {}
// struct Bar {}
// struct Baz {}
//
// impl Display for Bar {}
// impl Display for Baz {}
//
// impl<T> Debug for Foo<T> where T: Display {}
// ```
//
// If we pose the goal `exists<T> { T: Debug }`, we can't say
// for sure what `T` must be (it could be either `Foo<Bar>` or
// `Foo<Baz>`, but we *can* say for sure that it must be of the
// form `Foo<?0>`.
Ok(Solution::Ambig(Guidance::Definite(canonical_subst.0)))
}
}
fn interner(&self) -> I {
self.solver.interner()
}
}
fn is_trivial_canonical_subst<I: Interner>(interner: I, subst: &Substitution<I>) -> bool {
// A subst is trivial if..
subst.iter(interner).enumerate().all(|(index, parameter)| {
let is_trivial = |b: Option<BoundVar>| match b {
None => false,
Some(bound_var) => {
if let Some(index1) = bound_var.index_if_innermost() {
index == index1
} else {
false
}
}
};
match parameter.data(interner) {
// All types and consts are mapped to distinct variables. Since this
// has been canonicalized, those will also be the first N
// variables.
GenericArgData::Ty(t) => is_trivial(t.bound_var(interner)),
GenericArgData::Const(t) => is_trivial(t.bound_var(interner)),
GenericArgData::Lifetime(t) => is_trivial(t.bound_var(interner)),
}
})
}