Skip to content

Instantly share code, notes, and snippets.

@nikomatsakis
Last active June 8, 2018 15:47
Show Gist options
  • Save nikomatsakis/c1cedab1d38831873d154aa56f82fc86 to your computer and use it in GitHub Desktop.
Save nikomatsakis/c1cedab1d38831873d154aa56f82fc86 to your computer and use it in GitHub Desktop.

Add Self: Trait into parameter environment for traits

Diff

diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index e23826ee99..33b01685df 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -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 mut predicates = bounds.predicates;
+    let predicates = bounds.predicates;
 
     // Finally, we have to normalize the bounds in the environment, in
     // case they contain any associated type projections. This process
@@ -2732,10 +2732,10 @@ 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());
-    }
+    // 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);

Reason

Normalizing the parameter environment for traits fails otherwise. Consider the following excerpt from libcore:

pub trait RawFloat
    : Float
    + Copy
    + Debug
    + LowerExp
    + Mul<Output=Self>
    + Div<Output=Self>
    + Neg<Output=Self>
where
    Self: Float<Bits = <Self as RawFloat>::RawBits>
{ ... }

we are not able to normalize <Self as RawFloat>::RawBits. Put another way, on what basis would we conclude that this type is well-formed?


Add Self: Trait into WF obligations

Diff

modified   src/librustc/ty/wf.rs
@@ -456,11 +456,11 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
     {
         // Add in a predicate that `Self:Trait` (where `Trait` is the
         // current trait).
-        let self_trait = if !trait_ref.has_escaping_regions() {
+        let self_trait = None; /*if !trait_ref.has_escaping_regions() {
             Some(trait_ref.to_predicate())
         } else {
             None
-        };
+        };*/
 
         let predicates = self.infcx.tcx.predicates_of(trait_ref.def_id)

Reason

We get the following test failures:

https://gist.github.com/nikomatsakis/b2ff6bd75da58c34a5582c9f55655a68

However, it's unclear that all of these are problematic, though some are a bit weird.


Filter Self: Trait in method entailment check

Diff

modified   src/librustc_typeck/check/compare_method.rs
@@ -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,
@@ -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 {
+        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
+            }
+        });
+    }

Reason

It seems clear why this is needed.


Add Self: Trait into trait items predicate list (in collect)

Diff

@@ -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());
+    }
+
     // Collect the region predicates that were declared inline as
     // well. In the case of parameters declared on a fn or method, we

Reason


Attempted modification #1

Unstaged changes (4)
modified   src/librustc/ty/mod.rs
@@ -2735,6 +2735,8 @@ fn param_env<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     if is_trait(tcx, def_id) {
         // Add `Self: Trait` into the ParamEnv.
         predicates.push(ty::TraitRef::identity(tcx, def_id).to_predicate());
+    } else if let Some(trait_def_id) = is_trait_item(tcx, def_id) {
+        predicates.push(ty::TraitRef::identity(tcx, trait_def_id).to_predicate());
     }
 
     let unnormalized_env = ty::ParamEnv::new(tcx.intern_predicates(&predicates),
@@ -2755,6 +2757,20 @@ pub fn is_trait<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
     }
 }
 
+/// If `def_id` is a trait item, returns the def-id of the trait (else `None`).
+fn is_trait_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Option<DefId> {
+    // FIXME I would rather do this with the def-key but I can't be bothered
+    // to refactor that right now -- also this is wrong across crates.
+    if let Some(id) = tcx.hir.as_local_node_id(def_id) {
+        match tcx.hir.find(id) {
+            Some(hir::map::NodeTraitItem(_)) => Some(tcx.hir.get_parent_did(id)),
+            _ => None,
+        }
+    } else {
+        None
+    }
+}
+
 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 {
modified   src/librustc_typeck/check/compare_method.rs
@@ -181,31 +181,7 @@ 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 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 {
-        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
-            }
-        });
-    }
+    let trait_m_predicates = tcx.predicates_of(trait_m.def_id);
 
     // Check region bounds.
     check_region_bounds_on_impl_method(tcx,
modified   src/librustc_typeck/collect.rs
@@ -1321,14 +1321,12 @@ 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) => {
-            is_trait_item = true;
             &item.generics
         }
 
@@ -1422,14 +1420,6 @@ 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());
-    }
-
     // 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.

results in

error[E0282]: type annotations needed
   --> libcore/num/dec2flt/rawfp.rs:188:32
    |
188 |         debug_assert!(x as f32 == fp_to_float(Fp { f: x, e: 0 }));
    |                                ^^ cannot infer type for `_`

(haven't had time to investigate further)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment