use crate::normalize_deep::DeepNormalizer;
use crate::slg::{self, ResolventOps, TruncatingInferenceTable};
use crate::{ExClause, Literal, TimeStamp};
use chalk_ir::fold::shift::Shift;
use chalk_ir::fold::Fold;
use chalk_ir::interner::{HasInterner, Interner};
use chalk_ir::zip::{Zip, Zipper};
use chalk_ir::*;
use chalk_solve::infer::InferenceTable;
use tracing::{debug, instrument};
impl<I: Interner> ResolventOps<I> for TruncatingInferenceTable<I> {
#[instrument(level = "debug", skip(self, interner, environment, subst))]
fn resolvent_clause(
&mut self,
db: &dyn UnificationDatabase<I>,
interner: &I,
environment: &Environment<I>,
goal: &DomainGoal<I>,
subst: &Substitution<I>,
clause: &ProgramClause<I>,
) -> Fallible<ExClause<I>> {
let ProgramClauseImplication {
consequence,
conditions,
constraints,
priority: _,
} = {
let ProgramClauseData(implication) = clause.data(interner);
self.infer
.instantiate_binders_existentially(interner, implication)
};
debug!(?consequence, ?conditions, ?constraints);
let unification_result = self.infer.relate(
interner,
db,
environment,
Variance::Invariant,
goal,
&consequence,
)?;
let mut ex_clause = ExClause {
subst: subst.clone(),
ambiguous: false,
constraints: vec![],
subgoals: vec![],
delayed_subgoals: vec![],
answer_time: TimeStamp::default(),
floundered_subgoals: vec![],
};
slg::into_ex_clause(interner, unification_result, &mut ex_clause);
ex_clause
.constraints
.extend(constraints.as_slice(interner).to_owned());
ex_clause
.subgoals
.extend(conditions.iter(interner).map(|c| match c.data(interner) {
GoalData::Not(c1) => {
Literal::Negative(InEnvironment::new(environment, Goal::clone(c1)))
}
_ => Literal::Positive(InEnvironment::new(environment, Goal::clone(c))),
}));
Ok(ex_clause)
}
#[instrument(level = "debug", skip(self, interner))]
fn apply_answer_subst(
&mut self,
interner: &I,
unification_database: &dyn UnificationDatabase<I>,
ex_clause: &mut ExClause<I>,
selected_goal: &InEnvironment<Goal<I>>,
answer_table_goal: &Canonical<InEnvironment<Goal<I>>>,
canonical_answer_subst: &Canonical<AnswerSubst<I>>,
) -> Fallible<()> {
debug!(selected_goal = ?DeepNormalizer::normalize_deep(&mut self.infer, interner, selected_goal));
let AnswerSubst {
subst: answer_subst,
constraints: answer_constraints,
delayed_subgoals,
} = self
.infer
.instantiate_canonical(interner, &canonical_answer_subst);
AnswerSubstitutor::substitute(
interner,
unification_database,
&mut self.infer,
&selected_goal.environment,
&answer_subst,
ex_clause,
&answer_table_goal.value,
selected_goal,
)?;
ex_clause
.constraints
.extend(answer_constraints.as_slice(interner).to_vec());
ex_clause.delayed_subgoals.extend(delayed_subgoals);
Ok(())
}
}
struct AnswerSubstitutor<'t, I: Interner> {
table: &'t mut InferenceTable<I>,
environment: &'t Environment<I>,
answer_subst: &'t Substitution<I>,
outer_binder: DebruijnIndex,
ex_clause: &'t mut ExClause<I>,
interner: &'t I,
unification_database: &'t dyn UnificationDatabase<I>,
}
impl<I: Interner> AnswerSubstitutor<'_, I> {
fn substitute<T: Zip<I>>(
interner: &I,
unification_database: &dyn UnificationDatabase<I>,
table: &mut InferenceTable<I>,
environment: &Environment<I>,
answer_subst: &Substitution<I>,
ex_clause: &mut ExClause<I>,
answer: &T,
pending: &T,
) -> Fallible<()> {
let mut this = AnswerSubstitutor {
interner,
unification_database,
table,
environment,
answer_subst,
ex_clause,
outer_binder: DebruijnIndex::INNERMOST,
};
Zip::zip_with(&mut this, Variance::Invariant, answer, pending)?;
Ok(())
}
fn unify_free_answer_var(
&mut self,
interner: &I,
db: &dyn UnificationDatabase<I>,
variance: Variance,
answer_var: BoundVar,
pending: GenericArgData<I>,
) -> Fallible<bool> {
let answer_index = match answer_var.index_if_bound_at(self.outer_binder) {
Some(index) => index,
None => return Ok(false),
};
let answer_param = self.answer_subst.at(interner, answer_index);
let pending_shifted = pending
.shifted_out_to(interner, self.outer_binder)
.unwrap_or_else(|_| {
panic!(
"truncate extracted a pending value that references internal binder: {:?}",
pending,
)
});
slg::into_ex_clause(
interner,
self.table.relate(
interner,
db,
&self.environment,
variance,
answer_param,
&GenericArg::new(interner, pending_shifted),
)?,
self.ex_clause,
);
Ok(true)
}
fn assert_matching_vars(
&mut self,
answer_var: BoundVar,
pending_var: BoundVar,
) -> Fallible<()> {
let BoundVar {
debruijn: answer_depth,
index: answer_index,
} = answer_var;
let BoundVar {
debruijn: pending_depth,
index: pending_index,
} = pending_var;
assert!(answer_depth.within(self.outer_binder));
assert_eq!(answer_depth, pending_depth);
assert_eq!(answer_index, pending_index,);
Ok(())
}
}
impl<'i, I: Interner> Zipper<'i, I> for AnswerSubstitutor<'i, I> {
fn zip_tys(&mut self, variance: Variance, answer: &Ty<I>, pending: &Ty<I>) -> Fallible<()> {
let interner = self.interner;
if let Some(pending) = self.table.normalize_ty_shallow(interner, pending) {
return Zip::zip_with(self, variance, answer, &pending);
}
if let TyKind::BoundVar(answer_depth) = answer.kind(interner) {
if self.unify_free_answer_var(
interner,
self.unification_database,
variance,
*answer_depth,
GenericArgData::Ty(pending.clone()),
)? {
return Ok(());
}
}
match (answer.kind(interner), pending.kind(interner)) {
(TyKind::BoundVar(answer_depth), TyKind::BoundVar(pending_depth)) => {
self.assert_matching_vars(*answer_depth, *pending_depth)
}
(TyKind::Dyn(answer), TyKind::Dyn(pending)) => {
Zip::zip_with(self, variance, answer, pending)
}
(TyKind::Alias(answer), TyKind::Alias(pending)) => {
Zip::zip_with(self, variance, answer, pending)
}
(TyKind::Placeholder(answer), TyKind::Placeholder(pending)) => {
Zip::zip_with(self, variance, answer, pending)
}
(TyKind::Function(answer), TyKind::Function(pending)) => Zip::zip_with(
self,
variance,
&answer.clone().into_binders(interner),
&pending.clone().into_binders(interner),
),
(TyKind::InferenceVar(_, _), _) | (_, TyKind::InferenceVar(_, _)) => panic!(
"unexpected inference var in answer `{:?}` or pending goal `{:?}`",
answer, pending,
),
(TyKind::Adt(id_a, substitution_a), TyKind::Adt(id_b, substitution_b)) => {
if id_a != id_b {
return Err(NoSolution);
}
self.zip_substs(
variance,
Some(self.unification_database().adt_variance(*id_a)),
substitution_a.as_slice(interner),
substitution_b.as_slice(interner),
)
}
(
TyKind::AssociatedType(id_a, substitution_a),
TyKind::AssociatedType(id_b, substitution_b),
) => {
if id_a != id_b {
return Err(NoSolution);
}
self.zip_substs(
variance,
None,
substitution_a.as_slice(interner),
substitution_b.as_slice(interner),
)
}
(TyKind::Scalar(scalar_a), TyKind::Scalar(scalar_b)) => {
Zip::zip_with(self, variance, scalar_a, scalar_b)
}
(TyKind::Str, TyKind::Str) => Ok(()),
(TyKind::Tuple(arity_a, substitution_a), TyKind::Tuple(arity_b, substitution_b)) => {
if arity_a != arity_b {
return Err(NoSolution);
}
self.zip_substs(
variance,
None,
substitution_a.as_slice(interner),
substitution_b.as_slice(interner),
)
}
(
TyKind::OpaqueType(id_a, substitution_a),
TyKind::OpaqueType(id_b, substitution_b),
) => {
if id_a != id_b {
return Err(NoSolution);
}
self.zip_substs(
variance,
None,
substitution_a.as_slice(interner),
substitution_b.as_slice(interner),
)
}
(TyKind::Slice(ty_a), TyKind::Slice(ty_b)) => Zip::zip_with(self, variance, ty_a, ty_b),
(TyKind::FnDef(id_a, substitution_a), TyKind::FnDef(id_b, substitution_b)) => {
if id_a != id_b {
return Err(NoSolution);
}
self.zip_substs(
variance,
Some(self.unification_database().fn_def_variance(*id_a)),
substitution_a.as_slice(interner),
substitution_b.as_slice(interner),
)
}
(
TyKind::Ref(mutability_a, lifetime_a, ty_a),
TyKind::Ref(mutability_b, lifetime_b, ty_b),
) => {
if mutability_a != mutability_b {
return Err(NoSolution);
}
Zip::zip_with(
self,
variance.xform(Variance::Contravariant),
lifetime_a,
lifetime_b,
)?;
let output_variance = match mutability_a {
Mutability::Not => Variance::Covariant,
Mutability::Mut => Variance::Invariant,
};
Zip::zip_with(self, variance.xform(output_variance), ty_a, ty_b)
}
(TyKind::Raw(mutability_a, ty_a), TyKind::Raw(mutability_b, ty_b)) => {
if mutability_a != mutability_b {
return Err(NoSolution);
}
let ty_variance = match mutability_a {
Mutability::Not => Variance::Covariant,
Mutability::Mut => Variance::Invariant,
};
Zip::zip_with(self, variance.xform(ty_variance), ty_a, ty_b)
}
(TyKind::Never, TyKind::Never) => Ok(()),
(TyKind::Array(ty_a, const_a), TyKind::Array(ty_b, const_b)) => {
Zip::zip_with(self, variance, ty_a, ty_b)?;
Zip::zip_with(self, variance, const_a, const_b)
}
(TyKind::Closure(id_a, substitution_a), TyKind::Closure(id_b, substitution_b)) => {
if id_a != id_b {
return Err(NoSolution);
}
self.zip_substs(
variance,
None,
substitution_a.as_slice(interner),
substitution_b.as_slice(interner),
)
}
(TyKind::Generator(id_a, substitution_a), TyKind::Generator(id_b, substitution_b)) => {
if id_a != id_b {
return Err(NoSolution);
}
self.zip_substs(
variance,
None,
substitution_a.as_slice(interner),
substitution_b.as_slice(interner),
)
}
(
TyKind::GeneratorWitness(id_a, substitution_a),
TyKind::GeneratorWitness(id_b, substitution_b),
) => {
if id_a != id_b {
return Err(NoSolution);
}
self.zip_substs(
variance,
None,
substitution_a.as_slice(interner),
substitution_b.as_slice(interner),
)
}
(TyKind::Foreign(id_a), TyKind::Foreign(id_b)) => {
Zip::zip_with(self, variance, id_a, id_b)
}
(TyKind::Error, TyKind::Error) => Ok(()),
(_, _) => panic!(
"structural mismatch between answer `{:?}` and pending goal `{:?}`",
answer, pending,
),
}
}
fn zip_lifetimes(
&mut self,
variance: Variance,
answer: &Lifetime<I>,
pending: &Lifetime<I>,
) -> Fallible<()> {
let interner = self.interner;
if let Some(pending) = self.table.normalize_lifetime_shallow(interner, pending) {
return Zip::zip_with(self, variance, answer, &pending);
}
if let LifetimeData::BoundVar(answer_depth) = answer.data(interner) {
if self.unify_free_answer_var(
interner,
self.unification_database,
variance,
*answer_depth,
GenericArgData::Lifetime(pending.clone()),
)? {
return Ok(());
}
}
match (answer.data(interner), pending.data(interner)) {
(LifetimeData::BoundVar(answer_depth), LifetimeData::BoundVar(pending_depth)) => {
self.assert_matching_vars(*answer_depth, *pending_depth)
}
(LifetimeData::Static, LifetimeData::Static)
| (LifetimeData::Placeholder(_), LifetimeData::Placeholder(_))
| (LifetimeData::Erased, LifetimeData::Erased)
| (LifetimeData::Empty(_), LifetimeData::Empty(_)) => {
assert_eq!(answer, pending);
Ok(())
}
(LifetimeData::InferenceVar(_), _) | (_, LifetimeData::InferenceVar(_)) => panic!(
"unexpected inference var in answer `{:?}` or pending goal `{:?}`",
answer, pending,
),
(LifetimeData::Static, _)
| (LifetimeData::BoundVar(_), _)
| (LifetimeData::Placeholder(_), _)
| (LifetimeData::Erased, _)
| (LifetimeData::Empty(_), _) => panic!(
"structural mismatch between answer `{:?}` and pending goal `{:?}`",
answer, pending,
),
(LifetimeData::Phantom(void, _), _) => match *void {},
}
}
fn zip_consts(
&mut self,
variance: Variance,
answer: &Const<I>,
pending: &Const<I>,
) -> Fallible<()> {
let interner = self.interner;
if let Some(pending) = self.table.normalize_const_shallow(interner, pending) {
return Zip::zip_with(self, variance, answer, &pending);
}
let ConstData {
ty: answer_ty,
value: answer_value,
} = answer.data(interner);
let ConstData {
ty: pending_ty,
value: pending_value,
} = pending.data(interner);
self.zip_tys(variance, answer_ty, pending_ty)?;
if let ConstValue::BoundVar(answer_depth) = answer_value {
if self.unify_free_answer_var(
interner,
self.unification_database,
variance,
*answer_depth,
GenericArgData::Const(pending.clone()),
)? {
return Ok(());
}
}
match (answer_value, pending_value) {
(ConstValue::BoundVar(answer_depth), ConstValue::BoundVar(pending_depth)) => {
self.assert_matching_vars(*answer_depth, *pending_depth)
}
(ConstValue::Placeholder(_), ConstValue::Placeholder(_)) => {
assert_eq!(answer, pending);
Ok(())
}
(ConstValue::Concrete(c1), ConstValue::Concrete(c2)) => {
assert!(c1.const_eq(answer_ty, c2, interner));
Ok(())
}
(ConstValue::InferenceVar(_), _) | (_, ConstValue::InferenceVar(_)) => panic!(
"unexpected inference var in answer `{:?}` or pending goal `{:?}`",
answer, pending,
),
(ConstValue::BoundVar(_), _)
| (ConstValue::Placeholder(_), _)
| (ConstValue::Concrete(_), _) => panic!(
"structural mismatch between answer `{:?}` and pending goal `{:?}`",
answer, pending,
),
}
}
fn zip_binders<T>(
&mut self,
variance: Variance,
answer: &Binders<T>,
pending: &Binders<T>,
) -> Fallible<()>
where
T: HasInterner<Interner = I> + Zip<I> + Fold<I, Result = T>,
{
self.outer_binder.shift_in();
Zip::zip_with(
self,
variance,
answer.skip_binders(),
pending.skip_binders(),
)?;
self.outer_binder.shift_out();
Ok(())
}
fn interner(&self) -> &'i I {
self.interner
}
fn unification_database(&self) -> &dyn UnificationDatabase<I> {
self.unification_database
}
}