Extend rustc_on_unimplemented flag: if a message is available at an impl, this message will be displayed instead
This commit is contained in:
parent
c7ab8840c8
commit
e9797d4be5
@ -43,6 +43,7 @@
|
|||||||
// Since libcore defines many fundamental lang items, all tests live in a
|
// Since libcore defines many fundamental lang items, all tests live in a
|
||||||
// separate crate, libcoretest, to avoid bizarre issues.
|
// separate crate, libcoretest, to avoid bizarre issues.
|
||||||
|
|
||||||
|
#![cfg_attr(stage0, allow(unused_attributes))]
|
||||||
#![crate_name = "core"]
|
#![crate_name = "core"]
|
||||||
#![stable(feature = "core", since = "1.6.0")]
|
#![stable(feature = "core", since = "1.6.0")]
|
||||||
#![crate_type = "rlib"]
|
#![crate_type = "rlib"]
|
||||||
|
@ -167,7 +167,7 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
|
|||||||
|
|
||||||
/// A map returned by `skolemize_late_bound_regions()` indicating the skolemized
|
/// A map returned by `skolemize_late_bound_regions()` indicating the skolemized
|
||||||
/// region that each late-bound region was replaced with.
|
/// 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?
|
/// Why did we require that the two types be related?
|
||||||
///
|
///
|
||||||
|
@ -26,14 +26,16 @@ use super::{
|
|||||||
|
|
||||||
use fmt_macros::{Parser, Piece, Position};
|
use fmt_macros::{Parser, Piece, Position};
|
||||||
use hir::def_id::DefId;
|
use hir::def_id::DefId;
|
||||||
use infer::InferCtxt;
|
use infer::{self, InferCtxt, TypeOrigin};
|
||||||
use ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt};
|
use ty::{self, ToPredicate, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable};
|
||||||
use ty::fast_reject;
|
use ty::fast_reject;
|
||||||
use ty::fold::{TypeFoldable, TypeFolder};
|
use ty::fold::{TypeFoldable, TypeFolder};
|
||||||
|
use ty::::subst::{self, Subst};
|
||||||
use util::nodemap::{FnvHashMap, FnvHashSet};
|
use util::nodemap::{FnvHashMap, FnvHashSet};
|
||||||
|
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use syntax::ast;
|
||||||
use syntax::attr::{AttributeMethods, AttrMetaMethods};
|
use syntax::attr::{AttributeMethods, AttrMetaMethods};
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use syntax::codemap::Span;
|
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> {
|
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||||
pub fn report_fulfillment_errors(&self, errors: &Vec<FulfillmentError<'tcx>>) {
|
pub fn report_fulfillment_errors(&self, errors: &Vec<FulfillmentError<'tcx>>) {
|
||||||
for error in errors {
|
for error in errors {
|
||||||
@ -403,7 +553,31 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
|||||||
self.resolve_type_vars_if_possible(trait_predicate);
|
self.resolve_type_vars_if_possible(trait_predicate);
|
||||||
|
|
||||||
if self.tcx.sess.has_errors() && trait_predicate.references_error() {
|
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 {
|
} else {
|
||||||
let trait_ref = trait_predicate.to_poly_trait_ref();
|
let trait_ref = trait_predicate.to_poly_trait_ref();
|
||||||
|
|
||||||
@ -450,7 +624,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
|||||||
}
|
}
|
||||||
err
|
err
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
ty::Predicate::Equate(ref predicate) => {
|
ty::Predicate::Equate(ref predicate) => {
|
||||||
let predicate = self.resolve_type_vars_if_possible(predicate);
|
let predicate = self.resolve_type_vars_if_possible(predicate);
|
||||||
let err = self.equality_predicate(span,
|
let err = self.equality_predicate(span,
|
||||||
|
@ -136,7 +136,6 @@ impl<'a, 'gcx, 'tcx> Substs<'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> Encodable for Substs<'tcx> {
|
impl<'tcx> Encodable for Substs<'tcx> {
|
||||||
|
|
||||||
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
|
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
cstore::tls::with_encoding_context(s, |ecx, rbml_w| {
|
cstore::tls::with_encoding_context(s, |ecx, rbml_w| {
|
||||||
ecx.encode_substs(rbml_w, self);
|
ecx.encode_substs(rbml_w, self);
|
||||||
|
34
src/test/compile-fail/check_on_unimplemented.rs
Normal file
34
src/test/compile-fail/check_on_unimplemented.rs
Normal file
@ -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
|
||||||
|
}
|
20
src/test/compile-fail/check_on_unimplemented_on_slice.rs
Normal file
20
src/test/compile-fail/check_on_unimplemented_on_slice.rs
Normal file
@ -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
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user