diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs index 26eca0938b189..a5111cdf7c8f5 100644 --- a/src/librustc/middle/borrowck/check_loans.rs +++ b/src/librustc/middle/borrowck/check_loans.rs @@ -400,50 +400,82 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { for restr_path in loan1.restricted_paths.iter() { if *restr_path != loan2_base_path { continue; } - let old_pronoun = if new_loan.loan_path == old_loan.loan_path { + // If new_loan is something like `x.a`, and old_loan is something like `x.b`, we would + // normally generate a rather confusing message (in this case, for multiple mutable + // borrows): + // + // error: cannot borrow `x.b` as mutable more than once at a time + // note: previous borrow of `x.a` occurs here; the mutable borrow prevents + // subsequent moves, borrows, or modification of `x.a` until the borrow ends + // + // What we want to do instead is get the 'common ancestor' of the two borrow paths and + // use that for most of the message instead, giving is something like this: + // + // error: cannot borrow `x` as mutable more than once at a time + // note: previous borrow of `x` occurs here (through borrowing `x.a`); the mutable + // borrow prevents subsequent moves, borrows, or modification of `x` until the + // borrow ends + + let common = new_loan.loan_path.common(&*old_loan.loan_path); + let (nl, ol, new_loan_msg, old_loan_msg) = + if new_loan.loan_path.has_fork(&*old_loan.loan_path) && common.is_some() { + let nl = self.bccx.loan_path_to_string(&common.unwrap()); + let ol = nl.clone(); + let new_loan_msg = format!(" (here through borrowing `{}`)", + self.bccx.loan_path_to_string( + &*new_loan.loan_path)); + let old_loan_msg = format!(" (through borrowing `{}`)", + self.bccx.loan_path_to_string( + &*old_loan.loan_path)); + (nl, ol, new_loan_msg, old_loan_msg) + } else { + (self.bccx.loan_path_to_string(&*new_loan.loan_path), + self.bccx.loan_path_to_string(&*old_loan.loan_path), + String::new(), String::new()) + }; + + let ol_pronoun = if new_loan.loan_path == old_loan.loan_path { "it".to_string() } else { - format!("`{}`", - self.bccx.loan_path_to_string(&*old_loan.loan_path)) + format!("`{}`", ol) }; match (new_loan.kind, old_loan.kind) { (ty::MutBorrow, ty::MutBorrow) => { self.bccx.span_err( new_loan.span, - format!("cannot borrow `{}` as mutable \ + format!("cannot borrow `{}`{} as mutable \ more than once at a time", - self.bccx.loan_path_to_string( - &*new_loan.loan_path)).as_slice()); + nl, new_loan_msg).as_slice()) } (ty::UniqueImmBorrow, _) => { self.bccx.span_err( new_loan.span, format!("closure requires unique access to `{}` \ - but {} is already borrowed", - self.bccx.loan_path_to_string(&*new_loan.loan_path), - old_pronoun).as_slice()); + but {} is already borrowed{}", + nl, ol_pronoun, old_loan_msg).as_slice()); } (_, ty::UniqueImmBorrow) => { self.bccx.span_err( new_loan.span, - format!("cannot borrow `{}` as {} because \ + format!("cannot borrow `{}`{} as {} because \ previous closure requires unique access", - self.bccx.loan_path_to_string(&*new_loan.loan_path), - new_loan.kind.to_user_str()).as_slice()); + nl, new_loan_msg, new_loan.kind.to_user_str()).as_slice()); } (_, _) => { self.bccx.span_err( new_loan.span, - format!("cannot borrow `{}` as {} because \ - {} is also borrowed as {}", - self.bccx.loan_path_to_string(&*new_loan.loan_path), + format!("cannot borrow `{}`{} as {} because \ + {} is also borrowed as {}{}", + nl, + new_loan_msg, new_loan.kind.to_user_str(), - old_pronoun, - old_loan.kind.to_user_str()).as_slice()); + ol_pronoun, + old_loan.kind.to_user_str(), + old_loan_msg).as_slice()); } } @@ -452,8 +484,7 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { self.bccx.span_note( span, format!("borrow occurs due to use of `{}` in closure", - self.bccx.loan_path_to_string( - &*new_loan.loan_path)).as_slice()); + nl).as_slice()); } _ => { } } @@ -463,30 +494,29 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { format!("the mutable borrow prevents subsequent \ moves, borrows, or modification of `{0}` \ until the borrow ends", - self.bccx.loan_path_to_string( - &*old_loan.loan_path)) + ol) } ty::ImmBorrow => { format!("the immutable borrow prevents subsequent \ moves or mutable borrows of `{0}` \ until the borrow ends", - self.bccx.loan_path_to_string(&*old_loan.loan_path)) + ol) } ty::UniqueImmBorrow => { format!("the unique capture prevents subsequent \ moves or borrows of `{0}` \ until the borrow ends", - self.bccx.loan_path_to_string(&*old_loan.loan_path)) + ol) } }; let borrow_summary = match old_loan.cause { euv::ClosureCapture(_) => { - format!("previous borrow of `{}` occurs here due to \ + format!("previous borrow of `{}` occurs here{} due to \ use in closure", - self.bccx.loan_path_to_string(&*old_loan.loan_path)) + ol, old_loan_msg) } euv::OverloadedOperator(..) | @@ -496,8 +526,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { euv::ForLoop(..) | euv::RefBinding(..) | euv::MatchDiscriminant(..) => { - format!("previous borrow of `{}` occurs here", - self.bccx.loan_path_to_string(&*old_loan.loan_path)) + format!("previous borrow of `{}` occurs here{}", + ol, old_loan_msg) } }; diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index d4d6fae53e3a3..b411620dac0e7 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -298,6 +298,51 @@ impl LoanPath { LpExtend(ref base, _, _) => base.kill_scope(tcx), } } + + fn has_fork(&self, other: &LoanPath) -> bool { + match (self, other) { + (&LpExtend(ref base, _, LpInterior(id)), &LpExtend(ref base2, _, LpInterior(id2))) => + if id == id2 { + base.has_fork(&**base2) + } else { + true + }, + (&LpExtend(ref base, _, LpDeref(_)), _) => base.has_fork(other), + (_, &LpExtend(ref base, _, LpDeref(_))) => self.has_fork(&**base), + _ => false, + } + } + + fn depth(&self) -> uint { + match *self { + LpExtend(ref base, _, LpDeref(_)) => base.depth(), + LpExtend(ref base, _, LpInterior(_)) => base.depth() + 1, + _ => 0, + } + } + + fn common(&self, other: &LoanPath) -> Option { + match (self, other) { + (&LpExtend(ref base, a, LpInterior(id)), &LpExtend(ref base2, _, LpInterior(id2))) => + if id == id2 { + base.common(&**base2).map(|x| { + let xd = x.depth(); + if base.depth() == xd && base2.depth() == xd { + LpExtend(Rc::new(x), a, LpInterior(id)) + } else { + x + } + }) + } else { + base.common(&**base2) + }, + (&LpExtend(ref base, _, LpDeref(_)), _) => base.common(other), + (_, &LpExtend(ref other, _, LpDeref(_))) => self.common(&**other), + (&LpVar(id), &LpVar(id2)) => if id == id2 { Some(LpVar(id)) } else { None }, + (&LpUpvar(id), &LpUpvar(id2)) => if id == id2 { Some(LpUpvar(id)) } else { None }, + _ => None, + } + } } pub fn opt_loan_path(cmt: &mc::cmt) -> Option> { @@ -416,24 +461,58 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { MovedInCapture => "capture", }; - match the_move.kind { + let (ol, moved_lp_msg) = match the_move.kind { move_data::Declared => { self.tcx.sess.span_err( use_span, format!("{} of possibly uninitialized variable: `{}`", verb, self.loan_path_to_string(lp)).as_slice()); + (self.loan_path_to_string(moved_lp), + String::new()) } _ => { - let partially = if lp == moved_lp {""} else {"partially "}; + // If moved_lp is something like `x.a`, and lp is something like `x.b`, we would + // normally generate a rather confusing message: + // + // error: use of moved value: `x.b` + // note: `x.a` moved here... + // + // What we want to do instead is get the 'common ancestor' of the two moves and + // use that for most of the message instead, giving is something like this: + // + // error: use of moved value: `x` + // note: `x` moved here (through moving `x.a`)... + + let common = moved_lp.common(lp); + let has_common = common.is_some(); + let has_fork = moved_lp.has_fork(lp); + let (nl, ol, moved_lp_msg) = + if has_fork && has_common { + let nl = self.loan_path_to_string(&common.unwrap()); + let ol = nl.clone(); + let moved_lp_msg = format!(" (through moving `{}`)", + self.loan_path_to_string(moved_lp)); + (nl, ol, moved_lp_msg) + } else { + (self.loan_path_to_string(lp), + self.loan_path_to_string(moved_lp), + String::new()) + }; + + let partial = moved_lp.depth() > lp.depth(); + let msg = if !has_fork && partial { "partially " } + else if has_fork && !has_common { "collaterally "} + else { "" }; self.tcx.sess.span_err( use_span, format!("{} of {}moved value: `{}`", verb, - partially, - self.loan_path_to_string(lp)).as_slice()); + msg, + nl).as_slice()); + (ol, moved_lp_msg) } - } + }; match the_move.kind { move_data::Declared => {} @@ -456,8 +535,9 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { "moved by default (use `copy` to override)"); self.tcx.sess.span_note( expr_span, - format!("`{}` moved here because it has type `{}`, which is {}", - self.loan_path_to_string(moved_lp), + format!("`{}` moved here{} because it has type `{}`, which is {}", + ol, + moved_lp_msg, expr_ty.user_string(self.tcx), suggestion).as_slice()); } @@ -465,10 +545,11 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { move_data::MovePat => { let pat_ty = ty::node_id_to_type(self.tcx, the_move.id); self.tcx.sess.span_note(self.tcx.map.span(the_move.id), - format!("`{}` moved here because it has type `{}`, \ + format!("`{}` moved here{} because it has type `{}`, \ which is moved by default (use `ref` to \ override)", - self.loan_path_to_string(moved_lp), + ol, + moved_lp_msg, pat_ty.user_string(self.tcx)).as_slice()); } @@ -491,9 +572,10 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { capture that instead to override)"); self.tcx.sess.span_note( expr_span, - format!("`{}` moved into closure environment here because it \ + format!("`{}` moved into closure environment here{} because it \ has type `{}`, which is {}", - self.loan_path_to_string(moved_lp), + ol, + moved_lp_msg, expr_ty.user_string(self.tcx), suggestion).as_slice()); } @@ -602,6 +684,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { span: Span, kind: AliasableViolationKind, cause: mc::AliasableReason) { + let mut is_closure = false; let prefix = match kind { MutabilityViolation => { "cannot assign to data" @@ -625,6 +708,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { } BorrowViolation(euv::ClosureInvocation) => { + is_closure = true; "closure invocation" } @@ -649,7 +733,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { mc::AliasableManaged => { self.tcx.sess.span_err( span, - format!("{} in a `@` pointer", prefix).as_slice()); + format!("{} in a `Gc` pointer", prefix).as_slice()); } mc::AliasableBorrowed => { self.tcx.sess.span_err( @@ -657,6 +741,12 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { format!("{} in a `&` reference", prefix).as_slice()); } } + + if is_closure { + self.tcx.sess.span_note( + span, + "closures behind references must be called via `&mut`"); + } } pub fn note_and_explain_bckerr(&self, err: BckError) { diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index 2503fb2541b90..fa68814ea16fc 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -59,7 +59,7 @@ use middle::subst::{VecPerParamSpace}; use middle::ty; use middle::typeck::lookup_def_tcx; use middle::typeck::infer; -use middle::typeck::rscope::{ExplicitRscope, RegionScope, SpecificRscope}; +use middle::typeck::rscope::{UnelidableRscope, RegionScope, SpecificRscope}; use middle::typeck::rscope; use middle::typeck::TypeAndSubsts; use middle::typeck; @@ -67,10 +67,11 @@ use util::ppaux::{Repr, UserString}; use std::collections::HashMap; use std::rc::Rc; -use syntax::abi; -use syntax::{ast, ast_util}; +use std::iter::AdditiveIterator; +use syntax::{abi, ast, ast_util}; use syntax::codemap::Span; use syntax::parse::token; +use syntax::print::pprust; pub trait AstConv<'tcx> { fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx>; @@ -147,10 +148,49 @@ pub fn opt_ast_region_to_region<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( None => { match rscope.anon_regions(default_span, 1) { - Err(()) => { + Err(v) => { debug!("optional region in illegal location"); span_err!(this.tcx().sess, default_span, E0106, "missing lifetime specifier"); + match v { + Some(v) => { + let mut m = String::new(); + let len = v.len(); + for (i, (name, n)) in v.move_iter().enumerate() { + m.push_str(if n == 1 { + format!("`{}`", name) + } else { + format!("one of `{}`'s {} elided lifetimes", name, n) + }.as_slice()); + + if len == 2 && i == 0 { + m.push_str(" or "); + } else if i == len - 2 { + m.push_str(", or "); + } else if i != len - 1 { + m.push_str(", "); + } + } + if len == 1 { + span_note!(this.tcx().sess, default_span, + "this function's return type contains a borrowed value, but \ + the signature does not say which {} it is borrowed from", + m); + } else if len == 0 { + span_note!(this.tcx().sess, default_span, + "this function's return type contains a borrowed value, but \ + there is no value for it to be borrowed from"); + span_note!(this.tcx().sess, default_span, + "consider giving it a 'static lifetime"); + } else { + span_note!(this.tcx().sess, default_span, + "this function's return type contains a borrowed value, but \ + the signature does not say whether it is borrowed from {}", + m); + } + } + None => {}, + } ty::ReStatic } @@ -217,7 +257,7 @@ fn ast_path_substs<'tcx,AC,RS>( match anon_regions { Ok(v) => v.into_iter().collect(), - Err(()) => Vec::from_fn(expected_num_region_params, + Err(_) => Vec::from_fn(expected_num_region_params, |_| ty::ReStatic) // hokey } }; @@ -1153,15 +1193,20 @@ fn ty_of_method_or_bare_fn<'tcx, AC: AstConv<'tcx>>( }; // HACK(eddyb) replace the fake self type in the AST with the actual type. - let input_tys = if self_ty.is_some() { + let input_params = if self_ty.is_some() { decl.inputs.slice_from(1) } else { decl.inputs.as_slice() }; - let input_tys = input_tys.iter().map(|a| ty_of_arg(this, &rb, a, None)); - let self_and_input_tys: Vec<_> = + let input_tys = input_params.iter().map(|a| ty_of_arg(this, &rb, a, None)); + let input_pats: Vec = input_params.iter() + .map(|a| pprust::pat_to_string(&*a.pat)) + .collect(); + let self_and_input_tys: Vec = self_ty.into_iter().chain(input_tys).collect(); + let mut lifetimes_for_params: Vec<(String, Vec)> = Vec::new(); + // Second, if there was exactly one lifetime (either a substitution or a // reference) in the arguments, then any anonymous regions in the output // have that lifetime. @@ -1172,15 +1217,25 @@ fn ty_of_method_or_bare_fn<'tcx, AC: AstConv<'tcx>>( drop(self_and_input_tys_iter.next()) } - let mut accumulator = Vec::new(); - for input_type in self_and_input_tys_iter { - ty::accumulate_lifetimes_in_type(&mut accumulator, *input_type) + for (input_type, input_pat) in self_and_input_tys_iter.zip(input_pats.into_iter()) { + let mut accumulator = Vec::new(); + ty::accumulate_lifetimes_in_type(&mut accumulator, *input_type); + lifetimes_for_params.push((input_pat, accumulator)); } - if accumulator.len() == 1 { - implied_output_region = Some(*accumulator.get(0)); + + if lifetimes_for_params.iter().map(|&(_, ref x)| x.len()).sum() == 1 { + implied_output_region = + Some(lifetimes_for_params.iter() + .filter_map(|&(_, ref x)| + if x.len() == 1 { Some(x[0]) } else { None }) + .next().unwrap()); } } + let param_lifetimes: Vec<(String, uint)> = lifetimes_for_params.into_iter() + .map(|(n, v)| (n, v.len())) + .collect(); + let output_ty = match decl.output.node { ast::TyInfer => this.ty_infer(decl.output.span), _ => { @@ -1193,7 +1248,7 @@ fn ty_of_method_or_bare_fn<'tcx, AC: AstConv<'tcx>>( // All regions must be explicitly specified in the output // if the lifetime elision rules do not apply. This saves // the user from potentially-confusing errors. - let rb = ExplicitRscope; + let rb = UnelidableRscope::new(param_lifetimes); ast_ty_to_ty(this, &rb, &*decl.output) } } diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index e49936f5a4c9e..9b3bf46b94cf6 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -1601,7 +1601,7 @@ impl<'a, 'tcx> RegionScope for infer::InferCtxt<'a, 'tcx> { } fn anon_regions(&self, span: Span, count: uint) - -> Result , ()> { + -> Result, Option>> { Ok(Vec::from_fn(count, |_| { self.next_region_var(infer::MiscVariable(span)) })) diff --git a/src/librustc/middle/typeck/rscope.rs b/src/librustc/middle/typeck/rscope.rs index 530f65855d42c..2845e3954b5c6 100644 --- a/src/librustc/middle/typeck/rscope.rs +++ b/src/librustc/middle/typeck/rscope.rs @@ -29,7 +29,7 @@ pub trait RegionScope { fn anon_regions(&self, span: Span, count: uint) - -> Result , ()>; + -> Result, Option>>; fn default_region_bound(&self, span: Span) -> Option; } @@ -46,8 +46,31 @@ impl RegionScope for ExplicitRscope { fn anon_regions(&self, _span: Span, _count: uint) - -> Result , ()> { - Err(()) + -> Result, Option>> { + Err(None) + } +} + +// Same as `ExplicitRscope`, but provides some extra information for diagnostics +pub struct UnelidableRscope(Vec<(String, uint)>); + +impl UnelidableRscope { + pub fn new(v: Vec<(String, uint)>) -> UnelidableRscope { + UnelidableRscope(v) + } +} + +impl RegionScope for UnelidableRscope { + fn default_region_bound(&self, _span: Span) -> Option { + None + } + + fn anon_regions(&self, + _span: Span, + _count: uint) + -> Result, Option>> { + let UnelidableRscope(ref v) = *self; + Err(Some(v.clone())) } } @@ -72,7 +95,7 @@ impl RegionScope for SpecificRscope { fn anon_regions(&self, _span: Span, count: uint) - -> Result , ()> + -> Result, Option>> { Ok(Vec::from_elem(count, self.default)) } @@ -109,7 +132,7 @@ impl RegionScope for BindingRscope { fn anon_regions(&self, _: Span, count: uint) - -> Result , ()> + -> Result, Option>> { Ok(Vec::from_fn(count, |_| self.next_region())) } diff --git a/src/test/compile-fail/borrowck-box-insensitivity.rs b/src/test/compile-fail/borrowck-box-insensitivity.rs index c9b384e0b007d..d05c03547ac4c 100644 --- a/src/test/compile-fail/borrowck-box-insensitivity.rs +++ b/src/test/compile-fail/borrowck-box-insensitivity.rs @@ -31,19 +31,22 @@ struct D { fn copy_after_move() { let a = box A { x: box 0, y: 1 }; let _x = a.x; - let _y = a.y; //~ ERROR use of partially moved + let _y = a.y; //~ ERROR use of moved + //~^^ NOTE `a` moved here (through moving `a.x`) } fn move_after_move() { let a = box B { x: box 0, y: box 1 }; let _x = a.x; - let _y = a.y; //~ ERROR use of partially moved + let _y = a.y; //~ ERROR use of moved + //~^^ NOTE `a` moved here (through moving `a.x`) } fn borrow_after_move() { let a = box A { x: box 0, y: 1 }; let _x = a.x; - let _y = &a.y; //~ ERROR use of partially moved + let _y = &a.y; //~ ERROR use of moved + //~^^ NOTE `a` moved here (through moving `a.x`) } fn move_after_borrow() { @@ -79,19 +82,19 @@ fn mut_borrow_after_borrow() { fn copy_after_move_nested() { let a = box C { x: box A { x: box 0, y: 1 }, y: 2 }; let _x = a.x.x; - let _y = a.y; //~ ERROR use of partially moved + let _y = a.y; //~ ERROR use of collaterally moved } fn move_after_move_nested() { let a = box D { x: box A { x: box 0, y: 1 }, y: box 2 }; let _x = a.x.x; - let _y = a.y; //~ ERROR use of partially moved + let _y = a.y; //~ ERROR use of collaterally moved } fn borrow_after_move_nested() { let a = box C { x: box A { x: box 0, y: 1 }, y: 2 }; let _x = a.x.x; - let _y = &a.y; //~ ERROR use of partially moved + let _y = &a.y; //~ ERROR use of collaterally moved } fn move_after_borrow_nested() { diff --git a/src/test/compile-fail/borrowck-field-sensitivity.rs b/src/test/compile-fail/borrowck-field-sensitivity.rs index 72042b8373d84..49c93e3aa9e0c 100644 --- a/src/test/compile-fail/borrowck-field-sensitivity.rs +++ b/src/test/compile-fail/borrowck-field-sensitivity.rs @@ -13,13 +13,13 @@ struct A { a: int, b: Box } fn deref_after_move() { let x = A { a: 1, b: box 2 }; drop(x.b); - drop(*x.b); //~ ERROR use of partially moved value: `*x.b` + drop(*x.b); //~ ERROR use of moved value: `*x.b` } fn deref_after_fu_move() { let x = A { a: 1, b: box 2 }; let y = A { a: 3, .. x }; - drop(*x.b); //~ ERROR use of partially moved value: `*x.b` + drop(*x.b); //~ ERROR use of moved value: `*x.b` } fn borrow_after_move() { diff --git a/src/test/compile-fail/issue-17263.rs b/src/test/compile-fail/issue-17263.rs new file mode 100644 index 0000000000000..b610a2b0c91d6 --- /dev/null +++ b/src/test/compile-fail/issue-17263.rs @@ -0,0 +1,23 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Foo { a: int, b: int } + +fn main() { + let mut x = box Foo { a: 1, b: 2 }; + let (a, b) = (&mut x.a, &mut x.b); + //~^ ERROR cannot borrow `x` (here through borrowing `x.b`) as mutable more than once at a time + //~^^ NOTE previous borrow of `x` occurs here (through borrowing `x.a`) + + let mut foo = box Foo { a: 1, b: 2 }; + let (c, d) = (&mut foo.a, &foo.b); + //~^ ERROR cannot borrow `foo` (here through borrowing `foo.b`) as immutable + //~^^ NOTE previous borrow of `foo` occurs here (through borrowing `foo.a`) +} diff --git a/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs b/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs index aef3f7a40b555..5fa8c5db5b01c 100644 --- a/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs +++ b/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs @@ -10,11 +10,13 @@ // Lifetime annotation needed because we have no arguments. fn f() -> &int { //~ ERROR missing lifetime specifier +//~^ NOTE there is no value for it to be borrowed from fail!() } // Lifetime annotation needed because we have two by-reference parameters. -fn g(_: &int, _: &int) -> &int { //~ ERROR missing lifetime specifier +fn g(_x: &int, _y: &int) -> &int { //~ ERROR missing lifetime specifier +//~^ NOTE the signature does not say whether it is borrowed from `_x` or `_y` fail!() } @@ -24,7 +26,8 @@ struct Foo<'a> { // Lifetime annotation needed because we have two lifetimes: one as a parameter // and one on the reference. -fn h(_: &Foo) -> &int { //~ ERROR missing lifetime specifier +fn h(_x: &Foo) -> &int { //~ ERROR missing lifetime specifier +//~^ NOTE the signature does not say which one of `_x`'s 2 elided lifetimes it is borrowed from fail!() } diff --git a/src/test/compile-fail/liveness-use-after-move.rs b/src/test/compile-fail/liveness-use-after-move.rs index 0b8e65fee0844..cd7401a65aeaa 100644 --- a/src/test/compile-fail/liveness-use-after-move.rs +++ b/src/test/compile-fail/liveness-use-after-move.rs @@ -13,6 +13,6 @@ extern crate debug; fn main() { let x = box 5i; let y = x; - println!("{:?}", *x); //~ ERROR use of partially moved value: `*x` + println!("{:?}", *x); //~ ERROR use of moved value: `*x` y.clone(); } diff --git a/src/test/compile-fail/use-after-move-self-based-on-type.rs b/src/test/compile-fail/use-after-move-self-based-on-type.rs index b11650a6a4f1f..a1b7f83da2fcf 100644 --- a/src/test/compile-fail/use-after-move-self-based-on-type.rs +++ b/src/test/compile-fail/use-after-move-self-based-on-type.rs @@ -19,7 +19,7 @@ impl Drop for S { impl S { pub fn foo(self) -> int { self.bar(); - return self.x; //~ ERROR use of partially moved value: `self.x` + return self.x; //~ ERROR use of moved value: `self.x` } pub fn bar(self) {} diff --git a/src/test/compile-fail/use-after-move-self.rs b/src/test/compile-fail/use-after-move-self.rs index 22c3ec7c3417c..607d6163208c9 100644 --- a/src/test/compile-fail/use-after-move-self.rs +++ b/src/test/compile-fail/use-after-move-self.rs @@ -16,7 +16,7 @@ struct S { impl S { pub fn foo(self) -> int { self.bar(); - return *self.x; //~ ERROR use of partially moved value: `*self.x` + return *self.x; //~ ERROR use of moved value: `*self.x` } pub fn bar(self) {}