From 9ca1009fc18c2f2cecfd23510959688cbf0ad41d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 7 Mar 2017 16:32:37 +0100 Subject: [PATCH] Immovable types prototype where the Move trait is builtin --- .../language-features/optin-builtin-traits.md | 6 +- src/liballoc/boxed.rs | 33 ++- src/liballoc/lib.rs | 1 + src/libcore/cell.rs | 34 +++ src/libcore/default.rs | 15 +- src/libcore/marker.rs | 77 +++++ src/libcore/nonzero.rs | 12 + src/libcore/ops/deref.rs | 8 + src/libcore/ops/function.rs | 44 +++ src/libcore/ptr.rs | 53 +++- src/librustc/dep_graph/dep_node.rs | 3 + src/librustc/hir/intravisit.rs | 4 +- src/librustc/hir/lowering.rs | 9 +- src/librustc/hir/mod.rs | 2 +- src/librustc/hir/print.rs | 11 +- .../error_reporting/different_lifetimes.rs | 2 +- src/librustc/middle/lang_items.rs | 3 + src/librustc/middle/resolve_lifetime.rs | 9 +- src/librustc/mir/mod.rs | 9 + src/librustc/traits/error_reporting.rs | 6 +- src/librustc/traits/select.rs | 97 +++++++ src/librustc/traits/specialize/mod.rs | 20 +- src/librustc/ty/maps/config.rs | 6 + src/librustc/ty/maps/mod.rs | 8 + src/librustc/ty/maps/plumbing.rs | 3 + src/librustc/ty/mod.rs | 170 ++++++++++- src/librustc/ty/util.rs | 23 ++ src/librustc/util/ppaux.rs | 14 +- src/librustc_driver/driver.rs | 4 + src/librustc_mir/borrow_check.rs | 2 +- src/librustc_mir/dataflow/impls/mod.rs | 4 + .../dataflow/impls/observed_lvals.rs | 267 ++++++++++++++++++ src/librustc_mir/dataflow/mod.rs | 2 + .../dataflow/move_paths/builder.rs | 26 +- src/librustc_mir/dataflow/move_paths/mod.rs | 28 +- src/librustc_mir/diagnostics.rs | 2 + src/librustc_mir/transform/elaborate_drops.rs | 2 +- src/librustc_mir/transform/mod.rs | 3 + src/librustc_mir/transform/move_check.rs | 137 +++++++++ src/librustc_mir/transform/rustc_peek.rs | 2 +- src/librustc_passes/ast_validation.rs | 25 +- src/librustc_typeck/astconv.rs | 51 +++- src/librustc_typeck/check/mod.rs | 2 +- src/librustc_typeck/coherence/mod.rs | 11 +- src/librustc_typeck/collect.rs | 89 +++--- src/librustc_typeck/diagnostics.rs | 5 +- src/librustdoc/clean/mod.rs | 67 ++++- src/libstd/lib.rs | 1 + src/libstd/panic.rs | 22 ++ .../compile-fail/auto-impl-future-compat.rs | 6 +- .../auxiliary/tdticc_coherence_lib.rs | 5 +- src/test/compile-fail/immovable-types.rs | 66 +++++ src/test/compile-fail/maybe-bounds-where.rs | 3 +- src/test/compile-fail/maybe-bounds.rs | 9 +- .../move-trait-leaks-in-impl-trait.rs | 33 +++ .../no-move-immovable-box-content.rs | 29 ++ src/test/compile-fail/no-move-observed.rs | 22 ++ src/test/compile-fail/phantom-oibit.rs | 4 +- src/test/compile-fail/privacy-sanity.rs | 9 +- .../trait-static-method-generic-inference.rs | 2 +- ...-default-trait-impl-constituent-types-2.rs | 5 +- ...ck-default-trait-impl-constituent-types.rs | 5 +- .../typeck-default-trait-impl-negation.rs | 7 +- .../typeck-default-trait-impl-precedence.rs | 5 +- src/test/run-pass/auto-traits.rs | 8 +- src/test/run-pass/issue-29516.rs | 5 +- src/test/run-pass/static-immovable.rs | 17 ++ .../rustdoc/auxiliary/rustdoc-default-impl.rs | 5 +- .../rustdoc-impl-parts-crosscrate.rs | 5 +- src/test/rustdoc/impl-parts.rs | 5 +- src/test/ui/impl-trait/equality-recursive.rs | 26 ++ .../ui/impl-trait/equality-recursive.stderr | 32 +++ src/test/ui/impl-trait/equality.rs | 9 - src/test/ui/impl-trait/equality.stderr | 26 +- .../ui/on-unimplemented/multiple-impls.stderr | 26 +- src/test/ui/on-unimplemented/on-impl.stderr | 10 +- 76 files changed, 1599 insertions(+), 219 deletions(-) create mode 100644 src/librustc_mir/dataflow/impls/observed_lvals.rs create mode 100644 src/librustc_mir/transform/move_check.rs create mode 100644 src/test/compile-fail/immovable-types.rs create mode 100644 src/test/compile-fail/move-trait-leaks-in-impl-trait.rs create mode 100644 src/test/compile-fail/no-move-immovable-box-content.rs create mode 100644 src/test/compile-fail/no-move-observed.rs create mode 100644 src/test/run-pass/static-immovable.rs create mode 100644 src/test/ui/impl-trait/equality-recursive.rs create mode 100644 src/test/ui/impl-trait/equality-recursive.stderr diff --git a/src/doc/unstable-book/src/language-features/optin-builtin-traits.md b/src/doc/unstable-book/src/language-features/optin-builtin-traits.md index 97b57c690fcc8..afe0988d754e3 100644 --- a/src/doc/unstable-book/src/language-features/optin-builtin-traits.md +++ b/src/doc/unstable-book/src/language-features/optin-builtin-traits.md @@ -22,9 +22,11 @@ impl !Type for Trait Example: ```rust -#![feature(optin_builtin_traits)] +#![feature(optin_builtin_traits, immovable_types)] -auto trait Valid {} +use std::marker::Move; + +auto trait Valid: ?Move {} struct True; struct False; diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 2226cee6e3684..a349b503298e4 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -64,6 +64,8 @@ use core::cmp::Ordering; use core::fmt; use core::hash::{self, Hash, Hasher}; use core::iter::FusedIterator; +#[cfg(not(stage0))] +use core::marker::Move; use core::marker::{self, Unsize}; use core::mem; use core::ops::{CoerceUnsized, Deref, DerefMut, Generator, GeneratorState}; @@ -103,13 +105,21 @@ pub struct ExchangeHeapSingleton { _force_singleton: (), } +/// docs +#[cfg(stage0)] +#[lang = "owned_box"] +#[fundamental] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Box(Unique); + /// A pointer type for heap allocation. /// /// See the [module-level documentation](../../std/boxed/index.html) for more. +#[cfg(not(stage0))] #[lang = "owned_box"] #[fundamental] #[stable(feature = "rust1", since = "1.0.0")] -pub struct Box(Unique); +pub struct Box(Unique); /// `IntermediateBox` represents uninitialized backing storage for `Box`. /// @@ -377,12 +387,21 @@ impl Box { } #[stable(feature = "rust1", since = "1.0.0")] +#[cfg(stage0)] unsafe impl<#[may_dangle] T: ?Sized> Drop for Box { fn drop(&mut self) { // FIXME: Do nothing, drop is currently performed by compiler. } } +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg(not(stage0))] +unsafe impl<#[may_dangle] T: ?Sized+?Move> Drop for Box { + fn drop(&mut self) { + // FIXME: Do nothing, drop is currently performed by compiler. + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Default for Box { /// Creates a `Box`, with the `Default` value for T. @@ -752,6 +771,18 @@ impl FusedIterator for Box {} /// } /// } /// ``` +#[cfg(not(stage0))] +#[rustc_paren_sugar] +#[unstable(feature = "fnbox", + reason = "will be deprecated if and when `Box` becomes usable", issue = "28796")] +pub trait FnBox { + type Output: ?Move; + + fn call_box(self: Box, args: A) -> Self::Output; +} + +/// docs +#[cfg(stage0)] #[rustc_paren_sugar] #[unstable(feature = "fnbox", reason = "will be deprecated if and when `Box` becomes usable", issue = "28796")] diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 3cc3ea467966b..78445441b4edd 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -116,6 +116,7 @@ #![feature(slice_rsplit)] #![feature(specialization)] #![feature(staged_api)] +#![cfg_attr(not(stage0), feature(immovable_types))] #![feature(str_internals)] #![feature(trusted_len)] #![feature(unboxed_closures)] diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index d02576ae54607..ad023867664dc 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -185,6 +185,8 @@ use marker::Unsize; use mem; use ops::{Deref, DerefMut, CoerceUnsized}; use ptr; +#[cfg(not(stage0))] +use marker::Move; /// A mutable memory location. /// @@ -1292,3 +1294,35 @@ fn assert_coerce_unsized(a: UnsafeCell<&i32>, b: Cell<&i32>, c: RefCell<&i32>) { let _: Cell<&Send> = b; let _: RefCell<&Send> = c; } + +/// A cell that is always movable, even if the interior type is immovable. +/// This prevents pointers to the inner type which means it can never be observed. +#[cfg(not(stage0))] +#[unstable(feature = "immovable_types", issue = "0")] +#[lang = "movable_cell"] +#[allow(missing_debug_implementations)] +#[derive(Default)] +pub struct MovableCell { + value: T, +} + +#[unstable(feature = "immovable_types", issue = "0")] +#[cfg(not(stage0))] +impl MovableCell { + /// Creates a new MovableCell. + pub const fn new(value: T) -> Self { + MovableCell { + value: value, + } + } + + /// Extracts the inner value. + pub fn into_inner(self) -> T { + self.value + } + + /// Replaces the inner value. + pub fn replace(&mut self, new_value: T) -> T { + mem::replace(self, MovableCell::new(new_value)).into_inner() + } +} \ No newline at end of file diff --git a/src/libcore/default.rs b/src/libcore/default.rs index ab36e29b1e1d4..d5211b4ec484f 100644 --- a/src/libcore/default.rs +++ b/src/libcore/default.rs @@ -12,6 +12,9 @@ #![stable(feature = "rust1", since = "1.0.0")] +#[cfg(not(stage0))] +use marker::Move; + /// A trait for giving a type a useful default value. /// /// Sometimes, you want to fall back to some kind of default value, and @@ -90,8 +93,9 @@ /// bar: f32, /// } /// ``` +#[cfg(not(stage0))] #[stable(feature = "rust1", since = "1.0.0")] -pub trait Default: Sized { +pub trait Default: Sized + ?Move { /// Returns the "default value" for a type. /// /// Default values are often some kind of initial value, identity value, or anything else that @@ -125,6 +129,15 @@ pub trait Default: Sized { fn default() -> Self; } +/// docs +#[cfg(stage0)] +#[stable(feature = "rust1", since = "1.0.0")] +pub trait Default: Sized { + /// docs + #[stable(feature = "rust1", since = "1.0.0")] + fn default() -> Self; +} + macro_rules! default_impl { ($t:ty, $v:expr, $doc:tt) => { #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 0a8127f4ce4fe..5954c8bc1f42b 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -41,6 +41,16 @@ use hash::Hasher; #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(stage0, lang = "send")] #[rustc_on_unimplemented = "`{Self}` cannot be sent between threads safely"] +#[cfg(not(stage0))] +pub unsafe trait Send: ?Move { + // empty. +} + +/// docs +#[stable(feature = "rust1", since = "1.0.0")] +#[lang = "send"] +#[rustc_on_unimplemented = "`{Self}` cannot be sent between threads safely"] +#[cfg(stage0)] pub unsafe trait Send { // empty. } @@ -50,11 +60,20 @@ pub unsafe trait Send { #[allow(auto_impl)] unsafe impl Send for .. { } +#[cfg(stage0)] #[stable(feature = "rust1", since = "1.0.0")] impl !Send for *const T { } +#[cfg(stage0)] #[stable(feature = "rust1", since = "1.0.0")] impl !Send for *mut T { } +#[cfg(not(stage0))] +#[stable(feature = "rust1", since = "1.0.0")] +impl !Send for *const T { } +#[cfg(not(stage0))] +#[stable(feature = "rust1", since = "1.0.0")] +impl !Send for *mut T { } + /// Types with a constant size known at compile time. /// /// All type parameters have an implicit bound of `Sized`. The special syntax @@ -92,10 +111,37 @@ impl !Send for *mut T { } #[lang = "sized"] #[rustc_on_unimplemented = "`{Self}` does not have a constant size known at compile-time"] #[fundamental] // for Default, for example, which requires that `[T]: !Default` be evaluatable +#[cfg(not(stage0))] +pub trait Sized: ?Move { + // Empty. +} + +/// docs +#[stable(feature = "rust1", since = "1.0.0")] +#[lang = "sized"] +#[rustc_on_unimplemented = "`{Self}` does not have a constant size known at compile-time"] +#[fundamental] // for Default, for example, which requires that `[T]: !Default` be evaluatable +#[cfg(stage0)] pub trait Sized { // Empty. } +/// Types that can be moved after being borrowed. +#[cfg(not(stage0))] +#[lang = "move"] +#[unstable(feature = "immovable_types", issue = "0")] +pub unsafe trait Move: ?Move { + // Empty. +} + +/// A zero-sized struct which is immovable. +#[cfg(not(stage0))] +#[lang = "immovable"] +#[unstable(feature = "immovable_types", issue = "0")] +#[allow(missing_debug_implementations)] +#[derive(Default)] +pub struct Immovable; + /// Types that can be "unsized" to a dynamically-sized type. /// /// For example, the sized array type `[i8; 2]` implements `Unsize<[i8]>` and @@ -346,6 +392,16 @@ pub trait Copy : Clone { #[stable(feature = "rust1", since = "1.0.0")] #[lang = "sync"] #[rustc_on_unimplemented = "`{Self}` cannot be shared between threads safely"] +#[cfg(not(stage0))] +pub unsafe trait Sync: ?Move { + // Empty +} + +/// docs +#[stable(feature = "rust1", since = "1.0.0")] +#[lang = "sync"] +#[rustc_on_unimplemented = "`{Self}` cannot be shared between threads safely"] +#[cfg(stage0)] pub unsafe trait Sync { // Empty } @@ -355,11 +411,20 @@ pub unsafe trait Sync { #[allow(auto_impl)] unsafe impl Sync for .. { } +#[cfg(stage0)] #[stable(feature = "rust1", since = "1.0.0")] impl !Sync for *const T { } +#[cfg(stage0)] #[stable(feature = "rust1", since = "1.0.0")] impl !Sync for *mut T { } +#[cfg(not(stage0))] +#[stable(feature = "rust1", since = "1.0.0")] +impl !Sync for *const T { } +#[cfg(not(stage0))] +#[stable(feature = "rust1", since = "1.0.0")] +impl !Sync for *mut T { } + macro_rules! impls{ ($t: ident) => ( #[stable(feature = "rust1", since = "1.0.0")] @@ -546,6 +611,13 @@ macro_rules! impls{ /// as not to indicate ownership. /// /// [drop check]: ../../nomicon/dropck.html +#[cfg(not(stage0))] +#[lang = "phantom_data"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct PhantomData; + +/// docs +#[cfg(stage0)] #[lang = "phantom_data"] #[stable(feature = "rust1", since = "1.0.0")] pub struct PhantomData; @@ -564,6 +636,11 @@ mod impls { /// This affects, for example, whether a `static` of that type is /// placed in read-only static memory or writable static memory. #[lang = "freeze"] +#[cfg(not(stage0))] +unsafe trait Freeze: ?Move {} + +#[lang = "freeze"] +#[cfg(stage0)] unsafe trait Freeze {} #[allow(unknown_lints)] diff --git a/src/libcore/nonzero.rs b/src/libcore/nonzero.rs index edc0104f46b5c..e90d3b4d6c0d7 100644 --- a/src/libcore/nonzero.rs +++ b/src/libcore/nonzero.rs @@ -14,6 +14,8 @@ issue = "27730")] use ops::CoerceUnsized; +#[cfg(not(stage0))] +use marker::Move; /// Unsafe trait to indicate what types are usable with the NonZero struct pub unsafe trait Zeroable { @@ -24,6 +26,7 @@ pub unsafe trait Zeroable { macro_rules! impl_zeroable_for_pointer_types { ( $( $Ptr: ty )+ ) => { $( + #[cfg(stage0)] /// For fat pointers to be considered "zero", only the "data" part needs to be null. unsafe impl Zeroable for $Ptr { #[inline] @@ -32,6 +35,15 @@ macro_rules! impl_zeroable_for_pointer_types { (*self as *mut u8).is_null() } } + #[cfg(not(stage0))] + /// For fat pointers to be considered "zero", only the "data" part needs to be null. + unsafe impl Zeroable for $Ptr { + #[inline] + fn is_zero(&self) -> bool { + // Cast because `is_null` is only available on thin pointers + (*self as *mut u8).is_null() + } + } )+ } } diff --git a/src/libcore/ops/deref.rs b/src/libcore/ops/deref.rs index 80c48c7b28efd..fd9d4b5e0cf4f 100644 --- a/src/libcore/ops/deref.rs +++ b/src/libcore/ops/deref.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[cfg(not(stage0))] +use marker::Move; + /// Used for immutable dereferencing operations, like `*v`. /// /// In addition to being used for explicit dereferencing operations with the @@ -72,7 +75,12 @@ pub trait Deref { /// The resulting type after dereferencing. #[stable(feature = "rust1", since = "1.0.0")] + #[cfg(stage0)] type Target: ?Sized; + /// The resulting type after dereferencing + #[stable(feature = "rust1", since = "1.0.0")] + #[cfg(not(stage0))] + type Target: ?Sized+?Move; /// Dereferences the value. #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/ops/function.rs b/src/libcore/ops/function.rs index d10fcb86b2411..74d8162ccb2b5 100644 --- a/src/libcore/ops/function.rs +++ b/src/libcore/ops/function.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[cfg(not(stage0))] +use marker::Move; + /// The version of the call operator that takes an immutable receiver. /// /// Instances of `Fn` can be called repeatedly without mutating state. @@ -67,6 +70,18 @@ #[stable(feature = "rust1", since = "1.0.0")] #[rustc_paren_sugar] #[fundamental] // so that regex can rely that `&str: !FnMut` +#[cfg(not(stage0))] +pub trait Fn : FnMut { + /// This is called when the call operator is used. + #[unstable(feature = "fn_traits", issue = "29625")] + extern "rust-call" fn call(&self, args: Args) -> Self::Output; +} +/// docs +#[lang = "fn"] +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_paren_sugar] +#[fundamental] // so that regex can rely that `&str: !FnMut` +#[cfg(stage0)] pub trait Fn : FnMut { /// Performs the call operation. #[unstable(feature = "fn_traits", issue = "29625")] @@ -140,6 +155,18 @@ pub trait Fn : FnMut { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_paren_sugar] #[fundamental] // so that regex can rely that `&str: !FnMut` +#[cfg(not(stage0))] +pub trait FnMut : FnOnce { + /// This is called when the call operator is used. + #[unstable(feature = "fn_traits", issue = "29625")] + extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output; +} +/// docs +#[lang = "fn_mut"] +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_paren_sugar] +#[fundamental] // so that regex can rely that `&str: !FnMut` +#[cfg(stage0)] pub trait FnMut : FnOnce { /// Performs the call operation. #[unstable(feature = "fn_traits", issue = "29625")] @@ -213,6 +240,23 @@ pub trait FnMut : FnOnce { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_paren_sugar] #[fundamental] // so that regex can rely that `&str: !FnMut` +#[cfg(not(stage0))] +pub trait FnOnce { + /// The returned type after the call operator is used. + #[stable(feature = "fn_once_output", since = "1.12.0")] + type Output: ?Move; + + /// This is called when the call operator is used. + #[unstable(feature = "fn_traits", issue = "29625")] + extern "rust-call" fn call_once(self, args: Args) -> Self::Output; +} + +/// docs +#[lang = "fn_once"] +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_paren_sugar] +#[fundamental] // so that regex can rely that `&str: !FnMut` +#[cfg(stage0)] pub trait FnOnce { /// The returned type after the call operator is used. #[stable(feature = "fn_once_output", since = "1.12.0")] diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 126558e3025d5..b94b5e4366387 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -21,6 +21,8 @@ use intrinsics; use ops::CoerceUnsized; use fmt; use hash; +#[cfg(not(stage0))] +use marker::Move; use marker::{PhantomData, Unsize}; use mem; use nonzero::NonZero; @@ -2065,24 +2067,51 @@ impl *mut T { } // Equality for pointers +#[cfg(stage0)] #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for *const T { #[inline] fn eq(&self, other: &*const T) -> bool { *self == *other } } +#[cfg(stage0)] #[stable(feature = "rust1", since = "1.0.0")] impl Eq for *const T {} +#[cfg(stage0)] #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for *mut T { #[inline] fn eq(&self, other: &*mut T) -> bool { *self == *other } } +#[cfg(stage0)] #[stable(feature = "rust1", since = "1.0.0")] impl Eq for *mut T {} +// Equality for pointers +#[cfg(not(stage0))] +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for *const T { + #[inline] + fn eq(&self, other: &*const T) -> bool { *self == *other } +} + +#[cfg(not(stage0))] +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for *const T {} + +#[cfg(not(stage0))] +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for *mut T { + #[inline] + fn eq(&self, other: &*mut T) -> bool { *self == *other } +} + +#[cfg(not(stage0))] +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for *mut T {} + /// Compare raw pointers for equality. /// /// This is the same as using the `==` operator, but less generic: @@ -2293,7 +2322,8 @@ impl PartialOrd for *mut T { #[allow(missing_debug_implementations)] #[unstable(feature = "unique", reason = "needs an RFC to flesh out design", issue = "27730")] -pub struct Unique { +#[cfg(not(stage0))] +pub struct Unique { pointer: NonZero<*const T>, // NOTE: this marker has no consequences for variance, but is necessary // for dropck to understand that we logically own a `T`. @@ -2302,6 +2332,15 @@ pub struct Unique { // /~https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#phantom-data _marker: PhantomData, } +/// docs +#[cfg(stage0)] +#[allow(missing_debug_implementations)] +#[unstable(feature = "unique", reason = "needs an RFC to flesh out design", + issue = "27730")] +pub struct Unique { + pointer: NonZero<*const T>, + _marker: PhantomData, +} /// `Unique` pointers are `Send` if `T` is `Send` because the data they /// reference is unaliased. Note that this aliasing invariant is @@ -2432,7 +2471,8 @@ impl<'a, T: ?Sized> From<&'a T> for Unique { #[allow(missing_debug_implementations)] #[unstable(feature = "shared", reason = "needs an RFC to flesh out design", issue = "27730")] -pub struct Shared { +#[cfg(not(stage0))] +pub struct Shared { pointer: NonZero<*const T>, // NOTE: this marker has no consequences for variance, but is necessary // for dropck to understand that we logically own a `T`. @@ -2441,6 +2481,15 @@ pub struct Shared { // /~https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#phantom-data _marker: PhantomData, } +/// docs +#[cfg(stage0)] +#[allow(missing_debug_implementations)] +#[unstable(feature = "shared", reason = "needs an RFC to flesh out design", + issue = "27730")] +pub struct Shared { + pointer: NonZero<*const T>, + _marker: PhantomData, +} /// `Shared` pointers are not `Send` because the data they reference may be aliased. // NB: This impl is unnecessary, but should provide better error messages. diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 523a244c8361b..b44523c429ec4 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -479,6 +479,7 @@ define_dep_nodes!( <'tcx> [] BorrowCheck(DefId), [] MirBorrowCheck(DefId), [] UnsafetyCheckResult(DefId), + [] MoveCheck(DefId), [] Reachability, [] MirKeys, @@ -509,6 +510,7 @@ define_dep_nodes!( <'tcx> [] IsForeignItem(DefId), [] TypeParamPredicates { item_id: DefId, param_id: DefId }, [] SizedConstraint(DefId), + [] MoveConstraint(DefId), [] DtorckConstraint(DefId), [] AdtDestructor(DefId), [] AssociatedItemDefIds(DefId), @@ -528,6 +530,7 @@ define_dep_nodes!( <'tcx> [] IsCopy { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> }, [] IsSized { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> }, [] IsFreeze { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> }, + [] IsMove { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> }, [] NeedsDrop { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> }, [] Layout { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> }, diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index 84be68cb19765..d51d4fed6fe7b 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -586,9 +586,7 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) { visitor.visit_nested_body(length) } TyTraitObject(ref bounds, ref lifetime) => { - for bound in bounds { - visitor.visit_poly_trait_ref(bound, TraitBoundModifier::None); - } + walk_list!(visitor, visit_ty_param_bound, bounds); visitor.visit_lifetime(lifetime); } TyImplTraitExistential(ref bounds) => { diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 8c9d1a38e7088..8af5d35392ebe 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -750,10 +750,7 @@ impl<'a> LoweringContext<'a> { let mut lifetime_bound = None; let bounds = bounds.iter().filter_map(|bound| { match *bound { - TraitTyParamBound(ref ty, TraitBoundModifier::None) => { - Some(self.lower_poly_trait_ref(ty, itctx)) - } - TraitTyParamBound(_, TraitBoundModifier::Maybe) => None, + TraitTyParamBound(..) => Some(self.lower_ty_param_bound(bound, itctx)), RegionTyParamBound(ref lifetime) => { if lifetime_bound.is_none() { lifetime_bound = Some(self.lower_lifetime(lifetime)); @@ -3139,11 +3136,13 @@ impl<'a> LoweringContext<'a> { span, }; + let bound = hir::TraitTyParamBound(principal, hir::TraitBoundModifier::None); + // The original ID is taken by the `PolyTraitRef`, // so the `Ty` itself needs a different one. id = self.next_id(); - hir::TyTraitObject(hir_vec![principal], self.elided_lifetime(span)) + hir::TyTraitObject(hir_vec![bound], self.elided_lifetime(span)) } else { hir::TyPath(hir::QPath::Resolved(None, path)) } diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 1346685e02afe..ac07ce276b2f8 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1485,7 +1485,7 @@ pub enum Ty_ { TyPath(QPath), /// A trait object type `Bound1 + Bound2 + Bound3` /// where `Bound` is a trait or a lifetime. - TyTraitObject(HirVec, Lifetime), + TyTraitObject(TyParamBounds, Lifetime), /// An exsitentially quantified (there exists a type satisfying) `impl /// Bound1 + Bound2 + Bound3` type where `Bound` is a trait or a lifetime. TyImplTraitExistential(TyParamBounds), diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index 451e870f500dd..49179c2a55f9d 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -406,16 +406,7 @@ impl<'a> State<'a> { self.print_qpath(qpath, false)? } hir::TyTraitObject(ref bounds, ref lifetime) => { - let mut first = true; - for bound in bounds { - self.nbsp()?; - if first { - first = false; - } else { - self.word_space("+")?; - } - self.print_poly_trait_ref(bound)?; - } + self.print_bounds("", &bounds[..])?; if !lifetime.is_elided() { self.word_space("+")?; self.print_lifetime(lifetime)?; diff --git a/src/librustc/infer/error_reporting/different_lifetimes.rs b/src/librustc/infer/error_reporting/different_lifetimes.rs index c64bd610962eb..c196b8872abaa 100644 --- a/src/librustc/infer/error_reporting/different_lifetimes.rs +++ b/src/librustc/infer/error_reporting/different_lifetimes.rs @@ -254,7 +254,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> { hir::TyTraitObject(ref bounds, _) => { for bound in bounds { self.depth += 1; - self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None); + self.visit_ty_param_bound(bound); self.depth -= 1; } } diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 19a43f3b5dd84..d4337660655f2 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -230,6 +230,7 @@ language_item_table! { F64ImplItem, "f64", f64_impl; SizedTraitLangItem, "sized", sized_trait; + MoveTraitLangItem, "move", move_trait; UnsizeTraitLangItem, "unsize", unsize_trait; CopyTraitLangItem, "copy", copy_trait; CloneTraitLangItem, "clone", clone_trait; @@ -266,6 +267,7 @@ language_item_table! { IndexMutTraitLangItem, "index_mut", index_mut_trait; UnsafeCellTypeLangItem, "unsafe_cell", unsafe_cell_type; + MovableCellTypeLangItem, "movable_cell", movable_cell_type; DerefTraitLangItem, "deref", deref_trait; DerefMutTraitLangItem, "deref_mut", deref_mut_trait; @@ -305,6 +307,7 @@ language_item_table! { OwnedBoxLangItem, "owned_box", owned_box; + ImmovableItem, "immovable", immovable; PhantomDataItem, "phantom_data", phantom_data; NonZeroItem, "non_zero", non_zero; diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 8c299612064d5..b15408ecbf260 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -389,7 +389,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } hir::TyTraitObject(ref bounds, ref lifetime) => { for bound in bounds { - self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None); + match *bound { + hir::TraitTyParamBound(ref trait_ref, modifier) => { + self.visit_poly_trait_ref(trait_ref, modifier); + } + _ => panic!(), + } } if lifetime.is_elided() { self.resolve_object_lifetime_default(lifetime) @@ -1230,7 +1235,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } if let hir::TyTraitObject(ref bounds, ref lifetime) = ty.node { for bound in bounds { - self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None); + self.visit_ty_param_bound(bound); } // Stay on the safe side and don't include the object diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 355fb570c000b..c7f78e6162e51 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1707,6 +1707,15 @@ impl Location { dominators.is_dominated_by(other.block, self.block) } } + + pub fn source_info<'tcx>(&self, mir: &Mir<'tcx>) -> SourceInfo { + let block = &mir[self.block]; + if let Some(stmt) = block.statements.get(self.statement_index) { + stmt.source_info + } else { + block.terminator().source_info + } + } } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 7c38cf75b8d5a..a7f8c91f0136f 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -1063,10 +1063,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // has_errors() to be sure that compilation isn't happening // anyway. In that case, why inundate the user. if !self.tcx.sess.has_errors() { - if - self.tcx.lang_items().sized_trait() - .map_or(false, |sized_id| sized_id == trait_ref.def_id()) - { + if self.tcx.lang_items().move_trait() == Some(trait_ref.def_id()) || + self.tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) { self.need_type_info(body_id, span, self_ty); } else { let mut err = struct_span_err!(self.tcx.sess, diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 7716770d318ba..94cbe949d5b4a 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -1348,6 +1348,12 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { let sized_conditions = self.sized_conditions(obligation); self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates)?; + } else if lang_items.move_trait() == Some(def_id) { + // Move is never implementable by end-users, it is + // always automatically computed. + let move_conditions = self.move_conditions(obligation); + self.assemble_builtin_bound_candidates(move_conditions, + &mut candidates)?; } else if lang_items.unsize_trait() == Some(def_id) { self.assemble_candidates_for_unsizing(obligation, &mut candidates); } else { @@ -2054,6 +2060,94 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { } } + fn move_conditions(&mut self, obligation: &TraitObligation<'tcx>) + -> BuiltinImplConditions<'tcx> + { + use self::BuiltinImplConditions::{Ambiguous, None, Never, Where}; + + // NOTE: binder moved to (*) + let self_ty = self.infcx.shallow_resolve( + obligation.predicate.skip_binder().self_ty()); + + match self_ty.sty { + // TyForeign isn't possible to move since you cannot get its size + // Therefore its fine for it to implement Move + ty::TyForeign(..) | + + ty::TyInfer(ty::IntVar(_)) | ty::TyInfer(ty::FloatVar(_)) | + ty::TyUint(_) | ty::TyInt(_) | ty::TyBool | ty::TyFloat(_) | + ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyRawPtr(..) | + ty::TyChar | ty::TyRef(..) | + ty::TyStr | ty::TyNever | + ty::TyError => { + // safe for everything + Where(ty::Binder(Vec::new())) + } + + ty::TySlice(element_ty) | + ty::TyArray(element_ty, _) => { + // (*) binder moved here + Where(ty::Binder(vec![element_ty])) + } + + ty::TyClosure(def_id, substs) => { + Where(ty::Binder(substs.upvar_tys(def_id, self.infcx.tcx).collect())) + } + + ty::TyGenerator(def_id, substs, interior) => { + Where(ty::Binder(substs.upvar_tys(def_id, self.infcx.tcx) + .chain(iter::once(interior.witness)) + .collect())) + } + + ty::TyDynamic(predicate, _) => { + // Trait objects with a Move trait bound are movable + let move_trait = self.infcx.tcx.lang_items().move_trait().unwrap(); + // We can skip the binder here because + // ExistentialPredicate::AutoTrait cannot contain lifetimes + let test = |p| p == &ty::ExistentialPredicate::AutoTrait(move_trait); + if predicate.skip_binder().into_iter().any(test) { + Where(ty::Binder(Vec::new())) + } else { + Never + } + } + + ty::TyTuple(tys, _) => { + Where(ty::Binder(tys.into_iter().map(|t| *t).collect())) + } + + ty::TyAdt(def, substs) => { + if def.is_immovable() { + Never + } else { + let move_constr = def.move_constraint(self.tcx()); + // (*) binder moved here + Where(ty::Binder( + move_constr.iter().map(|ty| ty.subst(self.tcx(), substs)).collect() + )) + } + } + + ty::TyAnon(def_id, substs) => { + // We can resolve the `impl Trait` to its concrete type, + // which enforces a DAG between the functions requiring + // the auto trait bounds in question. + Where(ty::Binder(vec![self.tcx().type_of(def_id).subst(self.tcx(), substs)])) + } + + ty::TyProjection(_) | ty::TyParam(_) => None, + ty::TyInfer(ty::TyVar(_)) => Ambiguous, + + ty::TyInfer(ty::FreshTy(_)) + | ty::TyInfer(ty::FreshIntTy(_)) + | ty::TyInfer(ty::FreshFloatTy(_)) => { + bug!("asked to assemble builtin bounds of unexpected type: {:?}", + self_ty); + } + } + } + fn copy_clone_conditions(&mut self, obligation: &TraitObligation<'tcx>) -> BuiltinImplConditions<'tcx> { @@ -2382,6 +2476,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { _ if Some(trait_def) == lang_items.sized_trait() => { self.sized_conditions(obligation) } + _ if Some(trait_def) == lang_items.move_trait() => { + self.move_conditions(obligation) + } _ if Some(trait_def) == lang_items.copy_trait() => { self.copy_clone_conditions(obligation) } diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs index d8d0715ff3957..0542830692f13 100644 --- a/src/librustc/traits/specialize/mod.rs +++ b/src/librustc/traits/specialize/mod.rs @@ -375,12 +375,15 @@ fn to_pretty_impl_header(tcx: TyCtxt, impl_def_id: DefId) -> Option { let substs = Substs::identity_for_item(tcx, impl_def_id); // FIXME: Currently only handles ?Sized. - // Needs to support ?Move and ?DynSized when they are implemented. - let mut types_without_default_bounds = FxHashSet::default(); + // Needs to support ?DynSized when they are implemented. + let mut types_without_sized = FxHashSet::default(); + let mut types_without_move = FxHashSet::default(); let sized_trait = tcx.lang_items().sized_trait(); + let move_trait = tcx.lang_items().move_trait(); if !substs.is_noop() { - types_without_default_bounds.extend(substs.types()); + types_without_sized.extend(substs.types()); + types_without_move.extend(substs.types()); w.push('<'); w.push_str(&substs.iter().map(|k| k.to_string()).collect::>().join(", ")); w.push('>'); @@ -395,15 +398,22 @@ fn to_pretty_impl_header(tcx: TyCtxt, impl_def_id: DefId) -> Option { for p in predicates { if let Some(poly_trait_ref) = p.to_opt_poly_trait_ref() { if Some(poly_trait_ref.def_id()) == sized_trait { - types_without_default_bounds.remove(poly_trait_ref.self_ty()); + types_without_sized.remove(poly_trait_ref.self_ty()); + continue; + } + if Some(poly_trait_ref.def_id()) == move_trait { + types_without_move.remove(poly_trait_ref.self_ty()); continue; } } pretty_predicates.push(p.to_string()); } - for ty in types_without_default_bounds { + for ty in types_without_sized { pretty_predicates.push(format!("{}: ?Sized", ty)); } + for ty in types_without_move { + pretty_predicates.push(format!("{}: ?Move", ty)); + } if !pretty_predicates.is_empty() { write!(w, "\n where {}", pretty_predicates.join(", ")).unwrap(); } diff --git a/src/librustc/ty/maps/config.rs b/src/librustc/ty/maps/config.rs index 066b80cefa4b5..32abde06415c9 100644 --- a/src/librustc/ty/maps/config.rs +++ b/src/librustc/ty/maps/config.rs @@ -67,6 +67,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::is_freeze_raw<'tcx> { } } +impl<'tcx> QueryDescription<'tcx> for queries::is_move_raw<'tcx> { + fn describe(_tcx: TyCtxt, env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> String { + format!("computing whether `{}` is `Move`", env.value) + } +} + impl<'tcx> QueryDescription<'tcx> for queries::needs_drop_raw<'tcx> { fn describe(_tcx: TyCtxt, env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> String { format!("computing whether `{}` needs drop", env.value) diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index 2f648e8d3ff82..3ce91be82be85 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -104,6 +104,7 @@ define_maps! { <'tcx> [] fn adt_def: AdtDefOfItem(DefId) -> &'tcx ty::AdtDef, [] fn adt_destructor: AdtDestructor(DefId) -> Option, [] fn adt_sized_constraint: SizedConstraint(DefId) -> &'tcx [Ty<'tcx>], + [] fn adt_move_constraint: MoveConstraint(DefId) -> &'tcx [Ty<'tcx>], [] fn adt_dtorck_constraint: DtorckConstraint(DefId) -> ty::DtorckConstraint<'tcx>, /// True if this is a const fn @@ -198,6 +199,8 @@ define_maps! { <'tcx> // FIXME: shouldn't this return a `Result<(), BorrowckErrors>` instead? [] fn mir_borrowck: MirBorrowCheck(DefId) -> (), + [] fn moveck: MoveCheck(DefId) -> (), + /// Gets a complete map from all types to their inherent impls. /// Not meant to be used directly outside of coherence. /// (Defined only for LOCAL_CRATE) @@ -262,6 +265,7 @@ define_maps! { <'tcx> [] fn is_copy_raw: is_copy_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool, [] fn is_sized_raw: is_sized_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool, [] fn is_freeze_raw: is_freeze_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool, + [] fn is_move_raw: is_move_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool, [] fn needs_drop_raw: needs_drop_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool, [] fn layout_raw: layout_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> Result<&'tcx ty::layout::LayoutDetails, @@ -438,6 +442,10 @@ fn is_freeze_dep_node<'tcx>(param_env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepCo DepConstructor::IsFreeze { param_env } } +fn is_move_dep_node<'tcx>(param_env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepConstructor<'tcx> { + DepConstructor::IsMove { param_env } +} + fn needs_drop_dep_node<'tcx>(param_env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepConstructor<'tcx> { DepConstructor::NeedsDrop { param_env } } diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs index 1ca8fc6eb480f..65c3281a8f377 100644 --- a/src/librustc/ty/maps/plumbing.rs +++ b/src/librustc/ty/maps/plumbing.rs @@ -725,6 +725,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, DepKind::IsCopy | DepKind::IsSized | DepKind::IsFreeze | + DepKind::IsMove | DepKind::NeedsDrop | DepKind::Layout | DepKind::ConstEval | @@ -768,6 +769,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, DepKind::BorrowCheck => { force!(borrowck, def_id!()); } DepKind::MirBorrowCheck => { force!(mir_borrowck, def_id!()); } DepKind::UnsafetyCheckResult => { force!(unsafety_check_result, def_id!()); } + DepKind::MoveCheck => { force!(moveck, def_id!()); } DepKind::Reachability => { force!(reachable_set, LOCAL_CRATE); } DepKind::MirKeys => { force!(mir_keys, LOCAL_CRATE); } DepKind::CrateVariances => { force!(crate_variances, LOCAL_CRATE); } @@ -790,6 +792,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, DepKind::IsConstFn => { force!(is_const_fn, def_id!()); } DepKind::IsForeignItem => { force!(is_foreign_item, def_id!()); } DepKind::SizedConstraint => { force!(adt_sized_constraint, def_id!()); } + DepKind::MoveConstraint => { force!(adt_move_constraint, def_id!()); } DepKind::DtorckConstraint => { force!(adt_dtorck_constraint, def_id!()); } DepKind::AdtDestructor => { force!(adt_destructor, def_id!()); } DepKind::AssociatedItemDefIds => { force!(associated_item_def_ids, def_id!()); } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 48ec92a255b4c..eafb6266b4e4d 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -40,7 +40,7 @@ use std::collections::BTreeMap; use std::cmp; use std::fmt; use std::hash::{Hash, Hasher}; -use std::iter::FromIterator; +use std::iter::{self, FromIterator}; use std::ops::Deref; use std::rc::Rc; use std::slice; @@ -1367,6 +1367,9 @@ bitflags! { /// /// See RFC 2008 (/~https://github.com/rust-lang/rfcs/pull/2008). const IS_NON_EXHAUSTIVE = 1 << 5; + + const IS_IMMOVABLE = 1 << 6; + const IS_MOVABLE_CELL = 1 << 7; } } @@ -1561,6 +1564,12 @@ impl<'a, 'gcx, 'tcx> AdtDef { if attr::contains_name(&attrs, "fundamental") { flags = flags | AdtFlags::IS_FUNDAMENTAL; } + if Some(did) == tcx.lang_items().immovable() { + flags = flags | AdtFlags::IS_IMMOVABLE; + } + if Some(did) == tcx.lang_items().movable_cell_type() { + flags = flags | AdtFlags::IS_MOVABLE_CELL; + } if Some(did) == tcx.lang_items().phantom_data() { flags = flags | AdtFlags::IS_PHANTOM_DATA; } @@ -1638,6 +1647,18 @@ impl<'a, 'gcx, 'tcx> AdtDef { self.flags.intersects(AdtFlags::IS_FUNDAMENTAL) } + /// Returns true if this is Immovable. + #[inline] + pub fn is_immovable(&self) -> bool { + self.flags.intersects(AdtFlags::IS_IMMOVABLE) + } + + /// Returns true if this is MovableCell. + #[inline] + pub fn is_movable_cell(&self) -> bool { + self.flags.intersects(AdtFlags::IS_MOVABLE_CELL) + } + /// Returns true if this is PhantomData. #[inline] pub fn is_phantom_data(&self) -> bool { @@ -1887,6 +1908,123 @@ impl<'a, 'gcx, 'tcx> AdtDef { debug!("sized_constraint_for_ty({:?}) = {:?}", ty, result); result } + + /// Returns a list of types such that `Self: Move` if and only + /// if that type is Move, or `TyErr` if this type is recursive. + pub fn move_constraint(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> &'tcx [Ty<'tcx>] { + match queries::adt_move_constraint::try_get(tcx, DUMMY_SP, self.did) { + Ok(tys) => tys, + Err(mut bug) => { + debug!("adt_move_constraint: {:?} is recursive", self); + // This should be reported as an error by `check_representable`. + // + // Consider the type as Move in the meanwhile to avoid + // further errors. Delay our `bug` diagnostic here to get + // emitted later as well in case we accidentally otherwise don't + // emit an error. + bug.delay_as_bug(); + tcx.intern_type_list(&[tcx.types.err]) + } + } + } + + fn move_constraint_for_ty(&self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + ty: Ty<'tcx>) + -> Vec> { + let result = match ty.sty { + // TyForeign isn't possible to move since you cannot get its size + // Therefore its fine for it to implement Move + TyForeign(..) | + + TyBool | TyChar | TyInt(..) | TyUint(..) | TyFloat(..) | + TyRawPtr(..) | TyRef(..) | TyFnDef(..) | TyFnPtr(_) | + TyStr | TyNever => { + vec![] + } + + TyArray(t, _) | TySlice(t) => vec![t], + + // FIXME: Should TyClosure/TyGenerator panic since they can't appear in ADTs? + TyClosure(def_id, substs) => { + substs.upvar_tys(def_id, tcx) + .flat_map(|ty| self.move_constraint_for_ty(tcx, ty)) + .collect() + } + + TyGenerator(def_id, substs, interior) => { + substs.upvar_tys(def_id, tcx) + .chain(iter::once(interior.witness)) + .flat_map(|ty| self.move_constraint_for_ty(tcx, ty)) + .collect() + } + + TyDynamic(predicate, _) => { + // Trait objects with a Move trait bound are movable + let move_trait = tcx.lang_items().move_trait().unwrap(); + // We can skip the binder here because + // ExistentialPredicate::AutoTrait cannot contain lifetimes + let test = |p| p == &ty::ExistentialPredicate::AutoTrait(move_trait); + if predicate.skip_binder().into_iter().any(test) { + vec![] + } else { + vec![ty] + } + } + + // FIXME: Shouldn't Error always implement Move and Sized to avoid errors? + TyError => vec![ty], + + TyTuple(tys, _) => { + tys.into_iter() + .flat_map(|ty| self.move_constraint_for_ty(tcx, *ty)) + .collect() + } + + TyAdt(adt, substs) => { + // recursive case + let adt_tys = adt.move_constraint(tcx); + debug!("move_constraint_for_ty({:?}) intermediate = {:?}", + ty, adt_tys); + adt_tys.iter() + .map(|ty| ty.subst(tcx, substs)) + .flat_map(|ty| self.move_constraint_for_ty(tcx, ty)) + .collect() + } + + TyProjection(..) | TyAnon(..) => { + // FIXME: Should we replace TyAnon with its underlying type here? + // must calculate explicitly. + // FIXME: consider special-casing always-Move projections + vec![ty] + } + + TyParam(..) => { + // perf hack: if there is a `T: Move` bound, then + // we know that `T` is Move and do not need to check + // it on the impl. + + let move_trait = tcx.lang_items().move_trait().unwrap(); + let move_predicate = Binder(TraitRef { + def_id: move_trait, + substs: tcx.mk_substs_trait(ty, &[]) + }).to_predicate(); + let predicates = tcx.predicates_of(self.did).predicates; + if predicates.into_iter().any(|p| p == move_predicate) { + vec![] + } else { + vec![ty] + } + } + + TyInfer(..) => { + bug!("unexpected type `{:?}` in move_constraint_for_ty", + ty) + } + }; + debug!("move_constraint_for_ty({:?}) = {:?}", ty, result); + result + } } impl<'a, 'gcx, 'tcx> VariantDef { @@ -2501,6 +2639,35 @@ fn adt_sized_constraint<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, result } +/// Calculates the Move-constraint. +/// +/// In fact, there are only a few options for the types in the constraint: +/// - an obviously-immovable type +/// - a type parameter or projection whose Moveness can't be known +/// - a tuple of type parameters or projections, if there are multiple +/// such. +/// - a TyError, if a type contained itself. The representability +/// check should catch this case. +fn adt_move_constraint<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId) + -> &'tcx [Ty<'tcx>] { + let def = tcx.adt_def(def_id); + + if def.is_movable_cell() { + return tcx.intern_type_list(&[]); + } + + let result = tcx.intern_type_list(&def.variants.iter().flat_map(|v| { + v.fields.last() + }).flat_map(|f| { + def.move_constraint_for_ty(tcx, tcx.type_of(f.did)) + }).collect::>()); + + debug!("adt_move_constraint: {:?} => {:?}", def, result); + + result +} + /// Calculates the dtorck constraint for a type. fn adt_dtorck_constraint<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) @@ -2625,6 +2792,7 @@ pub fn provide(providers: &mut ty::maps::Providers) { associated_item, associated_item_def_ids, adt_sized_constraint, + adt_move_constraint, adt_dtorck_constraint, def_span, param_env, diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 23dd3f1bc2bba..cadeb8e5623a0 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -837,6 +837,14 @@ impl<'a, 'tcx> ty::TyS<'tcx> { tcx.at(span).is_freeze_raw(param_env.and(self)) } + pub fn is_move(&'tcx self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + span: Span)-> bool + { + tcx.at(span).is_move_raw(param_env.and(self)) + } + /// If `ty.needs_drop(...)` returns `true`, then `ty` is definitely /// non-copy and *might* have a destructor attached; if it returns /// `false`, then `ty` definitely has no destructor (i.e. no drop glue). @@ -1079,6 +1087,20 @@ fn is_freeze_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, DUMMY_SP)) } +fn is_move_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) + -> bool +{ + let (param_env, ty) = query.into_parts(); + let trait_def_id = tcx.require_lang_item(lang_items::MoveTraitLangItem); + tcx.infer_ctxt() + .enter(|infcx| traits::type_known_to_meet_bound(&infcx, + param_env, + ty, + trait_def_id, + DUMMY_SP)) +} + fn needs_drop_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool @@ -1216,6 +1238,7 @@ pub fn provide(providers: &mut ty::maps::Providers) { is_copy_raw, is_sized_raw, is_freeze_raw, + is_move_raw, needs_drop_raw, ..*providers }; diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index acb929981fbf2..b60050e8a2303 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -560,9 +560,21 @@ define_print! { cx.parameterized(f, principal.substs, principal.def_id, &projections)?; } + let move_id = tcx.lang_items().move_trait().unwrap(); + + let mut is_move = false; + // Builtin bounds. for did in self.auto_traits() { - write!(f, " + {}", tcx.item_path_str(did))?; + if move_id == did { + is_move = true; + } else { + write!(f, " + {}", tcx.item_path_str(did))?; + } + } + + if !is_move { + write!(f, " + ?Move")?; } Ok(()) diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 8f2917fed7a29..d621b2b414821 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -1063,6 +1063,10 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(control: &CompileController, "MIR borrow checking", || for def_id in tcx.body_owners() { tcx.mir_borrowck(def_id) }); + time(time_passes, + "MIR move checking", + || for def_id in tcx.body_owners() { tcx.moveck(def_id) }); + time(time_passes, "MIR effect checking", || for def_id in tcx.body_owners() { diff --git a/src/librustc_mir/borrow_check.rs b/src/librustc_mir/borrow_check.rs index cdac72b6dffb0..9de5ad4aae346 100644 --- a/src/librustc_mir/borrow_check.rs +++ b/src/librustc_mir/borrow_check.rs @@ -77,7 +77,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, let id = tcx.hir.as_local_node_id(def_id) .expect("do_mir_borrowck: non-local DefId"); - let move_data: MoveData<'tcx> = match MoveData::gather_moves(input_mir, tcx, param_env) { + let move_data: MoveData<'tcx> = match MoveData::gather_moves(input_mir, tcx, param_env, false) { Ok(move_data) => move_data, Err((move_data, move_errors)) => { for move_error in move_errors { diff --git a/src/librustc_mir/dataflow/impls/mod.rs b/src/librustc_mir/dataflow/impls/mod.rs index 147f3d796b91c..031e9afd7741b 100644 --- a/src/librustc_mir/dataflow/impls/mod.rs +++ b/src/librustc_mir/dataflow/impls/mod.rs @@ -37,6 +37,10 @@ pub use self::storage_liveness::*; #[allow(dead_code)] pub(super) mod borrows; +mod observed_lvals; + +pub use self::observed_lvals::*; + /// `MaybeInitializedLvals` tracks all l-values that might be /// initialized upon reaching a particular point in the control flow /// for a function. diff --git a/src/librustc_mir/dataflow/impls/observed_lvals.rs b/src/librustc_mir/dataflow/impls/observed_lvals.rs new file mode 100644 index 0000000000000..eef5fe1b31909 --- /dev/null +++ b/src/librustc_mir/dataflow/impls/observed_lvals.rs @@ -0,0 +1,267 @@ +// 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. + +pub use super::*; + +use rustc::mir::*; +use rustc::mir::visit::Visitor; +use dataflow::BitDenotation; +use dataflow::move_paths::LookupResult; + +struct ObservedLvalueVisitor<'a, 'tcx: 'a, 'b, 'c: 'b> { + data: &'a MaybeObservedLvals<'a, 'tcx>, + sets: &'b mut BlockSets<'c, MovePathIndex>, +} + +// Ignore statics and paths through derefs +fn ignore<'tcx>(lvalue: &Lvalue<'tcx>) -> bool { + match *lvalue { + Lvalue::Local(..) => false, + Lvalue::Static(..) => true, + Lvalue::Projection(ref proj) => { + match proj.elem { + ProjectionElem::Deref => true, + _ => ignore(&proj.base) + } + } + } +} + +fn observed_paths<'a, 'tcx, F>(rvalue: &Rvalue<'tcx>, + location: Location, + move_data: &'a MoveData<'tcx>, + mut each_path: F) + where F: FnMut(MovePathIndex) +{ + if let Rvalue::Ref(_, _, ref lvalue) = *rvalue { + // Ignore indirect (through a Deref) lvalues + // These must either be already observed (by a borrow) or inside a Box + // You cannot move immovables values out of a Box and you cannot move values + // out of pointers at all. + if ignore(lvalue) { + return; + } + + debug!("observed l-value {:?} at {:?}", lvalue, location); + // Mark the lvalue as observed and also all children contained within + match move_data.rev_lookup.find(lvalue) { + LookupResult::Exact(path) | + LookupResult::Parent(Some(path)) => { + debug!("marking path: {:?}", move_data.move_paths[path].lvalue); + + move_data.all_paths_from(path, |subpath| { + debug!("marking subpath: {:?}", move_data.move_paths[subpath].lvalue); + each_path(subpath); + true + }) + } + LookupResult::Parent(None) => (), + } + } +} + +impl<'a, 'tcx, 'b, 'c> Visitor<'tcx> for ObservedLvalueVisitor<'a, 'tcx, 'b, 'c> { + fn visit_rvalue(&mut self, + rvalue: &Rvalue<'tcx>, + location: Location) { + observed_paths(rvalue, location, self.data.move_data, |path| { + self.sets.gen(&path); + }); + + self.super_rvalue(rvalue, location) + } +} + +#[derive(Copy, Clone)] +pub struct MaybeObservedLvals<'a, 'tcx: 'a> { + _tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &'a Mir<'tcx>, + move_data: &'a MoveData<'tcx>, +} + +impl<'a, 'tcx: 'a> MaybeObservedLvals<'a, 'tcx> { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &'a Mir<'tcx>, + move_data: &'a MoveData<'tcx>) + -> Self { + MaybeObservedLvals { _tcx: tcx, mir: mir, move_data } + } +} + +impl<'a, 'tcx: 'a> HasMoveData<'tcx> for MaybeObservedLvals<'a, 'tcx> { + fn move_data(&self) -> &MoveData<'tcx> { &self.move_data } +} + +impl<'a, 'tcx> BitDenotation for MaybeObservedLvals<'a, 'tcx> { + type Idx = MovePathIndex; + fn name() -> &'static str { "maybe_observed" } + fn bits_per_block(&self) -> usize { + self.move_data().move_paths.len() + } + + fn start_block_effect(&self, _sets: &mut BlockSets) { + // Nothing is observed on function entry + } + + fn statement_effect(&self, + sets: &mut BlockSets, + loc: Location) { + ObservedLvalueVisitor { + data: self, + sets, + }.visit_statement(loc.block, &self.mir[loc.block].statements[loc.statement_index], loc); + } + + fn terminator_effect(&self, + sets: &mut BlockSets, + loc: Location) { + ObservedLvalueVisitor { + data: self, + sets, + }.visit_terminator(loc.block, self.mir[loc.block].terminator(), loc); + } + + fn propagate_call_return(&self, + _in_out: &mut IdxSet, + _call_bb: mir::BasicBlock, + _dest_bb: mir::BasicBlock, + _dest_lval: &mir::Lvalue) { + // Nothing to do when a call returns successfully + } +} + +impl<'a, 'tcx> BitwiseOperator for MaybeObservedLvals<'a, 'tcx> { + #[inline] + fn join(&self, pred1: usize, pred2: usize) -> usize { + pred1 | pred2 // "maybe" means we union effects of both preds + } +} + +impl<'a, 'tcx> DataflowOperator for MaybeObservedLvals<'a, 'tcx> { + #[inline] + fn bottom_value() -> bool { + false // bottom = unobserved + } +} + +struct LvalObserversVisitor<'a, 'tcx: 'a, 'b, 'c: 'b> { + data: &'a LvalObservers<'a, 'tcx>, + sets: &'b mut BlockSets<'c, usize>, +} + +impl<'a, 'tcx, 'b, 'c> Visitor<'tcx> for LvalObserversVisitor<'a, 'tcx, 'b, 'c> { + fn visit_rvalue(&mut self, + rvalue: &Rvalue<'tcx>, + location: Location) { + if let Rvalue::Ref(..) = *rvalue { + if let Some(idx) = self.data.observers.iter().position(|e| e == &location) { + self.sets.gen(&idx); + } + } + + self.super_rvalue(rvalue, location) + } +} + +#[derive(Clone)] +pub struct LvalObservers<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &'a Mir<'tcx>, + move_data: &'a MoveData<'tcx>, + filter: MovePathIndex, + pub(crate) observers: Vec, +} + +impl<'a, 'tcx> Visitor<'tcx> for LvalObservers<'a, 'tcx> { + fn visit_rvalue(&mut self, + rvalue: &Rvalue<'tcx>, + location: Location) { + let mut observed = false; + + observed_paths(rvalue, location, self.move_data, |path| { + if path == self.filter { + observed = true; + } + }); + + if observed { + self.observers.push(location); + } + + self.super_rvalue(rvalue, location) + } +} + + +impl<'a, 'tcx: 'a> LvalObservers<'a, 'tcx> { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &'a Mir<'tcx>, + move_data: &'a MoveData<'tcx>, + filter: MovePathIndex) + -> Self { + LvalObservers { tcx: tcx, mir: mir, move_data, filter, observers: Vec::new() } + } +} + +impl<'a, 'tcx: 'a> HasMoveData<'tcx> for LvalObservers<'a, 'tcx> { + fn move_data(&self) -> &MoveData<'tcx> { &self.move_data } +} + +impl<'a, 'tcx> BitDenotation for LvalObservers<'a, 'tcx> { + type Idx = usize; + fn name() -> &'static str { "maybe_lval_observers" } + fn bits_per_block(&self) -> usize { + self.observers.len() + } + + fn start_block_effect(&self, _sets: &mut BlockSets) { + // Nothing is observed on function entry + } + + fn statement_effect(&self, + sets: &mut BlockSets, + loc: Location) { + LvalObserversVisitor { + data: self, + sets, + }.visit_statement(loc.block, &self.mir[loc.block].statements[loc.statement_index], loc); + } + + fn terminator_effect(&self, + sets: &mut BlockSets, + loc: Location) { + LvalObserversVisitor { + data: self, + sets, + }.visit_terminator(loc.block, self.mir[loc.block].terminator(), loc); + } + + fn propagate_call_return(&self, + _in_out: &mut IdxSet, + _call_bb: mir::BasicBlock, + _dest_bb: mir::BasicBlock, + _dest_lval: &mir::Lvalue) { + // Nothing to do when a call returns successfully + } +} + +impl<'a, 'tcx> BitwiseOperator for LvalObservers<'a, 'tcx> { + #[inline] + fn join(&self, pred1: usize, pred2: usize) -> usize { + pred1 | pred2 // "maybe" means we union effects of both preds + } +} + +impl<'a, 'tcx> DataflowOperator for LvalObservers<'a, 'tcx> { + #[inline] + fn bottom_value() -> bool { + false // bottom = unobserved + } +} diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs index bca9324d5b0aa..f27988fb54485 100644 --- a/src/librustc_mir/dataflow/mod.rs +++ b/src/librustc_mir/dataflow/mod.rs @@ -27,6 +27,8 @@ use std::usize; pub use self::impls::{MaybeStorageLive}; pub use self::impls::{MaybeInitializedLvals, MaybeUninitializedLvals}; pub use self::impls::{DefinitelyInitializedLvals, MovingOutStatements}; +pub use self::impls::{MaybeObservedLvals, LvalObservers}; + pub use self::impls::borrows::{Borrows, BorrowData, BorrowIndex}; pub(crate) use self::drop_flag_effects::*; diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index a0212de605eee..93d08a6a826e3 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -31,12 +31,14 @@ struct MoveDataBuilder<'a, 'gcx: 'tcx, 'tcx: 'a> { param_env: ty::ParamEnv<'gcx>, data: MoveData<'tcx>, errors: Vec>, + move_check: bool, } impl<'a, 'gcx, 'tcx> MoveDataBuilder<'a, 'gcx, 'tcx> { fn new(mir: &'a Mir<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>, - param_env: ty::ParamEnv<'gcx>) + param_env: ty::ParamEnv<'gcx>, + move_check: bool) -> Self { let mut move_paths = IndexVec::new(); let mut path_map = IndexVec::new(); @@ -46,6 +48,7 @@ impl<'a, 'gcx, 'tcx> MoveDataBuilder<'a, 'gcx, 'tcx> { tcx, param_env, errors: Vec::new(), + move_check, data: MoveData { moves: IndexVec::new(), loc_map: LocationMap::new(mir), @@ -199,10 +202,11 @@ impl<'a, 'gcx, 'tcx> MoveDataBuilder<'a, 'gcx, 'tcx> { pub(super) fn gather_moves<'a, 'gcx, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>, - param_env: ty::ParamEnv<'gcx>) + param_env: ty::ParamEnv<'gcx>, + move_check: bool) -> Result, (MoveData<'tcx>, Vec>)> { - let mut builder = MoveDataBuilder::new(mir, tcx, param_env); + let mut builder = MoveDataBuilder::new(mir, tcx, param_env, move_check); for (bb, block) in mir.basic_blocks().iter_enumerated() { for (i, stmt) in block.statements.iter().enumerate() { @@ -283,7 +287,11 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { self.gather_operand(operand); } } - Rvalue::Ref(..) | + Rvalue::Ref(_, _, ref location) => { + if self.builder.move_check { + self.create_move_path(location); + } + } Rvalue::Discriminant(..) | Rvalue::Len(..) | Rvalue::NullaryOp(NullOp::SizeOf, _) | @@ -324,7 +332,10 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { } TerminatorKind::Drop { ref location, target: _, unwind: _ } => { - self.gather_move(location, false); + // Drops are by reference, so they don't move the value + if !self.builder.move_check { + self.gather_move(location, false); + } } TerminatorKind::DropAndReplace { ref location, ref value, .. } => { self.create_move_path(location); @@ -363,6 +374,11 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { return } + if self.builder.move_check && erased_ty.is_move(gcx, self.builder.param_env, DUMMY_SP) { + debug!("gather_move({:?}, {:?}) - {:?} is Move. skipping", self.loc, lval, lv_ty); + return + } + let path = match self.move_path_for(lval) { Ok(path) | Err(MoveError::UnionMove { path }) => path, Err(error @ MoveError::IllegalMove { .. }) => { diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs index 73218c9815024..4f9ea6bd227ba 100644 --- a/src/librustc_mir/dataflow/move_paths/mod.rs +++ b/src/librustc_mir/dataflow/move_paths/mod.rs @@ -128,6 +128,29 @@ pub struct MoveData<'tcx> { pub rev_lookup: MovePathLookup<'tcx>, } +impl<'tcx> MoveData<'tcx> { + pub fn all_paths_from(&self, root: MovePathIndex, mut each_path: F) + where F: FnMut(MovePathIndex) -> bool + { + fn all_paths_from<'tcx, F>(move_data: &MoveData<'tcx>, + root: MovePathIndex, each_path: &mut F) + where F: FnMut(MovePathIndex) -> bool + { + if !each_path(root) { + return; + } + + let mut next_child_index = move_data.move_paths[root].first_child; + while let Some(child_index) = next_child_index { + all_paths_from(move_data, child_index, each_path); + next_child_index = move_data.move_paths[child_index].next_sibling; + } + } + + all_paths_from(self, root, &mut each_path); + } +} + pub trait HasMoveData<'tcx> { fn move_data(&self) -> &MoveData<'tcx>; } @@ -258,8 +281,9 @@ impl<'tcx> MoveError<'tcx> { impl<'a, 'gcx, 'tcx> MoveData<'tcx> { pub fn gather_moves(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>, - param_env: ty::ParamEnv<'gcx>) + param_env: ty::ParamEnv<'gcx>, + move_check: bool) -> Result>)> { - builder::gather_moves(mir, tcx, param_env) + builder::gather_moves(mir, tcx, param_env, move_check) } } diff --git a/src/librustc_mir/diagnostics.rs b/src/librustc_mir/diagnostics.rs index 0f67f7bf6deb4..9866c25c1ac17 100644 --- a/src/librustc_mir/diagnostics.rs +++ b/src/librustc_mir/diagnostics.rs @@ -1782,4 +1782,6 @@ register_diagnostics! { E0594, // cannot assign to {} E0598, // lifetime of {} is too short to guarantee its contents can be... E0625, // thread-local statics cannot be accessed at compile-time + E0801, // cannot move value whose address is observed + E0802, // cannot move immovable value out of a Box type } diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs index c24256cc92cde..0e8891abce1c8 100644 --- a/src/librustc_mir/transform/elaborate_drops.rs +++ b/src/librustc_mir/transform/elaborate_drops.rs @@ -50,7 +50,7 @@ impl MirPass for ElaborateDrops { _ => return } let param_env = tcx.param_env(src.def_id); - let move_data = MoveData::gather_moves(mir, tcx, param_env).unwrap(); + let move_data = MoveData::gather_moves(mir, tcx, param_env, false).unwrap(); let elaborate_patch = { let mir = &*mir; let env = MoveDataParamEnv { diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 441f9be9be1f4..7bdcc0c1108c9 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -35,6 +35,7 @@ pub mod elaborate_drops; pub mod add_call_guards; pub mod promote_consts; pub mod qualify_consts; +pub mod move_check; pub mod dump_mir; pub mod deaggregator; pub mod instcombine; @@ -46,6 +47,7 @@ pub mod nll; pub(crate) fn provide(providers: &mut Providers) { self::qualify_consts::provide(providers); self::check_unsafety::provide(providers); + self::move_check::provide(providers); *providers = Providers { mir_keys, mir_built, @@ -206,6 +208,7 @@ fn mir_validated<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx // this point, before we steal the mir-const result. let _ = tcx.mir_const_qualif(def_id); } + let _ = tcx.moveck(def_id); let mut mir = tcx.mir_const(def_id).steal(); run_passes![tcx, mir, def_id, 1; diff --git a/src/librustc_mir/transform/move_check.rs b/src/librustc_mir/transform/move_check.rs new file mode 100644 index 0000000000000..098581199bdc6 --- /dev/null +++ b/src/librustc_mir/transform/move_check.rs @@ -0,0 +1,137 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use dataflow::move_paths::{HasMoveData, MoveData}; +use dataflow::{MaybeObservedLvals, LvalObservers}; +use dataflow::state_for_location; +use dataflow; +use rustc::ty::TyCtxt; +use rustc::hir::def_id::DefId; +use rustc::mir::visit::Visitor; +use rustc::mir::{Mir, Lvalue, ProjectionElem}; +use rustc_data_structures::indexed_set::IdxSetBuf; +use rustc::ty::maps::Providers; + +fn deref_box<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &Mir<'tcx>, + lvalue: &Lvalue<'tcx>) -> bool { + match *lvalue { + Lvalue::Local(..) | Lvalue::Static(..) => false, + Lvalue::Projection(ref proj) => { + match proj.elem { + ProjectionElem::Deref => proj.base.ty(mir, tcx).to_ty(tcx).is_box(), + _ => deref_box(tcx, mir, &proj.base) + } + } + } +} + +// FIXME: This needs to run before optimizations like SimplifyCfg? and SimplifyBranches +fn moveck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) { + // NB: this borrow is valid because all the consumers of + // `mir_const` force this. + let mir = &*tcx.mir_const(def_id).borrow(); + let id = tcx.hir.as_local_node_id(def_id).unwrap(); + let param_env = tcx.param_env(def_id); + let move_data = match MoveData::gather_moves(mir, tcx, param_env, true) { + Ok(move_data) => move_data, + // Ignore move errors, borrowck will report those + Err((move_data, _)) => move_data, + }; + // If there are no moves of immovables types, there is nothing to check. + if move_data.moves.is_empty() { + return; + } + + let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); + let analysis = MaybeObservedLvals::new(tcx, mir, &move_data); + let observed = + dataflow::do_dataflow(tcx, mir, id, &[], &dead_unwinds, analysis, + |bd, p| &bd.move_data().move_paths[p]); + + for (i, path) in move_data.move_paths.iter_enumerated() { + debug!("move path: {:?} => {:?}", i, path.lvalue); + } + + // Enumerated moves where the types do not implement Copy or Move + for move_out in move_data.moves.iter() { + let move_lvalue = &move_data.move_paths[move_out.path].lvalue; + let span = move_out.source.source_info(mir).span; + + // Are we moving an immovable type out of an box? + if deref_box(tcx, mir, move_lvalue) { + span_err!(tcx.sess, span, E0802, "cannot move immovable value out of a Box type"); + } + + let state = state_for_location(move_out.source, &analysis, &observed); + + let mut invalid_move = false; + + debug!("move out path: {:?}", move_lvalue); + + // Check if the lvalue moved and all its children are either unobserved or implements Move + move_data.all_paths_from(move_out.path, |subpath| { + // Don't look for further errors if this move was already found to be an error + if invalid_move { + return false; + } + + let lvalue = &move_data.move_paths[subpath].lvalue; + let lvalue_ty = lvalue.ty(mir, tcx).to_ty(tcx); + + debug!("checking subpath: {:?}", move_data.move_paths[subpath].lvalue); + debug!("is-observed: {}, move: {}", state.contains(&subpath), + lvalue_ty.is_move(tcx.global_tcx(), param_env, span)); + + if state.contains(&subpath) && !lvalue_ty.is_move(tcx.global_tcx(), param_env, span) { + // The subpath was observed. Run a dataflow analysis to find out which borrows + // caused the subpath to get observed and add those locations to the error + // as notes. + + let mut observers = LvalObservers::new(tcx, mir, &move_data, subpath); + observers.visit_mir(mir); + + static STR: &'static &'static str = &"<>"; + + let observer_result = + dataflow::do_dataflow(tcx, mir, id, &[], &dead_unwinds, observers.clone(), + |_, _| STR); + + let state = state_for_location(move_out.source, &observers, &observer_result); + + let mut err = struct_span_err!(tcx.sess, span, E0801, + "cannot move value whose address is observed"); + + err.note(&format!("required because `{}` does not implement Move", lvalue_ty)); + + for (i, loc) in observers.observers.iter().enumerate() { + if state.contains(&i) { + span_note!(err, + loc.source_info(mir).span, + "the address was observed here"); + } + } + + err.emit(); + + invalid_move = true; + } + + true + }); + } +} + +pub(crate) fn provide(providers: &mut Providers) { + *providers = Providers { + moveck, + ..*providers + }; +} diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs index 32d4a14c7f757..10ad8e52a0259 100644 --- a/src/librustc_mir/transform/rustc_peek.rs +++ b/src/librustc_mir/transform/rustc_peek.rs @@ -45,7 +45,7 @@ impl MirPass for SanityCheck { let attributes = tcx.get_attrs(def_id); let param_env = tcx.param_env(def_id); - let move_data = MoveData::gather_moves(mir, tcx, param_env).unwrap(); + let move_data = MoveData::gather_moves(mir, tcx, param_env, false).unwrap(); let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env }; let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); let flow_inits = diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 40adc6bcb122f..2d82bcd94cf9c 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -91,19 +91,6 @@ impl<'a> AstValidator<'a> { } } - fn no_questions_in_bounds(&self, bounds: &TyParamBounds, where_: &str, is_trait: bool) { - for bound in bounds { - if let TraitTyParamBound(ref poly, TraitBoundModifier::Maybe) = *bound { - let mut err = self.err_handler().struct_span_err(poly.span, - &format!("`?Trait` is not permitted in {}", where_)); - if is_trait { - err.note(&format!("traits are `?{}` by default", poly.trait_ref.path)); - } - err.emit(); - } - } - } - /// matches '-' lit | lit (cf. parser::Parser::parse_pat_literal_maybe_minus), /// or path for ranges. /// @@ -168,7 +155,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> { any_lifetime_bounds = true; } } - self.no_questions_in_bounds(bounds, "trait object types", false); } TyKind::ImplTrait(ref bounds) => { if !bounds.iter() @@ -241,16 +227,21 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.err_handler().span_err(item.span, "auto traits cannot have generics"); } - if !bounds.is_empty() { + let normal_bounds = bounds.iter().any(|bound| { + match *bound { + TyParamBound::TraitTyParamBound(_, TraitBoundModifier::Maybe) => false, + _ => true, + } + }); + if normal_bounds { self.err_handler().span_err(item.span, - "auto traits cannot have super traits"); + "auto traits cannot have super traits or lifetime bounds on Self"); } if !trait_items.is_empty() { self.err_handler().span_err(item.span, "auto traits cannot contain items"); } } - self.no_questions_in_bounds(bounds, "supertraits", true); for trait_item in trait_items { if let TraitItemKind::Method(ref sig, ref block) = trait_item.node { self.check_trait_fn_not_const(sig.constness); diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 7aaf65e1fd07d..efc8ecc75c76f 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -542,12 +542,34 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { fn conv_object_ty_poly_trait_ref(&self, span: Span, - trait_bounds: &[hir::PolyTraitRef], + bounds: &[hir::TyParamBound], lifetime: &hir::Lifetime) -> Ty<'tcx> { let tcx = self.tcx(); + let move_id = tcx.lang_items().move_trait().unwrap(); + let mut is_move = true; + let mut trait_bounds = Vec::new(); + for bound in bounds { + match *bound { + hir::TraitTyParamBound(ref trait_ref, hir::TraitBoundModifier::None) => { + trait_bounds.push(trait_ref.clone()); + } + hir::TraitTyParamBound(ref trait_ref, hir::TraitBoundModifier::Maybe) => { + if self.trait_def_id(&trait_ref.trait_ref) == move_id { + is_move = false; + } else { + tcx.sess.span_warn(span, + "default bound relaxed for a trait object, but \ + this does nothing because the given bound is not \ + a default. Only `?Move` is supported"); + } + }, + _ => unreachable!(), + } + } + if trait_bounds.is_empty() { span_err!(tcx.sess, span, E0224, "at least one non-builtin trait is required for an object type"); @@ -567,7 +589,11 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { &mut vec![]); } - let (auto_traits, trait_bounds) = split_auto_traits(tcx, &trait_bounds[1..]); + let (mut auto_traits, trait_bounds) = split_auto_traits(tcx, &trait_bounds[1..]); + + if is_move { + auto_traits.push(move_id); + } if !trait_bounds.is_empty() { let b = &trait_bounds[0]; @@ -1305,12 +1331,18 @@ fn report_lifetime_number_error(tcx: TyCtxt, span: Span, number: usize, expected .emit(); } +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct ImplicitBounds { + pub sized_trait: bool, + pub move_trait: bool, +} + // A helper struct for conveniently grouping a set of bounds which we pass to // and return from functions in multiple places. #[derive(PartialEq, Eq, Clone, Debug)] pub struct Bounds<'tcx> { pub region_bounds: Vec>, - pub implicitly_sized: bool, + pub implicit_bounds: ImplicitBounds, pub trait_bounds: Vec>, pub projection_bounds: Vec>, } @@ -1321,8 +1353,19 @@ impl<'a, 'gcx, 'tcx> Bounds<'tcx> { { let mut vec = Vec::new(); + // If it could be move, and is, add the move predicate + if self.implicit_bounds.move_trait { + if let Some(sized) = tcx.lang_items().move_trait() { + let trait_ref = ty::TraitRef { + def_id: sized, + substs: tcx.mk_substs_trait(param_ty, &[]) + }; + vec.push(trait_ref.to_predicate()); + } + } + // If it could be sized, and is, add the sized predicate - if self.implicitly_sized { + if self.implicit_bounds.sized_trait { if let Some(sized) = tcx.lang_items().sized_trait() { let trait_ref = ty::TraitRef { def_id: sized, diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index b3a07027fb032..35f24989704a6 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1932,7 +1932,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Require that the predicate holds for the concrete type. let cause = traits::ObligationCause::new(span, self.body_id, - traits::SizedReturnType); + traits::MiscObligation); self.register_predicate(traits::Obligation::new(cause, self.param_env, predicate)); diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index 90a0952af0425..f853741cbd0b5 100644 --- a/src/librustc_typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -53,7 +53,7 @@ fn enforce_trait_manually_implementable(tcx: TyCtxt, impl_def_id: DefId, trait_d let did = Some(trait_def_id); let li = tcx.lang_items(); - // Disallow *all* explicit impls of `Sized` and `Unsize` for now. + // Disallow *all* explicit impls of `Sized`, `Move` and `Unsize` for now. if did == li.sized_trait() { let span = tcx.span_of_impl(impl_def_id).unwrap(); struct_span_err!(tcx.sess, @@ -74,6 +74,15 @@ fn enforce_trait_manually_implementable(tcx: TyCtxt, impl_def_id: DefId, trait_d return; } + if did == li.move_trait() { + let span = tcx.span_of_impl(impl_def_id).unwrap(); + span_err!(tcx.sess, + span, + E0800, + "explicit impls for the `Move` trait are not permitted"); + return; + } + if tcx.sess.features.borrow().unboxed_closures { // the feature gate allows all Fn traits return; diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index b5fbbeb1692e6..4a9c4d7acfe8f 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -24,10 +24,9 @@ //! At present, however, we do run collection across all items in the //! crate as a kind of pass. This should eventually be factored away. -use astconv::{AstConv, Bounds}; +use astconv::{AstConv, Bounds, ImplicitBounds}; use lint; use constrained_type_params as ctp; -use middle::lang_items::SizedTraitLangItem; use middle::const_val::ConstVal; use middle::resolve_lifetime as rl; use rustc::traits::Reveal; @@ -683,7 +682,10 @@ fn super_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let superbounds1 = compute_bounds(&icx, self_param_ty, bounds, - SizedByDefault::No, + ImplicitBounds { + sized_trait: false, + move_trait: true, + }, item.span); let superbounds1 = superbounds1.predicates(tcx, self_param_ty); @@ -1279,48 +1281,35 @@ fn impl_polarity<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } -// Is it marked with ?Sized -fn is_unsized<'gcx: 'tcx, 'tcx>(astconv: &AstConv<'gcx, 'tcx>, +// Is it marked with ?Move/?Sized +fn opt_out_bounds<'gcx: 'tcx, 'tcx>(astconv: &AstConv<'gcx, 'tcx>, ast_bounds: &[hir::TyParamBound], - span: Span) -> bool + span: Span, + mut defaults: ImplicitBounds) -> ImplicitBounds { let tcx = astconv.tcx(); - - // Try to find an unbound in bounds. - let mut unbound = None; for ab in ast_bounds { if let &hir::TraitTyParamBound(ref ptr, hir::TraitBoundModifier::Maybe) = ab { - if unbound.is_none() { - unbound = Some(ptr.trait_ref.clone()); + let trait_id = if let Def::Trait(def_id) = ptr.trait_ref.path.def { + Some(def_id) } else { - span_err!(tcx.sess, span, E0203, - "type parameter has more than one relaxed default \ - bound, only one is supported"); - } - } - } + None + }; - let kind_id = tcx.lang_items().require(SizedTraitLangItem); - match unbound { - Some(ref tpb) => { - // FIXME(#8559) currently requires the unbound to be built-in. - if let Ok(kind_id) = kind_id { - if tpb.path.def != Def::Trait(kind_id) { - tcx.sess.span_warn(span, - "default bound relaxed for a type parameter, but \ - this does nothing because the given bound is not \ - a default. Only `?Sized` is supported"); - } + if defaults.move_trait && trait_id == tcx.lang_items().move_trait() { + defaults.move_trait = false; + } else if defaults.sized_trait && trait_id == tcx.lang_items().sized_trait() { + defaults.sized_trait = false; + } else { + tcx.sess.span_warn(span, + "default bound relaxed for a type parameter, but \ + this does nothing because the given bound is not \ + a default. Only `?Sized` and `?Move` is supported"); } } - _ if kind_id.is_ok() => { - return false; - } - // No lang item for Sized, so we can't add it as a bound. - None => {} } - true + defaults } /// Returns the early-bound lifetimes declared in this generics @@ -1418,7 +1407,10 @@ fn explicit_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Collect the bounds, i.e. the `A+B+'c` in `impl A+B+'c`. let bounds = compute_bounds(&icx, anon_ty, bounds, - SizedByDefault::Yes, + ImplicitBounds { + sized_trait: true, + move_trait: false, + }, span); return ty::GenericPredicates { parent: None, @@ -1475,7 +1467,10 @@ fn explicit_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let bounds = compute_bounds(&icx, param_ty, ¶m.bounds, - SizedByDefault::Yes, + ImplicitBounds { + sized_trait: true, + move_trait: true, + }, param.span); predicates.extend(bounds.predicates(tcx, param_ty)); } @@ -1550,7 +1545,10 @@ fn explicit_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let bounds = compute_bounds(&ItemCtxt::new(tcx, def_id), assoc_ty, bounds, - SizedByDefault::Yes, + ImplicitBounds { + sized_trait: true, + move_trait: true, + }, trait_item.span); bounds.predicates(tcx, assoc_ty).into_iter() @@ -1565,7 +1563,10 @@ fn explicit_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let param_ty = ty::ParamTy::new(index, name).to_ty(tcx); index += 1; let bounds = compute_bounds(&icx, param_ty, info.bounds, - SizedByDefault::Yes, + ImplicitBounds { + sized_trait: true, + move_trait: true, + }, info.span); predicates.extend(bounds.predicates(tcx, param_ty)); } @@ -1590,15 +1591,13 @@ fn explicit_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } -pub enum SizedByDefault { Yes, No, } - /// Translate the AST's notion of ty param bounds (which are an enum consisting of a newtyped Ty or /// a region) to ty's notion of ty param bounds, which can either be user-defined traits, or the /// built-in trait (formerly known as kind): Send. pub fn compute_bounds<'gcx: 'tcx, 'tcx>(astconv: &AstConv<'gcx, 'tcx>, param_ty: Ty<'tcx>, ast_bounds: &[hir::TyParamBound], - sized_by_default: SizedByDefault, + defaults: ImplicitBounds, span: Span) -> Bounds<'tcx> { @@ -1630,15 +1629,11 @@ pub fn compute_bounds<'gcx: 'tcx, 'tcx>(astconv: &AstConv<'gcx, 'tcx>, trait_bounds.sort_by(|a,b| a.def_id().cmp(&b.def_id())); - let implicitly_sized = if let SizedByDefault::Yes = sized_by_default { - !is_unsized(astconv, ast_bounds, span) - } else { - false - }; + let implicit_bounds = opt_out_bounds(astconv, ast_bounds, span, defaults); Bounds { region_bounds, - implicitly_sized, + implicit_bounds, trait_bounds, projection_bounds, } diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 328b7f9fdefca..74585a4b63f6b 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -1855,11 +1855,11 @@ unsafe impl !Clone for Foo { } This will compile: ```ignore (ignore auto_trait future compatibility warning) -#![feature(optin_builtin_traits)] +#![feature(optin_builtin_traits, immovable_types)] struct Foo; -trait Enterprise {} +trait Enterprise: ?std::marker::Move {} impl Enterprise for .. { } @@ -4721,4 +4721,5 @@ register_diagnostics! { E0632, // cannot provide explicit type parameters when `impl Trait` is used in // argument position. E0641, // cannot cast to/from a pointer with an unknown kind + E0800, // explicit impls for the `Move` trait are not permitted } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 1d107c169b046..46f266dbb9faf 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -725,6 +725,23 @@ impl TyParamBound { }, hir::TraitBoundModifier::Maybe) } + fn maybe_move(cx: &DocContext) -> TyParamBound { + let did = cx.tcx.require_lang_item(lang_items::MoveTraitLangItem); + let empty = cx.tcx.intern_substs(&[]); + let path = external_path(cx, &cx.tcx.item_name(did), + Some(did), false, vec![], empty); + inline::record_extern_fqn(cx, did, TypeKind::Trait); + TraitBound(PolyTrait { + trait_: ResolvedPath { + path, + typarams: None, + did, + is_generic: false, + }, + lifetimes: vec![] + }, hir::TraitBoundModifier::Maybe) + } + fn is_sized_bound(&self, cx: &DocContext) -> bool { use rustc::hir::TraitBoundModifier as TBM; if let TyParamBound::TraitBound(PolyTrait { ref trait_, .. }, TBM::None) = *self { @@ -734,6 +751,16 @@ impl TyParamBound { } false } + + fn is_move_bound(&self, cx: &DocContext) -> bool { + use rustc::hir::TraitBoundModifier as TBM; + if let TyParamBound::TraitBound(PolyTrait { ref trait_, .. }, TBM::None) = *self { + if trait_.def_id() == cx.tcx.lang_items().move_trait() { + return true; + } + } + false + } } impl Clean for hir::TyParamBound { @@ -1089,20 +1116,26 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, let mut where_predicates = preds.predicates.to_vec().clean(cx); - // Type parameters and have a Sized bound by default unless removed with - // ?Sized. Scan through the predicates and mark any type parameter with - // a Sized bound, removing the bounds as we find them. + // Type parameters have a Sized and Move bound by default unless removed with + // ?Sized or ?Move. Scan through the predicates and mark any type parameter with + // a Sized or Move bound, removing the bounds as we find them. // - // Note that associated types also have a sized bound by default, but we + // Note that associated types also have a sized and move bound by default, but we // don't actually know the set of associated types right here so that's // handled in cleaning associated types let mut sized_params = FxHashSet(); + let mut move_params = FxHashSet(); where_predicates.retain(|pred| { match *pred { WP::BoundPredicate { ty: Generic(ref g), ref bounds } => { if bounds.iter().any(|b| b.is_sized_bound(cx)) { sized_params.insert(g.clone()); false + } else if bounds.iter().any(|b| b.is_move_bound(cx)) { + move_params.insert(g.clone()); + // FIXME: Why is this stripping away bounds? + // Can there only be 1 bound in the bounds because of cleaning? + false } else { true } @@ -1111,8 +1144,8 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, } }); - // Run through the type parameters again and insert a ?Sized - // unbound for any we didn't find to be Sized. + // Run through the type parameters again and insert a ?Sized or a ?Move + // unbound for any we didn't find to be Sized or Move. for tp in &stripped_typarams { if !sized_params.contains(&tp.name) { where_predicates.push(WP::BoundPredicate { @@ -1120,6 +1153,12 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, bounds: vec![TyParamBound::maybe_sized(cx)], }) } + if !move_params.contains(&tp.name) { + where_predicates.push(WP::BoundPredicate { + ty: Type::Generic(tp.name.clone()), + bounds: vec![TyParamBound::maybe_move(cx)], + }) + } } // It would be nice to collect all of the bounds on a type and recombine @@ -1541,6 +1580,11 @@ impl<'tcx> Clean for ty::AssociatedItem { Some(i) => { bounds.remove(i); } None => bounds.push(TyParamBound::maybe_sized(cx)), } + // Do the same thing for Move/?Move bounds + match bounds.iter().position(|b| b.is_move_bound(cx)) { + Some(i) => { bounds.remove(i); } + None => bounds.push(TyParamBound::maybe_move(cx)), + } let ty = if self.defaultness.has_value() { Some(cx.tcx.type_of(self.def_id)) @@ -1941,11 +1985,14 @@ impl Clean for hir::Ty { } } TyTraitObject(ref bounds, ref lifetime) => { - match bounds[0].clean(cx).trait_ { + let bound = match bounds[0] { + hir::TraitTyParamBound(ref trait_ref, + hir::TraitBoundModifier::None) => trait_ref, + _ => panic!(), + }; + match bound.clean(cx).trait_ { ResolvedPath { path, typarams: None, did, is_generic } => { - let mut bounds: Vec<_> = bounds[1..].iter().map(|bound| { - TraitBound(bound.clean(cx), hir::TraitBoundModifier::None) - }).collect(); + let mut bounds: Vec<_> = bounds[1..].clean(cx); if !lifetime.is_elided() { bounds.push(RegionBound(lifetime.clean(cx))); } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 37cc7a49b5271..dfb8d6b470464 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -279,6 +279,7 @@ #![feature(heap_api)] #![feature(i128)] #![feature(i128_type)] +#![cfg_attr(not(stage0), feature(immovable_types))] #![feature(inclusive_range)] #![feature(int_error_internals)] #![feature(integer_atomics)] diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs index 385076e50ddea..6143f9c639e68 100644 --- a/src/libstd/panic.rs +++ b/src/libstd/panic.rs @@ -98,6 +98,14 @@ pub use panicking::{take_hook, set_hook, PanicInfo, Location}; /// wrapper struct in this module can be used to force this trait to be /// implemented for any closed over variables passed to the `catch_unwind` function /// (more on this below). +#[cfg(not(stage0))] +#[stable(feature = "catch_unwind", since = "1.9.0")] +#[rustc_on_unimplemented = "the type {Self} may not be safely transferred \ + across an unwind boundary"] +pub trait UnwindSafe: ?::marker::Move {} + +/// docs +#[cfg(stage0)] #[stable(feature = "catch_unwind", since = "1.9.0")] #[rustc_on_unimplemented = "the type {Self} may not be safely transferred \ across an unwind boundary"] @@ -111,10 +119,16 @@ pub trait UnwindSafe {} /// /// This is a "helper marker trait" used to provide impl blocks for the /// `UnwindSafe` trait, for more information see that documentation. +#[cfg(not(stage0))] #[stable(feature = "catch_unwind", since = "1.9.0")] #[rustc_on_unimplemented = "the type {Self} may contain interior mutability \ and a reference may not be safely transferrable \ across a catch_unwind boundary"] +pub trait RefUnwindSafe: ?::marker::Move {} + +/// docs +#[cfg(stage0)] +#[stable(feature = "catch_unwind", since = "1.9.0")] pub trait RefUnwindSafe {} /// A simple wrapper around a type to assert that it is unwind safe. @@ -191,8 +205,12 @@ pub struct AssertUnwindSafe( #[allow(unknown_lints)] #[allow(auto_impl)] impl UnwindSafe for .. {} +#[cfg(stage0)] #[stable(feature = "catch_unwind", since = "1.9.0")] impl<'a, T: ?Sized> !UnwindSafe for &'a mut T {} +#[cfg(not(stage0))] +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl<'a, T: ?Sized+?::marker::Move> !UnwindSafe for &'a mut T {} #[stable(feature = "catch_unwind", since = "1.9.0")] impl<'a, T: RefUnwindSafe + ?Sized> UnwindSafe for &'a T {} #[stable(feature = "catch_unwind", since = "1.9.0")] @@ -226,8 +244,12 @@ impl UnwindSafe for Arc {} #[allow(unknown_lints)] #[allow(auto_impl)] impl RefUnwindSafe for .. {} +#[cfg(stage0)] #[stable(feature = "catch_unwind", since = "1.9.0")] impl !RefUnwindSafe for UnsafeCell {} +#[cfg(not(stage0))] +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl !RefUnwindSafe for UnsafeCell {} #[stable(feature = "catch_unwind", since = "1.9.0")] impl RefUnwindSafe for AssertUnwindSafe {} diff --git a/src/test/compile-fail/auto-impl-future-compat.rs b/src/test/compile-fail/auto-impl-future-compat.rs index 5c32a75639880..26619849f0748 100644 --- a/src/test/compile-fail/auto-impl-future-compat.rs +++ b/src/test/compile-fail/auto-impl-future-compat.rs @@ -8,9 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(optin_builtin_traits)] +#![feature(optin_builtin_traits, immovable_types)] -trait Foo {} +use std::marker::Move; + +trait Foo: ?Move {} impl Foo for .. {} //~^ ERROR The form `impl Foo for .. {}` will be removed, please use `auto trait Foo {}` //~^^ WARN this was previously accepted by the compiler diff --git a/src/test/compile-fail/auxiliary/tdticc_coherence_lib.rs b/src/test/compile-fail/auxiliary/tdticc_coherence_lib.rs index 1e1c55de87e17..66554571f2867 100644 --- a/src/test/compile-fail/auxiliary/tdticc_coherence_lib.rs +++ b/src/test/compile-fail/auxiliary/tdticc_coherence_lib.rs @@ -10,8 +10,11 @@ #![feature(optin_builtin_traits, core)] #![crate_type = "rlib"] +#![feature(immovable_types)] -pub trait DefaultedTrait { } +use std::marker::Move; + +pub trait DefaultedTrait: ?Move { } #[allow(auto_impl)] impl DefaultedTrait for .. { } diff --git a/src/test/compile-fail/immovable-types.rs b/src/test/compile-fail/immovable-types.rs new file mode 100644 index 0000000000000..f9902420d5466 --- /dev/null +++ b/src/test/compile-fail/immovable-types.rs @@ -0,0 +1,66 @@ +// 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(immovable_types)] + +use std::marker::{Move, Immovable}; + +fn mov(_: T) {} + +pub fn main() { + // Illegal: Moving out of children of observed values + { + let a = (Immovable, true); + &a; + mov(a.0); //~ ERROR cannot move value whose address is observed + } + + // Illegal: Moving out of observed values + { + let a = (Immovable, true); + &a; + mov(a); //~ ERROR cannot move value whose address is observed + } + + // Illegal: Moving out of values with observed children + { + let a = (Immovable, true); + &a.0; + mov(a); //~ ERROR cannot move value whose address is observed + } + + // Illegal: Moving out of observed children + { + let a = (Immovable, true); + &a.0; + mov(a.0); //~ ERROR cannot move value whose address is observed + } + + // Legal: Moving out of movable children of observed values + { + let a = (Immovable, true); + &a; + mov(a.1); + } + + // Legal: Moving out of siblings of observed values + { + let a = (Immovable, Immovable); + &a.0; + mov(a.1); + } + + // Legal: Moving out of value with an observed movable children + { + let a = (Immovable, true); + &a.1; + mov(a); + } +} diff --git a/src/test/compile-fail/maybe-bounds-where.rs b/src/test/compile-fail/maybe-bounds-where.rs index 211fac2ee234c..12de0721a1f5d 100644 --- a/src/test/compile-fail/maybe-bounds-where.rs +++ b/src/test/compile-fail/maybe-bounds-where.rs @@ -23,8 +23,7 @@ struct S4(T) where for<'a> T: ?Trait<'a>; //~^ ERROR `?Trait` bounds are only permitted at the point where a type parameter is declared struct S5(*const T) where T: ?Trait<'static> + ?Sized; -//~^ ERROR type parameter has more than one relaxed default bound -//~| WARN default bound relaxed for a type parameter +//~^ WARN default bound relaxed for a type parameter impl S1 { fn f() where T: ?Sized {} diff --git a/src/test/compile-fail/maybe-bounds.rs b/src/test/compile-fail/maybe-bounds.rs index b0b412bbf89ec..d988c3f3e989a 100644 --- a/src/test/compile-fail/maybe-bounds.rs +++ b/src/test/compile-fail/maybe-bounds.rs @@ -8,10 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -trait Tr: ?Sized {} //~ ERROR `?Trait` is not permitted in supertraits - //~^ NOTE traits are `?Sized` by default +trait Tr: ?Sized {} //~ WARN default bound relaxed -type A1 = Tr + ?Sized; //~ ERROR `?Trait` is not permitted in trait object types -type A2 = for<'a> Tr + ?Sized; //~ ERROR `?Trait` is not permitted in trait object types +type A1 = Tr + ?Sized; //~ WARN default bound relaxed +type A2 = for<'a> Tr + ?Sized; //~ WARN default bound relaxed -fn main() {} +fn main() { dummy_error } //~ ERROR cannot find value diff --git a/src/test/compile-fail/move-trait-leaks-in-impl-trait.rs b/src/test/compile-fail/move-trait-leaks-in-impl-trait.rs new file mode 100644 index 0000000000000..dc802d0757db6 --- /dev/null +++ b/src/test/compile-fail/move-trait-leaks-in-impl-trait.rs @@ -0,0 +1,33 @@ +// 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(conservative_impl_trait, immovable_types)] + +use std::marker::{Move, Immovable}; + +trait Foo: ?Move {} + +impl Foo for T {} + +fn immovable() -> impl Foo { + Immovable +} + +fn movable() -> impl Foo { + true +} + +fn test(_: T) {} + +fn main() { + test(immovable()); + //~^ ERROR the trait bound `std::marker::Immovable: std::marker::Move` is not satisfied + test(movable()); +} diff --git a/src/test/compile-fail/no-move-immovable-box-content.rs b/src/test/compile-fail/no-move-immovable-box-content.rs new file mode 100644 index 0000000000000..90928bf34013a --- /dev/null +++ b/src/test/compile-fail/no-move-immovable-box-content.rs @@ -0,0 +1,29 @@ +// 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(immovable_types)] + +use std::marker::Immovable; + +fn new_box_immovable() -> Box { + // FIXME: we still can't create boxes of immovable types using + // `Box::new`, please change this to use `Box::new` when that is possible + panic!("please change this to use Box::new when that can be used") +} + +fn move_from_box(b: Box) { + *b; //~ ERROR cannot move immovable value out of a Box type +} + +fn main() { + let a = new_box_immovable(); + &*a; // ok + move_from_box(a); // ok +} \ No newline at end of file diff --git a/src/test/compile-fail/no-move-observed.rs b/src/test/compile-fail/no-move-observed.rs new file mode 100644 index 0000000000000..340682f94cd70 --- /dev/null +++ b/src/test/compile-fail/no-move-observed.rs @@ -0,0 +1,22 @@ +// 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(immovable_types)] + +use std::marker::Move; + +fn foo(x: T) +{ + &x; + let y = x; + //~^ ERROR cannot move value whose address is observed +} + +fn main() { } diff --git a/src/test/compile-fail/phantom-oibit.rs b/src/test/compile-fail/phantom-oibit.rs index 1c1cb396a54f2..89e09362da51a 100644 --- a/src/test/compile-fail/phantom-oibit.rs +++ b/src/test/compile-fail/phantom-oibit.rs @@ -13,10 +13,12 @@ // (`impl Trait for ..`)) #![feature(optin_builtin_traits)] +#![feature(immovable_types)] +use std::marker::Move; use std::marker::{PhantomData}; -unsafe trait Zen {} +unsafe trait Zen: ?Move {} #[allow(auto_impl)] unsafe impl Zen for .. {} diff --git a/src/test/compile-fail/privacy-sanity.rs b/src/test/compile-fail/privacy-sanity.rs index 34082adb8f9a5..3ce32225d4cf4 100644 --- a/src/test/compile-fail/privacy-sanity.rs +++ b/src/test/compile-fail/privacy-sanity.rs @@ -9,8 +9,11 @@ // except according to those terms. #![feature(optin_builtin_traits)] +#![feature(immovable_types)] -trait MarkerTr {} +use std::marker::Move; + +trait MarkerTr: ?Move {} pub trait Tr { fn f(); const C: u8; @@ -39,7 +42,7 @@ pub extern "C" { //~ ERROR unnecessary visibility qualifier } const MAIN: u8 = { - trait MarkerTr {} + trait MarkerTr: ?Move {} pub trait Tr { fn f(); const C: u8; @@ -71,7 +74,7 @@ const MAIN: u8 = { }; fn main() { - trait MarkerTr {} + trait MarkerTr: ?Move {} pub trait Tr { fn f(); const C: u8; diff --git a/src/test/compile-fail/trait-static-method-generic-inference.rs b/src/test/compile-fail/trait-static-method-generic-inference.rs index 0e357d9d4d531..9df112a9bf272 100644 --- a/src/test/compile-fail/trait-static-method-generic-inference.rs +++ b/src/test/compile-fail/trait-static-method-generic-inference.rs @@ -32,7 +32,7 @@ mod base { pub fn foo() { let _f: base::Foo = base::HasNew::new(); - //~^ ERROR type annotations required + //~^ ERROR type annotations } fn main() { } diff --git a/src/test/compile-fail/typeck-default-trait-impl-constituent-types-2.rs b/src/test/compile-fail/typeck-default-trait-impl-constituent-types-2.rs index a837d8c9ca74e..cf9e970dfcc4d 100644 --- a/src/test/compile-fail/typeck-default-trait-impl-constituent-types-2.rs +++ b/src/test/compile-fail/typeck-default-trait-impl-constituent-types-2.rs @@ -9,8 +9,11 @@ // except according to those terms. #![feature(optin_builtin_traits)] +#![feature(immovable_types)] -trait MyTrait {} +use std::marker::Move; + +trait MyTrait: ?Move {} #[allow(auto_impl)] impl MyTrait for .. {} diff --git a/src/test/compile-fail/typeck-default-trait-impl-constituent-types.rs b/src/test/compile-fail/typeck-default-trait-impl-constituent-types.rs index bed184eb4ccca..dde89a9814838 100644 --- a/src/test/compile-fail/typeck-default-trait-impl-constituent-types.rs +++ b/src/test/compile-fail/typeck-default-trait-impl-constituent-types.rs @@ -9,8 +9,11 @@ // except according to those terms. #![feature(optin_builtin_traits)] +#![feature(immovable_types)] -trait MyTrait {} +use std::marker::Move; + +trait MyTrait: ?Move {} #[allow(auto_impl)] impl MyTrait for .. {} diff --git a/src/test/compile-fail/typeck-default-trait-impl-negation.rs b/src/test/compile-fail/typeck-default-trait-impl-negation.rs index f3a6d8a342e22..e036dfbdd83fe 100644 --- a/src/test/compile-fail/typeck-default-trait-impl-negation.rs +++ b/src/test/compile-fail/typeck-default-trait-impl-negation.rs @@ -9,13 +9,16 @@ // except according to those terms. #![feature(optin_builtin_traits)] +#![feature(immovable_types)] -trait MyTrait {} +use std::marker::Move; + +trait MyTrait: ?Move {} #[allow(auto_impl)] impl MyTrait for .. {} -unsafe trait MyUnsafeTrait {} +unsafe trait MyUnsafeTrait: ?Move {} #[allow(auto_impl)] unsafe impl MyUnsafeTrait for .. {} diff --git a/src/test/compile-fail/typeck-default-trait-impl-precedence.rs b/src/test/compile-fail/typeck-default-trait-impl-precedence.rs index bdd6487b86d74..e5b245adf1d2d 100644 --- a/src/test/compile-fail/typeck-default-trait-impl-precedence.rs +++ b/src/test/compile-fail/typeck-default-trait-impl-precedence.rs @@ -14,8 +14,11 @@ // impls whose types unify. #![feature(optin_builtin_traits)] +#![feature(immovable_types)] -trait Defaulted { } +use std::marker::Move; + +trait Defaulted: ?Move { } #[allow(auto_impl)] impl Defaulted for .. { } impl<'a,T:Signed> Defaulted for &'a T { } diff --git a/src/test/run-pass/auto-traits.rs b/src/test/run-pass/auto-traits.rs index e42aca9ccbd13..9e800a1f1ca7f 100644 --- a/src/test/run-pass/auto-traits.rs +++ b/src/test/run-pass/auto-traits.rs @@ -8,14 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(optin_builtin_traits)] +#![feature(optin_builtin_traits, immovable_types)] -auto trait Auto {} +use std::marker::Move; + +auto trait Auto: ?Move {} // Redundant but accepted until we remove it. #[allow(auto_impl)] impl Auto for .. {} -unsafe auto trait AutoUnsafe {} +unsafe auto trait AutoUnsafe: ?Move {} impl !Auto for bool {} impl !AutoUnsafe for bool {} diff --git a/src/test/run-pass/issue-29516.rs b/src/test/run-pass/issue-29516.rs index 5fa0a002a10db..c1cad28235f0a 100644 --- a/src/test/run-pass/issue-29516.rs +++ b/src/test/run-pass/issue-29516.rs @@ -8,9 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(immovable_types)] #![feature(optin_builtin_traits)] -trait NotSame {} +use std::marker::Move; + +trait NotSame: ?Move {} #[allow(auto_impl)] impl NotSame for .. {} impl !NotSame for (A, A) {} diff --git a/src/test/run-pass/static-immovable.rs b/src/test/run-pass/static-immovable.rs new file mode 100644 index 0000000000000..2fc388e134590 --- /dev/null +++ b/src/test/run-pass/static-immovable.rs @@ -0,0 +1,17 @@ +// 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(immovable_types)] + +use std::marker::Immovable; + +static FOO: Immovable = Immovable; + +pub fn main() {} diff --git a/src/test/rustdoc/auxiliary/rustdoc-default-impl.rs b/src/test/rustdoc/auxiliary/rustdoc-default-impl.rs index 4fd55bd482cd9..d3aadf96bb575 100644 --- a/src/test/rustdoc/auxiliary/rustdoc-default-impl.rs +++ b/src/test/rustdoc/auxiliary/rustdoc-default-impl.rs @@ -9,12 +9,13 @@ // except according to those terms. #![feature(optin_builtin_traits)] +#![feature(immovable_types)] #![feature(core)] pub mod bar { - use std::marker; + use std::marker::Move; - pub trait Bar {} + pub trait Bar: ?Move {} #[allow(auto_impl)] impl Bar for .. {} diff --git a/src/test/rustdoc/auxiliary/rustdoc-impl-parts-crosscrate.rs b/src/test/rustdoc/auxiliary/rustdoc-impl-parts-crosscrate.rs index d886778278dfd..6c83075ff7d80 100644 --- a/src/test/rustdoc/auxiliary/rustdoc-impl-parts-crosscrate.rs +++ b/src/test/rustdoc/auxiliary/rustdoc-impl-parts-crosscrate.rs @@ -9,8 +9,11 @@ // except according to those terms. #![feature(optin_builtin_traits)] +#![feature(immovable_types)] -pub trait AnOibit {} +use std::marker::Move; + +pub trait AnOibit: ?Move {} #[allow(auto_impl)] impl AnOibit for .. {} diff --git a/src/test/rustdoc/impl-parts.rs b/src/test/rustdoc/impl-parts.rs index f74f66ce72905..cd50f8f11d638 100644 --- a/src/test/rustdoc/impl-parts.rs +++ b/src/test/rustdoc/impl-parts.rs @@ -9,8 +9,11 @@ // except according to those terms. #![feature(optin_builtin_traits)] +#![feature(immovable_types)] -pub trait AnOibit {} +use std::marker::Move; + +pub trait AnOibit: ?Move {} #[allow(auto_impl)] impl AnOibit for .. {} diff --git a/src/test/ui/impl-trait/equality-recursive.rs b/src/test/ui/impl-trait/equality-recursive.rs new file mode 100644 index 0000000000000..f600270d2ba38 --- /dev/null +++ b/src/test/ui/impl-trait/equality-recursive.rs @@ -0,0 +1,26 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(conservative_impl_trait, specialization)] + +trait Foo: Copy + ToString {} + +impl Foo for T {} + +fn sum_to(n: u32) -> impl Foo { + if n == 0 { + 0 + } else { + n + sum_to(n - 1) + //~^ ERROR no implementation for `u32 + impl Foo` + } +} + +fn main() {} \ No newline at end of file diff --git a/src/test/ui/impl-trait/equality-recursive.stderr b/src/test/ui/impl-trait/equality-recursive.stderr new file mode 100644 index 0000000000000..9f93b0cf778c4 --- /dev/null +++ b/src/test/ui/impl-trait/equality-recursive.stderr @@ -0,0 +1,32 @@ +error[E0391]: unsupported cyclic reference between types/traits detected + --> $DIR/equality-recursive.rs:17:1 + | +17 | / fn sum_to(n: u32) -> impl Foo { +18 | | if n == 0 { +19 | | 0 +20 | | } else { +... | +23 | | } +24 | | } + | |_^ cyclic reference + | +note: the cycle begins when processing `sum_to`... + --> $DIR/equality-recursive.rs:17:1 + | +17 | / fn sum_to(n: u32) -> impl Foo { +18 | | if n == 0 { +19 | | 0 +20 | | } else { +... | +23 | | } +24 | | } + | |_^ +note: ...which then requires processing `sum_to::{{impl-Trait}}`... + --> $DIR/equality-recursive.rs:17:22 + | +17 | fn sum_to(n: u32) -> impl Foo { + | ^^^^^^^^ + = note: ...which then again requires processing `sum_to`, completing the cycle. + +error: aborting due to previous error + diff --git a/src/test/ui/impl-trait/equality.rs b/src/test/ui/impl-trait/equality.rs index 96db53ad2e46e..04539a9a64069 100644 --- a/src/test/ui/impl-trait/equality.rs +++ b/src/test/ui/impl-trait/equality.rs @@ -27,15 +27,6 @@ fn two(x: bool) -> impl Foo { //~| expected i32, found u32 } -fn sum_to(n: u32) -> impl Foo { - if n == 0 { - 0 - } else { - n + sum_to(n - 1) - //~^ ERROR no implementation for `u32 + impl Foo` - } -} - trait Leak: Sized { type T; fn leak(self) -> Self::T; diff --git a/src/test/ui/impl-trait/equality.stderr b/src/test/ui/impl-trait/equality.stderr index 3fc08a0900fb9..0432429c66d63 100644 --- a/src/test/ui/impl-trait/equality.stderr +++ b/src/test/ui/impl-trait/equality.stderr @@ -7,49 +7,41 @@ error[E0308]: mismatched types = note: expected type `i32` found type `u32` -error[E0277]: the trait bound `u32: std::ops::Add` is not satisfied - --> $DIR/equality.rs:34:11 - | -34 | n + sum_to(n - 1) - | ^ no implementation for `u32 + impl Foo` - | - = help: the trait `std::ops::Add` is not implemented for `u32` - error[E0308]: mismatched types - --> $DIR/equality.rs:53:18 + --> $DIR/equality.rs:44:18 | -53 | let _: u32 = hide(0_u32); +44 | let _: u32 = hide(0_u32); | ^^^^^^^^^^^ expected u32, found anonymized type | = note: expected type `u32` found type `impl Foo` error[E0308]: mismatched types - --> $DIR/equality.rs:59:18 + --> $DIR/equality.rs:50:18 | -59 | let _: i32 = Leak::leak(hide(0_i32)); +50 | let _: i32 = Leak::leak(hide(0_i32)); | ^^^^^^^^^^^^^^^^^^^^^^^ expected i32, found associated type | = note: expected type `i32` found type `::T` error[E0308]: mismatched types - --> $DIR/equality.rs:66:10 + --> $DIR/equality.rs:57:10 | -66 | x = (x.1, +57 | x = (x.1, | ^^^ expected u32, found i32 | = note: expected type `impl Foo` (u32) found type `impl Foo` (i32) error[E0308]: mismatched types - --> $DIR/equality.rs:69:10 + --> $DIR/equality.rs:60:10 | -69 | x.0); +60 | x.0); | ^^^ expected i32, found u32 | = note: expected type `impl Foo` (i32) found type `impl Foo` (u32) -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors diff --git a/src/test/ui/on-unimplemented/multiple-impls.stderr b/src/test/ui/on-unimplemented/multiple-impls.stderr index a1fa8b720a829..d377f46547f48 100644 --- a/src/test/ui/on-unimplemented/multiple-impls.stderr +++ b/src/test/ui/on-unimplemented/multiple-impls.stderr @@ -7,14 +7,6 @@ error[E0277]: the trait bound `[i32]: Index` is not satisfied = help: the trait `Index` is not implemented for `[i32]` = note: required by `Index::index` -error[E0277]: the trait bound `[i32]: Index` is not satisfied - --> $DIR/multiple-impls.rs:43:5 - | -43 | Index::index(&[] as &[i32], 2u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trait message - | - = help: the trait `Index` is not implemented for `[i32]` - error[E0277]: the trait bound `[i32]: Index>` is not satisfied --> $DIR/multiple-impls.rs:49:5 | @@ -24,14 +16,6 @@ error[E0277]: the trait bound `[i32]: Index>` is not satisfied = help: the trait `Index>` is not implemented for `[i32]` = note: required by `Index::index` -error[E0277]: the trait bound `[i32]: Index>` is not satisfied - --> $DIR/multiple-impls.rs:49:5 - | -49 | Index::index(&[] as &[i32], Foo(2u32)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ on impl for Foo - | - = help: the trait `Index>` is not implemented for `[i32]` - error[E0277]: the trait bound `[i32]: Index>` is not satisfied --> $DIR/multiple-impls.rs:55:5 | @@ -41,13 +25,5 @@ error[E0277]: the trait bound `[i32]: Index>` is not satisfied = help: the trait `Index>` is not implemented for `[i32]` = note: required by `Index::index` -error[E0277]: the trait bound `[i32]: Index>` is not satisfied - --> $DIR/multiple-impls.rs:55:5 - | -55 | Index::index(&[] as &[i32], Bar(2u32)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ on impl for Bar - | - = help: the trait `Index>` is not implemented for `[i32]` - -error: aborting due to 6 previous errors +error: aborting due to 3 previous errors diff --git a/src/test/ui/on-unimplemented/on-impl.stderr b/src/test/ui/on-unimplemented/on-impl.stderr index c8c06bf44fd6f..d3edc9dde8033 100644 --- a/src/test/ui/on-unimplemented/on-impl.stderr +++ b/src/test/ui/on-unimplemented/on-impl.stderr @@ -7,13 +7,5 @@ error[E0277]: the trait bound `[i32]: Index` is not satisfied = help: the trait `Index` is not implemented for `[i32]` = note: required by `Index::index` -error[E0277]: the trait bound `[i32]: Index` is not satisfied - --> $DIR/on-impl.rs:32:5 - | -32 | Index::::index(&[1, 2, 3] as &[i32], 2u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ a usize is required to index into a slice - | - = help: the trait `Index` is not implemented for `[i32]` - -error: aborting due to 2 previous errors +error: aborting due to previous error