From 0619eeb739b997701f3e70b964a327573e03f1b3 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 7 May 2016 23:51:13 +0200 Subject: [PATCH 1/7] Add default rule for Makefile and make it displays some help. Add examples for "make help" command --- Makefile.in | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Makefile.in b/Makefile.in index ce67abad39b24..55c091aef76d2 100644 --- a/Makefile.in +++ b/Makefile.in @@ -108,6 +108,20 @@ # # run `make nitty-gritty` # +# # Make command examples +# +# ## Docs linked commands +# +# * make check-stage1-rustdocck: Builds rustdoc. It has the advantage to compile +# quite quickly since we're only using stage1 +# executables. +# * make doc/error-index.md: Gets all doc blocks from doc comments and error +# explanations to put them in a markdown file. You +# can then test them by running +# "rustdoc --test error-index.md". +# +# And of course, the wonderfully useful 'make tidy'! Always run it before opening a pull request to rust! +# # # # @@ -257,3 +271,9 @@ ifneq ($(strip $(findstring TAGS.emacs,$(MAKECMDGOALS)) \ CFG_INFO := $(info cfg: including ctags rules) include $(CFG_SRC_DIR)mk/ctags.mk endif + +.DEFAULT: + @echo "\n======================================================" + @echo "== If you need help, run 'make help' or 'make tips' ==" + @echo "======================================================\n" + exit 1 From e9797d4be51e1118773877a078859f997c4191b2 Mon Sep 17 00:00:00 2001 From: ggomez Date: Thu, 21 Jan 2016 10:57:21 +0100 Subject: [PATCH 2/7] 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 873059d3ccceb..e1bbdf4a7ae41 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 29d8a808de5f0..41982ddc78b65 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 6c037ebd2bc73..b23ca4ae83284 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 9aec6b3599778..2db9ceb8a05c8 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 0000000000000..042fdb070f42f --- /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 0000000000000..d594b1cea8bce --- /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 +} From b8fad79a07ef287a80959ec8c4f82e7e6803b053 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 23 Jan 2016 06:44:38 +0100 Subject: [PATCH 3/7] Improve error message for Index trait on slices --- src/libcore/slice.rs | 4 + src/librustc/traits/error_reporting.rs | 107 ++++++++++++++++------ src/test/compile-fail/on_unimplemented.rs | 46 ++++++++++ 3 files changed, 129 insertions(+), 28 deletions(-) create mode 100644 src/test/compile-fail/on_unimplemented.rs diff --git a/src/libcore/slice.rs b/src/libcore/slice.rs index 07f76aca14d30..e4b98ed6445c7 100644 --- a/src/libcore/slice.rs +++ b/src/libcore/slice.rs @@ -523,6 +523,8 @@ impl SliceExt for [T] { } #[stable(feature = "rust1", since = "1.0.0")] +#[allow(unused_attributes)] +#[rustc_on_unimplemented = "a usize is required to index into a slice"] impl ops::Index for [T] { type Output = T; @@ -533,6 +535,8 @@ impl ops::Index for [T] { } #[stable(feature = "rust1", since = "1.0.0")] +#[allow(unused_attributes)] +#[rustc_on_unimplemented = "a usize is required to index into a slice"] impl ops::IndexMut for [T] { #[inline] fn index_mut(&mut self, index: usize) -> &mut T { diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index b23ca4ae83284..88502e2b6ffbe 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -29,8 +29,8 @@ use hir::def_id::DefId; 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 ty::fold::TypeFolder; +use ty::subst::{self, Subst}; use util::nodemap::{FnvHashMap, FnvHashSet}; use std::cmp; @@ -62,7 +62,7 @@ impl<'a, 'gcx, 'tcx> TraitErrorKey<'tcx> { } } -fn impl_self_ty<'a, 'tcx>(fcx: &InferCtxt<'a, 'tcx>, +fn impl_substs<'a, 'tcx>(fcx: &InferCtxt<'a, 'tcx>, did: DefId, obligation: PredicateObligation<'tcx>) -> subst::Substs<'tcx> { @@ -82,10 +82,36 @@ fn impl_self_ty<'a, 'tcx>(fcx: &InferCtxt<'a, 'tcx>, substs } +/*fn check_type_parameters<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, + trait_substs: &subst::Substs<'tcx>, + impl_substs: &subst::Substs<'tcx>, + obligation: &PredicateObligation<'tcx>) -> bool { + let trait_types = trait_substs.types.as_slice(); + let impl_types = impl_substs.types.as_slice(); + + let mut failed = 0; + for index_to_ignore in 0..trait_types.len() { + for (index, (trait_type, impl_type)) in trait_types.iter() + .zip(impl_types.iter()) + .enumerate() { + if index_to_ignore != index && + infer::mk_eqty(infcx, true, + TypeOrigin::Misc(obligation.cause.span), + trait_type, + impl_type).is_err() { + failed += 1; + break; + } + } + } + failed == trait_types.len() - 1 +}*/ + fn get_current_failing_impl<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, trait_ref: &TraitRef<'tcx>, obligation: &PredicateObligation<'tcx>) - -> Option { + -> Option<(DefId, subst::Substs<'tcx>)> { + println!("1"); let simp = fast_reject::simplify_type(infcx.tcx, trait_ref.self_ty(), true); @@ -93,24 +119,37 @@ fn get_current_failing_impl<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, match simp { Some(_) => { - let mut ret = None; + println!("2"); + let mut matching_impls = Vec::new(); 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; - } - } - } + let substs = impl_substs(infcx, def_id, obligation.clone()); + let imp = imp.subst(infcx.tcx, &substs); + + if infer::mk_eqty(infcx, true, + TypeOrigin::Misc(obligation.cause.span), + trait_ref.self_ty(), + imp.self_ty()).is_ok() { + //if check_type_parameters(infcx, &trait_ref.substs, &imp.substs, obligation) { + matching_impls.push((def_id, imp.substs.clone())); + //} } + println!("=> {:?} /// {:?}", def_id, imp.substs); }); - ret + if matching_impls.len() == 0 { + println!("3"); + None + } else if matching_impls.len() == 1 { + println!("4"); + Some(matching_impls[0].clone()) + } else { + println!("5"); + // we need to determine which type is the good one! + for &(ref m, ref n) in matching_impls.iter() { + println!("=> {:?} /// {:?}", m, n); + } + Some(matching_impls[0].clone()) + } }, None => None, } @@ -128,19 +167,21 @@ fn find_attr<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, None } -fn report_on_unimplemented<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, - trait_ref: &TraitRef<'tcx>, - obligation: &PredicateObligation<'tcx>) - -> Option { +fn on_unimplemented_note<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + obligation: &PredicateObligation<'tcx>) -> Option { + let trait_ref = trait_ref.skip_binder(); + //let def_id = trait_ref.def_id; + let mut report = None; let def_id = match get_current_failing_impl(infcx, trait_ref, obligation) { - Some(def_id) => { + 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, + None => trait_ref.def_id, }; let span = obligation.cause.span; let mut report = None; @@ -572,11 +613,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // "the type `T` can't be frobnicated" // which is somewhat confusing. err.help(&format!("consider adding a `where {}` bound", - trait_ref.to_predicate())); + 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. + obligation) { + // If it has a custom "#[rustc_on_unimplemented]" + // error message, let's display it! err.note(&s); } else { let trait_ref = trait_predicate.to_poly_trait_ref(); @@ -624,6 +665,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } err } + // Check if it has a custom "#[rustc_on_unimplemented]" + // error message, report with that message if it does + /*let custom_note = report_on_unimplemented(infcx, &trait_ref.0, + obligation); + if let Some(s) = custom_note { + err.fileline_note(obligation.cause.span, &s); + } else { + note_obligation_cause(infcx, &mut err, obligation); + }*/ + err.emit(); } ty::Predicate::Equate(ref predicate) => { diff --git a/src/test/compile-fail/on_unimplemented.rs b/src/test/compile-fail/on_unimplemented.rs new file mode 100644 index 0000000000000..beba3bd04c12a --- /dev/null +++ b/src/test/compile-fail/on_unimplemented.rs @@ -0,0 +1,46 @@ +// 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; +} + +struct Foo { + i: usize, +} + +#[rustc_on_unimplemented = "a Foo is required to index into a slice"] +impl Index for [i32] { + type Output = i32; + fn index(&self, index: Foo) -> &i32 { + &self[index.i] + } +} + +#[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 +} From 61e6169ffeb00461be77c87675d5ce250f99d503 Mon Sep 17 00:00:00 2001 From: ggomez Date: Tue, 3 May 2016 17:03:00 +0200 Subject: [PATCH 4/7] Improve weight algorithm and tests --- src/librustc/traits/error_reporting.rs | 159 ++++++++++++------ .../compile-fail/check_on_unimplemented.rs | 1 + src/test/compile-fail/on_unimplemented.rs | 34 +++- 3 files changed, 134 insertions(+), 60 deletions(-) diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 88502e2b6ffbe..cee8561b378fd 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -27,10 +27,10 @@ use super::{ use fmt_macros::{Parser, Piece, Position}; use hir::def_id::DefId; use infer::{self, InferCtxt, TypeOrigin}; -use ty::{self, ToPredicate, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable}; +use ty::{self, ToPredicate, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVariants}; use ty::fast_reject; use ty::fold::TypeFolder; -use ty::subst::{self, Subst}; +use ty::subst::{self, ParamSpace, Subst}; use util::nodemap::{FnvHashMap, FnvHashSet}; use std::cmp; @@ -63,9 +63,9 @@ impl<'a, 'gcx, 'tcx> TraitErrorKey<'tcx> { } fn impl_substs<'a, 'tcx>(fcx: &InferCtxt<'a, 'tcx>, - did: DefId, - obligation: PredicateObligation<'tcx>) - -> subst::Substs<'tcx> { + did: DefId, + obligation: PredicateObligation<'tcx>) + -> subst::Substs<'tcx> { let tcx = fcx.tcx; let ity = tcx.lookup_item_type(did); @@ -82,36 +82,102 @@ fn impl_substs<'a, 'tcx>(fcx: &InferCtxt<'a, 'tcx>, substs } -/*fn check_type_parameters<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, - trait_substs: &subst::Substs<'tcx>, - impl_substs: &subst::Substs<'tcx>, - obligation: &PredicateObligation<'tcx>) -> bool { - let trait_types = trait_substs.types.as_slice(); - let impl_types = impl_substs.types.as_slice(); - - let mut failed = 0; - for index_to_ignore in 0..trait_types.len() { - for (index, (trait_type, impl_type)) in trait_types.iter() - .zip(impl_types.iter()) - .enumerate() { - if index_to_ignore != index && - infer::mk_eqty(infcx, true, - TypeOrigin::Misc(obligation.cause.span), - trait_type, - impl_type).is_err() { - failed += 1; - break; - } +trait AssociatedWeight { + fn get_weight(&self) -> usize; +} + +impl<'a> AssociatedWeight for TypeVariants<'a> { + // First number is for "global" weight and second number is for bigger precision + fn get_weight(&self) -> usize { + match *self { + TypeVariants::TyBool => 11, + TypeVariants::TyChar => 12, + TypeVariants::TyStr => 13, + TypeVariants::TyInt(_) => 21, + TypeVariants::TyUint(_) => 22, + TypeVariants::TyFloat(_) => 23, + TypeVariants::TyRawPtr(_) => 24, + TypeVariants::TyEnum(_, _) => 31, + TypeVariants::TyStruct(_, _) => 32, + TypeVariants::TyBox(_) => 33, + TypeVariants::TyTuple(_) => 34, + TypeVariants::TyArray(_, _) => 41, + TypeVariants::TySlice(_) => 42, + TypeVariants::TyRef(_, _) => 51, + TypeVariants::TyFnDef(_, _, _) => 52, + TypeVariants::TyFnPtr(_) => 53, + TypeVariants::TyTrait(_) => 61, + TypeVariants::TyClosure(_, _) => 71, + TypeVariants::TyProjection(_) => 81, + TypeVariants::TyParam(_) => 82, + TypeVariants::TyInfer(_) => 83, + TypeVariants::TyError => 91, } } - failed == trait_types.len() - 1 -}*/ +} + +// The "closer" the types are, the lesser the weight +fn get_weight_diff(a: &ty::TypeVariants, b: &TypeVariants, big_weight: bool) -> usize { + let w1 = if big_weight { a.get_weight() / 10 } else { a.get_weight() % 10 }; + let w2 = if big_weight { b.get_weight() / 10 } else { b.get_weight() % 10 }; + + if w1 < w2 { + w2 - w1 + } else { + w1 - w2 + } +} + +// Once we have "globally matching" types, we need to run another filter on them +fn filter_matching_types<'tcx>(weights: &[(usize, usize)], + 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, false); + } + filtered_weights.push((pos, weight)); + } + filtered_weights.sort_by(|a, b| a.1.cmp(&b.1)); + filtered_weights[0].0 +} + +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, true); + } + 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 + } +} fn get_current_failing_impl<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, trait_ref: &TraitRef<'tcx>, obligation: &PredicateObligation<'tcx>) -> Option<(DefId, subst::Substs<'tcx>)> { - println!("1"); let simp = fast_reject::simplify_type(infcx.tcx, trait_ref.self_ty(), true); @@ -119,7 +185,6 @@ fn get_current_failing_impl<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, match simp { Some(_) => { - println!("2"); let mut matching_impls = Vec::new(); trait_def.for_each_impl(infcx.tcx, |def_id| { let imp = infcx.tcx.impl_trait_ref(def_id).unwrap(); @@ -130,25 +195,19 @@ fn get_current_failing_impl<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, TypeOrigin::Misc(obligation.cause.span), trait_ref.self_ty(), imp.self_ty()).is_ok() { - //if check_type_parameters(infcx, &trait_ref.substs, &imp.substs, obligation) { - matching_impls.push((def_id, imp.substs.clone())); - //} + matching_impls.push((def_id, imp.substs.clone())); } - println!("=> {:?} /// {:?}", def_id, imp.substs); }); if matching_impls.len() == 0 { - println!("3"); None } else if matching_impls.len() == 1 { - println!("4"); Some(matching_impls[0].clone()) } else { - println!("5"); + let end = trait_ref.input_types().len() - 1; // we need to determine which type is the good one! - for &(ref m, ref n) in matching_impls.iter() { - println!("=> {:?} /// {:?}", m, n); - } - Some(matching_impls[0].clone()) + Some(matching_impls[get_best_matching_type(&matching_impls, + &trait_ref.input_types()[0..end])] + .clone()) } }, None => None, @@ -171,7 +230,6 @@ fn on_unimplemented_note<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, obligation: &PredicateObligation<'tcx>) -> Option { let trait_ref = trait_ref.skip_binder(); - //let def_id = trait_ref.def_id; let mut report = None; let def_id = match get_current_failing_impl(infcx, trait_ref, obligation) { Some((def_id, _)) => { @@ -603,8 +661,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // Try to report a help message if !trait_ref.has_infer_types() && - predicate_can_apply(infcx, trait_ref) - { + predicate_can_apply(infcx, trait_ref) { // If a where-clause may be useful, remind the // user that they can add it. // @@ -662,18 +719,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // similar impls. self.report_similar_impl_candidates(trait_ref, &mut err); + // If we can't show anything useful, try to find + // similar impls. + let impl_candidates = + find_similar_impl_candidates(infcx, trait_ref); + if impl_candidates.len() > 0 { + self.report_similar_impl_candidates(trait_ref, &mut err); } err } - // Check if it has a custom "#[rustc_on_unimplemented]" - // error message, report with that message if it does - /*let custom_note = report_on_unimplemented(infcx, &trait_ref.0, - obligation); - if let Some(s) = custom_note { - err.fileline_note(obligation.cause.span, &s); - } else { - note_obligation_cause(infcx, &mut err, obligation); - }*/ + note_obligation_cause(infcx, &mut err, obligation); err.emit(); } diff --git a/src/test/compile-fail/check_on_unimplemented.rs b/src/test/compile-fail/check_on_unimplemented.rs index 042fdb070f42f..4471b625d7912 100644 --- a/src/test/compile-fail/check_on_unimplemented.rs +++ b/src/test/compile-fail/check_on_unimplemented.rs @@ -31,4 +31,5 @@ impl Index for [i32] { fn main() { Index::::index(&[1, 2, 3] as &[i32], 2u32); //~ ERROR E0277 //~| NOTE a usize is required + //~| NOTE required by } diff --git a/src/test/compile-fail/on_unimplemented.rs b/src/test/compile-fail/on_unimplemented.rs index beba3bd04c12a..1a5b5ff206ad0 100644 --- a/src/test/compile-fail/on_unimplemented.rs +++ b/src/test/compile-fail/on_unimplemented.rs @@ -19,15 +19,11 @@ trait Index { fn index(&self, index: Idx) -> &Self::Output; } -struct Foo { - i: usize, -} - -#[rustc_on_unimplemented = "a Foo is required to index into a slice"] -impl Index for [i32] { +#[rustc_on_unimplemented = "a isize is required to index into a slice"] +impl Index for [i32] { type Output = i32; - fn index(&self, index: Foo) -> &i32 { - &self[index.i] + fn index(&self, index: isize) -> &i32 { + &self[index as usize] } } @@ -39,8 +35,30 @@ impl Index for [i32] { } } +trait Foo { + fn f(&self, a: &A, b: &B); +} + +#[rustc_on_unimplemented = "two i32 Foo trait takes"] +impl Foo for [i32] { + fn f(&self, a: &i32, b: &i32) {} +} + +#[rustc_on_unimplemented = "two u32 Foo trait takes"] +impl Foo for [i32] { + fn f(&self, a: &u32, b: &u32) {} +} + #[rustc_error] fn main() { Index::::index(&[1, 2, 3] as &[i32], 2u32); //~ ERROR E0277 //~| NOTE a usize is required + //~| NOTE required by + Index::::index(&[1, 2, 3] as &[i32], 2i32); //~ ERROR E0277 + //~| NOTE a isize is required + //~| NOTE required by + + Foo::::f(&[1, 2, 3] as &[i32], &2usize, &2usize); //~ ERROR E0277 + //~| NOTE two u32 Foo trait + //~| NOTE required by } From e6c0ff65edef9d31593bda378693542906376021 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 10 May 2016 22:53:51 +0200 Subject: [PATCH 5/7] Create Weight enum Add more comments for the global understanding --- src/librustc/traits/error_reporting.rs | 98 +++++++++++++++++--------- 1 file changed, 64 insertions(+), 34 deletions(-) diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index cee8561b378fd..9f7e6b29e84a2 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -82,45 +82,60 @@ fn impl_substs<'a, 'tcx>(fcx: &InferCtxt<'a, 'tcx>, substs } +// Enum used to differentiate the "big" and "little" weights. +enum Weight { + Coarse, + Precise, +} + trait AssociatedWeight { - fn get_weight(&self) -> usize; + fn get_weight(&self) -> (u32, u32); } impl<'a> AssociatedWeight for TypeVariants<'a> { - // First number is for "global" weight and second number is for bigger precision - fn get_weight(&self) -> usize { + // Left number is for "global"/"big" weight and right number is for better precision. + fn get_weight(&self) -> (u32, u32) { match *self { - TypeVariants::TyBool => 11, - TypeVariants::TyChar => 12, - TypeVariants::TyStr => 13, - TypeVariants::TyInt(_) => 21, - TypeVariants::TyUint(_) => 22, - TypeVariants::TyFloat(_) => 23, - TypeVariants::TyRawPtr(_) => 24, - TypeVariants::TyEnum(_, _) => 31, - TypeVariants::TyStruct(_, _) => 32, - TypeVariants::TyBox(_) => 33, - TypeVariants::TyTuple(_) => 34, - TypeVariants::TyArray(_, _) => 41, - TypeVariants::TySlice(_) => 42, - TypeVariants::TyRef(_, _) => 51, - TypeVariants::TyFnDef(_, _, _) => 52, - TypeVariants::TyFnPtr(_) => 53, - TypeVariants::TyTrait(_) => 61, - TypeVariants::TyClosure(_, _) => 71, - TypeVariants::TyProjection(_) => 81, - TypeVariants::TyParam(_) => 82, - TypeVariants::TyInfer(_) => 83, - TypeVariants::TyError => 91, + 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, big_weight: bool) -> usize { - let w1 = if big_weight { a.get_weight() / 10 } else { a.get_weight() % 10 }; - let w2 = if big_weight { b.get_weight() / 10 } else { b.get_weight() % 10 }; - +// 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 { @@ -128,8 +143,15 @@ fn get_weight_diff(a: &ty::TypeVariants, b: &TypeVariants, big_weight: bool) -> } } -// Once we have "globally matching" types, we need to run another filter on them -fn filter_matching_types<'tcx>(weights: &[(usize, usize)], +// 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 { @@ -144,7 +166,7 @@ fn filter_matching_types<'tcx>(weights: &[(usize, usize)], .get_slice(ParamSpace::TypeSpace) .iter() .zip(trait_types.iter()) { - weight += get_weight_diff(&type_to_compare.sty, &original_type.sty, false); + weight += get_weight_diff(&type_to_compare.sty, &original_type.sty, Weight::Precise); } filtered_weights.push((pos, weight)); } @@ -152,6 +174,14 @@ fn filter_matching_types<'tcx>(weights: &[(usize, usize)], 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!(); @@ -162,7 +192,7 @@ fn get_best_matching_type<'tcx>(imps: &[(DefId, subst::Substs<'tcx>)], .get_slice(ParamSpace::TypeSpace) .iter() .zip(trait_types.iter()) { - weight += get_weight_diff(&type_to_compare.sty, &original_type.sty, true); + weight += get_weight_diff(&type_to_compare.sty, &original_type.sty, Weight::Coarse); } weights.push((pos, weight)); } From 4a3acfd9371f59368108ad5e7be28f4268ed862b Mon Sep 17 00:00:00 2001 From: ggomez Date: Wed, 11 May 2016 14:33:14 +0200 Subject: [PATCH 6/7] Update to eddyb's PR --- src/librustc/traits/error_reporting.rs | 328 ++++++++++--------------- 1 file changed, 129 insertions(+), 199 deletions(-) diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 9f7e6b29e84a2..508261ddfdd6c 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -26,7 +26,7 @@ use super::{ use fmt_macros::{Parser, Piece, Position}; use hir::def_id::DefId; -use infer::{self, InferCtxt, TypeOrigin}; +use infer::{InferCtxt, TypeOrigin}; use ty::{self, ToPredicate, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVariants}; use ty::fast_reject; use ty::fold::TypeFolder; @@ -37,7 +37,6 @@ use std::cmp; use std::fmt; use syntax::ast; use syntax::attr::{AttributeMethods, AttrMetaMethods}; -use syntax::ast; use syntax::codemap::Span; use syntax::errors::DiagnosticBuilder; @@ -62,26 +61,6 @@ impl<'a, 'gcx, 'tcx> TraitErrorKey<'tcx> { } } -fn impl_substs<'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 -} - // Enum used to differentiate the "big" and "little" weights. enum Weight { Coarse, @@ -204,141 +183,6 @@ fn get_best_matching_type<'tcx>(imps: &[(DefId, subst::Substs<'tcx>)], } } -fn get_current_failing_impl<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, - trait_ref: &TraitRef<'tcx>, - obligation: &PredicateObligation<'tcx>) - -> Option<(DefId, subst::Substs<'tcx>)> { - 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 matching_impls = Vec::new(); - trait_def.for_each_impl(infcx.tcx, |def_id| { - let imp = infcx.tcx.impl_trait_ref(def_id).unwrap(); - let substs = impl_substs(infcx, def_id, obligation.clone()); - let imp = imp.subst(infcx.tcx, &substs); - - if infer::mk_eqty(infcx, 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()) - } - }, - 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 on_unimplemented_note<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, - obligation: &PredicateObligation<'tcx>) -> Option { - let trait_ref = trait_ref.skip_binder(); - let mut report = None; - 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 { @@ -405,16 +249,101 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } } + fn impl_substs(&self, + did: DefId, + obligation: PredicateObligation<'tcx>) + -> subst::Substs<'tcx> { + let tcx = self.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 = self.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())); + self.type_vars_for_defs(obligation.cause.span, + subst::ParamSpace::TypeSpace, + &mut substs, + tps); + 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()) + } + }, + None => None, + } + } + + fn find_attr(&self, + def_id: DefId, + attr_name: &str) + -> Option { + for item in self.tcx.get_attrs(def_id).iter() { + if item.check_name(attr_name) { + return Some(item.clone()); + } + } + None + } + fn on_unimplemented_note(&self, trait_ref: ty::PolyTraitRef<'tcx>, - span: Span) -> Option { + obligation: &PredicateObligation<'tcx>) -> Option { let trait_ref = trait_ref.skip_binder(); - let def_id = trait_ref.def_id; + 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() { if item.check_name("rustc_on_unimplemented") { let err_sp = item.meta().span.substitute_dummy(span); - let def = self.tcx.lookup_trait_def(def_id); + let def = self.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() @@ -474,6 +403,37 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { report } + fn find_similar_impl_candidates(&self, + trait_ref: ty::PolyTraitRef<'tcx>) + -> Vec> + { + let simp = fast_reject::simplify_type(self.tcx, + trait_ref.skip_binder().self_ty(), + true); + let mut impl_candidates = Vec::new(); + let trait_def = self.tcx.lookup_trait_def(trait_ref.def_id()); + + match simp { + Some(simp) => trait_def.for_each_impl(self.tcx, |def_id| { + let imp = self.tcx.impl_trait_ref(def_id).unwrap(); + let imp_simp = fast_reject::simplify_type(self.tcx, + imp.self_ty(), + true); + if let Some(imp_simp) = imp_simp { + if simp != imp_simp { + return; + } + } + impl_candidates.push(imp); + }), + None => trait_def.for_each_impl(self.tcx, |def_id| { + impl_candidates.push( + self.tcx.impl_trait_ref(def_id).unwrap()); + }) + }; + impl_candidates + } + fn report_similar_impl_candidates(&self, trait_ref: ty::PolyTraitRef<'tcx>, err: &mut DiagnosticBuilder) @@ -682,30 +642,7 @@ 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() { - 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) { - // If it has a custom "#[rustc_on_unimplemented]" - // error message, let's display it! - err.note(&s); + return; } else { let trait_ref = trait_predicate.to_poly_trait_ref(); @@ -727,8 +664,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // Try to report a help message if !trait_ref.has_infer_types() && - self.predicate_can_apply(trait_ref) - { + self.predicate_can_apply(trait_ref) { // If a where-clause may be useful, remind the // user that they can add it. // @@ -737,29 +673,23 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // "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) = - self.on_unimplemented_note(trait_ref, span) { - // Otherwise, if there is an on-unimplemented note, - // display it. + trait_ref.to_predicate())); + } else if let Some(s) = self.on_unimplemented_note(trait_ref, + obligation) { + // If it has a custom "#[rustc_on_unimplemented]" + // error message, let's display it! err.note(&s); } else { // If we can't show anything useful, try to find // similar impls. - - self.report_similar_impl_candidates(trait_ref, &mut err); - // If we can't show anything useful, try to find - // similar impls. let impl_candidates = - find_similar_impl_candidates(infcx, trait_ref); + self.find_similar_impl_candidates(trait_ref); if impl_candidates.len() > 0 { self.report_similar_impl_candidates(trait_ref, &mut err); + } } err } - note_obligation_cause(infcx, &mut err, obligation); - err.emit(); } ty::Predicate::Equate(ref predicate) => { From 8e8f3911aa4e68d7f0a88a7d011a08c07b2cd189 Mon Sep 17 00:00:00 2001 From: bnewbold Date: Wed, 11 May 2016 11:53:07 -0400 Subject: [PATCH 7/7] trivial fixes to documentation (book) --- src/doc/book/crates-and-modules.md | 2 +- src/doc/book/error-handling.md | 4 ++-- src/doc/book/testing.md | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/doc/book/crates-and-modules.md b/src/doc/book/crates-and-modules.md index b3ccefe0a6b43..43ac30c35c6c6 100644 --- a/src/doc/book/crates-and-modules.md +++ b/src/doc/book/crates-and-modules.md @@ -115,7 +115,7 @@ $ ls target/debug build deps examples libphrases-a7448e02a0468eaa.rlib native ``` -`libphrases-hash.rlib` is the compiled crate. Before we see how to use this +`libphrases-.rlib` is the compiled crate. Before we see how to use this crate from another crate, let’s break it up into multiple files. # Multiple File Crates diff --git a/src/doc/book/error-handling.md b/src/doc/book/error-handling.md index a10e98fac7adf..c914c33a5a4b9 100644 --- a/src/doc/book/error-handling.md +++ b/src/doc/book/error-handling.md @@ -225,7 +225,7 @@ sense to put it into a function: ```rust # fn find(_: &str, _: char) -> Option { None } // Returns the extension of the given file name, where the extension is defined -// as all characters proceeding the first `.`. +// as all characters following the first `.`. // If `file_name` has no `.`, then `None` is returned. fn extension_explicit(file_name: &str) -> Option<&str> { match find(file_name, '.') { @@ -274,7 +274,7 @@ to get rid of the case analysis: ```rust # fn find(_: &str, _: char) -> Option { None } // Returns the extension of the given file name, where the extension is defined -// as all characters proceeding the first `.`. +// as all characters following the first `.`. // If `file_name` has no `.`, then `None` is returned. fn extension(file_name: &str) -> Option<&str> { find(file_name, '.').map(|i| &file_name[i+1..]) diff --git a/src/doc/book/testing.md b/src/doc/book/testing.md index 59d07e4f81c6e..4ea114c4bee68 100644 --- a/src/doc/book/testing.md +++ b/src/doc/book/testing.md @@ -84,8 +84,8 @@ fn it_works() { ``` `assert!` is a macro provided by Rust which takes one argument: if the argument -is `true`, nothing happens. If the argument is `false`, it `panic!`s. Let's run -our tests again: +is `true`, nothing happens. If the argument is `false`, it will `panic!`. Let's +run our tests again: ```bash $ cargo test