diff --git a/chalk-solve/src/clauses.rs b/chalk-solve/src/clauses.rs index e3fdd351b7a..705b714c5f7 100644 --- a/chalk-solve/src/clauses.rs +++ b/chalk-solve/src/clauses.rs @@ -624,7 +624,10 @@ pub fn program_clauses_that_could_match( if let Some(well_known) = trait_datum.well_known { builtin_traits::add_builtin_assoc_program_clauses( - db, builder, well_known, self_ty, + db, + builder, + well_known, + self_ty.clone(), )?; } @@ -645,6 +648,18 @@ pub fn program_clauses_that_could_match( proj.associated_ty_id, ); } + + // When `self_ty` is dyn type or opaque type, there may be associated type bounds + // for which we generate `Normalize` clauses. + match self_ty.kind(interner) { + // FIXME: see the fixme for the analogous code for Implemented goals. + TyKind::Dyn(_) => dyn_ty::build_dyn_self_ty_clauses(db, builder, self_ty), + TyKind::OpaqueType(id, _) => { + db.opaque_ty_data(*id) + .to_program_clauses(builder, environment); + } + _ => {} + } } AliasTy::Opaque(_) => (), }, diff --git a/chalk-solve/src/clauses/super_traits.rs b/chalk-solve/src/clauses/super_traits.rs index 3110b03e8d1..c6040b6dc28 100644 --- a/chalk-solve/src/clauses/super_traits.rs +++ b/chalk-solve/src/clauses/super_traits.rs @@ -1,34 +1,43 @@ +use itertools::{Either, Itertools}; use rustc_hash::FxHashSet; use super::builder::ClauseBuilder; -use crate::RustIrDatabase; +use crate::{split::Split, RustIrDatabase}; use chalk_ir::{ - fold::shift::Shift, interner::Interner, Binders, BoundVar, DebruijnIndex, TraitId, TraitRef, - WhereClause, + fold::shift::Shift, interner::Interner, AliasEq, AliasTy, Binders, BoundVar, DebruijnIndex, + Normalize, ProjectionTy, TraitId, TraitRef, Ty, 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.) +/// Generate `Implemented` and `Normalize` clauses for `dyn Trait` and opaque types. +/// We need to generate those 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 + // 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 = + // + // When `SuperTrait` has `AliasEq` bounds like `trait SuperTrait: AnotherTrait`, + // we also push + // - for `dyn Trait`: + // `Normalize(::Assoc -> Ty) :- AssocWC, WC` + // - for placeholder `!T` of `opaque type T: Trait = HiddenTy`: + // `Normalize(::Assoc -> Ty) :- AssocWC, WC` + // where `WC` and `AssocWC` are the where clauses for `AnotherTrait` and `AnotherTrait::Assoc` + // respectively. + let (super_trait_refs, super_trait_proj) = 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| { + builder.push_binders(q_super_trait_ref, |builder, super_trait_ref| { let trait_datum = db.trait_datum(super_trait_ref.trait_id); let wc = trait_datum .where_clauses() @@ -37,12 +46,40 @@ pub(super) fn push_trait_super_clauses( builder.push_clause(super_trait_ref, wc); }); } + + for q_super_trait_proj in super_trait_proj { + builder.push_binders(q_super_trait_proj, |builder, (proj, ty)| { + let assoc_ty_datum = db.associated_ty_data(proj.associated_ty_id); + let trait_datum = db.trait_datum(assoc_ty_datum.trait_id); + let assoc_wc = assoc_ty_datum + .binders + .map_ref(|b| &b.where_clauses) + .into_iter() + .map(|wc| wc.cloned().substitute(interner, &proj.substitution)); + + let impl_params = db.trait_parameters_from_projection(&proj); + let impl_wc = trait_datum + .where_clauses() + .into_iter() + .map(|wc| wc.cloned().substitute(interner, impl_params)); + builder.push_clause( + Normalize { + alias: AliasTy::Projection(proj.clone()), + ty, + }, + impl_wc.chain(assoc_wc), + ); + }); + } } -pub fn super_traits( +fn super_traits( db: &dyn RustIrDatabase, trait_id: TraitId, -) -> Binders>>> { +) -> Binders<( + Vec>>, + Vec, Ty)>>, +)> { let interner = db.interner(); let mut seen_traits = FxHashSet::default(); let trait_datum = db.trait_datum(trait_id); @@ -57,13 +94,21 @@ pub fn super_traits( }, ); let mut trait_refs = Vec::new(); - go(db, trait_ref, &mut seen_traits, &mut trait_refs); + let mut aliases = Vec::new(); + go( + db, + trait_ref, + &mut seen_traits, + &mut trait_refs, + &mut aliases, + ); fn go( db: &dyn RustIrDatabase, trait_ref: Binders>, seen_traits: &mut FxHashSet>, trait_refs: &mut Vec>>, + aliases: &mut Vec, Ty)>>, ) { let interner = db.interner(); let trait_id = trait_ref.skip_binders().trait_id; @@ -73,32 +118,39 @@ pub fn super_traits( } trait_refs.push(trait_ref.clone()); let trait_datum = db.trait_datum(trait_id); - let super_trait_refs = trait_datum + let (super_trait_refs, super_trait_projs): (Vec<_>, Vec<_>) = 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()); + .filter(|qwc| { + let trait_ref = match qwc.skip_binders() { + WhereClause::Implemented(tr) => tr.clone(), + WhereClause::AliasEq(AliasEq { + alias: AliasTy::Projection(p), + .. + }) => db.trait_ref_from_projection(p), + _ => return false, + }; + // We're looking for where clauses of the form + // `Self: Trait` or `::Assoc`. `Self` is + // ^1.0 because we're one binder in. + trait_ref.self_type_parameter(interner).bound_var(interner) + == Some(BoundVar::new(DebruijnIndex::ONE, 0)) + }) + .cloned() + .partition_map(|qwc| { + let (value, binders) = qwc.into_value_and_skipped_binders(); - // 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, - }) + match value { + WhereClause::Implemented(tr) => Either::Left(Binders::new(binders, tr)), + WhereClause::AliasEq(AliasEq { + alias: AliasTy::Projection(p), + ty, + }) => Either::Right(Binders::new(binders, (p, ty))), + _ => unreachable!(), + } }) - .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 @@ -109,10 +161,15 @@ pub fn super_traits( // 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); + go(db, q_super_trait_ref, seen_traits, trait_refs, aliases); + } + for q_super_trait_proj in super_trait_projs { + let actual_binders = Binders::new(trait_ref.binders.clone(), q_super_trait_proj); + let q_super_trait_proj = actual_binders.fuse_binders(interner); + aliases.push(q_super_trait_proj); } seen_traits.remove(&trait_id); } - Binders::new(trait_datum.binders.binders.clone(), trait_refs) + Binders::new(trait_datum.binders.binders.clone(), (trait_refs, aliases)) } diff --git a/tests/test/existential_types.rs b/tests/test/existential_types.rs index d6e23d49398..cf9be75bbdc 100644 --- a/tests/test/existential_types.rs +++ b/tests/test/existential_types.rs @@ -406,6 +406,31 @@ fn dyn_associated_type_binding() { } } +#[test] +fn dyn_assoc_in_super_trait_bounds() { + test! { + program { + trait Base { type Output; } + trait Trait where Self: Base {} + } + + goal { + forall<'s> { + dyn Trait + 's: Trait + } + } yields { + expect![[r#"Unique"#]] + } + + goal { + forall<'s> { + dyn Trait + 's: Base + } + } yields { + expect![[r#"Unique"#]] + } + } +} #[test] fn dyn_well_formed() { test! { diff --git a/tests/test/opaque_types.rs b/tests/test/opaque_types.rs index 767db8c8a15..6821ae53222 100644 --- a/tests/test/opaque_types.rs +++ b/tests/test/opaque_types.rs @@ -283,3 +283,33 @@ fn opaque_super_trait() { } } } + +#[test] +fn opaque_assoc_in_super_trait_bounds() { + test! { + program { + trait Foo { + type A; + } + trait EmptyFoo where Self: Foo { } + impl Foo for i32 { + type A = (); + } + impl EmptyFoo for T where T: Foo { } + + opaque type T: EmptyFoo = i32; + } + + goal { + T: EmptyFoo + } yields { + expect![[r#"Unique"#]] + } + + goal { + T: Foo + } yields { + expect![[r#"Unique"#]] + } + } +} diff --git a/tests/test/projection.rs b/tests/test/projection.rs index a98a660dfed..c212cd27765 100644 --- a/tests/test/projection.rs +++ b/tests/test/projection.rs @@ -1134,3 +1134,34 @@ fn projection_to_opaque() { } } } + +#[test] +fn projection_from_super_trait_bounds() { + test! { + program { + trait Foo { + type A; + } + trait Bar where Self: Foo {} + impl Foo for i32 { + type A = (); + } + impl Bar for i32 {} + opaque type Opaque: Bar = i32; + } + + goal { + forall<'a> { + ::A = () + } + } yields { + expect![[r#"Unique"#]] + } + + goal { + ::A = () + } yields { + expect![[r#"Unique"#]] + } + } +}