Skip to content

Commit e9797d4

Browse files
Extend rustc_on_unimplemented flag: if a message is available at an impl, this message will be displayed instead
1 parent c7ab884 commit e9797d4

File tree

6 files changed

+235
-6
lines changed

6 files changed

+235
-6
lines changed

src/libcore/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
// Since libcore defines many fundamental lang items, all tests live in a
4444
// separate crate, libcoretest, to avoid bizarre issues.
4545

46+
#![cfg_attr(stage0, allow(unused_attributes))]
4647
#![crate_name = "core"]
4748
#![stable(feature = "core", since = "1.6.0")]
4849
#![crate_type = "rlib"]

src/librustc/infer/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
167167

168168
/// A map returned by `skolemize_late_bound_regions()` indicating the skolemized
169169
/// region that each late-bound region was replaced with.
170-
pub type SkolemizationMap = FnvHashMap<ty::BoundRegion,ty::Region>;
170+
pub type SkolemizationMap = FnvHashMap<ty::BoundRegion, ty::Region>;
171171

172172
/// Why did we require that the two types be related?
173173
///

src/librustc/traits/error_reporting.rs

Lines changed: 179 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,16 @@ use super::{
2626

2727
use fmt_macros::{Parser, Piece, Position};
2828
use hir::def_id::DefId;
29-
use infer::InferCtxt;
30-
use ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt};
29+
use infer::{self, InferCtxt, TypeOrigin};
30+
use ty::{self, ToPredicate, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable};
3131
use ty::fast_reject;
3232
use ty::fold::{TypeFoldable, TypeFolder};
33+
use ty::::subst::{self, Subst};
3334
use util::nodemap::{FnvHashMap, FnvHashSet};
3435

3536
use std::cmp;
3637
use std::fmt;
38+
use syntax::ast;
3739
use syntax::attr::{AttributeMethods, AttrMetaMethods};
3840
use syntax::ast;
3941
use syntax::codemap::Span;
@@ -60,6 +62,154 @@ impl<'a, 'gcx, 'tcx> TraitErrorKey<'tcx> {
6062
}
6163
}
6264

