Skip to content

Add rustc_on_unimplemented for Index implementation on slice #31071

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

Closed
Closed
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
1 change: 1 addition & 0 deletions src/libcore/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
// Since libcore defines many fundamental lang items, all tests live in a
// separate crate, libcoretest, to avoid bizarre issues.

#![cfg_attr(stage0, allow(unused_attributes))]
#![crate_name = "core"]
#![stable(feature = "core", since = "1.6.0")]
#![crate_type = "rlib"]
Expand Down
4 changes: 4 additions & 0 deletions src/libcore/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,8 @@ 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"]
impl<T> ops::Index<usize> for [T] {
type Output = T;

Expand All @@ -513,6 +515,8 @@ 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"]
impl<T> ops::IndexMut<usize> for [T] {
#[inline]
fn index_mut(&mut self, index: usize) -> &mut T {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ pub struct InferCtxt<'a, 'tcx: 'a> {

/// A map returned by `skolemize_late_bound_regions()` indicating the skolemized
/// region that each late-bound region was replaced with.
pub type SkolemizationMap = FnvHashMap<ty::BoundRegion,ty::Region>;
pub type SkolemizationMap = FnvHashMap<ty::BoundRegion, ty::Region>;

/// Why did we require that the two types be related?
///
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/infer/type_variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
let (relations, default) = match old_value {
Bounded { relations, default } => (relations, default),
Known(_) => panic!("Asked to instantiate variable that is \
already instantiated")
already instantiated")
};

for &(dir, vid) in &relations {
Expand Down
1 change: 0 additions & 1 deletion src/librustc/middle/subst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,6 @@ impl<'tcx> Substs<'tcx> {
}

impl<'tcx> Encodable for Substs<'tcx> {

fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
cstore::tls::with_encoding_context(s, |ecx, rbml_w| {
ecx.encode_substs(rbml_w, self);
Expand Down
154 changes: 132 additions & 22 deletions src/librustc/middle/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ use super::{

use fmt_macros::{Parser, Piece, Position};
use middle::def_id::DefId;
use middle::infer::InferCtxt;
use middle::infer::{self, InferCtxt, TypeOrigin};
use middle::ty::{self, ToPredicate, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable};
use middle::ty::fast_reject;
use middle::subst::{self, Subst};
use util::nodemap::{FnvHashMap, FnvHashSet};

use std::cmp;
use std::fmt;
use syntax::ast;
use syntax::attr::{AttributeMethods, AttrMetaMethods};
use syntax::codemap::Span;
use syntax::errors::DiagnosticBuilder;
Expand Down Expand Up @@ -105,15 +107,123 @@ pub fn report_projection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
}
}

fn impl_substs<'a, 'tcx>(fcx: &InferCtxt<'a, 'tcx>,
did: DefId,
obligation: PredicateObligation<'tcx>)
-> subst::Substs<'tcx> {
let tcx = fcx.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.ty);

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

fn check_type_parameters<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
trait_substs: &subst::Substs<'tcx>,
impl_substs: &subst::Substs<'tcx>,
obligation: &PredicateObligation<'tcx>) -> bool {
let trait_types = trait_substs.types.as_slice();
let impl_types = impl_substs.types.as_slice();

let mut failed = 0;
for index_to_ignore in 0..trait_types.len() {
for (index, (trait_type, impl_type)) in trait_types.iter()
.zip(impl_types.iter())
.enumerate() {
if index_to_ignore != index &&
infer::mk_eqty(infcx, true,
TypeOrigin::Misc(obligation.cause.span),
trait_type,
impl_type).is_err() {
failed += 1;
break;
}
}
}
failed == trait_types.len() - 1
}

fn get_current_failing_impl<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
trait_ref: &TraitRef<'tcx>,
obligation: &PredicateObligation<'tcx>)
-> Option<(DefId, subst::Substs<'tcx>)> {
let simp = fast_reject::simplify_type(infcx.tcx,
trait_ref.self_ty(),
true);
let trait_def = infcx.tcx.lookup_trait_def(trait_ref.def_id);

match simp {
Some(_) => {
let mut matching_impls = Vec::new();
trait_def.for_each_impl(infcx.tcx, |def_id| {
let imp = infcx.tcx.impl_trait_ref(def_id).unwrap();
let substs = impl_substs(infcx, def_id, obligation.clone());
let imp = imp.subst(infcx.tcx, &substs);

if infer::mk_eqty(infcx, true,
TypeOrigin::Misc(obligation.cause.span),
trait_ref.self_ty(),
imp.self_ty()).is_ok() {
if check_type_parameters(infcx, &trait_ref.substs, &imp.substs, obligation) {
matching_impls.push((def_id, imp.substs.clone()));
}
}
});
if matching_impls.len() == 0 {
None
} else if matching_impls.len() == 1 {
Some(matching_impls[0].clone())
} else {
// we need to determine which type is the good one!
Some(matching_impls[0].clone())
}
},
None => None,
}
}

fn find_attr<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
def_id: DefId,
attr_name: &str)
-> Option<ast::Attribute> {
for item in infcx.tcx.get_attrs(def_id).iter() {
if item.check_name(attr_name) {
return Some(item.clone());
}
}
None
}

fn report_on_unimplemented<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
trait_ref: &TraitRef<'tcx>,
span: Span) -> Option<String> {
let def_id = trait_ref.def_id;
obligation: &PredicateObligation<'tcx>)
-> Option<String> {
let def_id = match get_current_failing_impl(infcx, trait_ref, obligation) {
Some((def_id, _)) => {
if let Some(_) = find_attr(infcx, def_id, "rustc_on_unimplemented") {
def_id
} else {
trait_ref.def_id
}
},
None => trait_ref.def_id,
};
let span = obligation.cause.span;
let mut report = None;

for item in infcx.tcx.get_attrs(def_id).iter() {
if item.check_name("rustc_on_unimplemented") {
let err_sp = item.meta().span.substitute_dummy(span);
let def = infcx.tcx.lookup_trait_def(def_id);
let def = infcx.tcx.lookup_trait_def(trait_ref.def_id);
let trait_str = def.trait_ref.to_string();
if let Some(ref istring) = item.value_str() {
let mut generic_map = def.generics.types.iter_enumerated()
Expand All @@ -134,24 +244,24 @@ fn report_on_unimplemented<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
Some(val) => Some(val),
None => {
span_err!(infcx.tcx.sess, err_sp, E0272,
"the #[rustc_on_unimplemented] \
attribute on \
trait definition for {} refers to \
non-existent type parameter {}",
trait_str, s);
"the #[rustc_on_unimplemented] \
attribute on \
trait definition for {} refers to \
non-existent type parameter {}",
trait_str, s);
errored = true;
None
}
},
_ => {
span_err!(infcx.tcx.sess, err_sp, E0273,
"the #[rustc_on_unimplemented] \
attribute on \
trait definition for {} must have named \
format arguments, \
eg `#[rustc_on_unimplemented = \
\"foo {{T}}\"]`",
trait_str);
span_err!(infcx.tcx.sess, err_sp, E0273,
"the #[rustc_on_unimplemented] \
attribute on \
trait definition for {} must have named \
format arguments, \
eg `#[rustc_on_unimplemented = \
\"foo {{T}}\"]`",
trait_str);
errored = true;
None
}
Expand All @@ -164,10 +274,10 @@ fn report_on_unimplemented<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
}
} else {
span_err!(infcx.tcx.sess, err_sp, E0274,
"the #[rustc_on_unimplemented] attribute on \
trait definition for {} must have a value, \
eg `#[rustc_on_unimplemented = \"foo\"]`",
trait_str);
"the #[rustc_on_unimplemented] attribute on \
trait definition for {} must have a value, \
eg `#[rustc_on_unimplemented = \"foo\"]`",
trait_str);
}
break;
}
Expand Down Expand Up @@ -368,7 +478,7 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
// Check if it has a custom "#[rustc_on_unimplemented]"
// error message, report with that message if it does
let custom_note = report_on_unimplemented(infcx, &trait_ref.0,
obligation.cause.span);
obligation);
if let Some(s) = custom_note {
err.fileline_note(obligation.cause.span, &s);
} else {
Expand Down
8 changes: 4 additions & 4 deletions src/librustc/middle/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,10 @@ fn consider_unification_despite_ambiguity<'cx,'tcx>(selcx: &mut SelectionContext
/// them with a fully resolved type where possible. The return value
/// combines the normalized result and any additional obligations that
/// were incurred as result.
pub fn normalize<'a,'b,'tcx,T>(selcx: &'a mut SelectionContext<'b,'tcx>,
cause: ObligationCause<'tcx>,
value: &T)
-> Normalized<'tcx, T>
pub fn normalize<'a, 'b, 'tcx, T>(selcx: &'a mut SelectionContext<'b,'tcx>,
cause: ObligationCause<'tcx>,
value: &T)
-> Normalized<'tcx, T>
where T : TypeFoldable<'tcx>
{
normalize_with_depth(selcx, cause, 0, value)
Expand Down
34 changes: 34 additions & 0 deletions src/test/compile-fail/check_on_unimplemented.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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)]

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

#[rustc_on_unimplemented = "a usize is required to index into a slice"]
impl Index<usize> for [i32] {
type Output = i32;
fn index(&self, index: usize) -> &i32 {
&self[index]
}
}

#[rustc_error]
fn main() {
Index::<u32>::index(&[1, 2, 3] as &[i32], 2u32); //~ ERROR E0277
//~| NOTE a usize is required
}
20 changes: 20 additions & 0 deletions src/test/compile-fail/check_on_unimplemented_on_slice.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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 new Index error message for slices

#![feature(rustc_attrs)]

#[rustc_error]
fn main() {
let x = &[1, 2, 3] as &[i32];
x[1i32]; //~ ERROR E0277
//~| NOTE a usize is required
}