From b930b3155ec4f6bdbcc5f193c89aa8fdcc23c1d5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 26 Jan 2020 13:13:39 +0100 Subject: [PATCH 01/10] introduce `Goals` type to replace `Vec>` --- chalk-integration/src/lowering.rs | 17 +++----- chalk-ir/src/cast.rs | 4 +- chalk-ir/src/debug.rs | 34 ++++++++------- chalk-ir/src/fold/boring_impls.rs | 13 ++++++ chalk-ir/src/lib.rs | 57 +++++++++++++++++++++++--- chalk-ir/src/zip.rs | 7 ++++ chalk-solve/src/clauses/builder.rs | 4 +- chalk-solve/src/solve/slg.rs | 2 +- chalk-solve/src/solve/slg/resolvent.rs | 8 ++-- 9 files changed, 106 insertions(+), 40 deletions(-) diff --git a/chalk-integration/src/lowering.rs b/chalk-integration/src/lowering.rs index 507d3d66f0d..f8ac9b6857b 100644 --- a/chalk-integration/src/lowering.rs +++ b/chalk-integration/src/lowering.rs @@ -1134,12 +1134,9 @@ impl LowerClause for Clause { let implications = env.in_binders(self.all_parameters(), |env| { let consequences: Vec> = self.consequence.lower(env)?; - let conditions: Vec> = self - .conditions - .iter() - .map(|g| g.lower(env)) - .rev() // (*) - .collect::>()?; + let conditions = chalk_ir::Goals::from_fallible( + self.conditions.iter().map(|g| g.lower(env)).rev(), // (*) + )?; // (*) Subtle: in the SLG solver, we pop conditions from R to // L. To preserve the expected order (L to R), we must @@ -1270,11 +1267,9 @@ impl<'k> LowerGoal> for Goal { Ok(chalk_ir::GoalData::Implies(where_clauses?, g.lower(env)?).intern()) } Goal::And(g1, g2s) => { - let mut goals = vec![]; - goals.push(g1.lower(env)?); - for g2 in g2s { - goals.push(g2.lower(env)?); - } + let goals = chalk_ir::Goals::from_fallible( + Some(g1).into_iter().chain(g2s).map(|g| g.lower(env)), + )?; Ok(chalk_ir::GoalData::All(goals).intern()) } Goal::Not(g) => Ok(chalk_ir::GoalData::Not(g.lower(env)?).intern()), diff --git a/chalk-ir/src/cast.rs b/chalk-ir/src/cast.rs index 9d6fbdebe11..9c5a2da1b9d 100644 --- a/chalk-ir/src/cast.rs +++ b/chalk-ir/src/cast.rs @@ -183,7 +183,7 @@ where fn cast_to(self) -> ProgramClause { ProgramClause::Implies(ProgramClauseImplication { consequence: self.cast(), - conditions: vec![], + conditions: Goals::new(), }) } } @@ -199,7 +199,7 @@ where } else { ProgramClause::ForAll(self.map(|bound| ProgramClauseImplication { consequence: bound.cast(), - conditions: vec![], + conditions: Goals::new(), })) } } diff --git a/chalk-ir/src/debug.rs b/chalk-ir/src/debug.rs index 19baa615538..e4a5cc93f51 100644 --- a/chalk-ir/src/debug.rs +++ b/chalk-ir/src/debug.rs @@ -279,17 +279,7 @@ impl Debug for Goal { write!(fmt, "> {{ {:?} }}", subgoal.value) } GoalData::Implies(ref wc, ref g) => write!(fmt, "if ({:?}) {{ {:?} }}", wc, g), - GoalData::All(ref goals) => { - write!(fmt, "all(")?; - for (goal, index) in goals.iter().zip(0..) { - if index > 0 { - write!(fmt, ", ")?; - } - write!(fmt, "{:?}", goal)?; - } - write!(fmt, ")")?; - Ok(()) - } + GoalData::All(ref goals) => write!(fmt, "all{:?}", goals), GoalData::Not(ref g) => write!(fmt, "not {{ {:?} }}", g), GoalData::EqGoal(ref wc) => write!(fmt, "{:?}", wc), GoalData::DomainGoal(ref wc) => write!(fmt, "{:?}", wc), @@ -298,6 +288,20 @@ impl Debug for Goal { } } +impl Debug for Goals { + fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { + write!(fmt, "(")?; + for (goal, index) in self.iter().zip(0..) { + if index > 0 { + write!(fmt, ", ")?; + } + write!(fmt, "{:?}", goal)?; + } + write!(fmt, ")")?; + Ok(()) + } +} + impl Debug for Binders { fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { let Binders { @@ -334,16 +338,18 @@ impl Debug for ProgramClauseImplication { fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { write!(fmt, "{:?}", self.consequence)?; - let conds = self.conditions.len(); + let conditions = self.conditions.as_slice(); + + let conds = conditions.len(); if conds == 0 { return Ok(()); } write!(fmt, " :- ")?; - for cond in &self.conditions[..conds - 1] { + for cond in &conditions[..conds - 1] { write!(fmt, "{:?}, ", cond)?; } - write!(fmt, "{:?}", self.conditions[conds - 1]) + write!(fmt, "{:?}", conditions[conds - 1]) } } diff --git a/chalk-ir/src/fold/boring_impls.rs b/chalk-ir/src/fold/boring_impls.rs index 43c5c30280a..f3b58be8687 100644 --- a/chalk-ir/src/fold/boring_impls.rs +++ b/chalk-ir/src/fold/boring_impls.rs @@ -124,6 +124,19 @@ impl> Fold for Substitution> Fold for Goals { + type Result = Goals; + fn fold_with( + &self, + folder: &mut dyn Folder, + binders: usize, + ) -> Fallible { + Ok(Goals::from_fallible( + self.iter().map(|p| p.fold_with(folder, binders)), + )?) + } +} + #[macro_export] macro_rules! copy_fold { ($t:ty) => { diff --git a/chalk-ir/src/lib.rs b/chalk-ir/src/lib.rs index fd4b7f938f4..e94c6cd0055 100644 --- a/chalk-ir/src/lib.rs +++ b/chalk-ir/src/lib.rs @@ -952,7 +952,7 @@ impl Iterator for BindersIntoIterator { #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Fold, HasTypeFamily)] pub struct ProgramClauseImplication { pub consequence: DomainGoal, - pub conditions: Vec>, + pub conditions: Goals, } #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Fold)] @@ -968,7 +968,7 @@ impl ProgramClause { if implication.conditions.is_empty() { ProgramClause::Implies(ProgramClauseImplication { consequence: implication.consequence.into_from_env_goal(), - conditions: vec![], + conditions: Goals::new(), }) } else { ProgramClause::Implies(implication) @@ -1013,6 +1013,52 @@ impl UCanonical { } } +#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, HasTypeFamily)] +/// A general goal; this is the full range of questions you can pose to Chalk. +pub struct Goals { + goals: Vec>, +} + +impl Goals { + pub fn from(goals: impl IntoIterator>>) -> Self { + use crate::cast::Caster; + Goals { + goals: goals.into_iter().casted().collect(), + } + } + + pub fn new() -> Self { + Self::from(None::>) + } + + pub fn from_fallible( + goals: impl IntoIterator>, E>>, + ) -> Result { + use crate::cast::Caster; + let goals = goals + .into_iter() + .casted() + .collect::>, _>>()?; + Ok(Goals::from(goals)) + } + + pub fn iter(&self) -> std::slice::Iter<'_, Goal> { + self.as_slice().iter() + } + + pub fn is_empty(&self) -> bool { + self.as_slice().is_empty() + } + + pub fn len(&self) -> usize { + self.as_slice().len() + } + + pub fn as_slice(&self) -> &[Goal] { + &self.goals + } +} + #[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, HasTypeFamily)] /// A general goal; this is the full range of questions you can pose to Chalk. pub struct Goal { @@ -1107,8 +1153,7 @@ where if let Some(goal0) = iter.next() { if let Some(goal1) = iter.next() { // More than one goal to prove - let mut goals = vec![goal0, goal1]; - goals.extend(iter); + let goals = Goals::from(Some(goal0).into_iter().chain(Some(goal1)).chain(iter)); GoalData::All(goals).intern() } else { // One goal to prove @@ -1116,7 +1161,7 @@ where } } else { // No goals to prove, always true - GoalData::All(vec![]).intern() + GoalData::All(Goals::new()).intern() } } } @@ -1128,7 +1173,7 @@ pub enum GoalData { /// (deBruijn index). Quantified(QuantifierKind, Binders>), Implies(Vec>, Goal), - All(Vec>), + All(Goals), Not(Goal), /// Make two things equal; the rules for doing so are well known to the logic diff --git a/chalk-ir/src/zip.rs b/chalk-ir/src/zip.rs index 32d9241b151..9180010d29f 100644 --- a/chalk-ir/src/zip.rs +++ b/chalk-ir/src/zip.rs @@ -212,6 +212,13 @@ impl Zip for Environment { } } +impl Zip for Goals { + fn zip_with>(zipper: &mut Z, a: &Self, b: &Self) -> Fallible<()> { + Zip::zip_with(zipper, a.as_slice(), b.as_slice())?; + Ok(()) + } +} + /// Generates a Zip impl that requires the two enums be the same /// variant, then zips each field of the variant in turn. Only works /// if all variants have a single parenthesized value right now. diff --git a/chalk-solve/src/clauses/builder.rs b/chalk-solve/src/clauses/builder.rs index 4a71aa5c3de..f63f1725f06 100644 --- a/chalk-solve/src/clauses/builder.rs +++ b/chalk-solve/src/clauses/builder.rs @@ -1,4 +1,4 @@ -use crate::cast::{Cast, CastTo, Caster}; +use crate::cast::{Cast, CastTo}; use crate::RustIrDatabase; use chalk_ir::family::{HasTypeFamily, TypeFamily}; use chalk_ir::fold::Fold; @@ -46,7 +46,7 @@ impl<'me, TF: TypeFamily> ClauseBuilder<'me, TF> { ) { let clause = ProgramClauseImplication { consequence: consequence.cast(), - conditions: conditions.into_iter().casted().collect(), + conditions: Goals::from(conditions), }; if self.binders.len() == 0 { diff --git a/chalk-solve/src/solve/slg.rs b/chalk-solve/src/solve/slg.rs index 4d4fb0cb59d..47301d94ef2 100644 --- a/chalk-solve/src/solve/slg.rs +++ b/chalk-solve/src/solve/slg.rs @@ -184,7 +184,7 @@ impl context::Context for SlgContext { HhGoal::Exists(binders_goal) } GoalData::Implies(dg, subgoal) => HhGoal::Implies(dg, subgoal), - GoalData::All(goals) => HhGoal::All(goals), + GoalData::All(goals) => HhGoal::All(goals.iter().cloned().collect()), GoalData::Not(g1) => HhGoal::Not(g1), GoalData::EqGoal(EqGoal { a, b }) => HhGoal::Unify((), a, b), GoalData::DomainGoal(domain_goal) => HhGoal::DomainGoal(domain_goal), diff --git a/chalk-solve/src/solve/slg/resolvent.rs b/chalk-solve/src/solve/slg/resolvent.rs index 2a105ea9971..2fef479731d 100644 --- a/chalk-solve/src/solve/slg/resolvent.rs +++ b/chalk-solve/src/solve/slg/resolvent.rs @@ -111,11 +111,11 @@ impl context::ResolventOps> for TruncatingInferen // Add the `conditions` from the program clause into the result too. ex_clause .subgoals - .extend(conditions.into_iter().map(|c| match c.data() { - GoalData::Not(c) => { - Literal::Negative(InEnvironment::new(environment, Goal::clone(c))) + .extend(conditions.iter().map(|c| match c.data() { + GoalData::Not(c1) => { + Literal::Negative(InEnvironment::new(environment, Goal::clone(c1))) } - _ => Literal::Positive(InEnvironment::new(environment, c)), + _ => Literal::Positive(InEnvironment::new(environment, Goal::clone(c))), })); Ok(ex_clause) From 741aa1a76dbe74d1c2dbc5f71c5f0cc03f792fcd Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 26 Jan 2020 13:52:30 +0100 Subject: [PATCH 02/10] introduce `SuperFold` trait --- chalk-ir/src/fold.rs | 89 +++++++++++++++++++------------ chalk-solve/src/solve/truncate.rs | 7 +-- 2 files changed, 58 insertions(+), 38 deletions(-) diff --git a/chalk-ir/src/fold.rs b/chalk-ir/src/fold.rs index 2db3a6a5aab..f938f637d12 100644 --- a/chalk-ir/src/fold.rs +++ b/chalk-ir/src/fold.rs @@ -95,7 +95,7 @@ where TTF: TargetTypeFamily, { fn fold_ty(&mut self, ty: &Ty, binders: usize) -> Fallible> { - super_fold_ty(self, ty, binders) + ty.super_fold_with(self, binders) } fn fold_lifetime( @@ -103,7 +103,7 @@ where lifetime: &Lifetime, binders: usize, ) -> Fallible> { - super_fold_lifetime(self, lifetime, binders) + lifetime.super_fold_with(self, binders) } } @@ -300,29 +300,42 @@ pub trait Fold = TF>: Debug { -> Fallible; } -pub fn super_fold_ty( - folder: &mut dyn Folder, - ty: &Ty, - binders: usize, -) -> Fallible> +/// For types where "fold" invokes a callback on the `Folder`, the +/// `SuperFold` trait captures the recursive behavior that folds all +/// the contents of the type. +pub trait SuperFold = TF>: Fold { + fn super_fold_with( + &self, + folder: &mut dyn Folder, + binders: usize, + ) -> Fallible; +} + +impl SuperFold for Ty where TF: TypeFamily, TTF: TargetTypeFamily, { - match ty.data() { - TyData::BoundVar(depth) => { - if *depth >= binders { - folder.fold_free_var_ty(*depth - binders, binders) - } else { - Ok(TyData::::BoundVar(*depth).intern()) + fn super_fold_with( + &self, + folder: &mut dyn Folder, + binders: usize, + ) -> Fallible> { + match self.data() { + TyData::BoundVar(depth) => { + if *depth >= binders { + folder.fold_free_var_ty(*depth - binders, binders) + } else { + Ok(TyData::::BoundVar(*depth).intern()) + } } + TyData::Dyn(clauses) => Ok(TyData::Dyn(clauses.fold_with(folder, binders)?).intern()), + TyData::InferenceVar(var) => folder.fold_inference_ty(*var, binders), + TyData::Apply(apply) => Ok(TyData::Apply(apply.fold_with(folder, binders)?).intern()), + TyData::Placeholder(ui) => Ok(folder.fold_free_placeholder_ty(*ui, binders)?), + TyData::Alias(proj) => Ok(TyData::Alias(proj.fold_with(folder, binders)?).intern()), + TyData::Function(fun) => Ok(TyData::Function(fun.fold_with(folder, binders)?).intern()), } - TyData::Dyn(clauses) => Ok(TyData::Dyn(clauses.fold_with(folder, binders)?).intern()), - TyData::InferenceVar(var) => folder.fold_inference_ty(*var, binders), - TyData::Apply(apply) => Ok(TyData::Apply(apply.fold_with(folder, binders)?).intern()), - TyData::Placeholder(ui) => Ok(folder.fold_free_placeholder_ty(*ui, binders)?), - TyData::Alias(alias) => Ok(TyData::Alias(alias.fold_with(folder, binders)?).intern()), - TyData::Function(fun) => Ok(TyData::Function(fun.fold_with(folder, binders)?).intern()), } } @@ -341,24 +354,30 @@ impl> Fold for Ty { } } -pub fn super_fold_lifetime( - folder: &mut dyn Folder, - lifetime: &Lifetime, - binders: usize, -) -> Fallible> { - match lifetime.data() { - LifetimeData::BoundVar(depth) => { - if *depth >= binders { - folder.fold_free_var_lifetime(depth - binders, binders) - } else { - Ok(LifetimeData::::BoundVar(*depth).intern()) +impl SuperFold for Lifetime +where + TF: TypeFamily, + TTF: TargetTypeFamily, +{ + fn super_fold_with( + &self, + folder: &mut dyn Folder, + binders: usize, + ) -> Fallible> { + match self.data() { + LifetimeData::BoundVar(depth) => { + if *depth >= binders { + folder.fold_free_var_lifetime(depth - binders, binders) + } else { + Ok(LifetimeData::::BoundVar(*depth).intern()) + } } + LifetimeData::InferenceVar(var) => folder.fold_inference_lifetime(*var, binders), + LifetimeData::Placeholder(universe) => { + folder.fold_free_placeholder_lifetime(*universe, binders) + } + LifetimeData::Phantom(..) => unreachable!(), } - LifetimeData::InferenceVar(var) => folder.fold_inference_lifetime(*var, binders), - LifetimeData::Placeholder(universe) => { - folder.fold_free_placeholder_lifetime(*universe, binders) - } - LifetimeData::Phantom(..) => unreachable!(), } } diff --git a/chalk-solve/src/solve/truncate.rs b/chalk-solve/src/solve/truncate.rs index 1a067a5b59b..be60b447545 100644 --- a/chalk-solve/src/solve/truncate.rs +++ b/chalk-solve/src/solve/truncate.rs @@ -5,7 +5,8 @@ use chalk_engine::fallible::*; use chalk_ir::family::TypeFamily; use chalk_ir::fold::shift::Shift; use chalk_ir::fold::{ - self, DefaultFreeVarFolder, DefaultInferenceFolder, DefaultPlaceholderFolder, Fold, TypeFolder, + DefaultFreeVarFolder, DefaultInferenceFolder, DefaultPlaceholderFolder, Fold, SuperFold, + TypeFolder, }; use chalk_ir::*; use std::fmt::Debug; @@ -81,7 +82,7 @@ impl TypeFolder for Truncater<'_, TF> { let pre_size = self.current_size; self.current_size += 1; - let result = fold::super_fold_ty(self, ty, binders)?; + let result = ty.super_fold_with(self, binders)?; // We wish to maintain the invariant that: // @@ -109,7 +110,7 @@ impl TypeFolder for Truncater<'_, TF> { } fn fold_lifetime(&mut self, lifetime: &Lifetime, binders: usize) -> Fallible> { - fold::super_fold_lifetime(self, lifetime, binders) + lifetime.super_fold_with(self, binders) } } From 4d5596f2fb4824ec075bd410644f70cbd8832ff4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 26 Jan 2020 13:57:33 +0100 Subject: [PATCH 03/10] introduce `SuperFold` derive --- chalk-derive/src/lib.rs | 134 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/chalk-derive/src/lib.rs b/chalk-derive/src/lib.rs index 919252887c1..81b74378d1c 100644 --- a/chalk-derive/src/lib.rs +++ b/chalk-derive/src/lib.rs @@ -147,6 +147,140 @@ pub fn derive_fold(item: TokenStream) -> TokenStream { panic!("derive(Fold) requires a parameter that implements HasTypeFamily or TypeFamily"); } +/// Derives Fold for structs and enums for which one of the following is true: +/// - It has a `#[has_type_family(TheTypeFamily)]` attribute +/// - There is a single parameter `T: HasTypeFamily` (does not have to be named `T`) +/// - There is a single parameter `TF: TypeFamily` (does not have to be named `TF`) +#[proc_macro_derive(SuperFold, attributes(has_type_family))] +pub fn derive_super_fold(item: TokenStream) -> TokenStream { + let input = parse_macro_input!(item as DeriveInput); + let (impl_generics, ty_generics, where_clause_ref) = input.generics.split_for_impl(); + + let type_name = input.ident; + let body = derive_fold_body(&type_name, input.data); + + if let Some(attr) = input + .attrs + .iter() + .find(|a| a.path.is_ident("has_type_family")) + { + // Hardcoded type-family: + // + // impl SuperFold for Type { + // } + let arg = attr + .parse_args::() + .expect("Expected has_type_family argument"); + + return TokenStream::from(quote! { + impl #impl_generics SuperFold < #arg, #arg > for #type_name #ty_generics #where_clause_ref { + fn super_fold_with( + &self, + folder: &mut dyn Folder < #arg, #arg >, + binders: usize, + ) -> ::chalk_engine::fallible::Fallible { + #body + } + } + }); + } + + match input.generics.params.len() { + 1 => {} + + 0 => { + panic!("Fold derive requires a single type parameter or a `#[has_type_family]` attr"); + } + + _ => { + panic!("Fold derive only works with a single type parameter"); + } + }; + + let generic_param0 = &input.generics.params[0]; + + if let Some(param) = has_type_family(&generic_param0) { + // HasTypeFamily bound: + // + // Example: + // + // impl SuperFold<_TF, _TTF> for Binders + // where + // T: HasTypeFamily, + // T: Fold<_TF, _TTF, Result = _U>, + // U: HasTypeFamily, + // { + // } + + let mut impl_generics = input.generics.clone(); + impl_generics.params.extend(vec![ + GenericParam::Type(syn::parse(quote! { _TF: TypeFamily }.into()).unwrap()), + GenericParam::Type(syn::parse(quote! { _TTF: TargetTypeFamily<_TF> }.into()).unwrap()), + GenericParam::Type( + syn::parse(quote! { _U: HasTypeFamily }.into()).unwrap(), + ), + ]); + + let mut where_clause = where_clause_ref + .cloned() + .unwrap_or_else(|| syn::parse2(quote![where]).unwrap()); + where_clause + .predicates + .push(syn::parse2(quote! { #param: HasTypeFamily }).unwrap()); + where_clause + .predicates + .push(syn::parse2(quote! { #param: Fold<_TF, _TTF, Result = _U> }).unwrap()); + + return TokenStream::from(quote! { + impl #impl_generics SuperFold < _TF, _TTF > for #type_name < #param > + #where_clause + { + fn super_fold_with( + &self, + folder: &mut dyn Folder < _TF, _TTF >, + binders: usize, + ) -> ::chalk_engine::fallible::Fallible { + #body + } + } + }); + } + + // TypeFamily bound: + // + // Example: + // + // impl SuperFold for Foo + // where + // TF: TypeFamily, + // _TTF: TargetTypeFamily, + // { + // } + + if let Some(tf) = is_type_family(&generic_param0) { + let mut impl_generics = input.generics.clone(); + impl_generics.params.extend(vec![GenericParam::Type( + syn::parse(quote! { _TTF: TargetTypeFamily<#tf> }.into()).unwrap(), + )]); + + return TokenStream::from(quote! { + impl #impl_generics SuperFold < #tf, _TTF > for #type_name < #tf > + #where_clause_ref + { + fn super_fold_with( + &self, + folder: &mut dyn Folder < #tf, _TTF >, + binders: usize, + ) -> ::chalk_engine::fallible::Fallible { + #body + } + } + }); + } + + panic!("derive(Fold) requires a parameter that implements HasTypeFamily or TypeFamily"); +} + /// Generates the body of the Fold impl fn derive_fold_body(type_name: &Ident, data: Data) -> proc_macro2::TokenStream { match data { From 59929d1d12fc75ce1cd7c83a690d7b9e29fac7bd Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 27 Jan 2020 07:37:26 +0100 Subject: [PATCH 04/10] merge the panolpy of folding traits into one trait The previous setup was over-designed and cumbersome to use, even if in some bizarre sense theoretically elegant. I apologize. --- chalk-ir/src/fold.rs | 179 ++++++++---------------- chalk-ir/src/fold/shift.rs | 24 ++-- chalk-ir/src/fold/subst.rs | 12 +- chalk-ir/src/lib.rs | 15 +- chalk-solve/src/infer/canonicalize.rs | 16 +-- chalk-solve/src/infer/invert.rs | 18 +-- chalk-solve/src/infer/normalize_deep.rs | 16 +-- chalk-solve/src/infer/ucanonicalize.rs | 40 +++--- chalk-solve/src/infer/unify.rs | 16 +-- chalk-solve/src/solve/truncate.rs | 17 +-- 10 files changed, 121 insertions(+), 232 deletions(-) diff --git a/chalk-ir/src/fold.rs b/chalk-ir/src/fold.rs index f938f637d12..fc2e9e48d38 100644 --- a/chalk-ir/src/fold.rs +++ b/chalk-ir/src/fold.rs @@ -62,101 +62,75 @@ pub use self::subst::Subst; /// ```rust,ignore /// let x = x.fold_with(&mut folder, 0); /// ``` -pub trait Folder: - FreeVarFolder + InferenceFolder + PlaceholderFolder + TypeFolder -{ -} - -pub trait TypeFolder { - fn fold_ty(&mut self, ty: &Ty, binders: usize) -> Fallible>; - fn fold_lifetime(&mut self, lifetime: &Lifetime, binders: usize) - -> Fallible>; -} - -impl Folder for T -where - T: FreeVarFolder + InferenceFolder + PlaceholderFolder + TypeFolder, - TF: TypeFamily, - TTF: TypeFamily, -{ -} - -/// A convenience trait that indicates that this folder doesn't take -/// any action on types in particular, but just recursively folds -/// their contents (note that free variables that are encountered in -/// that process may still be substituted). The vast majority of -/// folders implement this trait. -pub trait DefaultTypeFolder {} - -impl TypeFolder for T -where - T: FreeVarFolder + InferenceFolder + PlaceholderFolder + DefaultTypeFolder, - TF: TypeFamily, - TTF: TargetTypeFamily, -{ +pub trait Folder = TF> { + /// Creates a `dyn` value from this folder. Unfortunately, this + /// must be added manually to each impl of Folder; it permits the + /// default implements below to create a `&mut dyn Folder` from + /// `Self` without knowing what `Self` is (by invoking this + /// method). Effectively, this limits impls of `Folder` to types + /// for which we are able to create a dyn value (i.e., not `[T]` + /// types). + fn as_dyn(&mut self) -> &mut dyn Folder; + + /// Top-level callback: invoked for each `Ty` that is + /// encountered when folding. By default, invokes + /// `super_fold_with`, which will in turn invoke the more + /// specialized folding methods below, like `fold_free_var_ty`. fn fold_ty(&mut self, ty: &Ty, binders: usize) -> Fallible> { - ty.super_fold_with(self, binders) + ty.super_fold_with(self.as_dyn(), binders) } + /// Top-level callback: invoked for each `Lifetime` that is + /// encountered when folding. By default, invokes + /// `super_fold_with`, which will in turn invoke the more + /// specialized folding methods below, like `fold_free_lifetime_ty`. fn fold_lifetime( &mut self, lifetime: &Lifetime, binders: usize, ) -> Fallible> { - lifetime.super_fold_with(self, binders) + lifetime.super_fold_with(self.as_dyn(), binders) } -} -/// The methods for folding **free variables**. These are `BoundVar` -/// instances where the binder is not something we folded over. This -/// is used when you are instantiating previously bound things with some -/// replacement. -pub trait FreeVarFolder { - /// Invoked for `TyData::BoundVar` instances that are not bound within the type being folded - /// over: - /// - /// - `depth` is the depth of the `TyData::BoundVar`; this has been adjusted to account for binders - /// in scope. - /// - `binders` is the number of binders in scope. - /// - /// This should return a type suitable for a context with `binders` in scope. - fn fold_free_var_ty(&mut self, depth: usize, binders: usize) -> Fallible>; - - /// As `fold_free_var_ty`, but for lifetimes. - fn fold_free_var_lifetime(&mut self, depth: usize, binders: usize) -> Fallible>; -} - -/// A convenience trait. If you implement this, you get an -/// implementation of `FreeVarFolder` for free that simply ignores -/// free values (that is, it replaces them with themselves). -/// -/// You can make it panic if a free-variable is found by overriding -/// `forbid` to return true. -pub trait DefaultFreeVarFolder { - fn forbid() -> bool { + /// If overridden to return true, then folding will panic if a + /// free variable is encountered. This should be done if free + /// type/lifetime variables are not expected. + fn forbid_free_vars(&self) -> bool { false } -} -impl FreeVarFolder for T { + /// Invoked for `TyData::BoundVar` instances that are not bound + /// within the type being folded over: + /// + /// - `depth` is the depth of the `TyData::BoundVar`; this has + /// been adjusted to account for binders in scope. + /// - `binders` is the number of binders in scope. + /// + /// This should return a type suitable for a context with + /// `binders` in scope. fn fold_free_var_ty(&mut self, depth: usize, binders: usize) -> Fallible> { - if T::forbid() { + if self.forbid_free_vars() { panic!("unexpected free variable with depth `{:?}`", depth) } else { Ok(TyData::::BoundVar(depth + binders).intern()) } } + /// As `fold_free_var_ty`, but for lifetimes. fn fold_free_var_lifetime(&mut self, depth: usize, binders: usize) -> Fallible> { - if T::forbid() { + if self.forbid_free_vars() { panic!("unexpected free variable with depth `{:?}`", depth) } else { Ok(LifetimeData::::BoundVar(depth + binders).intern()) } } -} -pub trait PlaceholderFolder { + /// If overriden to return true, we will panic when a free + /// placeholder type/lifetime is encountered. + fn forbid_free_placeholders(&self) -> bool { + false + } + /// Invoked for each occurrence of a placeholder type; these are /// used when we instantiate binders universally. Returns a type /// to use instead, which should be suitably shifted to account @@ -164,59 +138,38 @@ pub trait PlaceholderFolder { /// /// - `universe` is the universe of the `TypeName::ForAll` that was found /// - `binders` is the number of binders in scope - fn fold_free_placeholder_ty( - &mut self, - universe: PlaceholderIndex, - binders: usize, - ) -> Fallible>; - - /// As with `fold_free_placeholder_ty`, but for lifetimes. - fn fold_free_placeholder_lifetime( - &mut self, - universe: PlaceholderIndex, - binders: usize, - ) -> Fallible>; -} - -/// A convenience trait. If you implement this, you get an -/// implementation of `PlaceholderFolder` for free that simply ignores -/// placeholder values (that is, it replaces them with themselves). -/// -/// You can make it panic if a free-variable is found by overriding -/// `forbid` to return true. -pub trait DefaultPlaceholderFolder { - fn forbid() -> bool { - false - } -} - -impl PlaceholderFolder for T { fn fold_free_placeholder_ty( &mut self, universe: PlaceholderIndex, _binders: usize, ) -> Fallible> { - if T::forbid() { + if self.forbid_free_placeholders() { panic!("unexpected placeholder type `{:?}`", universe) } else { Ok(universe.to_ty::()) } } + /// As with `fold_free_placeholder_ty`, but for lifetimes. fn fold_free_placeholder_lifetime( &mut self, universe: PlaceholderIndex, _binders: usize, ) -> Fallible> { - if T::forbid() { + if self.forbid_free_placeholders() { panic!("unexpected placeholder lifetime `{:?}`", universe) } else { Ok(universe.to_lifetime::()) } } -} -pub trait InferenceFolder { + /// If overridden to return true, inference variables will trigger + /// panics when folded. Used when inference variables are + /// unexpected. + fn forbid_inference_vars(&self) -> bool { + false + } + /// Invoked for each occurrence of a inference type; these are /// used when we instantiate binders universally. Returns a type /// to use instead, which should be suitably shifted to account @@ -224,43 +177,21 @@ pub trait InferenceFolder { /// /// - `universe` is the universe of the `TypeName::ForAll` that was found /// - `binders` is the number of binders in scope - fn fold_inference_ty(&mut self, var: InferenceVar, binders: usize) -> Fallible>; - - /// As with `fold_free_inference_ty`, but for lifetimes. - fn fold_inference_lifetime( - &mut self, - var: InferenceVar, - binders: usize, - ) -> Fallible>; -} - -/// A convenience trait. If you implement this, you get an -/// implementation of `InferenceFolder` for free that simply ignores -/// inference values (that is, it replaces them with themselves). -/// -/// You can make it panic if a free-variable is found by overriding -/// `forbid` to return true. -pub trait DefaultInferenceFolder { - fn forbid() -> bool { - false - } -} - -impl InferenceFolder for T { fn fold_inference_ty(&mut self, var: InferenceVar, _binders: usize) -> Fallible> { - if T::forbid() { + if self.forbid_inference_vars() { panic!("unexpected inference type `{:?}`", var) } else { Ok(var.to_ty::()) } } + /// As with `fold_free_inference_ty`, but for lifetimes. fn fold_inference_lifetime( &mut self, var: InferenceVar, _binders: usize, ) -> Fallible> { - if T::forbid() { + if self.forbid_inference_vars() { panic!("unexpected inference lifetime `'{:?}`", var) } else { Ok(var.to_lifetime::()) diff --git a/chalk-ir/src/fold/shift.rs b/chalk-ir/src/fold/shift.rs index 97ef1c3ba3b..60f8794bab8 100644 --- a/chalk-ir/src/fold/shift.rs +++ b/chalk-ir/src/fold/shift.rs @@ -1,6 +1,4 @@ -use super::{ - DefaultInferenceFolder, DefaultPlaceholderFolder, DefaultTypeFolder, Fold, FreeVarFolder, -}; +use super::Fold; use crate::*; /// Methods for converting debruijn indices to move values into or out @@ -86,9 +84,11 @@ impl Shifter { } } -impl DefaultTypeFolder for Shifter {} +impl Folder for Shifter { + fn as_dyn(&mut self) -> &mut dyn Folder { + self + } -impl FreeVarFolder for Shifter { fn fold_free_var_ty(&mut self, depth: usize, binders: usize) -> Fallible> { Ok(TyData::::BoundVar(self.adjust(depth, binders)).intern()) } @@ -98,10 +98,6 @@ impl FreeVarFolder for Shifter { } } -impl DefaultPlaceholderFolder for Shifter {} - -impl DefaultInferenceFolder for Shifter {} - //--------------------------------------------------------------------------- /// A shifter that reduces debruijn indices -- in other words, which lifts a value @@ -128,9 +124,11 @@ impl DownShifter { } } -impl DefaultTypeFolder for DownShifter {} +impl Folder for DownShifter { + fn as_dyn(&mut self) -> &mut dyn Folder { + self + } -impl FreeVarFolder for DownShifter { fn fold_free_var_ty(&mut self, depth: usize, binders: usize) -> Fallible> { Ok(TyData::::BoundVar(self.adjust(depth, binders)?).intern()) } @@ -139,7 +137,3 @@ impl FreeVarFolder for DownShifter { Ok(LifetimeData::::BoundVar(self.adjust(depth, binders)?).intern()) } } - -impl DefaultPlaceholderFolder for DownShifter {} - -impl DefaultInferenceFolder for DownShifter {} diff --git a/chalk-ir/src/fold/subst.rs b/chalk-ir/src/fold/subst.rs index 610875cd0d8..2d8582d4651 100644 --- a/chalk-ir/src/fold/subst.rs +++ b/chalk-ir/src/fold/subst.rs @@ -8,15 +8,17 @@ pub struct Subst<'s, TF: TypeFamily> { parameters: &'s [Parameter], } -impl<'s, TF: TypeFamily> Subst<'s, TF> { +impl Subst<'_, TF> { pub fn apply>(parameters: &[Parameter], value: &T) -> T::Result { value.fold_with(&mut Subst { parameters }, 0).unwrap() } } -impl<'b, TF: TypeFamily> DefaultTypeFolder for Subst<'b, TF> {} +impl Folder for Subst<'_, TF> { + fn as_dyn(&mut self) -> &mut dyn Folder { + self + } -impl<'b, TF: TypeFamily> FreeVarFolder for Subst<'b, TF> { fn fold_free_var_ty(&mut self, depth: usize, binders: usize) -> Fallible> { if depth >= self.parameters.len() { Ok(TyData::::BoundVar(depth - self.parameters.len() + binders).intern()) @@ -39,7 +41,3 @@ impl<'b, TF: TypeFamily> FreeVarFolder for Subst<'b, TF> { } } } - -impl<'b, TF: TypeFamily> DefaultPlaceholderFolder for Subst<'b, TF> {} - -impl<'b, TF: TypeFamily> DefaultInferenceFolder for Subst<'b, TF> {} diff --git a/chalk-ir/src/lib.rs b/chalk-ir/src/lib.rs index e94c6cd0055..0a0c5e86cc3 100644 --- a/chalk-ir/src/lib.rs +++ b/chalk-ir/src/lib.rs @@ -1,9 +1,6 @@ use crate::cast::{Cast, CastTo}; use crate::fold::shift::Shift; -use crate::fold::{ - DefaultInferenceFolder, DefaultPlaceholderFolder, DefaultTypeFolder, Fold, Folder, - FreeVarFolder, Subst, -}; +use crate::fold::{Fold, Folder, Subst}; use chalk_derive::{Fold, HasTypeFamily}; use chalk_engine::fallible::*; use lalrpop_intern::InternedString; @@ -1352,11 +1349,11 @@ where } } -impl<'a, TF: TypeFamily> DefaultTypeFolder for &'a Substitution {} - -impl<'a, TF: TypeFamily> DefaultInferenceFolder for &'a Substitution {} +impl Folder for &Substitution { + fn as_dyn(&mut self) -> &mut dyn Folder { + self + } -impl<'a, TF: TypeFamily> FreeVarFolder for &'a Substitution { fn fold_free_var_ty(&mut self, depth: usize, binders: usize) -> Fallible> { let ty = self.at(depth); let ty = ty.assert_ty_ref(); @@ -1370,8 +1367,6 @@ impl<'a, TF: TypeFamily> FreeVarFolder for &'a Substitution { } } -impl<'a, TF: TypeFamily> DefaultPlaceholderFolder for &'a Substitution {} - /// Combines a substitution (`subst`) with a set of region constraints /// (`constraints`). This represents the result of a query; the /// substitution stores the values for the query's unknown variables, diff --git a/chalk-solve/src/infer/canonicalize.rs b/chalk-solve/src/infer/canonicalize.rs index a8b4730d47d..d702f66bfee 100644 --- a/chalk-solve/src/infer/canonicalize.rs +++ b/chalk-solve/src/infer/canonicalize.rs @@ -1,9 +1,7 @@ use chalk_engine::fallible::*; use chalk_ir::family::{HasTypeFamily, TypeFamily}; use chalk_ir::fold::shift::Shift; -use chalk_ir::fold::{ - DefaultFreeVarFolder, DefaultTypeFolder, Fold, InferenceFolder, PlaceholderFolder, -}; +use chalk_ir::fold::{Fold, Folder}; use chalk_ir::*; use std::cmp::max; @@ -98,9 +96,11 @@ impl<'q, TF: TypeFamily> Canonicalizer<'q, TF> { } } -impl DefaultTypeFolder for Canonicalizer<'_, TF> {} +impl Folder for Canonicalizer<'_, TF> { + fn as_dyn(&mut self) -> &mut dyn Folder { + self + } -impl PlaceholderFolder for Canonicalizer<'_, TF> { fn fold_free_placeholder_ty( &mut self, universe: PlaceholderIndex, @@ -118,15 +118,11 @@ impl PlaceholderFolder for Canonicalizer<'_, TF> { self.max_universe = max(self.max_universe, universe.ui); Ok(universe.to_lifetime::()) } -} -impl DefaultFreeVarFolder for Canonicalizer<'_, TF> { - fn forbid() -> bool { + fn forbid_free_vars(&self) -> bool { true } -} -impl InferenceFolder for Canonicalizer<'_, TF> { fn fold_inference_ty(&mut self, var: InferenceVar, binders: usize) -> Fallible> { debug_heading!("fold_inference_ty(depth={:?}, binders={:?})", var, binders); let var = EnaVariable::from(var); diff --git a/chalk-solve/src/infer/invert.rs b/chalk-solve/src/infer/invert.rs index de01ea5958f..9f84eef2039 100644 --- a/chalk-solve/src/infer/invert.rs +++ b/chalk-solve/src/infer/invert.rs @@ -2,9 +2,7 @@ use chalk_engine::fallible::*; use chalk_ir::family::HasTypeFamily; use chalk_ir::family::TypeFamily; use chalk_ir::fold::shift::Shift; -use chalk_ir::fold::{ - DefaultFreeVarFolder, DefaultInferenceFolder, DefaultTypeFolder, Fold, PlaceholderFolder, -}; +use chalk_ir::fold::{Fold, Folder}; use chalk_ir::*; use std::collections::HashMap; @@ -115,9 +113,11 @@ impl<'q, TF: TypeFamily> Inverter<'q, TF> { } } -impl DefaultTypeFolder for Inverter<'_, TF> {} +impl Folder for Inverter<'_, TF> { + fn as_dyn(&mut self) -> &mut dyn Folder { + self + } -impl PlaceholderFolder for Inverter<'_, TF> { fn fold_free_placeholder_ty( &mut self, universe: PlaceholderIndex, @@ -145,16 +145,12 @@ impl PlaceholderFolder for Inverter<'_, TF> { .to_lifetime() .shifted_in(binders)) } -} -impl DefaultFreeVarFolder for Inverter<'_, TF> { - fn forbid() -> bool { + fn forbid_free_vars(&self) -> bool { true } -} -impl DefaultInferenceFolder for Inverter<'_, TF> { - fn forbid() -> bool { + fn forbid_inference_vars(&self) -> bool { true } } diff --git a/chalk-solve/src/infer/normalize_deep.rs b/chalk-solve/src/infer/normalize_deep.rs index 7bf97543292..2a04893bfff 100644 --- a/chalk-solve/src/infer/normalize_deep.rs +++ b/chalk-solve/src/infer/normalize_deep.rs @@ -1,9 +1,7 @@ use chalk_engine::fallible::*; use chalk_ir::family::TypeFamily; use chalk_ir::fold::shift::Shift; -use chalk_ir::fold::{ - DefaultFreeVarFolder, DefaultPlaceholderFolder, DefaultTypeFolder, Fold, InferenceFolder, -}; +use chalk_ir::fold::{Fold, Folder}; use chalk_ir::*; use super::{EnaVariable, InferenceTable}; @@ -31,11 +29,11 @@ struct DeepNormalizer<'table, TF: TypeFamily> { table: &'table mut InferenceTable, } -impl DefaultTypeFolder for DeepNormalizer<'_, TF> {} - -impl DefaultPlaceholderFolder for DeepNormalizer<'_, TF> {} +impl Folder for DeepNormalizer<'_, TF> { + fn as_dyn(&mut self) -> &mut dyn Folder { + self + } -impl InferenceFolder for DeepNormalizer<'_, TF> { fn fold_inference_ty(&mut self, var: InferenceVar, binders: usize) -> Fallible> { let var = EnaVariable::from(var); match self.table.probe_ty_var(var) { @@ -55,10 +53,8 @@ impl InferenceFolder for DeepNormalizer<'_, TF> { None => Ok(var.to_lifetime()), // FIXME shift } } -} -impl DefaultFreeVarFolder for DeepNormalizer<'_, TF> { - fn forbid() -> bool { + fn forbid_free_vars(&self) -> bool { true } } diff --git a/chalk-solve/src/infer/ucanonicalize.rs b/chalk-solve/src/infer/ucanonicalize.rs index b48ddfdee58..963356f1881 100644 --- a/chalk-solve/src/infer/ucanonicalize.rs +++ b/chalk-solve/src/infer/ucanonicalize.rs @@ -1,8 +1,6 @@ use chalk_engine::fallible::*; use chalk_ir::family::TypeFamily; -use chalk_ir::fold::{ - DefaultFreeVarFolder, DefaultInferenceFolder, DefaultTypeFolder, Fold, PlaceholderFolder, -}; +use chalk_ir::fold::{Fold, Folder}; use chalk_ir::*; use super::InferenceTable; @@ -231,9 +229,11 @@ struct UCollector<'q> { universes: &'q mut UniverseMap, } -impl DefaultTypeFolder for UCollector<'_> {} +impl Folder for UCollector<'_> { + fn as_dyn(&mut self) -> &mut dyn Folder { + self + } -impl PlaceholderFolder for UCollector<'_> { fn fold_free_placeholder_ty( &mut self, universe: PlaceholderIndex, @@ -251,12 +251,8 @@ impl PlaceholderFolder for UCollector<'_> { self.universes.add(universe.ui); Ok(universe.to_lifetime::()) } -} - -impl DefaultFreeVarFolder for UCollector<'_> {} -impl DefaultInferenceFolder for UCollector<'_> { - fn forbid() -> bool { + fn forbid_inference_vars(&self) -> bool { true } } @@ -265,15 +261,15 @@ struct UMapToCanonical<'q> { universes: &'q UniverseMap, } -impl DefaultTypeFolder for UMapToCanonical<'_> {} +impl Folder for UMapToCanonical<'_> { + fn as_dyn(&mut self) -> &mut dyn Folder { + self + } -impl DefaultInferenceFolder for UMapToCanonical<'_> { - fn forbid() -> bool { + fn forbid_inference_vars(&self) -> bool { true } -} -impl PlaceholderFolder for UMapToCanonical<'_> { fn fold_free_placeholder_ty( &mut self, universe0: PlaceholderIndex, @@ -301,15 +297,15 @@ impl PlaceholderFolder for UMapToCanonical<'_> { } } -impl DefaultFreeVarFolder for UMapToCanonical<'_> {} - struct UMapFromCanonical<'q> { universes: &'q UniverseMap, } -impl DefaultTypeFolder for UMapFromCanonical<'_> {} +impl Folder for UMapFromCanonical<'_> { + fn as_dyn(&mut self) -> &mut dyn Folder { + self + } -impl PlaceholderFolder for UMapFromCanonical<'_> { fn fold_free_placeholder_ty( &mut self, universe0: PlaceholderIndex, @@ -335,12 +331,8 @@ impl PlaceholderFolder for UMapFromCanonical<'_> { } .to_lifetime::()) } -} - -impl DefaultFreeVarFolder for UMapFromCanonical<'_> {} -impl DefaultInferenceFolder for UMapFromCanonical<'_> { - fn forbid() -> bool { + fn forbid_inference_vars(&self) -> bool { true } } diff --git a/chalk-solve/src/infer/unify.rs b/chalk-solve/src/infer/unify.rs index ad439b788dc..408aee01d9e 100644 --- a/chalk-solve/src/infer/unify.rs +++ b/chalk-solve/src/infer/unify.rs @@ -4,9 +4,7 @@ use crate::infer::instantiate::IntoBindersAndValue; use chalk_engine::fallible::*; use chalk_ir::cast::Cast; use chalk_ir::family::TypeFamily; -use chalk_ir::fold::{ - DefaultFreeVarFolder, DefaultTypeFolder, Fold, InferenceFolder, PlaceholderFolder, -}; +use chalk_ir::fold::{Fold, Folder}; use chalk_ir::zip::{Zip, Zipper}; use std::fmt::Debug; @@ -375,9 +373,11 @@ impl<'u, 't, TF: TypeFamily> OccursCheck<'u, 't, TF> { } } -impl DefaultTypeFolder for OccursCheck<'_, '_, TF> {} +impl Folder for OccursCheck<'_, '_, TF> { + fn as_dyn(&mut self) -> &mut dyn Folder { + self + } -impl PlaceholderFolder for OccursCheck<'_, '_, TF> { fn fold_free_placeholder_ty( &mut self, universe: PlaceholderIndex, @@ -419,9 +419,7 @@ impl PlaceholderFolder for OccursCheck<'_, '_, TF> { Ok(ui.to_lifetime::()) // no need to shift, not relative to depth } } -} -impl InferenceFolder for OccursCheck<'_, '_, TF> { fn fold_inference_ty(&mut self, var: InferenceVar, _binders: usize) -> Fallible> { let var = EnaVariable::from(var); match self.unifier.table.unify.probe_value(var) { @@ -494,10 +492,8 @@ impl InferenceFolder for OccursCheck<'_, '_, TF> { } } } -} -impl DefaultFreeVarFolder for OccursCheck<'_, '_, TF> { - fn forbid() -> bool { + fn forbid_free_vars(&self) -> bool { true } } diff --git a/chalk-solve/src/solve/truncate.rs b/chalk-solve/src/solve/truncate.rs index be60b447545..337bd14d0e7 100644 --- a/chalk-solve/src/solve/truncate.rs +++ b/chalk-solve/src/solve/truncate.rs @@ -4,10 +4,7 @@ use crate::infer::InferenceTable; use chalk_engine::fallible::*; use chalk_ir::family::TypeFamily; use chalk_ir::fold::shift::Shift; -use chalk_ir::fold::{ - DefaultFreeVarFolder, DefaultInferenceFolder, DefaultPlaceholderFolder, Fold, SuperFold, - TypeFolder, -}; +use chalk_ir::fold::{Fold, Folder, SuperFold}; use chalk_ir::*; use std::fmt::Debug; @@ -73,7 +70,11 @@ impl<'infer, TF: TypeFamily> Truncater<'infer, TF> { } } -impl TypeFolder for Truncater<'_, TF> { +impl Folder for Truncater<'_, TF> { + fn as_dyn(&mut self) -> &mut dyn Folder { + self + } + fn fold_ty(&mut self, ty: &Ty, binders: usize) -> Fallible> { if let Some(normalized_ty) = self.infer.normalize_shallow(ty) { return self.fold_ty(&normalized_ty, binders); @@ -114,12 +115,6 @@ impl TypeFolder for Truncater<'_, TF> { } } -impl DefaultFreeVarFolder for Truncater<'_, TF> {} - -impl DefaultInferenceFolder for Truncater<'_, TF> {} - -impl DefaultPlaceholderFolder for Truncater<'_, TF> {} - #[test] fn truncate_types() { let mut table = InferenceTable::::new(); From da94001add18e04b78537af7816df7228b71ba6a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 2 Feb 2020 05:36:27 -0500 Subject: [PATCH 05/10] introduce `fold_goal` and `fold_program_clause` callbacks --- chalk-ir/src/fold.rs | 77 ++++++++++++++++++++++++++----- chalk-ir/src/fold/boring_impls.rs | 12 ----- chalk-ir/src/lib.rs | 6 +-- 3 files changed, 69 insertions(+), 26 deletions(-) diff --git a/chalk-ir/src/fold.rs b/chalk-ir/src/fold.rs index fc2e9e48d38..2b815f7f240 100644 --- a/chalk-ir/src/fold.rs +++ b/chalk-ir/src/fold.rs @@ -92,6 +92,20 @@ pub trait Folder = TF> { lifetime.super_fold_with(self.as_dyn(), binders) } + /// Invoked for every program clause. By default, recursively folds the goals contents. + fn fold_program_clause( + &mut self, + clause: &ProgramClause, + binders: usize, + ) -> Fallible> { + clause.super_fold_with(self.as_dyn(), binders) + } + + /// Invoked for every goal. By default, recursively folds the goals contents. + fn fold_goal(&mut self, goal: &Goal, binders: usize) -> Fallible> { + goal.super_fold_with(self.as_dyn(), binders) + } + /// If overridden to return true, then folding will panic if a /// free variable is encountered. This should be done if free /// type/lifetime variables are not expected. @@ -242,6 +256,22 @@ pub trait SuperFold = TF>: Fold Fallible; } +/// "Folding" a type invokes the `fold_ty` method on the folder; this +/// usually (in turn) invokes `super_fold_ty` to fold the individual +/// parts. +impl> Fold for Ty { + type Result = Ty; + + fn fold_with( + &self, + folder: &mut dyn Folder, + binders: usize, + ) -> Fallible { + folder.fold_ty(self, binders) + } +} + +/// "Super fold" for a type invokes te more detailed callbacks on the type impl SuperFold for Ty where TF: TypeFamily, @@ -270,18 +300,18 @@ where } } -/// "Folding" a type invokes the `fold_ty` method on the folder; this -/// usually (in turn) invokes `super_fold_ty` to fold the individual +/// "Folding" a lifetime invokes the `fold_lifetime` method on the folder; this +/// usually (in turn) invokes `super_fold_lifetime` to fold the individual /// parts. -impl> Fold for Ty { - type Result = Ty; +impl> Fold for Lifetime { + type Result = Lifetime; fn fold_with( &self, folder: &mut dyn Folder, binders: usize, ) -> Fallible { - folder.fold_ty(self, binders) + folder.fold_lifetime(self, binders) } } @@ -312,17 +342,42 @@ where } } -/// "Folding" a lifetime invokes the `fold_lifetime` method on the folder; this -/// usually (in turn) invokes `super_fold_lifetime` to fold the individual -/// parts. -impl> Fold for Lifetime { - type Result = Lifetime; +/// Folding a goal invokes the `fold_goal` callback (which will, by +/// default, invoke super-fold). +impl> Fold for Goal { + type Result = Goal; fn fold_with( &self, folder: &mut dyn Folder, binders: usize, ) -> Fallible { - folder.fold_lifetime(self, binders) + folder.fold_goal(self, binders) + } +} + +/// Superfold folds recursively. +impl> SuperFold for Goal { + fn super_fold_with( + &self, + folder: &mut dyn Folder, + binders: usize, + ) -> Fallible { + Ok(Goal::new(self.data().fold_with(folder, binders)?)) + } +} + +/// Folding a program clause invokes the `fold_program_clause` +/// callback on the folder (which will, by default, invoke the +/// `super_fold_with` method on the program clause). +impl> Fold for ProgramClause { + type Result = ProgramClause; + + fn fold_with( + &self, + folder: &mut dyn Folder, + binders: usize, + ) -> Fallible { + folder.fold_program_clause(self, binders) } } diff --git a/chalk-ir/src/fold/boring_impls.rs b/chalk-ir/src/fold/boring_impls.rs index f3b58be8687..318eb8a5191 100644 --- a/chalk-ir/src/fold/boring_impls.rs +++ b/chalk-ir/src/fold/boring_impls.rs @@ -99,18 +99,6 @@ impl> Fold for Parameter } } -impl> Fold for Goal { - type Result = Goal; - fn fold_with( - &self, - folder: &mut dyn Folder, - binders: usize, - ) -> Fallible { - let data = self.data().fold_with(folder, binders)?; - Ok(Goal::new(data)) - } -} - impl> Fold for Substitution { type Result = Substitution; fn fold_with( diff --git a/chalk-ir/src/lib.rs b/chalk-ir/src/lib.rs index 0a0c5e86cc3..6fa2cb451b7 100644 --- a/chalk-ir/src/lib.rs +++ b/chalk-ir/src/lib.rs @@ -1,7 +1,7 @@ use crate::cast::{Cast, CastTo}; use crate::fold::shift::Shift; -use crate::fold::{Fold, Folder, Subst}; -use chalk_derive::{Fold, HasTypeFamily}; +use crate::fold::{Fold, Folder, Subst, SuperFold}; +use chalk_derive::{Fold, HasTypeFamily, SuperFold}; use chalk_engine::fallible::*; use lalrpop_intern::InternedString; use std::collections::BTreeSet; @@ -952,7 +952,7 @@ pub struct ProgramClauseImplication { pub conditions: Goals, } -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Fold)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, SuperFold)] pub enum ProgramClause { Implies(ProgramClauseImplication), ForAll(Binders>), From 7a6116dbb4a0b5984836096a08b863572ffb8cca Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 2 Feb 2020 06:35:37 -0500 Subject: [PATCH 06/10] start to document folding --- book/src/SUMMARY.md | 2 + book/src/types/operations.md | 4 ++ book/src/types/operations/fold.md | 94 +++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 book/src/types/operations.md create mode 100644 book/src/types/operations/fold.md diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 226bd7443a8..f8d268c8b65 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -10,6 +10,8 @@ - [Rust types](./types/rust_types.md) - [Application types](./types/rust_types/application_ty.md) - [Rust lifetimes](./types/rust_lifetimes.md) + - [Operations](./types/operations.md) + - [Fold and the Folder trait](./types/operations/fold.md) - [Representing traits, impls, and other parts of Rust programs](./rust_ir.md) - [Lowering Rust IR to logic](./clauses.md) - [Unification and type equality](./clauses/type_equality.md) diff --git a/book/src/types/operations.md b/book/src/types/operations.md new file mode 100644 index 00000000000..002d75a3a2c --- /dev/null +++ b/book/src/types/operations.md @@ -0,0 +1,4 @@ +# Operations + +This chapter describes various patterns and utilities for manipulating +Rust types. diff --git a/book/src/types/operations/fold.md b/book/src/types/operations/fold.md new file mode 100644 index 00000000000..4611fa0fa47 --- /dev/null +++ b/book/src/types/operations/fold.md @@ -0,0 +1,94 @@ +# Fold and the Folder trait + +The [`Fold`] trait permits one to traverse a type or other term in the +chalk-ir and make a copy of it, possibly making small substitutions or +alterations along the way. Folding also allows copying a term from one +type family to another. + +[`Fold`]: http://rust-lang.github.io/chalk/chalk_ir/fold/trait.Fold.html + +To use the fold-trait, one invokes the [`Fold::fold_with`] method, supplying some +"folder" as well as the number of "in scope binders" for that term (typically `0` +to start): + +```rust,ignore +let output_ty = input_ty.fold_with(&mut folder, 0); +``` + +[`Fold::fold_with`]: http://rust-lang.github.io/chalk/chalk_ir/fold/trait.Fold.html#tymethod.fold_with + +The folder is some instance of the [`Folder`] trait. This trait +defines a few key callbacks that allow you to substitute different +values as the fold proceeds. For example, when a type is folded, the +folder can substitute a new type in its place. + +[`Folder`]: http://rust-lang.github.io/chalk/chalk_ir/fold/trait.Folder.html + +## Uses for folders + +A common use for `Fold` is to permit a substitution -- that is, +replacing generic type parameters with their values. + +## From Fold to Folder to SuperFold and back again + +The overall flow of folding is like this. + +1. [`Fold::fold_with`] is invoked on the outermost term. It recursively + walks the term. +2. For those sorts of terms (types, lifetimes, goals, program clauses) that have + callbacks in the [`Folder`] trait, invoking [`Fold::fold_with`] will in turn + invoke the corresponding method on the [`Folder`] trait, such as `Folder::fold_ty`. +3. The default implementation of `Folder::fold_ty`, in turn, invokes + `SuperFold::super_fold_with`. This will recursively fold the + contents of the type. In some cases, the `super_fold_with` + implementation invokes more specialized methods on [`Folder`], such + as [`Folder::fold_free_var_ty`], which makes it easier to write + folders that just intercept *certain* types. + +Thus, as a user, you can customize folding by: + +* Defining your own `Folder` type +* Implementing the appropriate methods to "intercept" types/lifetimes/etc at the right level of + detail +* In those methods, if you find a case where you would prefer not to + substitute a new value, then invoke `SuperFold::super_fold_with` to + return to the default behavior. + +## The `binders` argument + +Each callback in the [`Folder`] trait takes a `binders` argument. This indicates +the number of binders that we have traversed during folding, which is relevant for debruijn indices. +So e.g. a bound variable with depth 1, if invoked with a `binders` value of 1, indicates something that was bound to something external to the fold. + +XXX explain with examples and in more detail + +## The `Fold::Result` associated type + +The `Fold` trait defines a [`Result`] associated type, indicating the +type that will result from folding. + +[`Result`]: http://rust-lang.github.io/chalk/chalk_ir/fold/trait.Fold.html#associatedtype.Result + +## When to implement the Fold and SuperFold traits + +Any piece of IR that represents a kind of "term" (e.g., a type, part +of a type, or a goal, etc) in the logic should implement `Fold`. We +also implement `Fold` for common collection types like `Vec` as well +as tuples, references, etc. + +The `SuperFold` trait should only be implemented for those types that +have a callback defined on the `Folder` trait (e.g., types and +lifetimes). + +## Derives + +Using the `chalk-derive` crate, you can auto-derive the `Fold` and +`SuperFold` traits. The derives are a bit cludgy and require: + +* You must import `Fold` (or `SuperFold`, respectively) into scope. +* The type you are deriving `Fold` on must have either: + * A type parameter that has a `TypeFamily` bound, like `TF: TypeFamily` + * A type parameter that has a `HasTypeFamily` bound, like `TF: HasTypeFamily` + * The `has_type_family(XXX)` attribute. + + From 32a3b7d5ee47d9d64ae49309865b7443d744da29 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2020 18:28:17 -0500 Subject: [PATCH 07/10] remove super-fold derive --- book/src/types/operations/fold.md | 7 +- chalk-derive/src/lib.rs | 134 ------------------------------ chalk-ir/src/fold/boring_impls.rs | 17 ++++ chalk-ir/src/lib.rs | 4 +- 4 files changed, 23 insertions(+), 139 deletions(-) diff --git a/book/src/types/operations/fold.md b/book/src/types/operations/fold.md index 4611fa0fa47..61622a992a7 100644 --- a/book/src/types/operations/fold.md +++ b/book/src/types/operations/fold.md @@ -82,10 +82,11 @@ lifetimes). ## Derives -Using the `chalk-derive` crate, you can auto-derive the `Fold` and -`SuperFold` traits. The derives are a bit cludgy and require: +Using the `chalk-derive` crate, you can auto-derive the `Fold` trait. +There isn't presently a derive for `SuperFold` since it is very rare +to require it. The derive for `Fold` is a bit cludgy and requires: -* You must import `Fold` (or `SuperFold`, respectively) into scope. +* You must import `Fold` into scope. * The type you are deriving `Fold` on must have either: * A type parameter that has a `TypeFamily` bound, like `TF: TypeFamily` * A type parameter that has a `HasTypeFamily` bound, like `TF: HasTypeFamily` diff --git a/chalk-derive/src/lib.rs b/chalk-derive/src/lib.rs index 81b74378d1c..919252887c1 100644 --- a/chalk-derive/src/lib.rs +++ b/chalk-derive/src/lib.rs @@ -147,140 +147,6 @@ pub fn derive_fold(item: TokenStream) -> TokenStream { panic!("derive(Fold) requires a parameter that implements HasTypeFamily or TypeFamily"); } -/// Derives Fold for structs and enums for which one of the following is true: -/// - It has a `#[has_type_family(TheTypeFamily)]` attribute -/// - There is a single parameter `T: HasTypeFamily` (does not have to be named `T`) -/// - There is a single parameter `TF: TypeFamily` (does not have to be named `TF`) -#[proc_macro_derive(SuperFold, attributes(has_type_family))] -pub fn derive_super_fold(item: TokenStream) -> TokenStream { - let input = parse_macro_input!(item as DeriveInput); - let (impl_generics, ty_generics, where_clause_ref) = input.generics.split_for_impl(); - - let type_name = input.ident; - let body = derive_fold_body(&type_name, input.data); - - if let Some(attr) = input - .attrs - .iter() - .find(|a| a.path.is_ident("has_type_family")) - { - // Hardcoded type-family: - // - // impl SuperFold for Type { - // } - let arg = attr - .parse_args::() - .expect("Expected has_type_family argument"); - - return TokenStream::from(quote! { - impl #impl_generics SuperFold < #arg, #arg > for #type_name #ty_generics #where_clause_ref { - fn super_fold_with( - &self, - folder: &mut dyn Folder < #arg, #arg >, - binders: usize, - ) -> ::chalk_engine::fallible::Fallible { - #body - } - } - }); - } - - match input.generics.params.len() { - 1 => {} - - 0 => { - panic!("Fold derive requires a single type parameter or a `#[has_type_family]` attr"); - } - - _ => { - panic!("Fold derive only works with a single type parameter"); - } - }; - - let generic_param0 = &input.generics.params[0]; - - if let Some(param) = has_type_family(&generic_param0) { - // HasTypeFamily bound: - // - // Example: - // - // impl SuperFold<_TF, _TTF> for Binders - // where - // T: HasTypeFamily, - // T: Fold<_TF, _TTF, Result = _U>, - // U: HasTypeFamily, - // { - // } - - let mut impl_generics = input.generics.clone(); - impl_generics.params.extend(vec![ - GenericParam::Type(syn::parse(quote! { _TF: TypeFamily }.into()).unwrap()), - GenericParam::Type(syn::parse(quote! { _TTF: TargetTypeFamily<_TF> }.into()).unwrap()), - GenericParam::Type( - syn::parse(quote! { _U: HasTypeFamily }.into()).unwrap(), - ), - ]); - - let mut where_clause = where_clause_ref - .cloned() - .unwrap_or_else(|| syn::parse2(quote![where]).unwrap()); - where_clause - .predicates - .push(syn::parse2(quote! { #param: HasTypeFamily }).unwrap()); - where_clause - .predicates - .push(syn::parse2(quote! { #param: Fold<_TF, _TTF, Result = _U> }).unwrap()); - - return TokenStream::from(quote! { - impl #impl_generics SuperFold < _TF, _TTF > for #type_name < #param > - #where_clause - { - fn super_fold_with( - &self, - folder: &mut dyn Folder < _TF, _TTF >, - binders: usize, - ) -> ::chalk_engine::fallible::Fallible { - #body - } - } - }); - } - - // TypeFamily bound: - // - // Example: - // - // impl SuperFold for Foo - // where - // TF: TypeFamily, - // _TTF: TargetTypeFamily, - // { - // } - - if let Some(tf) = is_type_family(&generic_param0) { - let mut impl_generics = input.generics.clone(); - impl_generics.params.extend(vec![GenericParam::Type( - syn::parse(quote! { _TTF: TargetTypeFamily<#tf> }.into()).unwrap(), - )]); - - return TokenStream::from(quote! { - impl #impl_generics SuperFold < #tf, _TTF > for #type_name < #tf > - #where_clause_ref - { - fn super_fold_with( - &self, - folder: &mut dyn Folder < #tf, _TTF >, - binders: usize, - ) -> ::chalk_engine::fallible::Fallible { - #body - } - } - }); - } - - panic!("derive(Fold) requires a parameter that implements HasTypeFamily or TypeFamily"); -} - /// Generates the body of the Fold impl fn derive_fold_body(type_name: &Ident, data: Data) -> proc_macro2::TokenStream { match data { diff --git a/chalk-ir/src/fold/boring_impls.rs b/chalk-ir/src/fold/boring_impls.rs index 318eb8a5191..8a3a65122a0 100644 --- a/chalk-ir/src/fold/boring_impls.rs +++ b/chalk-ir/src/fold/boring_impls.rs @@ -173,6 +173,23 @@ id_fold!(StructId); id_fold!(TraitId); id_fold!(AssocTypeId); +impl> SuperFold for ProgramClause { + fn super_fold_with( + &self, + folder: &mut dyn Folder, + binders: usize, + ) -> ::chalk_engine::fallible::Fallible { + match self { + ProgramClause::Implies(pci) => { + Ok(ProgramClause::Implies(pci.fold_with(folder, binders)?)) + } + ProgramClause::ForAll(pci) => { + Ok(ProgramClause::ForAll(pci.fold_with(folder, binders)?)) + } + } + } +} + impl> Fold for PhantomData { type Result = PhantomData; diff --git a/chalk-ir/src/lib.rs b/chalk-ir/src/lib.rs index 6fa2cb451b7..46bc88c3e75 100644 --- a/chalk-ir/src/lib.rs +++ b/chalk-ir/src/lib.rs @@ -1,7 +1,7 @@ use crate::cast::{Cast, CastTo}; use crate::fold::shift::Shift; use crate::fold::{Fold, Folder, Subst, SuperFold}; -use chalk_derive::{Fold, HasTypeFamily, SuperFold}; +use chalk_derive::{Fold, HasTypeFamily}; use chalk_engine::fallible::*; use lalrpop_intern::InternedString; use std::collections::BTreeSet; @@ -952,7 +952,7 @@ pub struct ProgramClauseImplication { pub conditions: Goals, } -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, SuperFold)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum ProgramClause { Implies(ProgramClauseImplication), ForAll(Binders>), From 9bb1ac05e12ad8dbdd7c292ee40a075969c072ab Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2020 18:30:15 -0500 Subject: [PATCH 08/10] address nits --- chalk-integration/src/lowering.rs | 9 ++++----- chalk-ir/src/lib.rs | 10 +++++----- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/chalk-integration/src/lowering.rs b/chalk-integration/src/lowering.rs index f8ac9b6857b..868247c4796 100644 --- a/chalk-integration/src/lowering.rs +++ b/chalk-integration/src/lowering.rs @@ -1135,13 +1135,12 @@ impl LowerClause for Clause { let consequences: Vec> = self.consequence.lower(env)?; let conditions = chalk_ir::Goals::from_fallible( - self.conditions.iter().map(|g| g.lower(env)).rev(), // (*) + // Subtle: in the SLG solver, we pop conditions from R to + // L. To preserve the expected order (L to R), we must + // therefore reverse. + self.conditions.iter().map(|g| g.lower(env)).rev(), )?; - // (*) Subtle: in the SLG solver, we pop conditions from R to - // L. To preserve the expected order (L to R), we must - // therefore reverse. - let implications = consequences .into_iter() .map(|consequence| chalk_ir::ProgramClauseImplication { diff --git a/chalk-ir/src/lib.rs b/chalk-ir/src/lib.rs index 46bc88c3e75..aea1b64578e 100644 --- a/chalk-ir/src/lib.rs +++ b/chalk-ir/src/lib.rs @@ -1011,12 +1011,16 @@ impl UCanonical { } #[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, HasTypeFamily)] -/// A general goal; this is the full range of questions you can pose to Chalk. +/// A list of goals. pub struct Goals { goals: Vec>, } impl Goals { + pub fn new() -> Self { + Self::from(None::>) + } + pub fn from(goals: impl IntoIterator>>) -> Self { use crate::cast::Caster; Goals { @@ -1024,10 +1028,6 @@ impl Goals { } } - pub fn new() -> Self { - Self::from(None::>) - } - pub fn from_fallible( goals: impl IntoIterator>, E>>, ) -> Result { From 68393dae865daf83af13ef647445625a34b5f27b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2020 18:30:39 -0500 Subject: [PATCH 09/10] fix fold-trait ref in book --- book/src/types/operations/fold.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/src/types/operations/fold.md b/book/src/types/operations/fold.md index 61622a992a7..24942e735c7 100644 --- a/book/src/types/operations/fold.md +++ b/book/src/types/operations/fold.md @@ -7,7 +7,7 @@ type family to another. [`Fold`]: http://rust-lang.github.io/chalk/chalk_ir/fold/trait.Fold.html -To use the fold-trait, one invokes the [`Fold::fold_with`] method, supplying some +To use the [`Fold`] trait, one invokes the [`Fold::fold_with`] method, supplying some "folder" as well as the number of "in scope binders" for that term (typically `0` to start): From 870e9c5659af32b0a220846c1dfdec96700a9fb3 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2020 18:39:47 -0500 Subject: [PATCH 10/10] remove hard-coded vec and replace with `TypeFamily` assoc type --- chalk-ir/src/family.rs | 27 +++++++++++++++++++++++++++ chalk-ir/src/lib.rs | 6 +++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/chalk-ir/src/family.rs b/chalk-ir/src/family.rs index fb1ce92b597..d7915f22249 100644 --- a/chalk-ir/src/family.rs +++ b/chalk-ir/src/family.rs @@ -1,6 +1,7 @@ use crate::tls; use crate::AliasTy; use crate::AssocTypeId; +use crate::Goal; use crate::GoalData; use crate::LifetimeData; use crate::Parameter; @@ -69,6 +70,14 @@ pub trait TypeFamily: Debug + Copy + Eq + Ord + Hash { /// converted back to its underlying data via `goal_data`. type InternedGoal: Debug + Clone + Eq + Ord + Hash; + /// "Interned" representation of a list of goals. In normal user code, + /// `Self::InternedGoals` is not referenced. Instead, we refer to + /// `Goals`, which wraps this type. + /// + /// An `InternedGoals` is created by `intern_goals` and can be + /// converted back to its underlying data via `goals_data`. + type InternedGoals: Debug + Clone + Eq + Ord + Hash; + /// "Interned" representation of a "substitution". In normal user code, /// `Self::InternedSubstitution` is not referenced. Instead, we refer to /// `Substitution`, which wraps this type. @@ -155,6 +164,15 @@ pub trait TypeFamily: Debug + Copy + Eq + Ord + Hash { /// Lookup the `GoalData` that was interned to create a `InternedGoal`. fn goal_data(goal: &Self::InternedGoal) -> &GoalData; + /// Create an "interned" goals from `data`. This is not + /// normally invoked directly; instead, you invoke + /// `GoalsData::intern` (which will ultimately call this + /// method). + fn intern_goals(data: impl IntoIterator>) -> Self::InternedGoals; + + /// Lookup the `GoalsData` that was interned to create a `InternedGoals`. + fn goals_data(goals: &Self::InternedGoals) -> &[Goal]; + /// Create an "interned" substitution from `data`. This is not /// normally invoked directly; instead, you invoke /// `SubstitutionData::intern` (which will ultimately call this @@ -198,6 +216,7 @@ impl TypeFamily for ChalkIr { type InternedLifetime = LifetimeData; type InternedParameter = ParameterData; type InternedGoal = Arc>; + type InternedGoals = Vec>; type InternedSubstitution = Vec>; type DefId = RawId; @@ -258,6 +277,14 @@ impl TypeFamily for ChalkIr { goal } + fn intern_goals(data: impl IntoIterator>) -> Vec> { + data.into_iter().collect() + } + + fn goals_data(goals: &Vec>) -> &[Goal] { + goals + } + fn intern_substitution( data: impl IntoIterator, E>>, ) -> Result>, E> { diff --git a/chalk-ir/src/lib.rs b/chalk-ir/src/lib.rs index aea1b64578e..31e967c01b1 100644 --- a/chalk-ir/src/lib.rs +++ b/chalk-ir/src/lib.rs @@ -1013,7 +1013,7 @@ impl UCanonical { #[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, HasTypeFamily)] /// A list of goals. pub struct Goals { - goals: Vec>, + goals: TF::InternedGoals, } impl Goals { @@ -1024,7 +1024,7 @@ impl Goals { pub fn from(goals: impl IntoIterator>>) -> Self { use crate::cast::Caster; Goals { - goals: goals.into_iter().casted().collect(), + goals: TF::intern_goals(goals.into_iter().casted()), } } @@ -1052,7 +1052,7 @@ impl Goals { } pub fn as_slice(&self) -> &[Goal] { - &self.goals + TF::goals_data(&self.goals) } }