From e9797d4be51e1118773877a078859f997c4191b2 Mon Sep 17 00:00:00 2001 From: ggomez Date: Thu, 21 Jan 2016 10:57:21 +0100 Subject: [PATCH] Extend rustc_on_unimplemented flag: if a message is available at an impl, this message will be displayed instead --- src/libcore/lib.rs | 1 + src/librustc/infer/mod.rs | 2 +- src/librustc/traits/error_reporting.rs | 183 +++++++++++++++++- src/librustc/ty/subst.rs | 1 - .../compile-fail/check_on_unimplemented.rs | 34 ++++ .../check_on_unimplemented_on_slice.rs | 20 ++ 6 files changed, 235 insertions(+), 6 deletions(-) create mode 100644 src/test/compile-fail/check_on_unimplemented.rs create mode 100644 src/test/compile-fail/check_on_unimplemented_on_slice.rs 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; +pub type SkolemizationMap = FnvHashMap; /// 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 { + 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 { + 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 { + 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::>(); + 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>) { 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(&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 or the MIT license +// , 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 { + type Output: ?Sized; + fn index(&self, index: Idx) -> &Self::Output; +} + +#[rustc_on_unimplemented = "a usize is required to index into a slice"] +impl Index for [i32] { + type Output = i32; + fn index(&self, index: usize) -> &i32 { + &self[index] + } +} + +#[rustc_error] +fn main() { + Index::::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 or the MIT license +// , 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 +}