Improve error message for Index trait on slices

This commit is contained in:
Guillaume Gomez 2016-01-23 06:44:38 +01:00 committed by ggomez
parent e9797d4be5
commit b8fad79a07
3 changed files with 129 additions and 28 deletions

View File

@ -523,6 +523,8 @@ impl<T> 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<T> ops::Index<usize> for [T] {
type Output = T;
@ -533,6 +535,8 @@ impl<T> ops::Index<usize> 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<T> ops::IndexMut<usize> for [T] {
#[inline]
fn index_mut(&mut self, index: usize) -> &mut T {

View File

@ -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<DefId> {
-> 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<String> {
fn on_unimplemented_note<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
obligation: &PredicateObligation<'tcx>) -> Option<String> {
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) => {

View File

@ -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 <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;
}
struct Foo {
i: usize,
}
#[rustc_on_unimplemented = "a Foo is required to index into a slice"]
impl Index<Foo> 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<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
}