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:
bors 2016-05-28 07:22:51 -07:00
commit f1776fe244
36 changed files with 779 additions and 604 deletions

View File

@ -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 {

View File

@ -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.
///

View File

@ -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);

View File

@ -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());
}

View File

@ -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

View File

@ -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

View File

@ -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};

View File

@ -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>(

View File

@ -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 {

View File

@ -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);
}
}

View File

@ -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));
}

View File

@ -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)
}

View 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
}
}

View File

@ -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 => {

View File

@ -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 {

View File

@ -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

View File

@ -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!

View File

@ -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)
}
}

View File

@ -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))
}
}

View File

@ -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);

View File

@ -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`

View File

@ -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

View File

@ -682,6 +682,7 @@ mod test {
println!("r#\"\n{}\"#", str);
assert_eq!(str, &r#"
--> dummy.txt:11:1
|>
11 |> e--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
|> ^
...

View File

@ -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,
});
}
}
}

View File

@ -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..]);

View File

@ -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());
}

View File

@ -19,5 +19,4 @@ fn main() {
let foo = Foo;
let ref_foo = &&Foo;
ref_foo.foo(); //~ ERROR E0055
//~^ ERROR E0275
}

View File

@ -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>) {

View 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>) {
}

View 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 => {},
}
}

View File

@ -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,

View File

@ -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
}

View File

@ -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));
}
}

View File

@ -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!
}

View 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

View File

@ -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`