Skip to content

Commit d0ca0ca

Browse files
committed
Auto merge of rust-lang#33694 - arielb1:fuzzy-on-unimplemented, r=nikomatsakis
implement fuzzy matching in on_unimplemented Fixes rust-lang#31062 r? @nikomatsakis
2 parents 0c5d651 + b9a201c commit d0ca0ca

File tree

9 files changed

+165
-46
lines changed

9 files changed

+165
-46
lines changed

src/libcore/slice.rs

+12-5
Original file line numberDiff line numberDiff line change
@@ -523,8 +523,7 @@ 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"]
526+
#[rustc_on_unimplemented = "slice indices are of type `usize`"]
528527
impl<T> ops::Index<usize> for [T] {
529528
type Output = T;
530529

@@ -535,8 +534,7 @@ impl<T> ops::Index<usize> for [T] {
535534
}
536535

537536
#[stable(feature = "rust1", since = "1.0.0")]
538-
#[allow(unused_attributes)]
539-
#[rustc_on_unimplemented = "a usize is required to index into a slice"]
537+
#[rustc_on_unimplemented = "slice indices are of type `usize`"]
540538
impl<T> ops::IndexMut<usize> for [T] {
541539
#[inline]
542540
fn index_mut(&mut self, index: usize) -> &mut T {
@@ -570,6 +568,7 @@ fn slice_index_order_fail(index: usize, end: usize) -> ! {
570568
/// Requires that `begin <= end` and `end <= self.len()`,
571569
/// otherwise slicing will panic.
572570
#[stable(feature = "rust1", since = "1.0.0")]
571+
#[rustc_on_unimplemented = "slice indices are of type `usize`"]
573572
impl<T> ops::Index<ops::Range<usize>> for [T] {
574573
type Output = [T];
575574

@@ -596,6 +595,7 @@ impl<T> ops::Index<ops::Range<usize>> for [T] {
596595
///
597596
/// Equivalent to `&self[0 .. end]`
598597
#[stable(feature = "rust1", since = "1.0.0")]
598+
#[rustc_on_unimplemented = "slice indices are of type `usize`"]
599599
impl<T> ops::Index<ops::RangeTo<usize>> for [T] {
600600
type Output = [T];
601601

@@ -611,6 +611,7 @@ impl<T> ops::Index<ops::RangeTo<usize>> for [T] {
611611
///
612612
/// Equivalent to `&self[begin .. self.len()]`
613613
#[stable(feature = "rust1", since = "1.0.0")]
614+
#[rustc_on_unimplemented = "slice indices are of type `usize`"]
614615
impl<T> ops::Index<ops::RangeFrom<usize>> for [T] {
615616
type Output = [T];
616617

@@ -636,6 +637,7 @@ impl<T> ops::Index<RangeFull> for [T] {
636637
}
637638

638639
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
640+
#[rustc_on_unimplemented = "slice indices are of type `usize`"]
639641
impl<T> ops::Index<ops::RangeInclusive<usize>> for [T] {
640642
type Output = [T];
641643

@@ -651,6 +653,7 @@ impl<T> ops::Index<ops::RangeInclusive<usize>> for [T] {
651653
}
652654
}
653655
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
656+
#[rustc_on_unimplemented = "slice indices are of type `usize`"]
654657
impl<T> ops::Index<ops::RangeToInclusive<usize>> for [T] {
655658
type Output = [T];
656659

@@ -671,6 +674,7 @@ impl<T> ops::Index<ops::RangeToInclusive<usize>> for [T] {
671674
/// Requires that `begin <= end` and `end <= self.len()`,
672675
/// otherwise slicing will panic.
673676
#[stable(feature = "rust1", since = "1.0.0")]
677+
#[rustc_on_unimplemented = "slice indices are of type `usize`"]
674678
impl<T> ops::IndexMut<ops::Range<usize>> for [T] {
675679
#[inline]
676680
fn index_mut(&mut self, index: ops::Range<usize>) -> &mut [T] {
@@ -695,6 +699,7 @@ impl<T> ops::IndexMut<ops::Range<usize>> for [T] {
695699
///
696700
/// Equivalent to `&mut self[0 .. end]`
697701
#[stable(feature = "rust1", since = "1.0.0")]
702+
#[rustc_on_unimplemented = "slice indices are of type `usize`"]
698703
impl<T> ops::IndexMut<ops::RangeTo<usize>> for [T] {
699704
#[inline]
700705
fn index_mut(&mut self, index: ops::RangeTo<usize>) -> &mut [T] {
@@ -708,6 +713,7 @@ impl<T> ops::IndexMut<ops::RangeTo<usize>> for [T] {
708713
///
709714
/// Equivalent to `&mut self[begin .. self.len()]`
710715
#[stable(feature = "rust1", since = "1.0.0")]
716+
#[rustc_on_unimplemented = "slice indices are of type `usize`"]
711717
impl<T> ops::IndexMut<ops::RangeFrom<usize>> for [T] {
712718
#[inline]
713719
fn index_mut(&mut self, index: ops::RangeFrom<usize>) -> &mut [T] {
@@ -730,6 +736,7 @@ impl<T> ops::IndexMut<RangeFull> for [T] {
730736
}
731737

732738
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
739+
#[rustc_on_unimplemented = "slice indices are of type `usize`"]
733740
impl<T> ops::IndexMut<ops::RangeInclusive<usize>> for [T] {
734741
#[inline]
735742
fn index_mut(&mut self, index: ops::RangeInclusive<usize>) -> &mut [T] {
@@ -743,6 +750,7 @@ impl<T> ops::IndexMut<ops::RangeInclusive<usize>> for [T] {
743750
}
744751
}
745752
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
753+
#[rustc_on_unimplemented = "slice indices are of type `usize`"]
746754
impl<T> ops::IndexMut<ops::RangeToInclusive<usize>> for [T] {
747755
#[inline]
748756
fn index_mut(&mut self, index: ops::RangeToInclusive<usize>) -> &mut [T] {
@@ -1933,4 +1941,3 @@ macro_rules! impl_marker_for {
19331941

19341942
impl_marker_for!(BytewiseEquality,
19351943
u8 i8 u16 i16 u32 i32 u64 i64 usize isize char bool);
1936-

src/librustc/traits/error_reporting.rs

+72-26
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use infer::{InferCtxt};
3030
use ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable};
3131
use ty::fast_reject;
3232
use ty::fold::TypeFolder;
33-
use ty::subst::{self, Subst};
33+
use ty::subst::{self, Subst, TypeSpace};
3434
use util::nodemap::{FnvHashMap, FnvHashSet};
3535

3636
use std::cmp;
@@ -135,65 +135,111 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
135135

136136
let ity = tcx.lookup_item_type(did);
137137
let (tps, rps, _) =
138-
(ity.generics.types.get_slice(subst::TypeSpace),
139-
ity.generics.regions.get_slice(subst::TypeSpace),
138+
(ity.generics.types.get_slice(TypeSpace),
139+
ity.generics.regions.get_slice(TypeSpace),
140140
ity.ty);
141141

142142
let rps = self.region_vars_for_defs(obligation.cause.span, rps);
143143
let mut substs = subst::Substs::new(
144144
subst::VecPerParamSpace::empty(),
145145
subst::VecPerParamSpace::new(rps, Vec::new(), Vec::new()));
146146
self.type_vars_for_defs(obligation.cause.span,
147-
subst::ParamSpace::TypeSpace,
147+
TypeSpace,
148148
&mut substs,
149149
tps);
150150
substs
151151
}
152152

153-
fn impl_with_self_type_of(&self,
154-
trait_ref: ty::PolyTraitRef<'tcx>,
155-
obligation: &PredicateObligation<'tcx>)
156-
-> Option<DefId>
153+
fn fuzzy_match_tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
154+
/// returns the fuzzy category of a given type, or None
155+
/// if the type can be equated to any type.
156+
fn type_category<'tcx>(t: Ty<'tcx>) -> Option<u32> {
157+
match t.sty {
158+
ty::TyBool => Some(0),
159+
ty::TyChar => Some(1),
160+
ty::TyStr => Some(2),
161+
ty::TyInt(..) | ty::TyUint(..) |
162+
ty::TyInfer(ty::IntVar(..)) => Some(3),
163+
ty::TyFloat(..) | ty::TyInfer(ty::FloatVar(..)) => Some(4),
164+
ty::TyEnum(..) => Some(5),
165+
ty::TyStruct(..) => Some(6),
166+
ty::TyBox(..) | ty::TyRef(..) | ty::TyRawPtr(..) => Some(7),
167+
ty::TyArray(..) | ty::TySlice(..) => Some(8),
168+
ty::TyFnDef(..) | ty::TyFnPtr(..) => Some(9),
169+
ty::TyTrait(..) => Some(10),
170+
ty::TyClosure(..) => Some(11),
171+
ty::TyTuple(..) => Some(12),
172+
ty::TyProjection(..) => Some(13),
173+
ty::TyParam(..) => Some(14),
174+
ty::TyInfer(..) | ty::TyError => None
175+
}
176+
}
177+
178+
match (type_category(a), type_category(b)) {
179+
(Some(cat_a), Some(cat_b)) => match (&a.sty, &b.sty) {
180+
(&ty::TyStruct(def_a, _), &ty::TyStruct(def_b, _)) |
181+
(&ty::TyEnum(def_a, _), &ty::TyEnum(def_b, _)) =>
182+
def_a == def_b,
183+
_ => cat_a == cat_b
184+
},
185+
// infer and error can be equated to all types
186+
_ => true
187+
}
188+
}
189+
190+
fn impl_similar_to(&self,
191+
trait_ref: ty::PolyTraitRef<'tcx>,
192+
obligation: &PredicateObligation<'tcx>)
193+
-> Option<DefId>
157194
{
158195
let tcx = self.tcx;
159-
let mut result = None;
160-
let mut ambiguous = false;
161196

162-
let trait_self_ty = tcx.erase_late_bound_regions(&trait_ref).self_ty();
197+
let trait_ref = tcx.erase_late_bound_regions(&trait_ref);
198+
let trait_self_ty = trait_ref.self_ty();
163199

164-
if trait_self_ty.is_ty_var() {
165-
return None;
166-
}
200+
let mut self_match_impls = vec![];
201+
let mut fuzzy_match_impls = vec![];
167202

168-
self.tcx.lookup_trait_def(trait_ref.def_id())
203+
self.tcx.lookup_trait_def(trait_ref.def_id)
169204
.for_each_relevant_impl(self.tcx, trait_self_ty, |def_id| {
170-
let impl_self_ty = tcx
205+
let impl_trait_ref = tcx
171206
.impl_trait_ref(def_id)
172207
.unwrap()
173-
.self_ty()
174208
.subst(tcx, &self.impl_substs(def_id, obligation.clone()));
175209

176-
if !tcx.has_attr(def_id, "rustc_on_unimplemented") {
177-
return;
178-
}
210+
let impl_self_ty = impl_trait_ref.self_ty();
179211

180212
if let Ok(..) = self.can_equate(&trait_self_ty, &impl_self_ty) {
181-
ambiguous = result.is_some();
182-
result = Some(def_id);
213+
self_match_impls.push(def_id);
214+
215+
if trait_ref.substs.types.get_slice(TypeSpace).iter()
216+
.zip(impl_trait_ref.substs.types.get_slice(TypeSpace))
217+
.all(|(u,v)| self.fuzzy_match_tys(u, v))
218+
{
219+
fuzzy_match_impls.push(def_id);
220+
}
183221
}
184222
});
185223

186-
if ambiguous {
187-
None
224+
let impl_def_id = if self_match_impls.len() == 1 {
225+
self_match_impls[0]
226+
} else if fuzzy_match_impls.len() == 1 {
227+
fuzzy_match_impls[0]
188228
} else {
189-
result
229+
return None
230+
};
231+
232+
if tcx.has_attr(impl_def_id, "rustc_on_unimplemented") {
233+
Some(impl_def_id)
234+
} else {
235+
None
190236
}
191237
}
192238

193239
fn on_unimplemented_note(&self,
194240
trait_ref: ty::PolyTraitRef<'tcx>,
195241
obligation: &PredicateObligation<'tcx>) -> Option<String> {
196-
let def_id = self.impl_with_self_type_of(trait_ref, obligation)
242+
let def_id = self.impl_similar_to(trait_ref, obligation)
197243
.unwrap_or(trait_ref.def_id());
198244
let trait_ref = trait_ref.skip_binder();
199245

src/librustc_metadata/common.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,7 @@ pub const tag_rustc_version: usize = 0x10f;
247247
pub fn rustc_version() -> String {
248248
format!(
249249
"rustc {}",
250-
// option_env!("CFG_VERSION").unwrap_or("unknown version")
251-
"nightly edition"
250+
option_env!("CFG_VERSION").unwrap_or("unknown version")
252251
)
253252
}
254253

src/librustc_typeck/check/mod.rs

+22-12
Original file line numberDiff line numberDiff line change
@@ -732,17 +732,26 @@ pub fn check_item_type<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx hir::Item) {
732732
let impl_def_id = ccx.tcx.map.local_def_id(it.id);
733733
match ccx.tcx.impl_trait_ref(impl_def_id) {
734734
Some(impl_trait_ref) => {
735-
check_impl_items_against_trait(ccx,
736-
it.span,
737-
impl_def_id,
738-
&impl_trait_ref,
739-
impl_items);
735+
let trait_def_id = impl_trait_ref.def_id;
736+
737+
check_impl_items_against_trait(ccx,
738+
it.span,
739+
impl_def_id,
740+
&impl_trait_ref,
741+
impl_items);
742+
check_on_unimplemented(
743+
ccx,
744+
&ccx.tcx.lookup_trait_def(trait_def_id).generics,
745+
it,
746+
ccx.tcx.item_name(trait_def_id));
740747
}
741748
None => { }
742749
}
743750
}
744-
hir::ItemTrait(_, ref generics, _, _) => {
745-
check_trait_on_unimplemented(ccx, generics, it);
751+
hir::ItemTrait(..) => {
752+
let def_id = ccx.tcx.map.local_def_id(it.id);
753+
let generics = &ccx.tcx.lookup_trait_def(def_id).generics;
754+
check_on_unimplemented(ccx, generics, it, it.name);
746755
}
747756
hir::ItemStruct(..) => {
748757
check_struct(ccx, it.id, it.span);
@@ -854,15 +863,16 @@ fn check_trait_fn_not_const<'a,'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
854863
}
855864
}
856865

857-
fn check_trait_on_unimplemented<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
858-
generics: &hir::Generics,
859-
item: &hir::Item) {
866+
fn check_on_unimplemented<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
867+
generics: &ty::Generics,
868+
item: &hir::Item,
869+
name: ast::Name) {
860870
if let Some(ref attr) = item.attrs.iter().find(|a| {
861871
a.check_name("rustc_on_unimplemented")
862872
}) {
863873
if let Some(ref istring) = attr.value_str() {
864874
let parser = Parser::new(&istring);
865-
let types = &generics.ty_params;
875+
let types = &generics.types;
866876
for token in parser {
867877
match token {
868878
Piece::String(_) => (), // Normal string, no need to check it
@@ -878,7 +888,7 @@ fn check_trait_on_unimplemented<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
878888
span_err!(ccx.tcx.sess, attr.span, E0230,
879889
"there is no type parameter \
880890
{} on trait {}",
881-
s, item.name);
891+
s, name);
882892
}
883893
},
884894
// `{:1}` and `{}` are not to be used
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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+
struct Foo<T>(T);
17+
struct Bar<T>(T);
18+
19+
#[rustc_on_unimplemented = "trait message"]
20+
trait Index<Idx: ?Sized> {
21+
type Output: ?Sized;
22+
fn index(&self, index: Idx) -> &Self::Output;
23+
}
24+
25+
#[rustc_on_unimplemented = "on impl for Foo"]
26+
impl Index<Foo<usize>> for [i32] {
27+
type Output = i32;
28+
fn index(&self, _index: Foo<usize>) -> &i32 {
29+
loop {}
30+
}
31+
}
32+
33+
#[rustc_on_unimplemented = "on impl for Bar"]
34+
impl Index<Bar<usize>> for [i32] {
35+
type Output = i32;
36+
fn index(&self, _index: Bar<usize>) -> &i32 {
37+
loop {}
38+
}
39+
}
40+
41+
#[rustc_error]
42+
fn main() {
43+
Index::index(&[] as &[i32], 2u32);
44+
//~^ ERROR E0277
45+
//~| NOTE trait message
46+
//~| NOTE required by
47+
Index::index(&[] as &[i32], Foo(2u32));
48+
//~^ ERROR E0277
49+
//~| NOTE on impl for Foo
50+
//~| NOTE required by
51+
Index::index(&[] as &[i32], Bar(2u32));
52+
//~^ ERROR E0277
53+
//~| NOTE on impl for Bar
54+
//~| NOTE required by
55+
}

src/test/compile-fail/check_on_unimplemented_on_slice.rs renamed to src/test/compile-fail/on-unimplemented/slice-index.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,7 @@ use std::ops::Index;
1818
fn main() {
1919
let x = &[1, 2, 3] as &[i32];
2020
x[1i32]; //~ ERROR E0277
21-
//~| NOTE a usize is required
21+
//~| NOTE slice indices are of type `usize`
22+
x[..1i32]; //~ ERROR E0277
23+
//~| NOTE slice indices are of type `usize`
2224
}

0 commit comments

Comments
 (0)