Skip to content

Rollup of 3 pull requests #33562

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

Merged
merged 10 commits into from
May 12, 2016
20 changes: 20 additions & 0 deletions Makefile.in
Original file line number Diff line number Diff line change
@@ -107,6 +107,20 @@
#
# run `make nitty-gritty`
#
# # Make command examples
#
# ## Docs linked commands
#
# * make check-stage1-rustdocck: Builds rustdoc. It has the advantage to compile
# quite quickly since we're only using stage1
# executables.
# * make doc/error-index.md: Gets all doc blocks from doc comments and error
# explanations to put them in a markdown file. You
# can then test them by running
# "rustdoc --test error-index.md".
#
# And of course, the wonderfully useful 'make tidy'! Always run it before opening a pull request to rust!
#
# </tips>
#
# <nitty-gritty>
@@ -256,3 +270,9 @@ ifneq ($(strip $(findstring TAGS.emacs,$(MAKECMDGOALS)) \
CFG_INFO := $(info cfg: including ctags rules)
include $(CFG_SRC_DIR)mk/ctags.mk
endif

.DEFAULT:
@echo "\n======================================================"
@echo "== If you need help, run 'make help' or 'make tips' =="
@echo "======================================================\n"
exit 1
2 changes: 1 addition & 1 deletion src/doc/book/crates-and-modules.md
Original file line number Diff line number Diff line change
@@ -115,7 +115,7 @@ $ ls target/debug
build deps examples libphrases-a7448e02a0468eaa.rlib native
```

`libphrases-hash.rlib` is the compiled crate. Before we see how to use this
`libphrases-<hash>.rlib` is the compiled crate. Before we see how to use this
crate from another crate, let’s break it up into multiple files.

# Multiple File Crates
4 changes: 2 additions & 2 deletions src/doc/book/error-handling.md
Original file line number Diff line number Diff line change
@@ -225,7 +225,7 @@ sense to put it into a function:
```rust
# fn find(_: &str, _: char) -> Option<usize> { None }
// Returns the extension of the given file name, where the extension is defined
// as all characters proceeding the first `.`.
// as all characters following the first `.`.
// If `file_name` has no `.`, then `None` is returned.
fn extension_explicit(file_name: &str) -> Option<&str> {
match find(file_name, '.') {
@@ -274,7 +274,7 @@ to get rid of the case analysis:
```rust
# fn find(_: &str, _: char) -> Option<usize> { None }
// Returns the extension of the given file name, where the extension is defined
// as all characters proceeding the first `.`.
// as all characters following the first `.`.
// If `file_name` has no `.`, then `None` is returned.
fn extension(file_name: &str) -> Option<&str> {
find(file_name, '.').map(|i| &file_name[i+1..])
4 changes: 2 additions & 2 deletions src/doc/book/testing.md
Original file line number Diff line number Diff line change
@@ -84,8 +84,8 @@ fn it_works() {
```

`assert!` is a macro provided by Rust which takes one argument: if the argument
is `true`, nothing happens. If the argument is `false`, it `panic!`s. Let's run
our tests again:
is `true`, nothing happens. If the argument is `false`, it will `panic!`. Let's
run our tests again:

```bash
$ cargo test
1 change: 1 addition & 0 deletions src/libcore/lib.rs
Original file line number Diff line number Diff line change
@@ -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"]
4 changes: 4 additions & 0 deletions src/libcore/slice.rs
Original file line number Diff line number Diff line change
@@ -523,6 +523,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;

@@ -533,6 +535,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 {
2 changes: 1 addition & 1 deletion src/librustc/infer/mod.rs
Original file line number Diff line number Diff line change
@@ -167,7 +167,7 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, '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?
///
277 changes: 259 additions & 18 deletions src/librustc/traits/error_reporting.rs
Original file line number Diff line number Diff line change
@@ -26,16 +26,17 @@ use super::{

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

use std::cmp;
use std::fmt;
use syntax::attr::{AttributeMethods, AttrMetaMethods};
use syntax::ast;
use syntax::attr::{AttributeMethods, AttrMetaMethods};
use syntax::codemap::Span;
use syntax::errors::DiagnosticBuilder;

@@ -60,6 +61,128 @@ impl<'a, 'gcx, 'tcx> TraitErrorKey<'tcx> {
}
}

// Enum used to differentiate the "big" and "little" weights.
enum Weight {
Coarse,
Precise,
}

trait AssociatedWeight {
fn get_weight(&self) -> (u32, u32);
}

impl<'a> AssociatedWeight for TypeVariants<'a> {
// Left number is for "global"/"big" weight and right number is for better precision.
fn get_weight(&self) -> (u32, u32) {
match *self {
TypeVariants::TyBool => (1, 1),
TypeVariants::TyChar => (1, 2),
TypeVariants::TyStr => (1, 3),

TypeVariants::TyInt(_) => (2, 1),
TypeVariants::TyUint(_) => (2, 2),
TypeVariants::TyFloat(_) => (2, 3),
TypeVariants::TyRawPtr(_) => (2, 4),

TypeVariants::TyEnum(_, _) => (3, 1),
TypeVariants::TyStruct(_, _) => (3, 2),
TypeVariants::TyBox(_) => (3, 3),
TypeVariants::TyTuple(_) => (3, 4),

TypeVariants::TyArray(_, _) => (4, 1),
TypeVariants::TySlice(_) => (4, 2),

TypeVariants::TyRef(_, _) => (5, 1),
TypeVariants::TyFnDef(_, _, _) => (5, 2),
TypeVariants::TyFnPtr(_) => (5, 3),

TypeVariants::TyTrait(_) => (6, 1),

TypeVariants::TyClosure(_, _) => (7, 1),

TypeVariants::TyProjection(_) => (8, 1),
TypeVariants::TyParam(_) => (8, 2),
TypeVariants::TyInfer(_) => (8, 3),

TypeVariants::TyError => (9, 1),
}
}
}

// The "closer" the types are, the lesser the weight.
fn get_weight_diff(a: &ty::TypeVariants, b: &TypeVariants, weight: Weight) -> u32 {
let (w1, w2) = match weight {
Weight::Coarse => (a.get_weight().0, b.get_weight().0),
Weight::Precise => (a.get_weight().1, b.get_weight().1),
};
if w1 < w2 {
w2 - w1
} else {
w1 - w2
}
}

// Once we have "globally matching" types, we need to run another filter on them.
//
// In the function `get_best_matching_type`, we got the types which might fit the
// most to the type we're looking for. This second filter now intends to get (if
// possible) the type which fits the most.
//
// For example, the trait expects an `usize` and here you have `u32` and `i32`.
// Obviously, the "correct" one is `u32`.
fn filter_matching_types<'tcx>(weights: &[(usize, u32)],
imps: &[(DefId, subst::Substs<'tcx>)],
trait_types: &[ty::Ty<'tcx>])
-> usize {
let matching_weight = weights[0].1;
let iter = weights.iter().filter(|&&(_, weight)| weight == matching_weight);
let mut filtered_weights = vec!();

for &(pos, _) in iter {
let mut weight = 0;
for (type_to_compare, original_type) in imps[pos].1
.types
.get_slice(ParamSpace::TypeSpace)
.iter()
.zip(trait_types.iter()) {
weight += get_weight_diff(&type_to_compare.sty, &original_type.sty, Weight::Precise);
}
filtered_weights.push((pos, weight));
}
filtered_weights.sort_by(|a, b| a.1.cmp(&b.1));
filtered_weights[0].0
}

// Here, we run the "big" filter. Little example:
//
// We receive a `String`, an `u32` and an `i32`.
// The trait expected an `usize`.
// From human point of view, it's easy to determine that `String` doesn't correspond to
// the expected type at all whereas `u32` and `i32` could.
//
// This first filter intends to only keep the types which match the most.
fn get_best_matching_type<'tcx>(imps: &[(DefId, subst::Substs<'tcx>)],
trait_types: &[ty::Ty<'tcx>]) -> usize {
let mut weights = vec!();
for (pos, imp) in imps.iter().enumerate() {
let mut weight = 0;
for (type_to_compare, original_type) in imp.1
.types
.get_slice(ParamSpace::TypeSpace)
.iter()
.zip(trait_types.iter()) {
weight += get_weight_diff(&type_to_compare.sty, &original_type.sty, Weight::Coarse);
}
weights.push((pos, weight));
}
weights.sort_by(|a, b| a.1.cmp(&b.1));
if weights[0].1 == weights[1].1 {
filter_matching_types(&weights, &imps, trait_types)
} else {
weights[0].0
}
}

impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
pub fn report_fulfillment_errors(&self, errors: &Vec<FulfillmentError<'tcx>>) {
for error in errors {
@@ -126,16 +249,101 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}
}

fn impl_substs(&self,
did: DefId,
obligation: PredicateObligation<'tcx>)
-> subst::Substs<'tcx> {
let tcx = self.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 = 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,
&mut substs,
tps);
substs
}

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

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

if self.eq_types(true,
TypeOrigin::Misc(obligation.cause.span),
trait_ref.self_ty(),
imp.self_ty()).is_ok() {
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 {
let end = trait_ref.input_types().len() - 1;
// we need to determine which type is the good one!
Some(matching_impls[get_best_matching_type(&matching_impls,
&trait_ref.input_types()[0..end])]
.clone())
}
},
None => None,
}
}

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

fn on_unimplemented_note(&self,
trait_ref: ty::PolyTraitRef<'tcx>,
span: Span) -> Option<String> {
obligation: &PredicateObligation<'tcx>) -> Option<String> {
let trait_ref = trait_ref.skip_binder();
let def_id = trait_ref.def_id;
let def_id = match self.get_current_failing_impl(trait_ref, obligation) {
Some((def_id, _)) => {
if let Some(_) = self.find_attr(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 self.tcx.get_attrs(def_id).iter() {
if item.check_name("rustc_on_unimplemented") {
let err_sp = item.meta().span.substitute_dummy(span);
let def = self.tcx.lookup_trait_def(def_id);
let def = self.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()
@@ -195,6 +403,37 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
report
}

fn find_similar_impl_candidates(&self,
trait_ref: ty::PolyTraitRef<'tcx>)
-> Vec<ty::TraitRef<'tcx>>
{
let simp = fast_reject::simplify_type(self.tcx,
trait_ref.skip_binder().self_ty(),
true);
let mut impl_candidates = Vec::new();
let trait_def = self.tcx.lookup_trait_def(trait_ref.def_id());

match simp {
Some(simp) => trait_def.for_each_impl(self.tcx, |def_id| {
let imp = self.tcx.impl_trait_ref(def_id).unwrap();
let imp_simp = fast_reject::simplify_type(self.tcx,
imp.self_ty(),
true);
if let Some(imp_simp) = imp_simp {
if simp != imp_simp {
return;
}
}
impl_candidates.push(imp);
}),
None => trait_def.for_each_impl(self.tcx, |def_id| {
impl_candidates.push(
self.tcx.impl_trait_ref(def_id).unwrap());
})
};
impl_candidates
}

fn report_similar_impl_candidates(&self,
trait_ref: ty::PolyTraitRef<'tcx>,
err: &mut DiagnosticBuilder)
@@ -425,8 +664,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
// Try to report a help message

if !trait_ref.has_infer_types() &&
self.predicate_can_apply(trait_ref)
{
self.predicate_can_apply(trait_ref) {
// If a where-clause may be useful, remind the
// user that they can add it.
//
@@ -435,22 +673,25 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
// "the type `T` can't be frobnicated"
// which is somewhat confusing.
err.help(&format!("consider adding a `where {}` bound",
trait_ref.to_predicate()
));
} else if let Some(s) =
self.on_unimplemented_note(trait_ref, span) {
// Otherwise, if there is an on-unimplemented note,
// display it.
trait_ref.to_predicate()));
} else if let Some(s) = self.on_unimplemented_note(trait_ref,
obligation) {
// If it has a custom "#[rustc_on_unimplemented]"
// error message, let's display it!
err.note(&s);
} else {
// If we can't show anything useful, try to find
// similar impls.

self.report_similar_impl_candidates(trait_ref, &mut err);
let impl_candidates =
self.find_similar_impl_candidates(trait_ref);
if impl_candidates.len() > 0 {
self.report_similar_impl_candidates(trait_ref, &mut err);
}
}
err
}
},
}

ty::Predicate::Equate(ref predicate) => {
let predicate = self.resolve_type_vars_if_possible(predicate);
let err = self.equality_predicate(span,
1 change: 0 additions & 1 deletion src/librustc/ty/subst.rs
Original file line number Diff line number Diff line change
@@ -136,7 +136,6 @@ impl<'a, 'gcx, '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);
35 changes: 35 additions & 0 deletions src/test/compile-fail/check_on_unimplemented.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// 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
//~| NOTE required by
}
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
}
64 changes: 64 additions & 0 deletions src/test/compile-fail/on_unimplemented.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// 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 isize is required to index into a slice"]
impl Index<isize> for [i32] {
type Output = i32;
fn index(&self, index: isize) -> &i32 {
&self[index as usize]
}
}

#[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]
}
}

trait Foo<A, B> {
fn f(&self, a: &A, b: &B);
}

#[rustc_on_unimplemented = "two i32 Foo trait takes"]
impl Foo<i32, i32> for [i32] {
fn f(&self, a: &i32, b: &i32) {}
}

#[rustc_on_unimplemented = "two u32 Foo trait takes"]
impl Foo<u32, u32> for [i32] {
fn f(&self, a: &u32, b: &u32) {}
}

#[rustc_error]
fn main() {
Index::<u32>::index(&[1, 2, 3] as &[i32], 2u32); //~ ERROR E0277
//~| NOTE a usize is required
//~| NOTE required by
Index::<i32>::index(&[1, 2, 3] as &[i32], 2i32); //~ ERROR E0277
//~| NOTE a isize is required
//~| NOTE required by

Foo::<usize, usize>::f(&[1, 2, 3] as &[i32], &2usize, &2usize); //~ ERROR E0277
//~| NOTE two u32 Foo trait
//~| NOTE required by
}