diff options
Diffstat (limited to 'vendor/chalk-engine/src/slg')
-rw-r--r-- | vendor/chalk-engine/src/slg/aggregate.rs | 642 | ||||
-rw-r--r-- | vendor/chalk-engine/src/slg/resolvent.rs | 731 |
2 files changed, 1373 insertions, 0 deletions
diff --git a/vendor/chalk-engine/src/slg/aggregate.rs b/vendor/chalk-engine/src/slg/aggregate.rs new file mode 100644 index 000000000..cce4e14dd --- /dev/null +++ b/vendor/chalk-engine/src/slg/aggregate.rs @@ -0,0 +1,642 @@ +use crate::context::{self, AnswerResult}; +use crate::slg::SlgContextOps; +use crate::slg::SubstitutionExt; +use crate::CompleteAnswer; +use chalk_ir::cast::Cast; +use chalk_ir::interner::Interner; +use chalk_ir::*; +use chalk_solve::ext::*; +use chalk_solve::infer::InferenceTable; +use chalk_solve::solve::{Guidance, Solution}; + +use std::fmt::Debug; + +/// Methods for combining solutions to yield an aggregate solution. +pub trait AggregateOps<I: Interner> { + fn make_solution( + &self, + root_goal: &UCanonical<InEnvironment<Goal<I>>>, + answers: impl context::AnswerStream<I>, + should_continue: impl std::ops::Fn() -> bool, + ) -> Option<Solution<I>>; +} + +/// Draws as many answers as it needs from `answers` (but +/// no more!) in order to come up with a solution. +impl<I: Interner> AggregateOps<I> for SlgContextOps<'_, I> { + fn make_solution( + &self, + root_goal: &UCanonical<InEnvironment<Goal<I>>>, + mut answers: impl context::AnswerStream<I>, + should_continue: impl std::ops::Fn() -> bool, + ) -> Option<Solution<I>> { + let interner = self.program.interner(); + let CompleteAnswer { subst, ambiguous } = match answers.next_answer(&should_continue) { + AnswerResult::NoMoreSolutions => { + // No answers at all + return None; + } + AnswerResult::Answer(answer) => answer, + AnswerResult::Floundered => CompleteAnswer { + subst: self.identity_constrained_subst(root_goal), + ambiguous: true, + }, + AnswerResult::QuantumExceeded => { + return Some(Solution::Ambig(Guidance::Unknown)); + } + }; + + // Exactly 1 unconditional answer? + let next_answer = answers.peek_answer(&should_continue); + if next_answer.is_quantum_exceeded() { + if subst.value.subst.is_identity_subst(interner) { + return Some(Solution::Ambig(Guidance::Unknown)); + } else { + return Some(Solution::Ambig(Guidance::Suggested( + subst.map(interner, |cs| cs.subst), + ))); + } + } + if next_answer.is_no_more_solutions() && !ambiguous { + return Some(Solution::Unique(subst)); + } + + // Otherwise, we either have >1 answer, or else we have + // ambiguity. Either way, we are only going to be giving back + // **guidance**, and with guidance, the caller doesn't get + // back any region constraints. So drop them from our `subst` + // variable. + // + // FIXME-- there is actually a 3rd possibility. We could have + // >1 answer where all the answers have the same substitution, + // but different region constraints. We should collapse those + // cases into an `OR` region constraint at some point, but I + // leave that for future work. This is basically + // rust-lang/rust#21974. + let mut subst = subst.map(interner, |cs| cs.subst); + + // Extract answers and merge them into `subst`. Stop once we have + // a trivial subst (or run out of answers). + let mut num_answers = 1; + let guidance = loop { + if subst.value.is_empty(interner) || is_trivial(interner, &subst) { + break Guidance::Unknown; + } + + if !answers + .any_future_answer(|ref mut new_subst| new_subst.may_invalidate(interner, &subst)) + { + break Guidance::Definite(subst); + } + + if let Some(expected_answers) = self.expected_answers { + if num_answers >= expected_answers { + panic!("Too many answers for solution."); + } + } + + let new_subst = match answers.next_answer(&should_continue) { + AnswerResult::Answer(answer1) => answer1.subst, + AnswerResult::Floundered => { + // FIXME: this doesn't trigger for any current tests + self.identity_constrained_subst(root_goal) + } + AnswerResult::NoMoreSolutions => { + break Guidance::Definite(subst); + } + AnswerResult::QuantumExceeded => { + break Guidance::Suggested(subst); + } + }; + subst = merge_into_guidance(interner, &root_goal.canonical, subst, &new_subst); + num_answers += 1; + }; + + if let Some(expected_answers) = self.expected_answers { + assert_eq!( + expected_answers, num_answers, + "Not enough answers for solution." + ); + } + Some(Solution::Ambig(guidance)) + } +} + +/// Given a current substitution used as guidance for `root_goal`, and +/// a new possible answer to `root_goal`, returns a new set of +/// guidance that encompasses both of them. This is often more general +/// than the old guidance. For example, if we had a guidance of `?0 = +/// u32` and the new answer is `?0 = i32`, then the guidance would +/// become `?0 = ?X` (where `?X` is some fresh variable). +fn merge_into_guidance<I: Interner>( + interner: I, + root_goal: &Canonical<InEnvironment<Goal<I>>>, + guidance: Canonical<Substitution<I>>, + answer: &Canonical<ConstrainedSubst<I>>, +) -> Canonical<Substitution<I>> { + let mut infer = InferenceTable::new(); + let Canonical { + value: ConstrainedSubst { + subst: subst1, + constraints: _, + }, + binders: _, + } = answer; + + // Collect the types that the two substitutions have in + // common. + let aggr_generic_args: Vec<_> = guidance + .value + .iter(interner) + .zip(subst1.iter(interner)) + .enumerate() + .map(|(index, (p1, p2))| { + // We have two values for some variable X that + // appears in the root goal. Find out the universe + // of X. + let universe = *root_goal.binders.as_slice(interner)[index].skip_kind(); + + match p1.data(interner) { + GenericArgData::Ty(_) => (), + GenericArgData::Lifetime(_) => { + // Ignore the lifetimes from the substitution: we're just + // creating guidance here anyway. + return infer + .new_variable(universe) + .to_lifetime(interner) + .cast(interner); + } + GenericArgData::Const(_) => (), + }; + + // Combine the two types into a new type. + let mut aggr = AntiUnifier { + infer: &mut infer, + universe, + interner, + }; + aggr.aggregate_generic_args(p1, p2) + }) + .collect(); + + let aggr_subst = Substitution::from_iter(interner, aggr_generic_args); + + infer.canonicalize(interner, aggr_subst).quantified +} + +fn is_trivial<I: Interner>(interner: I, subst: &Canonical<Substitution<I>>) -> bool { + // A subst is trivial if.. + subst + .value + .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)), + + // And no lifetime mappings. (This is too strict, but we never + // product substs with lifetimes.) + GenericArgData::Lifetime(_) => false, + } + }) +} + +/// [Anti-unification] is the act of taking two things that do not +/// unify and finding a minimal generalization of them. So for +/// example `Vec<u32>` anti-unified with `Vec<i32>` might be +/// `Vec<?X>`. This is a **very simplistic** anti-unifier. +/// +/// NOTE: The values here are canonicalized, but output is not, this means +/// that any escaping bound variables that we see have to be replaced with +/// inference variables. +/// +/// [Anti-unification]: https://en.wikipedia.org/wiki/Anti-unification_(computer_science) +struct AntiUnifier<'infer, I: Interner> { + infer: &'infer mut InferenceTable<I>, + universe: UniverseIndex, + interner: I, +} + +impl<I: Interner> AntiUnifier<'_, I> { + fn aggregate_tys(&mut self, ty0: &Ty<I>, ty1: &Ty<I>) -> Ty<I> { + let interner = self.interner; + match (ty0.kind(interner), ty1.kind(interner)) { + // If we see bound things on either side, just drop in a + // fresh variable. This means we will sometimes + // overgeneralize. So for example if we have two + // solutions that are both `(X, X)`, we just produce `(Y, + // Z)` in all cases. + (TyKind::InferenceVar(_, _), TyKind::InferenceVar(_, _)) => self.new_ty_variable(), + + // Ugh. Aggregating two types like `for<'a> fn(&'a u32, + // &'a u32)` and `for<'a, 'b> fn(&'a u32, &'b u32)` seems + // kinda hard. Don't try to be smart for now, just plop a + // variable in there and be done with it. + // This also ensures that any bound variables we do see + // were bound by `Canonical`. + (TyKind::BoundVar(_), TyKind::BoundVar(_)) + | (TyKind::Function(_), TyKind::Function(_)) + | (TyKind::Dyn(_), TyKind::Dyn(_)) => self.new_ty_variable(), + + ( + TyKind::Alias(AliasTy::Projection(proj1)), + TyKind::Alias(AliasTy::Projection(proj2)), + ) => self.aggregate_projection_tys(proj1, proj2), + + ( + TyKind::Alias(AliasTy::Opaque(opaque_ty1)), + TyKind::Alias(AliasTy::Opaque(opaque_ty2)), + ) => self.aggregate_opaque_ty_tys(opaque_ty1, opaque_ty2), + + (TyKind::Placeholder(placeholder1), TyKind::Placeholder(placeholder2)) => { + self.aggregate_placeholder_tys(placeholder1, placeholder2) + } + + (TyKind::Adt(id_a, substitution_a), TyKind::Adt(id_b, substitution_b)) => self + .aggregate_name_and_substs(id_a, substitution_a, id_b, substitution_b) + .map(|(&name, substitution)| TyKind::Adt(name, substitution).intern(interner)) + .unwrap_or_else(|| self.new_ty_variable()), + ( + TyKind::AssociatedType(id_a, substitution_a), + TyKind::AssociatedType(id_b, substitution_b), + ) => self + .aggregate_name_and_substs(id_a, substitution_a, id_b, substitution_b) + .map(|(&name, substitution)| { + TyKind::AssociatedType(name, substitution).intern(interner) + }) + .unwrap_or_else(|| self.new_ty_variable()), + (TyKind::Scalar(scalar_a), TyKind::Scalar(scalar_b)) => { + if scalar_a == scalar_b { + TyKind::Scalar(*scalar_a).intern(interner) + } else { + self.new_ty_variable() + } + } + (TyKind::Str, TyKind::Str) => TyKind::Str.intern(interner), + (TyKind::Tuple(arity_a, substitution_a), TyKind::Tuple(arity_b, substitution_b)) => { + self.aggregate_name_and_substs(arity_a, substitution_a, arity_b, substitution_b) + .map(|(&name, substitution)| TyKind::Tuple(name, substitution).intern(interner)) + .unwrap_or_else(|| self.new_ty_variable()) + } + ( + TyKind::OpaqueType(id_a, substitution_a), + TyKind::OpaqueType(id_b, substitution_b), + ) => self + .aggregate_name_and_substs(id_a, substitution_a, id_b, substitution_b) + .map(|(&name, substitution)| { + TyKind::OpaqueType(name, substitution).intern(interner) + }) + .unwrap_or_else(|| self.new_ty_variable()), + (TyKind::Slice(ty_a), TyKind::Slice(ty_b)) => { + TyKind::Slice(self.aggregate_tys(ty_a, ty_b)).intern(interner) + } + (TyKind::FnDef(id_a, substitution_a), TyKind::FnDef(id_b, substitution_b)) => self + .aggregate_name_and_substs(id_a, substitution_a, id_b, substitution_b) + .map(|(&name, substitution)| TyKind::FnDef(name, substitution).intern(interner)) + .unwrap_or_else(|| self.new_ty_variable()), + (TyKind::Ref(id_a, lifetime_a, ty_a), TyKind::Ref(id_b, lifetime_b, ty_b)) => { + if id_a == id_b { + TyKind::Ref( + *id_a, + self.aggregate_lifetimes(lifetime_a, lifetime_b), + self.aggregate_tys(ty_a, ty_b), + ) + .intern(interner) + } else { + self.new_ty_variable() + } + } + (TyKind::Raw(id_a, ty_a), TyKind::Raw(id_b, ty_b)) => { + if id_a == id_b { + TyKind::Raw(*id_a, self.aggregate_tys(ty_a, ty_b)).intern(interner) + } else { + self.new_ty_variable() + } + } + (TyKind::Never, TyKind::Never) => TyKind::Never.intern(interner), + (TyKind::Array(ty_a, const_a), TyKind::Array(ty_b, const_b)) => TyKind::Array( + self.aggregate_tys(ty_a, ty_b), + self.aggregate_consts(const_a, const_b), + ) + .intern(interner), + (TyKind::Closure(id_a, substitution_a), TyKind::Closure(id_b, substitution_b)) => self + .aggregate_name_and_substs(id_a, substitution_a, id_b, substitution_b) + .map(|(&name, substitution)| TyKind::Closure(name, substitution).intern(interner)) + .unwrap_or_else(|| self.new_ty_variable()), + (TyKind::Generator(id_a, substitution_a), TyKind::Generator(id_b, substitution_b)) => { + self.aggregate_name_and_substs(id_a, substitution_a, id_b, substitution_b) + .map(|(&name, substitution)| { + TyKind::Generator(name, substitution).intern(interner) + }) + .unwrap_or_else(|| self.new_ty_variable()) + } + ( + TyKind::GeneratorWitness(id_a, substitution_a), + TyKind::GeneratorWitness(id_b, substitution_b), + ) => self + .aggregate_name_and_substs(id_a, substitution_a, id_b, substitution_b) + .map(|(&name, substitution)| { + TyKind::GeneratorWitness(name, substitution).intern(interner) + }) + .unwrap_or_else(|| self.new_ty_variable()), + (TyKind::Foreign(id_a), TyKind::Foreign(id_b)) => { + if id_a == id_b { + TyKind::Foreign(*id_a).intern(interner) + } else { + self.new_ty_variable() + } + } + (TyKind::Error, TyKind::Error) => TyKind::Error.intern(interner), + + (_, _) => self.new_ty_variable(), + } + } + + fn aggregate_placeholder_tys( + &mut self, + index1: &PlaceholderIndex, + index2: &PlaceholderIndex, + ) -> Ty<I> { + let interner = self.interner; + if index1 != index2 { + self.new_ty_variable() + } else { + TyKind::Placeholder(*index1).intern(interner) + } + } + + fn aggregate_projection_tys( + &mut self, + proj1: &ProjectionTy<I>, + proj2: &ProjectionTy<I>, + ) -> Ty<I> { + let interner = self.interner; + let ProjectionTy { + associated_ty_id: name1, + substitution: substitution1, + } = proj1; + let ProjectionTy { + associated_ty_id: name2, + substitution: substitution2, + } = proj2; + + self.aggregate_name_and_substs(name1, substitution1, name2, substitution2) + .map(|(&associated_ty_id, substitution)| { + TyKind::Alias(AliasTy::Projection(ProjectionTy { + associated_ty_id, + substitution, + })) + .intern(interner) + }) + .unwrap_or_else(|| self.new_ty_variable()) + } + + fn aggregate_opaque_ty_tys( + &mut self, + opaque_ty1: &OpaqueTy<I>, + opaque_ty2: &OpaqueTy<I>, + ) -> Ty<I> { + let OpaqueTy { + opaque_ty_id: name1, + substitution: substitution1, + } = opaque_ty1; + let OpaqueTy { + opaque_ty_id: name2, + substitution: substitution2, + } = opaque_ty2; + + self.aggregate_name_and_substs(name1, substitution1, name2, substitution2) + .map(|(&opaque_ty_id, substitution)| { + TyKind::Alias(AliasTy::Opaque(OpaqueTy { + opaque_ty_id, + substitution, + })) + .intern(self.interner) + }) + .unwrap_or_else(|| self.new_ty_variable()) + } + + fn aggregate_name_and_substs<N>( + &mut self, + name1: N, + substitution1: &Substitution<I>, + name2: N, + substitution2: &Substitution<I>, + ) -> Option<(N, Substitution<I>)> + where + N: Copy + Eq + Debug, + { + let interner = self.interner; + if name1 != name2 { + return None; + } + + let name = name1; + + assert_eq!( + substitution1.len(interner), + substitution2.len(interner), + "does {:?} take {} substitution or {}? can't both be right", + name, + substitution1.len(interner), + substitution2.len(interner) + ); + + let substitution = Substitution::from_iter( + interner, + substitution1 + .iter(interner) + .zip(substitution2.iter(interner)) + .map(|(p1, p2)| self.aggregate_generic_args(p1, p2)), + ); + + Some((name, substitution)) + } + + fn aggregate_generic_args(&mut self, p1: &GenericArg<I>, p2: &GenericArg<I>) -> GenericArg<I> { + let interner = self.interner; + match (p1.data(interner), p2.data(interner)) { + (GenericArgData::Ty(ty1), GenericArgData::Ty(ty2)) => { + self.aggregate_tys(ty1, ty2).cast(interner) + } + (GenericArgData::Lifetime(l1), GenericArgData::Lifetime(l2)) => { + self.aggregate_lifetimes(l1, l2).cast(interner) + } + (GenericArgData::Const(c1), GenericArgData::Const(c2)) => { + self.aggregate_consts(c1, c2).cast(interner) + } + (GenericArgData::Ty(_), _) + | (GenericArgData::Lifetime(_), _) + | (GenericArgData::Const(_), _) => { + panic!("mismatched parameter kinds: p1={:?} p2={:?}", p1, p2) + } + } + } + + fn aggregate_lifetimes(&mut self, l1: &Lifetime<I>, l2: &Lifetime<I>) -> Lifetime<I> { + let interner = self.interner; + match (l1.data(interner), l2.data(interner)) { + (LifetimeData::Phantom(void, ..), _) | (_, LifetimeData::Phantom(void, ..)) => { + match *void {} + } + (LifetimeData::BoundVar(..), _) | (_, LifetimeData::BoundVar(..)) => { + self.new_lifetime_variable() + } + _ => { + if l1 == l2 { + l1.clone() + } else { + self.new_lifetime_variable() + } + } + } + } + + fn aggregate_consts(&mut self, c1: &Const<I>, c2: &Const<I>) -> Const<I> { + let interner = self.interner; + + // It would be nice to check that c1 and c2 have the same type, even though + // on this stage of solving they should already have the same type. + + let ConstData { + ty: c1_ty, + value: c1_value, + } = c1.data(interner); + let ConstData { + ty: _c2_ty, + value: c2_value, + } = c2.data(interner); + + let ty = c1_ty.clone(); + + match (c1_value, c2_value) { + (ConstValue::InferenceVar(_), _) | (_, ConstValue::InferenceVar(_)) => { + self.new_const_variable(ty) + } + + (ConstValue::BoundVar(_), _) | (_, ConstValue::BoundVar(_)) => { + self.new_const_variable(ty) + } + + (ConstValue::Placeholder(_), ConstValue::Placeholder(_)) => { + if c1 == c2 { + c1.clone() + } else { + self.new_const_variable(ty) + } + } + (ConstValue::Concrete(e1), ConstValue::Concrete(e2)) => { + if e1.const_eq(&ty, e2, interner) { + c1.clone() + } else { + self.new_const_variable(ty) + } + } + + (ConstValue::Placeholder(_), _) | (_, ConstValue::Placeholder(_)) => { + self.new_const_variable(ty) + } + } + } + + fn new_ty_variable(&mut self) -> Ty<I> { + let interner = self.interner; + self.infer.new_variable(self.universe).to_ty(interner) + } + + fn new_lifetime_variable(&mut self) -> Lifetime<I> { + let interner = self.interner; + self.infer.new_variable(self.universe).to_lifetime(interner) + } + + fn new_const_variable(&mut self, ty: Ty<I>) -> Const<I> { + let interner = self.interner; + self.infer + .new_variable(self.universe) + .to_const(interner, ty) + } +} + +#[cfg(test)] +mod test { + use crate::slg::aggregate::AntiUnifier; + use chalk_integration::{arg, ty}; + use chalk_ir::UniverseIndex; + use chalk_solve::infer::InferenceTable; + + /// Test the equivalent of `Vec<i32>` vs `Vec<u32>` + #[test] + fn vec_i32_vs_vec_u32() { + use chalk_integration::interner::ChalkIr; + let mut infer: InferenceTable<ChalkIr> = InferenceTable::new(); + let mut anti_unifier = AntiUnifier { + infer: &mut infer, + universe: UniverseIndex::root(), + interner: ChalkIr, + }; + + let ty = anti_unifier.aggregate_tys( + &ty!(apply (item 0) (apply (item 1))), + &ty!(apply (item 0) (apply (item 2))), + ); + assert_eq!(ty!(apply (item 0) (infer 0)), ty); + } + + /// Test the equivalent of `Vec<i32>` vs `Vec<i32>` + #[test] + fn vec_i32_vs_vec_i32() { + use chalk_integration::interner::ChalkIr; + let interner = ChalkIr; + let mut infer: InferenceTable<ChalkIr> = InferenceTable::new(); + let mut anti_unifier = AntiUnifier { + interner, + infer: &mut infer, + universe: UniverseIndex::root(), + }; + + let ty = anti_unifier.aggregate_tys( + &ty!(apply (item 0) (apply (item 1))), + &ty!(apply (item 0) (apply (item 1))), + ); + assert_eq!(ty!(apply (item 0) (apply (item 1))), ty); + } + + /// Test the equivalent of `Vec<X>` vs `Vec<Y>` + #[test] + fn vec_x_vs_vec_y() { + use chalk_integration::interner::ChalkIr; + let interner = ChalkIr; + let mut infer: InferenceTable<ChalkIr> = InferenceTable::new(); + let mut anti_unifier = AntiUnifier { + interner, + infer: &mut infer, + universe: UniverseIndex::root(), + }; + + // Note that the `var 0` and `var 1` in these types would be + // referring to canonicalized free variables, not variables in + // `infer`. + let ty = anti_unifier.aggregate_tys( + &ty!(apply (item 0) (infer 0)), + &ty!(apply (item 0) (infer 1)), + ); + + // But this `var 0` is from `infer. + assert_eq!(ty!(apply (item 0) (infer 0)), ty); + } +} diff --git a/vendor/chalk-engine/src/slg/resolvent.rs b/vendor/chalk-engine/src/slg/resolvent.rs new file mode 100644 index 000000000..c6d0f8d5c --- /dev/null +++ b/vendor/chalk-engine/src/slg/resolvent.rs @@ -0,0 +1,731 @@ +use crate::normalize_deep::DeepNormalizer; +use crate::slg::ResolventOps; +use crate::{ExClause, Literal, TimeStamp}; +use chalk_ir::cast::Caster; +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}; + +/////////////////////////////////////////////////////////////////////////// +// SLG RESOLVENTS +// +// The "SLG Resolvent" is used to combine a *goal* G with some +// clause or answer *C*. It unifies the goal's selected literal +// with the clause and then inserts the clause's conditions into +// the goal's list of things to prove, basically. Although this is +// one operation in EWFS, we have specialized variants for merging +// a program clause and an answer (though they share some code in +// common). +// +// Terminology note: The NFTD and RR papers use the term +// "resolvent" to mean both the factor and the resolvent, but EWFS +// distinguishes the two. We follow EWFS here since -- in the code +// -- we tend to know whether there are delayed literals or not, +// and hence to know which code path we actually want. +// +// From EWFS: +// +// Let G be an X-clause A :- D | L1,...Ln, where N > 0, and Li be selected atom. +// +// Let C be an X-clause with no delayed literals. Let +// +// C' = A' :- L'1...L'm +// +// be a variant of C such that G and C' have no variables in +// common. +// +// Let Li and A' be unified with MGU S. +// +// Then: +// +// S(A :- D | L1...Li-1, L1'...L'm, Li+1...Ln) +// +// is the SLG resolvent of G with C. + +impl<I: Interner> ResolventOps<I> for InferenceTable<I> { + /// Applies the SLG resolvent algorithm to incorporate a program + /// clause into the main X-clause, producing a new X-clause that + /// must be solved. + /// + /// # Parameters + /// + /// - `goal` is the goal G that we are trying to solve + /// - `clause` is the program clause that may be useful to that end + #[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>> { + // Relating the above description to our situation: + // + // - `goal` G, except with binders for any existential variables. + // - Also, we always select the first literal in `ex_clause.literals`, so `i` is 0. + // - `clause` is C, except with binders for any existential variables. + + // C' in the description above is `consequence :- conditions`. + // + // Note that G and C' have no variables in common. + let ProgramClauseImplication { + consequence, + conditions, + constraints, + priority: _, + } = { + let ProgramClauseData(implication) = clause.data(interner); + + self.instantiate_binders_existentially(interner, implication.clone()) + }; + debug!(?consequence, ?conditions, ?constraints); + + // Unify the selected literal Li with C'. + let unification_result = self.relate( + interner, + db, + environment, + Variance::Invariant, + goal, + &consequence, + )?; + + // Final X-clause that we will return. + let mut ex_clause = ExClause { + subst: subst.clone(), + ambiguous: false, + constraints: vec![], + subgoals: vec![], + delayed_subgoals: vec![], + answer_time: TimeStamp::default(), + floundered_subgoals: vec![], + }; + + // Add the subgoals/region-constraints that unification gave us. + ex_clause.subgoals.extend( + unification_result + .goals + .into_iter() + .casted(interner) + .map(Literal::Positive), + ); + + ex_clause + .constraints + .extend(constraints.as_slice(interner).to_owned()); + + // Add the `conditions` from the program clause into the result too. + 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) + } + + /////////////////////////////////////////////////////////////////////////// + // apply_answer_subst + // + // Apply answer subst has the job of "plugging in" the answer to a + // query into the pending ex-clause. To see how it works, it's worth stepping + // up one level. Imagine that first we are trying to prove a goal A: + // + // A :- T: Foo<Vec<?U>>, ?U: Bar + // + // this spawns a subgoal `T: Foo<Vec<?0>>`, and it's this subgoal that + // has now produced an answer `?0 = u32`. When the goal A spawned the + // subgoal, it will also have registered a `PendingExClause` with its + // current state. At the point where *this* method has been invoked, + // that pending ex-clause has been instantiated with fresh variables and setup, + // so we have four bits of incoming information: + // + // - `ex_clause`, which is the remaining stuff to prove for the goal A. + // Here, the inference variable `?U` has been instantiated with a fresh variable + // `?X`. + // - `A :- ?X: Bar` + // - `selected_goal`, which is the thing we were trying to prove when we + // spawned the subgoal. It shares inference variables with `ex_clause`. + // - `T: Foo<Vec<?X>>` + // - `answer_table_goal`, which is the subgoal in canonical form: + // - `for<type> T: Foo<Vec<?0>>` + // - `canonical_answer_subst`, which is an answer to `answer_table_goal`. + // - `[?0 = u32]` + // + // In this case, this function will (a) unify `u32` and `?X` and then + // (b) return `ex_clause` (extended possibly with new region constraints + // and subgoals). + // + // One way to do this would be to (a) substitute + // `canonical_answer_subst` into `answer_table_goal` (yielding `T: + // Foo<Vec<u32>>`) and then (b) instantiate the result with fresh + // variables (no effect in this instance) and then (c) unify that with + // `selected_goal` (yielding, indirectly, that `?X = u32`). But that + // is not what we do: it's inefficient, to start, but it also causes + // problems because unification of projections can make new + // sub-goals. That is, even if the answers don't involve any + // projections, the table goals might, and this can create an infinite + // loop (see also #74). + // + // What we do instead is to (a) instantiate the substitution, which + // may have free variables in it (in this case, it would not, and the + // instantiation would have no effect) and then (b) zip + // `answer_table_goal` and `selected_goal` without having done any + // substitution. After all, these ought to be basically the same, + // since `answer_table_goal` was created by canonicalizing (and + // possibly truncating, but we'll get to that later) + // `selected_goal`. Then, whenever we reach a "free variable" in + // `answer_table_goal`, say `?0`, we go to the instantiated answer + // substitution and lookup the result (in this case, `u32`). We take + // that result and unify it with whatever we find in `selected_goal` + // (in this case, `?X`). + // + // Let's cover then some corner cases. First off, what is this + // business of instantiating the answer? Well, the answer may not be a + // simple type like `u32`, it could be a "family" of types, like + // `for<type> Vec<?0>` -- i.e., `Vec<X>: Bar` for *any* `X`. In that + // case, the instantiation would produce a substitution `[?0 := + // Vec<?Y>]` (note that the key is not affected, just the value). So + // when we do the unification, instead of unifying `?X = u32`, we + // would unify `?X = Vec<?Y>`. + // + // Next, truncation. One key thing is that the `answer_table_goal` may + // not be *exactly* the same as the `selected_goal` -- we will + // truncate it if it gets too deep. so, in our example, it may be that + // instead of `answer_table_goal` being `for<type> T: Foo<Vec<?0>>`, + // it could have been truncated to `for<type> T: Foo<?0>` (which is a + // more general goal). In that case, let's say that the answer is + // still `[?0 = u32]`, meaning that `T: Foo<u32>` is true (which isn't + // actually interesting to our original goal). When we do the zip + // then, we will encounter `?0` in the `answer_table_goal` and pair + // that with `Vec<?X>` from the pending goal. We will attempt to unify + // `Vec<?X>` with `u32` (from the substitution), which will fail. That + // failure will get propagated back up. + + #[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(self, interner, selected_goal.clone())); + + // C' is now `answer`. No variables in common with G. + let AnswerSubst { + subst: answer_subst, + + // Assuming unification succeeds, we incorporate the + // region constraints from the answer into the result; + // we'll need them if this answer (which is not yet known + // to be true) winds up being true, and otherwise (if the + // answer is false or unknown) it doesn't matter. + constraints: answer_constraints, + + delayed_subgoals, + } = self.instantiate_canonical(interner, canonical_answer_subst); + + AnswerSubstitutor::substitute( + interner, + unification_database, + self, + &selected_goal.environment, + &answer_subst, + ex_clause, + &answer_table_goal.value, + selected_goal, + )?; + ex_clause + .constraints + .extend(answer_constraints.as_slice(interner).to_vec()); + // at that point we should only have goals that stemmed + // from non trivial self cycles + 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>, + + /// Tracks the debrujn index of the first binder that is outside + /// the term we are traversing. This starts as `DebruijnIndex::INNERMOST`, + /// since we have not yet traversed *any* binders, but when we visit + /// the inside of a binder, it would be incremented. + /// + /// Example: If we are visiting `(for<type> A, B, C, for<type> for<type> D)`, + /// then this would be: + /// + /// * `1`, when visiting `A`, + /// * `0`, when visiting `B` and `C`, + /// * `2`, when visiting `D`. + outer_binder: DebruijnIndex, + + ex_clause: &'t mut ExClause<I>, + interner: 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, + + // This variable is bound in the answer, not free, so it + // doesn't represent a reference into the answer substitution. + 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) + .expect("truncate extracted a pending value that references internal binder"); + + let result = self.table.relate( + interner, + db, + self.environment, + variance, + answer_param, + &GenericArg::new(interner, pending_shifted), + )?; + + self.ex_clause.subgoals.extend( + result + .goals + .into_iter() + .casted(interner) + .map(Literal::Positive), + ); + + Ok(true) + } + + /// When we encounter a variable in the answer goal, we first try + /// `unify_free_answer_var`. Assuming that this fails, the + /// variable must be a bound variable in the answer goal -- in + /// that case, there should be a corresponding bound variable in + /// the pending goal. This bit of code just checks that latter + /// case. + 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; + + // Both bound variables are bound within the term we are matching + assert!(answer_depth.within(self.outer_binder)); + + // They are bound at the same (relative) depth + assert_eq!(answer_depth, pending_depth); + + // They are bound at the same index within the binder + assert_eq!(answer_index, pending_index,); + + Ok(()) + } +} + +impl<'i, I: Interner> Zipper<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 the answer has a variable here, then this is one of the + // "inputs" to the subgoal table. We need to extract the + // resulting answer that the subgoal found and unify it with + // the value from our "pending subgoal". + 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(()); + } + } + + // Otherwise, the answer and the selected subgoal ought to be a perfect match for + // one another. + 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); + } + // The lifetime is `Contravariant` + Zip::zip_with( + self, + variance.xform(Variance::Contravariant), + lifetime_a, + lifetime_b, + )?; + // The type is `Covariant` when not mut, `Invariant` otherwise + 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 { + self.interner + } + + fn unification_database(&self) -> &dyn UnificationDatabase<I> { + self.unification_database + } +} |