mirror of https://github.com/rust-lang/rust.git
Auto merge of #33927 - Manishearth:rollup, r=Manishearth
Rollup of 15 pull requests - Successful merges: #33820, #33821, #33822, #33824, #33825, #33831, #33832, #33848, #33849, #33852, #33854, #33856, #33859, #33860, #33861 - Failed merges:
This commit is contained in:
commit
f1776fe244
|
@ -80,13 +80,40 @@ mod imp {
|
||||||
if align <= MIN_ALIGN {
|
if align <= MIN_ALIGN {
|
||||||
libc::malloc(size as libc::size_t) as *mut u8
|
libc::malloc(size as libc::size_t) as *mut u8
|
||||||
} else {
|
} else {
|
||||||
let mut out = ptr::null_mut();
|
aligned_malloc(size, align)
|
||||||
let ret = libc::posix_memalign(&mut out, align as libc::size_t, size as libc::size_t);
|
}
|
||||||
if ret != 0 {
|
}
|
||||||
ptr::null_mut()
|
|
||||||
} else {
|
#[cfg(target_os = "android")]
|
||||||
out as *mut u8
|
unsafe fn aligned_malloc(size: usize, align: usize) -> *mut u8 {
|
||||||
}
|
// On android we currently target API level 9 which unfortunately
|
||||||
|
// doesn't have the `posix_memalign` API used below. Instead we use
|
||||||
|
// `memalign`, but this unfortunately has the property on some systems
|
||||||
|
// where the memory returned cannot be deallocated by `free`!
|
||||||
|
//
|
||||||
|
// Upon closer inspection, however, this appears to work just fine with
|
||||||
|
// Android, so for this platform we should be fine to call `memalign`
|
||||||
|
// (which is present in API level 9). Some helpful references could
|
||||||
|
// possibly be chromium using memalign [1], attempts at documenting that
|
||||||
|
// memalign + free is ok [2] [3], or the current source of chromium
|
||||||
|
// which still uses memalign on android [4].
|
||||||
|
//
|
||||||
|
// [1]: https://codereview.chromium.org/10796020/
|
||||||
|
// [2]: https://code.google.com/p/android/issues/detail?id=35391
|
||||||
|
// [3]: https://bugs.chromium.org/p/chromium/issues/detail?id=138579
|
||||||
|
// [4]: https://chromium.googlesource.com/chromium/src/base/+/master/
|
||||||
|
// /memory/aligned_memory.cc
|
||||||
|
libc::memalign(align as libc::size_t, size as libc::size_t) as *mut u8
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "android"))]
|
||||||
|
unsafe fn aligned_malloc(size: usize, align: usize) -> *mut u8 {
|
||||||
|
let mut out = ptr::null_mut();
|
||||||
|
let ret = libc::posix_memalign(&mut out, align as libc::size_t, size as libc::size_t);
|
||||||
|
if ret != 0 {
|
||||||
|
ptr::null_mut()
|
||||||
|
} else {
|
||||||
|
out as *mut u8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -411,14 +411,17 @@ pub struct EscapeUnicode {
|
||||||
hex_digit_idx: usize,
|
hex_digit_idx: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The enum values are ordered so that their representation is the
|
||||||
|
// same as the remaining length (besides the hexadecimal digits). This
|
||||||
|
// likely makes `len()` a single load from memory) and inline-worth.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum EscapeUnicodeState {
|
enum EscapeUnicodeState {
|
||||||
Backslash,
|
|
||||||
Type,
|
|
||||||
LeftBrace,
|
|
||||||
Value,
|
|
||||||
RightBrace,
|
|
||||||
Done,
|
Done,
|
||||||
|
RightBrace,
|
||||||
|
Value,
|
||||||
|
LeftBrace,
|
||||||
|
Type,
|
||||||
|
Backslash,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
@ -457,19 +460,17 @@ impl Iterator for EscapeUnicode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
let n = match self.state {
|
let n = self.len();
|
||||||
EscapeUnicodeState::Backslash => 5,
|
|
||||||
EscapeUnicodeState::Type => 4,
|
|
||||||
EscapeUnicodeState::LeftBrace => 3,
|
|
||||||
EscapeUnicodeState::Value => 2,
|
|
||||||
EscapeUnicodeState::RightBrace => 1,
|
|
||||||
EscapeUnicodeState::Done => 0,
|
|
||||||
};
|
|
||||||
let n = n + self.hex_digit_idx;
|
|
||||||
(n, Some(n))
|
(n, Some(n))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn count(self) -> usize {
|
||||||
|
self.len()
|
||||||
|
}
|
||||||
|
|
||||||
fn last(self) -> Option<char> {
|
fn last(self) -> Option<char> {
|
||||||
match self.state {
|
match self.state {
|
||||||
EscapeUnicodeState::Done => None,
|
EscapeUnicodeState::Done => None,
|
||||||
|
@ -483,6 +484,22 @@ impl Iterator for EscapeUnicode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[stable(feature = "exact_size_escape", since = "1.11.0")]
|
||||||
|
impl ExactSizeIterator for EscapeUnicode {
|
||||||
|
#[inline]
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
// The match is a single memory access with no branching
|
||||||
|
self.hex_digit_idx + match self.state {
|
||||||
|
EscapeUnicodeState::Done => 0,
|
||||||
|
EscapeUnicodeState::RightBrace => 1,
|
||||||
|
EscapeUnicodeState::Value => 2,
|
||||||
|
EscapeUnicodeState::LeftBrace => 3,
|
||||||
|
EscapeUnicodeState::Type => 4,
|
||||||
|
EscapeUnicodeState::Backslash => 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An iterator that yields the literal escape code of a `char`.
|
/// An iterator that yields the literal escape code of a `char`.
|
||||||
///
|
///
|
||||||
/// This `struct` is created by the [`escape_default()`] method on [`char`]. See
|
/// This `struct` is created by the [`escape_default()`] method on [`char`]. See
|
||||||
|
@ -498,9 +515,9 @@ pub struct EscapeDefault {
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum EscapeDefaultState {
|
enum EscapeDefaultState {
|
||||||
Backslash(char),
|
|
||||||
Char(char),
|
|
||||||
Done,
|
Done,
|
||||||
|
Char(char),
|
||||||
|
Backslash(char),
|
||||||
Unicode(EscapeUnicode),
|
Unicode(EscapeUnicode),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -523,22 +540,15 @@ impl Iterator for EscapeDefault {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
match self.state {
|
let n = self.len();
|
||||||
EscapeDefaultState::Char(_) => (1, Some(1)),
|
(n, Some(n))
|
||||||
EscapeDefaultState::Backslash(_) => (2, Some(2)),
|
|
||||||
EscapeDefaultState::Unicode(ref iter) => iter.size_hint(),
|
|
||||||
EscapeDefaultState::Done => (0, Some(0)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn count(self) -> usize {
|
fn count(self) -> usize {
|
||||||
match self.state {
|
self.len()
|
||||||
EscapeDefaultState::Char(_) => 1,
|
|
||||||
EscapeDefaultState::Unicode(iter) => iter.count(),
|
|
||||||
EscapeDefaultState::Done => 0,
|
|
||||||
EscapeDefaultState::Backslash(_) => 2,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn nth(&mut self, n: usize) -> Option<char> {
|
fn nth(&mut self, n: usize) -> Option<char> {
|
||||||
|
@ -578,6 +588,18 @@ impl Iterator for EscapeDefault {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[stable(feature = "exact_size_escape", since = "1.11.0")]
|
||||||
|
impl ExactSizeIterator for EscapeDefault {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
match self.state {
|
||||||
|
EscapeDefaultState::Done => 0,
|
||||||
|
EscapeDefaultState::Char(_) => 1,
|
||||||
|
EscapeDefaultState::Backslash(_) => 2,
|
||||||
|
EscapeDefaultState::Unicode(ref iter) => iter.len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An iterator over `u8` entries represending the UTF-8 encoding of a `char`
|
/// An iterator over `u8` entries represending the UTF-8 encoding of a `char`
|
||||||
/// value.
|
/// value.
|
||||||
///
|
///
|
||||||
|
|
|
@ -276,6 +276,12 @@ fn eu_iterator_specializations() {
|
||||||
// Check last
|
// Check last
|
||||||
assert_eq!(iter.clone().last(), Some('}'));
|
assert_eq!(iter.clone().last(), Some('}'));
|
||||||
|
|
||||||
|
// Check len
|
||||||
|
assert_eq!(iter.len(), len - offset);
|
||||||
|
|
||||||
|
// Check size_hint (= len in ExactSizeIterator)
|
||||||
|
assert_eq!(iter.size_hint(), (iter.len(), Some(iter.len())));
|
||||||
|
|
||||||
// Check counting
|
// Check counting
|
||||||
assert_eq!(iter.clone().count(), len - offset);
|
assert_eq!(iter.clone().count(), len - offset);
|
||||||
|
|
||||||
|
|
|
@ -280,12 +280,9 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) {
|
||||||
visitor.visit_path(path, item.id);
|
visitor.visit_path(path, item.id);
|
||||||
}
|
}
|
||||||
ViewPathList(ref prefix, ref list) => {
|
ViewPathList(ref prefix, ref list) => {
|
||||||
if !list.is_empty() {
|
visitor.visit_path(prefix, item.id);
|
||||||
for item in list {
|
for item in list {
|
||||||
visitor.visit_path_list_item(prefix, item)
|
visitor.visit_path_list_item(prefix, item)
|
||||||
}
|
|
||||||
} else {
|
|
||||||
visitor.visit_path(prefix, item.id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -419,12 +416,8 @@ pub fn walk_path<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v Path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn walk_path_list_item<'v, V: Visitor<'v>>(visitor: &mut V,
|
pub fn walk_path_list_item<'v, V: Visitor<'v>>(visitor: &mut V,
|
||||||
prefix: &'v Path,
|
_prefix: &'v Path,
|
||||||
item: &'v PathListItem) {
|
item: &'v PathListItem) {
|
||||||
for segment in &prefix.segments {
|
|
||||||
visitor.visit_path_segment(prefix.span, segment);
|
|
||||||
}
|
|
||||||
|
|
||||||
walk_opt_name(visitor, item.span, item.node.name());
|
walk_opt_name(visitor, item.span, item.node.name());
|
||||||
walk_opt_name(visitor, item.span, item.node.rename());
|
walk_opt_name(visitor, item.span, item.node.rename());
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,6 +163,11 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
|
||||||
// If the number of errors increases, that's also a sign (line
|
// If the number of errors increases, that's also a sign (line
|
||||||
// `tained_by_errors`) to avoid reporting certain kinds of errors.
|
// `tained_by_errors`) to avoid reporting certain kinds of errors.
|
||||||
err_count_on_creation: usize,
|
err_count_on_creation: usize,
|
||||||
|
|
||||||
|
// This flag is used for debugging, and is set to true if there are
|
||||||
|
// any obligations set during the current snapshot. In that case, the
|
||||||
|
// snapshot can't be rolled back.
|
||||||
|
pub obligations_in_snapshot: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A map returned by `skolemize_late_bound_regions()` indicating the skolemized
|
/// A map returned by `skolemize_late_bound_regions()` indicating the skolemized
|
||||||
|
@ -476,7 +481,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'gcx> {
|
||||||
normalize: false,
|
normalize: false,
|
||||||
projection_mode: ProjectionMode::AnyFinal,
|
projection_mode: ProjectionMode::AnyFinal,
|
||||||
tainted_by_errors_flag: Cell::new(false),
|
tainted_by_errors_flag: Cell::new(false),
|
||||||
err_count_on_creation: self.sess.err_count()
|
err_count_on_creation: self.sess.err_count(),
|
||||||
|
obligations_in_snapshot: Cell::new(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -515,7 +521,8 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> {
|
||||||
normalize: normalize,
|
normalize: normalize,
|
||||||
projection_mode: projection_mode,
|
projection_mode: projection_mode,
|
||||||
tainted_by_errors_flag: Cell::new(false),
|
tainted_by_errors_flag: Cell::new(false),
|
||||||
err_count_on_creation: tcx.sess.err_count()
|
err_count_on_creation: tcx.sess.err_count(),
|
||||||
|
obligations_in_snapshot: Cell::new(false),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -542,6 +549,7 @@ pub struct CombinedSnapshot {
|
||||||
int_snapshot: unify::Snapshot<ty::IntVid>,
|
int_snapshot: unify::Snapshot<ty::IntVid>,
|
||||||
float_snapshot: unify::Snapshot<ty::FloatVid>,
|
float_snapshot: unify::Snapshot<ty::FloatVid>,
|
||||||
region_vars_snapshot: RegionSnapshot,
|
region_vars_snapshot: RegionSnapshot,
|
||||||
|
obligations_in_snapshot: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper trait for shortening the lifetimes inside a
|
/// Helper trait for shortening the lifetimes inside a
|
||||||
|
@ -809,11 +817,15 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_snapshot(&self) -> CombinedSnapshot {
|
fn start_snapshot(&self) -> CombinedSnapshot {
|
||||||
|
let obligations_in_snapshot = self.obligations_in_snapshot.get();
|
||||||
|
self.obligations_in_snapshot.set(false);
|
||||||
|
|
||||||
CombinedSnapshot {
|
CombinedSnapshot {
|
||||||
type_snapshot: self.type_variables.borrow_mut().snapshot(),
|
type_snapshot: self.type_variables.borrow_mut().snapshot(),
|
||||||
int_snapshot: self.int_unification_table.borrow_mut().snapshot(),
|
int_snapshot: self.int_unification_table.borrow_mut().snapshot(),
|
||||||
float_snapshot: self.float_unification_table.borrow_mut().snapshot(),
|
float_snapshot: self.float_unification_table.borrow_mut().snapshot(),
|
||||||
region_vars_snapshot: self.region_vars.start_snapshot(),
|
region_vars_snapshot: self.region_vars.start_snapshot(),
|
||||||
|
obligations_in_snapshot: obligations_in_snapshot,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -822,7 +834,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||||
let CombinedSnapshot { type_snapshot,
|
let CombinedSnapshot { type_snapshot,
|
||||||
int_snapshot,
|
int_snapshot,
|
||||||
float_snapshot,
|
float_snapshot,
|
||||||
region_vars_snapshot } = snapshot;
|
region_vars_snapshot,
|
||||||
|
obligations_in_snapshot } = snapshot;
|
||||||
|
|
||||||
|
assert!(!self.obligations_in_snapshot.get());
|
||||||
|
self.obligations_in_snapshot.set(obligations_in_snapshot);
|
||||||
|
|
||||||
self.type_variables
|
self.type_variables
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
|
@ -842,7 +858,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||||
let CombinedSnapshot { type_snapshot,
|
let CombinedSnapshot { type_snapshot,
|
||||||
int_snapshot,
|
int_snapshot,
|
||||||
float_snapshot,
|
float_snapshot,
|
||||||
region_vars_snapshot } = snapshot;
|
region_vars_snapshot,
|
||||||
|
obligations_in_snapshot } = snapshot;
|
||||||
|
|
||||||
|
self.obligations_in_snapshot.set(obligations_in_snapshot);
|
||||||
|
|
||||||
self.type_variables
|
self.type_variables
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
|
@ -904,12 +923,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||||
let CombinedSnapshot { type_snapshot,
|
let CombinedSnapshot { type_snapshot,
|
||||||
int_snapshot,
|
int_snapshot,
|
||||||
float_snapshot,
|
float_snapshot,
|
||||||
region_vars_snapshot } = self.start_snapshot();
|
region_vars_snapshot,
|
||||||
|
obligations_in_snapshot } = self.start_snapshot();
|
||||||
|
|
||||||
let r = self.commit_if_ok(|_| f());
|
let r = self.commit_if_ok(|_| f());
|
||||||
|
|
||||||
debug!("commit_regions_if_ok: rolling back everything but regions");
|
debug!("commit_regions_if_ok: rolling back everything but regions");
|
||||||
|
|
||||||
|
assert!(!self.obligations_in_snapshot.get());
|
||||||
|
self.obligations_in_snapshot.set(obligations_in_snapshot);
|
||||||
|
|
||||||
// Roll back any non-region bindings - they should be resolved
|
// Roll back any non-region bindings - they should be resolved
|
||||||
// inside `f`, with, e.g. `resolve_type_vars_if_possible`.
|
// inside `f`, with, e.g. `resolve_type_vars_if_possible`.
|
||||||
self.type_variables
|
self.type_variables
|
||||||
|
|
|
@ -171,6 +171,8 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
|
||||||
// debug output much nicer to read and so on.
|
// debug output much nicer to read and so on.
|
||||||
let obligation = infcx.resolve_type_vars_if_possible(&obligation);
|
let obligation = infcx.resolve_type_vars_if_possible(&obligation);
|
||||||
|
|
||||||
|
infcx.obligations_in_snapshot.set(true);
|
||||||
|
|
||||||
if infcx.tcx.fulfilled_predicates.borrow().check_duplicate(&obligation.predicate)
|
if infcx.tcx.fulfilled_predicates.borrow().check_duplicate(&obligation.predicate)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
|
|
|
@ -31,7 +31,7 @@ pub use self::coherence::overlapping_impls;
|
||||||
pub use self::coherence::OrphanCheckErr;
|
pub use self::coherence::OrphanCheckErr;
|
||||||
pub use self::fulfill::{FulfillmentContext, GlobalFulfilledPredicates, RegionObligation};
|
pub use self::fulfill::{FulfillmentContext, GlobalFulfilledPredicates, RegionObligation};
|
||||||
pub use self::project::{MismatchedProjectionTypes, ProjectionMode};
|
pub use self::project::{MismatchedProjectionTypes, ProjectionMode};
|
||||||
pub use self::project::{normalize, Normalized};
|
pub use self::project::{normalize, normalize_projection_type, Normalized};
|
||||||
pub use self::object_safety::ObjectSafetyViolation;
|
pub use self::object_safety::ObjectSafetyViolation;
|
||||||
pub use self::object_safety::MethodViolationCode;
|
pub use self::object_safety::MethodViolationCode;
|
||||||
pub use self::select::{EvaluationCache, SelectionContext, SelectionCache};
|
pub use self::select::{EvaluationCache, SelectionContext, SelectionCache};
|
||||||
|
|
|
@ -207,7 +207,7 @@ fn project_and_unify_type<'cx, 'gcx, 'tcx>(
|
||||||
debug!("project_and_unify_type(obligation={:?})",
|
debug!("project_and_unify_type(obligation={:?})",
|
||||||
obligation);
|
obligation);
|
||||||
|
|
||||||
let Normalized { value: normalized_ty, obligations } =
|
let Normalized { value: normalized_ty, mut obligations } =
|
||||||
match opt_normalize_projection_type(selcx,
|
match opt_normalize_projection_type(selcx,
|
||||||
obligation.predicate.projection_ty.clone(),
|
obligation.predicate.projection_ty.clone(),
|
||||||
obligation.cause.clone(),
|
obligation.cause.clone(),
|
||||||
|
@ -224,8 +224,9 @@ fn project_and_unify_type<'cx, 'gcx, 'tcx>(
|
||||||
let origin = TypeOrigin::RelateOutputImplTypes(obligation.cause.span);
|
let origin = TypeOrigin::RelateOutputImplTypes(obligation.cause.span);
|
||||||
match infcx.eq_types(true, origin, normalized_ty, obligation.predicate.ty) {
|
match infcx.eq_types(true, origin, normalized_ty, obligation.predicate.ty) {
|
||||||
Ok(InferOk { obligations: inferred_obligations, .. }) => {
|
Ok(InferOk { obligations: inferred_obligations, .. }) => {
|
||||||
// FIXME(#32730) propagate obligations
|
// FIXME(#32730) once obligations are generated in inference, drop this assertion
|
||||||
assert!(inferred_obligations.is_empty());
|
assert!(inferred_obligations.is_empty());
|
||||||
|
obligations.extend(inferred_obligations);
|
||||||
Ok(Some(obligations))
|
Ok(Some(obligations))
|
||||||
},
|
},
|
||||||
Err(err) => Err(MismatchedProjectionTypes { err: err }),
|
Err(err) => Err(MismatchedProjectionTypes { err: err }),
|
||||||
|
@ -710,7 +711,8 @@ fn assemble_candidates_from_predicates<'cx, 'gcx, 'tcx, I>(
|
||||||
origin,
|
origin,
|
||||||
data_poly_trait_ref,
|
data_poly_trait_ref,
|
||||||
obligation_poly_trait_ref)
|
obligation_poly_trait_ref)
|
||||||
// FIXME(#32730) propagate obligations
|
// FIXME(#32730) once obligations are propagated from unification in
|
||||||
|
// inference, drop this assertion
|
||||||
.map(|InferOk { obligations, .. }| assert!(obligations.is_empty()))
|
.map(|InferOk { obligations, .. }| assert!(obligations.is_empty()))
|
||||||
.is_ok()
|
.is_ok()
|
||||||
});
|
});
|
||||||
|
@ -1047,8 +1049,8 @@ fn confirm_fn_pointer_candidate<'cx, 'gcx, 'tcx>(
|
||||||
fn_pointer_vtable: VtableFnPointerData<'tcx, PredicateObligation<'tcx>>)
|
fn_pointer_vtable: VtableFnPointerData<'tcx, PredicateObligation<'tcx>>)
|
||||||
-> (Ty<'tcx>, Vec<PredicateObligation<'tcx>>)
|
-> (Ty<'tcx>, Vec<PredicateObligation<'tcx>>)
|
||||||
{
|
{
|
||||||
// FIXME(#32730) propagate obligations (fn pointer vtable nested obligations ONLY come from
|
// FIXME(#32730) drop this assertion once obligations are propagated from inference (fn pointer
|
||||||
// unification in inference)
|
// vtable nested obligations ONLY come from unification in inference)
|
||||||
assert!(fn_pointer_vtable.nested.is_empty());
|
assert!(fn_pointer_vtable.nested.is_empty());
|
||||||
let fn_type = selcx.infcx().shallow_resolve(fn_pointer_vtable.fn_ty);
|
let fn_type = selcx.infcx().shallow_resolve(fn_pointer_vtable.fn_ty);
|
||||||
let sig = fn_type.fn_sig();
|
let sig = fn_type.fn_sig();
|
||||||
|
@ -1130,13 +1132,14 @@ fn confirm_param_env_candidate<'cx, 'gcx, 'tcx>(
|
||||||
obligation.predicate.item_name);
|
obligation.predicate.item_name);
|
||||||
|
|
||||||
let origin = TypeOrigin::RelateOutputImplTypes(obligation.cause.span);
|
let origin = TypeOrigin::RelateOutputImplTypes(obligation.cause.span);
|
||||||
match infcx.eq_trait_refs(false,
|
let obligations = match infcx.eq_trait_refs(false,
|
||||||
origin,
|
origin,
|
||||||
obligation.predicate.trait_ref.clone(),
|
obligation.predicate.trait_ref.clone(),
|
||||||
projection.projection_ty.trait_ref.clone()) {
|
projection.projection_ty.trait_ref.clone()) {
|
||||||
Ok(InferOk { obligations, .. }) => {
|
Ok(InferOk { obligations, .. }) => {
|
||||||
// FIXME(#32730) propagate obligations
|
// FIXME(#32730) once obligations are generated in inference, remove this assertion
|
||||||
assert!(obligations.is_empty());
|
assert!(obligations.is_empty());
|
||||||
|
obligations
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
span_bug!(
|
span_bug!(
|
||||||
|
@ -1146,9 +1149,9 @@ fn confirm_param_env_candidate<'cx, 'gcx, 'tcx>(
|
||||||
projection,
|
projection,
|
||||||
e);
|
e);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
(projection.ty, vec!())
|
(projection.ty, obligations)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn confirm_impl_candidate<'cx, 'gcx, 'tcx>(
|
fn confirm_impl_candidate<'cx, 'gcx, 'tcx>(
|
||||||
|
|
|
@ -187,51 +187,49 @@ fn fulfill_implication<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
|
||||||
source_trait_ref: ty::TraitRef<'tcx>,
|
source_trait_ref: ty::TraitRef<'tcx>,
|
||||||
target_impl: DefId)
|
target_impl: DefId)
|
||||||
-> Result<&'tcx Substs<'tcx>, ()> {
|
-> Result<&'tcx Substs<'tcx>, ()> {
|
||||||
infcx.commit_if_ok(|_| {
|
let selcx = &mut SelectionContext::new(&infcx);
|
||||||
let selcx = &mut SelectionContext::new(&infcx);
|
let target_substs = fresh_type_vars_for_impl(&infcx, DUMMY_SP, target_impl);
|
||||||
let target_substs = fresh_type_vars_for_impl(&infcx, DUMMY_SP, target_impl);
|
let (target_trait_ref, obligations) = impl_trait_ref_and_oblig(selcx,
|
||||||
let (target_trait_ref, obligations) = impl_trait_ref_and_oblig(selcx,
|
target_impl,
|
||||||
target_impl,
|
&target_substs);
|
||||||
&target_substs);
|
|
||||||
|
|
||||||
// do the impls unify? If not, no specialization.
|
// do the impls unify? If not, no specialization.
|
||||||
if let Err(_) = infcx.eq_trait_refs(true,
|
if let Err(_) = infcx.eq_trait_refs(true,
|
||||||
TypeOrigin::Misc(DUMMY_SP),
|
TypeOrigin::Misc(DUMMY_SP),
|
||||||
source_trait_ref,
|
source_trait_ref,
|
||||||
target_trait_ref) {
|
target_trait_ref) {
|
||||||
debug!("fulfill_implication: {:?} does not unify with {:?}",
|
debug!("fulfill_implication: {:?} does not unify with {:?}",
|
||||||
source_trait_ref,
|
source_trait_ref,
|
||||||
target_trait_ref);
|
target_trait_ref);
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// attempt to prove all of the predicates for impl2 given those for impl1
|
// attempt to prove all of the predicates for impl2 given those for impl1
|
||||||
// (which are packed up in penv)
|
// (which are packed up in penv)
|
||||||
|
|
||||||
let mut fulfill_cx = FulfillmentContext::new();
|
let mut fulfill_cx = FulfillmentContext::new();
|
||||||
for oblig in obligations.into_iter() {
|
for oblig in obligations.into_iter() {
|
||||||
fulfill_cx.register_predicate_obligation(&infcx, oblig);
|
fulfill_cx.register_predicate_obligation(&infcx, oblig);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(errors) = infcx.drain_fulfillment_cx(&mut fulfill_cx, &()) {
|
if let Err(errors) = infcx.drain_fulfillment_cx(&mut fulfill_cx, &()) {
|
||||||
// no dice!
|
// no dice!
|
||||||
debug!("fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} given \
|
debug!("fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} given \
|
||||||
{:?}",
|
{:?}",
|
||||||
source_trait_ref,
|
source_trait_ref,
|
||||||
target_trait_ref,
|
target_trait_ref,
|
||||||
errors,
|
errors,
|
||||||
infcx.parameter_environment.caller_bounds);
|
infcx.parameter_environment.caller_bounds);
|
||||||
Err(())
|
Err(())
|
||||||
} else {
|
} else {
|
||||||
debug!("fulfill_implication: an impl for {:?} specializes {:?}",
|
debug!("fulfill_implication: an impl for {:?} specializes {:?}",
|
||||||
source_trait_ref,
|
source_trait_ref,
|
||||||
target_trait_ref);
|
target_trait_ref);
|
||||||
|
|
||||||
// Now resolve the *substitution* we built for the target earlier, replacing
|
// Now resolve the *substitution* we built for the target earlier, replacing
|
||||||
// the inference variables inside with whatever we got from fulfillment.
|
// the inference variables inside with whatever we got from fulfillment.
|
||||||
Ok(infcx.resolve_type_vars_if_possible(&target_substs))
|
Ok(infcx.resolve_type_vars_if_possible(&target_substs))
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SpecializesCache {
|
pub struct SpecializesCache {
|
||||||
|
|
|
@ -235,8 +235,9 @@ impl<'a, 'gcx, 'tcx> ty::TyS<'tcx> {
|
||||||
None => {
|
None => {
|
||||||
span_bug!(
|
span_bug!(
|
||||||
expr_span,
|
expr_span,
|
||||||
"the {}th autoderef failed: {}",
|
"the {}th autoderef for {} failed: {}",
|
||||||
autoderef,
|
autoderef,
|
||||||
|
expr_id,
|
||||||
adjusted_ty);
|
adjusted_ty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -977,7 +977,11 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
|
||||||
if let Categorization::Local(local_id) = err.cmt.cat {
|
if let Categorization::Local(local_id) = err.cmt.cat {
|
||||||
let span = self.tcx.map.span(local_id);
|
let span = self.tcx.map.span(local_id);
|
||||||
if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) {
|
if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) {
|
||||||
if snippet != "self" {
|
if snippet.starts_with("ref ") {
|
||||||
|
db.span_label(span,
|
||||||
|
&format!("use `{}` here to make mutable",
|
||||||
|
snippet.replace("ref ", "ref mut ")));
|
||||||
|
} else if snippet != "self" {
|
||||||
db.span_label(span,
|
db.span_label(span,
|
||||||
&format!("use `mut {}` here to make mutable", snippet));
|
&format!("use `mut {}` here to make mutable", snippet));
|
||||||
}
|
}
|
||||||
|
|
|
@ -401,10 +401,6 @@ mod svh_visitor {
|
||||||
SawPath.hash(self.st); visit::walk_path(self, path)
|
SawPath.hash(self.st); visit::walk_path(self, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_path_list_item(&mut self, prefix: &'a Path, item: &'a PathListItem) {
|
|
||||||
SawPath.hash(self.st); visit::walk_path_list_item(self, prefix, item)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_block(&mut self, b: &'a Block) {
|
fn visit_block(&mut self, b: &'a Block) {
|
||||||
SawBlock.hash(self.st); visit::walk_block(self, b)
|
SawBlock.hash(self.st); visit::walk_block(self, b)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,210 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use astconv::AstConv;
|
||||||
|
|
||||||
|
use super::FnCtxt;
|
||||||
|
|
||||||
|
use rustc::traits;
|
||||||
|
use rustc::ty::{self, Ty, TraitRef};
|
||||||
|
use rustc::ty::{ToPredicate, TypeFoldable};
|
||||||
|
use rustc::ty::{MethodCall, MethodCallee};
|
||||||
|
use rustc::ty::subst::Substs;
|
||||||
|
use rustc::ty::{LvaluePreference, NoPreference, PreferMutLvalue};
|
||||||
|
use rustc::hir;
|
||||||
|
|
||||||
|
use syntax::codemap::Span;
|
||||||
|
use syntax::parse::token;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
enum AutoderefKind {
|
||||||
|
Builtin,
|
||||||
|
Overloaded
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Autoderef<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
||||||
|
fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
|
||||||
|
steps: Vec<(Ty<'tcx>, AutoderefKind)>,
|
||||||
|
cur_ty: Ty<'tcx>,
|
||||||
|
obligations: Vec<traits::PredicateObligation<'tcx>>,
|
||||||
|
at_start: bool,
|
||||||
|
span: Span
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'gcx, 'tcx> Iterator for Autoderef<'a, 'gcx, 'tcx> {
|
||||||
|
type Item = (Ty<'tcx>, usize);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let tcx = self.fcx.tcx;
|
||||||
|
|
||||||
|
debug!("autoderef: steps={:?}, cur_ty={:?}",
|
||||||
|
self.steps, self.cur_ty);
|
||||||
|
if self.at_start {
|
||||||
|
self.at_start = false;
|
||||||
|
debug!("autoderef stage #0 is {:?}", self.cur_ty);
|
||||||
|
return Some((self.cur_ty, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.steps.len() == tcx.sess.recursion_limit.get() {
|
||||||
|
// We've reached the recursion limit, error gracefully.
|
||||||
|
span_err!(tcx.sess, self.span, E0055,
|
||||||
|
"reached the recursion limit while auto-dereferencing {:?}",
|
||||||
|
self.cur_ty);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.cur_ty.is_ty_var() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, deref if type is derefable:
|
||||||
|
let (kind, new_ty) = if let Some(mt) = self.cur_ty.builtin_deref(false, NoPreference) {
|
||||||
|
(AutoderefKind::Builtin, mt.ty)
|
||||||
|
} else {
|
||||||
|
match self.overloaded_deref_ty(self.cur_ty) {
|
||||||
|
Some(ty) => (AutoderefKind::Overloaded, ty),
|
||||||
|
_ => return None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if new_ty.references_error() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.steps.push((self.cur_ty, kind));
|
||||||
|
debug!("autoderef stage #{:?} is {:?} from {:?}", self.steps.len(),
|
||||||
|
new_ty, (self.cur_ty, kind));
|
||||||
|
self.cur_ty = new_ty;
|
||||||
|
|
||||||
|
Some((self.cur_ty, self.steps.len()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
|
||||||
|
fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||||
|
debug!("overloaded_deref_ty({:?})", ty);
|
||||||
|
|
||||||
|
let tcx = self.fcx.tcx();
|
||||||
|
|
||||||
|
// <cur_ty as Deref>
|
||||||
|
let trait_ref = TraitRef {
|
||||||
|
def_id: match tcx.lang_items.deref_trait() {
|
||||||
|
Some(f) => f,
|
||||||
|
None => return None
|
||||||
|
},
|
||||||
|
substs: tcx.mk_substs(Substs::new_trait(vec![], vec![], self.cur_ty))
|
||||||
|
};
|
||||||
|
|
||||||
|
let cause = traits::ObligationCause::misc(self.span, self.fcx.body_id);
|
||||||
|
|
||||||
|
let mut selcx = traits::SelectionContext::new(self.fcx);
|
||||||
|
let obligation = traits::Obligation::new(cause.clone(), trait_ref.to_predicate());
|
||||||
|
if !selcx.evaluate_obligation(&obligation) {
|
||||||
|
debug!("overloaded_deref_ty: cannot match obligation");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let normalized = traits::normalize_projection_type(
|
||||||
|
&mut selcx,
|
||||||
|
ty::ProjectionTy {
|
||||||
|
trait_ref: trait_ref,
|
||||||
|
item_name: token::intern("Target")
|
||||||
|
},
|
||||||
|
cause,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized);
|
||||||
|
self.obligations.extend(normalized.obligations);
|
||||||
|
|
||||||
|
Some(self.fcx.resolve_type_vars_if_possible(&normalized.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unambiguous_final_ty(&self) -> Ty<'tcx> {
|
||||||
|
self.fcx.structurally_resolved_type(self.span, self.cur_ty)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finalize<'b, I>(self, pref: LvaluePreference, exprs: I)
|
||||||
|
where I: IntoIterator<Item=&'b hir::Expr>
|
||||||
|
{
|
||||||
|
let methods : Vec<_> = self.steps.iter().map(|&(ty, kind)| {
|
||||||
|
if let AutoderefKind::Overloaded = kind {
|
||||||
|
self.fcx.try_overloaded_deref(self.span, None, ty, pref)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
debug!("finalize({:?}) - {:?},{:?}", pref, methods, self.obligations);
|
||||||
|
|
||||||
|
for expr in exprs {
|
||||||
|
debug!("finalize - finalizing #{} - {:?}", expr.id, expr);
|
||||||
|
for (n, method) in methods.iter().enumerate() {
|
||||||
|
if let &Some(method) = method {
|
||||||
|
let method_call = MethodCall::autoderef(expr.id, n as u32);
|
||||||
|
self.fcx.tables.borrow_mut().method_map.insert(method_call, method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for obligation in self.obligations {
|
||||||
|
self.fcx.register_predicate(obligation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||||
|
pub fn autoderef(&'a self,
|
||||||
|
span: Span,
|
||||||
|
base_ty: Ty<'tcx>)
|
||||||
|
-> Autoderef<'a, 'gcx, 'tcx>
|
||||||
|
{
|
||||||
|
Autoderef {
|
||||||
|
fcx: self,
|
||||||
|
steps: vec![],
|
||||||
|
cur_ty: self.resolve_type_vars_if_possible(&base_ty),
|
||||||
|
obligations: vec![],
|
||||||
|
at_start: true,
|
||||||
|
span: span
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_overloaded_deref(&self,
|
||||||
|
span: Span,
|
||||||
|
base_expr: Option<&hir::Expr>,
|
||||||
|
base_ty: Ty<'tcx>,
|
||||||
|
lvalue_pref: LvaluePreference)
|
||||||
|
-> Option<MethodCallee<'tcx>>
|
||||||
|
{
|
||||||
|
debug!("try_overloaded_deref({:?},{:?},{:?},{:?})",
|
||||||
|
span, base_expr, base_ty, lvalue_pref);
|
||||||
|
// Try DerefMut first, if preferred.
|
||||||
|
let method = match (lvalue_pref, self.tcx.lang_items.deref_mut_trait()) {
|
||||||
|
(PreferMutLvalue, Some(trait_did)) => {
|
||||||
|
self.lookup_method_in_trait(span, base_expr,
|
||||||
|
token::intern("deref_mut"), trait_did,
|
||||||
|
base_ty, None)
|
||||||
|
}
|
||||||
|
_ => None
|
||||||
|
};
|
||||||
|
|
||||||
|
// Otherwise, fall back to Deref.
|
||||||
|
let method = match (method, self.tcx.lang_items.deref_trait()) {
|
||||||
|
(None, Some(trait_did)) => {
|
||||||
|
self.lookup_method_in_trait(span, base_expr,
|
||||||
|
token::intern("deref"), trait_did,
|
||||||
|
base_ty, None)
|
||||||
|
}
|
||||||
|
(method, _) => method
|
||||||
|
};
|
||||||
|
|
||||||
|
method
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,7 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use super::{DeferredCallResolution, Expectation, FnCtxt,
|
use super::{DeferredCallResolution, Expectation, FnCtxt,
|
||||||
TupleArgumentsFlag, UnresolvedTypeAction};
|
TupleArgumentsFlag};
|
||||||
|
|
||||||
use CrateCtxt;
|
use CrateCtxt;
|
||||||
use middle::cstore::LOCAL_CRATE;
|
use middle::cstore::LOCAL_CRATE;
|
||||||
|
@ -72,15 +72,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||||
{
|
{
|
||||||
self.check_expr(callee_expr);
|
self.check_expr(callee_expr);
|
||||||
let original_callee_ty = self.expr_ty(callee_expr);
|
let original_callee_ty = self.expr_ty(callee_expr);
|
||||||
let (callee_ty, _, result) =
|
|
||||||
self.autoderef(callee_expr.span,
|
let mut autoderef = self.autoderef(callee_expr.span, original_callee_ty);
|
||||||
original_callee_ty,
|
let result = autoderef.by_ref().flat_map(|(adj_ty, idx)| {
|
||||||
|| Some(callee_expr),
|
self.try_overloaded_call_step(call_expr, callee_expr, adj_ty, idx)
|
||||||
UnresolvedTypeAction::Error,
|
}).next();
|
||||||
LvaluePreference::NoPreference,
|
let callee_ty = autoderef.unambiguous_final_ty();
|
||||||
|adj_ty, idx| {
|
autoderef.finalize(LvaluePreference::NoPreference, Some(callee_expr));
|
||||||
self.try_overloaded_call_step(call_expr, callee_expr, adj_ty, idx)
|
|
||||||
});
|
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
None => {
|
None => {
|
||||||
|
|
|
@ -60,7 +60,7 @@
|
||||||
//! sort of a minor point so I've opted to leave it for later---after all
|
//! sort of a minor point so I've opted to leave it for later---after all
|
||||||
//! we may want to adjust precisely when coercions occur.
|
//! we may want to adjust precisely when coercions occur.
|
||||||
|
|
||||||
use check::{FnCtxt, UnresolvedTypeAction};
|
use check::{FnCtxt};
|
||||||
|
|
||||||
use rustc::hir;
|
use rustc::hir;
|
||||||
use rustc::infer::{Coercion, InferOk, TypeOrigin, TypeTrace};
|
use rustc::infer::{Coercion, InferOk, TypeOrigin, TypeTrace};
|
||||||
|
@ -220,7 +220,8 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
|
||||||
-> CoerceResult<'tcx>
|
-> CoerceResult<'tcx>
|
||||||
// FIXME(eddyb) use copyable iterators when that becomes ergonomic.
|
// FIXME(eddyb) use copyable iterators when that becomes ergonomic.
|
||||||
where E: Fn() -> I,
|
where E: Fn() -> I,
|
||||||
I: IntoIterator<Item=&'a hir::Expr> {
|
I: IntoIterator<Item=&'a hir::Expr>
|
||||||
|
{
|
||||||
|
|
||||||
debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b);
|
debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b);
|
||||||
|
|
||||||
|
@ -240,18 +241,16 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
|
||||||
|
|
||||||
let span = self.origin.span();
|
let span = self.origin.span();
|
||||||
|
|
||||||
let lvalue_pref = LvaluePreference::from_mutbl(mt_b.mutbl);
|
|
||||||
let mut first_error = None;
|
let mut first_error = None;
|
||||||
let mut r_borrow_var = None;
|
let mut r_borrow_var = None;
|
||||||
let (_, autoderefs, success) = self.autoderef(span, a, exprs,
|
let mut autoderef = self.autoderef(span, a);
|
||||||
UnresolvedTypeAction::Ignore,
|
let mut success = None;
|
||||||
lvalue_pref,
|
|
||||||
|referent_ty, autoderef|
|
for (referent_ty, autoderefs) in autoderef.by_ref() {
|
||||||
{
|
if autoderefs == 0 {
|
||||||
if autoderef == 0 {
|
|
||||||
// Don't let this pass, otherwise it would cause
|
// Don't let this pass, otherwise it would cause
|
||||||
// &T to autoref to &&T.
|
// &T to autoref to &&T.
|
||||||
return None;
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point, we have deref'd `a` to `referent_ty`. So
|
// At this point, we have deref'd `a` to `referent_ty`. So
|
||||||
|
@ -326,7 +325,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
|
||||||
// and let regionck figure it out.
|
// and let regionck figure it out.
|
||||||
let r = if !self.use_lub {
|
let r = if !self.use_lub {
|
||||||
r_b // [2] above
|
r_b // [2] above
|
||||||
} else if autoderef == 1 {
|
} else if autoderefs == 1 {
|
||||||
r_a // [3] above
|
r_a // [3] above
|
||||||
} else {
|
} else {
|
||||||
if r_borrow_var.is_none() { // create var lazilly, at most once
|
if r_borrow_var.is_none() { // create var lazilly, at most once
|
||||||
|
@ -341,23 +340,22 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
|
||||||
mutbl: mt_b.mutbl // [1] above
|
mutbl: mt_b.mutbl // [1] above
|
||||||
});
|
});
|
||||||
match self.unify(derefd_ty_a, b) {
|
match self.unify(derefd_ty_a, b) {
|
||||||
Ok(ty) => Some(ty),
|
Ok(ty) => { success = Some((ty, autoderefs)); break },
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if first_error.is_none() {
|
if first_error.is_none() {
|
||||||
first_error = Some(err);
|
first_error = Some(err);
|
||||||
}
|
}
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
// Extract type or return an error. We return the first error
|
// Extract type or return an error. We return the first error
|
||||||
// we got, which should be from relating the "base" type
|
// we got, which should be from relating the "base" type
|
||||||
// (e.g., in example above, the failure from relating `Vec<T>`
|
// (e.g., in example above, the failure from relating `Vec<T>`
|
||||||
// to the target type), since that should be the least
|
// to the target type), since that should be the least
|
||||||
// confusing.
|
// confusing.
|
||||||
let ty = match success {
|
let (ty, autoderefs) = match success {
|
||||||
Some(ty) => ty,
|
Some(d) => d,
|
||||||
None => {
|
None => {
|
||||||
let err = first_error.expect("coerce_borrowed_pointer had no error");
|
let err = first_error.expect("coerce_borrowed_pointer had no error");
|
||||||
debug!("coerce_borrowed_pointer: failed with err = {:?}", err);
|
debug!("coerce_borrowed_pointer: failed with err = {:?}", err);
|
||||||
|
@ -365,6 +363,10 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This commits the obligations to the fulfillcx. After this succeeds,
|
||||||
|
// this snapshot can't be rolled back.
|
||||||
|
autoderef.finalize(LvaluePreference::from_mutbl(mt_b.mutbl), exprs());
|
||||||
|
|
||||||
// Now apply the autoref. We have to extract the region out of
|
// Now apply the autoref. We have to extract the region out of
|
||||||
// the final ref type we got.
|
// the final ref type we got.
|
||||||
if ty == a && mt_a.mutbl == hir::MutImmutable && autoderefs == 1 {
|
if ty == a && mt_a.mutbl == hir::MutImmutable && autoderefs == 1 {
|
||||||
|
|
|
@ -279,78 +279,63 @@ pub fn compare_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
|
||||||
// type.
|
// type.
|
||||||
|
|
||||||
// Compute skolemized form of impl and trait method tys.
|
// Compute skolemized form of impl and trait method tys.
|
||||||
let impl_fty = tcx.mk_fn_ptr(impl_m.fty);
|
let tcx = infcx.tcx;
|
||||||
let impl_fty = impl_fty.subst(tcx, impl_to_skol_substs);
|
let origin = TypeOrigin::MethodCompatCheck(impl_m_span);
|
||||||
let trait_fty = tcx.mk_fn_ptr(trait_m.fty);
|
|
||||||
let trait_fty = trait_fty.subst(tcx, &trait_to_skol_substs);
|
|
||||||
|
|
||||||
let err = infcx.commit_if_ok(|snapshot| {
|
let (impl_sig, _) =
|
||||||
let tcx = infcx.tcx;
|
infcx.replace_late_bound_regions_with_fresh_var(impl_m_span,
|
||||||
let origin = TypeOrigin::MethodCompatCheck(impl_m_span);
|
infer::HigherRankedType,
|
||||||
|
&impl_m.fty.sig);
|
||||||
|
let impl_sig =
|
||||||
|
impl_sig.subst(tcx, impl_to_skol_substs);
|
||||||
|
let impl_sig =
|
||||||
|
assoc::normalize_associated_types_in(&infcx,
|
||||||
|
&mut fulfillment_cx,
|
||||||
|
impl_m_span,
|
||||||
|
impl_m_body_id,
|
||||||
|
&impl_sig);
|
||||||
|
let impl_fty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
|
||||||
|
unsafety: impl_m.fty.unsafety,
|
||||||
|
abi: impl_m.fty.abi,
|
||||||
|
sig: ty::Binder(impl_sig)
|
||||||
|
}));
|
||||||
|
debug!("compare_impl_method: impl_fty={:?}", impl_fty);
|
||||||
|
|
||||||
let (impl_sig, _) =
|
let trait_sig = tcx.liberate_late_bound_regions(
|
||||||
infcx.replace_late_bound_regions_with_fresh_var(impl_m_span,
|
infcx.parameter_environment.free_id_outlive,
|
||||||
infer::HigherRankedType,
|
&trait_m.fty.sig);
|
||||||
&impl_m.fty.sig);
|
let trait_sig =
|
||||||
let impl_sig =
|
trait_sig.subst(tcx, &trait_to_skol_substs);
|
||||||
impl_sig.subst(tcx, impl_to_skol_substs);
|
let trait_sig =
|
||||||
let impl_sig =
|
assoc::normalize_associated_types_in(&infcx,
|
||||||
assoc::normalize_associated_types_in(&infcx,
|
&mut fulfillment_cx,
|
||||||
&mut fulfillment_cx,
|
impl_m_span,
|
||||||
impl_m_span,
|
impl_m_body_id,
|
||||||
impl_m_body_id,
|
&trait_sig);
|
||||||
&impl_sig);
|
let trait_fty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
|
||||||
let impl_fty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
|
unsafety: trait_m.fty.unsafety,
|
||||||
unsafety: impl_m.fty.unsafety,
|
abi: trait_m.fty.abi,
|
||||||
abi: impl_m.fty.abi,
|
sig: ty::Binder(trait_sig)
|
||||||
sig: ty::Binder(impl_sig)
|
}));
|
||||||
}));
|
|
||||||
debug!("compare_impl_method: impl_fty={:?}",
|
|
||||||
impl_fty);
|
|
||||||
|
|
||||||
let (trait_sig, skol_map) =
|
debug!("compare_impl_method: trait_fty={:?}", trait_fty);
|
||||||
infcx.skolemize_late_bound_regions(&trait_m.fty.sig, snapshot);
|
|
||||||
let trait_sig =
|
|
||||||
trait_sig.subst(tcx, &trait_to_skol_substs);
|
|
||||||
let trait_sig =
|
|
||||||
assoc::normalize_associated_types_in(&infcx,
|
|
||||||
&mut fulfillment_cx,
|
|
||||||
impl_m_span,
|
|
||||||
impl_m_body_id,
|
|
||||||
&trait_sig);
|
|
||||||
let trait_fty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
|
|
||||||
unsafety: trait_m.fty.unsafety,
|
|
||||||
abi: trait_m.fty.abi,
|
|
||||||
sig: ty::Binder(trait_sig)
|
|
||||||
}));
|
|
||||||
|
|
||||||
debug!("compare_impl_method: trait_fty={:?}",
|
if let Err(terr) = infcx.sub_types(false, origin, impl_fty, trait_fty) {
|
||||||
|
debug!("sub_types failed: impl ty {:?}, trait ty {:?}",
|
||||||
|
impl_fty,
|
||||||
trait_fty);
|
trait_fty);
|
||||||
|
span_err!(tcx.sess, impl_m_span, E0053,
|
||||||
infcx.sub_types(false, origin, impl_fty, trait_fty)?;
|
"method `{}` has an incompatible type for trait: {}",
|
||||||
|
trait_m.name,
|
||||||
infcx.leak_check(false, &skol_map, snapshot)
|
terr);
|
||||||
});
|
return
|
||||||
|
|
||||||
match err {
|
|
||||||
Ok(()) => { }
|
|
||||||
Err(terr) => {
|
|
||||||
debug!("checking trait method for compatibility: impl ty {:?}, trait ty {:?}",
|
|
||||||
impl_fty,
|
|
||||||
trait_fty);
|
|
||||||
span_err!(tcx.sess, impl_m_span, E0053,
|
|
||||||
"method `{}` has an incompatible type for trait: {}",
|
|
||||||
trait_m.name,
|
|
||||||
terr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that all obligations are satisfied by the implementation's
|
// Check that all obligations are satisfied by the implementation's
|
||||||
// version.
|
// version.
|
||||||
match fulfillment_cx.select_all_or_error(&infcx) {
|
if let Err(ref errors) = fulfillment_cx.select_all_or_error(&infcx) {
|
||||||
Err(ref errors) => { infcx.report_fulfillment_errors(errors) }
|
infcx.report_fulfillment_errors(errors);
|
||||||
Ok(_) => {}
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, resolve all regions. This catches wily misuses of
|
// Finally, resolve all regions. This catches wily misuses of
|
||||||
|
|
|
@ -11,11 +11,10 @@
|
||||||
use super::probe;
|
use super::probe;
|
||||||
|
|
||||||
use check::{FnCtxt, callee};
|
use check::{FnCtxt, callee};
|
||||||
use check::UnresolvedTypeAction;
|
|
||||||
use hir::def_id::DefId;
|
use hir::def_id::DefId;
|
||||||
use rustc::ty::subst::{self};
|
use rustc::ty::subst::{self};
|
||||||
use rustc::traits;
|
use rustc::traits;
|
||||||
use rustc::ty::{self, NoPreference, PreferMutLvalue, Ty};
|
use rustc::ty::{self, LvaluePreference, NoPreference, PreferMutLvalue, Ty};
|
||||||
use rustc::ty::adjustment::{AdjustDerefRef, AutoDerefRef, AutoPtr};
|
use rustc::ty::adjustment::{AdjustDerefRef, AutoDerefRef, AutoPtr};
|
||||||
use rustc::ty::fold::TypeFoldable;
|
use rustc::ty::fold::TypeFoldable;
|
||||||
use rustc::infer::{self, InferOk, TypeOrigin};
|
use rustc::infer::{self, InferOk, TypeOrigin};
|
||||||
|
@ -133,10 +132,10 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
||||||
ty: fty,
|
ty: fty,
|
||||||
substs: all_substs
|
substs: all_substs
|
||||||
};
|
};
|
||||||
// If this is an `&mut self` method, bias the receiver
|
|
||||||
// expression towards mutability (this will switch
|
if let Some(hir::MutMutable) = pick.autoref {
|
||||||
// e.g. `Deref` to `DerefMut` in overloaded derefs and so on).
|
self.convert_lvalue_derefs_to_mutable();
|
||||||
self.fixup_derefs_on_method_receiver_if_necessary(&callee);
|
}
|
||||||
|
|
||||||
callee
|
callee
|
||||||
}
|
}
|
||||||
|
@ -164,22 +163,14 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
||||||
(None, None)
|
(None, None)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Commit the autoderefs by calling `autoderef again, but this
|
// Commit the autoderefs by calling `autoderef` again, but this
|
||||||
// time writing the results into the various tables.
|
// time writing the results into the various tables.
|
||||||
let (autoderefd_ty, n, result) = self.autoderef(self.span,
|
let mut autoderef = self.autoderef(self.span, unadjusted_self_ty);
|
||||||
unadjusted_self_ty,
|
let (autoderefd_ty, n) = autoderef.nth(pick.autoderefs).unwrap();
|
||||||
|| Some(self.self_expr),
|
|
||||||
UnresolvedTypeAction::Error,
|
|
||||||
NoPreference,
|
|
||||||
|_, n| {
|
|
||||||
if n == pick.autoderefs {
|
|
||||||
Some(())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
});
|
|
||||||
assert_eq!(n, pick.autoderefs);
|
assert_eq!(n, pick.autoderefs);
|
||||||
assert_eq!(result, Some(()));
|
|
||||||
|
autoderef.unambiguous_final_ty();
|
||||||
|
autoderef.finalize(LvaluePreference::NoPreference, Some(self.self_expr));
|
||||||
|
|
||||||
// Write out the final adjustment.
|
// Write out the final adjustment.
|
||||||
self.write_adjustment(self.self_expr.id, AdjustDerefRef(AutoDerefRef {
|
self.write_adjustment(self.self_expr.id, AdjustDerefRef(AutoDerefRef {
|
||||||
|
@ -293,27 +284,21 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
||||||
// yield an object-type (e.g., `&Object` or `Box<Object>`
|
// yield an object-type (e.g., `&Object` or `Box<Object>`
|
||||||
// etc).
|
// etc).
|
||||||
|
|
||||||
let (_, _, result) = self.fcx.autoderef(self.span,
|
// FIXME: this feels, like, super dubious
|
||||||
self_ty,
|
self.fcx.autoderef(self.span, self_ty)
|
||||||
|| None,
|
.filter_map(|(ty, _)| {
|
||||||
UnresolvedTypeAction::Error,
|
match ty.sty {
|
||||||
NoPreference,
|
ty::TyTrait(ref data) => Some(closure(self, ty, &data)),
|
||||||
|ty, _| {
|
_ => None,
|
||||||
match ty.sty {
|
}
|
||||||
ty::TyTrait(ref data) => Some(closure(self, ty, &data)),
|
})
|
||||||
_ => None,
|
.next()
|
||||||
}
|
.unwrap_or_else(|| {
|
||||||
});
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Some(r) => r,
|
|
||||||
None => {
|
|
||||||
span_bug!(
|
span_bug!(
|
||||||
self.span,
|
self.span,
|
||||||
"self-type `{}` for ObjectPick never dereferenced to an object",
|
"self-type `{}` for ObjectPick never dereferenced to an object",
|
||||||
self_ty)
|
self_ty)
|
||||||
}
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn instantiate_method_substs(&mut self,
|
fn instantiate_method_substs(&mut self,
|
||||||
|
@ -463,24 +448,10 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// RECONCILIATION
|
// RECONCILIATION
|
||||||
|
|
||||||
/// When we select a method with an `&mut self` receiver, we have to go convert any
|
/// When we select a method with a mutable autoref, we have to go convert any
|
||||||
/// auto-derefs, indices, etc from `Deref` and `Index` into `DerefMut` and `IndexMut`
|
/// auto-derefs, indices, etc from `Deref` and `Index` into `DerefMut` and `IndexMut`
|
||||||
/// respectively.
|
/// respectively.
|
||||||
fn fixup_derefs_on_method_receiver_if_necessary(&self,
|
fn convert_lvalue_derefs_to_mutable(&self) {
|
||||||
method_callee: &ty::MethodCallee) {
|
|
||||||
let sig = match method_callee.ty.sty {
|
|
||||||
ty::TyFnDef(_, _, ref f) => f.sig.clone(),
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
match sig.0.inputs[0].sty {
|
|
||||||
ty::TyRef(_, ty::TypeAndMut {
|
|
||||||
ty: _,
|
|
||||||
mutbl: hir::MutMutable,
|
|
||||||
}) => {}
|
|
||||||
_ => return,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gather up expressions we want to munge.
|
// Gather up expressions we want to munge.
|
||||||
let mut exprs = Vec::new();
|
let mut exprs = Vec::new();
|
||||||
exprs.push(self.self_expr);
|
exprs.push(self.self_expr);
|
||||||
|
@ -495,8 +466,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("fixup_derefs_on_method_receiver_if_necessary: exprs={:?}",
|
debug!("convert_lvalue_derefs_to_mutable: exprs={:?}", exprs);
|
||||||
exprs);
|
|
||||||
|
|
||||||
// Fix up autoderefs and derefs.
|
// Fix up autoderefs and derefs.
|
||||||
for (i, &expr) in exprs.iter().rev().enumerate() {
|
for (i, &expr) in exprs.iter().rev().enumerate() {
|
||||||
|
@ -509,23 +479,17 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
||||||
Some(_) | None => 0,
|
Some(_) | None => 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("fixup_derefs_on_method_receiver_if_necessary: i={} expr={:?} \
|
debug!("convert_lvalue_derefs_to_mutable: i={} expr={:?} \
|
||||||
autoderef_count={}",
|
autoderef_count={}",
|
||||||
i, expr, autoderef_count);
|
i, expr, autoderef_count);
|
||||||
|
|
||||||
if autoderef_count > 0 {
|
if autoderef_count > 0 {
|
||||||
self.autoderef(expr.span,
|
let mut autoderef = self.autoderef(expr.span, self.expr_ty(expr));
|
||||||
self.expr_ty(expr),
|
autoderef.nth(autoderef_count).unwrap_or_else(|| {
|
||||||
|| Some(expr),
|
span_bug!(expr.span, "expr was deref-able {} times but now isn't?",
|
||||||
UnresolvedTypeAction::Error,
|
autoderef_count);
|
||||||
PreferMutLvalue,
|
|
||||||
|_, autoderefs| {
|
|
||||||
if autoderefs == autoderef_count + 1 {
|
|
||||||
Some(())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
autoderef.finalize(PreferMutLvalue, Some(expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't retry the first one or we might infinite loop!
|
// Don't retry the first one or we might infinite loop!
|
||||||
|
|
|
@ -13,13 +13,13 @@ use super::NoMatchData;
|
||||||
use super::{CandidateSource, ImplSource, TraitSource};
|
use super::{CandidateSource, ImplSource, TraitSource};
|
||||||
use super::suggest;
|
use super::suggest;
|
||||||
|
|
||||||
use check::{FnCtxt, UnresolvedTypeAction};
|
use check::{FnCtxt};
|
||||||
use hir::def_id::DefId;
|
use hir::def_id::DefId;
|
||||||
use hir::def::Def;
|
use hir::def::Def;
|
||||||
use rustc::ty::subst;
|
use rustc::ty::subst;
|
||||||
use rustc::ty::subst::Subst;
|
use rustc::ty::subst::Subst;
|
||||||
use rustc::traits;
|
use rustc::traits;
|
||||||
use rustc::ty::{self, NoPreference, Ty, ToPolyTraitRef, TraitRef, TypeFoldable};
|
use rustc::ty::{self, Ty, ToPolyTraitRef, TraitRef, TypeFoldable};
|
||||||
use rustc::infer::{InferOk, TypeOrigin};
|
use rustc::infer::{InferOk, TypeOrigin};
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use syntax::codemap::{Span, DUMMY_SP};
|
use syntax::codemap::{Span, DUMMY_SP};
|
||||||
|
@ -208,25 +208,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||||
fn create_steps(&self,
|
fn create_steps(&self,
|
||||||
span: Span,
|
span: Span,
|
||||||
self_ty: Ty<'tcx>)
|
self_ty: Ty<'tcx>)
|
||||||
-> Option<Vec<CandidateStep<'tcx>>> {
|
-> Option<Vec<CandidateStep<'tcx>>>
|
||||||
let mut steps = Vec::new();
|
{
|
||||||
|
// FIXME: we don't need to create the entire steps in one pass
|
||||||
|
|
||||||
let (final_ty, dereferences, _) = self.autoderef(span,
|
let mut autoderef = self.autoderef(span, self_ty);
|
||||||
self_ty,
|
let mut steps: Vec<_> = autoderef.by_ref().map(|(ty, d)| CandidateStep {
|
||||||
|| None,
|
self_ty: ty,
|
||||||
UnresolvedTypeAction::Error,
|
autoderefs: d,
|
||||||
NoPreference,
|
unsize: false
|
||||||
|t, d| {
|
}).collect();
|
||||||
steps.push(CandidateStep {
|
|
||||||
self_ty: t,
|
|
||||||
autoderefs: d,
|
|
||||||
unsize: false
|
|
||||||
});
|
|
||||||
None::<()> // keep iterating until we can't anymore
|
|
||||||
});
|
|
||||||
|
|
||||||
|
let final_ty = autoderef.unambiguous_final_ty();
|
||||||
match final_ty.sty {
|
match final_ty.sty {
|
||||||
ty::TyArray(elem_ty, _) => {
|
ty::TyArray(elem_ty, _) => {
|
||||||
|
let dereferences = steps.len() - 1;
|
||||||
|
|
||||||
steps.push(CandidateStep {
|
steps.push(CandidateStep {
|
||||||
self_ty: self.tcx.mk_slice(elem_ty),
|
self_ty: self.tcx.mk_slice(elem_ty),
|
||||||
autoderefs: dereferences,
|
autoderefs: dereferences,
|
||||||
|
@ -237,6 +234,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug!("create_steps: steps={:?}", steps);
|
||||||
|
|
||||||
Some(steps)
|
Some(steps)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
use CrateCtxt;
|
use CrateCtxt;
|
||||||
|
|
||||||
use check::{self, FnCtxt, UnresolvedTypeAction};
|
use check::{FnCtxt};
|
||||||
use rustc::hir::map as hir_map;
|
use rustc::hir::map as hir_map;
|
||||||
use rustc::ty::{self, Ty, ToPolyTraitRef, ToPredicate, TypeFoldable};
|
use rustc::ty::{self, Ty, ToPolyTraitRef, ToPredicate, TypeFoldable};
|
||||||
use middle::cstore;
|
use middle::cstore;
|
||||||
|
@ -21,7 +21,6 @@ use hir::def::Def;
|
||||||
use hir::def_id::DefId;
|
use hir::def_id::DefId;
|
||||||
use middle::lang_items::FnOnceTraitLangItem;
|
use middle::lang_items::FnOnceTraitLangItem;
|
||||||
use rustc::ty::subst::Substs;
|
use rustc::ty::subst::Substs;
|
||||||
use rustc::ty::LvaluePreference;
|
|
||||||
use rustc::traits::{Obligation, SelectionContext};
|
use rustc::traits::{Obligation, SelectionContext};
|
||||||
use util::nodemap::{FnvHashSet};
|
use util::nodemap::{FnvHashSet};
|
||||||
|
|
||||||
|
@ -48,42 +47,28 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||||
ty::TyClosure(..) | ty::TyFnDef(..) | ty::TyFnPtr(_) => true,
|
ty::TyClosure(..) | ty::TyFnDef(..) | ty::TyFnPtr(_) => true,
|
||||||
// If it's not a simple function, look for things which implement FnOnce
|
// If it's not a simple function, look for things which implement FnOnce
|
||||||
_ => {
|
_ => {
|
||||||
if let Ok(fn_once_trait_did) =
|
let fn_once = match tcx.lang_items.require(FnOnceTraitLangItem) {
|
||||||
tcx.lang_items.require(FnOnceTraitLangItem) {
|
Ok(fn_once) => fn_once,
|
||||||
let (_, _, opt_is_fn) = self.autoderef(span,
|
Err(..) => return false
|
||||||
ty,
|
};
|
||||||
|| None,
|
|
||||||
UnresolvedTypeAction::Ignore,
|
|
||||||
LvaluePreference::NoPreference,
|
|
||||||
|ty, _| {
|
|
||||||
self.probe(|_| {
|
|
||||||
let fn_once_substs =
|
|
||||||
Substs::new_trait(vec![self.next_ty_var()], vec![], ty);
|
|
||||||
let trait_ref =
|
|
||||||
ty::TraitRef::new(fn_once_trait_did,
|
|
||||||
tcx.mk_substs(fn_once_substs));
|
|
||||||
let poly_trait_ref = trait_ref.to_poly_trait_ref();
|
|
||||||
let obligation = Obligation::misc(span,
|
|
||||||
self.body_id,
|
|
||||||
poly_trait_ref
|
|
||||||
.to_predicate());
|
|
||||||
let mut selcx = SelectionContext::new(self);
|
|
||||||
|
|
||||||
if selcx.evaluate_obligation(&obligation) {
|
self.autoderef(span, ty).any(|(ty, _)| self.probe(|_| {
|
||||||
Some(())
|
let fn_once_substs =
|
||||||
} else {
|
Substs::new_trait(vec![self.next_ty_var()], vec![], ty);
|
||||||
None
|
let trait_ref =
|
||||||
}
|
ty::TraitRef::new(fn_once,
|
||||||
})
|
tcx.mk_substs(fn_once_substs));
|
||||||
});
|
let poly_trait_ref = trait_ref.to_poly_trait_ref();
|
||||||
|
let obligation = Obligation::misc(span,
|
||||||
opt_is_fn.is_some()
|
self.body_id,
|
||||||
} else {
|
poly_trait_ref
|
||||||
false
|
.to_predicate());
|
||||||
}
|
SelectionContext::new(self).evaluate_obligation(&obligation)
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report_method_error(&self,
|
pub fn report_method_error(&self,
|
||||||
span: Span,
|
span: Span,
|
||||||
rcvr_ty: Ty<'tcx>,
|
rcvr_ty: Ty<'tcx>,
|
||||||
|
@ -384,15 +369,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||||
return is_local(self.resolve_type_vars_with_obligations(rcvr_ty));
|
return is_local(self.resolve_type_vars_with_obligations(rcvr_ty));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.autoderef(span, rcvr_ty, || None,
|
self.autoderef(span, rcvr_ty).any(|(ty, _)| is_local(ty))
|
||||||
check::UnresolvedTypeAction::Ignore, ty::NoPreference,
|
|
||||||
|ty, _| {
|
|
||||||
if is_local(ty) {
|
|
||||||
Some(())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}).2.is_some()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -129,6 +129,7 @@ use rustc_back::slice;
|
||||||
use rustc_const_eval::eval_repeat_count;
|
use rustc_const_eval::eval_repeat_count;
|
||||||
|
|
||||||
mod assoc;
|
mod assoc;
|
||||||
|
mod autoderef;
|
||||||
pub mod dropck;
|
pub mod dropck;
|
||||||
pub mod _match;
|
pub mod _match;
|
||||||
pub mod writeback;
|
pub mod writeback;
|
||||||
|
@ -1412,17 +1413,6 @@ impl<'a, 'gcx, 'tcx> RegionScope for FnCtxt<'a, 'gcx, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether `autoderef` requires types to resolve.
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum UnresolvedTypeAction {
|
|
||||||
/// Produce an error and return `TyError` whenever a type cannot
|
|
||||||
/// be resolved (i.e. it is `TyInfer`).
|
|
||||||
Error,
|
|
||||||
/// Go on without emitting any errors, and return the unresolved
|
|
||||||
/// type. Useful for probing, e.g. in coercions.
|
|
||||||
Ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Controls whether the arguments are tupled. This is used for the call
|
/// Controls whether the arguments are tupled. This is used for the call
|
||||||
/// operator.
|
/// operator.
|
||||||
///
|
///
|
||||||
|
@ -2228,120 +2218,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes an autoderef loop for the type `t`. At each step, invokes `should_stop`
|
|
||||||
/// to decide whether to terminate the loop. Returns the final type and number of
|
|
||||||
/// derefs that it performed.
|
|
||||||
///
|
|
||||||
/// Note: this method does not modify the adjustments table. The caller is responsible for
|
|
||||||
/// inserting an AutoAdjustment record into the `self` using one of the suitable methods.
|
|
||||||
pub fn autoderef<'b, E, I, T, F>(&self,
|
|
||||||
sp: Span,
|
|
||||||
base_ty: Ty<'tcx>,
|
|
||||||
maybe_exprs: E,
|
|
||||||
unresolved_type_action: UnresolvedTypeAction,
|
|
||||||
mut lvalue_pref: LvaluePreference,
|
|
||||||
mut should_stop: F)
|
|
||||||
-> (Ty<'tcx>, usize, Option<T>)
|
|
||||||
// FIXME(eddyb) use copyable iterators when that becomes ergonomic.
|
|
||||||
where E: Fn() -> I,
|
|
||||||
I: IntoIterator<Item=&'b hir::Expr>,
|
|
||||||
F: FnMut(Ty<'tcx>, usize) -> Option<T>,
|
|
||||||
{
|
|
||||||
debug!("autoderef(base_ty={:?}, lvalue_pref={:?})",
|
|
||||||
base_ty, lvalue_pref);
|
|
||||||
|
|
||||||
let mut t = base_ty;
|
|
||||||
for autoderefs in 0..self.tcx.sess.recursion_limit.get() {
|
|
||||||
let resolved_t = match unresolved_type_action {
|
|
||||||
UnresolvedTypeAction::Error => {
|
|
||||||
self.structurally_resolved_type(sp, t)
|
|
||||||
}
|
|
||||||
UnresolvedTypeAction::Ignore => {
|
|
||||||
// We can continue even when the type cannot be resolved
|
|
||||||
// (i.e. it is an inference variable) because `Ty::builtin_deref`
|
|
||||||
// and `try_overloaded_deref` both simply return `None`
|
|
||||||
// in such a case without producing spurious errors.
|
|
||||||
self.resolve_type_vars_if_possible(&t)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if resolved_t.references_error() {
|
|
||||||
return (resolved_t, autoderefs, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
match should_stop(resolved_t, autoderefs) {
|
|
||||||
Some(x) => return (resolved_t, autoderefs, Some(x)),
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, deref if type is derefable:
|
|
||||||
|
|
||||||
// Super subtle: it might seem as though we should
|
|
||||||
// pass `opt_expr` to `try_overloaded_deref`, so that
|
|
||||||
// the (implicit) autoref of using an overloaded deref
|
|
||||||
// would get added to the adjustment table. However we
|
|
||||||
// do not do that, because it's kind of a
|
|
||||||
// "meta-adjustment" -- instead, we just leave it
|
|
||||||
// unrecorded and know that there "will be" an
|
|
||||||
// autoref. regionck and other bits of the code base,
|
|
||||||
// when they encounter an overloaded autoderef, have
|
|
||||||
// to do some reconstructive surgery. This is a pretty
|
|
||||||
// complex mess that is begging for a proper MIR.
|
|
||||||
let mt = if let Some(mt) = resolved_t.builtin_deref(false, lvalue_pref) {
|
|
||||||
mt
|
|
||||||
} else if let Some(method) = self.try_overloaded_deref(sp, None,
|
|
||||||
resolved_t, lvalue_pref) {
|
|
||||||
for expr in maybe_exprs() {
|
|
||||||
let method_call = MethodCall::autoderef(expr.id, autoderefs as u32);
|
|
||||||
self.tables.borrow_mut().method_map.insert(method_call, method);
|
|
||||||
}
|
|
||||||
self.make_overloaded_lvalue_return_type(method)
|
|
||||||
} else {
|
|
||||||
return (resolved_t, autoderefs, None);
|
|
||||||
};
|
|
||||||
|
|
||||||
t = mt.ty;
|
|
||||||
if mt.mutbl == hir::MutImmutable {
|
|
||||||
lvalue_pref = NoPreference;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We've reached the recursion limit, error gracefully.
|
|
||||||
span_err!(self.tcx.sess, sp, E0055,
|
|
||||||
"reached the recursion limit while auto-dereferencing {:?}",
|
|
||||||
base_ty);
|
|
||||||
(self.tcx.types.err, 0, None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_overloaded_deref(&self,
|
|
||||||
span: Span,
|
|
||||||
base_expr: Option<&hir::Expr>,
|
|
||||||
base_ty: Ty<'tcx>,
|
|
||||||
lvalue_pref: LvaluePreference)
|
|
||||||
-> Option<MethodCallee<'tcx>>
|
|
||||||
{
|
|
||||||
// Try DerefMut first, if preferred.
|
|
||||||
let method = match (lvalue_pref, self.tcx.lang_items.deref_mut_trait()) {
|
|
||||||
(PreferMutLvalue, Some(trait_did)) => {
|
|
||||||
self.lookup_method_in_trait(span, base_expr,
|
|
||||||
token::intern("deref_mut"), trait_did,
|
|
||||||
base_ty, None)
|
|
||||||
}
|
|
||||||
_ => None
|
|
||||||
};
|
|
||||||
|
|
||||||
// Otherwise, fall back to Deref.
|
|
||||||
let method = match (method, self.tcx.lang_items.deref_trait()) {
|
|
||||||
(None, Some(trait_did)) => {
|
|
||||||
self.lookup_method_in_trait(span, base_expr,
|
|
||||||
token::intern("deref"), trait_did,
|
|
||||||
base_ty, None)
|
|
||||||
}
|
|
||||||
(method, _) => method
|
|
||||||
};
|
|
||||||
|
|
||||||
method
|
|
||||||
}
|
|
||||||
|
|
||||||
/// For the overloaded lvalue expressions (`*x`, `x[3]`), the trait
|
/// For the overloaded lvalue expressions (`*x`, `x[3]`), the trait
|
||||||
/// returns a type of `&T`, but the actual type we assign to the
|
/// returns a type of `&T`, but the actual type we assign to the
|
||||||
/// *expression* is `T`. So this function just peels off the return
|
/// *expression* is `T`. So this function just peels off the return
|
||||||
|
@ -2371,29 +2247,28 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||||
// autoderef that normal method probing does. They could likely be
|
// autoderef that normal method probing does. They could likely be
|
||||||
// consolidated.
|
// consolidated.
|
||||||
|
|
||||||
let (ty, autoderefs, final_mt) = self.autoderef(base_expr.span,
|
let mut autoderef = self.autoderef(base_expr.span, base_ty);
|
||||||
base_ty,
|
|
||||||
|| Some(base_expr),
|
|
||||||
UnresolvedTypeAction::Error,
|
|
||||||
lvalue_pref,
|
|
||||||
|adj_ty, idx| {
|
|
||||||
self.try_index_step(MethodCall::expr(expr.id), expr, base_expr,
|
|
||||||
adj_ty, idx, false, lvalue_pref, idx_ty)
|
|
||||||
});
|
|
||||||
|
|
||||||
if final_mt.is_some() {
|
while let Some((adj_ty, autoderefs)) = autoderef.next() {
|
||||||
return final_mt;
|
if let Some(final_mt) = self.try_index_step(
|
||||||
}
|
MethodCall::expr(expr.id),
|
||||||
|
expr, base_expr, adj_ty, autoderefs,
|
||||||
|
false, lvalue_pref, idx_ty)
|
||||||
|
{
|
||||||
|
autoderef.finalize(lvalue_pref, Some(base_expr));
|
||||||
|
return Some(final_mt);
|
||||||
|
}
|
||||||
|
|
||||||
// After we have fully autoderef'd, if the resulting type is [T; n], then
|
if let ty::TyArray(element_ty, _) = adj_ty.sty {
|
||||||
// do a final unsized coercion to yield [T].
|
autoderef.finalize(lvalue_pref, Some(base_expr));
|
||||||
if let ty::TyArray(element_ty, _) = ty.sty {
|
let adjusted_ty = self.tcx.mk_slice(element_ty);
|
||||||
let adjusted_ty = self.tcx.mk_slice(element_ty);
|
return self.try_index_step(
|
||||||
self.try_index_step(MethodCall::expr(expr.id), expr, base_expr,
|
MethodCall::expr(expr.id), expr, base_expr,
|
||||||
adjusted_ty, autoderefs, true, lvalue_pref, idx_ty)
|
adjusted_ty, autoderefs, true, lvalue_pref, idx_ty);
|
||||||
} else {
|
}
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
autoderef.unambiguous_final_ty();
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// To type-check `base_expr[index_expr]`, we progressively autoderef
|
/// To type-check `base_expr[index_expr]`, we progressively autoderef
|
||||||
|
@ -3034,32 +2909,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||||
let expr_t = self.structurally_resolved_type(expr.span,
|
let expr_t = self.structurally_resolved_type(expr.span,
|
||||||
self.expr_ty(base));
|
self.expr_ty(base));
|
||||||
let mut private_candidate = None;
|
let mut private_candidate = None;
|
||||||
let (_, autoderefs, field_ty) = self.autoderef(expr.span,
|
let mut autoderef = self.autoderef(expr.span, expr_t);
|
||||||
expr_t,
|
while let Some((base_t, autoderefs)) = autoderef.next() {
|
||||||
|| Some(base),
|
if let ty::TyStruct(base_def, substs) = base_t.sty {
|
||||||
UnresolvedTypeAction::Error,
|
debug!("struct named {:?}", base_t);
|
||||||
lvalue_pref,
|
if let Some(field) = base_def.struct_variant().find_field_named(field.node) {
|
||||||
|base_t, _| {
|
let field_ty = self.field_ty(expr.span, field, substs);
|
||||||
if let ty::TyStruct(base_def, substs) = base_t.sty {
|
if field.vis.is_accessible_from(self.body_id, &self.tcx().map) {
|
||||||
debug!("struct named {:?}", base_t);
|
autoderef.finalize(lvalue_pref, Some(base));
|
||||||
if let Some(field) = base_def.struct_variant().find_field_named(field.node) {
|
self.write_ty(expr.id, field_ty);
|
||||||
let field_ty = self.field_ty(expr.span, field, substs);
|
self.write_autoderef_adjustment(base.id, autoderefs);
|
||||||
if field.vis.is_accessible_from(self.body_id, &self.tcx().map) {
|
return;
|
||||||
return Some(field_ty);
|
|
||||||
}
|
|
||||||
private_candidate = Some((base_def.did, field_ty));
|
|
||||||
}
|
}
|
||||||
|
private_candidate = Some((base_def.did, field_ty));
|
||||||
}
|
}
|
||||||
None
|
|
||||||
});
|
|
||||||
match field_ty {
|
|
||||||
Some(field_ty) => {
|
|
||||||
self.write_ty(expr.id, field_ty);
|
|
||||||
self.write_autoderef_adjustment(base.id, autoderefs);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
None => {}
|
|
||||||
}
|
}
|
||||||
|
autoderef.unambiguous_final_ty();
|
||||||
|
|
||||||
if let Some((did, field_ty)) = private_candidate {
|
if let Some((did, field_ty)) = private_candidate {
|
||||||
let struct_path = self.tcx().item_path_str(did);
|
let struct_path = self.tcx().item_path_str(did);
|
||||||
|
@ -3132,42 +2998,39 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||||
self.expr_ty(base));
|
self.expr_ty(base));
|
||||||
let mut private_candidate = None;
|
let mut private_candidate = None;
|
||||||
let mut tuple_like = false;
|
let mut tuple_like = false;
|
||||||
let (_, autoderefs, field_ty) = self.autoderef(expr.span,
|
let mut autoderef = self.autoderef(expr.span, expr_t);
|
||||||
expr_t,
|
while let Some((base_t, autoderefs)) = autoderef.next() {
|
||||||
|| Some(base),
|
let field = match base_t.sty {
|
||||||
UnresolvedTypeAction::Error,
|
ty::TyStruct(base_def, substs) => {
|
||||||
lvalue_pref,
|
tuple_like = base_def.struct_variant().is_tuple_struct();
|
||||||
|base_t, _| {
|
if !tuple_like { continue }
|
||||||
let (base_def, substs) = match base_t.sty {
|
|
||||||
ty::TyStruct(base_def, substs) => (base_def, substs),
|
|
||||||
ty::TyTuple(ref v) => {
|
|
||||||
tuple_like = true;
|
|
||||||
return if idx.node < v.len() { Some(v[idx.node]) } else { None }
|
|
||||||
}
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
tuple_like = base_def.struct_variant().is_tuple_struct();
|
debug!("tuple struct named {:?}", base_t);
|
||||||
if !tuple_like { return None }
|
base_def.struct_variant().fields.get(idx.node).and_then(|field| {
|
||||||
|
let field_ty = self.field_ty(expr.span, field, substs);
|
||||||
debug!("tuple struct named {:?}", base_t);
|
private_candidate = Some((base_def.did, field_ty));
|
||||||
if let Some(field) = base_def.struct_variant().fields.get(idx.node) {
|
if field.vis.is_accessible_from(self.body_id, &self.tcx().map) {
|
||||||
let field_ty = self.field_ty(expr.span, field, substs);
|
Some(field_ty)
|
||||||
if field.vis.is_accessible_from(self.body_id, &self.tcx().map) {
|
} else {
|
||||||
return Some(field_ty);
|
None
|
||||||
}
|
}
|
||||||
private_candidate = Some((base_def.did, field_ty));
|
})
|
||||||
}
|
}
|
||||||
None
|
ty::TyTuple(ref v) => {
|
||||||
});
|
tuple_like = true;
|
||||||
match field_ty {
|
v.get(idx.node).cloned()
|
||||||
Some(field_ty) => {
|
}
|
||||||
|
_ => continue
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(field_ty) = field {
|
||||||
|
autoderef.finalize(lvalue_pref, Some(base));
|
||||||
self.write_ty(expr.id, field_ty);
|
self.write_ty(expr.id, field_ty);
|
||||||
self.write_autoderef_adjustment(base.id, autoderefs);
|
self.write_autoderef_adjustment(base.id, autoderefs);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
None => {}
|
|
||||||
}
|
}
|
||||||
|
autoderef.unambiguous_final_ty();
|
||||||
|
|
||||||
if let Some((did, field_ty)) = private_candidate {
|
if let Some((did, field_ty)) = private_candidate {
|
||||||
let struct_path = self.tcx().item_path_str(did);
|
let struct_path = self.tcx().item_path_str(did);
|
||||||
|
|
|
@ -212,6 +212,13 @@ impl<T: Error> Error for Box<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[stable(feature = "fmt_error", since = "1.11.0")]
|
||||||
|
impl Error for fmt::Error {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
"an error occurred when formatting an argument"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// copied from any.rs
|
// copied from any.rs
|
||||||
impl Error + 'static {
|
impl Error + 'static {
|
||||||
/// Returns true if the boxed type is the same as `T`
|
/// Returns true if the boxed type is the same as `T`
|
||||||
|
|
|
@ -39,14 +39,14 @@ pub fn take_handler() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> {
|
||||||
take_hook()
|
take_hook()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A marker trait which represents "panic safe" types in Rust.
|
/// A marker trait which represents "unwind safe" types in Rust.
|
||||||
///
|
///
|
||||||
/// This trait is implemented by default for many types and behaves similarly in
|
/// This trait is implemented by default for many types and behaves similarly in
|
||||||
/// terms of inference of implementation to the `Send` and `Sync` traits. The
|
/// terms of inference of implementation to the `Send` and `Sync` traits. The
|
||||||
/// purpose of this trait is to encode what types are safe to cross a `recover`
|
/// purpose of this trait is to encode what types are safe to cross a `catch_unwind`
|
||||||
/// boundary with no fear of panic safety.
|
/// boundary with no fear of unwind safety.
|
||||||
///
|
///
|
||||||
/// ## What is panic safety?
|
/// ## What is unwind safety?
|
||||||
///
|
///
|
||||||
/// In Rust a function can "return" early if it either panics or calls a
|
/// In Rust a function can "return" early if it either panics or calls a
|
||||||
/// function which transitively panics. This sort of control flow is not always
|
/// function which transitively panics. This sort of control flow is not always
|
||||||
|
@ -59,62 +59,62 @@ pub fn take_handler() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> {
|
||||||
///
|
///
|
||||||
/// Typically in Rust, it is difficult to perform step (2) because catching a
|
/// Typically in Rust, it is difficult to perform step (2) because catching a
|
||||||
/// panic involves either spawning a thread (which in turns makes it difficult
|
/// panic involves either spawning a thread (which in turns makes it difficult
|
||||||
/// to later witness broken invariants) or using the `recover` function in this
|
/// to later witness broken invariants) or using the `catch_unwind` function in this
|
||||||
/// module. Additionally, even if an invariant is witnessed, it typically isn't a
|
/// module. Additionally, even if an invariant is witnessed, it typically isn't a
|
||||||
/// problem in Rust because there's no uninitialized values (like in C or C++).
|
/// problem in Rust because there are no uninitialized values (like in C or C++).
|
||||||
///
|
///
|
||||||
/// It is possible, however, for **logical** invariants to be broken in Rust,
|
/// It is possible, however, for **logical** invariants to be broken in Rust,
|
||||||
/// which can end up causing behavioral bugs. Another key aspect of panic safety
|
/// which can end up causing behavioral bugs. Another key aspect of unwind safety
|
||||||
/// in Rust is that, in the absence of `unsafe` code, a panic cannot lead to
|
/// in Rust is that, in the absence of `unsafe` code, a panic cannot lead to
|
||||||
/// memory unsafety.
|
/// memory unsafety.
|
||||||
///
|
///
|
||||||
/// That was a bit of a whirlwind tour of panic safety, but for more information
|
/// That was a bit of a whirlwind tour of unwind safety, but for more information
|
||||||
/// about panic safety and how it applies to Rust, see an [associated RFC][rfc].
|
/// about unwind safety and how it applies to Rust, see an [associated RFC][rfc].
|
||||||
///
|
///
|
||||||
/// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md
|
/// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md
|
||||||
///
|
///
|
||||||
/// ## What is `UnwindSafe`?
|
/// ## What is `UnwindSafe`?
|
||||||
///
|
///
|
||||||
/// Now that we've got an idea of what panic safety is in Rust, it's also
|
/// Now that we've got an idea of what unwind safety is in Rust, it's also
|
||||||
/// important to understand what this trait represents. As mentioned above, one
|
/// important to understand what this trait represents. As mentioned above, one
|
||||||
/// way to witness broken invariants is through the `recover` function in this
|
/// way to witness broken invariants is through the `catch_unwind` function in this
|
||||||
/// module as it allows catching a panic and then re-using the environment of
|
/// module as it allows catching a panic and then re-using the environment of
|
||||||
/// the closure.
|
/// the closure.
|
||||||
///
|
///
|
||||||
/// Simply put, a type `T` implements `UnwindSafe` if it cannot easily allow
|
/// Simply put, a type `T` implements `UnwindSafe` if it cannot easily allow
|
||||||
/// witnessing a broken invariant through the use of `recover` (catching a
|
/// witnessing a broken invariant through the use of `catch_unwind` (catching a
|
||||||
/// panic). This trait is a marker trait, so it is automatically implemented for
|
/// panic). This trait is a marker trait, so it is automatically implemented for
|
||||||
/// many types, and it is also structurally composed (e.g. a struct is recover
|
/// many types, and it is also structurally composed (e.g. a struct is unwind
|
||||||
/// safe if all of its components are recover safe).
|
/// safe if all of its components are unwind safe).
|
||||||
///
|
///
|
||||||
/// Note, however, that this is not an unsafe trait, so there is not a succinct
|
/// Note, however, that this is not an unsafe trait, so there is not a succinct
|
||||||
/// contract that this trait is providing. Instead it is intended as more of a
|
/// contract that this trait is providing. Instead it is intended as more of a
|
||||||
/// "speed bump" to alert users of `recover` that broken invariants may be
|
/// "speed bump" to alert users of `catch_unwind` that broken invariants may be
|
||||||
/// witnessed and may need to be accounted for.
|
/// witnessed and may need to be accounted for.
|
||||||
///
|
///
|
||||||
/// ## Who implements `UnwindSafe`?
|
/// ## Who implements `UnwindSafe`?
|
||||||
///
|
///
|
||||||
/// Types such as `&mut T` and `&RefCell<T>` are examples which are **not**
|
/// Types such as `&mut T` and `&RefCell<T>` are examples which are **not**
|
||||||
/// recover safe. The general idea is that any mutable state which can be shared
|
/// unwind safe. The general idea is that any mutable state which can be shared
|
||||||
/// across `recover` is not recover safe by default. This is because it is very
|
/// across `catch_unwind` is not unwind safe by default. This is because it is very
|
||||||
/// easy to witness a broken invariant outside of `recover` as the data is
|
/// easy to witness a broken invariant outside of `catch_unwind` as the data is
|
||||||
/// simply accessed as usual.
|
/// simply accessed as usual.
|
||||||
///
|
///
|
||||||
/// Types like `&Mutex<T>`, however, are recover safe because they implement
|
/// Types like `&Mutex<T>`, however, are unwind safe because they implement
|
||||||
/// poisoning by default. They still allow witnessing a broken invariant, but
|
/// poisoning by default. They still allow witnessing a broken invariant, but
|
||||||
/// they already provide their own "speed bumps" to do so.
|
/// they already provide their own "speed bumps" to do so.
|
||||||
///
|
///
|
||||||
/// ## When should `UnwindSafe` be used?
|
/// ## When should `UnwindSafe` be used?
|
||||||
///
|
///
|
||||||
/// Is not intended that most types or functions need to worry about this trait.
|
/// Is not intended that most types or functions need to worry about this trait.
|
||||||
/// It is only used as a bound on the `recover` function and as mentioned above,
|
/// It is only used as a bound on the `catch_unwind` function and as mentioned above,
|
||||||
/// the lack of `unsafe` means it is mostly an advisory. The `AssertUnwindSafe`
|
/// the lack of `unsafe` means it is mostly an advisory. The `AssertUnwindSafe`
|
||||||
/// wrapper struct in this module can be used to force this trait to be
|
/// wrapper struct in this module can be used to force this trait to be
|
||||||
/// implemented for any closed over variables passed to the `recover` function
|
/// implemented for any closed over variables passed to the `catch_unwind` function
|
||||||
/// (more on this below).
|
/// (more on this below).
|
||||||
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
||||||
#[rustc_on_unimplemented = "the type {Self} may not be safely transferred \
|
#[rustc_on_unimplemented = "the type {Self} may not be safely transferred \
|
||||||
across a recover boundary"]
|
across an unwind boundary"]
|
||||||
pub trait UnwindSafe {}
|
pub trait UnwindSafe {}
|
||||||
|
|
||||||
/// Deprecated, renamed to UnwindSafe
|
/// Deprecated, renamed to UnwindSafe
|
||||||
|
@ -126,7 +126,7 @@ pub trait RecoverSafe {}
|
||||||
impl<T: UnwindSafe> RecoverSafe for T {}
|
impl<T: UnwindSafe> RecoverSafe for T {}
|
||||||
|
|
||||||
/// A marker trait representing types where a shared reference is considered
|
/// A marker trait representing types where a shared reference is considered
|
||||||
/// recover safe.
|
/// unwind safe.
|
||||||
///
|
///
|
||||||
/// This trait is namely not implemented by `UnsafeCell`, the root of all
|
/// This trait is namely not implemented by `UnsafeCell`, the root of all
|
||||||
/// interior mutability.
|
/// interior mutability.
|
||||||
|
@ -136,23 +136,23 @@ impl<T: UnwindSafe> RecoverSafe for T {}
|
||||||
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
||||||
#[rustc_on_unimplemented = "the type {Self} contains interior mutability \
|
#[rustc_on_unimplemented = "the type {Self} contains interior mutability \
|
||||||
and a reference may not be safely transferrable \
|
and a reference may not be safely transferrable \
|
||||||
across a recover boundary"]
|
across a catch_unwind boundary"]
|
||||||
pub trait RefUnwindSafe {}
|
pub trait RefUnwindSafe {}
|
||||||
|
|
||||||
/// A simple wrapper around a type to assert that it is panic safe.
|
/// A simple wrapper around a type to assert that it is unwind safe.
|
||||||
///
|
///
|
||||||
/// When using `recover` it may be the case that some of the closed over
|
/// When using `catch_unwind` it may be the case that some of the closed over
|
||||||
/// variables are not panic safe. For example if `&mut T` is captured the
|
/// variables are not unwind safe. For example if `&mut T` is captured the
|
||||||
/// compiler will generate a warning indicating that it is not panic safe. It
|
/// compiler will generate a warning indicating that it is not unwind safe. It
|
||||||
/// may not be the case, however, that this is actually a problem due to the
|
/// may not be the case, however, that this is actually a problem due to the
|
||||||
/// specific usage of `recover` if panic safety is specifically taken into
|
/// specific usage of `catch_unwind` if unwind safety is specifically taken into
|
||||||
/// account. This wrapper struct is useful for a quick and lightweight
|
/// account. This wrapper struct is useful for a quick and lightweight
|
||||||
/// annotation that a variable is indeed panic safe.
|
/// annotation that a variable is indeed unwind safe.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// One way to use `AssertUnwindSafe` is to assert that the entire closure
|
/// One way to use `AssertUnwindSafe` is to assert that the entire closure
|
||||||
/// itself is recover safe, bypassing all checks for all variables:
|
/// itself is unwind safe, bypassing all checks for all variables:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use std::panic::{self, AssertUnwindSafe};
|
/// use std::panic::{self, AssertUnwindSafe};
|
||||||
|
@ -160,7 +160,7 @@ pub trait RefUnwindSafe {}
|
||||||
/// let mut variable = 4;
|
/// let mut variable = 4;
|
||||||
///
|
///
|
||||||
/// // This code will not compile because the closure captures `&mut variable`
|
/// // This code will not compile because the closure captures `&mut variable`
|
||||||
/// // which is not considered panic safe by default.
|
/// // which is not considered unwind safe by default.
|
||||||
///
|
///
|
||||||
/// // panic::catch_unwind(|| {
|
/// // panic::catch_unwind(|| {
|
||||||
/// // variable += 3;
|
/// // variable += 3;
|
||||||
|
@ -239,7 +239,7 @@ impl<T> UnwindSafe for AssertUnwindSafe<T> {}
|
||||||
impl<T> UnwindSafe for AssertRecoverSafe<T> {}
|
impl<T> UnwindSafe for AssertRecoverSafe<T> {}
|
||||||
|
|
||||||
// not covered via the Shared impl above b/c the inner contents use
|
// not covered via the Shared impl above b/c the inner contents use
|
||||||
// Cell/AtomicUsize, but the usage here is recover safe so we can lift the
|
// Cell/AtomicUsize, but the usage here is unwind safe so we can lift the
|
||||||
// impl up one level to Arc/Rc itself
|
// impl up one level to Arc/Rc itself
|
||||||
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
||||||
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for Rc<T> {}
|
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for Rc<T> {}
|
||||||
|
@ -352,9 +352,9 @@ impl<R, F: FnOnce() -> R> FnOnce<()> for AssertRecoverSafe<F> {
|
||||||
/// that all captured variables are safe to cross this boundary. The purpose of
|
/// that all captured variables are safe to cross this boundary. The purpose of
|
||||||
/// this bound is to encode the concept of [exception safety][rfc] in the type
|
/// this bound is to encode the concept of [exception safety][rfc] in the type
|
||||||
/// system. Most usage of this function should not need to worry about this
|
/// system. Most usage of this function should not need to worry about this
|
||||||
/// bound as programs are naturally panic safe without `unsafe` code. If it
|
/// bound as programs are naturally unwind safe without `unsafe` code. If it
|
||||||
/// becomes a problem the associated `AssertUnwindSafe` wrapper type in this
|
/// becomes a problem the associated `AssertUnwindSafe` wrapper type in this
|
||||||
/// module can be used to quickly assert that the usage here is indeed exception
|
/// module can be used to quickly assert that the usage here is indeed unwind
|
||||||
/// safe.
|
/// safe.
|
||||||
///
|
///
|
||||||
/// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md
|
/// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md
|
||||||
|
|
|
@ -682,6 +682,7 @@ mod test {
|
||||||
println!("r#\"\n{}\"#", str);
|
println!("r#\"\n{}\"#", str);
|
||||||
assert_eq!(str, &r#"
|
assert_eq!(str, &r#"
|
||||||
--> dummy.txt:11:1
|
--> dummy.txt:11:1
|
||||||
|
|>
|
||||||
11 |> e-lä-vän
|
11 |> e-lä-vän
|
||||||
|> ^
|
|> ^
|
||||||
"#[1..]);
|
"#[1..]);
|
||||||
|
@ -746,6 +747,7 @@ mod test {
|
||||||
|
|
||||||
let expect_start = &r#"
|
let expect_start = &r#"
|
||||||
--> dummy.txt:1:6
|
--> dummy.txt:1:6
|
||||||
|
|>
|
||||||
1 |> _____aaaaaa____bbbbbb__cccccdd_
|
1 |> _____aaaaaa____bbbbbb__cccccdd_
|
||||||
|> ^^^^^^ ^^^^^^ ^^^^^^^
|
|> ^^^^^^ ^^^^^^ ^^^^^^^
|
||||||
"#[1..];
|
"#[1..];
|
||||||
|
@ -818,6 +820,7 @@ mod test {
|
||||||
|
|
||||||
let expect0 = &r#"
|
let expect0 = &r#"
|
||||||
--> dummy.txt:5:1
|
--> dummy.txt:5:1
|
||||||
|
|>
|
||||||
5 |> ccccc
|
5 |> ccccc
|
||||||
|> ^
|
|> ^
|
||||||
...
|
...
|
||||||
|
@ -830,6 +833,7 @@ mod test {
|
||||||
|
|
||||||
let expect = &r#"
|
let expect = &r#"
|
||||||
--> dummy.txt:1:1
|
--> dummy.txt:1:1
|
||||||
|
|>
|
||||||
1 |> aaaaa
|
1 |> aaaaa
|
||||||
|> ^
|
|> ^
|
||||||
...
|
...
|
||||||
|
|
|
@ -478,6 +478,13 @@ impl FileInfo {
|
||||||
}],
|
}],
|
||||||
kind: RenderedLineKind::PrimaryFileName,
|
kind: RenderedLineKind::PrimaryFileName,
|
||||||
});
|
});
|
||||||
|
output.push(RenderedLine {
|
||||||
|
text: vec![StyledString {
|
||||||
|
text: "".to_string(),
|
||||||
|
style: Style::FileNameStyle,
|
||||||
|
}],
|
||||||
|
kind: RenderedLineKind::Annotations,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
output.push(RenderedLine {
|
output.push(RenderedLine {
|
||||||
|
@ -487,6 +494,13 @@ impl FileInfo {
|
||||||
}],
|
}],
|
||||||
kind: RenderedLineKind::OtherFileName,
|
kind: RenderedLineKind::OtherFileName,
|
||||||
});
|
});
|
||||||
|
output.push(RenderedLine {
|
||||||
|
text: vec![StyledString {
|
||||||
|
text: "".to_string(),
|
||||||
|
style: Style::FileNameStyle,
|
||||||
|
}],
|
||||||
|
kind: RenderedLineKind::Annotations,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,6 +98,7 @@ fn foo() {
|
||||||
let text = make_string(&lines);
|
let text = make_string(&lines);
|
||||||
assert_eq!(&text[..], &"
|
assert_eq!(&text[..], &"
|
||||||
--> foo.rs:3:2
|
--> foo.rs:3:2
|
||||||
|
|>
|
||||||
3 |> \tbar;
|
3 |> \tbar;
|
||||||
|> \t^^^
|
|> \t^^^
|
||||||
"[1..]);
|
"[1..]);
|
||||||
|
@ -130,6 +131,7 @@ fn foo() {
|
||||||
println!("text=\n{}", text);
|
println!("text=\n{}", text);
|
||||||
assert_eq!(&text[..], &r#"
|
assert_eq!(&text[..], &r#"
|
||||||
::: foo.rs
|
::: foo.rs
|
||||||
|
|>
|
||||||
3 |> vec.push(vec.pop().unwrap());
|
3 |> vec.push(vec.pop().unwrap());
|
||||||
|> --- --- - previous borrow ends here
|
|> --- --- - previous borrow ends here
|
||||||
|> | |
|
|> | |
|
||||||
|
@ -199,12 +201,14 @@ fn bar() {
|
||||||
// Note that the `|>` remain aligned across both files:
|
// Note that the `|>` remain aligned across both files:
|
||||||
assert_eq!(&text[..], &r#"
|
assert_eq!(&text[..], &r#"
|
||||||
--> foo.rs:3:14
|
--> foo.rs:3:14
|
||||||
|
|>
|
||||||
3 |> vec.push(vec.pop().unwrap());
|
3 |> vec.push(vec.pop().unwrap());
|
||||||
|> --- ^^^ - c
|
|> --- ^^^ - c
|
||||||
|> | |
|
|> | |
|
||||||
|> | b
|
|> | b
|
||||||
|> a
|
|> a
|
||||||
::: bar.rs
|
::: bar.rs
|
||||||
|
|>
|
||||||
17 |> vec.push();
|
17 |> vec.push();
|
||||||
|> --- - f
|
|> --- - f
|
||||||
|> |
|
|> |
|
||||||
|
@ -249,6 +253,7 @@ fn foo() {
|
||||||
println!("text=\n{}", text);
|
println!("text=\n{}", text);
|
||||||
assert_eq!(&text[..], &r#"
|
assert_eq!(&text[..], &r#"
|
||||||
::: foo.rs
|
::: foo.rs
|
||||||
|
|>
|
||||||
3 |> let name = find_id(&data, 22).unwrap();
|
3 |> let name = find_id(&data, 22).unwrap();
|
||||||
|> ---- immutable borrow begins here
|
|> ---- immutable borrow begins here
|
||||||
...
|
...
|
||||||
|
@ -288,6 +293,7 @@ fn foo() {
|
||||||
println!("text=r#\"\n{}\".trim_left()", text);
|
println!("text=r#\"\n{}\".trim_left()", text);
|
||||||
assert_eq!(&text[..], &r#"
|
assert_eq!(&text[..], &r#"
|
||||||
::: foo.rs
|
::: foo.rs
|
||||||
|
|>
|
||||||
3 |> vec.push(vec.pop().unwrap());
|
3 |> vec.push(vec.pop().unwrap());
|
||||||
|> -------- ------ D
|
|> -------- ------ D
|
||||||
|> ||
|
|> ||
|
||||||
|
@ -324,6 +330,7 @@ fn foo() {
|
||||||
println!("text=r#\"\n{}\".trim_left()", text);
|
println!("text=r#\"\n{}\".trim_left()", text);
|
||||||
assert_eq!(&text[..], &r#"
|
assert_eq!(&text[..], &r#"
|
||||||
::: foo.rs
|
::: foo.rs
|
||||||
|
|>
|
||||||
3 |> vec.push(vec.pop().unwrap());
|
3 |> vec.push(vec.pop().unwrap());
|
||||||
|> --- --- - previous borrow ends here
|
|> --- --- - previous borrow ends here
|
||||||
|> | |
|
|> | |
|
||||||
|
@ -362,6 +369,7 @@ fn foo() {
|
||||||
println!("text=r#\"\n{}\".trim_left()", text);
|
println!("text=r#\"\n{}\".trim_left()", text);
|
||||||
assert_eq!(&text[..], &r#"
|
assert_eq!(&text[..], &r#"
|
||||||
::: foo.rs
|
::: foo.rs
|
||||||
|
|>
|
||||||
4 |> let mut vec2 = vec;
|
4 |> let mut vec2 = vec;
|
||||||
|> --- `vec` moved here because it has type `collections::vec::Vec<i32>`
|
|> --- `vec` moved here because it has type `collections::vec::Vec<i32>`
|
||||||
...
|
...
|
||||||
|
@ -398,6 +406,7 @@ fn foo() {
|
||||||
println!("text=&r#\"\n{}\n\"#[1..]", text);
|
println!("text=&r#\"\n{}\n\"#[1..]", text);
|
||||||
assert_eq!(text, &r#"
|
assert_eq!(text, &r#"
|
||||||
::: foo.rs
|
::: foo.rs
|
||||||
|
|>
|
||||||
3 |> let mut vec = vec![0, 1, 2];
|
3 |> let mut vec = vec![0, 1, 2];
|
||||||
|> --- ---
|
|> --- ---
|
||||||
4 |> let mut vec2 = vec;
|
4 |> let mut vec2 = vec;
|
||||||
|
@ -429,6 +438,7 @@ impl SomeTrait for () {
|
||||||
println!("r#\"\n{}\"", text);
|
println!("r#\"\n{}\"", text);
|
||||||
assert_eq!(text, &r#"
|
assert_eq!(text, &r#"
|
||||||
::: foo.rs
|
::: foo.rs
|
||||||
|
|>
|
||||||
3 |> fn foo(x: u32) {
|
3 |> fn foo(x: u32) {
|
||||||
|> -
|
|> -
|
||||||
"#[1..]);
|
"#[1..]);
|
||||||
|
@ -458,6 +468,7 @@ fn span_overlap_label() {
|
||||||
println!("r#\"\n{}\"", text);
|
println!("r#\"\n{}\"", text);
|
||||||
assert_eq!(text, &r#"
|
assert_eq!(text, &r#"
|
||||||
::: foo.rs
|
::: foo.rs
|
||||||
|
|>
|
||||||
2 |> fn foo(x: u32) {
|
2 |> fn foo(x: u32) {
|
||||||
|> --------------
|
|> --------------
|
||||||
|> | |
|
|> | |
|
||||||
|
@ -492,6 +503,7 @@ fn span_overlap_label2() {
|
||||||
println!("r#\"\n{}\"", text);
|
println!("r#\"\n{}\"", text);
|
||||||
assert_eq!(text, &r#"
|
assert_eq!(text, &r#"
|
||||||
::: foo.rs
|
::: foo.rs
|
||||||
|
|>
|
||||||
2 |> fn foo(x: u32) {
|
2 |> fn foo(x: u32) {
|
||||||
|> --------------
|
|> --------------
|
||||||
|> | |
|
|> | |
|
||||||
|
@ -537,6 +549,7 @@ fn span_overlap_label3() {
|
||||||
println!("r#\"\n{}\"", text);
|
println!("r#\"\n{}\"", text);
|
||||||
assert_eq!(text, &r#"
|
assert_eq!(text, &r#"
|
||||||
::: foo.rs
|
::: foo.rs
|
||||||
|
|>
|
||||||
3 |> let closure = || {
|
3 |> let closure = || {
|
||||||
|> - foo
|
|> - foo
|
||||||
4 |> inner
|
4 |> inner
|
||||||
|
@ -577,6 +590,7 @@ fn main() {
|
||||||
println!("r#\"\n{}\"", text);
|
println!("r#\"\n{}\"", text);
|
||||||
assert_eq!(text, &r#"
|
assert_eq!(text, &r#"
|
||||||
--> foo.rs:11:2
|
--> foo.rs:11:2
|
||||||
|
|>
|
||||||
11 |> }
|
11 |> }
|
||||||
|> -
|
|> -
|
||||||
"#[1..]);
|
"#[1..]);
|
||||||
|
|
|
@ -233,12 +233,9 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) {
|
||||||
visitor.visit_path(path, item.id);
|
visitor.visit_path(path, item.id);
|
||||||
}
|
}
|
||||||
ViewPathList(ref prefix, ref list) => {
|
ViewPathList(ref prefix, ref list) => {
|
||||||
if !list.is_empty() {
|
visitor.visit_path(prefix, item.id);
|
||||||
for item in list {
|
for item in list {
|
||||||
visitor.visit_path_list_item(prefix, item)
|
visitor.visit_path_list_item(prefix, item)
|
||||||
}
|
|
||||||
} else {
|
|
||||||
visitor.visit_path(prefix, item.id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -368,12 +365,8 @@ pub fn walk_path<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v Path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn walk_path_list_item<'v, V: Visitor<'v>>(visitor: &mut V, prefix: &'v Path,
|
pub fn walk_path_list_item<'v, V: Visitor<'v>>(visitor: &mut V, _prefix: &'v Path,
|
||||||
item: &'v PathListItem) {
|
item: &'v PathListItem) {
|
||||||
for segment in &prefix.segments {
|
|
||||||
visitor.visit_path_segment(prefix.span, segment);
|
|
||||||
}
|
|
||||||
|
|
||||||
walk_opt_ident(visitor, item.span, item.node.name());
|
walk_opt_ident(visitor, item.span, item.node.name());
|
||||||
walk_opt_ident(visitor, item.span, item.node.rename());
|
walk_opt_ident(visitor, item.span, item.node.rename());
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,5 +19,4 @@ fn main() {
|
||||||
let foo = Foo;
|
let foo = Foo;
|
||||||
let ref_foo = &&Foo;
|
let ref_foo = &&Foo;
|
||||||
ref_foo.foo(); //~ ERROR E0055
|
ref_foo.foo(); //~ ERROR E0055
|
||||||
//~^ ERROR E0275
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@ fn assign_field1<'a>(x: Own<Point>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assign_field2<'a>(x: &'a Own<Point>) {
|
fn assign_field2<'a>(x: &'a Own<Point>) {
|
||||||
x.y = 3; //~ ERROR cannot assign
|
x.y = 3; //~ ERROR cannot borrow
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assign_field3<'a>(x: &'a mut Own<Point>) {
|
fn assign_field3<'a>(x: &'a mut Own<Point>) {
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut v = Vec::new();
|
||||||
|
foo(&mut v);
|
||||||
|
//~^ ERROR mismatched types
|
||||||
|
//~| expected struct `std::collections::HashSet`, found struct `std::vec::Vec`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo(h: &mut HashSet<u32>) {
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
// 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.
|
||||||
|
fn main() {
|
||||||
|
let mut op = Some(2);
|
||||||
|
match op {
|
||||||
|
Some(ref v) => { let a = &mut v; },
|
||||||
|
//~^ ERROR:cannot borrow immutable
|
||||||
|
//~| use `ref mut v` here to make mutable
|
||||||
|
None => {},
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,7 +34,8 @@ impl<'a, 't> Foo<'a, 't> for &'a isize {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) {
|
fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) {
|
||||||
//~^ ERROR method `wrong_bound1` has an incompatible type for trait
|
//~^ ERROR method not compatible with trait
|
||||||
|
//~^^ ERROR method not compatible with trait
|
||||||
//
|
//
|
||||||
// Note: This is a terrible error message. It is caused
|
// Note: This is a terrible error message. It is caused
|
||||||
// because, in the trait, 'b is early bound, and in the impl,
|
// because, in the trait, 'b is early bound, and in the impl,
|
||||||
|
|
|
@ -23,7 +23,7 @@ impl<'a> get_ctxt for has_ctxt<'a> {
|
||||||
|
|
||||||
// Here an error occurs because we used `&self` but
|
// Here an error occurs because we used `&self` but
|
||||||
// the definition used `&`:
|
// the definition used `&`:
|
||||||
fn get_ctxt(&self) -> &'a ctxt { //~ ERROR method `get_ctxt` has an incompatible type
|
fn get_ctxt(&self) -> &'a ctxt { //~ ERROR method not compatible with trait
|
||||||
self.c
|
self.c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
#![feature(rand, core)]
|
#![feature(rand)]
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
|
@ -18,6 +18,11 @@ use std::process::Command;
|
||||||
use std::__rand::{thread_rng, Rng};
|
use std::__rand::{thread_rng, Rng};
|
||||||
use std::{char, env};
|
use std::{char, env};
|
||||||
|
|
||||||
|
pub fn check_old_skool() -> bool {
|
||||||
|
use std::env;
|
||||||
|
env::var("RUST_NEW_ERROR_FORMAT").is_err()
|
||||||
|
}
|
||||||
|
|
||||||
// creates a file with `fn main() { <random ident> }` and checks the
|
// creates a file with `fn main() { <random ident> }` and checks the
|
||||||
// compiler emits a span of the appropriate length (for the
|
// compiler emits a span of the appropriate length (for the
|
||||||
// "unresolved name" message); currently just using the number of code
|
// "unresolved name" message); currently just using the number of code
|
||||||
|
@ -65,10 +70,17 @@ fn main() {
|
||||||
|
|
||||||
let err = String::from_utf8_lossy(&result.stderr);
|
let err = String::from_utf8_lossy(&result.stderr);
|
||||||
|
|
||||||
// the span should end the line (e.g no extra ~'s)
|
if check_old_skool() {
|
||||||
let expected_span = format!("^{}\n", repeat("~").take(n - 1)
|
// the span should end the line (e.g no extra ~'s)
|
||||||
.collect::<String>());
|
let expected_span = format!("^{}\n", repeat("~").take(n - 1)
|
||||||
assert!(err.contains(&expected_span));
|
.collect::<String>());
|
||||||
|
assert!(err.contains(&expected_span));
|
||||||
|
} else {
|
||||||
|
// the span should end the line (e.g no extra ~'s)
|
||||||
|
let expected_span = format!("^{}\n", repeat("^").take(n - 1)
|
||||||
|
.collect::<String>());
|
||||||
|
assert!(err.contains(&expected_span));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test multi-column characters and tabs
|
// Test multi-column characters and tabs
|
||||||
|
@ -77,9 +89,6 @@ fn main() {
|
||||||
r#"extern "路濫狼á́́" fn foo() {{}} extern "路濫狼á́" fn bar() {{}}"#);
|
r#"extern "路濫狼á́́" fn foo() {{}} extern "路濫狼á́" fn bar() {{}}"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extra characters. Every line is preceded by `filename:lineno <actual code>`
|
|
||||||
let offset = main_file.to_str().unwrap().len() + 3;
|
|
||||||
|
|
||||||
let result = Command::new("sh")
|
let result = Command::new("sh")
|
||||||
.arg("-c")
|
.arg("-c")
|
||||||
.arg(format!("{} {}",
|
.arg(format!("{} {}",
|
||||||
|
@ -91,17 +100,31 @@ fn main() {
|
||||||
|
|
||||||
// Test both the length of the snake and the leading spaces up to it
|
// Test both the length of the snake and the leading spaces up to it
|
||||||
|
|
||||||
// First snake is 8 ~s long, with 7 preceding spaces (excluding file name/line offset)
|
if check_old_skool() {
|
||||||
let expected_span = format!("\n{}^{}\n",
|
// Extra characters. Every line is preceded by `filename:lineno <actual code>`
|
||||||
repeat(" ").take(offset + 7).collect::<String>(),
|
let offset = main_file.to_str().unwrap().len() + 3;
|
||||||
repeat("~").take(8).collect::<String>());
|
|
||||||
assert!(err.contains(&expected_span));
|
// First snake is 8 ~s long, with 7 preceding spaces (excluding file name/line offset)
|
||||||
// Second snake is only 7 ~s long, with 36 preceding spaces,
|
let expected_span = format!("\n{}^{}\n",
|
||||||
// because rustc counts chars() now rather than width(). This
|
repeat(" ").take(offset + 7).collect::<String>(),
|
||||||
// is because width() functions are to be removed from
|
repeat("~").take(8).collect::<String>());
|
||||||
// librustc_unicode
|
assert!(err.contains(&expected_span));
|
||||||
let expected_span = format!("\n{}^{}\n",
|
// Second snake is only 7 ~s long, with 36 preceding spaces,
|
||||||
repeat(" ").take(offset + 36).collect::<String>(),
|
// because rustc counts chars() now rather than width(). This
|
||||||
repeat("~").take(7).collect::<String>());
|
// is because width() functions are to be removed from
|
||||||
assert!(err.contains(&expected_span));
|
// librustc_unicode
|
||||||
|
let expected_span = format!("\n{}^{}\n",
|
||||||
|
repeat(" ").take(offset + 36).collect::<String>(),
|
||||||
|
repeat("~").take(7).collect::<String>());
|
||||||
|
assert!(err.contains(&expected_span));
|
||||||
|
} else {
|
||||||
|
let expected_span = format!("\n |>{}{}\n",
|
||||||
|
repeat(" ").take(8).collect::<String>(),
|
||||||
|
repeat("^").take(9).collect::<String>());
|
||||||
|
assert!(err.contains(&expected_span));
|
||||||
|
let expected_span = format!("\n |>{}{}\n",
|
||||||
|
repeat(" ").take(37).collect::<String>(),
|
||||||
|
repeat("^").take(8).collect::<String>());
|
||||||
|
assert!(err.contains(&expected_span));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
|
// rustc-env:RUST_NEW_ERROR_FORMAT
|
||||||
extern {
|
extern {
|
||||||
fn write(fildes: i32, buf: *const i8, nbyte: u64) -> i64;
|
fn write(fildes: i32, buf: *const i8, nbyte: u64) -> i64;
|
||||||
}
|
}
|
||||||
|
@ -24,25 +25,16 @@ macro_rules! write {
|
||||||
unsafe {
|
unsafe {
|
||||||
write(stdout, $arr.as_ptr() as *const i8,
|
write(stdout, $arr.as_ptr() as *const i8,
|
||||||
$arr.len() * size_of($arr[0]));
|
$arr.len() * size_of($arr[0]));
|
||||||
//~^ ERROR mismatched types
|
|
||||||
//~| expected u64, found usize
|
|
||||||
//~| expected type
|
|
||||||
//~| found type
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! cast {
|
macro_rules! cast {
|
||||||
($x:expr) => ($x as ()) //~ ERROR non-scalar cast
|
($x:expr) => ($x as ())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let hello = ['H', 'e', 'y'];
|
let hello = ['H', 'e', 'y'];
|
||||||
write!(hello);
|
write!(hello);
|
||||||
//~^ NOTE in this expansion of write!
|
|
||||||
//~| NOTE in this expansion of write!
|
|
||||||
//~| NOTE in this expansion of write!
|
|
||||||
|
|
||||||
cast!(2);
|
cast!(2);
|
||||||
//~^ NOTE in this expansion of cast!
|
|
||||||
}
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
error: mismatched types [--explain E0308]
|
||||||
|
--> $DIR/issue-26480.rs:27:19
|
||||||
|
|>
|
||||||
|
27 |> $arr.len() * size_of($arr[0]));
|
||||||
|
|> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected u64, found usize
|
||||||
|
$DIR/issue-26480.rs:38:5: 38:19: note: in this expansion of write! (defined in $DIR/issue-26480.rs)
|
||||||
|
|
||||||
|
error: non-scalar cast: `_` as `()`
|
||||||
|
--> $DIR/issue-26480.rs:33:19
|
||||||
|
|>
|
||||||
|
33 |> ($x:expr) => ($x as ())
|
||||||
|
|> ^^^^^^^^
|
||||||
|
$DIR/issue-26480.rs:39:5: 39:14: note: in this expansion of cast! (defined in $DIR/issue-26480.rs)
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
|
@ -1,5 +1,6 @@
|
||||||
error: mismatched types [--explain E0308]
|
error: mismatched types [--explain E0308]
|
||||||
--> $DIR/main.rs:14:18
|
--> $DIR/main.rs:14:18
|
||||||
|
|>
|
||||||
14 |> let x: u32 = (
|
14 |> let x: u32 = (
|
||||||
|> ^ expected u32, found ()
|
|> ^ expected u32, found ()
|
||||||
note: expected type `u32`
|
note: expected type `u32`
|
||||||
|
|
Loading…
Reference in New Issue