65+
fn impl_self_ty<'a, 'tcx>(fcx: &InferCtxt<'a, 'tcx>,
66+
did: DefId,
67+
obligation: PredicateObligation<'tcx>)
68+
-> subst::Substs<'tcx> {
69+
let tcx = fcx.tcx;
70+
71+
let ity = tcx.lookup_item_type(did);
72+
let (tps, rps, _) =
73+
(ity.generics.types.get_slice(subst::TypeSpace),
74+
ity.generics.regions.get_slice(subst::TypeSpace),
75+
ity.ty);
76+
77+
let rps = fcx.region_vars_for_defs(obligation.cause.span, rps);
78+
let mut substs = subst::Substs::new(
79+
subst::VecPerParamSpace::empty(),
80+
subst::VecPerParamSpace::new(rps, Vec::new(), Vec::new()));
81+
fcx.type_vars_for_defs(obligation.cause.span, subst::ParamSpace::TypeSpace, &mut substs, tps);
82+
substs
83+
}
84+
85+
fn get_current_failing_impl<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
86+
trait_ref: &TraitRef<'tcx>,
87+
obligation: &PredicateObligation<'tcx>)
88+
-> Option<DefId> {
89+
let simp = fast_reject::simplify_type(infcx.tcx,
90+
trait_ref.self_ty(),
91+
true);
92+
let trait_def = infcx.tcx.lookup_trait_def(trait_ref.def_id);
93+
94+
match simp {
95+
Some(_) => {
96+
let mut ret = None;
97+
trait_def.for_each_impl(infcx.tcx, |def_id| {
98+
let imp = infcx.tcx.impl_trait_ref(def_id).unwrap();
99+
let imp = imp.subst(infcx.tcx, &impl_self_ty(infcx, def_id, obligation.clone()));
100+
if ret.is_none() {
101+
for error in infcx.reported_trait_errors.borrow().iter() {
102+
if let ty::Predicate::Trait(ref t) = error.predicate {
103+
if infer::mk_eqty(infcx, true, TypeOrigin::Misc(obligation.cause.span),
104+
t.skip_binder().trait_ref.self_ty(),
105+
imp.self_ty()).is_ok() {
106+
ret = Some(def_id);
107+
break;
108+
}
109+
}
110+
}
111+
}
112+
});
113+
ret
114+
},
115+
None => None,
116+
}
117+
}
118+
119+
fn find_attr<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
120+
def_id: DefId,
121+
attr_name: &str)
122+
-> Option<ast::Attribute> {
123+
for item in infcx.tcx.get_attrs(def_id).iter() {
124+
if item.check_name(attr_name) {
125+
return Some(item.clone());
126+
}
127+
}
128+
None
129+
}
130+
131+
fn report_on_unimplemented<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
132+
trait_ref: &TraitRef<'tcx>,
133+
obligation: &PredicateObligation<'tcx>)
134+
-> Option<String> {
135+
let def_id = match get_current_failing_impl(infcx, trait_ref, obligation) {
136+
Some(def_id) => {
137+
if let Some(_) = find_attr(infcx, def_id, "rustc_on_unimplemented") {
138+
def_id
139+
} else {
140+
trait_ref.def_id
141+
}
142+
},
143+
None => trait_ref.def_id,
144+
};
145+
let span = obligation.cause.span;
146+
let mut report = None;
147+
148+
for item in infcx.tcx.get_attrs(def_id).iter() {
149+
if item.check_name("rustc_on_unimplemented") {
150+
let err_sp = item.meta().span.substitute_dummy(span);
151+
let def = infcx.tcx.lookup_trait_def(trait_ref.def_id);
152+
let trait_str = def.trait_ref.to_string();
153+
if let Some(ref istring) = item.value_str() {
154+
let mut generic_map = def.generics.types.iter_enumerated()
155+
.map(|(param, i, gen)| {
156+
(gen.name.as_str().to_string(),
157+
trait_ref.substs.types.get(param, i)
158+
.to_string())
159+
}).collect::<FnvHashMap<String, String>>();
160+
generic_map.insert("Self".to_string(),
161+
trait_ref.self_ty().to_string());
162+
let parser = Parser::new(&istring);
163+
let mut errored = false;
164+
let err: String = parser.filter_map(|p| {
165+
match p {
166+
Piece::String(s) => Some(s),
167+
Piece::NextArgument(a) => match a.position {
168+
Position::ArgumentNamed(s) => match generic_map.get(s) {
169+
Some(val) => Some(val),
170+
None => {
171+
span_err!(infcx.tcx.sess, err_sp, E0272,
172+
"the #[rustc_on_unimplemented] \
173+
attribute on \
174+
trait definition for {} refers to \
175+
non-existent type parameter {}",
176+
trait_str, s);
177+
errored = true;
178+
None
179+
}
180+
},
181+
_ => {
182+
span_err!(infcx.tcx.sess, err_sp, E0273,
183+
"the #[rustc_on_unimplemented] \
184+
attribute on \
185+
trait definition for {} must have named \
186+
format arguments, \
187+
eg `#[rustc_on_unimplemented = \
188+
\"foo {{T}}\"]`",
189+
trait_str);
190+
errored = true;
191+
None
192+
}
193+
}
194+
}
195+
}).collect();
196+
// Report only if the format string checks out
197+
if !errored {
198+
report = Some(err);
199+
}
200+
} else {
201+
span_err!(infcx.tcx.sess, err_sp, E0274,
202+
"the #[rustc_on_unimplemented] attribute on \
203+
trait definition for {} must have a value, \
204+
eg `#[rustc_on_unimplemented = \"foo\"]`",
205+
trait_str);
206+
}
207+
break;
208+
}
209+
}
210+
report
211+
}
212+
63213
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
64214
pub fn report_fulfillment_errors(&self, errors: &Vec<FulfillmentError<'tcx>>) {
65215
for error in errors {
@@ -403,7 +553,31 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
403553
self.resolve_type_vars_if_possible(trait_predicate);
404554

405555
if self.tcx.sess.has_errors() && trait_predicate.references_error() {
406-
return;
556+
let trait_ref = trait_predicate.to_poly_trait_ref();
557+
let mut err = struct_span_err!(
558+
infcx.tcx.sess, obligation.cause.span, E0277,
559+
"the trait bound `{}` is not satisfied",
560+
trait_ref.to_predicate());
561+
562+
// Try to report a help message
563+
564+
if !trait_ref.has_infer_types() &&
565+
predicate_can_apply(infcx, trait_ref)
566+
{
567+
// If a where-clause may be useful, remind the
568+
// user that they can add it.
569+
//
570+
// don't display an on-unimplemented note, as
571+
// these notes will often be of the form
572+
// "the type `T` can't be frobnicated"
573+
// which is somewhat confusing.
574+
err.help(&format!("consider adding a `where {}` bound",
575+
trait_ref.to_predicate()));
576+
} else if let Some(s) = on_unimplemented_note(infcx, trait_ref,
577+
obligation.cause.span) {
578+
// Otherwise, if there is an on-unimplemented note,
579+
// display it.
580+
err.note(&s);
407581
} else {
408582
let trait_ref = trait_predicate.to_poly_trait_ref();
409583

@@ -450,7 +624,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
450624
}
451625
err
452626
}
453-
},
627+
}
628+
454629
ty::Predicate::Equate(ref predicate) => {
455630
let predicate = self.resolve_type_vars_if_possible(predicate);
456631
let err = self.equality_predicate(span,

src/librustc/ty/subst.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@ impl<'a, 'gcx, 'tcx> Substs<'tcx> {
136136
}
137137

138138
impl<'tcx> Encodable for Substs<'tcx> {
139-
140139
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
141140
cstore::tls::with_encoding_context(s, |ecx, rbml_w| {
142141
ecx.encode_substs(rbml_w, self);
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test if the on_unimplemented message override works
12+
13+
#![feature(on_unimplemented)]
14+
#![feature(rustc_attrs)]
15+
16+
#[rustc_on_unimplemented = "invalid"]
17+
trait Index<Idx: ?Sized> {
18+
type Output: ?Sized;
19+
fn index(&self, index: Idx) -> &Self::Output;
20+
}
21+
22+
#[rustc_on_unimplemented = "a usize is required to index into a slice"]
23+
impl Index<usize> for [i32] {
24+
type Output = i32;
25+
fn index(&self, index: usize) -> &i32 {
26+
&self[index]
27+
}
28+
}
29+
30+
#[rustc_error]
31+
fn main() {
32+
Index::<u32>::index(&[1, 2, 3] as &[i32], 2u32); //~ ERROR E0277
33+
//~| NOTE a usize is required
34+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test new Index error message for slices
12+
13+
#![feature(rustc_attrs)]
14+
15+
#[rustc_error]
16+
fn main() {
17+
let x = &[1, 2, 3] as &[i32];
18+
x[1i32]; //~ ERROR E0277
19+
//~| NOTE a usize is required
20+
}

0 commit comments

Comments
 (0)