Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement fuzzy matching in on_unimplemented #33694

Merged
merged 2 commits into from
May 19, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions src/libcore/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -523,8 +523,7 @@ 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"]
#[rustc_on_unimplemented = "slice indices are of type `usize`"]
impl<T> ops::Index<usize> for [T] {
type Output = T;

Expand All @@ -535,8 +534,7 @@ 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"]
#[rustc_on_unimplemented = "slice indices are of type `usize`"]
impl<T> ops::IndexMut<usize> for [T] {
#[inline]
fn index_mut(&mut self, index: usize) -> &mut T {
Expand Down Expand Up @@ -570,6 +568,7 @@ fn slice_index_order_fail(index: usize, end: usize) -> ! {
/// Requires that `begin <= end` and `end <= self.len()`,
/// otherwise slicing will panic.
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_on_unimplemented = "slice indices are of type `usize`"]
impl<T> ops::Index<ops::Range<usize>> for [T] {
type Output = [T];

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

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

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

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

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

Expand All @@ -671,6 +674,7 @@ impl<T> ops::Index<ops::RangeToInclusive<usize>> for [T] {
/// Requires that `begin <= end` and `end <= self.len()`,
/// otherwise slicing will panic.
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_on_unimplemented = "slice indices are of type `usize`"]
impl<T> ops::IndexMut<ops::Range<usize>> for [T] {
#[inline]
fn index_mut(&mut self, index: ops::Range<usize>) -> &mut [T] {
Expand All @@ -695,6 +699,7 @@ impl<T> ops::IndexMut<ops::Range<usize>> for [T] {
///
/// Equivalent to `&mut self[0 .. end]`
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_on_unimplemented = "slice indices are of type `usize`"]
impl<T> ops::IndexMut<ops::RangeTo<usize>> for [T] {
#[inline]
fn index_mut(&mut self, index: ops::RangeTo<usize>) -> &mut [T] {
Expand All @@ -708,6 +713,7 @@ impl<T> ops::IndexMut<ops::RangeTo<usize>> for [T] {
///
/// Equivalent to `&mut self[begin .. self.len()]`
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_on_unimplemented = "slice indices are of type `usize`"]
impl<T> ops::IndexMut<ops::RangeFrom<usize>> for [T] {
#[inline]
fn index_mut(&mut self, index: ops::RangeFrom<usize>) -> &mut [T] {
Expand All @@ -730,6 +736,7 @@ impl<T> ops::IndexMut<RangeFull> for [T] {
}

#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
#[rustc_on_unimplemented = "slice indices are of type `usize`"]
impl<T> ops::IndexMut<ops::RangeInclusive<usize>> for [T] {
#[inline]
fn index_mut(&mut self, index: ops::RangeInclusive<usize>) -> &mut [T] {
Expand All @@ -743,6 +750,7 @@ impl<T> ops::IndexMut<ops::RangeInclusive<usize>> for [T] {
}
}
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
#[rustc_on_unimplemented = "slice indices are of type `usize`"]
impl<T> ops::IndexMut<ops::RangeToInclusive<usize>> for [T] {
#[inline]
fn index_mut(&mut self, index: ops::RangeToInclusive<usize>) -> &mut [T] {
Expand Down Expand Up @@ -1933,4 +1941,3 @@ macro_rules! impl_marker_for {

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

98 changes: 72 additions & 26 deletions src/librustc/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use infer::{InferCtxt};
use ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable};
use ty::fast_reject;
use ty::fold::TypeFolder;
use ty::subst::{self, Subst};
use ty::subst::{self, Subst, TypeSpace};
use util::nodemap::{FnvHashMap, FnvHashSet};

use std::cmp;
Expand Down Expand Up @@ -135,65 +135,111 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, '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.generics.types.get_slice(TypeSpace),
ity.generics.regions.get_slice(TypeSpace),
ity.ty);

let rps = self.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()));
self.type_vars_for_defs(obligation.cause.span,
subst::ParamSpace::TypeSpace,
TypeSpace,
&mut substs,
tps);
substs
}

fn impl_with_self_type_of(&self,
trait_ref: ty::PolyTraitRef<'tcx>,
obligation: &PredicateObligation<'tcx>)
-> Option<DefId>
fn fuzzy_match_tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
/// returns the fuzzy category of a given type, or None
/// if the type can be equated to any type.
fn type_category<'tcx>(t: Ty<'tcx>) -> Option<u32> {
match t.sty {
ty::TyBool => Some(0),
ty::TyChar => Some(1),
ty::TyStr => Some(2),
ty::TyInt(..) | ty::TyUint(..) |
ty::TyInfer(ty::IntVar(..)) => Some(3),
ty::TyFloat(..) | ty::TyInfer(ty::FloatVar(..)) => Some(4),
ty::TyEnum(..) => Some(5),
ty::TyStruct(..) => Some(6),
ty::TyBox(..) | ty::TyRef(..) | ty::TyRawPtr(..) => Some(7),
ty::TyArray(..) | ty::TySlice(..) => Some(8),
ty::TyFnDef(..) | ty::TyFnPtr(..) => Some(9),
ty::TyTrait(..) => Some(10),
ty::TyClosure(..) => Some(11),
ty::TyTuple(..) => Some(12),
ty::TyProjection(..) => Some(13),
ty::TyParam(..) => Some(14),
ty::TyInfer(..) | ty::TyError => None
}
}

match (type_category(a), type_category(b)) {
(Some(cat_a), Some(cat_b)) => match (&a.sty, &b.sty) {
(&ty::TyStruct(def_a, _), &ty::TyStruct(def_b, _)) |
(&ty::TyEnum(def_a, _), &ty::TyEnum(def_b, _)) =>
def_a == def_b,
_ => cat_a == cat_b
},
// infer and error can be equated to all types
_ => true
}
}

fn impl_similar_to(&self,
trait_ref: ty::PolyTraitRef<'tcx>,
obligation: &PredicateObligation<'tcx>)
-> Option<DefId>
{
let tcx = self.tcx;
let mut result = None;
let mut ambiguous = false;

let trait_self_ty = tcx.erase_late_bound_regions(&trait_ref).self_ty();
let trait_ref = tcx.erase_late_bound_regions(&trait_ref);
let trait_self_ty = trait_ref.self_ty();

if trait_self_ty.is_ty_var() {
return None;
}
let mut self_match_impls = vec![];
let mut fuzzy_match_impls = vec![];

self.tcx.lookup_trait_def(trait_ref.def_id())
self.tcx.lookup_trait_def(trait_ref.def_id)
.for_each_relevant_impl(self.tcx, trait_self_ty, |def_id| {
let impl_self_ty = tcx
let impl_trait_ref = tcx
.impl_trait_ref(def_id)
.unwrap()
.self_ty()
.subst(tcx, &self.impl_substs(def_id, obligation.clone()));

if !tcx.has_attr(def_id, "rustc_on_unimplemented") {
return;
}
let impl_self_ty = impl_trait_ref.self_ty();

if let Ok(..) = self.can_equate(&trait_self_ty, &impl_self_ty) {
ambiguous = result.is_some();
result = Some(def_id);
self_match_impls.push(def_id);

if trait_ref.substs.types.get_slice(TypeSpace).iter()
.zip(impl_trait_ref.substs.types.get_slice(TypeSpace))
.all(|(u,v)| self.fuzzy_match_tys(u, v))
{
fuzzy_match_impls.push(def_id);
}
}
});

if ambiguous {
None
let impl_def_id = if self_match_impls.len() == 1 {
self_match_impls[0]
} else if fuzzy_match_impls.len() == 1 {
fuzzy_match_impls[0]
} else {
result
return None
};

if tcx.has_attr(impl_def_id, "rustc_on_unimplemented") {
Some(impl_def_id)
} else {
None
}
}

fn on_unimplemented_note(&self,
trait_ref: ty::PolyTraitRef<'tcx>,
obligation: &PredicateObligation<'tcx>) -> Option<String> {
let def_id = self.impl_with_self_type_of(trait_ref, obligation)
let def_id = self.impl_similar_to(trait_ref, obligation)
.unwrap_or(trait_ref.def_id());
let trait_ref = trait_ref.skip_binder();

Expand Down
3 changes: 1 addition & 2 deletions src/librustc_metadata/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,7 @@ pub const tag_rustc_version: usize = 0x10f;
pub fn rustc_version() -> String {
format!(
"rustc {}",
// option_env!("CFG_VERSION").unwrap_or("unknown version")
"nightly edition"
option_env!("CFG_VERSION").unwrap_or("unknown version")
)
}

Expand Down
34 changes: 22 additions & 12 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -732,17 +732,26 @@ pub fn check_item_type<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx hir::Item) {
let impl_def_id = ccx.tcx.map.local_def_id(it.id);
match ccx.tcx.impl_trait_ref(impl_def_id) {
Some(impl_trait_ref) => {
check_impl_items_against_trait(ccx,
it.span,
impl_def_id,
&impl_trait_ref,
impl_items);
let trait_def_id = impl_trait_ref.def_id;

check_impl_items_against_trait(ccx,
it.span,
impl_def_id,
&impl_trait_ref,
impl_items);
check_on_unimplemented(
ccx,
&ccx.tcx.lookup_trait_def(trait_def_id).generics,
it,
ccx.tcx.item_name(trait_def_id));
}
None => { }
}
}
hir::ItemTrait(_, ref generics, _, _) => {
check_trait_on_unimplemented(ccx, generics, it);
hir::ItemTrait(..) => {
let def_id = ccx.tcx.map.local_def_id(it.id);
let generics = &ccx.tcx.lookup_trait_def(def_id).generics;
check_on_unimplemented(ccx, generics, it, it.name);
}
hir::ItemStruct(..) => {
check_struct(ccx, it.id, it.span);
Expand Down Expand Up @@ -854,15 +863,16 @@ fn check_trait_fn_not_const<'a,'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
}
}

fn check_trait_on_unimplemented<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
generics: &hir::Generics,
item: &hir::Item) {
fn check_on_unimplemented<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
generics: &ty::Generics,
item: &hir::Item,
name: ast::Name) {
if let Some(ref attr) = item.attrs.iter().find(|a| {
a.check_name("rustc_on_unimplemented")
}) {
if let Some(ref istring) = attr.value_str() {
let parser = Parser::new(&istring);
let types = &generics.ty_params;
let types = &generics.types;
for token in parser {
match token {
Piece::String(_) => (), // Normal string, no need to check it
Expand All @@ -878,7 +888,7 @@ fn check_trait_on_unimplemented<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
span_err!(ccx.tcx.sess, attr.span, E0230,
"there is no type parameter \
{} on trait {}",
s, item.name);
s, name);
}
},
// `{:1}` and `{}` are not to be used
Expand Down
55 changes: 55 additions & 0 deletions src/test/compile-fail/on-unimplemented/multiple-impls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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)]

struct Foo<T>(T);
struct Bar<T>(T);

#[rustc_on_unimplemented = "trait message"]
trait Index<Idx: ?Sized> {
type Output: ?Sized;
fn index(&self, index: Idx) -> &Self::Output;
}

#[rustc_on_unimplemented = "on impl for Foo"]
impl Index<Foo<usize>> for [i32] {
type Output = i32;
fn index(&self, _index: Foo<usize>) -> &i32 {
loop {}
}
}

#[rustc_on_unimplemented = "on impl for Bar"]
impl Index<Bar<usize>> for [i32] {
type Output = i32;
fn index(&self, _index: Bar<usize>) -> &i32 {
loop {}
}
}

#[rustc_error]
fn main() {
Index::index(&[] as &[i32], 2u32);
//~^ ERROR E0277
//~| NOTE trait message
//~| NOTE required by
Index::index(&[] as &[i32], Foo(2u32));
//~^ ERROR E0277
//~| NOTE on impl for Foo
//~| NOTE required by
Index::index(&[] as &[i32], Bar(2u32));
//~^ ERROR E0277
//~| NOTE on impl for Bar
//~| NOTE required by
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,7 @@ use std::ops::Index;
fn main() {
let x = &[1, 2, 3] as &[i32];
x[1i32]; //~ ERROR E0277
//~| NOTE a usize is required
//~| NOTE slice indices are of type `usize`
x[..1i32]; //~ ERROR E0277
//~| NOTE slice indices are of type `usize`
}