about summary refs log tree commit diff
diff options
context:
space:
mode:
authorggomez <ggomez@ggo.ifr.lan>2016-01-21 10:57:21 +0100
committerggomez <guillaume1.gomez@gmail.com>2016-05-11 13:18:57 +0200
commite9797d4be51e1118773877a078859f997c4191b2 (patch)
tree9c318f540e75a27d69600258bf1944d64f4b41df
parentc7ab8840c8cfcd62e9f7212371ed3e31ad2ae73a (diff)
downloadrust-e9797d4be51e1118773877a078859f997c4191b2.tar.gz
rust-e9797d4be51e1118773877a078859f997c4191b2.zip
Extend rustc_on_unimplemented flag: if a message is available at an impl, this message will be displayed instead
-rw-r--r--src/libcore/lib.rs1
-rw-r--r--src/librustc/infer/mod.rs2
-rw-r--r--src/librustc/traits/error_reporting.rs183
-rw-r--r--src/librustc/ty/subst.rs1
-rw-r--r--src/test/compile-fail/check_on_unimplemented.rs34
-rw-r--r--src/test/compile-fail/check_on_unimplemented_on_slice.rs20
6 files changed, 235 insertions, 6 deletions
diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs
index 873059d3ccc..e1bbdf4a7ae 100644
--- a/src/libcore/lib.rs
+++ b/src/libcore/lib.rs
@@ -43,6 +43,7 @@
 // Since libcore defines many fundamental lang items, all tests live in a
 // separate crate, libcoretest, to avoid bizarre issues.
 
+#![cfg_attr(stage0, allow(unused_attributes))]
 #![crate_name = "core"]
 #![stable(feature = "core", since = "1.6.0")]
 #![crate_type = "rlib"]
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index 29d8a808de5..41982ddc78b 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -167,7 +167,7 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
 
 /// A map returned by `skolemize_late_bound_regions()` indicating the skolemized
 /// region that each late-bound region was replaced with.
-pub type SkolemizationMap = FnvHashMap<ty::BoundRegion,ty::Region>;
+pub type SkolemizationMap = FnvHashMap<ty::BoundRegion, ty::Region>;
 
 /// Why did we require that the two types be related?
 ///
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index 6c037ebd2bc..b23ca4ae832 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -26,14 +26,16 @@ use super::{
 
 use fmt_macros::{Parser, Piece, Position};
 use hir::def_id::DefId;
-use infer::InferCtxt;
-use ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt};
+use infer::{self, InferCtxt, TypeOrigin};
+use ty::{self, ToPredicate, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable};
 use ty::fast_reject;
 use ty::fold::{TypeFoldable, TypeFolder};
+use ty::::subst::{self, Subst};
 use util::nodemap::{FnvHashMap, FnvHashSet};
 
 use std::cmp;
 use std::fmt;
+use syntax::ast;
 use syntax::attr::{AttributeMethods, AttrMetaMethods};
 use syntax::ast;
 use syntax::codemap::Span;
@@ -60,6 +62,154 @@ impl<'a, 'gcx, 'tcx> TraitErrorKey<'tcx> {
     }
 }
 
