]> git.lizzy.rs Git - rust.git/commitdiff
rewrite fuzzy `on_unimplemented` matching to avoid ICEs
authorAriel Ben-Yehuda <ariel.byd@gmail.com>
Sat, 14 May 2016 06:01:57 +0000 (23:01 -0700)
committerAriel Ben-Yehuda <ariel.byd@gmail.com>
Sat, 14 May 2016 06:01:57 +0000 (23:01 -0700)
src/librustc/traits/error_reporting.rs
src/test/compile-fail/on_unimplemented.rs [deleted file]

index a04c1d989f424a2a121d60e34002b97ba91235ee..df8cfd73192f266973441a4c71611f15d48263ce 100644 (file)
 
 use fmt_macros::{Parser, Piece, Position};
 use hir::def_id::DefId;
-use infer::{InferCtxt, TypeOrigin};
-use ty::{self, ToPredicate, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVariants};
+use infer::{InferCtxt};
+use ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable};
 use ty::fast_reject;
 use ty::fold::TypeFolder;
-use ty::subst::{self, ParamSpace, Subst};
+use ty::subst::{self, Subst};
 use util::nodemap::{FnvHashMap, FnvHashSet};
 
 use std::cmp;
@@ -61,128 +61,6 @@ fn from_error(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
     }
 }
 
