Skip to content

Commit 521c0ae

Browse files
Rollup merge of #33401 - GuillaumeGomez:index_indication, r=pnkfelix
Add rustc_on_unimplemented for Index implementation on slice Reopening of #31071. It also extends the possibility of `#[rustc_on_unimplemented]` by providing a small type filter in order to find the ones which corresponds the most. r? @pnkfelix
2 parents e37f859 + 4a3acfd commit 521c0ae

File tree

8 files changed

+384
-20
lines changed

8 files changed

+384
-20
lines changed

src/libcore/lib.rs

+1
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/libcore/slice.rs

+4
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,8 @@ impl<T> SliceExt for [T] {
523523
}
524524

525525
#[stable(feature = "rust1", since = "1.0.0")]
526+
#[allow(unused_attributes)]
527+
#[rustc_on_unimplemented = "a usize is required to index into a slice"]
526528
impl<T> ops::Index<usize> for [T] {
527529
type Output = T;
528530

@@ -533,6 +535,8 @@ impl<T> ops::Index<usize> for [T] {
533535
}
534536

535537
#[stable(feature = "rust1", since = "1.0.0")]
538+
#[allow(unused_attributes)]
539+
#[rustc_on_unimplemented = "a usize is required to index into a slice"]
536540
impl<T> ops::IndexMut<usize> for [T] {
537541
#[inline]
538542
fn index_mut(&mut self, index: usize) -> &mut T {

src/librustc/infer/mod.rs

+1-1
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

+259-18
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,17 @@ 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::{InferCtxt, TypeOrigin};
30+
use ty::{self, ToPredicate, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVariants};
3131
use ty::fast_reject;
32-
use ty::fold::{TypeFoldable, TypeFolder};
32+
use ty::fold::TypeFolder;
33+
use ty::subst::{self, ParamSpace, Subst};
3334
use util::nodemap::{FnvHashMap, FnvHashSet};
3435

3536
use std::cmp;
3637
use std::fmt;
37-
use syntax::attr::{AttributeMethods, AttrMetaMethods};
3838
use syntax::ast;
39+
use syntax::attr::{AttributeMethods, AttrMetaMethods};
3940
use syntax::codemap::Span;
4041
use syntax::errors::DiagnosticBuilder;
4142

@@ -60,6 +61,128 @@ impl<'a, 'gcx, 'tcx> TraitErrorKey<'tcx> {
6061
}
6162
}
6263

64+
// Enum used to differentiate the "big" and "little" weights.
65+
enum Weight {
66+
Coarse,
67+
Precise,
68+
}
69+
70+
trait AssociatedWeight {
71+
fn get_weight(&self) -> (u32, u32);
72+
}
73+
74+
impl<'a> AssociatedWeight for TypeVariants<'a> {
75+
// Left number is for "global"/"big" weight and right number is for better precision.
76+
fn get_weight(&self) -> (u32, u32) {
77+
match *self {
78+
TypeVariants::TyBool => (1, 1),
79+
TypeVariants::TyChar => (1, 2),
80+
TypeVariants::TyStr => (1, 3),
81+
82+
TypeVariants::TyInt(_) => (2, 1),
83+
TypeVariants::TyUint(_) => (2, 2),
84+
TypeVariants::TyFloat(_) => (2, 3),
85+
TypeVariants::TyRawPtr(_) => (2, 4),
86+
87+
TypeVariants::TyEnum(_, _) => (3, 1),
88+
TypeVariants::TyStruct(_, _) => (3, 2),
89+
TypeVariants::TyBox(_) => (3, 3),
90+
TypeVariants::TyTuple(_) => (3, 4),
91+
92+
TypeVariants::TyArray(_, _) => (4, 1),
93+
TypeVariants::TySlice(_) => (4, 2),
94+
95+
TypeVariants::TyRef(_, _) => (5, 1),
96+
TypeVariants::TyFnDef(_, _, _) => (5, 2),
97+
TypeVariants::TyFnPtr(_) => (5, 3),
98+
99+
TypeVariants::TyTrait(_) => (6, 1),
100+
101+
TypeVariants::TyClosure(_, _) => (7, 1),
102+
103+
TypeVariants::TyProjection(_) => (8, 1),
104+
TypeVariants::TyParam(_) => (8, 2),
105+
TypeVariants::TyInfer(_) => (8, 3),
106+
107+
TypeVariants::TyError => (9, 1),
108+
}
109+
}
110+
}
111+
112+
// The "closer" the types are, the lesser the weight.
113+
fn get_weight_diff(a: &ty::TypeVariants, b: &TypeVariants, weight: Weight) -> u32 {
114+
let (w1, w2) = match weight {
115+
Weight::Coarse => (a.get_weight().0, b.get_weight().0),
116+
Weight::Precise => (a.get_weight().1, b.get_weight().1),
117+
};
118+
if w1 < w2 {
119+
w2 - w1
120+
} else {
121+
w1 - w2
122+
}
123+
}
124+
125+
// Once we have "globally matching" types, we need to run another filter on them.
126+
//
127+
// In the function `get_best_matching_type`, we got the types which might fit the
128+
// most to the type we're looking for. This second filter now intends to get (if
129+
// possible) the type which fits the most.
130+
//
131+
// For example, the trait expects an `usize` and here you have `u32` and `i32`.
132+
// Obviously, the "correct" one is `u32`.
133+
fn filter_matching_types<'tcx>(weights: &[(usize, u32)],
134+
imps: &[(DefId, subst::Substs<'tcx>)],
135+
trait_types: &[ty::Ty<'tcx>])
136+
-> usize {
137+
let matching_weight = weights[0].1;
138+
let iter = weights.iter().filter(|&&(_, weight)| weight == matching_weight);
139+
let mut filtered_weights = vec!();
140+
141+
for &(pos, _) in iter {
142+
let mut weight = 0;
143+
for (type_to_compare, original_type) in imps[pos].1
144+
.types
145+
.get_slice(ParamSpace::TypeSpace)
146+
.iter()
147+
.zip(trait_types.iter()) {
148+
weight += get_weight_diff(&type_to_compare.sty, &original_type.sty, Weight::Precise);
149+
}
150+
filtered_weights.push((pos, weight));
151+
}
152+
filtered_weights.sort_by(|a, b| a.1.cmp(&b.1));
153+
filtered_weights[0].0
154+
}
155+
156+
// Here, we run the "big" filter. Little example:
157+
//
158+
// We receive a `String`, an `u32` and an `i32`.
159+
// The trait expected an `usize`.
160+
// From human point of view, it's easy to determine that `String` doesn't correspond to
161+
// the expected type at all whereas `u32` and `i32` could.
162+
//
163+
// This first filter intends to only keep the types which match the most.
164+
fn get_best_matching_type<'tcx>(imps: &[(DefId, subst::Substs<'tcx>)],
165+
trait_types: &[ty::Ty<'tcx>]) -> usize {
166+
let mut weights = vec!();
167+
for (pos, imp) in imps.iter().enumerate() {
168+
let mut weight = 0;
169+
for (type_to_compare, original_type) in imp.1
170+
.types
171+
.get_slice(ParamSpace::TypeSpace)
172+
.iter()
173+
.zip(trait_types.iter()) {
174+
weight += get_weight_diff(&type_to_compare.sty, &original_type.sty, Weight::Coarse);
175+
}
176+
weights.push((pos, weight));
177+
}
178+
weights.sort_by(|a, b| a.1.cmp(&b.1));
179+
if weights[0].1 == weights[1].1 {
180+
filter_matching_types(&weights, &imps, trait_types)
181+
} else {
182+
weights[0].0
183+
}
184+
}
185+
63186
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
64187
pub fn report_fulfillment_errors(&self, errors: &Vec<FulfillmentError<'tcx>>) {
65188
for error in errors {
@@ -126,16 +249,101 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
126249
}
127250
}
128251

252+
fn impl_substs(&self,
253+
did: DefId,
254+
obligation: PredicateObligation<'tcx>)
255+
-> subst::Substs<'tcx> {
256+
let tcx = self.tcx;
257+
258+
let ity = tcx.lookup_item_type(did);
259+
let (tps, rps, _) =
260+
(ity.generics.types.get_slice(subst::TypeSpace),
261+
ity.generics.regions.get_slice(subst::TypeSpace),
262+
ity.ty);
263+
264+
let rps = self.region_vars_for_defs(obligation.cause.span, rps);
265+
let mut substs = subst::Substs::new(
266+
subst::VecPerParamSpace::empty(),
267+
subst::VecPerParamSpace::new(rps, Vec::new(), Vec::new()));
268+
self.type_vars_for_defs(obligation.cause.span,
269+
subst::ParamSpace::TypeSpace,
270+
&mut substs,
271+
tps);
272+
substs
273+
}
274+
275+
fn get_current_failing_impl(&self,
276+
trait_ref: &TraitRef<'tcx>,
277+
obligation: &PredicateObligation<'tcx>)
278+
-> Option<(DefId, subst::Substs<'tcx>)> {
279+
let simp = fast_reject::simplify_type(self.tcx,
280+
trait_ref.self_ty(),
281+
true);
282+
let trait_def = self.tcx.lookup_trait_def(trait_ref.def_id);
283+
284+
match simp {
285+
Some(_) => {
286+
let mut matching_impls = Vec::new();
287+
trait_def.for_each_impl(self.tcx, |def_id| {
288+
let imp = self.tcx.impl_trait_ref(def_id).unwrap();
289+
let substs = self.impl_substs(def_id, obligation.clone());
290+
let imp = imp.subst(self.tcx, &substs);
291+
292+
if self.eq_types(true,
293+
TypeOrigin::Misc(obligation.cause.span),
294+
trait_ref.self_ty(),
295+
imp.self_ty()).is_ok() {
296+
matching_impls.push((def_id, imp.substs.clone()));
297+
}
298+
});
299+
if matching_impls.len() == 0 {
300+
None
301+
} else if matching_impls.len() == 1 {
302+
Some(matching_impls[0].clone())
303+
} else {
304+
let end = trait_ref.input_types().len() - 1;
305+
// we need to determine which type is the good one!
306+
Some(matching_impls[get_best_matching_type(&matching_impls,
307+
&trait_ref.input_types()[0..end])]
308+
.clone())
309+
}
310+
},
311+
None => None,
312+
}
313+
}
314+
315+
fn find_attr(&self,
316+
def_id: DefId,
317+
attr_name: &str)
318+
-> Option<ast::Attribute> {
319+
for item in self.tcx.get_attrs(def_id).iter() {
320+
if item.check_name(attr_name) {
321+
return Some(item.clone());
322+
}
323+
}
324+
None
325+
}
326+
129327
fn on_unimplemented_note(&self,
130328
trait_ref: ty::PolyTraitRef<'tcx>,
131-
span: Span) -> Option<String> {
329+
obligation: &PredicateObligation<'tcx>) -> Option<String> {
132330
let trait_ref = trait_ref.skip_binder();
133-
let def_id = trait_ref.def_id;
331+
let def_id = match self.get_current_failing_impl(trait_ref, obligation) {
332+
Some((def_id, _)) => {
333+
if let Some(_) = self.find_attr(def_id, "rustc_on_unimplemented") {
334+
def_id
335+
} else {
336+
trait_ref.def_id
337+
}
338+
},
339+
None => trait_ref.def_id,
340+
};
341+
let span = obligation.cause.span;
134342
let mut report = None;
135343
for item in self.tcx.get_attrs(def_id).iter() {
136344
if item.check_name("rustc_on_unimplemented") {
137345
let err_sp = item.meta().span.substitute_dummy(span);
138-
let def = self.tcx.lookup_trait_def(def_id);
346+
let def = self.tcx.lookup_trait_def(trait_ref.def_id);
139347
let trait_str = def.trait_ref.to_string();
140348
if let Some(ref istring) = item.value_str() {
141349
let mut generic_map = def.generics.types.iter_enumerated()
@@ -195,6 +403,37 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
195403
report
196404
}
197405

406+
fn find_similar_impl_candidates(&self,
407+
trait_ref: ty::PolyTraitRef<'tcx>)
408+
-> Vec<ty::TraitRef<'tcx>>
409+
{
410+
let simp = fast_reject::simplify_type(self.tcx,
411+
trait_ref.skip_binder().self_ty(),
412+
true);
413+
let mut impl_candidates = Vec::new();
414+
let trait_def = self.tcx.lookup_trait_def(trait_ref.def_id());
415+
416+
match simp {
417+
Some(simp) => trait_def.for_each_impl(self.tcx, |def_id| {
418+
let imp = self.tcx.impl_trait_ref(def_id).unwrap();
419+
let imp_simp = fast_reject::simplify_type(self.tcx,
420+
imp.self_ty(),
421+
true);
422+
if let Some(imp_simp) = imp_simp {
423+
if simp != imp_simp {
424+
return;
425+
}
426+
}
427+
impl_candidates.push(imp);
428+
}),
429+
None => trait_def.for_each_impl(self.tcx, |def_id| {
430+
impl_candidates.push(
431+
self.tcx.impl_trait_ref(def_id).unwrap());
432+
})
433+
};
434+
impl_candidates
435+
}
436+
198437
fn report_similar_impl_candidates(&self,
199438
trait_ref: ty::PolyTraitRef<'tcx>,
200439
err: &mut DiagnosticBuilder)
@@ -425,8 +664,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
425664
// Try to report a help message
426665

427666
if !trait_ref.has_infer_types() &&
428-
self.predicate_can_apply(trait_ref)
429-
{
667+
self.predicate_can_apply(trait_ref) {
430668
// If a where-clause may be useful, remind the
431669
// user that they can add it.
432670
//
@@ -435,22 +673,25 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
435673
// "the type `T` can't be frobnicated"
436674
// which is somewhat confusing.
437675
err.help(&format!("consider adding a `where {}` bound",
438-
trait_ref.to_predicate()
439-
));
440-
} else if let Some(s) =
441-
self.on_unimplemented_note(trait_ref, span) {
442-
// Otherwise, if there is an on-unimplemented note,
443-
// display it.
676+
trait_ref.to_predicate()));
677+
} else if let Some(s) = self.on_unimplemented_note(trait_ref,
678+
obligation) {
679+
// If it has a custom "#[rustc_on_unimplemented]"
680+
// error message, let's display it!
444681
err.note(&s);
445682
} else {
446683
// If we can't show anything useful, try to find
447684
// similar impls.
448-
449-
self.report_similar_impl_candidates(trait_ref, &mut err);
685+
let impl_candidates =
686+
self.find_similar_impl_candidates(trait_ref);
687+
if impl_candidates.len() > 0 {
688+
self.report_similar_impl_candidates(trait_ref, &mut err);
689+
}
450690
}
451691
err
452692
}
453-
},
693+
}
694+
454695
ty::Predicate::Equate(ref predicate) => {
455696
let predicate = self.resolve_type_vars_if_possible(predicate);
456697
let err = self.equality_predicate(span,

src/librustc/ty/subst.rs

-1
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);

0 commit comments

Comments
 (0)