Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Projection bound validation #72788

Merged
merged 6 commits into from
Jun 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
17 changes: 17 additions & 0 deletions src/librustc_middle/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,23 @@ rustc_queries! {
cache_on_disk_if { key.is_local() }
}

/// Returns the list of predicates that can be used for
/// `SelectionCandidate::ProjectionCandidate` and
/// `ProjectionTyCandidate::TraitDef`.
matthewjasper marked this conversation as resolved.
Show resolved Hide resolved
/// 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) }
}

query native_libraries(_: CrateNum) -> Lrc<Vec<NativeLib>> {
desc { "looking up the native libraries of a linked crate" }
}
Expand Down
2 changes: 0 additions & 2 deletions src/librustc_middle/ty/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ impl FlagComputation {

&ty::Bound(debruijn, _) => {
self.add_binder(debruijn);
self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
}

&ty::Placeholder(..) => {
Expand Down Expand Up @@ -216,7 +215,6 @@ impl FlagComputation {
}
ty::ConstKind::Bound(debruijn, _) => {
self.add_binder(debruijn);
self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
}
ty::ConstKind::Param(_) => {
self.add_flags(TypeFlags::HAS_CT_PARAM);
Expand Down
3 changes: 0 additions & 3 deletions src/librustc_middle/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1589,19 +1589,16 @@ impl RegionKind {
flags = flags | TypeFlags::HAS_FREE_REGIONS;
flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS;
flags = flags | TypeFlags::HAS_RE_INFER;
flags = flags | TypeFlags::STILL_FURTHER_SPECIALIZABLE;
}
ty::RePlaceholder(..) => {
flags = flags | TypeFlags::HAS_FREE_REGIONS;
flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS;
flags = flags | TypeFlags::HAS_RE_PLACEHOLDER;
flags = flags | TypeFlags::STILL_FURTHER_SPECIALIZABLE;
}
ty::ReEarlyBound(..) => {
flags = flags | TypeFlags::HAS_FREE_REGIONS;
flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS;
flags = flags | TypeFlags::HAS_RE_PARAM;
flags = flags | TypeFlags::STILL_FURTHER_SPECIALIZABLE;
}
ty::ReFree { .. } => {
flags = flags | TypeFlags::HAS_FREE_REGIONS;
Expand Down
11 changes: 11 additions & 0 deletions src/librustc_middle/ty/subst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,17 @@ impl<'a, 'tcx> InternalSubsts<'tcx> {
/// in a different item, with `target_substs` as the base for
/// the target impl/trait, with the source child-specific
/// parameters (e.g., method parameters) on top of that base.
///
/// For example given:
///
/// trait X<S> { fn f<T>(); }
/// impl<U> X<U> for U { fn f<V>() {} }
///
/// * If `self` is `[Self, S, T]`: the identity substs of `f` in the trait.
/// * If `source_ancestor` is the def_id of the trait.
/// * If `target_substs` is `[U]`, the substs for the impl.
/// * Then we will return `[U, T]`, the subst for `f` in the impl that
/// are needed for it to match the trait.
pub fn rebase_onto(
&self,
tcx: TyCtxt<'tcx>,
Expand Down
13 changes: 8 additions & 5 deletions src/librustc_resolve/late/lifetimes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -883,7 +883,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
})
.collect();
if !lifetimes.is_empty() {
self.trait_ref_hack = true;
let next_early_index = self.next_early_index();
let scope = Scope::Binder {
lifetimes,
Expand All @@ -895,9 +894,10 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
let result = self.with(scope, |old_scope, this| {
this.check_lifetime_params(old_scope, &bound_generic_params);
this.visit_ty(&bounded_ty);
this.trait_ref_hack = true;
walk_list!(this, visit_param_bound, bounds);
this.trait_ref_hack = false;
});
self.trait_ref_hack = false;
result
} else {
self.visit_ty(&bounded_ty);
Expand Down Expand Up @@ -932,13 +932,15 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
debug!("visit_poly_trait_ref(trait_ref={:?})", trait_ref);

let should_pop_missing_lt = self.is_trait_ref_fn_scope(trait_ref);
if !self.trait_ref_hack

let trait_ref_hack = take(&mut self.trait_ref_hack);
if !trait_ref_hack
|| trait_ref.bound_generic_params.iter().any(|param| match param.kind {
GenericParamKind::Lifetime { .. } => true,
_ => false,
})
{
if self.trait_ref_hack {
if trait_ref_hack {
struct_span_err!(
self.tcx.sess,
trait_ref.span,
Expand Down Expand Up @@ -968,10 +970,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
this.check_lifetime_params(old_scope, &trait_ref.bound_generic_params);
walk_list!(this, visit_generic_param, trait_ref.bound_generic_params);
this.visit_trait_ref(&trait_ref.trait_ref);
})
});
} else {
self.visit_trait_ref(&trait_ref.trait_ref);
}
self.trait_ref_hack = trait_ref_hack;
if should_pop_missing_lt {
self.missing_named_lifetime_spots.pop();
}
Expand Down
21 changes: 13 additions & 8 deletions src/librustc_trait_selection/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -896,9 +896,12 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>(

let tcx = selcx.tcx();
// Check whether the self-type is itself a projection.
let (def_id, substs) = match obligation_trait_ref.self_ty().kind {
ty::Projection(ref data) => (data.trait_ref(tcx).def_id, data.substs),
ty::Opaque(def_id, substs) => (def_id, substs),
// If so, extract what we know from the trait and try to come up with a good answer.
let bounds = match obligation_trait_ref.self_ty().kind {
ty::Projection(ref data) => {
tcx.projection_predicates(data.item_def_id).subst(tcx, data.substs)
}
ty::Opaque(def_id, substs) => tcx.projection_predicates(def_id).subst(tcx, substs),
ty::Infer(ty::TyVar(_)) => {
// If the self-type is an inference variable, then it MAY wind up
// being a projected type, so induce an ambiguity.
Expand All @@ -908,17 +911,13 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>(
_ => return,
};

// If so, extract what we know from the trait and try to come up with a good answer.
let trait_predicates = tcx.predicates_of(def_id);
let bounds = trait_predicates.instantiate(tcx, substs);
let bounds = elaborate_predicates(tcx, bounds.predicates.into_iter()).map(|o| o.predicate);
assemble_candidates_from_predicates(
selcx,
obligation,
obligation_trait_ref,
candidate_set,
ProjectionTyCandidate::TraitDef,
bounds,
bounds.iter(),
)
}

Expand Down Expand Up @@ -1484,6 +1483,12 @@ fn confirm_impl_candidate<'cx, 'tcx>(
);
return Progress { ty: tcx.ty_error(), obligations: nested };
}
// If we're trying to normalize `<Vec<u32> as X>::A<S>` using
//`impl<T> X for Vec<T> { type A<Y> = Box<Y>; }`, then:
//
// * `obligation.predicate.substs` is `[Vec<u32>, S]`
// * `substs` is `[u32]`
// * `substs` ends up as `[u32, S]`
nikomatsakis marked this conversation as resolved.
Show resolved Hide resolved
let substs = obligation.predicate.substs.rebase_onto(tcx, trait_def_id, substs);
let substs =
translate_substs(selcx.infcx(), param_env, impl_def_id, substs, assoc_ty.defining_node);
Expand Down
50 changes: 22 additions & 28 deletions src/librustc_trait_selection/traits/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1273,9 +1273,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
placeholder_trait_predicate,
);

let (def_id, substs) = match placeholder_trait_predicate.trait_ref.self_ty().kind {
ty::Projection(ref data) => (data.trait_ref(self.tcx()).def_id, data.substs),
ty::Opaque(def_id, substs) => (def_id, substs),
let tcx = self.infcx.tcx;
let predicates = match placeholder_trait_predicate.trait_ref.self_ty().kind {
ty::Projection(ref data) => {
tcx.projection_predicates(data.item_def_id).subst(tcx, data.substs)
}
ty::Opaque(def_id, substs) => tcx.projection_predicates(def_id).subst(tcx, substs),
_ => {
span_bug!(
obligation.cause.span,
Expand All @@ -1285,32 +1288,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
);
}
};
debug!(
"match_projection_obligation_against_definition_bounds: \
def_id={:?}, substs={:?}",
def_id, substs
);

let predicates_of = self.tcx().predicates_of(def_id);
let bounds = predicates_of.instantiate(self.tcx(), substs);
debug!(
"match_projection_obligation_against_definition_bounds: \
bounds={:?}",
bounds
);

let elaborated_predicates =
util::elaborate_predicates(self.tcx(), bounds.predicates.into_iter());
let matching_bound = elaborated_predicates.filter_to_traits().find(|bound| {
self.infcx.probe(|_| {
self.match_projection(
obligation,
*bound,
placeholder_trait_predicate.trait_ref,
&placeholder_map,
snapshot,
)
})
let matching_bound = predicates.iter().find_map(|bound| {
if let ty::PredicateKind::Trait(bound, _) = bound.kind() {
let bound = bound.to_poly_trait_ref();
if self.infcx.probe(|_| {
self.match_projection(
obligation,
bound,
placeholder_trait_predicate.trait_ref,
&placeholder_map,
snapshot,
)
}) {
return Some(bound);
}
}
None
});

debug!(
Expand Down
Loading