-// Enum used to differentiate the "big" and "little" weights.
-enum Weight {
-    Coarse,
-    Precise,
-}
-
-trait AssociatedWeight {
-    fn get_weight(&self) -> (u32, u32);
-}
-
-impl<'a> AssociatedWeight for TypeVariants<'a> {
-    // Left number is for "global"/"big" weight and right number is for better precision.
-    fn get_weight(&self) -> (u32, u32) {
-        match *self {
-            TypeVariants::TyBool => (1, 1),
-            TypeVariants::TyChar => (1, 2),
-            TypeVariants::TyStr => (1, 3),
-
-            TypeVariants::TyInt(_) => (2, 1),
-            TypeVariants::TyUint(_) => (2, 2),
-            TypeVariants::TyFloat(_) => (2, 3),
-            TypeVariants::TyRawPtr(_) => (2, 4),
-
-            TypeVariants::TyEnum(_, _) => (3, 1),
-            TypeVariants::TyStruct(_, _) => (3, 2),
-            TypeVariants::TyBox(_) => (3, 3),
-            TypeVariants::TyTuple(_) => (3, 4),
-
-            TypeVariants::TyArray(_, _) => (4, 1),
-            TypeVariants::TySlice(_) => (4, 2),
-
-            TypeVariants::TyRef(_, _) => (5, 1),
-            TypeVariants::TyFnDef(_, _, _) => (5, 2),
-            TypeVariants::TyFnPtr(_) => (5, 3),
-
-            TypeVariants::TyTrait(_) => (6, 1),
-
-            TypeVariants::TyClosure(_, _) => (7, 1),
-
-            TypeVariants::TyProjection(_) => (8, 1),
-            TypeVariants::TyParam(_) => (8, 2),
-            TypeVariants::TyInfer(_) => (8, 3),
-
-            TypeVariants::TyError => (9, 1),
-        }
-    }
-}
-
-// The "closer" the types are, the lesser the weight.
-fn get_weight_diff(a: &ty::TypeVariants, b: &TypeVariants, weight: Weight) -> u32 {
-    let (w1, w2) = match weight {
-        Weight::Coarse => (a.get_weight().0, b.get_weight().0),
-        Weight::Precise => (a.get_weight().1, b.get_weight().1),
-    };
-    if w1 < w2 {
-        w2 - w1
-    } else {
-        w1 - w2
-    }
-}
-
-// Once we have "globally matching" types, we need to run another filter on them.
-//
-// In the function `get_best_matching_type`, we got the types which might fit the
-// most to the type we're looking for. This second filter now intends to get (if
-// possible) the type which fits the most.
-//
-// For example, the trait expects an `usize` and here you have `u32` and `i32`.
-// Obviously, the "correct" one is `u32`.
-fn filter_matching_types<'tcx>(weights: &[(usize, u32)],
-                               imps: &[(DefId, subst::Substs<'tcx>)],
-                               trait_types: &[ty::Ty<'tcx>])
-                               -> usize {
-    let matching_weight = weights[0].1;
-    let iter = weights.iter().filter(|&&(_, weight)| weight == matching_weight);
-    let mut filtered_weights = vec!();
-
-    for &(pos, _) in iter {
-        let mut weight = 0;
-        for (type_to_compare, original_type) in imps[pos].1
-                                                         .types
-                                                         .get_slice(ParamSpace::TypeSpace)
-                                                         .iter()
-                                                         .zip(trait_types.iter()) {
-            weight += get_weight_diff(&type_to_compare.sty, &original_type.sty, Weight::Precise);
-        }
-        filtered_weights.push((pos, weight));
-    }
-    filtered_weights.sort_by(|a, b| a.1.cmp(&b.1));
-    filtered_weights[0].0
-}
-
-// Here, we run the "big" filter. Little example:
-//
-// We receive a `String`, an `u32` and an `i32`.
-// The trait expected an `usize`.
-// From human point of view, it's easy to determine that `String` doesn't correspond to
-// the expected type at all whereas `u32` and `i32` could.
-//
-// This first filter intends to only keep the types which match the most.
-fn get_best_matching_type<'tcx>(imps: &[(DefId, subst::Substs<'tcx>)],
-                                trait_types: &[ty::Ty<'tcx>]) -> usize {
-    let mut weights = vec!();
-    for (pos, imp) in imps.iter().enumerate() {
-        let mut weight = 0;
-        for (type_to_compare, original_type) in imp.1
-                                                   .types
-                                                   .get_slice(ParamSpace::TypeSpace)
-                                                   .iter()
-                                                   .zip(trait_types.iter()) {
-            weight += get_weight_diff(&type_to_compare.sty, &original_type.sty, Weight::Coarse);
-        }
-        weights.push((pos, weight));
-    }
-    weights.sort_by(|a, b| a.1.cmp(&b.1));
-    if weights[0].1 == weights[1].1 {
-        filter_matching_types(&weights, &imps, trait_types)
-    } else {
-        weights[0].0
-    }
-}
-
 impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     pub fn report_fulfillment_errors(&self, errors: &Vec<FulfillmentError<'tcx>>) {
         for error in errors {
@@ -272,72 +150,46 @@ fn impl_substs(&self,
         substs
     }
 
-    fn get_current_failing_impl(&self,
-                                trait_ref: &TraitRef<'tcx>,
-                                obligation: &PredicateObligation<'tcx>)
-                                -> Option<(DefId, subst::Substs<'tcx>)> {
-        let simp = fast_reject::simplify_type(self.tcx,
-                                              trait_ref.self_ty(),
-                                              true);
-        let trait_def = self.tcx.lookup_trait_def(trait_ref.def_id);
-
-        match simp {
-            Some(_) => {
-                let mut matching_impls = Vec::new();
-                trait_def.for_each_impl(self.tcx, |def_id| {
-                    let imp = self.tcx.impl_trait_ref(def_id).unwrap();
-                    let substs = self.impl_substs(def_id, obligation.clone());
-                    let imp = imp.subst(self.tcx, &substs);
-
-                    if self.eq_types(true,
-                                      TypeOrigin::Misc(obligation.cause.span),
-                                      trait_ref.self_ty(),
-                                      imp.self_ty()).is_ok() {
-                        matching_impls.push((def_id, imp.substs.clone()));
-                    }
-                });
-                if matching_impls.len() == 0 {
-                    None
-                } else if matching_impls.len() == 1 {
-                    Some(matching_impls[0].clone())
-                } else {
-                    let end = trait_ref.input_types().len() - 1;
-                    // we need to determine which type is the good one!
-                    Some(matching_impls[get_best_matching_type(&matching_impls,
-                                                               &trait_ref.input_types()[0..end])]
-                                                              .clone())
+    fn impl_with_self_type_of(&self,
+                              trait_ref: ty::PolyTraitRef<'tcx>,
+                              obligation: &PredicateObligation<'tcx>)
+                              -> Option<DefId>
+    {
+        let tcx = self.tcx;
+        let mut result = None;
+        let mut ambiguous = false;
+
+        let trait_self_ty = tcx.erase_late_bound_regions(&trait_ref).self_ty();
+
+        self.tcx.lookup_trait_def(trait_ref.def_id())
+            .for_each_relevant_impl(self.tcx, trait_self_ty, |def_id| {
+                let impl_self_ty = tcx
+                    .impl_trait_ref(def_id)
+                    .unwrap()
+                    .self_ty()
+                    .subst(tcx, &self.impl_substs(def_id, obligation.clone()));
+
+                if let Ok(..) = self.can_equate(&trait_self_ty, &impl_self_ty) {
+                    ambiguous = result.is_some();
+                    result = Some(def_id);
                 }
-            },
-            None => None,
-        }
-    }
+            });
 
-    fn find_attr(&self,
-                 def_id: DefId,
-                 attr_name: &str)
-                 -> Option<ast::Attribute> {
-        for item in self.tcx.get_attrs(def_id).iter() {
-            if item.check_name(attr_name) {
-                return Some(item.clone());
+        match result {
+            Some(def_id) if !ambiguous && tcx.has_attr(def_id, "rustc_on_unimplemented") => {
+                result
             }
+            _ => None
         }
-        None
     }
 
     fn on_unimplemented_note(&self,
                              trait_ref: ty::PolyTraitRef<'tcx>,
                              obligation: &PredicateObligation<'tcx>) -> Option<String> {
+        let def_id = self.impl_with_self_type_of(trait_ref, obligation)
+            .unwrap_or(trait_ref.def_id());
         let trait_ref = trait_ref.skip_binder();
-        let def_id = match self.get_current_failing_impl(trait_ref, obligation) {
-            Some((def_id, _)) => {
-                if let Some(_) = self.find_attr(def_id, "rustc_on_unimplemented") {
-                    def_id
-                } else {
-                    trait_ref.def_id
-                }
-            },
-            None => trait_ref.def_id,
-        };
+
         let span = obligation.cause.span;
         let mut report = None;
         for item in self.tcx.get_attrs(def_id).iter() {
diff --git a/src/test/compile-fail/on_unimplemented.rs b/src/test/compile-fail/on_unimplemented.rs
deleted file mode 100644 (file)
index 1a5b5ff..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// Test if the on_unimplemented message override works
-
-#![feature(on_unimplemented)]
-#![feature(rustc_attrs)]
-
-#[rustc_on_unimplemented = "invalid"]
-trait Index<Idx: ?Sized> {
-    type Output: ?Sized;
-    fn index(&self, index: Idx) -> &Self::Output;
-}
-
-#[rustc_on_unimplemented = "a isize is required to index into a slice"]
-impl Index<isize> for [i32] {
-    type Output = i32;
-    fn index(&self, index: isize) -> &i32 {
-        &self[index as usize]
-    }
-}
-
-#[rustc_on_unimplemented = "a usize is required to index into a slice"]
-impl Index<usize> for [i32] {
-    type Output = i32;
-    fn index(&self, index: usize) -> &i32 {
-        &self[index]
-    }
-}
-
-trait Foo<A, B> {
-    fn f(&self, a: &A, b: &B);
-}
-
-#[rustc_on_unimplemented = "two i32 Foo trait takes"]
-impl Foo<i32, i32> for [i32] {
-    fn f(&self, a: &i32, b: &i32) {}
-}
-
-#[rustc_on_unimplemented = "two u32 Foo trait takes"]
-impl Foo<u32, u32> for [i32] {
-    fn f(&self, a: &u32, b: &u32) {}
-}
-
-#[rustc_error]
-fn main() {
-    Index::<u32>::index(&[1, 2, 3] as &[i32], 2u32); //~ ERROR E0277
-                                                     //~| NOTE a usize is required
-                                                     //~| NOTE required by
-    Index::<i32>::index(&[1, 2, 3] as &[i32], 2i32); //~ ERROR E0277
-                                                     //~| NOTE a isize is required
-                                                     //~| NOTE required by
-
-    Foo::<usize, usize>::f(&[1, 2, 3] as &[i32], &2usize, &2usize); //~ ERROR E0277
-                                                                    //~| NOTE two u32 Foo trait
-                                                                    //~| NOTE required by
-}