Skip to content

Commit

Permalink
Move bounds on associated types to the type
Browse files Browse the repository at this point in the history
Given `trait X { type U; }` the bound `<Self as X>::U` now lives
on the type, rather than the trait. This is feature gated on
`feature(generic_associated_types)` for now until more testing can
be done.

The also enabled type-generic associated types since we no longer
need "implies bounds".
  • Loading branch information
matthewjasper committed Jun 11, 2020
1 parent 373c735 commit 83c5028
Show file tree
Hide file tree
Showing 47 changed files with 343 additions and 495 deletions.
21 changes: 8 additions & 13 deletions src/librustc_infer/infer/outlives/verify.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use crate::infer::outlives::env::RegionBoundPairs;
use crate::infer::{GenericKind, VerifyBound};
use crate::traits;
use rustc_data_structures::captures::Captures;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst};
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
use rustc_middle::ty::{self, Ty, TyCtxt};

/// The `TypeOutlives` struct has the job of "lowering" a `T: 'a`
Expand Down Expand Up @@ -311,18 +310,14 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
fn region_bounds_declared_on_associated_item(
&self,
assoc_item_def_id: DefId,
) -> impl Iterator<Item = ty::Region<'tcx>> + 'cx + Captures<'tcx> {
) -> impl Iterator<Item = ty::Region<'tcx>> {
let tcx = self.tcx;
let assoc_item = tcx.associated_item(assoc_item_def_id);
let trait_def_id = assoc_item.container.assert_trait();
let trait_predicates = tcx.predicates_of(trait_def_id).predicates.iter().map(|(p, _)| *p);
let identity_substs = InternalSubsts::identity_for_item(tcx, assoc_item_def_id);
let identity_proj = tcx.mk_projection(assoc_item_def_id, identity_substs);
self.collect_outlives_from_predicate_list(
move |ty| ty == identity_proj,
traits::elaborate_predicates(tcx, trait_predicates).map(|o| o.predicate),
)
.map(|b| b.1)
let predicates = tcx.projection_predicates(assoc_item_def_id);
predicates
.into_iter()
.filter_map(|p| p.to_opt_type_outlives())
.filter_map(|p| p.no_bound_vars())
.map(|b| b.1)
}

/// Searches through a predicate list for a predicate `T: 'a`.
Expand Down
2 changes: 0 additions & 2 deletions src/librustc_infer/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ pub use self::project::{
Normalized, NormalizedTy, ProjectionCache, ProjectionCacheEntry, ProjectionCacheKey,
ProjectionCacheStorage, Reveal,
};
crate use self::util::elaborate_predicates;

pub use rustc_middle::traits::*;

/// An `Obligation` represents some trait reference (e.g., `int: Eq`) for
Expand Down
10 changes: 10 additions & 0 deletions src/librustc_middle/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,16 @@ rustc_queries! {
/// Returns the list of predicates that can be used for
/// `SelectionCandidate::ProjectionCandidate` and
/// `ProjectionTyCandidate::TraitDef`.
/// Specifically this is the bounds (equivalent to) those
/// written on the trait's type definition, or those
/// after the `impl` keyword
///
/// type X: Bound + 'lt
/// ^^^^^^^^^^^
/// impl Debug + Display
/// ^^^^^^^^^^^^^^^
///
/// `key` is the `DefId` of the associated type or opaque type.
query projection_predicates(key: DefId) -> &'tcx ty::List<ty::Predicate<'tcx>> {
desc { |tcx| "finding projection predicates for `{}`", tcx.def_path_str(key) }
}
Expand Down
61 changes: 45 additions & 16 deletions src/librustc_ty/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,34 +373,45 @@ fn asyncness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::IsAsync {
/// (`type X: Trait`) to be used as candidates. We also allow the same bounds
/// when desugared as bounds on the trait `where Self::X: Trait`.
///
/// Note that this filtering is done with the trait's identity substs to
/// Note that this filtering is done with the items identity substs to
/// simplify checking that these bounds are met in impls. This means that
/// a bound such as `for<'b> <Self as X<'b>>::U: Clone` can't be used, as in
/// `hr-associated-type-bound-1.rs`.
fn associated_type_projection_predicates(
tcx: TyCtxt<'_>,
def_id: DefId,
assoc_item_def_id: DefId,
) -> &'_ ty::List<ty::Predicate<'_>> {
let trait_id = tcx.associated_item(def_id).container.id();
let trait_substs = InternalSubsts::identity_for_item(tcx, trait_id);

let generic_trait_bounds = tcx.predicates_of(trait_id);
let trait_bounds = generic_trait_bounds.instantiate_identity(tcx);
let trait_predicates = util::elaborate_predicates(tcx, trait_bounds.predicates.into_iter());
let generic_trait_bounds = tcx.predicates_of(assoc_item_def_id);
// We include predicates from the trait as well to handle
// `where Self::X: Trait`.
let item_bounds = generic_trait_bounds.instantiate_identity(tcx);
let item_predicates = util::elaborate_predicates(tcx, item_bounds.predicates.into_iter());

let assoc_item_ty = ty::ProjectionTy {
item_def_id: assoc_item_def_id,
substs: InternalSubsts::identity_for_item(tcx, assoc_item_def_id),
};

let predicates = trait_predicates.filter_map(|obligation| {
let predicates = item_predicates.filter_map(|obligation| {
let pred = obligation.predicate;
match pred.kind() {
ty::PredicateKind::Trait(tr, _) => {
if let ty::Projection(p) = tr.skip_binder().self_ty().kind {
if p.item_def_id == def_id && p.substs.starts_with(trait_substs) {
if p == assoc_item_ty {
return Some(pred);
}
}
}
ty::PredicateKind::Projection(proj) => {
if let ty::Projection(p) = proj.skip_binder().projection_ty.self_ty().kind {
if p.item_def_id == def_id && p.substs.starts_with(trait_substs) {
if p == assoc_item_ty {
return Some(pred);
}
}
}
ty::PredicateKind::TypeOutlives(outlives) => {
if let ty::Projection(p) = outlives.skip_binder().0.kind {
if p == assoc_item_ty {
return Some(pred);
}
}
Expand All @@ -411,21 +422,25 @@ fn associated_type_projection_predicates(
});

let result = tcx.mk_predicates(predicates);
debug!("associated_type_projection_predicates({}) = {:?}", tcx.def_path_str(def_id), result);
debug!(
"associated_type_projection_predicates({}) = {:?}",
tcx.def_path_str(assoc_item_def_id),
result
);
result
}

/// Opaque types might not have the same issues as associated types, but we use a
/// Opaque types don't have the same issues as associated types, but we use a
/// similar filtering for consistency.
fn opaque_type_projection_predicates(
tcx: TyCtxt<'_>,
def_id: DefId,
) -> &'_ ty::List<ty::Predicate<'_>> {
let substs = InternalSubsts::identity_for_item(tcx, def_id);

let generics_bounds = tcx.predicates_of(def_id);
let bounds = generics_bounds.instantiate_identity(tcx);
let predicates = util::elaborate_predicates(tcx, bounds.predicates.into_iter());
let bounds = tcx.predicates_of(def_id);
let predicates =
util::elaborate_predicates(tcx, bounds.predicates.into_iter().map(|&(pred, _)| pred));

let filtered_predicates = predicates.filter_map(|obligation| {
let pred = obligation.predicate;
Expand All @@ -446,8 +461,22 @@ fn opaque_type_projection_predicates(
}
}
}
ty::PredicateKind::TypeOutlives(outlives) => {
if let ty::Opaque(opaque_def_id, opaque_substs) = outlives.skip_binder().0.kind {
if opaque_def_id == def_id && opaque_substs == substs {
return Some(pred);
}
} else {
// These can come from elaborating other predicates
return None;
}
}
// These can come from elaborating other predicates
ty::PredicateKind::RegionOutlives(_) => return None,
_ => {}
}
// Other predicates are currently only possible for type alias impl
// trait. After #72080 is merged this can be a (delayed?) bug.
None
});

Expand Down
44 changes: 14 additions & 30 deletions src/librustc_typeck/check/compare_method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1185,16 +1185,18 @@ fn compare_projection_bounds<'tcx>(
impl_ty_span: Span,
impl_trait_ref: ty::TraitRef<'tcx>,
) -> Result<(), ErrorReported> {
let is_gat = !tcx.generics_of(impl_ty.def_id).params.is_empty();
if impl_ty.defaultness.is_final() && !is_gat {
let have_gats = tcx.features().generic_associated_types;
if impl_ty.defaultness.is_final() && !have_gats {
// For "final", non-generic associate type implementations, we
// don't need this as described above.
return Ok(());
}

let param_env = tcx.param_env(impl_ty.def_id);

let impl_substs = InternalSubsts::identity_for_item(tcx, impl_ty.container.id());
let impl_ty_substs = InternalSubsts::identity_for_item(tcx, impl_ty.def_id);
let rebased_substs =
impl_ty_substs.rebase_onto(tcx, impl_ty.container.id(), impl_trait_ref.substs);
let impl_ty_value = tcx.type_of(impl_ty.def_id);

// Map the predicate from the trait to the corresponding one for the impl.
Expand All @@ -1207,32 +1209,9 @@ fn compare_projection_bounds<'tcx>(
// function would translate and partially normalize
// `[<Self as X<A>>::Y<'a>, A]` to `[&'a u32, &'x u32]`.
let translate_predicate_substs = move |predicate_substs: SubstsRef<'tcx>| {
let normalized_self = if !is_gat {
// projection_predicates only includes projections where the
// substs of the trait ref are exactly the trait's identity
// substs, so we can simply return the value from the impl.
impl_ty_value
} else {
let predicate_self_ty = predicate_substs.type_at(0);
let impl_ty_substs = if let ty::Projection(p) = predicate_self_ty.kind {
assert!(
p.item_def_id == trait_ty.def_id,
"projection_predicates returned predicate for the wrong type: {}",
predicate_self_ty,
);
p.substs.rebase_onto(tcx, impl_trait_ref.def_id, impl_substs)
} else {
bug!(
"projection_predicates returned predicate for the wrong type `{}`",
predicate_self_ty,
);
};
impl_ty_value.subst(tcx, impl_ty_substs)
};

tcx.mk_substs(
iter::once(normalized_self.into())
.chain(predicate_substs[1..].iter().map(|s| s.subst(tcx, impl_trait_ref.substs))),
iter::once(impl_ty_value.into())
.chain(predicate_substs[1..].iter().map(|s| s.subst(tcx, rebased_substs))),
)
};

Expand All @@ -1246,7 +1225,7 @@ fn compare_projection_bounds<'tcx>(
let cause = ObligationCause {
span: impl_ty_span,
body_id: impl_ty_hir_id,
code: ObligationCauseCode::ItemObligation(impl_trait_ref.def_id),
code: ObligationCauseCode::ItemObligation(trait_ty.def_id),
};

let predicates = tcx.projection_predicates(trait_ty.def_id);
Expand All @@ -1271,10 +1250,15 @@ fn compare_projection_bounds<'tcx>(
substs: projection_substs,
item_def_id: projection.projection_ty.item_def_id,
},
ty: projection.ty.subst(tcx, impl_trait_ref.substs),
ty: projection.ty.subst(tcx, rebased_substs),
}
})
.to_predicate(tcx),
ty::PredicateKind::TypeOutlives(poly_outlives) => poly_outlives
.map_bound(|outlives| {
ty::OutlivesPredicate(impl_ty_value, outlives.1.subst(tcx, rebased_substs))
})
.to_predicate(tcx),
_ => bug!("unexepected projection predicate kind: `{:?}`", predicate),
};

Expand Down
Loading

0 comments on commit 83c5028

Please sign in to comment.