From 698f8c2f01ea549d77d7dc3338a12e04c11057b9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:02:58 +0200 Subject: Adding upstream version 1.64.0+dfsg1. Signed-off-by: Daniel Baumann --- vendor/chalk-engine/src/slg/resolvent.rs | 731 +++++++++++++++++++++++++++++++ 1 file changed, 731 insertions(+) create mode 100644 vendor/chalk-engine/src/slg/resolvent.rs (limited to 'vendor/chalk-engine/src/slg/resolvent.rs') 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 ResolventOps for InferenceTable { + /// 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, + interner: I, + environment: &Environment, + goal: &DomainGoal, + subst: &Substitution, + clause: &ProgramClause, + ) -> Fallible> { + // 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>, ?U: Bar + // + // this spawns a subgoal `T: Foo>`, 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>` + // - `answer_table_goal`, which is the subgoal in canonical form: + // - `for T: Foo>` + // - `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>`) 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 Vec` -- i.e., `Vec: Bar` for *any* `X`. In that + // case, the instantiation would produce a substitution `[?0 := + // Vec]` (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`. + // + // 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 T: Foo>`, + // it could have been truncated to `for T: Foo` (which is a + // more general goal). In that case, let's say that the answer is + // still `[?0 = u32]`, meaning that `T: Foo` 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` from the pending goal. We will attempt to unify + // `Vec` 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, + ex_clause: &mut ExClause, + selected_goal: &InEnvironment>, + answer_table_goal: &Canonical>>, + canonical_answer_subst: Canonical>, + ) -> 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, + environment: &'t Environment, + answer_subst: &'t Substitution, + + /// 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 A, B, C, for for 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, + interner: I, + unification_database: &'t dyn UnificationDatabase, +} + +impl AnswerSubstitutor<'_, I> { + fn substitute>( + interner: I, + unification_database: &dyn UnificationDatabase, + table: &mut InferenceTable, + environment: &Environment, + answer_subst: &Substitution, + ex_clause: &mut ExClause, + 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, + variance: Variance, + answer_var: BoundVar, + pending: GenericArgData, + ) -> Fallible { + 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 for AnswerSubstitutor<'i, I> { + fn zip_tys(&mut self, variance: Variance, answer: &Ty, pending: &Ty) -> 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, + pending: &Lifetime, + ) -> 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, + pending: &Const, + ) -> 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( + &mut self, + variance: Variance, + answer: &Binders, + pending: &Binders, + ) -> Fallible<()> + where + T: HasInterner + Zip + Fold, + { + 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 { + self.unification_database + } +} -- cgit v1.2.3