Skip to content

error message when trying to move from an Rc or Arc is ungreat #54703

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/doc/unstable-book/src/language-features/lang-items.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,3 +311,5 @@ the source code.
- `freeze`: `libcore/marker.rs`
- `debug_trait`: `libcore/fmt/mod.rs`
- `non_zero`: `libcore/nonzero.rs`
- `arc`: `liballoc/sync.rs`
- `rc`: `liballoc/rc.rs`
1 change: 1 addition & 0 deletions src/liballoc/rc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ struct RcBox<T: ?Sized> {
/// type `T`.
///
/// [get_mut]: #method.get_mut
#[cfg_attr(all(not(stage0), not(test)), lang = "rc")]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Rc<T: ?Sized> {
ptr: NonNull<RcBox<T>>,
Expand Down
1 change: 1 addition & 0 deletions src/liballoc/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ const MAX_REFCOUNT: usize = (isize::MAX) as usize;
/// counting in general.
///
/// [rc_examples]: ../../std/rc/index.html#examples
#[cfg_attr(all(not(stage0), not(test)), lang = "arc")]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Arc<T: ?Sized> {
ptr: NonNull<ArcInner<T>>,
Expand Down
3 changes: 3 additions & 0 deletions src/librustc/middle/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,9 @@ language_item_table! {
AlignOffsetLangItem, "align_offset", align_offset_fn;

TerminationTraitLangItem, "termination", termination;

Arc, "arc", arc;
Rc, "rc", rc;
}

impl<'a, 'tcx, 'gcx> TyCtxt<'a, 'tcx, 'gcx> {
Expand Down
22 changes: 21 additions & 1 deletion src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1694,9 +1694,13 @@ bitflags! {
const IS_FUNDAMENTAL = 1 << 2;
const IS_UNION = 1 << 3;
const IS_BOX = 1 << 4;
/// Indicates whether the type is an `Arc`.
const IS_ARC = 1 << 5;
/// Indicates whether the type is an `Rc`.
const IS_RC = 1 << 6;
/// Indicates whether the variant list of this ADT is `#[non_exhaustive]`.
/// (i.e., this flag is never set unless this ADT is an enum).
const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 5;
const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 7;
}
}

Expand Down Expand Up @@ -2016,6 +2020,12 @@ impl<'a, 'gcx, 'tcx> AdtDef {
if Some(did) == tcx.lang_items().owned_box() {
flags = flags | AdtFlags::IS_BOX;
}
if Some(did) == tcx.lang_items().arc() {
flags = flags | AdtFlags::IS_ARC;
}
if Some(did) == tcx.lang_items().rc() {
flags = flags | AdtFlags::IS_RC;
}
if kind == AdtKind::Enum && tcx.has_attr(did, "non_exhaustive") {
debug!("found non-exhaustive variant list for {:?}", did);
flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE;
Expand Down Expand Up @@ -2094,6 +2104,16 @@ impl<'a, 'gcx, 'tcx> AdtDef {
self.flags.intersects(AdtFlags::IS_PHANTOM_DATA)
}

/// Returns `true` if this is `Arc<T>`.
pub fn is_arc(&self) -> bool {
self.flags.intersects(AdtFlags::IS_ARC)
}

/// Returns `true` if this is `Rc<T>`.
pub fn is_rc(&self) -> bool {
self.flags.intersects(AdtFlags::IS_RC)
}

/// Returns true if this is Box<T>.
#[inline]
pub fn is_box(&self) -> bool {
Expand Down
16 changes: 16 additions & 0 deletions src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1598,6 +1598,22 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
}
}

/// Returns `true` if this type is an `Arc<T>`.
pub fn is_arc(&self) -> bool {
match self.sty {
Adt(def, _) => def.is_arc(),
_ => false,
}
}

/// Returns `true` if this type is an `Rc<T>`.
pub fn is_rc(&self) -> bool {
match self.sty {
Adt(def, _) => def.is_rc(),
_ => false,
}
}

pub fn is_box(&self) -> bool {
match self.sty {
Adt(def, _) => def.is_box(),
Expand Down
123 changes: 118 additions & 5 deletions src/librustc_mir/borrow_check/move_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@
// except according to those terms.

use core::unicode::property::Pattern_White_Space;
use std::fmt::{self, Display};

use rustc::mir::*;
use rustc::ty;
use rustc_errors::{DiagnosticBuilder,Applicability};
use syntax_pos::Span;

use borrow_check::MirBorrowckCtxt;
use borrow_check::prefixes::PrefixSet;
use dataflow::move_paths::{IllegalMoveOrigin, IllegalMoveOriginKind};
use dataflow::move_paths::{LookupResult, MoveError, MovePathIndex};
use dataflow::move_paths::{
IllegalMoveOrigin, IllegalMoveOriginKind, InitLocation,
LookupResult, MoveError, MovePathIndex,
};
use util::borrowck_errors::{BorrowckErrors, Origin};

// Often when desugaring a pattern match we may have many individual moves in
Expand Down Expand Up @@ -61,6 +65,22 @@ enum GroupedMoveError<'tcx> {
},
}

enum BorrowedContentSource {
Arc,
Rc,
Other,
}

impl Display for BorrowedContentSource {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
BorrowedContentSource::Arc => write!(f, "an `Arc`"),
BorrowedContentSource::Rc => write!(f, "an `Rc`"),
BorrowedContentSource::Other => write!(f, "borrowed content"),
}
}
}

impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
pub(crate) fn report_move_errors(&mut self, move_errors: Vec<(Place<'tcx>, MoveError<'tcx>)>) {
let grouped_errors = self.group_move_errors(move_errors);
Expand Down Expand Up @@ -305,9 +325,12 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {

diag
}
_ => self.infcx.tcx.cannot_move_out_of(
span, "borrowed content", origin
),
_ => {
let source = self.borrowed_content_source(place);
self.infcx.tcx.cannot_move_out_of(
span, &format!("{}", source), origin
)
},
}
}
IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
Expand Down Expand Up @@ -471,4 +494,94 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
);
}
}

fn borrowed_content_source(&self, place: &Place<'tcx>) -> BorrowedContentSource {
// Look up the provided place and work out the move path index for it,
// we'll use this to work back through where this value came from and check whether it
// was originally part of an `Rc` or `Arc`.
let initial_mpi = match self.move_data.rev_lookup.find(place) {
LookupResult::Exact(mpi) | LookupResult::Parent(Some(mpi)) => mpi,
_ => return BorrowedContentSource::Other,
};

let mut queue = vec![initial_mpi];
let mut visited = Vec::new();
debug!("borrowed_content_source: queue={:?}", queue);
while let Some(mpi) = queue.pop() {
debug!(
"borrowed_content_source: mpi={:?} queue={:?} visited={:?}",
mpi, queue, visited
);

// Don't visit the same path twice.
if visited.contains(&mpi) {
continue;
}
visited.push(mpi);

for i in &self.move_data.init_path_map[mpi] {
let init = &self.move_data.inits[*i];
debug!("borrowed_content_source: init={:?}", init);
// We're only interested in statements that initialized a value, not the
// initializations from arguments.
let loc = match init.location {
InitLocation::Statement(stmt) => stmt,
_ => continue,
};

let bbd = &self.mir[loc.block];
let is_terminator = bbd.statements.len() == loc.statement_index;
debug!("borrowed_content_source: loc={:?} is_terminator={:?}", loc, is_terminator);
if !is_terminator {
let stmt = &bbd.statements[loc.statement_index];
debug!("borrowed_content_source: stmt={:?}", stmt);
// We're only interested in assignments (in particular, where the
// assignment came from - was it an `Rc` or `Arc`?).
if let StatementKind::Assign(_, box Rvalue::Ref(_, _, source)) = &stmt.kind {
let ty = source.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx);
let ty = match ty.sty {
ty::TyKind::Ref(_, ty, _) => ty,
_ => ty,
};
debug!("borrowed_content_source: ty={:?}", ty);

if ty.is_arc() {
return BorrowedContentSource::Arc;
} else if ty.is_rc() {
return BorrowedContentSource::Rc;
} else {
queue.push(init.path);
}
}
} else if let Some(Terminator {
kind: TerminatorKind::Call { args, .. },
..
}) = &bbd.terminator {
for arg in args {
let source = match arg {
Operand::Copy(place) | Operand::Move(place) => place,
_ => continue,
};

let ty = source.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx);
let ty = match ty.sty {
ty::TyKind::Ref(_, ty, _) => ty,
_ => ty,
};
debug!("borrowed_content_source: ty={:?}", ty);

if ty.is_arc() {
return BorrowedContentSource::Arc;
} else if ty.is_rc() {
return BorrowedContentSource::Rc;
} else {
queue.push(init.path);
}
}
}
}
}

BorrowedContentSource::Other
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error[E0507]: cannot move out of an `Rc`
--> $DIR/borrowck-move-out-of-overloaded-auto-deref.rs:17:14
|
LL | let _x = Rc::new(vec![1, 2]).into_iter();
| ^^^^^^^^^^^^^^^^^^^ cannot move out of an `Rc`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0507`.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error[E0507]: cannot move out of borrowed content
error[E0507]: cannot move out of an `Rc`
--> $DIR/borrowck-move-out-of-overloaded-auto-deref.rs:17:14
|
LL | let _x = Rc::new(vec![1, 2]).into_iter();
| ^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content
| ^^^^^^^^^^^^^^^^^^^ cannot move out of an `Rc`

error: aborting due to previous error

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
error[E0507]: cannot move out of borrowed content
error[E0507]: cannot move out of an `Rc`
--> $DIR/borrowck-move-out-of-overloaded-deref.rs:14:14
|
LL | let _x = *Rc::new("hi".to_string());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| cannot move out of borrowed content
| cannot move out of an `Rc`
| help: consider removing the `*`: `Rc::new("hi".to_string())`

error: aborting due to previous error
Expand Down
24 changes: 24 additions & 0 deletions src/test/ui/nll/issue-52086.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2017 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.

#![feature(nll)]

use std::rc::Rc;
use std::sync::Arc;

struct Bar { field: Vec<i32> }

fn main() {
let x = Rc::new(Bar { field: vec![] });
drop(x.field);

let y = Arc::new(Bar { field: vec![] });
drop(y.field);
}
15 changes: 15 additions & 0 deletions src/test/ui/nll/issue-52086.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error[E0507]: cannot move out of an `Rc`
--> $DIR/issue-52086.rs:20:10
|
LL | drop(x.field);
| ^^^^^^^ cannot move out of an `Rc`

error[E0507]: cannot move out of an `Arc`
--> $DIR/issue-52086.rs:23:10
|
LL | drop(y.field);
| ^^^^^^^ cannot move out of an `Arc`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0507`.
4 changes: 2 additions & 2 deletions src/test/ui/nll/move-errors.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ LL | let s = **r;
| cannot move out of borrowed content
| help: consider removing the `*`: `*r`

error[E0507]: cannot move out of borrowed content
error[E0507]: cannot move out of an `Rc`
--> $DIR/move-errors.rs:40:13
|
LL | let s = *r;
| ^^
| |
| cannot move out of borrowed content
| cannot move out of an `Rc`
| help: consider removing the `*`: `r`

error[E0508]: cannot move out of type `[A; 1]`, a non-copy array
Expand Down