Auto merge of #33694 - arielb1:fuzzy-on-unimplemented, r=nikomatsakis
implement fuzzy matching in on_unimplemented Fixes #31062 r? @nikomatsakis
This commit is contained in:
commit
d0ca0ca063
@ -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;
|
||||
|
||||
@ -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 {
|
||||
@ -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];
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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] {
|
||||
@ -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] {
|
||||
@ -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] {
|
||||
@ -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] {
|
||||
@ -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] {
|
||||
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
@ -135,8 +135,8 @@ 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);
|
||||
@ -144,56 +144,102 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
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>
|
||||
{
|
||||
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();
|
||||
|
||||
if trait_self_ty.is_ty_var() {
|
||||
return None;
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
self.tcx.lookup_trait_def(trait_ref.def_id())
|
||||
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 trait_ref = tcx.erase_late_bound_regions(&trait_ref);
|
||||
let trait_self_ty = trait_ref.self_ty();
|
||||
|
||||
let mut self_match_impls = vec![];
|
||||
let mut fuzzy_match_impls = vec![];
|
||||
|
||||
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();
|
||||
|
||||
|
@ -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")
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
@ -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
|
||||
@ -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
|
||||
|
55
src/test/compile-fail/on-unimplemented/multiple-impls.rs
Normal file
55
src/test/compile-fail/on-unimplemented/multiple-impls.rs
Normal file
@ -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
|
||||
}
|
@ -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`
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user