Skip to content

Commit

Permalink
implement fuzzy matching in on_unimplemented
Browse files Browse the repository at this point in the history
  • Loading branch information
arielb1 committed May 18, 2016
1 parent 310d899 commit b344c71
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 46 deletions.
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
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
}
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
}
File renamed without changes.

0 comments on commit b344c71

Please sign in to comment.