+fn impl_self_ty<'a, 'tcx>(fcx: &InferCtxt<'a, 'tcx>,
+                          did: DefId,
+                          obligation: PredicateObligation<'tcx>)
+                          -> subst::Substs<'tcx> {
+    let tcx = fcx.tcx;
+
+    let ity = tcx.lookup_item_type(did);
+    let (tps, rps, _) =
+        (ity.generics.types.get_slice(subst::TypeSpace),
+         ity.generics.regions.get_slice(subst::TypeSpace),
+         ity.ty);
+
+    let rps = fcx.region_vars_for_defs(obligation.cause.span, rps);
+    let mut substs = subst::Substs::new(
+        subst::VecPerParamSpace::empty(),
+        subst::VecPerParamSpace::new(rps, Vec::new(), Vec::new()));
+    fcx.type_vars_for_defs(obligation.cause.span, subst::ParamSpace::TypeSpace, &mut substs, tps);
+    substs
+}
+
+fn get_current_failing_impl<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
+                                      trait_ref: &TraitRef<'tcx>,
+                                      obligation: &PredicateObligation<'tcx>)
+                                     -> Option<DefId> {
+    let simp = fast_reject::simplify_type(infcx.tcx,
+                                          trait_ref.self_ty(),
+                                          true);
+    let trait_def = infcx.tcx.lookup_trait_def(trait_ref.def_id);
+
+    match simp {
+        Some(_) => {
+            let mut ret = None;
+            trait_def.for_each_impl(infcx.tcx, |def_id| {
+                let imp = infcx.tcx.impl_trait_ref(def_id).unwrap();
+                let imp = imp.subst(infcx.tcx, &impl_self_ty(infcx, def_id, obligation.clone()));
+                if ret.is_none() {
+                    for error in infcx.reported_trait_errors.borrow().iter() {
+                        if let ty::Predicate::Trait(ref t) = error.predicate {
+                            if infer::mk_eqty(infcx, true, TypeOrigin::Misc(obligation.cause.span),
+                                              t.skip_binder().trait_ref.self_ty(),
+                                              imp.self_ty()).is_ok() {
+                                ret = Some(def_id);
+                                break;
+                            }
+                        }
+                    }
+                }
+            });
+            ret
+        },
+        None => None,
+    }
+}
+
+fn find_attr<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
+                       def_id: DefId,
+                       attr_name: &str)
+                      -> Option<ast::Attribute> {
+    for item in infcx.tcx.get_attrs(def_id).iter() {
+        if item.check_name(attr_name) {
+            return Some(item.clone());
+        }
+    }
+    None
+}
+
+fn report_on_unimplemented<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
+                                     trait_ref: &TraitRef<'tcx>,
+                                     obligation: &PredicateObligation<'tcx>)
+                                    -> Option<String> {
+    let def_id = match get_current_failing_impl(infcx, trait_ref, obligation) {
+        Some(def_id) => {
+            if let Some(_) = find_attr(infcx, 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 infcx.tcx.get_attrs(def_id).iter() {
+        if item.check_name("rustc_on_unimplemented") {
+            let err_sp = item.meta().span.substitute_dummy(span);
+            let def = infcx.tcx.lookup_trait_def(trait_ref.def_id);
+            let trait_str = def.trait_ref.to_string();
+            if let Some(ref istring) = item.value_str() {
+                let mut generic_map = def.generics.types.iter_enumerated()
+                                         .map(|(param, i, gen)| {
+                                               (gen.name.as_str().to_string(),
+                                                trait_ref.substs.types.get(param, i)
+                                                         .to_string())
+                                              }).collect::<FnvHashMap<String, String>>();
+                generic_map.insert("Self".to_string(),
+                                   trait_ref.self_ty().to_string());
+                let parser = Parser::new(&istring);
+                let mut errored = false;
+                let err: String = parser.filter_map(|p| {
+                    match p {
+                        Piece::String(s) => Some(s),
+                        Piece::NextArgument(a) => match a.position {
+                            Position::ArgumentNamed(s) => match generic_map.get(s) {
+                                Some(val) => Some(val),
+                                None => {
+                                    span_err!(infcx.tcx.sess, err_sp, E0272,
+                                              "the #[rustc_on_unimplemented] \
+                                               attribute on \
+                                               trait definition for {} refers to \
+                                               non-existent type parameter {}",
+                                              trait_str, s);
+                                    errored = true;
+                                    None
+                                }
+                            },
+                            _ => {
+                                span_err!(infcx.tcx.sess, err_sp, E0273,
+                                          "the #[rustc_on_unimplemented] \
+                                           attribute on \
+                                           trait definition for {} must have named \
+                                           format arguments, \
+                                           eg `#[rustc_on_unimplemented = \
+                                           \"foo {{T}}\"]`",
+                                          trait_str);
+                                errored = true;
+                                None
+                            }
+                        }
+                    }
+                }).collect();
+                // Report only if the format string checks out
+                if !errored {
+                    report = Some(err);
+                }
+            } else {
+                span_err!(infcx.tcx.sess, err_sp, E0274,
+                          "the #[rustc_on_unimplemented] attribute on \
+                           trait definition for {} must have a value, \
+                           eg `#[rustc_on_unimplemented = \"foo\"]`",
+                          trait_str);
+            }
+            break;
+        }
+    }
+    report
+}
+
 impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     pub fn report_fulfillment_errors(&self, errors: &Vec<FulfillmentError<'tcx>>) {
         for error in errors {
@@ -403,7 +553,31 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                                 self.resolve_type_vars_if_possible(trait_predicate);
 
                             if self.tcx.sess.has_errors() && trait_predicate.references_error() {
-                                return;
+                                let trait_ref = trait_predicate.to_poly_trait_ref();
+                                let mut err = struct_span_err!(
+                                    infcx.tcx.sess, obligation.cause.span, E0277,
+                                    "the trait bound `{}` is not satisfied",
+                                    trait_ref.to_predicate());
+
+                            // Try to report a help message
+
+                            if !trait_ref.has_infer_types() &&
+                                predicate_can_apply(infcx, trait_ref)
+                            {
+                                // If a where-clause may be useful, remind the
+                                // user that they can add it.
+                                //
+                                // don't display an on-unimplemented note, as
+                                // these notes will often be of the form
+                                //     "the type `T` can't be frobnicated"
+                                // which is somewhat confusing.
+                                err.help(&format!("consider adding a `where {}` bound",
+                                    trait_ref.to_predicate()));
+                            } else if let Some(s) = on_unimplemented_note(infcx, trait_ref,
+                                                                          obligation.cause.span) {
+                                // Otherwise, if there is an on-unimplemented note,
+                                // display it.
+                                err.note(&s);
                             } else {
                                 let trait_ref = trait_predicate.to_poly_trait_ref();
 
@@ -450,7 +624,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                                 }
                                 err
                             }
-                        },
+                        }
+
                         ty::Predicate::Equate(ref predicate) => {
                             let predicate = self.resolve_type_vars_if_possible(predicate);
                             let err = self.equality_predicate(span,
diff --git a/src/librustc/ty/subst.rs b/src/librustc/ty/subst.rs
index 9aec6b35997..2db9ceb8a05 100644
--- a/src/librustc/ty/subst.rs
+++ b/src/librustc/ty/subst.rs
@@ -136,7 +136,6 @@ impl<'a, 'gcx, 'tcx> Substs<'tcx> {
 }
 
 impl<'tcx> Encodable for Substs<'tcx> {
-
     fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
         cstore::tls::with_encoding_context(s, |ecx, rbml_w| {
             ecx.encode_substs(rbml_w, self);
diff --git a/src/test/compile-fail/check_on_unimplemented.rs b/src/test/compile-fail/check_on_unimplemented.rs
new file mode 100644
index 00000000000..042fdb070f4
--- /dev/null
+++ b/src/test/compile-fail/check_on_unimplemented.rs
@@ -0,0 +1,34 @@
+// 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 usize is required to index into a slice"]
+impl Index<usize> for [i32] {
+    type Output = i32;
+    fn index(&self, index: usize) -> &i32 {
+        &self[index]
+    }
+}
+
+#[rustc_error]
+fn main() {
+    Index::<u32>::index(&[1, 2, 3] as &[i32], 2u32); //~ ERROR E0277
+                                                     //~| NOTE a usize is required
+}
diff --git a/src/test/compile-fail/check_on_unimplemented_on_slice.rs b/src/test/compile-fail/check_on_unimplemented_on_slice.rs
new file mode 100644
index 00000000000..d594b1cea8b
--- /dev/null
+++ b/src/test/compile-fail/check_on_unimplemented_on_slice.rs
@@ -0,0 +1,20 @@
+// 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 new Index error message for slices
+
+#![feature(rustc_attrs)]
+
+#[rustc_error]
+fn main() {
+    let x = &[1, 2, 3] as &[i32];
+    x[1i32]; //~ ERROR E0277
+             //~| NOTE a usize is required
+}