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

[WIP] Move Self: Trait predicate to trait items instead of the trait itself #50183

Closed
8 changes: 5 additions & 3 deletions src/librustc/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1164,6 +1164,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
}
}).collect();

debug!("winnowed to {} candidates for {:?}: {:?}",
candidates.len(),
stack,
candidates);

// If there are STILL multiple candidate, we can further
// reduce the list by dropping duplicates -- including
// resolving specializations.
Expand Down Expand Up @@ -1934,9 +1939,6 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
// attempt to evaluate recursive bounds to see if they are
// satisfied.

/// Returns true if `candidate_i` should be dropped in favor of
/// `candidate_j`. Generally speaking we will drop duplicate
/// candidates and prefer where-clause candidates.
/// Returns true if `victim` should be dropped in favor of
/// `other`. Generally speaking we will drop duplicate
/// candidates and prefer where-clause candidates.
Expand Down
23 changes: 22 additions & 1 deletion src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2718,7 +2718,7 @@ fn param_env<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
// Compute the bounds on Self and the type parameters.

let bounds = tcx.predicates_of(def_id).instantiate_identity(tcx);
let predicates = bounds.predicates;
let mut predicates = bounds.predicates;

// Finally, we have to normalize the bounds in the environment, in
// case they contain any associated type projections. This process
Expand All @@ -2732,6 +2732,11 @@ fn param_env<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
// are any errors at that point, so after type checking you can be
// sure that this will succeed without errors anyway.

if is_trait(tcx, def_id) {
// Add `Self: Trait` into the ParamEnv.
predicates.push(ty::TraitRef::identity(tcx, def_id).to_predicate());
}

let unnormalized_env = ty::ParamEnv::new(tcx.intern_predicates(&predicates),
traits::Reveal::UserFacing);

Expand All @@ -2742,6 +2747,22 @@ fn param_env<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
traits::normalize_param_env_or_error(tcx, def_id, unnormalized_env, cause)
}

pub fn is_trait<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
match tcx.def_key(def_id).disambiguated_data.data {
DefPathData::Trait(_) => true,
_ => false,
}
}

pub fn is_trait_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: NodeId) -> bool {
if let Some(hir::map::NodeItem(item)) = tcx.hir.find(id) {
if let hir::ItemTrait(..) = item.node {
return true;
}
}
false
}

fn crate_disambiguator<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
crate_num: CrateNum) -> CrateDisambiguator {
assert_eq!(crate_num, LOCAL_CRATE);
Expand Down
8 changes: 8 additions & 0 deletions src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,14 @@ impl<'tcx> TraitRef<'tcx> {
TraitRef { def_id: def_id, substs: substs }
}

/// Returns a TraitRef of the form `Self: Foo<U>`.
pub fn identity<'a, 'gcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, def_id: DefId) -> TraitRef<'tcx> {
TraitRef {
def_id,
substs: Substs::identity_for_item(tcx, def_id),
}
}

pub fn self_ty(&self) -> Ty<'tcx> {
self.substs.type_at(0)
}
Expand Down
32 changes: 30 additions & 2 deletions src/librustc/ty/wf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,11 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
/// Pushes the obligations required for `trait_ref` to be WF into
/// `self.out`.
fn compute_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>, elaborate: Elaborate) {
let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.substs);
let param_env = self.param_env;

let obligations = self.nominal_trait_obligations(trait_ref);

let cause = self.cause(traits::MiscObligation);
let param_env = self.param_env;

if let Elaborate::All = elaborate {
let predicates = obligations.iter()
Expand Down Expand Up @@ -449,6 +450,33 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
.collect()
}

