diff --git a/chalk-ir/src/could_match.rs b/chalk-ir/src/could_match.rs index c70a3be3f04..e0da91e34e2 100644 --- a/chalk-ir/src/could_match.rs +++ b/chalk-ir/src/could_match.rs @@ -33,7 +33,7 @@ where struct MatchZipper<'i, I> { interner: &'i I, db: &'i dyn UnificationDatabase, - }; + } impl<'i, I: Interner> Zipper<'i, I> for MatchZipper<'i, I> { fn zip_tys(&mut self, variance: Variance, a: &Ty, b: &Ty) -> Fallible<()> { diff --git a/chalk-solve/src/clauses.rs b/chalk-solve/src/clauses.rs index ea6851338e2..cbe78632a05 100644 --- a/chalk-solve/src/clauses.rs +++ b/chalk-solve/src/clauses.rs @@ -18,6 +18,7 @@ mod dyn_ty; mod env_elaborator; mod generalize; pub mod program_clauses; +mod super_traits; // yields the types "contained" in `app_ty` fn constituent_types(db: &dyn RustIrDatabase, ty: &TyKind) -> Vec> { diff --git a/chalk-solve/src/clauses/dyn_ty.rs b/chalk-solve/src/clauses/dyn_ty.rs index 33b85a427e0..505da43f972 100644 --- a/chalk-solve/src/clauses/dyn_ty.rs +++ b/chalk-solve/src/clauses/dyn_ty.rs @@ -1,11 +1,6 @@ -use rustc_hash::FxHashSet; - use super::{builder::ClauseBuilder, generalize}; use crate::RustIrDatabase; -use chalk_ir::{ - cast::Cast, fold::shift::Shift, interner::Interner, Binders, BoundVar, DebruijnIndex, TraitId, - TraitRef, Ty, TyKind, WhereClause, -}; +use chalk_ir::{cast::Cast, interner::Interner, Ty, TyKind, WhereClause}; /// If the self type `S` of an `Implemented` goal is a `dyn trait` type, we wish /// to generate program-clauses that indicates that it implements its own @@ -67,123 +62,20 @@ pub(super) fn build_dyn_self_ty_clauses( .cloned() .substitute(interner, &[self_ty.clone().cast(interner)]); - builder.push_binders(qwc, |builder, wc| match &wc { + builder.push_binders(qwc, |builder, bound| match &bound { // For the implemented traits, we need to elaborate super traits and add where clauses from the trait WhereClause::Implemented(trait_ref) => { - push_dyn_ty_impl_clauses(db, builder, trait_ref.clone()) + super::super_traits::push_trait_super_clauses( + builder.db, + builder, + trait_ref.clone(), + ) } - // Associated item bindings are just taken as facts (?) - WhereClause::AliasEq(_) => builder.push_fact(wc), + // FIXME: Associated item bindings are just taken as facts (?) + WhereClause::AliasEq(_) => builder.push_fact(bound), WhereClause::LifetimeOutlives(..) => {} WhereClause::TypeOutlives(..) => {} }); } }); } - -/// Generate `Implemented` clauses for a `dyn Trait` type. We need to generate -/// `Implemented` clauses for all super traits, and for each trait we require -/// its where clauses. (See #203.) -fn push_dyn_ty_impl_clauses( - db: &dyn RustIrDatabase, - builder: &mut ClauseBuilder<'_, I>, - trait_ref: TraitRef, -) { - let interner = db.interner(); - // We have some `dyn Trait`, and some `trait SuperTrait: WC` - // which is a super trait of `Trait` (including actually - // just being the same trait); then we want to push - // `Implemented(dyn Trait: SuperTrait) :- WC`. - - let super_trait_refs = - super_traits(db, trait_ref.trait_id).substitute(interner, &trait_ref.substitution); - - for q_super_trait_ref in super_trait_refs { - builder.push_binders(q_super_trait_ref.clone(), |builder, super_trait_ref| { - let trait_datum = db.trait_datum(super_trait_ref.trait_id); - let wc = trait_datum - .where_clauses() - .cloned() - .substitute(interner, &super_trait_ref.substitution); - builder.push_clause(super_trait_ref, wc); - }); - } -} - -pub fn super_traits( - db: &dyn RustIrDatabase, - trait_id: TraitId, -) -> Binders>>> { - let interner = db.interner(); - let mut seen_traits = FxHashSet::default(); - let trait_datum = db.trait_datum(trait_id); - let trait_ref = Binders::empty( - db.interner(), - TraitRef { - trait_id, - substitution: trait_datum - .binders - .identity_substitution(interner) - .shifted_in(interner), - }, - ); - let mut trait_refs = Vec::new(); - go(db, trait_ref, &mut seen_traits, &mut trait_refs); - - fn go( - db: &dyn RustIrDatabase, - trait_ref: Binders>, - seen_traits: &mut FxHashSet>, - trait_refs: &mut Vec>>, - ) { - let interner = db.interner(); - let trait_id = trait_ref.skip_binders().trait_id; - // Avoid cycles - if !seen_traits.insert(trait_id) { - return; - } - trait_refs.push(trait_ref.clone()); - let trait_datum = db.trait_datum(trait_id); - let super_trait_refs = trait_datum - .binders - .map_ref(|td| { - td.where_clauses - .iter() - .filter_map(|qwc| { - qwc.as_ref().filter_map(|wc| match wc { - WhereClause::Implemented(tr) => { - let self_ty = tr.self_type_parameter(db.interner()); - - // We're looking for where clauses - // of the form `Self: Trait`. That's - // ^1.0 because we're one binder in. - if self_ty.bound_var(db.interner()) - != Some(BoundVar::new(DebruijnIndex::ONE, 0)) - { - return None; - } - Some(tr.clone()) - } - WhereClause::AliasEq(_) => None, - WhereClause::LifetimeOutlives(..) => None, - WhereClause::TypeOutlives(..) => None, - }) - }) - .collect::>() - }) - // we skip binders on the trait_ref here and add them to the binders - // on the trait ref in the loop below. We could probably avoid this if - // we could turn the `Binders>` into a `Vec>` easily. - .substitute(db.interner(), &trait_ref.skip_binders().substitution); - for q_super_trait_ref in super_trait_refs { - // So now we need to combine the binders of trait_ref with the - // binders of super_trait_ref. - let actual_binders = Binders::new(trait_ref.binders.clone(), q_super_trait_ref); - let q_super_trait_ref = actual_binders.fuse_binders(interner); - go(db, q_super_trait_ref, seen_traits, trait_refs); - } - seen_traits.remove(&trait_id); - } - - Binders::new(trait_datum.binders.binders.clone(), trait_refs) -} diff --git a/chalk-solve/src/clauses/program_clauses.rs b/chalk-solve/src/clauses/program_clauses.rs index 736684bc721..19811ff8b30 100644 --- a/chalk-solve/src/clauses/program_clauses.rs +++ b/chalk-solve/src/clauses/program_clauses.rs @@ -195,10 +195,20 @@ impl ToProgramClauses for OpaqueTyDatum { let substitution = Substitution::from1(interner, alias_placeholder_ty); for bound in opaque_ty_bound.bounds { - // Implemented(!T<..>: Bound). let bound_with_placeholder_ty = bound.substitute(interner, &substitution); - builder.push_binders(bound_with_placeholder_ty, |builder, bound| { - builder.push_fact(bound); + builder.push_binders(bound_with_placeholder_ty, |builder, bound| match &bound { + // For the implemented traits, we need to elaborate super traits and add where clauses from the trait + WhereClause::Implemented(trait_ref) => { + super::super_traits::push_trait_super_clauses( + builder.db, + builder, + trait_ref.clone(), + ) + } + // FIXME: Associated item bindings are just taken as facts (?) + WhereClause::AliasEq(_) => builder.push_fact(bound), + WhereClause::LifetimeOutlives(..) => {} + WhereClause::TypeOutlives(..) => {} }); } }); diff --git a/chalk-solve/src/clauses/super_traits.rs b/chalk-solve/src/clauses/super_traits.rs new file mode 100644 index 00000000000..3110b03e8d1 --- /dev/null +++ b/chalk-solve/src/clauses/super_traits.rs @@ -0,0 +1,118 @@ +use rustc_hash::FxHashSet; + +use super::builder::ClauseBuilder; +use crate::RustIrDatabase; +use chalk_ir::{ + fold::shift::Shift, interner::Interner, Binders, BoundVar, DebruijnIndex, TraitId, TraitRef, + WhereClause, +}; + +/// Generate `Implemented` clauses for `dyn Trait` and opaque types. We need to generate +/// `Implemented` clauses for all super traits, and for each trait we require +/// its where clauses. (See #203.) +pub(super) fn push_trait_super_clauses( + db: &dyn RustIrDatabase, + builder: &mut ClauseBuilder<'_, I>, + trait_ref: TraitRef, +) { + let interner = db.interner(); + // Given`trait SuperTrait: WC`, which is a super trait + // of `Trait` (including actually just being the same trait); + // then we want to push + // - for `dyn Trait`: + // `Implemented(dyn Trait: SuperTrait) :- WC`. + // - for placeholder `!T` of `opaque type T: Trait = HiddenTy`: + // `Implemented(!T: SuperTrait) :- WC` + + let super_trait_refs = + super_traits(db, trait_ref.trait_id).substitute(interner, &trait_ref.substitution); + + for q_super_trait_ref in super_trait_refs { + builder.push_binders(q_super_trait_ref.clone(), |builder, super_trait_ref| { + let trait_datum = db.trait_datum(super_trait_ref.trait_id); + let wc = trait_datum + .where_clauses() + .cloned() + .substitute(interner, &super_trait_ref.substitution); + builder.push_clause(super_trait_ref, wc); + }); + } +} + +pub fn super_traits( + db: &dyn RustIrDatabase, + trait_id: TraitId, +) -> Binders>>> { + let interner = db.interner(); + let mut seen_traits = FxHashSet::default(); + let trait_datum = db.trait_datum(trait_id); + let trait_ref = Binders::empty( + db.interner(), + TraitRef { + trait_id, + substitution: trait_datum + .binders + .identity_substitution(interner) + .shifted_in(interner), + }, + ); + let mut trait_refs = Vec::new(); + go(db, trait_ref, &mut seen_traits, &mut trait_refs); + + fn go( + db: &dyn RustIrDatabase, + trait_ref: Binders>, + seen_traits: &mut FxHashSet>, + trait_refs: &mut Vec>>, + ) { + let interner = db.interner(); + let trait_id = trait_ref.skip_binders().trait_id; + // Avoid cycles + if !seen_traits.insert(trait_id) { + return; + } + trait_refs.push(trait_ref.clone()); + let trait_datum = db.trait_datum(trait_id); + let super_trait_refs = trait_datum + .binders + .map_ref(|td| { + td.where_clauses + .iter() + .filter_map(|qwc| { + qwc.as_ref().filter_map(|wc| match wc { + WhereClause::Implemented(tr) => { + let self_ty = tr.self_type_parameter(db.interner()); + + // We're looking for where clauses + // of the form `Self: Trait`. That's + // ^1.0 because we're one binder in. + if self_ty.bound_var(db.interner()) + != Some(BoundVar::new(DebruijnIndex::ONE, 0)) + { + return None; + } + Some(tr.clone()) + } + WhereClause::AliasEq(_) => None, + WhereClause::LifetimeOutlives(..) => None, + WhereClause::TypeOutlives(..) => None, + }) + }) + .collect::>() + }) + // we skip binders on the trait_ref here and add them to the binders + // on the trait ref in the loop below. We could probably avoid this if + // we could turn the `Binders>` into a `Vec>` easily. + .substitute(db.interner(), &trait_ref.skip_binders().substitution); + for q_super_trait_ref in super_trait_refs { + // So now we need to combine the binders of trait_ref with the + // binders of super_trait_ref. + let actual_binders = Binders::new(trait_ref.binders.clone(), q_super_trait_ref); + let q_super_trait_ref = actual_binders.fuse_binders(interner); + go(db, q_super_trait_ref, seen_traits, trait_refs); + } + seen_traits.remove(&trait_id); + } + + Binders::new(trait_datum.binders.binders.clone(), trait_refs) +} diff --git a/tests/test/opaque_types.rs b/tests/test/opaque_types.rs index 108d465db36..fe028313ed6 100644 --- a/tests/test/opaque_types.rs +++ b/tests/test/opaque_types.rs @@ -263,3 +263,23 @@ fn opaque_auto_traits_indirect() { } } } + +#[test] +fn opaque_super_trait() { + test! { + program { + trait Base {} + trait Super where Self: Base {} + impl Base for () {} + impl Super for () {} + + opaque type Opaque: Super = (); + } + + goal { + Opaque: Base + } yields { + "Unique" + } + } +}