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,6 +80,34 @@ mod imp {
|
||||
if align <= MIN_ALIGN {
|
||||
libc::malloc(size as libc::size_t) as *mut u8
|
||||
} else {
|
||||
aligned_malloc(size, align)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
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 {
|
||||
@ -88,7 +116,6 @@ mod imp {
|
||||
out as *mut u8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usize) -> *mut u8 {
|
||||
if align <= MIN_ALIGN {
|
||||
|
@ -411,14 +411,17 @@ pub struct EscapeUnicode {
|
||||
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)]
|
||||
enum EscapeUnicodeState {
|
||||
Backslash,
|
||||
Type,
|
||||
LeftBrace,
|
||||
Value,
|
||||
RightBrace,
|
||||
Done,
|
||||
RightBrace,
|
||||
Value,
|
||||
LeftBrace,
|
||||
Type,
|
||||
Backslash,
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
@ -457,19 +460,17 @@ impl Iterator for EscapeUnicode {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let n = match self.state {
|
||||
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;
|
||||
let n = self.len();
|
||||
(n, Some(n))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn count(self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
|
||||
fn last(self) -> Option<char> {
|
||||
match self.state {
|
||||
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`.
|
||||
///
|
||||
/// This `struct` is created by the [`escape_default()`] method on [`char`]. See
|
||||
@ -498,9 +515,9 @@ pub struct EscapeDefault {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum EscapeDefaultState {
|
||||
Backslash(char),
|
||||
Char(char),
|
||||
Done,
|
||||
Char(char),
|
||||
Backslash(char),
|
||||
Unicode(EscapeUnicode),
|
||||
}
|
||||
|
||||
@ -523,22 +540,15 @@ impl Iterator for EscapeDefault {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
match self.state {
|
||||
EscapeDefaultState::Char(_) => (1, Some(1)),
|
||||
EscapeDefaultState::Backslash(_) => (2, Some(2)),
|
||||
EscapeDefaultState::Unicode(ref iter) => iter.size_hint(),
|
||||
EscapeDefaultState::Done => (0, Some(0)),
|
||||
}
|
||||
let n = self.len();
|
||||
(n, Some(n))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn count(self) -> usize {
|
||||
match self.state {
|
||||
EscapeDefaultState::Char(_) => 1,
|
||||
EscapeDefaultState::Unicode(iter) => iter.count(),
|
||||
EscapeDefaultState::Done => 0,
|
||||
EscapeDefaultState::Backslash(_) => 2,
|
||||
}
|
||||
self.len()
|
||||
}
|
||||
|
||||
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`
|
||||
/// value.
|
||||
///
|
||||
|
@ -276,6 +276,12 @@ fn eu_iterator_specializations() {
|
||||
// Check last
|
||||
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
|
||||
assert_eq!(iter.clone().count(), len - offset);
|
||||
|
||||
|
@ -280,13 +280,10 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) {
|
||||
visitor.visit_path(path, item.id);
|
||||
}
|
||||
ViewPathList(ref prefix, ref list) => {
|
||||
if !list.is_empty() {
|
||||
visitor.visit_path(prefix, item.id);
|
||||
for item in list {
|
||||
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,
|
||||
prefix: &'v Path,
|
||||
_prefix: &'v Path,
|
||||
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.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
|
||||
// `tained_by_errors`) to avoid reporting certain kinds of errors.
|
||||
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
|
||||
@ -476,7 +481,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'gcx> {
|
||||
normalize: false,
|
||||
projection_mode: ProjectionMode::AnyFinal,
|
||||
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,
|
||||
projection_mode: projection_mode,
|
||||
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>,
|
||||
float_snapshot: unify::Snapshot<ty::FloatVid>,
|
||||
region_vars_snapshot: RegionSnapshot,
|
||||
obligations_in_snapshot: bool,
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
let obligations_in_snapshot = self.obligations_in_snapshot.get();
|
||||
self.obligations_in_snapshot.set(false);
|
||||
|
||||
CombinedSnapshot {
|
||||
type_snapshot: self.type_variables.borrow_mut().snapshot(),
|
||||
int_snapshot: self.int_unification_table.borrow_mut().snapshot(),
|
||||
float_snapshot: self.float_unification_table.borrow_mut().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,
|
||||
int_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
|
||||
.borrow_mut()
|
||||
@ -842,7 +858,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
let CombinedSnapshot { type_snapshot,
|
||||
int_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
|
||||
.borrow_mut()
|
||||
@ -904,12 +923,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
let CombinedSnapshot { type_snapshot,
|
||||
int_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());
|
||||
|
||||
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
|
||||
// inside `f`, with, e.g. `resolve_type_vars_if_possible`.
|
||||
self.type_variables
|
||||
|
@ -171,6 +171,8 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
|
||||
// debug output much nicer to read and so on.
|
||||
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)
|
||||
{
|
||||
return
|
||||
|
@ -31,7 +31,7 @@ pub use self::coherence::overlapping_impls;
|
||||
pub use self::coherence::OrphanCheckErr;
|
||||
pub use self::fulfill::{FulfillmentContext, GlobalFulfilledPredicates, RegionObligation};
|
||||
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::MethodViolationCode;
|
||||
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={:?})",
|
||||
obligation);
|
||||
|
||||
let Normalized { value: normalized_ty, obligations } =
|
||||
let Normalized { value: normalized_ty, mut obligations } =
|
||||
match opt_normalize_projection_type(selcx,
|
||||
obligation.predicate.projection_ty.clone(),
|
||||
obligation.cause.clone(),
|
||||
@ -224,8 +224,9 @@ fn project_and_unify_type<'cx, 'gcx, 'tcx>(
|
||||
let origin = TypeOrigin::RelateOutputImplTypes(obligation.cause.span);
|
||||
match infcx.eq_types(true, origin, normalized_ty, obligation.predicate.ty) {
|
||||
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());
|
||||
obligations.extend(inferred_obligations);
|
||||
Ok(Some(obligations))
|
||||
},
|
||||
Err(err) => Err(MismatchedProjectionTypes { err: err }),
|
||||
@ -710,7 +711,8 @@ fn assemble_candidates_from_predicates<'cx, 'gcx, 'tcx, I>(
|
||||
origin,
|
||||
data_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()))
|
||||
.is_ok()
|
||||
});
|
||||
@ -1047,8 +1049,8 @@ fn confirm_fn_pointer_candidate<'cx, 'gcx, 'tcx>(
|
||||
fn_pointer_vtable: VtableFnPointerData<'tcx, PredicateObligation<'tcx>>)
|
||||
-> (Ty<'tcx>, Vec<PredicateObligation<'tcx>>)
|
||||
{
|
||||
// FIXME(#32730) propagate obligations (fn pointer vtable nested obligations ONLY come from
|
||||
// unification in inference)
|
||||
// FIXME(#32730) drop this assertion once obligations are propagated from inference (fn pointer
|
||||
// vtable nested obligations ONLY come from unification in inference)
|
||||
assert!(fn_pointer_vtable.nested.is_empty());
|
||||
let fn_type = selcx.infcx().shallow_resolve(fn_pointer_vtable.fn_ty);
|
||||
let sig = fn_type.fn_sig();
|
||||
@ -1130,13 +1132,14 @@ fn confirm_param_env_candidate<'cx, 'gcx, 'tcx>(
|
||||
obligation.predicate.item_name);
|
||||
|
||||
let origin = TypeOrigin::RelateOutputImplTypes(obligation.cause.span);
|
||||
match infcx.eq_trait_refs(false,
|
||||
let obligations = match infcx.eq_trait_refs(false,
|
||||
origin,
|
||||
obligation.predicate.trait_ref.clone(),
|
||||
projection.projection_ty.trait_ref.clone()) {
|
||||
Ok(InferOk { obligations, .. }) => {
|
||||
// FIXME(#32730) propagate obligations
|
||||
// FIXME(#32730) once obligations are generated in inference, remove this assertion
|
||||
assert!(obligations.is_empty());
|
||||
obligations
|
||||
}
|
||||
Err(e) => {
|
||||
span_bug!(
|
||||
@ -1146,9 +1149,9 @@ fn confirm_param_env_candidate<'cx, 'gcx, 'tcx>(
|
||||
projection,
|
||||
e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(projection.ty, vec!())
|
||||
(projection.ty, obligations)
|
||||
}
|
||||
|
||||
fn confirm_impl_candidate<'cx, 'gcx, 'tcx>(
|
||||
|
@ -187,7 +187,6 @@ fn fulfill_implication<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
|
||||
source_trait_ref: ty::TraitRef<'tcx>,
|
||||
target_impl: DefId)
|
||||
-> Result<&'tcx Substs<'tcx>, ()> {
|
||||
infcx.commit_if_ok(|_| {
|
||||
let selcx = &mut SelectionContext::new(&infcx);
|
||||
let target_substs = fresh_type_vars_for_impl(&infcx, DUMMY_SP, target_impl);
|
||||
let (target_trait_ref, obligations) = impl_trait_ref_and_oblig(selcx,
|
||||
@ -231,7 +230,6 @@ fn fulfill_implication<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
|
||||
// the inference variables inside with whatever we got from fulfillment.
|
||||
Ok(infcx.resolve_type_vars_if_possible(&target_substs))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub struct SpecializesCache {
|
||||
|
@ -235,8 +235,9 @@ impl<'a, 'gcx, 'tcx> ty::TyS<'tcx> {
|
||||
None => {
|
||||
span_bug!(
|
||||
expr_span,
|
||||
"the {}th autoderef failed: {}",
|
||||
"the {}th autoderef for {} failed: {}",
|
||||
autoderef,
|
||||
expr_id,
|
||||
adjusted_ty);
|
||||
}
|
||||
}
|
||||
|
@ -977,7 +977,11 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
|
||||
if let Categorization::Local(local_id) = err.cmt.cat {
|
||||
let span = self.tcx.map.span(local_id);
|
||||
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,
|
||||
&format!("use `mut {}` here to make mutable", snippet));
|
||||
}
|
||||
|
@ -401,10 +401,6 @@ mod svh_visitor {
|
||||
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) {
|
||||
SawBlock.hash(self.st); visit::walk_block(self, b)
|
||||
}
|
||||
|
210
src/librustc_typeck/check/autoderef.rs
Normal file
210
src/librustc_typeck/check/autoderef.rs
Normal file
@ -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.
|
||||
|
||||
use super::{DeferredCallResolution, Expectation, FnCtxt,
|
||||
TupleArgumentsFlag, UnresolvedTypeAction};
|
||||
TupleArgumentsFlag};
|
||||
|
||||
use CrateCtxt;
|
||||
use middle::cstore::LOCAL_CRATE;
|
||||
@ -72,15 +72,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
{
|
||||
self.check_expr(callee_expr);
|
||||
let original_callee_ty = self.expr_ty(callee_expr);
|
||||
let (callee_ty, _, result) =
|
||||
self.autoderef(callee_expr.span,
|
||||
original_callee_ty,
|
||||
|| Some(callee_expr),
|
||||
UnresolvedTypeAction::Error,
|
||||
LvaluePreference::NoPreference,
|
||||
|adj_ty, idx| {
|
||||
|
||||
let mut autoderef = self.autoderef(callee_expr.span, original_callee_ty);
|
||||
let result = autoderef.by_ref().flat_map(|(adj_ty, idx)| {
|
||||
self.try_overloaded_call_step(call_expr, callee_expr, adj_ty, idx)
|
||||
});
|
||||
}).next();
|
||||
let callee_ty = autoderef.unambiguous_final_ty();
|
||||
autoderef.finalize(LvaluePreference::NoPreference, Some(callee_expr));
|
||||
|
||||
match result {
|
||||
None => {
|
||||
|
@ -60,7 +60,7 @@
|
||||
//! 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.
|
||||
|
||||
use check::{FnCtxt, UnresolvedTypeAction};
|
||||
use check::{FnCtxt};
|
||||
|
||||
use rustc::hir;
|
||||
use rustc::infer::{Coercion, InferOk, TypeOrigin, TypeTrace};
|
||||
@ -220,7 +220,8 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
|
||||
-> CoerceResult<'tcx>
|
||||
// FIXME(eddyb) use copyable iterators when that becomes ergonomic.
|
||||
where E: Fn() -> I,
|
||||
I: IntoIterator<Item=&'a hir::Expr> {
|
||||
I: IntoIterator<Item=&'a hir::Expr>
|
||||
{
|
||||
|
||||
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 lvalue_pref = LvaluePreference::from_mutbl(mt_b.mutbl);
|
||||
let mut first_error = None;
|
||||
let mut r_borrow_var = None;
|
||||
let (_, autoderefs, success) = self.autoderef(span, a, exprs,
|
||||
UnresolvedTypeAction::Ignore,
|
||||
lvalue_pref,
|
||||
|referent_ty, autoderef|
|
||||
{
|
||||
if autoderef == 0 {
|
||||
let mut autoderef = self.autoderef(span, a);
|
||||
let mut success = None;
|
||||
|
||||
for (referent_ty, autoderefs) in autoderef.by_ref() {
|
||||
if autoderefs == 0 {
|
||||
// Don't let this pass, otherwise it would cause
|
||||
// &T to autoref to &&T.
|
||||
return None;
|
||||
continue
|
||||
}
|
||||
|
||||
// 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.
|
||||
let r = if !self.use_lub {
|
||||
r_b // [2] above
|
||||
} else if autoderef == 1 {
|
||||
} else if autoderefs == 1 {
|
||||
r_a // [3] above
|
||||
} else {
|
||||
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
|
||||
});
|
||||
match self.unify(derefd_ty_a, b) {
|
||||
Ok(ty) => Some(ty),
|
||||
Ok(ty) => { success = Some((ty, autoderefs)); break },
|
||||
Err(err) => {
|
||||
if first_error.is_none() {
|
||||
first_error = Some(err);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Extract type or return an error. We return the first error
|
||||
// we got, which should be from relating the "base" type
|
||||
// (e.g., in example above, the failure from relating `Vec<T>`
|
||||
// to the target type), since that should be the least
|
||||
// confusing.
|
||||
let ty = match success {
|
||||
Some(ty) => ty,
|
||||
let (ty, autoderefs) = match success {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
let err = first_error.expect("coerce_borrowed_pointer had no error");
|
||||
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
|
||||
// the final ref type we got.
|
||||
if ty == a && mt_a.mutbl == hir::MutImmutable && autoderefs == 1 {
|
||||
|
@ -279,12 +279,6 @@ pub fn compare_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
|
||||
// type.
|
||||
|
||||
// Compute skolemized form of impl and trait method tys.
|
||||
let impl_fty = tcx.mk_fn_ptr(impl_m.fty);
|
||||
let impl_fty = impl_fty.subst(tcx, impl_to_skol_substs);
|
||||
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 tcx = infcx.tcx;
|
||||
let origin = TypeOrigin::MethodCompatCheck(impl_m_span);
|
||||
|
||||
@ -305,11 +299,11 @@ pub fn compare_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
|
||||
abi: impl_m.fty.abi,
|
||||
sig: ty::Binder(impl_sig)
|
||||
}));
|
||||
debug!("compare_impl_method: impl_fty={:?}",
|
||||
impl_fty);
|
||||
debug!("compare_impl_method: impl_fty={:?}", impl_fty);
|
||||
|
||||
let (trait_sig, skol_map) =
|
||||
infcx.skolemize_late_bound_regions(&trait_m.fty.sig, snapshot);
|
||||
let trait_sig = tcx.liberate_late_bound_regions(
|
||||
infcx.parameter_environment.free_id_outlive,
|
||||
&trait_m.fty.sig);
|
||||
let trait_sig =
|
||||
trait_sig.subst(tcx, &trait_to_skol_substs);
|
||||
let trait_sig =
|
||||
@ -324,33 +318,24 @@ pub fn compare_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
|
||||
sig: ty::Binder(trait_sig)
|
||||
}));
|
||||
|
||||
debug!("compare_impl_method: trait_fty={:?}",
|
||||
trait_fty);
|
||||
debug!("compare_impl_method: trait_fty={:?}", trait_fty);
|
||||
|
||||
infcx.sub_types(false, origin, impl_fty, trait_fty)?;
|
||||
|
||||
infcx.leak_check(false, &skol_map, snapshot)
|
||||
});
|
||||
|
||||
match err {
|
||||
Ok(()) => { }
|
||||
Err(terr) => {
|
||||
debug!("checking trait method for compatibility: impl ty {:?}, trait ty {:?}",
|
||||
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);
|
||||
span_err!(tcx.sess, impl_m_span, E0053,
|
||||
"method `{}` has an incompatible type for trait: {}",
|
||||
trait_m.name,
|
||||
terr);
|
||||
return;
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Check that all obligations are satisfied by the implementation's
|
||||
// version.
|
||||
match fulfillment_cx.select_all_or_error(&infcx) {
|
||||
Err(ref errors) => { infcx.report_fulfillment_errors(errors) }
|
||||
Ok(_) => {}
|
||||
if let Err(ref errors) = fulfillment_cx.select_all_or_error(&infcx) {
|
||||
infcx.report_fulfillment_errors(errors);
|
||||
return
|
||||
}
|
||||
|
||||
// Finally, resolve all regions. This catches wily misuses of
|
||||
|
@ -11,11 +11,10 @@
|
||||
use super::probe;
|
||||
|
||||
use check::{FnCtxt, callee};
|
||||
use check::UnresolvedTypeAction;
|
||||
use hir::def_id::DefId;
|
||||
use rustc::ty::subst::{self};
|
||||
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::fold::TypeFoldable;
|
||||
use rustc::infer::{self, InferOk, TypeOrigin};
|
||||
@ -133,10 +132,10 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
||||
ty: fty,
|
||||
substs: all_substs
|
||||
};
|
||||
// If this is an `&mut self` method, bias the receiver
|
||||
// expression towards mutability (this will switch
|
||||
// e.g. `Deref` to `DerefMut` in overloaded derefs and so on).
|
||||
self.fixup_derefs_on_method_receiver_if_necessary(&callee);
|
||||
|
||||
if let Some(hir::MutMutable) = pick.autoref {
|
||||
self.convert_lvalue_derefs_to_mutable();
|
||||
}
|
||||
|
||||
callee
|
||||
}
|
||||
@ -164,22 +163,14 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
||||
(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.
|
||||
let (autoderefd_ty, n, result) = self.autoderef(self.span,
|
||||
unadjusted_self_ty,
|
||||
|| Some(self.self_expr),
|
||||
UnresolvedTypeAction::Error,
|
||||
NoPreference,
|
||||
|_, n| {
|
||||
if n == pick.autoderefs {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let mut autoderef = self.autoderef(self.span, unadjusted_self_ty);
|
||||
let (autoderefd_ty, n) = autoderef.nth(pick.autoderefs).unwrap();
|
||||
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.
|
||||
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>`
|
||||
// etc).
|
||||
|
||||
let (_, _, result) = self.fcx.autoderef(self.span,
|
||||
self_ty,
|
||||
|| None,
|
||||
UnresolvedTypeAction::Error,
|
||||
NoPreference,
|
||||
|ty, _| {
|
||||
// FIXME: this feels, like, super dubious
|
||||
self.fcx.autoderef(self.span, self_ty)
|
||||
.filter_map(|(ty, _)| {
|
||||
match ty.sty {
|
||||
ty::TyTrait(ref data) => Some(closure(self, ty, &data)),
|
||||
_ => None,
|
||||
}
|
||||
});
|
||||
|
||||
match result {
|
||||
Some(r) => r,
|
||||
None => {
|
||||
})
|
||||
.next()
|
||||
.unwrap_or_else(|| {
|
||||
span_bug!(
|
||||
self.span,
|
||||
"self-type `{}` for ObjectPick never dereferenced to an object",
|
||||
self_ty)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn instantiate_method_substs(&mut self,
|
||||
@ -463,24 +448,10 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// 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`
|
||||
/// respectively.
|
||||
fn fixup_derefs_on_method_receiver_if_necessary(&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,
|
||||
}
|
||||
|
||||
fn convert_lvalue_derefs_to_mutable(&self) {
|
||||
// Gather up expressions we want to munge.
|
||||
let mut exprs = Vec::new();
|
||||
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={:?}",
|
||||
exprs);
|
||||
debug!("convert_lvalue_derefs_to_mutable: exprs={:?}", exprs);
|
||||
|
||||
// Fix up autoderefs and derefs.
|
||||
for (i, &expr) in exprs.iter().rev().enumerate() {
|
||||
@ -509,23 +479,17 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
||||
Some(_) | None => 0,
|
||||
};
|
||||
|
||||
debug!("fixup_derefs_on_method_receiver_if_necessary: i={} expr={:?} \
|
||||
debug!("convert_lvalue_derefs_to_mutable: i={} expr={:?} \
|
||||
autoderef_count={}",
|
||||
i, expr, autoderef_count);
|
||||
|
||||
if autoderef_count > 0 {
|
||||
self.autoderef(expr.span,
|
||||
self.expr_ty(expr),
|
||||
|| Some(expr),
|
||||
UnresolvedTypeAction::Error,
|
||||
PreferMutLvalue,
|
||||
|_, autoderefs| {
|
||||
if autoderefs == autoderef_count + 1 {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
let mut autoderef = self.autoderef(expr.span, self.expr_ty(expr));
|
||||
autoderef.nth(autoderef_count).unwrap_or_else(|| {
|
||||
span_bug!(expr.span, "expr was deref-able {} times but now isn't?",
|
||||
autoderef_count);
|
||||
});
|
||||
autoderef.finalize(PreferMutLvalue, Some(expr));
|
||||
}
|
||||
|
||||
// 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::suggest;
|
||||
|
||||
use check::{FnCtxt, UnresolvedTypeAction};
|
||||
use check::{FnCtxt};
|
||||
use hir::def_id::DefId;
|
||||
use hir::def::Def;
|
||||
use rustc::ty::subst;
|
||||
use rustc::ty::subst::Subst;
|
||||
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 syntax::ast;
|
||||
use syntax::codemap::{Span, DUMMY_SP};
|
||||
@ -208,25 +208,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
fn create_steps(&self,
|
||||
span: Span,
|
||||
self_ty: Ty<'tcx>)
|
||||
-> Option<Vec<CandidateStep<'tcx>>> {
|
||||
let mut steps = Vec::new();
|
||||
-> Option<Vec<CandidateStep<'tcx>>>
|
||||
{
|
||||
// FIXME: we don't need to create the entire steps in one pass
|
||||
|
||||
let (final_ty, dereferences, _) = self.autoderef(span,
|
||||
self_ty,
|
||||
|| None,
|
||||
UnresolvedTypeAction::Error,
|
||||
NoPreference,
|
||||
|t, d| {
|
||||
steps.push(CandidateStep {
|
||||
self_ty: t,
|
||||
let mut autoderef = self.autoderef(span, self_ty);
|
||||
let mut steps: Vec<_> = autoderef.by_ref().map(|(ty, d)| CandidateStep {
|
||||
self_ty: ty,
|
||||
autoderefs: d,
|
||||
unsize: false
|
||||
});
|
||||
None::<()> // keep iterating until we can't anymore
|
||||
});
|
||||
}).collect();
|
||||
|
||||
let final_ty = autoderef.unambiguous_final_ty();
|
||||
match final_ty.sty {
|
||||
ty::TyArray(elem_ty, _) => {
|
||||
let dereferences = steps.len() - 1;
|
||||
|
||||
steps.push(CandidateStep {
|
||||
self_ty: self.tcx.mk_slice(elem_ty),
|
||||
autoderefs: dereferences,
|
||||
@ -237,6 +234,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
_ => (),
|
||||
}
|
||||
|
||||
debug!("create_steps: steps={:?}", steps);
|
||||
|
||||
Some(steps)
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
use CrateCtxt;
|
||||
|
||||
use check::{self, FnCtxt, UnresolvedTypeAction};
|
||||
use check::{FnCtxt};
|
||||
use rustc::hir::map as hir_map;
|
||||
use rustc::ty::{self, Ty, ToPolyTraitRef, ToPredicate, TypeFoldable};
|
||||
use middle::cstore;
|
||||
@ -21,7 +21,6 @@ use hir::def::Def;
|
||||
use hir::def_id::DefId;
|
||||
use middle::lang_items::FnOnceTraitLangItem;
|
||||
use rustc::ty::subst::Substs;
|
||||
use rustc::ty::LvaluePreference;
|
||||
use rustc::traits::{Obligation, SelectionContext};
|
||||
use util::nodemap::{FnvHashSet};
|
||||
|
||||
@ -48,42 +47,28 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
ty::TyClosure(..) | ty::TyFnDef(..) | ty::TyFnPtr(_) => true,
|
||||
// If it's not a simple function, look for things which implement FnOnce
|
||||
_ => {
|
||||
if let Ok(fn_once_trait_did) =
|
||||
tcx.lang_items.require(FnOnceTraitLangItem) {
|
||||
let (_, _, opt_is_fn) = self.autoderef(span,
|
||||
ty,
|
||||
|| None,
|
||||
UnresolvedTypeAction::Ignore,
|
||||
LvaluePreference::NoPreference,
|
||||
|ty, _| {
|
||||
self.probe(|_| {
|
||||
let fn_once = match tcx.lang_items.require(FnOnceTraitLangItem) {
|
||||
Ok(fn_once) => fn_once,
|
||||
Err(..) => return false
|
||||
};
|
||||
|
||||
self.autoderef(span, ty).any(|(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,
|
||||
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,
|
||||
self.body_id,
|
||||
poly_trait_ref
|
||||
.to_predicate());
|
||||
let mut selcx = SelectionContext::new(self);
|
||||
SelectionContext::new(self).evaluate_obligation(&obligation)
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if selcx.evaluate_obligation(&obligation) {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
opt_is_fn.is_some()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn report_method_error(&self,
|
||||
span: Span,
|
||||
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));
|
||||
}
|
||||
|
||||
self.autoderef(span, rcvr_ty, || None,
|
||||
check::UnresolvedTypeAction::Ignore, ty::NoPreference,
|
||||
|ty, _| {
|
||||
if is_local(ty) {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).2.is_some()
|
||||
self.autoderef(span, rcvr_ty).any(|(ty, _)| is_local(ty))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,6 +129,7 @@ use rustc_back::slice;
|
||||
use rustc_const_eval::eval_repeat_count;
|
||||
|
||||
mod assoc;
|
||||
mod autoderef;
|
||||
pub mod dropck;
|
||||
pub mod _match;
|
||||
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
|
||||
/// 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
|
||||
/// returns a type of `&T`, but the actual type we assign to the
|
||||
/// *expression* is `T`. So this function just peels off the return
|
||||
@ -2371,30 +2247,29 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
// autoderef that normal method probing does. They could likely be
|
||||
// consolidated.
|
||||
|
||||
let (ty, autoderefs, final_mt) = self.autoderef(base_expr.span,
|
||||
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)
|
||||
});
|
||||
let mut autoderef = self.autoderef(base_expr.span, base_ty);
|
||||
|
||||
if final_mt.is_some() {
|
||||
return final_mt;
|
||||
while let Some((adj_ty, autoderefs)) = autoderef.next() {
|
||||
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
|
||||
// do a final unsized coercion to yield [T].
|
||||
if let ty::TyArray(element_ty, _) = ty.sty {
|
||||
if let ty::TyArray(element_ty, _) = adj_ty.sty {
|
||||
autoderef.finalize(lvalue_pref, Some(base_expr));
|
||||
let adjusted_ty = self.tcx.mk_slice(element_ty);
|
||||
self.try_index_step(MethodCall::expr(expr.id), expr, base_expr,
|
||||
adjusted_ty, autoderefs, true, lvalue_pref, idx_ty)
|
||||
} else {
|
||||
None
|
||||
return self.try_index_step(
|
||||
MethodCall::expr(expr.id), expr, base_expr,
|
||||
adjusted_ty, autoderefs, true, lvalue_pref, idx_ty);
|
||||
}
|
||||
}
|
||||
autoderef.unambiguous_final_ty();
|
||||
None
|
||||
}
|
||||
|
||||
/// To type-check `base_expr[index_expr]`, we progressively autoderef
|
||||
/// (and otherwise adjust) `base_expr`, looking for a type which either
|
||||
@ -3034,32 +2909,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
let expr_t = self.structurally_resolved_type(expr.span,
|
||||
self.expr_ty(base));
|
||||
let mut private_candidate = None;
|
||||
let (_, autoderefs, field_ty) = self.autoderef(expr.span,
|
||||
expr_t,
|
||||
|| Some(base),
|
||||
UnresolvedTypeAction::Error,
|
||||
lvalue_pref,
|
||||
|base_t, _| {
|
||||
let mut autoderef = self.autoderef(expr.span, expr_t);
|
||||
while let Some((base_t, autoderefs)) = autoderef.next() {
|
||||
if let ty::TyStruct(base_def, substs) = base_t.sty {
|
||||
debug!("struct named {:?}", base_t);
|
||||
if let Some(field) = base_def.struct_variant().find_field_named(field.node) {
|
||||
let field_ty = self.field_ty(expr.span, field, substs);
|
||||
if field.vis.is_accessible_from(self.body_id, &self.tcx().map) {
|
||||
return Some(field_ty);
|
||||
}
|
||||
private_candidate = Some((base_def.did, field_ty));
|
||||
}
|
||||
}
|
||||
None
|
||||
});
|
||||
match field_ty {
|
||||
Some(field_ty) => {
|
||||
autoderef.finalize(lvalue_pref, Some(base));
|
||||
self.write_ty(expr.id, field_ty);
|
||||
self.write_autoderef_adjustment(base.id, autoderefs);
|
||||
return;
|
||||
}
|
||||
None => {}
|
||||
private_candidate = Some((base_def.did, field_ty));
|
||||
}
|
||||
}
|
||||
}
|
||||
autoderef.unambiguous_final_ty();
|
||||
|
||||
if let Some((did, field_ty)) = private_candidate {
|
||||
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));
|
||||
let mut private_candidate = None;
|
||||
let mut tuple_like = false;
|
||||
let (_, autoderefs, field_ty) = self.autoderef(expr.span,
|
||||
expr_t,
|
||||
|| Some(base),
|
||||
UnresolvedTypeAction::Error,
|
||||
lvalue_pref,
|
||||
|base_t, _| {
|
||||
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,
|
||||
};
|
||||
|
||||
let mut autoderef = self.autoderef(expr.span, expr_t);
|
||||
while let Some((base_t, autoderefs)) = autoderef.next() {
|
||||
let field = match base_t.sty {
|
||||
ty::TyStruct(base_def, substs) => {
|
||||
tuple_like = base_def.struct_variant().is_tuple_struct();
|
||||
if !tuple_like { return None }
|
||||
if !tuple_like { continue }
|
||||
|
||||
debug!("tuple struct named {:?}", base_t);
|
||||
if let Some(field) = base_def.struct_variant().fields.get(idx.node) {
|
||||
base_def.struct_variant().fields.get(idx.node).and_then(|field| {
|
||||
let field_ty = self.field_ty(expr.span, field, substs);
|
||||
if field.vis.is_accessible_from(self.body_id, &self.tcx().map) {
|
||||
return Some(field_ty);
|
||||
}
|
||||
private_candidate = Some((base_def.did, field_ty));
|
||||
}
|
||||
if field.vis.is_accessible_from(self.body_id, &self.tcx().map) {
|
||||
Some(field_ty)
|
||||
} else {
|
||||
None
|
||||
});
|
||||
match field_ty {
|
||||
Some(field_ty) => {
|
||||
}
|
||||
})
|
||||
}
|
||||
ty::TyTuple(ref v) => {
|
||||
tuple_like = true;
|
||||
v.get(idx.node).cloned()
|
||||
}
|
||||
_ => continue
|
||||
};
|
||||
|
||||
if let Some(field_ty) = field {
|
||||
autoderef.finalize(lvalue_pref, Some(base));
|
||||
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 {
|
||||
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
|
||||
impl Error + 'static {
|
||||
/// 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()
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// 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`
|
||||
/// boundary with no fear of panic safety.
|
||||
/// purpose of this trait is to encode what types are safe to cross a `catch_unwind`
|
||||
/// 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
|
||||
/// 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
|
||||
/// 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
|
||||
/// 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,
|
||||
/// 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
|
||||
/// memory unsafety.
|
||||
///
|
||||
/// That was a bit of a whirlwind tour of panic safety, but for more information
|
||||
/// about panic safety and how it applies to Rust, see an [associated RFC][rfc].
|
||||
/// That was a bit of a whirlwind tour of unwind safety, but for more information
|
||||
/// 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
|
||||
///
|
||||
/// ## 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
|
||||
/// 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
|
||||
/// the closure.
|
||||
///
|
||||
/// 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
|
||||
/// many types, and it is also structurally composed (e.g. a struct is recover
|
||||
/// safe if all of its components are recover safe).
|
||||
/// many types, and it is also structurally composed (e.g. a struct is unwind
|
||||
/// safe if all of its components are unwind safe).
|
||||
///
|
||||
/// 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
|
||||
/// "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.
|
||||
///
|
||||
/// ## Who implements `UnwindSafe`?
|
||||
///
|
||||
/// 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
|
||||
/// across `recover` is not recover safe by default. This is because it is very
|
||||
/// easy to witness a broken invariant outside of `recover` as the data is
|
||||
/// unwind safe. The general idea is that any mutable state which can be shared
|
||||
/// across `catch_unwind` is not unwind safe by default. This is because it is very
|
||||
/// easy to witness a broken invariant outside of `catch_unwind` as the data is
|
||||
/// 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
|
||||
/// they already provide their own "speed bumps" to do so.
|
||||
///
|
||||
/// ## When should `UnwindSafe` be used?
|
||||
///
|
||||
/// 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`
|
||||
/// 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).
|
||||
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
||||
#[rustc_on_unimplemented = "the type {Self} may not be safely transferred \
|
||||
across a recover boundary"]
|
||||
across an unwind boundary"]
|
||||
pub trait UnwindSafe {}
|
||||
|
||||
/// Deprecated, renamed to UnwindSafe
|
||||
@ -126,7 +126,7 @@ pub trait RecoverSafe {}
|
||||
impl<T: UnwindSafe> RecoverSafe for T {}
|
||||
|
||||
/// 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
|
||||
/// interior mutability.
|
||||
@ -136,23 +136,23 @@ impl<T: UnwindSafe> RecoverSafe for T {}
|
||||
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
||||
#[rustc_on_unimplemented = "the type {Self} contains interior mutability \
|
||||
and a reference may not be safely transferrable \
|
||||
across a recover boundary"]
|
||||
across a catch_unwind boundary"]
|
||||
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
|
||||
/// variables are not panic safe. For example if `&mut T` is captured the
|
||||
/// compiler will generate a warning indicating that it is not panic safe. It
|
||||
/// When using `catch_unwind` it may be the case that some of the closed over
|
||||
/// variables are not unwind safe. For example if `&mut T` is captured the
|
||||
/// 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
|
||||
/// 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
|
||||
/// annotation that a variable is indeed panic safe.
|
||||
/// annotation that a variable is indeed unwind safe.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// 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};
|
||||
@ -160,7 +160,7 @@ pub trait RefUnwindSafe {}
|
||||
/// let mut variable = 4;
|
||||
///
|
||||
/// // 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(|| {
|
||||
/// // variable += 3;
|
||||
@ -239,7 +239,7 @@ impl<T> UnwindSafe for AssertUnwindSafe<T> {}
|
||||
impl<T> UnwindSafe for AssertRecoverSafe<T> {}
|
||||
|
||||
// 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
|
||||
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
||||
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
|
||||
/// 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
|
||||
/// 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
|
||||
/// 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.
|
||||
///
|
||||
/// [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);
|
||||
assert_eq!(str, &r#"
|
||||
--> dummy.txt:11:1
|
||||
|>
|
||||
11 |> e-lä-vän
|
||||
|> ^
|
||||
"#[1..]);
|
||||
@ -746,6 +747,7 @@ mod test {
|
||||
|
||||
let expect_start = &r#"
|
||||
--> dummy.txt:1:6
|
||||
|>
|
||||
1 |> _____aaaaaa____bbbbbb__cccccdd_
|
||||
|> ^^^^^^ ^^^^^^ ^^^^^^^
|
||||
"#[1..];
|
||||
@ -818,6 +820,7 @@ mod test {
|
||||
|
||||
let expect0 = &r#"
|
||||
--> dummy.txt:5:1
|
||||
|>
|
||||
5 |> ccccc
|
||||
|> ^
|
||||
...
|
||||
@ -830,6 +833,7 @@ mod test {
|
||||
|
||||
let expect = &r#"
|
||||
--> dummy.txt:1:1
|
||||
|>
|
||||
1 |> aaaaa
|
||||
|> ^
|
||||
...
|
||||
|
@ -478,6 +478,13 @@ impl FileInfo {
|
||||
}],
|
||||
kind: RenderedLineKind::PrimaryFileName,
|
||||
});
|
||||
output.push(RenderedLine {
|
||||
text: vec![StyledString {
|
||||
text: "".to_string(),
|
||||
style: Style::FileNameStyle,
|
||||
}],
|
||||
kind: RenderedLineKind::Annotations,
|
||||
});
|
||||
}
|
||||
None => {
|
||||
output.push(RenderedLine {
|
||||
@ -487,6 +494,13 @@ impl FileInfo {
|
||||
}],
|
||||
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);
|
||||
assert_eq!(&text[..], &"
|
||||
--> foo.rs:3:2
|
||||
|>
|
||||
3 |> \tbar;
|
||||
|> \t^^^
|
||||
"[1..]);
|
||||
@ -130,6 +131,7 @@ fn foo() {
|
||||
println!("text=\n{}", text);
|
||||
assert_eq!(&text[..], &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
3 |> vec.push(vec.pop().unwrap());
|
||||
|> --- --- - previous borrow ends here
|
||||
|> | |
|
||||
@ -199,12 +201,14 @@ fn bar() {
|
||||
// Note that the `|>` remain aligned across both files:
|
||||
assert_eq!(&text[..], &r#"
|
||||
--> foo.rs:3:14
|
||||
|>
|
||||
3 |> vec.push(vec.pop().unwrap());
|
||||
|> --- ^^^ - c
|
||||
|> | |
|
||||
|> | b
|
||||
|> a
|
||||
::: bar.rs
|
||||
|>
|
||||
17 |> vec.push();
|
||||
|> --- - f
|
||||
|> |
|
||||
@ -249,6 +253,7 @@ fn foo() {
|
||||
println!("text=\n{}", text);
|
||||
assert_eq!(&text[..], &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
3 |> let name = find_id(&data, 22).unwrap();
|
||||
|> ---- immutable borrow begins here
|
||||
...
|
||||
@ -288,6 +293,7 @@ fn foo() {
|
||||
println!("text=r#\"\n{}\".trim_left()", text);
|
||||
assert_eq!(&text[..], &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
3 |> vec.push(vec.pop().unwrap());
|
||||
|> -------- ------ D
|
||||
|> ||
|
||||
@ -324,6 +330,7 @@ fn foo() {
|
||||
println!("text=r#\"\n{}\".trim_left()", text);
|
||||
assert_eq!(&text[..], &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
3 |> vec.push(vec.pop().unwrap());
|
||||
|> --- --- - previous borrow ends here
|
||||
|> | |
|
||||
@ -362,6 +369,7 @@ fn foo() {
|
||||
println!("text=r#\"\n{}\".trim_left()", text);
|
||||
assert_eq!(&text[..], &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
4 |> let mut vec2 = vec;
|
||||
|> --- `vec` moved here because it has type `collections::vec::Vec<i32>`
|
||||
...
|
||||
@ -398,6 +406,7 @@ fn foo() {
|
||||
println!("text=&r#\"\n{}\n\"#[1..]", text);
|
||||
assert_eq!(text, &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
3 |> let mut vec = vec![0, 1, 2];
|
||||
|> --- ---
|
||||
4 |> let mut vec2 = vec;
|
||||
@ -429,6 +438,7 @@ impl SomeTrait for () {
|
||||
println!("r#\"\n{}\"", text);
|
||||
assert_eq!(text, &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
3 |> fn foo(x: u32) {
|
||||
|> -
|
||||
"#[1..]);
|
||||
@ -458,6 +468,7 @@ fn span_overlap_label() {
|
||||
println!("r#\"\n{}\"", text);
|
||||
assert_eq!(text, &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
2 |> fn foo(x: u32) {
|
||||
|> --------------
|
||||
|> | |
|
||||
@ -492,6 +503,7 @@ fn span_overlap_label2() {
|
||||
println!("r#\"\n{}\"", text);
|
||||
assert_eq!(text, &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
2 |> fn foo(x: u32) {
|
||||
|> --------------
|
||||
|> | |
|
||||
@ -537,6 +549,7 @@ fn span_overlap_label3() {
|
||||
println!("r#\"\n{}\"", text);
|
||||
assert_eq!(text, &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
3 |> let closure = || {
|
||||
|> - foo
|
||||
4 |> inner
|
||||
@ -577,6 +590,7 @@ fn main() {
|
||||
println!("r#\"\n{}\"", text);
|
||||
assert_eq!(text, &r#"
|
||||
--> foo.rs:11:2
|
||||
|>
|
||||
11 |> }
|
||||
|> -
|
||||
"#[1..]);
|
||||
|
@ -233,13 +233,10 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) {
|
||||
visitor.visit_path(path, item.id);
|
||||
}
|
||||
ViewPathList(ref prefix, ref list) => {
|
||||
if !list.is_empty() {
|
||||
visitor.visit_path(prefix, item.id);
|
||||
for item in list {
|
||||
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) {
|
||||
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.rename());
|
||||
}
|
||||
|
@ -19,5 +19,4 @@ fn main() {
|
||||
let foo = Foo;
|
||||
let ref_foo = &&Foo;
|
||||
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>) {
|
||||
x.y = 3; //~ ERROR cannot assign
|
||||
x.y = 3; //~ ERROR cannot borrow
|
||||
}
|
||||
|
||||
fn assign_field3<'a>(x: &'a mut Own<Point>) {
|
||||
|
21
src/test/compile-fail/issue-24819.rs
Normal file
21
src/test/compile-fail/issue-24819.rs
Normal file
@ -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>) {
|
||||
}
|
18
src/test/compile-fail/issue-33819.rs
Normal file
18
src/test/compile-fail/issue-33819.rs
Normal file
@ -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>) {
|
||||
//~^ 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
|
||||
// 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
|
||||
// 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
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(rand, core)]
|
||||
#![feature(rand)]
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
@ -18,6 +18,11 @@ use std::process::Command;
|
||||
use std::__rand::{thread_rng, Rng};
|
||||
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
|
||||
// compiler emits a span of the appropriate length (for the
|
||||
// "unresolved name" message); currently just using the number of code
|
||||
@ -65,10 +70,17 @@ fn main() {
|
||||
|
||||
let err = String::from_utf8_lossy(&result.stderr);
|
||||
|
||||
if check_old_skool() {
|
||||
// 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));
|
||||
} 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
|
||||
@ -77,9 +89,6 @@ fn main() {
|
||||
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")
|
||||
.arg("-c")
|
||||
.arg(format!("{} {}",
|
||||
@ -91,6 +100,10 @@ fn main() {
|
||||
|
||||
// Test both the length of the snake and the leading spaces up to it
|
||||
|
||||
if check_old_skool() {
|
||||
// Extra characters. Every line is preceded by `filename:lineno <actual code>`
|
||||
let offset = main_file.to_str().unwrap().len() + 3;
|
||||
|
||||
// First snake is 8 ~s long, with 7 preceding spaces (excluding file name/line offset)
|
||||
let expected_span = format!("\n{}^{}\n",
|
||||
repeat(" ").take(offset + 7).collect::<String>(),
|
||||
@ -104,4 +117,14 @@ fn main() {
|
||||
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
|
||||
// except according to those terms.
|
||||
|
||||
// rustc-env:RUST_NEW_ERROR_FORMAT
|
||||
extern {
|
||||
fn write(fildes: i32, buf: *const i8, nbyte: u64) -> i64;
|
||||
}
|
||||
@ -24,25 +25,16 @@ macro_rules! write {
|
||||
unsafe {
|
||||
write(stdout, $arr.as_ptr() as *const i8,
|
||||
$arr.len() * size_of($arr[0]));
|
||||
//~^ ERROR mismatched types
|
||||
//~| expected u64, found usize
|
||||
//~| expected type
|
||||
//~| found type
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
macro_rules! cast {
|
||||
($x:expr) => ($x as ()) //~ ERROR non-scalar cast
|
||||
($x:expr) => ($x as ())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let hello = ['H', 'e', 'y'];
|
||||
write!(hello);
|
||||
//~^ NOTE in this expansion of write!
|
||||
//~| NOTE in this expansion of write!
|
||||
//~| NOTE in this expansion of write!
|
||||
|
||||
cast!(2);
|
||||
//~^ NOTE in this expansion of cast!
|
||||
}
|
15
src/test/ui/mismatched_types/issue-26480.stderr
Normal file
15
src/test/ui/mismatched_types/issue-26480.stderr
Normal file
@ -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]
|
||||
--> $DIR/main.rs:14:18
|
||||
|>
|
||||
14 |> let x: u32 = (
|
||||
|> ^ expected u32, found ()
|
||||
note: expected type `u32`
|
||||
|
Loading…
Reference in New Issue
Block a user