auto merge of #17434 : P1start/rust/borrowck-messages, r=nikomatsakis

This was originally part of #17215.

Closes #15506.
Closes #15630.
Closes #17263.

This also partially implements #15838.
This commit is contained in:
bors 2014-10-02 11:32:25 +00:00
commit 84a4a07bbd
12 changed files with 299 additions and 72 deletions

View File

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

View File

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

View File

@ -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<String> = input_params.iter()
.map(|a| pprust::pat_to_string(&*a.pat))
.collect();
let self_and_input_tys: Vec<ty::t> =
self_ty.into_iter().chain(input_tys).collect();
let mut lifetimes_for_params: Vec<(String, Vec<ty::Region>)> = 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)
}
}

View File

@ -1601,7 +1601,7 @@ impl<'a, 'tcx> RegionScope for infer::InferCtxt<'a, 'tcx> {
}
fn anon_regions(&self, span: Span, count: uint)
-> Result<Vec<ty::Region> , ()> {
-> Result<Vec<ty::Region>, Option<Vec<(String, uint)>>> {
Ok(Vec::from_fn(count, |_| {
self.next_region_var(infer::MiscVariable(span))
}))

View File

@ -29,7 +29,7 @@ pub trait RegionScope {
fn anon_regions(&self,
span: Span,
count: uint)
-> Result<Vec<ty::Region> , ()>;
-> Result<Vec<ty::Region>, Option<Vec<(String, uint)>>>;
fn default_region_bound(&self, span: Span) -> Option<ty::Region>;
}
@ -46,8 +46,31 @@ impl RegionScope for ExplicitRscope {
fn anon_regions(&self,
_span: Span,
_count: uint)
-> Result<Vec<ty::Region> , ()> {
Err(())
-> Result<Vec<ty::Region>, Option<Vec<(String, uint)>>> {
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<ty::Region> {
None
}
fn anon_regions(&self,
_span: Span,
_count: uint)
-> Result<Vec<ty::Region>, Option<Vec<(String, uint)>>> {
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<Vec<ty::Region> , ()>
-> Result<Vec<ty::Region>, Option<Vec<(String, uint)>>>
{
Ok(Vec::from_elem(count, self.default))
}
@ -109,7 +132,7 @@ impl RegionScope for BindingRscope {
fn anon_regions(&self,
_: Span,
count: uint)
-> Result<Vec<ty::Region> , ()>
-> Result<Vec<ty::Region>, Option<Vec<(String, uint)>>>
{
Ok(Vec::from_fn(count, |_| self.next_region()))
}

View File

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

View File

@ -13,13 +13,13 @@ struct A { a: int, b: Box<int> }
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() {

View File

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

View File

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

View File

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

View File

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

View File

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