fn nominal_trait_obligations(&mut self,
trait_ref: &ty::TraitRef<'tcx>)
-> Vec<traits::PredicateObligation<'tcx>>
{
// Add in a predicate that `Self:Trait` (where `Trait` is the
// current trait).
let self_trait = if !trait_ref.has_escaping_regions() {
Some(trait_ref.to_predicate())
} else {
None
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels a bit ad-hoc to me. Can you remember why you added this? Also, when does this "has escaping regions" check return false? I would think .. never ? You could add a assert!(!trait_ref.has_escaping_regions()); instead (and perhaps prove me wrong).

If I understand correctly, this is saying that, to prove that trait Foo { .. } is well-formed, we have to prove that Self: Foo? I'm not sure why that would be useful.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are indeed places where trait_ref has escaping regions. There is a call skip_binder() above (look for (*)).

I believe an example that triggers this is here:

fn takes_lifetime(_f: for<'a> fn(&'a ()) -> <() as Lifetime<'a>>::Out) {
}

If the trait ref does have escaping regions, it would get filtered out below anyway. Except that trait_ref.to_predicate() invokes ty::Binder::dummy, which has the assertion you mentioned and we get a panic. Using ty::Binder::bind is incorrect and messes up our indices.

On a related note, this fn was copied and modified from the above. I should find a way to remove the repetition.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, this is saying that, to prove that trait Foo { .. } is well-formed, we have to prove that Self: Foo? I'm not sure why that would be useful.

It gets used to check bounds on fn items as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. I'll have to double check what's going on here then, I was confused.


let predicates = self.infcx.tcx.predicates_of(trait_ref.def_id)
.instantiate(self.infcx.tcx, trait_ref.substs)
.predicates;

let cause = self.cause(traits::ItemObligation(trait_ref.def_id));

self_trait.into_iter()
.chain(predicates.into_iter())
.map(|pred| traits::Obligation::new(cause.clone(),
self.param_env,
pred))
.filter(|pred| !pred.has_escaping_regions())
.collect()
}

fn from_object_ty(&mut self, ty: Ty<'tcx>,
data: ty::Binder<&'tcx ty::Slice<ty::ExistentialPredicate<'tcx>>>,
region: ty::Region<'tcx>) {
Expand Down
4 changes: 1 addition & 3 deletions src/librustc_traits/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,10 +234,8 @@ fn program_clauses_for_trait<'a, 'tcx>(
// ```

// `FromEnv(WC) :- FromEnv(Self: Trait<P1..Pn>)`, for each where clause WC
// FIXME: Remove the [1..] slice; this is a hack because the query
// predicates_of currently includes the trait itself (`Self: Trait<P1..Pn>`).
let where_clauses = &tcx.predicates_of(def_id).predicates;
let implied_bound_clauses = where_clauses[1..]
let implied_bound_clauses = where_clauses
.into_iter()
.map(|wc| implied_bound_from_trait(tcx, trait_pred, wc));

Expand Down
29 changes: 27 additions & 2 deletions src/librustc_typeck/check/compare_method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ use super::{Inherited, FnCtxt};
/// - impl_m_span: span to use for reporting errors
/// - trait_m: the method in the trait
/// - impl_trait_ref: the TraitRef corresponding to the trait implementation

pub fn compare_impl_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
impl_m: &ty::AssociatedItem,
impl_m_span: Span,
Expand Down Expand Up @@ -182,7 +181,31 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let impl_m_generics = tcx.generics_of(impl_m.def_id);
let trait_m_generics = tcx.generics_of(trait_m.def_id);
let impl_m_predicates = tcx.predicates_of(impl_m.def_id);
let trait_m_predicates = tcx.predicates_of(trait_m.def_id);
let mut trait_m_predicates = tcx.predicates_of(trait_m.def_id);

// A `Self: Trait` predicate on trait items breaks selection, so filter it out.
// FIXME: This is a big hack!
if let Some(trait_def_id) = trait_m_predicates.parent {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What error do you get here? This feels surprising too! I would rather that -- instead of "filtering this out" -- we add something to the environment that allows it to be proven true, if needed. But it seems like the presence of this should fall out from changes elsewhere.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A full explanation is here: https://paper.dropbox.com/doc/Removing-SelfTrait-from-predicates_of-notes-neNFC7UaxCj353Du3Dd2d#:h2=Other-questions

That whole doc is really just for this particular change (the title implies that it's for the whole task.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TL;DR: The extra predicate breaks downstream winnowing logic

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wondered if that would happen.

trait_m_predicates.predicates.retain(|pred| {
match pred {
ty::Predicate::Trait(trait_ref) => {
if trait_ref.def_id() == trait_def_id {
use ty::TypeVariants::TyParam;
if let TyParam(param) = trait_ref.skip_binder().self_ty().sty {
if param.is_self() {
debug!(
"compare_impl_method: removing `{:?}` from trait_m_predicates",
pred);
return false;
}
}
}
true
}
_ => true
}
});
}

// Check region bounds.
check_region_bounds_on_impl_method(tcx,
Expand Down Expand Up @@ -213,6 +236,8 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
hybrid_preds.predicates
.extend(trait_m_predicates.instantiate_own(tcx, trait_to_skol_substs).predicates);

debug!("compare_impl_method: impl_bounds+trait_bounds={:?}", hybrid_preds);

// Construct trait parameter environment and then shift it into the skolemized viewpoint.
// The key step here is to update the caller_bounds's predicates to be
// the new hybrid bounds we computed.
Expand Down
2 changes: 2 additions & 0 deletions src/librustc_typeck/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ fn check_where_clauses<'a, 'gcx, 'fcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>,
let predicates = predicates.instantiate_identity(fcx.tcx);
let predicates = fcx.normalize_associated_types_in(span, &predicates);

debug!("check_where_clauses: predicates={:?}", predicates.predicates);
let obligations =
predicates.predicates
.iter()
Expand All @@ -457,6 +458,7 @@ fn check_where_clauses<'a, 'gcx, 'fcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>,
span));

for obligation in obligations {
debug!("next obligation cause: {:?}", obligation.cause);
fcx.register_predicate(obligation);
}
}
Expand Down
21 changes: 15 additions & 6 deletions src/librustc_typeck/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1321,12 +1321,17 @@ pub fn explicit_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let node = tcx.hir.get(node_id);

let mut is_trait = None;
let mut is_trait_item = false;
let mut is_default_impl_trait = None;

let icx = ItemCtxt::new(tcx, def_id);
let no_generics = hir::Generics::empty();
let ast_generics = match node {
NodeTraitItem(item) => &item.generics,
NodeTraitItem(item) => {
is_trait_item = true;
&item.generics
}

NodeImplItem(item) => &item.generics,

NodeItem(item) => {
Expand Down Expand Up @@ -1401,12 +1406,8 @@ pub fn explicit_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
// and the explicit where-clauses, but to get the full set of predicates
// on a trait we need to add in the supertrait bounds and bounds found on
// associated types.
if let Some((trait_ref, _)) = is_trait {
if let Some((_trait_ref, _)) = is_trait {
predicates = tcx.super_predicates_of(def_id).predicates;

// Add in a predicate that `Self:Trait` (where `Trait` is the
// current trait). This is needed for builtin bounds.
predicates.push(trait_ref.to_poly_trait_ref().to_predicate());
}

// In default impls, we can assume that the self type implements
Expand All @@ -1421,6 +1422,14 @@ pub fn explicit_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
predicates.push(trait_ref.to_poly_trait_ref().to_predicate());
}

// For trait items, add `Self: Trait` predicate.
if is_trait_item {
let parent_id = tcx.hir.get_parent(node_id);
let parent_def_id = tcx.hir.local_def_id(parent_id);
debug_assert!(ty::is_trait_node(tcx, parent_id));
predicates.push(ty::TraitRef::identity(tcx, parent_def_id).to_predicate());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels right! ✅

}

// Collect the region predicates that were declared inline as
// well. In the case of parameters declared on a fn or method, we
// have to be careful to only iterate over early-bound regions.
Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/chalkify/lower_env1.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ error: program clause dump
LL | #[rustc_dump_program_clauses] //~ ERROR program clause dump
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: FromEnv(Self: Bar) :- FromEnv(Self: Bar).
= note: FromEnv(Self: Foo) :- FromEnv(Self: Bar).
= note: FromEnv(Self: Foo) :- FromEnv(Self: Bar).
= note: Implemented(Self: Bar) :- FromEnv(Self: Bar).

Expand All @@ -14,7 +14,7 @@ error: program clause dump
LL | #[rustc_dump_env_program_clauses] //~ ERROR program clause dump
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: FromEnv(Self: Bar) :- FromEnv(Self: Bar).
= note: FromEnv(Self: Foo) :- FromEnv(Self: Bar).
= note: FromEnv(Self: Foo) :- FromEnv(Self: Bar).
= note: Implemented(Self: Bar) :- FromEnv(Self: Bar).
= note: Implemented(Self: Foo) :- FromEnv(Self: Foo).
Expand Down