summaryrefslogtreecommitdiffstats
path: root/src/test/ui/moves
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /src/test/ui/moves
parentInitial commit. (diff)
downloadrustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz
rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--src/test/ui/moves/borrow-closures-instead-of-move.rs36
-rw-r--r--src/test/ui/moves/borrow-closures-instead-of-move.stderr53
-rw-r--r--src/test/ui/moves/issue-46099-move-in-macro.rs15
-rw-r--r--src/test/ui/moves/issue-46099-move-in-macro.stderr11
-rw-r--r--src/test/ui/moves/issue-72649-uninit-in-loop.rs74
-rw-r--r--src/test/ui/moves/issue-72649-uninit-in-loop.stderr63
-rw-r--r--src/test/ui/moves/issue-75904-move-closure-loop.rs15
-rw-r--r--src/test/ui/moves/issue-75904-move-closure-loop.stderr15
-rw-r--r--src/test/ui/moves/issue-99470-move-out-of-some.rs9
-rw-r--r--src/test/ui/moves/issue-99470-move-out-of-some.stderr16
-rw-r--r--src/test/ui/moves/move-1-unique.rs25
-rw-r--r--src/test/ui/moves/move-2-unique.rs10
-rw-r--r--src/test/ui/moves/move-2.rs6
-rw-r--r--src/test/ui/moves/move-3-unique.rs25
-rw-r--r--src/test/ui/moves/move-4-unique.rs18
-rw-r--r--src/test/ui/moves/move-4.rs18
-rw-r--r--src/test/ui/moves/move-arg-2-unique.rs12
-rw-r--r--src/test/ui/moves/move-arg-2.rs12
-rw-r--r--src/test/ui/moves/move-arg.rs5
-rw-r--r--src/test/ui/moves/move-deref-coercion.rs33
-rw-r--r--src/test/ui/moves/move-deref-coercion.stderr35
-rw-r--r--src/test/ui/moves/move-fn-self-receiver.rs79
-rw-r--r--src/test/ui/moves/move-fn-self-receiver.stderr169
-rw-r--r--src/test/ui/moves/move-guard-same-consts.rs25
-rw-r--r--src/test/ui/moves/move-guard-same-consts.stderr14
-rw-r--r--src/test/ui/moves/move-in-guard-1.rs15
-rw-r--r--src/test/ui/moves/move-in-guard-1.stderr14
-rw-r--r--src/test/ui/moves/move-in-guard-2.rs13
-rw-r--r--src/test/ui/moves/move-in-guard-2.stderr12
-rw-r--r--src/test/ui/moves/move-into-dead-array-1.rs15
-rw-r--r--src/test/ui/moves/move-into-dead-array-1.stderr11
-rw-r--r--src/test/ui/moves/move-into-dead-array-2.rs15
-rw-r--r--src/test/ui/moves/move-into-dead-array-2.stderr13
-rw-r--r--src/test/ui/moves/move-nullary-fn.rs13
-rw-r--r--src/test/ui/moves/move-of-addr-of-mut.rs12
-rw-r--r--src/test/ui/moves/move-of-addr-of-mut.stderr13
-rw-r--r--src/test/ui/moves/move-out-of-array-1.rs18
-rw-r--r--src/test/ui/moves/move-out-of-array-1.stderr12
-rw-r--r--src/test/ui/moves/move-out-of-array-ref.rs34
-rw-r--r--src/test/ui/moves/move-out-of-array-ref.stderr47
-rw-r--r--src/test/ui/moves/move-out-of-field.rs27
-rw-r--r--src/test/ui/moves/move-out-of-slice-1.rs11
-rw-r--r--src/test/ui/moves/move-out-of-slice-1.stderr14
-rw-r--r--src/test/ui/moves/move-out-of-slice-2.rs35
-rw-r--r--src/test/ui/moves/move-out-of-slice-2.stderr60
-rw-r--r--src/test/ui/moves/move-out-of-tuple-field.rs13
-rw-r--r--src/test/ui/moves/move-out-of-tuple-field.stderr23
-rw-r--r--src/test/ui/moves/move-scalar.rs10
-rw-r--r--src/test/ui/moves/moves-based-on-type-access-to-field.rs14
-rw-r--r--src/test/ui/moves/moves-based-on-type-access-to-field.stderr19
-rw-r--r--src/test/ui/moves/moves-based-on-type-block-bad.rs29
-rw-r--r--src/test/ui/moves/moves-based-on-type-block-bad.stderr15
-rw-r--r--src/test/ui/moves/moves-based-on-type-capture-clause-bad.rs9
-rw-r--r--src/test/ui/moves/moves-based-on-type-capture-clause-bad.stderr18
-rw-r--r--src/test/ui/moves/moves-based-on-type-capture-clause.rs12
-rw-r--r--src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.rs20
-rw-r--r--src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.stderr18
-rw-r--r--src/test/ui/moves/moves-based-on-type-distribute-copy-over-paren.rs49
-rw-r--r--src/test/ui/moves/moves-based-on-type-distribute-copy-over-paren.stderr27
-rw-r--r--src/test/ui/moves/moves-based-on-type-exprs.rs93
-rw-r--r--src/test/ui/moves/moves-based-on-type-exprs.stderr135
-rw-r--r--src/test/ui/moves/moves-based-on-type-match-bindings.rs21
-rw-r--r--src/test/ui/moves/moves-based-on-type-match-bindings.stderr14
-rw-r--r--src/test/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.rs10
-rw-r--r--src/test/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr13
-rw-r--r--src/test/ui/moves/moves-based-on-type-no-recursive-stack-closure.rs35
-rw-r--r--src/test/ui/moves/moves-based-on-type-no-recursive-stack-closure.stderr28
-rw-r--r--src/test/ui/moves/moves-based-on-type-tuple.rs10
-rw-r--r--src/test/ui/moves/moves-based-on-type-tuple.stderr14
-rw-r--r--src/test/ui/moves/moves-sru-moved-field.rs23
-rw-r--r--src/test/ui/moves/moves-sru-moved-field.stderr13
-rw-r--r--src/test/ui/moves/use_of_moved_value_clone_suggestions.rs6
-rw-r--r--src/test/ui/moves/use_of_moved_value_clone_suggestions.stderr13
-rw-r--r--src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed86
-rw-r--r--src/test/ui/moves/use_of_moved_value_copy_suggestions.rs86
-rw-r--r--src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr179
76 files changed, 2252 insertions, 0 deletions
diff --git a/src/test/ui/moves/borrow-closures-instead-of-move.rs b/src/test/ui/moves/borrow-closures-instead-of-move.rs
new file mode 100644
index 000000000..51771ced7
--- /dev/null
+++ b/src/test/ui/moves/borrow-closures-instead-of-move.rs
@@ -0,0 +1,36 @@
+fn takes_fn(f: impl Fn()) {
+ loop {
+ takes_fnonce(f);
+ //~^ ERROR use of moved value
+ //~| HELP consider borrowing
+ }
+}
+
+fn takes_fn_mut(m: impl FnMut()) {
+ if maybe() {
+ takes_fnonce(m);
+ //~^ HELP consider mutably borrowing
+ }
+ takes_fnonce(m);
+ //~^ ERROR use of moved value
+}
+
+fn has_closure() {
+ let mut x = 0;
+ let mut closure = || {
+ x += 1;
+ };
+ takes_fnonce(closure);
+ //~^ HELP consider mutably borrowing
+ closure();
+ //~^ ERROR borrow of moved value
+}
+
+fn maybe() -> bool {
+ false
+}
+
+// Could also be Fn[Mut], here it doesn't matter
+fn takes_fnonce(_: impl FnOnce()) {}
+
+fn main() {}
diff --git a/src/test/ui/moves/borrow-closures-instead-of-move.stderr b/src/test/ui/moves/borrow-closures-instead-of-move.stderr
new file mode 100644
index 000000000..3146b6959
--- /dev/null
+++ b/src/test/ui/moves/borrow-closures-instead-of-move.stderr
@@ -0,0 +1,53 @@
+error[E0382]: use of moved value: `f`
+ --> $DIR/borrow-closures-instead-of-move.rs:3:22
+ |
+LL | fn takes_fn(f: impl Fn()) {
+ | - move occurs because `f` has type `impl Fn()`, which does not implement the `Copy` trait
+LL | loop {
+LL | takes_fnonce(f);
+ | ^ value moved here, in previous iteration of loop
+ |
+help: consider borrowing `f`
+ |
+LL | takes_fnonce(&f);
+ | +
+
+error[E0382]: use of moved value: `m`
+ --> $DIR/borrow-closures-instead-of-move.rs:14:18
+ |
+LL | fn takes_fn_mut(m: impl FnMut()) {
+ | - move occurs because `m` has type `impl FnMut()`, which does not implement the `Copy` trait
+LL | if maybe() {
+LL | takes_fnonce(m);
+ | - value moved here
+...
+LL | takes_fnonce(m);
+ | ^ value used here after move
+ |
+help: consider mutably borrowing `m`
+ |
+LL | takes_fnonce(&mut m);
+ | ++++
+
+error[E0382]: borrow of moved value: `closure`
+ --> $DIR/borrow-closures-instead-of-move.rs:25:5
+ |
+LL | takes_fnonce(closure);
+ | ------- value moved here
+LL |
+LL | closure();
+ | ^^^^^^^ value borrowed here after move
+ |
+note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `x` out of its environment
+ --> $DIR/borrow-closures-instead-of-move.rs:21:9
+ |
+LL | x += 1;
+ | ^
+help: consider mutably borrowing `closure`
+ |
+LL | takes_fnonce(&mut closure);
+ | ++++
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/moves/issue-46099-move-in-macro.rs b/src/test/ui/moves/issue-46099-move-in-macro.rs
new file mode 100644
index 000000000..576fe1f4c
--- /dev/null
+++ b/src/test/ui/moves/issue-46099-move-in-macro.rs
@@ -0,0 +1,15 @@
+// Regression test for issue #46099
+// Tests that we don't emit spurious
+// 'value moved in previous iteration of loop' message
+
+macro_rules! test {
+ ($v:expr) => {{
+ drop(&$v);
+ $v
+ }}
+}
+
+fn main() {
+ let b = Box::new(true);
+ test!({b}); //~ ERROR use of moved value
+}
diff --git a/src/test/ui/moves/issue-46099-move-in-macro.stderr b/src/test/ui/moves/issue-46099-move-in-macro.stderr
new file mode 100644
index 000000000..baa87e3e9
--- /dev/null
+++ b/src/test/ui/moves/issue-46099-move-in-macro.stderr
@@ -0,0 +1,11 @@
+error[E0382]: use of moved value: `b`
+ --> $DIR/issue-46099-move-in-macro.rs:14:12
+ |
+LL | let b = Box::new(true);
+ | - move occurs because `b` has type `Box<bool>`, which does not implement the `Copy` trait
+LL | test!({b});
+ | ^ value used here after move
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/moves/issue-72649-uninit-in-loop.rs b/src/test/ui/moves/issue-72649-uninit-in-loop.rs
new file mode 100644
index 000000000..d76b69ecd
--- /dev/null
+++ b/src/test/ui/moves/issue-72649-uninit-in-loop.rs
@@ -0,0 +1,74 @@
+// Regression test for issue #72649
+// Tests that we don't emit spurious
+// 'value moved in previous iteration of loop' message
+
+struct NonCopy;
+
+fn good() {
+ loop {
+ let value = NonCopy{};
+ let _used = value;
+ }
+}
+
+fn moved_here_1() {
+ loop {
+ let value = NonCopy{};
+ //~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+ let _used = value;
+ //~^ NOTE value moved here
+ let _used2 = value; //~ ERROR use of moved value: `value`
+ //~^ NOTE value used here after move
+ }
+}
+
+fn moved_here_2() {
+ let value = NonCopy{};
+ //~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+ loop {
+ let _used = value;
+ //~^ NOTE value moved here
+ loop {
+ let _used2 = value; //~ ERROR use of moved value: `value`
+ //~^ NOTE value used here after move
+ }
+ }
+}
+
+fn moved_loop_1() {
+ let value = NonCopy{};
+ //~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+ loop {
+ let _used = value; //~ ERROR use of moved value: `value`
+ //~^ NOTE value moved here, in previous iteration of loop
+ }
+}
+
+fn moved_loop_2() {
+ let mut value = NonCopy{};
+ //~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+ let _used = value;
+ value = NonCopy{};
+ loop {
+ let _used2 = value; //~ ERROR use of moved value: `value`
+ //~^ NOTE value moved here, in previous iteration of loop
+ }
+}
+
+fn uninit_1() {
+ loop {
+ let value: NonCopy; //~ NOTE declared here
+ let _used = value; //~ ERROR binding `value` isn't initialized
+ //~^ NOTE `value` used here but it isn't initialized
+ }
+}
+
+fn uninit_2() {
+ let mut value: NonCopy; //~ NOTE declared here
+ loop {
+ let _used = value; //~ ERROR binding `value` isn't initialized
+ //~^ NOTE `value` used here but it isn't initialized
+ }
+}
+
+fn main() {}
diff --git a/src/test/ui/moves/issue-72649-uninit-in-loop.stderr b/src/test/ui/moves/issue-72649-uninit-in-loop.stderr
new file mode 100644
index 000000000..c7373b5be
--- /dev/null
+++ b/src/test/ui/moves/issue-72649-uninit-in-loop.stderr
@@ -0,0 +1,63 @@
+error[E0382]: use of moved value: `value`
+ --> $DIR/issue-72649-uninit-in-loop.rs:20:22
+ |
+LL | let value = NonCopy{};
+ | ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+LL |
+LL | let _used = value;
+ | ----- value moved here
+LL |
+LL | let _used2 = value;
+ | ^^^^^ value used here after move
+
+error[E0382]: use of moved value: `value`
+ --> $DIR/issue-72649-uninit-in-loop.rs:32:26
+ |
+LL | let value = NonCopy{};
+ | ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+...
+LL | let _used = value;
+ | ----- value moved here
+...
+LL | let _used2 = value;
+ | ^^^^^ value used here after move
+
+error[E0382]: use of moved value: `value`
+ --> $DIR/issue-72649-uninit-in-loop.rs:42:21
+ |
+LL | let value = NonCopy{};
+ | ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+...
+LL | let _used = value;
+ | ^^^^^ value moved here, in previous iteration of loop
+
+error[E0382]: use of moved value: `value`
+ --> $DIR/issue-72649-uninit-in-loop.rs:53:22
+ |
+LL | let mut value = NonCopy{};
+ | --------- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+...
+LL | let _used2 = value;
+ | ^^^^^ value moved here, in previous iteration of loop
+
+error[E0381]: used binding `value` isn't initialized
+ --> $DIR/issue-72649-uninit-in-loop.rs:61:21
+ |
+LL | let value: NonCopy;
+ | ----- binding declared here but left uninitialized
+LL | let _used = value;
+ | ^^^^^ `value` used here but it isn't initialized
+
+error[E0381]: used binding `value` isn't initialized
+ --> $DIR/issue-72649-uninit-in-loop.rs:69:21
+ |
+LL | let mut value: NonCopy;
+ | --------- binding declared here but left uninitialized
+LL | loop {
+LL | let _used = value;
+ | ^^^^^ `value` used here but it isn't initialized
+
+error: aborting due to 6 previous errors
+
+Some errors have detailed explanations: E0381, E0382.
+For more information about an error, try `rustc --explain E0381`.
diff --git a/src/test/ui/moves/issue-75904-move-closure-loop.rs b/src/test/ui/moves/issue-75904-move-closure-loop.rs
new file mode 100644
index 000000000..6641a0376
--- /dev/null
+++ b/src/test/ui/moves/issue-75904-move-closure-loop.rs
@@ -0,0 +1,15 @@
+// Regression test for issue #75904
+// Tests that we point at an expression
+// that required the upvar to be moved, rather than just borrowed.
+
+struct NotCopy;
+
+fn main() {
+ let mut a = NotCopy;
+ loop {
+ || { //~ ERROR use of moved value
+ &mut a;
+ a;
+ };
+ }
+}
diff --git a/src/test/ui/moves/issue-75904-move-closure-loop.stderr b/src/test/ui/moves/issue-75904-move-closure-loop.stderr
new file mode 100644
index 000000000..5e427a1fc
--- /dev/null
+++ b/src/test/ui/moves/issue-75904-move-closure-loop.stderr
@@ -0,0 +1,15 @@
+error[E0382]: use of moved value: `a`
+ --> $DIR/issue-75904-move-closure-loop.rs:10:9
+ |
+LL | let mut a = NotCopy;
+ | ----- move occurs because `a` has type `NotCopy`, which does not implement the `Copy` trait
+LL | loop {
+LL | || {
+ | ^^ value moved into closure here, in previous iteration of loop
+LL | &mut a;
+LL | a;
+ | - use occurs due to use in closure
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/moves/issue-99470-move-out-of-some.rs b/src/test/ui/moves/issue-99470-move-out-of-some.rs
new file mode 100644
index 000000000..f404cd3cd
--- /dev/null
+++ b/src/test/ui/moves/issue-99470-move-out-of-some.rs
@@ -0,0 +1,9 @@
+fn main() {
+ let x: &Option<Box<i32>> = &Some(Box::new(0));
+
+ match x {
+ //~^ ERROR cannot move out of `x` as enum variant `Some` which is behind a shared reference
+ &Some(_y) => (),
+ &None => (),
+ }
+}
diff --git a/src/test/ui/moves/issue-99470-move-out-of-some.stderr b/src/test/ui/moves/issue-99470-move-out-of-some.stderr
new file mode 100644
index 000000000..6e4a4e5ba
--- /dev/null
+++ b/src/test/ui/moves/issue-99470-move-out-of-some.stderr
@@ -0,0 +1,16 @@
+error[E0507]: cannot move out of `x` as enum variant `Some` which is behind a shared reference
+ --> $DIR/issue-99470-move-out-of-some.rs:4:11
+ |
+LL | match x {
+ | ^
+LL |
+LL | &Some(_y) => (),
+ | ---------
+ | | |
+ | | data moved here
+ | | move occurs because `_y` has type `Box<i32>`, which does not implement the `Copy` trait
+ | help: consider removing the `&`: `Some(_y)`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/moves/move-1-unique.rs b/src/test/ui/moves/move-1-unique.rs
new file mode 100644
index 000000000..f98d075d1
--- /dev/null
+++ b/src/test/ui/moves/move-1-unique.rs
@@ -0,0 +1,25 @@
+// run-pass
+#![allow(unused_mut)]
+#![allow(dead_code)]
+
+#[derive(Clone)]
+struct Triple {
+ x: isize,
+ y: isize,
+ z: isize,
+}
+
+fn test(x: bool, foo: Box<Triple>) -> isize {
+ let bar = foo;
+ let mut y: Box<Triple>;
+ if x { y = bar; } else { y = Box::new(Triple{x: 4, y: 5, z: 6}); }
+ return y.y;
+}
+
+pub fn main() {
+ let x: Box<_> = Box::new(Triple{x: 1, y: 2, z: 3});
+ assert_eq!(test(true, x.clone()), 2);
+ assert_eq!(test(true, x.clone()), 2);
+ assert_eq!(test(true, x.clone()), 2);
+ assert_eq!(test(false, x), 5);
+}
diff --git a/src/test/ui/moves/move-2-unique.rs b/src/test/ui/moves/move-2-unique.rs
new file mode 100644
index 000000000..8fda3c1c8
--- /dev/null
+++ b/src/test/ui/moves/move-2-unique.rs
@@ -0,0 +1,10 @@
+// run-pass
+#![allow(dead_code)]
+
+struct X { x: isize, y: isize, z: isize }
+
+pub fn main() {
+ let x: Box<_> = Box::new(X {x: 1, y: 2, z: 3});
+ let y = x;
+ assert_eq!(y.y, 2);
+}
diff --git a/src/test/ui/moves/move-2.rs b/src/test/ui/moves/move-2.rs
new file mode 100644
index 000000000..5e0100874
--- /dev/null
+++ b/src/test/ui/moves/move-2.rs
@@ -0,0 +1,6 @@
+// run-pass
+#![allow(dead_code)]
+
+struct X { x: isize, y: isize, z: isize }
+
+pub fn main() { let x: Box<_> = Box::new(X {x: 1, y: 2, z: 3}); let y = x; assert_eq!(y.y, 2); }
diff --git a/src/test/ui/moves/move-3-unique.rs b/src/test/ui/moves/move-3-unique.rs
new file mode 100644
index 000000000..8e5df2c3f
--- /dev/null
+++ b/src/test/ui/moves/move-3-unique.rs
@@ -0,0 +1,25 @@
+// run-pass
+#![allow(unused_mut)]
+#![allow(dead_code)]
+
+#[derive(Clone)]
+struct Triple {
+ x: isize,
+ y: isize,
+ z: isize,
+}
+
+fn test(x: bool, foo: Box<Triple>) -> isize {
+ let bar = foo;
+ let mut y: Box<Triple>;
+ if x { y = bar; } else { y = Box::new(Triple {x: 4, y: 5, z: 6}); }
+ return y.y;
+}
+
+pub fn main() {
+ let x: Box<_> = Box::new(Triple{x: 1, y: 2, z: 3});
+ for _ in 0_usize..10000_usize {
+ assert_eq!(test(true, x.clone()), 2);
+ }
+ assert_eq!(test(false, x), 5);
+}
diff --git a/src/test/ui/moves/move-4-unique.rs b/src/test/ui/moves/move-4-unique.rs
new file mode 100644
index 000000000..24aec7ea6
--- /dev/null
+++ b/src/test/ui/moves/move-4-unique.rs
@@ -0,0 +1,18 @@
+// run-pass
+#![allow(dead_code)]
+
+struct Triple {a: isize, b: isize, c: isize}
+
+fn test(foo: Box<Triple>) -> Box<Triple> {
+ let foo = foo;
+ let bar = foo;
+ let baz = bar;
+ let quux = baz;
+ return quux;
+}
+
+pub fn main() {
+ let x = Box::new(Triple{a: 1, b: 2, c: 3});
+ let y = test(x);
+ assert_eq!(y.c, 3);
+}
diff --git a/src/test/ui/moves/move-4.rs b/src/test/ui/moves/move-4.rs
new file mode 100644
index 000000000..63aa031a6
--- /dev/null
+++ b/src/test/ui/moves/move-4.rs
@@ -0,0 +1,18 @@
+// run-pass
+#![allow(dead_code)]
+
+struct Triple { a: isize, b: isize, c: isize }
+
+fn test(foo: Box<Triple>) -> Box<Triple> {
+ let foo = foo;
+ let bar = foo;
+ let baz = bar;
+ let quux = baz;
+ return quux;
+}
+
+pub fn main() {
+ let x = Box::new(Triple{ a: 1, b: 2, c: 3 });
+ let y = test(x);
+ assert_eq!(y.c, 3);
+}
diff --git a/src/test/ui/moves/move-arg-2-unique.rs b/src/test/ui/moves/move-arg-2-unique.rs
new file mode 100644
index 000000000..9622c8375
--- /dev/null
+++ b/src/test/ui/moves/move-arg-2-unique.rs
@@ -0,0 +1,12 @@
+// run-pass
+
+fn test(foo: Box<Vec<isize>> ) { assert_eq!((*foo)[0], 10); }
+
+pub fn main() {
+ let x = Box::new(vec![10]);
+ // Test forgetting a local by move-in
+ test(x);
+
+ // Test forgetting a temporary by move-in.
+ test(Box::new(vec![10]));
+}
diff --git a/src/test/ui/moves/move-arg-2.rs b/src/test/ui/moves/move-arg-2.rs
new file mode 100644
index 000000000..77ee06e19
--- /dev/null
+++ b/src/test/ui/moves/move-arg-2.rs
@@ -0,0 +1,12 @@
+// run-pass
+
+fn test(foo: Box<Vec<isize>>) { assert_eq!((*foo)[0], 10); }
+
+pub fn main() {
+ let x = Box::new(vec![10]);
+ // Test forgetting a local by move-in
+ test(x);
+
+ // Test forgetting a temporary by move-in.
+ test(Box::new(vec![10]));
+}
diff --git a/src/test/ui/moves/move-arg.rs b/src/test/ui/moves/move-arg.rs
new file mode 100644
index 000000000..5942cd89f
--- /dev/null
+++ b/src/test/ui/moves/move-arg.rs
@@ -0,0 +1,5 @@
+// run-pass
+
+fn test(foo: isize) { assert_eq!(foo, 10); }
+
+pub fn main() { let x = 10; test(x); }
diff --git a/src/test/ui/moves/move-deref-coercion.rs b/src/test/ui/moves/move-deref-coercion.rs
new file mode 100644
index 000000000..41154388f
--- /dev/null
+++ b/src/test/ui/moves/move-deref-coercion.rs
@@ -0,0 +1,33 @@
+use std::ops::Deref;
+
+struct NotCopy {
+ inner: bool
+}
+
+impl NotCopy {
+ fn inner_method(&self) {}
+}
+
+struct Foo {
+ first: NotCopy,
+ second: NotCopy
+}
+
+impl Deref for Foo {
+ type Target = NotCopy;
+ fn deref(&self) -> &NotCopy {
+ &self.second
+ }
+}
+
+fn use_field(val: Foo) {
+ let _val = val.first;
+ val.inner; //~ ERROR borrow of
+}
+
+fn use_method(val: Foo) {
+ let _val = val.first;
+ val.inner_method(); //~ ERROR borrow of
+}
+
+fn main() {}
diff --git a/src/test/ui/moves/move-deref-coercion.stderr b/src/test/ui/moves/move-deref-coercion.stderr
new file mode 100644
index 000000000..5760f4a7f
--- /dev/null
+++ b/src/test/ui/moves/move-deref-coercion.stderr
@@ -0,0 +1,35 @@
+error[E0382]: borrow of partially moved value: `val`
+ --> $DIR/move-deref-coercion.rs:25:5
+ |
+LL | let _val = val.first;
+ | --------- value partially moved here
+LL | val.inner;
+ | ^^^^^^^^^ value borrowed here after partial move
+ |
+ = note: partial move occurs because `val.first` has type `NotCopy`, which does not implement the `Copy` trait
+ = note: borrow occurs due to deref coercion to `NotCopy`
+note: deref defined here
+ --> $DIR/move-deref-coercion.rs:17:5
+ |
+LL | type Target = NotCopy;
+ | ^^^^^^^^^^^
+
+error[E0382]: borrow of partially moved value: `val`
+ --> $DIR/move-deref-coercion.rs:30:5
+ |
+LL | let _val = val.first;
+ | --------- value partially moved here
+LL | val.inner_method();
+ | ^^^^^^^^^^^^^^^^^^ value borrowed here after partial move
+ |
+ = note: partial move occurs because `val.first` has type `NotCopy`, which does not implement the `Copy` trait
+ = note: borrow occurs due to deref coercion to `NotCopy`
+note: deref defined here
+ --> $DIR/move-deref-coercion.rs:17:5
+ |
+LL | type Target = NotCopy;
+ | ^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/moves/move-fn-self-receiver.rs b/src/test/ui/moves/move-fn-self-receiver.rs
new file mode 100644
index 000000000..946642ef6
--- /dev/null
+++ b/src/test/ui/moves/move-fn-self-receiver.rs
@@ -0,0 +1,79 @@
+use std::pin::Pin;
+use std::rc::Rc;
+use std::ops::Add;
+
+struct Foo;
+
+impl Add for Foo {
+ type Output = ();
+ fn add(self, _rhs: Self) -> () {}
+}
+
+impl Foo {
+ fn use_self(self) {}
+ fn use_box_self(self: Box<Self>) {}
+ fn use_pin_box_self(self: Pin<Box<Self>>) {}
+ fn use_rc_self(self: Rc<Self>) {}
+ fn use_mut_self(&mut self) -> &mut Self { self }
+}
+
+struct Container(Vec<bool>);
+
+impl Container {
+ fn custom_into_iter(self) -> impl Iterator<Item = bool> {
+ self.0.into_iter()
+ }
+}
+
+fn move_out(val: Container) {
+ val.0.into_iter().next();
+ val.0; //~ ERROR use of moved
+
+ let foo = Foo;
+ foo.use_self();
+ foo; //~ ERROR use of moved
+
+ let second_foo = Foo;
+ second_foo.use_self();
+ second_foo; //~ ERROR use of moved
+
+ let boxed_foo = Box::new(Foo);
+ boxed_foo.use_box_self();
+ boxed_foo; //~ ERROR use of moved
+
+ let pin_box_foo = Box::pin(Foo);
+ pin_box_foo.use_pin_box_self();
+ pin_box_foo; //~ ERROR use of moved
+
+ let mut mut_foo = Foo;
+ let ret = mut_foo.use_mut_self();
+ mut_foo; //~ ERROR cannot move out
+ ret;
+
+ let rc_foo = Rc::new(Foo);
+ rc_foo.use_rc_self();
+ rc_foo; //~ ERROR use of moved
+
+ let foo_add = Foo;
+ foo_add + Foo;
+ foo_add; //~ ERROR use of moved
+
+ let implicit_into_iter = vec![true];
+ for _val in implicit_into_iter {}
+ implicit_into_iter; //~ ERROR use of moved
+
+ let explicit_into_iter = vec![true];
+ for _val in explicit_into_iter.into_iter() {}
+ explicit_into_iter; //~ ERROR use of moved
+
+ let container = Container(vec![]);
+ for _val in container.custom_into_iter() {}
+ container; //~ ERROR use of moved
+
+ let foo2 = Foo;
+ loop {
+ foo2.use_self(); //~ ERROR use of moved
+ }
+}
+
+fn main() {}
diff --git a/src/test/ui/moves/move-fn-self-receiver.stderr b/src/test/ui/moves/move-fn-self-receiver.stderr
new file mode 100644
index 000000000..3a686121a
--- /dev/null
+++ b/src/test/ui/moves/move-fn-self-receiver.stderr
@@ -0,0 +1,169 @@
+error[E0382]: use of moved value: `val.0`
+ --> $DIR/move-fn-self-receiver.rs:30:5
+ |
+LL | val.0.into_iter().next();
+ | ----------- `val.0` moved due to this method call
+LL | val.0;
+ | ^^^^^ value used here after move
+ |
+note: this function takes ownership of the receiver `self`, which moves `val.0`
+ --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
+ |
+LL | fn into_iter(self) -> Self::IntoIter;
+ | ^^^^
+ = note: move occurs because `val.0` has type `Vec<bool>`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `foo`
+ --> $DIR/move-fn-self-receiver.rs:34:5
+ |
+LL | let foo = Foo;
+ | --- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait
+LL | foo.use_self();
+ | ---------- `foo` moved due to this method call
+LL | foo;
+ | ^^^ value used here after move
+ |
+note: this function takes ownership of the receiver `self`, which moves `foo`
+ --> $DIR/move-fn-self-receiver.rs:13:17
+ |
+LL | fn use_self(self) {}
+ | ^^^^
+
+error[E0382]: use of moved value: `second_foo`
+ --> $DIR/move-fn-self-receiver.rs:38:5
+ |
+LL | let second_foo = Foo;
+ | ---------- move occurs because `second_foo` has type `Foo`, which does not implement the `Copy` trait
+LL | second_foo.use_self();
+ | ---------- `second_foo` moved due to this method call
+LL | second_foo;
+ | ^^^^^^^^^^ value used here after move
+
+error[E0382]: use of moved value: `boxed_foo`
+ --> $DIR/move-fn-self-receiver.rs:42:5
+ |
+LL | let boxed_foo = Box::new(Foo);
+ | --------- move occurs because `boxed_foo` has type `Box<Foo>`, which does not implement the `Copy` trait
+LL | boxed_foo.use_box_self();
+ | -------------- `boxed_foo` moved due to this method call
+LL | boxed_foo;
+ | ^^^^^^^^^ value used here after move
+ |
+note: this function takes ownership of the receiver `self`, which moves `boxed_foo`
+ --> $DIR/move-fn-self-receiver.rs:14:21
+ |
+LL | fn use_box_self(self: Box<Self>) {}
+ | ^^^^
+
+error[E0382]: use of moved value: `pin_box_foo`
+ --> $DIR/move-fn-self-receiver.rs:46:5
+ |
+LL | let pin_box_foo = Box::pin(Foo);
+ | ----------- move occurs because `pin_box_foo` has type `Pin<Box<Foo>>`, which does not implement the `Copy` trait
+LL | pin_box_foo.use_pin_box_self();
+ | ------------------ `pin_box_foo` moved due to this method call
+LL | pin_box_foo;
+ | ^^^^^^^^^^^ value used here after move
+ |
+note: this function takes ownership of the receiver `self`, which moves `pin_box_foo`
+ --> $DIR/move-fn-self-receiver.rs:15:25
+ |
+LL | fn use_pin_box_self(self: Pin<Box<Self>>) {}
+ | ^^^^
+
+error[E0505]: cannot move out of `mut_foo` because it is borrowed
+ --> $DIR/move-fn-self-receiver.rs:50:5
+ |
+LL | let ret = mut_foo.use_mut_self();
+ | ---------------------- borrow of `mut_foo` occurs here
+LL | mut_foo;
+ | ^^^^^^^ move out of `mut_foo` occurs here
+LL | ret;
+ | --- borrow later used here
+
+error[E0382]: use of moved value: `rc_foo`
+ --> $DIR/move-fn-self-receiver.rs:55:5
+ |
+LL | let rc_foo = Rc::new(Foo);
+ | ------ move occurs because `rc_foo` has type `Rc<Foo>`, which does not implement the `Copy` trait
+LL | rc_foo.use_rc_self();
+ | ------------- `rc_foo` moved due to this method call
+LL | rc_foo;
+ | ^^^^^^ value used here after move
+ |
+note: this function takes ownership of the receiver `self`, which moves `rc_foo`
+ --> $DIR/move-fn-self-receiver.rs:16:20
+ |
+LL | fn use_rc_self(self: Rc<Self>) {}
+ | ^^^^
+
+error[E0382]: use of moved value: `foo_add`
+ --> $DIR/move-fn-self-receiver.rs:59:5
+ |
+LL | let foo_add = Foo;
+ | ------- move occurs because `foo_add` has type `Foo`, which does not implement the `Copy` trait
+LL | foo_add + Foo;
+ | ------------- `foo_add` moved due to usage in operator
+LL | foo_add;
+ | ^^^^^^^ value used here after move
+ |
+note: calling this operator moves the left-hand side
+ --> $SRC_DIR/core/src/ops/arith.rs:LL:COL
+ |
+LL | fn add(self, rhs: Rhs) -> Self::Output;
+ | ^^^^
+
+error[E0382]: use of moved value: `implicit_into_iter`
+ --> $DIR/move-fn-self-receiver.rs:63:5
+ |
+LL | let implicit_into_iter = vec![true];
+ | ------------------ move occurs because `implicit_into_iter` has type `Vec<bool>`, which does not implement the `Copy` trait
+LL | for _val in implicit_into_iter {}
+ | ------------------ `implicit_into_iter` moved due to this implicit call to `.into_iter()`
+LL | implicit_into_iter;
+ | ^^^^^^^^^^^^^^^^^^ value used here after move
+ |
+help: consider iterating over a slice of the `Vec<bool>`'s content to avoid moving into the `for` loop
+ |
+LL | for _val in &implicit_into_iter {}
+ | +
+
+error[E0382]: use of moved value: `explicit_into_iter`
+ --> $DIR/move-fn-self-receiver.rs:67:5
+ |
+LL | let explicit_into_iter = vec![true];
+ | ------------------ move occurs because `explicit_into_iter` has type `Vec<bool>`, which does not implement the `Copy` trait
+LL | for _val in explicit_into_iter.into_iter() {}
+ | ----------- `explicit_into_iter` moved due to this method call
+LL | explicit_into_iter;
+ | ^^^^^^^^^^^^^^^^^^ value used here after move
+
+error[E0382]: use of moved value: `container`
+ --> $DIR/move-fn-self-receiver.rs:71:5
+ |
+LL | let container = Container(vec![]);
+ | --------- move occurs because `container` has type `Container`, which does not implement the `Copy` trait
+LL | for _val in container.custom_into_iter() {}
+ | ------------------ `container` moved due to this method call
+LL | container;
+ | ^^^^^^^^^ value used here after move
+ |
+note: this function takes ownership of the receiver `self`, which moves `container`
+ --> $DIR/move-fn-self-receiver.rs:23:25
+ |
+LL | fn custom_into_iter(self) -> impl Iterator<Item = bool> {
+ | ^^^^
+
+error[E0382]: use of moved value: `foo2`
+ --> $DIR/move-fn-self-receiver.rs:75:9
+ |
+LL | let foo2 = Foo;
+ | ---- move occurs because `foo2` has type `Foo`, which does not implement the `Copy` trait
+LL | loop {
+LL | foo2.use_self();
+ | ^^^^ ---------- `foo2` moved due to this method call, in previous iteration of loop
+
+error: aborting due to 12 previous errors
+
+Some errors have detailed explanations: E0382, E0505.
+For more information about an error, try `rustc --explain E0382`.
diff --git a/src/test/ui/moves/move-guard-same-consts.rs b/src/test/ui/moves/move-guard-same-consts.rs
new file mode 100644
index 000000000..b96ef8e19
--- /dev/null
+++ b/src/test/ui/moves/move-guard-same-consts.rs
@@ -0,0 +1,25 @@
+// #47295: We used to have a hack of special-casing adjacent amtch
+// arms whose patterns were composed solely of constants to not have
+// them linked in the cfg.
+//
+// This was broken for various reasons. In particular, that hack was
+// originally authored under the assunption that other checks
+// elsewhere would ensure that the two patterns did not overlap. But
+// that assumption did not hold, at least not in the long run (namely,
+// overlapping patterns were turned into warnings rather than errors).
+
+
+
+fn main() {
+ let x: Box<_> = Box::new(1);
+
+ let v = (1, 2);
+
+ match v {
+ (1, 2) if take(x) => (),
+ (1, 2) if take(x) => (), //~ ERROR use of moved value: `x`
+ _ => (),
+ }
+}
+
+fn take<T>(_: T) -> bool { false }
diff --git a/src/test/ui/moves/move-guard-same-consts.stderr b/src/test/ui/moves/move-guard-same-consts.stderr
new file mode 100644
index 000000000..2048fefef
--- /dev/null
+++ b/src/test/ui/moves/move-guard-same-consts.stderr
@@ -0,0 +1,14 @@
+error[E0382]: use of moved value: `x`
+ --> $DIR/move-guard-same-consts.rs:20:24
+ |
+LL | let x: Box<_> = Box::new(1);
+ | - move occurs because `x` has type `Box<i32>`, which does not implement the `Copy` trait
+...
+LL | (1, 2) if take(x) => (),
+ | - value moved here
+LL | (1, 2) if take(x) => (),
+ | ^ value used here after move
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/moves/move-in-guard-1.rs b/src/test/ui/moves/move-in-guard-1.rs
new file mode 100644
index 000000000..36e39fea6
--- /dev/null
+++ b/src/test/ui/moves/move-in-guard-1.rs
@@ -0,0 +1,15 @@
+pub fn main() {
+
+
+ let x: Box<_> = Box::new(1);
+
+ let v = (1, 2);
+
+ match v {
+ (1, _) if take(x) => (),
+ (_, 2) if take(x) => (), //~ ERROR use of moved value: `x`
+ _ => (),
+ }
+}
+
+fn take<T>(_: T) -> bool { false }
diff --git a/src/test/ui/moves/move-in-guard-1.stderr b/src/test/ui/moves/move-in-guard-1.stderr
new file mode 100644
index 000000000..5e9aa66b9
--- /dev/null
+++ b/src/test/ui/moves/move-in-guard-1.stderr
@@ -0,0 +1,14 @@
+error[E0382]: use of moved value: `x`
+ --> $DIR/move-in-guard-1.rs:10:24
+ |
+LL | let x: Box<_> = Box::new(1);
+ | - move occurs because `x` has type `Box<i32>`, which does not implement the `Copy` trait
+...
+LL | (1, _) if take(x) => (),
+ | - value moved here
+LL | (_, 2) if take(x) => (),
+ | ^ value used here after move
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/moves/move-in-guard-2.rs b/src/test/ui/moves/move-in-guard-2.rs
new file mode 100644
index 000000000..085b7ec6e
--- /dev/null
+++ b/src/test/ui/moves/move-in-guard-2.rs
@@ -0,0 +1,13 @@
+pub fn main() {
+ let x: Box<_> = Box::new(1);
+
+ let v = (1, 2);
+
+ match v {
+ (1, _) |
+ (_, 2) if take(x) => (), //~ ERROR use of moved value: `x`
+ _ => (),
+ }
+}
+
+fn take<T>(_: T) -> bool { false }
diff --git a/src/test/ui/moves/move-in-guard-2.stderr b/src/test/ui/moves/move-in-guard-2.stderr
new file mode 100644
index 000000000..8d636c11b
--- /dev/null
+++ b/src/test/ui/moves/move-in-guard-2.stderr
@@ -0,0 +1,12 @@
+error[E0382]: use of moved value: `x`
+ --> $DIR/move-in-guard-2.rs:8:24
+ |
+LL | let x: Box<_> = Box::new(1);
+ | - move occurs because `x` has type `Box<i32>`, which does not implement the `Copy` trait
+...
+LL | (_, 2) if take(x) => (),
+ | ^ value used here after move
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/moves/move-into-dead-array-1.rs b/src/test/ui/moves/move-into-dead-array-1.rs
new file mode 100644
index 000000000..0b8d76def
--- /dev/null
+++ b/src/test/ui/moves/move-into-dead-array-1.rs
@@ -0,0 +1,15 @@
+// Ensure that we cannot move into an uninitialized fixed-size array.
+
+struct D { _x: u8 }
+
+fn d() -> D { D { _x: 0 } }
+
+fn main() {
+ foo(1);
+ foo(3);
+}
+
+fn foo(i: usize) {
+ let mut a: [D; 4];
+ a[i] = d(); //~ ERROR E0381
+}
diff --git a/src/test/ui/moves/move-into-dead-array-1.stderr b/src/test/ui/moves/move-into-dead-array-1.stderr
new file mode 100644
index 000000000..344a6bbf0
--- /dev/null
+++ b/src/test/ui/moves/move-into-dead-array-1.stderr
@@ -0,0 +1,11 @@
+error[E0381]: used binding `a` isn't initialized
+ --> $DIR/move-into-dead-array-1.rs:14:5
+ |
+LL | let mut a: [D; 4];
+ | ----- binding declared here but left uninitialized
+LL | a[i] = d();
+ | ^^^^ `a` used here but it isn't initialized
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0381`.
diff --git a/src/test/ui/moves/move-into-dead-array-2.rs b/src/test/ui/moves/move-into-dead-array-2.rs
new file mode 100644
index 000000000..9b66ea35f
--- /dev/null
+++ b/src/test/ui/moves/move-into-dead-array-2.rs
@@ -0,0 +1,15 @@
+// Ensure that we cannot move into an uninitialized fixed-size array.
+
+struct D { _x: u8 }
+
+fn d() -> D { D { _x: 0 } }
+
+fn main() {
+ foo([d(), d(), d(), d()], 1);
+ foo([d(), d(), d(), d()], 3);
+}
+
+fn foo(mut a: [D; 4], i: usize) {
+ drop(a);
+ a[i] = d(); //~ ERROR use of moved value: `a`
+}
diff --git a/src/test/ui/moves/move-into-dead-array-2.stderr b/src/test/ui/moves/move-into-dead-array-2.stderr
new file mode 100644
index 000000000..19e476c04
--- /dev/null
+++ b/src/test/ui/moves/move-into-dead-array-2.stderr
@@ -0,0 +1,13 @@
+error[E0382]: use of moved value: `a`
+ --> $DIR/move-into-dead-array-2.rs:14:5
+ |
+LL | fn foo(mut a: [D; 4], i: usize) {
+ | ----- move occurs because `a` has type `[D; 4]`, which does not implement the `Copy` trait
+LL | drop(a);
+ | - value moved here
+LL | a[i] = d();
+ | ^^^^ value used here after move
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/moves/move-nullary-fn.rs b/src/test/ui/moves/move-nullary-fn.rs
new file mode 100644
index 000000000..14c9262c7
--- /dev/null
+++ b/src/test/ui/moves/move-nullary-fn.rs
@@ -0,0 +1,13 @@
+// run-pass
+// Issue #922
+// pretty-expanded FIXME #23616
+
+fn f2<F>(_thing: F) where F: FnOnce() { }
+
+fn f<F>(thing: F) where F: FnOnce() {
+ f2(thing);
+}
+
+pub fn main() {
+ f(|| {});
+}
diff --git a/src/test/ui/moves/move-of-addr-of-mut.rs b/src/test/ui/moves/move-of-addr-of-mut.rs
new file mode 100644
index 000000000..19fd70286
--- /dev/null
+++ b/src/test/ui/moves/move-of-addr-of-mut.rs
@@ -0,0 +1,12 @@
+// Ensure that taking a mutable raw ptr to an uninitialized variable does not change its
+// initializedness.
+
+struct S;
+
+fn main() {
+ let mut x: S;
+ std::ptr::addr_of_mut!(x); //~ ERROR E0381
+
+ let y = x; // Should error here if `addr_of_mut` is ever allowed on uninitialized variables
+ drop(y);
+}
diff --git a/src/test/ui/moves/move-of-addr-of-mut.stderr b/src/test/ui/moves/move-of-addr-of-mut.stderr
new file mode 100644
index 000000000..e75f2b1c0
--- /dev/null
+++ b/src/test/ui/moves/move-of-addr-of-mut.stderr
@@ -0,0 +1,13 @@
+error[E0381]: used binding `x` isn't initialized
+ --> $DIR/move-of-addr-of-mut.rs:8:5
+ |
+LL | let mut x: S;
+ | ----- binding declared here but left uninitialized
+LL | std::ptr::addr_of_mut!(x);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ `x` used here but it isn't initialized
+ |
+ = note: this error originates in the macro `std::ptr::addr_of_mut` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0381`.
diff --git a/src/test/ui/moves/move-out-of-array-1.rs b/src/test/ui/moves/move-out-of-array-1.rs
new file mode 100644
index 000000000..77cb73e47
--- /dev/null
+++ b/src/test/ui/moves/move-out-of-array-1.rs
@@ -0,0 +1,18 @@
+// Ensure that we cannot move out of a fixed-size array (especially
+// when the element type has a destructor).
+
+
+struct D { _x: u8 }
+
+impl Drop for D { fn drop(&mut self) { } }
+
+fn main() {
+ fn d() -> D { D { _x: 0 } }
+
+ let _d1 = foo([d(), d(), d(), d()], 1);
+ let _d3 = foo([d(), d(), d(), d()], 3);
+}
+
+fn foo(a: [D; 4], i: usize) -> D {
+ a[i] //~ ERROR cannot move out of type `[D; 4]`, a non-copy array
+}
diff --git a/src/test/ui/moves/move-out-of-array-1.stderr b/src/test/ui/moves/move-out-of-array-1.stderr
new file mode 100644
index 000000000..0af083e5b
--- /dev/null
+++ b/src/test/ui/moves/move-out-of-array-1.stderr
@@ -0,0 +1,12 @@
+error[E0508]: cannot move out of type `[D; 4]`, a non-copy array
+ --> $DIR/move-out-of-array-1.rs:17:5
+ |
+LL | a[i]
+ | ^^^^
+ | |
+ | cannot move out of here
+ | move occurs because `a[_]` has type `D`, which does not implement the `Copy` trait
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0508`.
diff --git a/src/test/ui/moves/move-out-of-array-ref.rs b/src/test/ui/moves/move-out-of-array-ref.rs
new file mode 100644
index 000000000..343f00ff2
--- /dev/null
+++ b/src/test/ui/moves/move-out-of-array-ref.rs
@@ -0,0 +1,34 @@
+// Ensure that we cannot move out of a reference to a fixed-size array
+
+struct D { _x: u8 }
+
+impl Drop for D { fn drop(&mut self) { } }
+
+fn move_elem(a: &[D; 4]) -> D {
+ let [_, e, _, _] = *a; //~ ERROR cannot move
+ e
+}
+
+fn move_subarr(a: &[D; 4]) -> [D; 2] {
+ let [_, s @ .. , _] = *a; //~ ERROR cannot move
+ s
+}
+
+fn move_elem_mut(a: &mut [D; 4]) -> D {
+ let [_, e, _, _] = *a; //~ ERROR cannot move
+ e
+}
+
+fn move_subarr_mut(a: &mut [D; 4]) -> [D; 2] {
+ let [_, s @ .. , _] = *a; //~ ERROR cannot move
+ s
+}
+
+fn main() {
+ fn d() -> D { D { _x: 0 } }
+
+ move_elem(&[d(), d(), d(), d()]);
+ move_subarr(&[d(), d(), d(), d()]);
+ move_elem_mut(&mut [d(), d(), d(), d()]);
+ move_subarr_mut(&mut [d(), d(), d(), d()]);
+}
diff --git a/src/test/ui/moves/move-out-of-array-ref.stderr b/src/test/ui/moves/move-out-of-array-ref.stderr
new file mode 100644
index 000000000..fd682e56a
--- /dev/null
+++ b/src/test/ui/moves/move-out-of-array-ref.stderr
@@ -0,0 +1,47 @@
+error[E0508]: cannot move out of type `[D; 4]`, a non-copy array
+ --> $DIR/move-out-of-array-ref.rs:8:24
+ |
+LL | let [_, e, _, _] = *a;
+ | - ^^
+ | | |
+ | | cannot move out of here
+ | | help: consider borrowing here: `&*a`
+ | data moved here
+ | move occurs because `e` has type `D`, which does not implement the `Copy` trait
+
+error[E0508]: cannot move out of type `[D; 4]`, a non-copy array
+ --> $DIR/move-out-of-array-ref.rs:13:27
+ |
+LL | let [_, s @ .. , _] = *a;
+ | ------ ^^
+ | | |
+ | | cannot move out of here
+ | | help: consider borrowing here: `&*a`
+ | data moved here
+ | move occurs because `s` has type `[D; 2]`, which does not implement the `Copy` trait
+
+error[E0508]: cannot move out of type `[D; 4]`, a non-copy array
+ --> $DIR/move-out-of-array-ref.rs:18:24
+ |
+LL | let [_, e, _, _] = *a;
+ | - ^^
+ | | |
+ | | cannot move out of here
+ | | help: consider borrowing here: `&*a`
+ | data moved here
+ | move occurs because `e` has type `D`, which does not implement the `Copy` trait
+
+error[E0508]: cannot move out of type `[D; 4]`, a non-copy array
+ --> $DIR/move-out-of-array-ref.rs:23:27
+ |
+LL | let [_, s @ .. , _] = *a;
+ | ------ ^^
+ | | |
+ | | cannot move out of here
+ | | help: consider borrowing here: `&*a`
+ | data moved here
+ | move occurs because `s` has type `[D; 2]`, which does not implement the `Copy` trait
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0508`.
diff --git a/src/test/ui/moves/move-out-of-field.rs b/src/test/ui/moves/move-out-of-field.rs
new file mode 100644
index 000000000..9f697db4f
--- /dev/null
+++ b/src/test/ui/moves/move-out-of-field.rs
@@ -0,0 +1,27 @@
+// run-pass
+
+use std::string::String;
+
+struct StringBuffer {
+ s: String,
+}
+
+impl StringBuffer {
+ pub fn append(&mut self, v: &str) {
+ self.s.push_str(v);
+ }
+}
+
+fn to_string(sb: StringBuffer) -> String {
+ sb.s
+}
+
+pub fn main() {
+ let mut sb = StringBuffer {
+ s: String::new(),
+ };
+ sb.append("Hello, ");
+ sb.append("World!");
+ let str = to_string(sb);
+ assert_eq!(str, "Hello, World!");
+}
diff --git a/src/test/ui/moves/move-out-of-slice-1.rs b/src/test/ui/moves/move-out-of-slice-1.rs
new file mode 100644
index 000000000..982648f5b
--- /dev/null
+++ b/src/test/ui/moves/move-out-of-slice-1.rs
@@ -0,0 +1,11 @@
+#![feature(box_patterns)]
+
+struct A;
+
+fn main() {
+ let a: Box<[A]> = Box::new([A]);
+ match a { //~ ERROR cannot move out of type `[A]`, a non-copy slice
+ box [a] => {},
+ _ => {}
+ }
+}
diff --git a/src/test/ui/moves/move-out-of-slice-1.stderr b/src/test/ui/moves/move-out-of-slice-1.stderr
new file mode 100644
index 000000000..ce5ddb3e1
--- /dev/null
+++ b/src/test/ui/moves/move-out-of-slice-1.stderr
@@ -0,0 +1,14 @@
+error[E0508]: cannot move out of type `[A]`, a non-copy slice
+ --> $DIR/move-out-of-slice-1.rs:7:11
+ |
+LL | match a {
+ | ^ cannot move out of here
+LL | box [a] => {},
+ | -
+ | |
+ | data moved here
+ | move occurs because `a` has type `A`, which does not implement the `Copy` trait
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0508`.
diff --git a/src/test/ui/moves/move-out-of-slice-2.rs b/src/test/ui/moves/move-out-of-slice-2.rs
new file mode 100644
index 000000000..59c02d42b
--- /dev/null
+++ b/src/test/ui/moves/move-out-of-slice-2.rs
@@ -0,0 +1,35 @@
+#![feature(unsized_locals)]
+//~^ WARN the feature `unsized_locals` is incomplete
+
+struct A;
+#[derive(Clone, Copy)]
+struct C;
+
+fn main() {
+ let a: Box<[A]> = Box::new([A]);
+ match *a {
+ //~^ ERROR cannot move out of type `[A]`, a non-copy slice
+ [a @ ..] => {}
+ _ => {}
+ }
+ let b: Box<[A]> = Box::new([A, A, A]);
+ match *b {
+ //~^ ERROR cannot move out of type `[A]`, a non-copy slice
+ [_, _, b @ .., _] => {}
+ _ => {}
+ }
+
+ // `[C]` isn't `Copy`, even if `C` is.
+ let c: Box<[C]> = Box::new([C]);
+ match *c {
+ //~^ ERROR cannot move out of type `[C]`, a non-copy slice
+ [c @ ..] => {}
+ _ => {}
+ }
+ let d: Box<[C]> = Box::new([C, C, C]);
+ match *d {
+ //~^ ERROR cannot move out of type `[C]`, a non-copy slice
+ [_, _, d @ .., _] => {}
+ _ => {}
+ }
+}
diff --git a/src/test/ui/moves/move-out-of-slice-2.stderr b/src/test/ui/moves/move-out-of-slice-2.stderr
new file mode 100644
index 000000000..9a863bf31
--- /dev/null
+++ b/src/test/ui/moves/move-out-of-slice-2.stderr
@@ -0,0 +1,60 @@
+warning: the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/move-out-of-slice-2.rs:1:12
+ |
+LL | #![feature(unsized_locals)]
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+ = note: see issue #48055 <https://github.com/rust-lang/rust/issues/48055> for more information
+
+error[E0508]: cannot move out of type `[A]`, a non-copy slice
+ --> $DIR/move-out-of-slice-2.rs:10:11
+ |
+LL | match *a {
+ | ^^ cannot move out of here
+LL |
+LL | [a @ ..] => {}
+ | ------
+ | |
+ | data moved here
+ | move occurs because `a` has type `[A]`, which does not implement the `Copy` trait
+
+error[E0508]: cannot move out of type `[A]`, a non-copy slice
+ --> $DIR/move-out-of-slice-2.rs:16:11
+ |
+LL | match *b {
+ | ^^ cannot move out of here
+LL |
+LL | [_, _, b @ .., _] => {}
+ | ------
+ | |
+ | data moved here
+ | move occurs because `b` has type `[A]`, which does not implement the `Copy` trait
+
+error[E0508]: cannot move out of type `[C]`, a non-copy slice
+ --> $DIR/move-out-of-slice-2.rs:24:11
+ |
+LL | match *c {
+ | ^^ cannot move out of here
+LL |
+LL | [c @ ..] => {}
+ | ------
+ | |
+ | data moved here
+ | move occurs because `c` has type `[C]`, which does not implement the `Copy` trait
+
+error[E0508]: cannot move out of type `[C]`, a non-copy slice
+ --> $DIR/move-out-of-slice-2.rs:30:11
+ |
+LL | match *d {
+ | ^^ cannot move out of here
+LL |
+LL | [_, _, d @ .., _] => {}
+ | ------
+ | |
+ | data moved here
+ | move occurs because `d` has type `[C]`, which does not implement the `Copy` trait
+
+error: aborting due to 4 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0508`.
diff --git a/src/test/ui/moves/move-out-of-tuple-field.rs b/src/test/ui/moves/move-out-of-tuple-field.rs
new file mode 100644
index 000000000..66912fa4d
--- /dev/null
+++ b/src/test/ui/moves/move-out-of-tuple-field.rs
@@ -0,0 +1,13 @@
+struct Foo(Box<isize>);
+
+
+
+fn main() {
+ let x: (Box<_>,) = (Box::new(1),);
+ let y = x.0;
+ let z = x.0; //~ ERROR use of moved value: `x.0`
+
+ let x = Foo(Box::new(1));
+ let y = x.0;
+ let z = x.0; //~ ERROR use of moved value: `x.0`
+}
diff --git a/src/test/ui/moves/move-out-of-tuple-field.stderr b/src/test/ui/moves/move-out-of-tuple-field.stderr
new file mode 100644
index 000000000..bb4eb7677
--- /dev/null
+++ b/src/test/ui/moves/move-out-of-tuple-field.stderr
@@ -0,0 +1,23 @@
+error[E0382]: use of moved value: `x.0`
+ --> $DIR/move-out-of-tuple-field.rs:8:13
+ |
+LL | let y = x.0;
+ | --- value moved here
+LL | let z = x.0;
+ | ^^^ value used here after move
+ |
+ = note: move occurs because `x.0` has type `Box<i32>`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `x.0`
+ --> $DIR/move-out-of-tuple-field.rs:12:13
+ |
+LL | let y = x.0;
+ | --- value moved here
+LL | let z = x.0;
+ | ^^^ value used here after move
+ |
+ = note: move occurs because `x.0` has type `Box<isize>`, which does not implement the `Copy` trait
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/moves/move-scalar.rs b/src/test/ui/moves/move-scalar.rs
new file mode 100644
index 000000000..98bfeb1bc
--- /dev/null
+++ b/src/test/ui/moves/move-scalar.rs
@@ -0,0 +1,10 @@
+// run-pass
+#![allow(unused_mut)]
+
+pub fn main() {
+
+ let y: isize = 42;
+ let mut x: isize;
+ x = y;
+ assert_eq!(x, 42);
+}
diff --git a/src/test/ui/moves/moves-based-on-type-access-to-field.rs b/src/test/ui/moves/moves-based-on-type-access-to-field.rs
new file mode 100644
index 000000000..e2003ed6e
--- /dev/null
+++ b/src/test/ui/moves/moves-based-on-type-access-to-field.rs
@@ -0,0 +1,14 @@
+// Tests that if you move from `x.f` or `x[0]`, `x` is inaccessible.
+// Also tests that we give a more specific error message.
+
+struct Foo { f: String, y: isize }
+fn consume(_s: String) {}
+fn touch<A>(_a: &A) {}
+
+fn f20() {
+ let x = vec!["hi".to_string()];
+ consume(x.into_iter().next().unwrap());
+ touch(&x[0]); //~ ERROR borrow of moved value: `x`
+}
+
+fn main() {}
diff --git a/src/test/ui/moves/moves-based-on-type-access-to-field.stderr b/src/test/ui/moves/moves-based-on-type-access-to-field.stderr
new file mode 100644
index 000000000..3cc8ca291
--- /dev/null
+++ b/src/test/ui/moves/moves-based-on-type-access-to-field.stderr
@@ -0,0 +1,19 @@
+error[E0382]: borrow of moved value: `x`
+ --> $DIR/moves-based-on-type-access-to-field.rs:11:12
+ |
+LL | let x = vec!["hi".to_string()];
+ | - move occurs because `x` has type `Vec<String>`, which does not implement the `Copy` trait
+LL | consume(x.into_iter().next().unwrap());
+ | ----------- `x` moved due to this method call
+LL | touch(&x[0]);
+ | ^ value borrowed here after move
+ |
+note: this function takes ownership of the receiver `self`, which moves `x`
+ --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
+ |
+LL | fn into_iter(self) -> Self::IntoIter;
+ | ^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/moves/moves-based-on-type-block-bad.rs b/src/test/ui/moves/moves-based-on-type-block-bad.rs
new file mode 100644
index 000000000..eca33167f
--- /dev/null
+++ b/src/test/ui/moves/moves-based-on-type-block-bad.rs
@@ -0,0 +1,29 @@
+#![feature(box_patterns)]
+
+
+struct S {
+ x: Box<E>
+}
+
+enum E {
+ Foo(Box<S>),
+ Bar(Box<isize>),
+ Baz
+}
+
+fn f<G>(s: &S, g: G) where G: FnOnce(&S) {
+ g(s)
+}
+
+fn main() {
+ let s = S { x: Box::new(E::Bar(Box::new(42))) };
+ loop {
+ f(&s, |hellothere| {
+ match hellothere.x { //~ ERROR cannot move out
+ box E::Foo(_) => {}
+ box E::Bar(x) => println!("{}", x.to_string()),
+ box E::Baz => {}
+ }
+ })
+ }
+}
diff --git a/src/test/ui/moves/moves-based-on-type-block-bad.stderr b/src/test/ui/moves/moves-based-on-type-block-bad.stderr
new file mode 100644
index 000000000..5ed91a0d5
--- /dev/null
+++ b/src/test/ui/moves/moves-based-on-type-block-bad.stderr
@@ -0,0 +1,15 @@
+error[E0507]: cannot move out of `hellothere.x` as enum variant `Bar` which is behind a shared reference
+ --> $DIR/moves-based-on-type-block-bad.rs:22:19
+ |
+LL | match hellothere.x {
+ | ^^^^^^^^^^^^ help: consider borrowing here: `&hellothere.x`
+LL | box E::Foo(_) => {}
+LL | box E::Bar(x) => println!("{}", x.to_string()),
+ | -
+ | |
+ | data moved here
+ | move occurs because `x` has type `Box<isize>`, which does not implement the `Copy` trait
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/moves/moves-based-on-type-capture-clause-bad.rs b/src/test/ui/moves/moves-based-on-type-capture-clause-bad.rs
new file mode 100644
index 000000000..b2f68352f
--- /dev/null
+++ b/src/test/ui/moves/moves-based-on-type-capture-clause-bad.rs
@@ -0,0 +1,9 @@
+use std::thread;
+
+fn main() {
+ let x = "Hello world!".to_string();
+ thread::spawn(move|| {
+ println!("{}", x);
+ });
+ println!("{}", x); //~ ERROR borrow of moved value
+}
diff --git a/src/test/ui/moves/moves-based-on-type-capture-clause-bad.stderr b/src/test/ui/moves/moves-based-on-type-capture-clause-bad.stderr
new file mode 100644
index 000000000..34b7ea658
--- /dev/null
+++ b/src/test/ui/moves/moves-based-on-type-capture-clause-bad.stderr
@@ -0,0 +1,18 @@
+error[E0382]: borrow of moved value: `x`
+ --> $DIR/moves-based-on-type-capture-clause-bad.rs:8:20
+ |
+LL | let x = "Hello world!".to_string();
+ | - move occurs because `x` has type `String`, which does not implement the `Copy` trait
+LL | thread::spawn(move|| {
+ | ------ value moved into closure here
+LL | println!("{}", x);
+ | - variable moved due to use in closure
+LL | });
+LL | println!("{}", x);
+ | ^ value borrowed here after move
+ |
+ = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/moves/moves-based-on-type-capture-clause.rs b/src/test/ui/moves/moves-based-on-type-capture-clause.rs
new file mode 100644
index 000000000..4a6a4ed28
--- /dev/null
+++ b/src/test/ui/moves/moves-based-on-type-capture-clause.rs
@@ -0,0 +1,12 @@
+// run-pass
+#![allow(unused_must_use)]
+// ignore-emscripten no threads support
+
+use std::thread;
+
+pub fn main() {
+ let x = "Hello world!".to_string();
+ thread::spawn(move|| {
+ println!("{}", x);
+ }).join();
+}
diff --git a/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.rs b/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.rs
new file mode 100644
index 000000000..4417fb926
--- /dev/null
+++ b/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.rs
@@ -0,0 +1,20 @@
+// Test for a subtle failure computing kinds of cyclic types, in which
+// temporary kinds wound up being stored in a cache and used later.
+// See rustc::ty::type_contents() for more information.
+
+
+struct List { key: isize, next: Option<Box<List>> }
+
+fn foo(node: Box<List>) -> isize {
+ let r = match node.next {
+ Some(right) => consume(right),
+ None => 0
+ };
+ consume(node) + r //~ ERROR use of partially moved value: `node`
+}
+
+fn consume(v: Box<List>) -> isize {
+ v.key
+}
+
+fn main() {}
diff --git a/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.stderr b/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.stderr
new file mode 100644
index 000000000..a315bbaab
--- /dev/null
+++ b/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.stderr
@@ -0,0 +1,18 @@
+error[E0382]: use of partially moved value: `node`
+ --> $DIR/moves-based-on-type-cyclic-types-issue-4821.rs:13:13
+ |
+LL | Some(right) => consume(right),
+ | ----- value partially moved here
+...
+LL | consume(node) + r
+ | ^^^^ value used here after partial move
+ |
+ = note: partial move occurs because value has type `Box<List>`, which does not implement the `Copy` trait
+help: borrow this field in the pattern to avoid moving `node.next.0`
+ |
+LL | Some(ref right) => consume(right),
+ | +++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/moves/moves-based-on-type-distribute-copy-over-paren.rs b/src/test/ui/moves/moves-based-on-type-distribute-copy-over-paren.rs
new file mode 100644
index 000000000..d256e18b6
--- /dev/null
+++ b/src/test/ui/moves/moves-based-on-type-distribute-copy-over-paren.rs
@@ -0,0 +1,49 @@
+// Tests that references to move-by-default values trigger moves when
+// they occur as part of various kinds of expressions.
+
+struct Foo<A> { f: A }
+fn touch<A>(_a: &A) {}
+
+fn f00() {
+ let x = "hi".to_string();
+ //~^ NOTE move occurs because `x` has type `String`
+ let _y = Foo { f:x };
+ //~^ NOTE value moved here
+ touch(&x); //~ ERROR borrow of moved value: `x`
+ //~^ NOTE value borrowed here after move
+}
+
+fn f05() {
+ let x = "hi".to_string();
+ //~^ NOTE move occurs because `x` has type `String`
+ let _y = Foo { f:(((x))) };
+ //~^ NOTE value moved here
+ touch(&x); //~ ERROR borrow of moved value: `x`
+ //~^ NOTE value borrowed here after move
+}
+
+fn f10() {
+ let x = "hi".to_string();
+ let _y = Foo { f:x.clone() };
+ touch(&x);
+}
+
+fn f20() {
+ let x = "hi".to_string();
+ let _y = Foo { f:(x).clone() };
+ touch(&x);
+}
+
+fn f30() {
+ let x = "hi".to_string();
+ let _y = Foo { f:((x)).clone() };
+ touch(&x);
+}
+
+fn f40() {
+ let x = "hi".to_string();
+ let _y = Foo { f:(((((((x)).clone()))))) };
+ touch(&x);
+}
+
+fn main() {}
diff --git a/src/test/ui/moves/moves-based-on-type-distribute-copy-over-paren.stderr b/src/test/ui/moves/moves-based-on-type-distribute-copy-over-paren.stderr
new file mode 100644
index 000000000..ee7971691
--- /dev/null
+++ b/src/test/ui/moves/moves-based-on-type-distribute-copy-over-paren.stderr
@@ -0,0 +1,27 @@
+error[E0382]: borrow of moved value: `x`
+ --> $DIR/moves-based-on-type-distribute-copy-over-paren.rs:12:11
+ |
+LL | let x = "hi".to_string();
+ | - move occurs because `x` has type `String`, which does not implement the `Copy` trait
+LL |
+LL | let _y = Foo { f:x };
+ | - value moved here
+LL |
+LL | touch(&x);
+ | ^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `x`
+ --> $DIR/moves-based-on-type-distribute-copy-over-paren.rs:21:11
+ |
+LL | let x = "hi".to_string();
+ | - move occurs because `x` has type `String`, which does not implement the `Copy` trait
+LL |
+LL | let _y = Foo { f:(((x))) };
+ | ------- value moved here
+LL |
+LL | touch(&x);
+ | ^^ value borrowed here after move
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/moves/moves-based-on-type-exprs.rs b/src/test/ui/moves/moves-based-on-type-exprs.rs
new file mode 100644
index 000000000..4a52d8d32
--- /dev/null
+++ b/src/test/ui/moves/moves-based-on-type-exprs.rs
@@ -0,0 +1,93 @@
+// Tests that references to move-by-default values trigger moves when
+// they occur as part of various kinds of expressions.
+
+
+struct Foo<A> { f: A }
+fn guard(_s: String) -> bool {panic!()}
+fn touch<A>(_a: &A) {}
+
+fn f10() {
+ let x = "hi".to_string();
+ let _y = Foo { f:x };
+ touch(&x); //~ ERROR borrow of moved value: `x`
+}
+
+fn f20() {
+ let x = "hi".to_string();
+ let _y = (x, 3);
+ touch(&x); //~ ERROR borrow of moved value: `x`
+}
+
+fn f21() {
+ let x = vec![1, 2, 3];
+ let _y = (x[0], 3);
+ touch(&x);
+}
+
+fn f30(cond: bool) {
+ let x = "hi".to_string();
+ let y = "ho".to_string();
+ let _y = if cond {
+ x
+ } else {
+ y
+ };
+ touch(&x); //~ ERROR borrow of moved value: `x`
+ touch(&y); //~ ERROR borrow of moved value: `y`
+}
+
+fn f40(cond: bool) {
+ let x = "hi".to_string();
+ let y = "ho".to_string();
+ let _y = match cond {
+ true => x,
+ false => y
+ };
+ touch(&x); //~ ERROR borrow of moved value: `x`
+ touch(&y); //~ ERROR borrow of moved value: `y`
+}
+
+fn f50(cond: bool) {
+ let x = "hi".to_string();
+ let y = "ho".to_string();
+ let _y = match cond {
+ _ if guard(x) => 10,
+ true => 10,
+ false => 20,
+ };
+ touch(&x); //~ ERROR borrow of moved value: `x`
+ touch(&y);
+}
+
+fn f70() {
+ let x = "hi".to_string();
+ let _y = [x];
+ touch(&x); //~ ERROR borrow of moved value: `x`
+}
+
+fn f80() {
+ let x = "hi".to_string();
+ let _y = vec![x];
+ touch(&x); //~ ERROR borrow of moved value: `x`
+}
+
+fn f100() {
+ let x = vec!["hi".to_string()];
+ let _y = x.into_iter().next().unwrap();
+ touch(&x); //~ ERROR borrow of moved value: `x`
+}
+
+fn f110() {
+ let x = vec!["hi".to_string()];
+ let _y = [x.into_iter().next().unwrap(); 1];
+ touch(&x); //~ ERROR borrow of moved value: `x`
+}
+
+fn f120() {
+ let mut x = vec!["hi".to_string(), "ho".to_string()];
+ x.swap(0, 1);
+ touch(&x[0]);
+ touch(&x[1]);
+}
+
+fn main() {}
diff --git a/src/test/ui/moves/moves-based-on-type-exprs.stderr b/src/test/ui/moves/moves-based-on-type-exprs.stderr
new file mode 100644
index 000000000..9bcec3674
--- /dev/null
+++ b/src/test/ui/moves/moves-based-on-type-exprs.stderr
@@ -0,0 +1,135 @@
+error[E0382]: borrow of moved value: `x`
+ --> $DIR/moves-based-on-type-exprs.rs:12:11
+ |
+LL | let x = "hi".to_string();
+ | - move occurs because `x` has type `String`, which does not implement the `Copy` trait
+LL | let _y = Foo { f:x };
+ | - value moved here
+LL | touch(&x);
+ | ^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `x`
+ --> $DIR/moves-based-on-type-exprs.rs:18:11
+ |
+LL | let x = "hi".to_string();
+ | - move occurs because `x` has type `String`, which does not implement the `Copy` trait
+LL | let _y = (x, 3);
+ | - value moved here
+LL | touch(&x);
+ | ^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `x`
+ --> $DIR/moves-based-on-type-exprs.rs:35:11
+ |
+LL | let x = "hi".to_string();
+ | - move occurs because `x` has type `String`, which does not implement the `Copy` trait
+...
+LL | x
+ | - value moved here
+...
+LL | touch(&x);
+ | ^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `y`
+ --> $DIR/moves-based-on-type-exprs.rs:36:11
+ |
+LL | let y = "ho".to_string();
+ | - move occurs because `y` has type `String`, which does not implement the `Copy` trait
+...
+LL | y
+ | - value moved here
+...
+LL | touch(&y);
+ | ^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `x`
+ --> $DIR/moves-based-on-type-exprs.rs:46:11
+ |
+LL | let x = "hi".to_string();
+ | - move occurs because `x` has type `String`, which does not implement the `Copy` trait
+...
+LL | true => x,
+ | - value moved here
+...
+LL | touch(&x);
+ | ^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `y`
+ --> $DIR/moves-based-on-type-exprs.rs:47:11
+ |
+LL | let y = "ho".to_string();
+ | - move occurs because `y` has type `String`, which does not implement the `Copy` trait
+...
+LL | false => y
+ | - value moved here
+...
+LL | touch(&y);
+ | ^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `x`
+ --> $DIR/moves-based-on-type-exprs.rs:58:11
+ |
+LL | let x = "hi".to_string();
+ | - move occurs because `x` has type `String`, which does not implement the `Copy` trait
+...
+LL | _ if guard(x) => 10,
+ | - value moved here
+...
+LL | touch(&x);
+ | ^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `x`
+ --> $DIR/moves-based-on-type-exprs.rs:65:11
+ |
+LL | let x = "hi".to_string();
+ | - move occurs because `x` has type `String`, which does not implement the `Copy` trait
+LL | let _y = [x];
+ | - value moved here
+LL | touch(&x);
+ | ^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `x`
+ --> $DIR/moves-based-on-type-exprs.rs:71:11
+ |
+LL | let x = "hi".to_string();
+ | - move occurs because `x` has type `String`, which does not implement the `Copy` trait
+LL | let _y = vec![x];
+ | - value moved here
+LL | touch(&x);
+ | ^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `x`
+ --> $DIR/moves-based-on-type-exprs.rs:77:11
+ |
+LL | let x = vec!["hi".to_string()];
+ | - move occurs because `x` has type `Vec<String>`, which does not implement the `Copy` trait
+LL | let _y = x.into_iter().next().unwrap();
+ | ----------- `x` moved due to this method call
+LL | touch(&x);
+ | ^^ value borrowed here after move
+ |
+note: this function takes ownership of the receiver `self`, which moves `x`
+ --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
+ |
+LL | fn into_iter(self) -> Self::IntoIter;
+ | ^^^^
+
+error[E0382]: borrow of moved value: `x`
+ --> $DIR/moves-based-on-type-exprs.rs:83:11
+ |
+LL | let x = vec!["hi".to_string()];
+ | - move occurs because `x` has type `Vec<String>`, which does not implement the `Copy` trait
+LL | let _y = [x.into_iter().next().unwrap(); 1];
+ | ----------- `x` moved due to this method call
+LL | touch(&x);
+ | ^^ value borrowed here after move
+ |
+note: this function takes ownership of the receiver `self`, which moves `x`
+ --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
+ |
+LL | fn into_iter(self) -> Self::IntoIter;
+ | ^^^^
+
+error: aborting due to 11 previous errors
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/moves/moves-based-on-type-match-bindings.rs b/src/test/ui/moves/moves-based-on-type-match-bindings.rs
new file mode 100644
index 000000000..4fb9b40e8
--- /dev/null
+++ b/src/test/ui/moves/moves-based-on-type-match-bindings.rs
@@ -0,0 +1,21 @@
+// Tests that bindings to move-by-default values trigger moves of the
+// discriminant. Also tests that the compiler explains the move in
+// terms of the binding, not the discriminant.
+
+struct Foo<A> { f: A }
+fn guard(_s: String) -> bool {panic!()}
+fn touch<A>(_a: &A) {}
+
+fn f10() {
+ let x = Foo {f: "hi".to_string()};
+
+ let y = match x {
+ Foo {f} => {}
+ };
+
+ touch(&x); //~ ERROR borrow of partially moved value: `x`
+ //~^ value borrowed here after partial move
+ //~| partial move occurs because `x.f` has type `String`
+}
+
+fn main() {}
diff --git a/src/test/ui/moves/moves-based-on-type-match-bindings.stderr b/src/test/ui/moves/moves-based-on-type-match-bindings.stderr
new file mode 100644
index 000000000..ad1a2db8b
--- /dev/null
+++ b/src/test/ui/moves/moves-based-on-type-match-bindings.stderr
@@ -0,0 +1,14 @@
+error[E0382]: borrow of partially moved value: `x`
+ --> $DIR/moves-based-on-type-match-bindings.rs:16:11
+ |
+LL | Foo {f} => {}
+ | - value partially moved here
+...
+LL | touch(&x);
+ | ^^ value borrowed here after partial move
+ |
+ = note: partial move occurs because `x.f` has type `String`, which does not implement the `Copy` trait
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.rs b/src/test/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.rs
new file mode 100644
index 000000000..76b7aab54
--- /dev/null
+++ b/src/test/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.rs
@@ -0,0 +1,10 @@
+#![feature(unboxed_closures)]
+
+fn to_fn<A,F:Fn<A>>(f: F) -> F { f }
+
+fn test(_x: Box<usize>) {}
+
+fn main() {
+ let i = Box::new(3);
+ let _f = to_fn(|| test(i)); //~ ERROR cannot move out
+}
diff --git a/src/test/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr b/src/test/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr
new file mode 100644
index 000000000..125e446c3
--- /dev/null
+++ b/src/test/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr
@@ -0,0 +1,13 @@
+error[E0507]: cannot move out of `i`, a captured variable in an `Fn` closure
+ --> $DIR/moves-based-on-type-move-out-of-closure-env-issue-1965.rs:9:28
+ |
+LL | let i = Box::new(3);
+ | - captured outer variable
+LL | let _f = to_fn(|| test(i));
+ | -- ^ move occurs because `i` has type `Box<usize>`, which does not implement the `Copy` trait
+ | |
+ | captured by this `Fn` closure
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/moves/moves-based-on-type-no-recursive-stack-closure.rs b/src/test/ui/moves/moves-based-on-type-no-recursive-stack-closure.rs
new file mode 100644
index 000000000..86fd37e78
--- /dev/null
+++ b/src/test/ui/moves/moves-based-on-type-no-recursive-stack-closure.rs
@@ -0,0 +1,35 @@
+// Tests correct kind-checking of the reason stack closures without the :Copy
+// bound must be noncopyable. For details see
+// https://smallcultfollowing.com/babysteps/blog/2013/04/30/the-case-of-the-recurring-closure/
+
+struct R<'a> {
+ // This struct is needed to create the
+ // otherwise infinite type of a fn that
+ // accepts itself as argument:
+ c: Box<dyn FnMut(&mut R, bool) + 'a>
+}
+
+fn innocent_looking_victim() {
+ let mut x = Some("hello".to_string());
+ conspirator(|f, writer| {
+ if writer {
+ x = None;
+ } else {
+ match x {
+ Some(ref msg) => {
+ (f.c)(f, true);
+ //~^ ERROR: cannot borrow `*f` as mutable more than once at a time
+ println!("{}", msg);
+ },
+ None => panic!("oops"),
+ }
+ }
+ })
+}
+
+fn conspirator<F>(mut f: F) where F: FnMut(&mut R, bool) {
+ let mut r = R {c: Box::new(f)};
+ f(&mut r, false) //~ ERROR borrow of moved value
+}
+
+fn main() { innocent_looking_victim() }
diff --git a/src/test/ui/moves/moves-based-on-type-no-recursive-stack-closure.stderr b/src/test/ui/moves/moves-based-on-type-no-recursive-stack-closure.stderr
new file mode 100644
index 000000000..4759b4589
--- /dev/null
+++ b/src/test/ui/moves/moves-based-on-type-no-recursive-stack-closure.stderr
@@ -0,0 +1,28 @@
+error[E0499]: cannot borrow `*f` as mutable more than once at a time
+ --> $DIR/moves-based-on-type-no-recursive-stack-closure.rs:20:27
+ |
+LL | (f.c)(f, true);
+ | ----- ^ second mutable borrow occurs here
+ | |
+ | first mutable borrow occurs here
+ | first borrow later used by call
+
+error[E0382]: borrow of moved value: `f`
+ --> $DIR/moves-based-on-type-no-recursive-stack-closure.rs:32:5
+ |
+LL | fn conspirator<F>(mut f: F) where F: FnMut(&mut R, bool) {
+ | ----- move occurs because `f` has type `F`, which does not implement the `Copy` trait
+LL | let mut r = R {c: Box::new(f)};
+ | - value moved here
+LL | f(&mut r, false)
+ | ^ value borrowed here after move
+ |
+help: consider mutably borrowing `f`
+ |
+LL | let mut r = R {c: Box::new(&mut f)};
+ | ++++
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0382, E0499.
+For more information about an error, try `rustc --explain E0382`.
diff --git a/src/test/ui/moves/moves-based-on-type-tuple.rs b/src/test/ui/moves/moves-based-on-type-tuple.rs
new file mode 100644
index 000000000..2e67d8f8a
--- /dev/null
+++ b/src/test/ui/moves/moves-based-on-type-tuple.rs
@@ -0,0 +1,10 @@
+fn dup(x: Box<isize>) -> Box<(Box<isize>,Box<isize>)> {
+
+
+ Box::new((x, x))
+ //~^ use of moved value: `x` [E0382]
+}
+
+fn main() {
+ dup(Box::new(3));
+}
diff --git a/src/test/ui/moves/moves-based-on-type-tuple.stderr b/src/test/ui/moves/moves-based-on-type-tuple.stderr
new file mode 100644
index 000000000..eef8ce61f
--- /dev/null
+++ b/src/test/ui/moves/moves-based-on-type-tuple.stderr
@@ -0,0 +1,14 @@
+error[E0382]: use of moved value: `x`
+ --> $DIR/moves-based-on-type-tuple.rs:4:18
+ |
+LL | fn dup(x: Box<isize>) -> Box<(Box<isize>,Box<isize>)> {
+ | - move occurs because `x` has type `Box<isize>`, which does not implement the `Copy` trait
+...
+LL | Box::new((x, x))
+ | - ^ value used here after move
+ | |
+ | value moved here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/moves/moves-sru-moved-field.rs b/src/test/ui/moves/moves-sru-moved-field.rs
new file mode 100644
index 000000000..72957c49f
--- /dev/null
+++ b/src/test/ui/moves/moves-sru-moved-field.rs
@@ -0,0 +1,23 @@
+type Noncopyable = Box<isize>;
+
+
+
+struct Foo {
+ copied: isize,
+ moved: Box<isize>,
+ noncopyable: Noncopyable
+}
+
+fn test0(f: Foo, g: Noncopyable, h: Noncopyable) {
+ // just copy implicitly copyable fields from `f`, no moves:
+ let _b = Foo {moved: Box::new(1), noncopyable: g, ..f};
+ let _c = Foo {moved: Box::new(2), noncopyable: h, ..f};
+}
+
+fn test1(f: Foo, g: Noncopyable, h: Noncopyable) {
+ // copying move-by-default fields from `f`, so move:
+ let _b = Foo {noncopyable: g, ..f};
+ let _c = Foo {noncopyable: h, ..f}; //~ ERROR use of moved value: `f.moved`
+}
+
+fn main() {}
diff --git a/src/test/ui/moves/moves-sru-moved-field.stderr b/src/test/ui/moves/moves-sru-moved-field.stderr
new file mode 100644
index 000000000..cf7213637
--- /dev/null
+++ b/src/test/ui/moves/moves-sru-moved-field.stderr
@@ -0,0 +1,13 @@
+error[E0382]: use of moved value: `f.moved`
+ --> $DIR/moves-sru-moved-field.rs:20:14
+ |
+LL | let _b = Foo {noncopyable: g, ..f};
+ | ------------------------- value moved here
+LL | let _c = Foo {noncopyable: h, ..f};
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ value used here after move
+ |
+ = note: move occurs because `f.moved` has type `Box<isize>`, which does not implement the `Copy` trait
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/moves/use_of_moved_value_clone_suggestions.rs b/src/test/ui/moves/use_of_moved_value_clone_suggestions.rs
new file mode 100644
index 000000000..d5c8d4e6b
--- /dev/null
+++ b/src/test/ui/moves/use_of_moved_value_clone_suggestions.rs
@@ -0,0 +1,6 @@
+// `Rc` is not ever `Copy`, we should not suggest adding `T: Copy` constraint
+fn duplicate_rc<T>(t: std::rc::Rc<T>) -> (std::rc::Rc<T>, std::rc::Rc<T>) {
+ (t, t) //~ use of moved value: `t`
+}
+
+fn main() {}
diff --git a/src/test/ui/moves/use_of_moved_value_clone_suggestions.stderr b/src/test/ui/moves/use_of_moved_value_clone_suggestions.stderr
new file mode 100644
index 000000000..c25981e6f
--- /dev/null
+++ b/src/test/ui/moves/use_of_moved_value_clone_suggestions.stderr
@@ -0,0 +1,13 @@
+error[E0382]: use of moved value: `t`
+ --> $DIR/use_of_moved_value_clone_suggestions.rs:3:9
+ |
+LL | fn duplicate_rc<T>(t: std::rc::Rc<T>) -> (std::rc::Rc<T>, std::rc::Rc<T>) {
+ | - move occurs because `t` has type `Rc<T>`, which does not implement the `Copy` trait
+LL | (t, t)
+ | - ^ value used here after move
+ | |
+ | value moved here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed b/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed
new file mode 100644
index 000000000..45acf5beb
--- /dev/null
+++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed
@@ -0,0 +1,86 @@
+// run-rustfix
+#![allow(dead_code)]
+
+fn duplicate_t<T: Copy>(t: T) -> (T, T) {
+ //~^ HELP consider restricting type parameter `T`
+ (t, t) //~ use of moved value: `t`
+}
+
+fn duplicate_opt<T: Copy>(t: Option<T>) -> (Option<T>, Option<T>) {
+ //~^ HELP consider restricting type parameter `T`
+ (t, t) //~ use of moved value: `t`
+}
+
+fn duplicate_tup1<T: Copy>(t: (T,)) -> ((T,), (T,)) {
+ //~^ HELP consider restricting type parameter `T`
+ (t, t) //~ use of moved value: `t`
+}
+
+fn duplicate_tup2<A: Copy, B: Copy>(t: (A, B)) -> ((A, B), (A, B)) {
+ //~^ HELP consider restricting type parameters
+ (t, t) //~ use of moved value: `t`
+}
+
+fn duplicate_custom<T: Copy + Trait>(t: S<T>) -> (S<T>, S<T>) {
+ //~^ HELP consider restricting type parameter `T`
+ (t, t) //~ use of moved value: `t`
+}
+
+struct S<T>(T);
+trait Trait {}
+impl<T: Trait + Clone> Clone for S<T> {
+ fn clone(&self) -> Self {
+ Self(self.0.clone())
+ }
+}
+impl<T: Trait + Copy> Copy for S<T> {}
+
+trait A {}
+trait B {}
+
+// Test where bounds are added with different bound placements
+fn duplicate_custom_1<T: Copy + Trait>(t: S<T>) -> (S<T>, S<T>) where {
+ //~^ HELP consider restricting type parameter `T`
+ (t, t) //~ use of moved value: `t`
+}
+
+fn duplicate_custom_2<T>(t: S<T>) -> (S<T>, S<T>)
+where
+ T: A + Copy + Trait,
+ //~^ HELP consider further restricting this bound
+{
+ (t, t) //~ use of moved value: `t`
+}
+
+fn duplicate_custom_3<T>(t: S<T>) -> (S<T>, S<T>)
+where
+ T: A + Copy + Trait,
+ //~^ HELP consider further restricting this bound
+ T: B,
+{
+ (t, t) //~ use of moved value: `t`
+}
+
+fn duplicate_custom_4<T: A + Copy + Trait>(t: S<T>) -> (S<T>, S<T>)
+//~^ HELP consider further restricting this bound
+where
+ T: B,
+{
+ (t, t) //~ use of moved value: `t`
+}
+
+#[rustfmt::skip]
+fn existing_colon<T: Copy>(t: T) {
+ //~^ HELP consider restricting type parameter `T`
+ [t, t]; //~ use of moved value: `t`
+}
+
+fn existing_colon_in_where<T>(t: T)
+where
+ T:, T: Copy
+ //~^ HELP consider further restricting type parameter `T`
+{
+ [t, t]; //~ use of moved value: `t`
+}
+
+fn main() {}
diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs b/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs
new file mode 100644
index 000000000..0a43dd1a9
--- /dev/null
+++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs
@@ -0,0 +1,86 @@
+// run-rustfix
+#![allow(dead_code)]
+
+fn duplicate_t<T>(t: T) -> (T, T) {
+ //~^ HELP consider restricting type parameter `T`
+ (t, t) //~ use of moved value: `t`
+}
+
+fn duplicate_opt<T>(t: Option<T>) -> (Option<T>, Option<T>) {
+ //~^ HELP consider restricting type parameter `T`
+ (t, t) //~ use of moved value: `t`
+}
+
+fn duplicate_tup1<T>(t: (T,)) -> ((T,), (T,)) {
+ //~^ HELP consider restricting type parameter `T`
+ (t, t) //~ use of moved value: `t`
+}
+
+fn duplicate_tup2<A, B>(t: (A, B)) -> ((A, B), (A, B)) {
+ //~^ HELP consider restricting type parameters
+ (t, t) //~ use of moved value: `t`
+}
+
+fn duplicate_custom<T>(t: S<T>) -> (S<T>, S<T>) {
+ //~^ HELP consider restricting type parameter `T`
+ (t, t) //~ use of moved value: `t`
+}
+
+struct S<T>(T);
+trait Trait {}
+impl<T: Trait + Clone> Clone for S<T> {
+ fn clone(&self) -> Self {
+ Self(self.0.clone())
+ }
+}
+impl<T: Trait + Copy> Copy for S<T> {}
+
+trait A {}
+trait B {}
+
+// Test where bounds are added with different bound placements
+fn duplicate_custom_1<T>(t: S<T>) -> (S<T>, S<T>) where {
+ //~^ HELP consider restricting type parameter `T`
+ (t, t) //~ use of moved value: `t`
+}
+
+fn duplicate_custom_2<T>(t: S<T>) -> (S<T>, S<T>)
+where
+ T: A,
+ //~^ HELP consider further restricting this bound
+{
+ (t, t) //~ use of moved value: `t`
+}
+
+fn duplicate_custom_3<T>(t: S<T>) -> (S<T>, S<T>)
+where
+ T: A,
+ //~^ HELP consider further restricting this bound
+ T: B,
+{
+ (t, t) //~ use of moved value: `t`
+}
+
+fn duplicate_custom_4<T: A>(t: S<T>) -> (S<T>, S<T>)
+//~^ HELP consider further restricting this bound
+where
+ T: B,
+{
+ (t, t) //~ use of moved value: `t`
+}
+
+#[rustfmt::skip]
+fn existing_colon<T:>(t: T) {
+ //~^ HELP consider restricting type parameter `T`
+ [t, t]; //~ use of moved value: `t`
+}
+
+fn existing_colon_in_where<T>(t: T)
+where
+ T:,
+ //~^ HELP consider further restricting type parameter `T`
+{
+ [t, t]; //~ use of moved value: `t`
+}
+
+fn main() {}
diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr b/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr
new file mode 100644
index 000000000..3e37fcb21
--- /dev/null
+++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr
@@ -0,0 +1,179 @@
+error[E0382]: use of moved value: `t`
+ --> $DIR/use_of_moved_value_copy_suggestions.rs:6:9
+ |
+LL | fn duplicate_t<T>(t: T) -> (T, T) {
+ | - move occurs because `t` has type `T`, which does not implement the `Copy` trait
+LL |
+LL | (t, t)
+ | - ^ value used here after move
+ | |
+ | value moved here
+ |
+help: consider restricting type parameter `T`
+ |
+LL | fn duplicate_t<T: Copy>(t: T) -> (T, T) {
+ | ++++++
+
+error[E0382]: use of moved value: `t`
+ --> $DIR/use_of_moved_value_copy_suggestions.rs:11:9
+ |
+LL | fn duplicate_opt<T>(t: Option<T>) -> (Option<T>, Option<T>) {
+ | - move occurs because `t` has type `Option<T>`, which does not implement the `Copy` trait
+LL |
+LL | (t, t)
+ | - ^ value used here after move
+ | |
+ | value moved here
+ |
+help: consider restricting type parameter `T`
+ |
+LL | fn duplicate_opt<T: Copy>(t: Option<T>) -> (Option<T>, Option<T>) {
+ | ++++++
+
+error[E0382]: use of moved value: `t`
+ --> $DIR/use_of_moved_value_copy_suggestions.rs:16:9
+ |
+LL | fn duplicate_tup1<T>(t: (T,)) -> ((T,), (T,)) {
+ | - move occurs because `t` has type `(T,)`, which does not implement the `Copy` trait
+LL |
+LL | (t, t)
+ | - ^ value used here after move
+ | |
+ | value moved here
+ |
+help: consider restricting type parameter `T`
+ |
+LL | fn duplicate_tup1<T: Copy>(t: (T,)) -> ((T,), (T,)) {
+ | ++++++
+
+error[E0382]: use of moved value: `t`
+ --> $DIR/use_of_moved_value_copy_suggestions.rs:21:9
+ |
+LL | fn duplicate_tup2<A, B>(t: (A, B)) -> ((A, B), (A, B)) {
+ | - move occurs because `t` has type `(A, B)`, which does not implement the `Copy` trait
+LL |
+LL | (t, t)
+ | - ^ value used here after move
+ | |
+ | value moved here
+ |
+help: consider restricting type parameters
+ |
+LL | fn duplicate_tup2<A: Copy, B: Copy>(t: (A, B)) -> ((A, B), (A, B)) {
+ | ++++++ ++++++
+
+error[E0382]: use of moved value: `t`
+ --> $DIR/use_of_moved_value_copy_suggestions.rs:26:9
+ |
+LL | fn duplicate_custom<T>(t: S<T>) -> (S<T>, S<T>) {
+ | - move occurs because `t` has type `S<T>`, which does not implement the `Copy` trait
+LL |
+LL | (t, t)
+ | - ^ value used here after move
+ | |
+ | value moved here
+ |
+help: consider restricting type parameter `T`
+ |
+LL | fn duplicate_custom<T: Copy + Trait>(t: S<T>) -> (S<T>, S<T>) {
+ | ++++++++++++++
+
+error[E0382]: use of moved value: `t`
+ --> $DIR/use_of_moved_value_copy_suggestions.rs:44:9
+ |
+LL | fn duplicate_custom_1<T>(t: S<T>) -> (S<T>, S<T>) where {
+ | - move occurs because `t` has type `S<T>`, which does not implement the `Copy` trait
+LL |
+LL | (t, t)
+ | - ^ value used here after move
+ | |
+ | value moved here
+ |
+help: consider restricting type parameter `T`
+ |
+LL | fn duplicate_custom_1<T: Copy + Trait>(t: S<T>) -> (S<T>, S<T>) where {
+ | ++++++++++++++
+
+error[E0382]: use of moved value: `t`
+ --> $DIR/use_of_moved_value_copy_suggestions.rs:52:9
+ |
+LL | fn duplicate_custom_2<T>(t: S<T>) -> (S<T>, S<T>)
+ | - move occurs because `t` has type `S<T>`, which does not implement the `Copy` trait
+...
+LL | (t, t)
+ | - ^ value used here after move
+ | |
+ | value moved here
+ |
+help: consider further restricting this bound
+ |
+LL | T: A + Copy + Trait,
+ | ++++++++++++++
+
+error[E0382]: use of moved value: `t`
+ --> $DIR/use_of_moved_value_copy_suggestions.rs:61:9
+ |
+LL | fn duplicate_custom_3<T>(t: S<T>) -> (S<T>, S<T>)
+ | - move occurs because `t` has type `S<T>`, which does not implement the `Copy` trait
+...
+LL | (t, t)
+ | - ^ value used here after move
+ | |
+ | value moved here
+ |
+help: consider further restricting this bound
+ |
+LL | T: A + Copy + Trait,
+ | ++++++++++++++
+
+error[E0382]: use of moved value: `t`
+ --> $DIR/use_of_moved_value_copy_suggestions.rs:69:9
+ |
+LL | fn duplicate_custom_4<T: A>(t: S<T>) -> (S<T>, S<T>)
+ | - move occurs because `t` has type `S<T>`, which does not implement the `Copy` trait
+...
+LL | (t, t)
+ | - ^ value used here after move
+ | |
+ | value moved here
+ |
+help: consider further restricting this bound
+ |
+LL | fn duplicate_custom_4<T: A + Copy + Trait>(t: S<T>) -> (S<T>, S<T>)
+ | ++++++++++++++
+
+error[E0382]: use of moved value: `t`
+ --> $DIR/use_of_moved_value_copy_suggestions.rs:75:9
+ |
+LL | fn existing_colon<T:>(t: T) {
+ | - move occurs because `t` has type `T`, which does not implement the `Copy` trait
+LL |
+LL | [t, t];
+ | - ^ value used here after move
+ | |
+ | value moved here
+ |
+help: consider restricting type parameter `T`
+ |
+LL | fn existing_colon<T: Copy>(t: T) {
+ | ++++
+
+error[E0382]: use of moved value: `t`
+ --> $DIR/use_of_moved_value_copy_suggestions.rs:83:9
+ |
+LL | fn existing_colon_in_where<T>(t: T)
+ | - move occurs because `t` has type `T`, which does not implement the `Copy` trait
+...
+LL | [t, t];
+ | - ^ value used here after move
+ | |
+ | value moved here
+ |
+help: consider further restricting type parameter `T`
+ |
+LL | T:, T: Copy
+ | ~~~~~~~~~
+
+error: aborting due to 11 previous errors
+
+For more information about this error, try `rustc --explain E0382`.