diff --git a/src/doc/unstable-book/src/language-features/lang-items.md b/src/doc/unstable-book/src/language-features/lang-items.md index bac619fd4a30d..b16739b4743a9 100644 --- a/src/doc/unstable-book/src/language-features/lang-items.md +++ b/src/doc/unstable-book/src/language-features/lang-items.md @@ -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` diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 9d951691a34e7..915b8e7787e99 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -282,6 +282,7 @@ struct RcBox { /// 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 { ptr: NonNull>, diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs index 116393bdad195..9e245fbd7bbe5 100644 --- a/src/liballoc/sync.rs +++ b/src/liballoc/sync.rs @@ -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 { ptr: NonNull>, diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index bfde4e4a3aed8..ef70a4cc45d56 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -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> { diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index b2281691bd660..633551aed18a7 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -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; } } @@ -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; @@ -2094,6 +2104,16 @@ impl<'a, 'gcx, 'tcx> AdtDef { self.flags.intersects(AdtFlags::IS_PHANTOM_DATA) } + /// Returns `true` if this is `Arc`. + pub fn is_arc(&self) -> bool { + self.flags.intersects(AdtFlags::IS_ARC) + } + + /// Returns `true` if this is `Rc`. + pub fn is_rc(&self) -> bool { + self.flags.intersects(AdtFlags::IS_RC) + } + /// Returns true if this is Box. #[inline] pub fn is_box(&self) -> bool { diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 6c40dd8923916..4f80bf45ab1f9 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1598,6 +1598,22 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } + /// Returns `true` if this type is an `Arc`. + pub fn is_arc(&self) -> bool { + match self.sty { + Adt(def, _) => def.is_arc(), + _ => false, + } + } + + /// Returns `true` if this type is an `Rc`. + 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(), diff --git a/src/librustc_mir/borrow_check/move_errors.rs b/src/librustc_mir/borrow_check/move_errors.rs index 1c55806872d57..693cfea3c95f0 100644 --- a/src/librustc_mir/borrow_check/move_errors.rs +++ b/src/librustc_mir/borrow_check/move_errors.rs @@ -9,6 +9,8 @@ // 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}; @@ -16,8 +18,10 @@ 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 @@ -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); @@ -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 } => { @@ -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 + } } diff --git a/src/test/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.ast.nll.stderr b/src/test/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.ast.nll.stderr new file mode 100644 index 0000000000000..d58beabb3038a --- /dev/null +++ b/src/test/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.ast.nll.stderr @@ -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`. diff --git a/src/test/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.mir.stderr b/src/test/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.mir.stderr index 94f7eba63a1ca..d58beabb3038a 100644 --- a/src/test/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.mir.stderr +++ b/src/test/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.mir.stderr @@ -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 diff --git a/src/test/ui/borrowck/borrowck-move-out-of-overloaded-deref.nll.stderr b/src/test/ui/borrowck/borrowck-move-out-of-overloaded-deref.nll.stderr index b9c47e6e8cfe9..c9c8cf104ce2e 100644 --- a/src/test/ui/borrowck/borrowck-move-out-of-overloaded-deref.nll.stderr +++ b/src/test/ui/borrowck/borrowck-move-out-of-overloaded-deref.nll.stderr @@ -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 diff --git a/src/test/ui/nll/issue-52086.rs b/src/test/ui/nll/issue-52086.rs new file mode 100644 index 0000000000000..248f4ba066023 --- /dev/null +++ b/src/test/ui/nll/issue-52086.rs @@ -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 or the MIT license +// , 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 } + +fn main() { + let x = Rc::new(Bar { field: vec![] }); + drop(x.field); + + let y = Arc::new(Bar { field: vec![] }); + drop(y.field); +} diff --git a/src/test/ui/nll/issue-52086.stderr b/src/test/ui/nll/issue-52086.stderr new file mode 100644 index 0000000000000..1455c49ea161c --- /dev/null +++ b/src/test/ui/nll/issue-52086.stderr @@ -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`. diff --git a/src/test/ui/nll/move-errors.stderr b/src/test/ui/nll/move-errors.stderr index 592768363096c..8b8268b5b2c49 100644 --- a/src/test/ui/nll/move-errors.stderr +++ b/src/test/ui/nll/move-errors.stderr @@ -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