about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/test/ui/nll/issue-21114-ebfull.rs20
-rw-r--r--src/test/ui/nll/issue-21114-kixunil.rs19
-rw-r--r--src/test/ui/nll/issue-54556-niconii.nll.stderr20
-rw-r--r--src/test/ui/nll/issue-54556-niconii.rs31
-rw-r--r--src/test/ui/nll/issue-54556-niconii.stderr14
-rw-r--r--src/test/ui/nll/issue-54556-stephaneyfx.nll.stderr19
-rw-r--r--src/test/ui/nll/issue-54556-stephaneyfx.rs35
-rw-r--r--src/test/ui/nll/issue-54556-stephaneyfx.stderr14
-rw-r--r--src/test/ui/nll/issue-54556-temps-in-tail-diagnostic.nll.stderr19
-rw-r--r--src/test/ui/nll/issue-54556-temps-in-tail-diagnostic.rs23
-rw-r--r--src/test/ui/nll/issue-54556-temps-in-tail-diagnostic.stderr14
-rw-r--r--src/test/ui/nll/issue-54556-used-vs-unused-tails.nll.stderr113
-rw-r--r--src/test/ui/nll/issue-54556-used-vs-unused-tails.rs48
-rw-r--r--src/test/ui/nll/issue-54556-used-vs-unused-tails.stderr86
-rw-r--r--src/test/ui/nll/issue-54556-wrap-it-up.nll.stderr14
-rw-r--r--src/test/ui/nll/issue-54556-wrap-it-up.rs28
-rw-r--r--src/test/ui/nll/issue-54556-wrap-it-up.stderr12
17 files changed, 529 insertions, 0 deletions
diff --git a/src/test/ui/nll/issue-21114-ebfull.rs b/src/test/ui/nll/issue-21114-ebfull.rs
new file mode 100644
index 00000000000..f5738968746
--- /dev/null
+++ b/src/test/ui/nll/issue-21114-ebfull.rs
@@ -0,0 +1,20 @@
+// (this works, but only in NLL)
+// compile-pass
+#![feature(nll)]
+
+use std::collections::HashMap;
+use std::sync::Mutex;
+
+fn i_used_to_be_able_to(foo: &Mutex<HashMap<usize, usize>>) -> Vec<(usize, usize)> {
+    let mut foo = foo.lock().unwrap();
+
+    foo.drain().collect()
+}
+
+fn but_after_nightly_update_now_i_gotta(foo: &Mutex<HashMap<usize, usize>>) -> Vec<(usize, usize)> {
+    let mut foo = foo.lock().unwrap();
+
+    return foo.drain().collect();
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/issue-21114-kixunil.rs b/src/test/ui/nll/issue-21114-kixunil.rs
new file mode 100644
index 00000000000..2add951b70b
--- /dev/null
+++ b/src/test/ui/nll/issue-21114-kixunil.rs
@@ -0,0 +1,19 @@
+// (this works, but only in NLL)
+// compile-pass
+#![feature(nll)]
+
+fn from_stdin(min: u64) -> Vec<u64> {
+    use std::io::BufRead;
+
+    let stdin = std::io::stdin();
+    let stdin = stdin.lock();
+
+    stdin.lines()
+        .map(Result::unwrap)
+        .map(|val| val.parse())
+        .map(Result::unwrap)
+        .filter(|val| *val >= min)
+        .collect()
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/issue-54556-niconii.nll.stderr b/src/test/ui/nll/issue-54556-niconii.nll.stderr
new file mode 100644
index 00000000000..40cd04de5ec
--- /dev/null
+++ b/src/test/ui/nll/issue-54556-niconii.nll.stderr
@@ -0,0 +1,20 @@
+error[E0597]: `counter` does not live long enough
+  --> $DIR/issue-54556-niconii.rs:22:20
+   |
+LL |     if let Ok(_) = counter.lock() { }
+   |                    ^^^^^^^-------
+   |                    |
+   |                    borrowed value does not live long enough
+   |                    a temporary with access to the borrow is created here ...
+...
+LL | }
+   | -
+   | |
+   | `counter` dropped here while still borrowed
+   | ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `std::result::Result<MutexGuard<'_>, ()>`
+   |
+   = note: The temporary is part of an expression at the end of a block. Consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped.
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/src/test/ui/nll/issue-54556-niconii.rs b/src/test/ui/nll/issue-54556-niconii.rs
new file mode 100644
index 00000000000..49b063f44f4
--- /dev/null
+++ b/src/test/ui/nll/issue-54556-niconii.rs
@@ -0,0 +1,31 @@
+// This is a reduction of a concrete test illustrating a case that was
+// annoying to Rust developer niconii (see comment thread on #21114).
+//
+// With resolving issue #54556, pnkfelix hopes that the new diagnostic
+// output produced by NLL helps to *explain* the semantic significance
+// of temp drop order, and thus why inserting a semi-colon after the
+// `if let` expression in `main` works.
+
+struct Mutex;
+struct MutexGuard<'a>(&'a Mutex);
+
+impl Drop for Mutex { fn drop(&mut self) { println!("Mutex::drop"); } }
+impl<'a> Drop for MutexGuard<'a> { fn drop(&mut self) { println!("MutexGuard::drop");  } }
+
+impl Mutex {
+    fn lock(&self) -> Result<MutexGuard, ()> { Ok(MutexGuard(self)) }
+}
+
+fn main() {
+    let counter = Mutex;
+
+    if let Ok(_) = counter.lock() { }
+
+    // With this code as written, the dynamic semantics here implies
+    // that `Mutex::drop` for `counter` runs *before*
+    // `MutexGuard::drop`, which would be unsound since `MutexGuard`
+    // still has a reference to `counter`.
+    //
+    // The goal of #54556 is to explain that within a compiler
+    // diagnostic.
+}
diff --git a/src/test/ui/nll/issue-54556-niconii.stderr b/src/test/ui/nll/issue-54556-niconii.stderr
new file mode 100644
index 00000000000..2d0de26ab30
--- /dev/null
+++ b/src/test/ui/nll/issue-54556-niconii.stderr
@@ -0,0 +1,14 @@
+error[E0597]: `counter` does not live long enough
+  --> $DIR/issue-54556-niconii.rs:22:20
+   |
+LL |     if let Ok(_) = counter.lock() { }
+   |                    ^^^^^^^ borrowed value does not live long enough
+...
+LL | }
+   | - `counter` dropped here while still borrowed
+   |
+   = note: values in a scope are dropped in the opposite order they are created
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/src/test/ui/nll/issue-54556-stephaneyfx.nll.stderr b/src/test/ui/nll/issue-54556-stephaneyfx.nll.stderr
new file mode 100644
index 00000000000..0bf76485eef
--- /dev/null
+++ b/src/test/ui/nll/issue-54556-stephaneyfx.nll.stderr
@@ -0,0 +1,19 @@
+error[E0597]: `stmt` does not live long enough
+  --> $DIR/issue-54556-stephaneyfx.rs:27:21
+   |
+LL |     let rows = Rows(&stmt);
+   |                     ^^^^^ borrowed value does not live long enough
+LL |     rows.map(|row| row).next()
+   |     ------------------- a temporary with access to the borrow is created here ...
+...
+LL | }
+   | -
+   | |
+   | `stmt` dropped here while still borrowed
+   | ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `std::iter::Map<Rows<'_>, [closure@$DIR/issue-54556-stephaneyfx.rs:28:14: 28:23]>`
+   |
+   = note: The temporary is part of an expression at the end of a block. Consider forcing this temporary to be dropped sooner, before the block's local variables are dropped. For example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block.
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/src/test/ui/nll/issue-54556-stephaneyfx.rs b/src/test/ui/nll/issue-54556-stephaneyfx.rs
new file mode 100644
index 00000000000..10a4e21497c
--- /dev/null
+++ b/src/test/ui/nll/issue-54556-stephaneyfx.rs
@@ -0,0 +1,35 @@
+// This is a reduction of a concrete test illustrating a case that was
+// annoying to Rust developer stephaneyfx (see issue #46413).
+//
+// With resolving issue #54556, pnkfelix hopes that the new diagnostic
+// output produced by NLL helps to *explain* the semantic significance
+// of temp drop order, and thus why storing the result in `x` and then
+// returning `x` works.
+
+pub struct Statement;
+
+pub struct Rows<'stmt>(&'stmt Statement);
+
+impl<'stmt> Drop for Rows<'stmt> {
+    fn drop(&mut self) {}
+}
+
+impl<'stmt> Iterator for Rows<'stmt> {
+    type Item = String;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        None
+    }
+}
+
+fn get_names() -> Option<String> {
+    let stmt = Statement;
+    let rows = Rows(&stmt);
+    rows.map(|row| row).next()
+    // let x = rows.map(|row| row).next();
+    // x
+    //
+    // Removing the map works too as does removing the Drop impl.
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/issue-54556-stephaneyfx.stderr b/src/test/ui/nll/issue-54556-stephaneyfx.stderr
new file mode 100644
index 00000000000..4e581a516b2
--- /dev/null
+++ b/src/test/ui/nll/issue-54556-stephaneyfx.stderr
@@ -0,0 +1,14 @@
+error[E0597]: `stmt` does not live long enough
+  --> $DIR/issue-54556-stephaneyfx.rs:27:22
+   |
+LL |     let rows = Rows(&stmt);
+   |                      ^^^^ borrowed value does not live long enough
+...
+LL | }
+   | - `stmt` dropped here while still borrowed
+   |
+   = note: values in a scope are dropped in the opposite order they are created
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/src/test/ui/nll/issue-54556-temps-in-tail-diagnostic.nll.stderr b/src/test/ui/nll/issue-54556-temps-in-tail-diagnostic.nll.stderr
new file mode 100644
index 00000000000..513dca7950a
--- /dev/null
+++ b/src/test/ui/nll/issue-54556-temps-in-tail-diagnostic.nll.stderr
@@ -0,0 +1,19 @@
+error[E0597]: `_thing1` does not live long enough
+  --> $DIR/issue-54556-temps-in-tail-diagnostic.rs:5:11
+   |
+LL |         D(&_thing1).end()
+   |         --^^^^^^^^-
+   |         | |
+   |         | borrowed value does not live long enough
+   |         a temporary with access to the borrow is created here ...
+LL |     }
+   |     - `_thing1` dropped here while still borrowed
+LL | 
+LL |     ;
+   |     - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
+   |
+   = note: The temporary is part of an expression at the end of a block. Consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped.
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/src/test/ui/nll/issue-54556-temps-in-tail-diagnostic.rs b/src/test/ui/nll/issue-54556-temps-in-tail-diagnostic.rs
new file mode 100644
index 00000000000..63b04333de4
--- /dev/null
+++ b/src/test/ui/nll/issue-54556-temps-in-tail-diagnostic.rs
@@ -0,0 +1,23 @@
+fn main() {
+    {
+        let mut _thing1 = D(Box::new("thing1"));
+        // D("other").next(&_thing1).end()
+        D(&_thing1).end()
+    }
+
+    ;
+}
+
+#[derive(Debug)]
+struct D<T: std::fmt::Debug>(T);
+
+impl<T: std::fmt::Debug>  Drop for D<T> {
+    fn drop(&mut self) {
+        println!("dropping {:?})", self);
+    }
+}
+
+impl<T: std::fmt::Debug> D<T> {
+    fn next<U: std::fmt::Debug>(&self, _other: U) -> D<U> { D(_other) }
+    fn end(&self) { }
+}
diff --git a/src/test/ui/nll/issue-54556-temps-in-tail-diagnostic.stderr b/src/test/ui/nll/issue-54556-temps-in-tail-diagnostic.stderr
new file mode 100644
index 00000000000..a74970f7118
--- /dev/null
+++ b/src/test/ui/nll/issue-54556-temps-in-tail-diagnostic.stderr
@@ -0,0 +1,14 @@
+error[E0597]: `_thing1` does not live long enough
+  --> $DIR/issue-54556-temps-in-tail-diagnostic.rs:5:12
+   |
+LL |         D(&_thing1).end()
+   |            ^^^^^^^ borrowed value does not live long enough
+LL |     }
+   |     - `_thing1` dropped here while still borrowed
+LL | 
+LL |     ;
+   |     - borrowed value needs to live until here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/src/test/ui/nll/issue-54556-used-vs-unused-tails.nll.stderr b/src/test/ui/nll/issue-54556-used-vs-unused-tails.nll.stderr
new file mode 100644
index 00000000000..9911fc97291
--- /dev/null
+++ b/src/test/ui/nll/issue-54556-used-vs-unused-tails.nll.stderr
@@ -0,0 +1,113 @@
+error[E0597]: `_t1` does not live long enough
+  --> $DIR/issue-54556-used-vs-unused-tails.rs:10:55
+   |
+LL |     {              let mut _t1 = D(Box::new("t1")); D(&_t1).end()    } ; // suggest `;`
+   |                                                     --^^^^-          - - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
+   |                                                     | |              |
+   |                                                     | |              `_t1` dropped here while still borrowed
+   |                                                     | borrowed value does not live long enough
+   |                                                     a temporary with access to the borrow is created here ...
+   |
+   = note: The temporary is part of an expression at the end of a block. Consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped.
+
+error[E0597]: `_t1` does not live long enough
+  --> $DIR/issue-54556-used-vs-unused-tails.rs:12:55
+   |
+LL |     {            { let mut _t1 = D(Box::new("t1")); D(&_t1).end() }  } ; // suggest `;`
+   |                                                     --^^^^-       -    - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
+   |                                                     | |           |
+   |                                                     | |           `_t1` dropped here while still borrowed
+   |                                                     | borrowed value does not live long enough
+   |                                                     a temporary with access to the borrow is created here ...
+   |
+   = note: The temporary is part of an expression at the end of a block. Consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped.
+
+error[E0597]: `_t1` does not live long enough
+  --> $DIR/issue-54556-used-vs-unused-tails.rs:14:55
+   |
+LL |     {            { let mut _t1 = D(Box::new("t1")); D(&_t1).end() }; }   // suggest `;`
+   |                                                     --^^^^-       -- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
+   |                                                     | |           |
+   |                                                     | |           `_t1` dropped here while still borrowed
+   |                                                     | borrowed value does not live long enough
+   |                                                     a temporary with access to the borrow is created here ...
+   |
+   = note: The temporary is part of an expression at the end of a block. Consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped.
+
+error[E0597]: `_t1` does not live long enough
+  --> $DIR/issue-54556-used-vs-unused-tails.rs:16:55
+   |
+LL |     let _ =      { let mut _t1 = D(Box::new("t1")); D(&_t1).end()    } ; // suggest `;`
+   |                                                     --^^^^-          - - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
+   |                                                     | |              |
+   |                                                     | |              `_t1` dropped here while still borrowed
+   |                                                     | borrowed value does not live long enough
+   |                                                     a temporary with access to the borrow is created here ...
+   |
+   = note: The temporary is part of an expression at the end of a block. Consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped.
+
+error[E0597]: `_t1` does not live long enough
+  --> $DIR/issue-54556-used-vs-unused-tails.rs:18:55
+   |
+LL |     let _u =     { let mut _t1 = D(Box::new("t1")); D(&_t1).unit()   } ; // suggest `;`
+   |                                                     --^^^^-          - - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
+   |                                                     | |              |
+   |                                                     | |              `_t1` dropped here while still borrowed
+   |                                                     | borrowed value does not live long enough
+   |                                                     a temporary with access to the borrow is created here ...
+   |
+   = note: The temporary is part of an expression at the end of a block. Consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped.
+
+error[E0597]: `_t1` does not live long enough
+  --> $DIR/issue-54556-used-vs-unused-tails.rs:20:55
+   |
+LL |     let _x =     { let mut _t1 = D(Box::new("t1")); D(&_t1).end()    } ; // `let x = ...; x`
+   |                                                     --^^^^-          - - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
+   |                                                     | |              |
+   |                                                     | |              `_t1` dropped here while still borrowed
+   |                                                     | borrowed value does not live long enough
+   |                                                     a temporary with access to the borrow is created here ...
+   |
+   = note: The temporary is part of an expression at the end of a block. Consider forcing this temporary to be dropped sooner, before the block's local variables are dropped. For example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block.
+
+error[E0597]: `_t1` does not live long enough
+  --> $DIR/issue-54556-used-vs-unused-tails.rs:24:55
+   |
+LL |     _y =         { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } ; // `let x = ...; x`
+   |                                                     --^^^^-       - - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
+   |                                                     | |           |
+   |                                                     | |           `_t1` dropped here while still borrowed
+   |                                                     | borrowed value does not live long enough
+   |                                                     a temporary with access to the borrow is created here ...
+   |
+   = note: The temporary is part of an expression at the end of a block. Consider forcing this temporary to be dropped sooner, before the block's local variables are dropped. For example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block.
+
+error[E0597]: `_t1` does not live long enough
+  --> $DIR/issue-54556-used-vs-unused-tails.rs:30:55
+   |
+LL | fn f_local_ref() { let mut _t1 = D(Box::new("t1")); D(&_t1).unit()   }  // suggest `;`
+   |                                                     --^^^^-          -
+   |                                                     | |              |
+   |                                                     | |              `_t1` dropped here while still borrowed
+   |                                                     | |              ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
+   |                                                     | borrowed value does not live long enough
+   |                                                     a temporary with access to the borrow is created here ...
+   |
+   = note: The temporary is part of an expression at the end of a block. Consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped.
+
+error[E0597]: `_t1` does not live long enough
+  --> $DIR/issue-54556-used-vs-unused-tails.rs:32:55
+   |
+LL | fn f() -> String { let mut _t1 = D(Box::new("t1")); D(&_t1).end()   }   // `let x = ...; x`
+   |                                                     --^^^^-         -
+   |                                                     | |             |
+   |                                                     | |             `_t1` dropped here while still borrowed
+   |                                                     | |             ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
+   |                                                     | borrowed value does not live long enough
+   |                                                     a temporary with access to the borrow is created here ...
+   |
+   = note: The temporary is part of an expression at the end of a block. Consider forcing this temporary to be dropped sooner, before the block's local variables are dropped. For example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block.
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/src/test/ui/nll/issue-54556-used-vs-unused-tails.rs b/src/test/ui/nll/issue-54556-used-vs-unused-tails.rs
new file mode 100644
index 00000000000..64e4f75724a
--- /dev/null
+++ b/src/test/ui/nll/issue-54556-used-vs-unused-tails.rs
@@ -0,0 +1,48 @@
+// Ths test case is exploring the space of how blocs with tail
+// expressions and statements can be composed, trying to keep each
+// case on one line so that we can compare them via a vertical scan
+// with the human eye.
+
+// Each comment on the right side of the line is summarizing the
+// expected suggestion from the diagnostic for issue #54556.
+
+fn main() {
+    {              let mut _t1 = D(Box::new("t1")); D(&_t1).end()    } ; // suggest `;`
+
+    {            { let mut _t1 = D(Box::new("t1")); D(&_t1).end() }  } ; // suggest `;`
+
+    {            { let mut _t1 = D(Box::new("t1")); D(&_t1).end() }; }   // suggest `;`
+
+    let _ =      { let mut _t1 = D(Box::new("t1")); D(&_t1).end()    } ; // suggest `;`
+
+    let _u =     { let mut _t1 = D(Box::new("t1")); D(&_t1).unit()   } ; // suggest `;`
+
+    let _x =     { let mut _t1 = D(Box::new("t1")); D(&_t1).end()    } ; // `let x = ...; x`
+    let _x =     { let mut _t1 = D(Box::new("t1")); let x = D(&_t1).end(); x } ; // no error
+
+    let mut _y;
+    _y =         { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } ; // `let x = ...; x`
+    _y =         { let mut _t1 = D(Box::new("t1")); let x = D(&_t1).end(); x } ; // no error
+}
+
+fn f_param_ref(_t1: D<Box<&'static str>>) {         D(&_t1).unit()   }  // no error
+
+fn f_local_ref() { let mut _t1 = D(Box::new("t1")); D(&_t1).unit()   }  // suggest `;`
+
+fn f() -> String { let mut _t1 = D(Box::new("t1")); D(&_t1).end()   }   // `let x = ...; x`
+
+
+#[derive(Debug)]
+struct D<T: std::fmt::Debug>(T);
+
+impl<T: std::fmt::Debug>  Drop for D<T> {
+    fn drop(&mut self) {
+        println!("dropping {:?})", self);
+    }
+}
+
+impl<T: std::fmt::Debug> D<T> {
+    fn next<U: std::fmt::Debug>(&self, _other: U) -> D<U> { D(_other) }
+    fn end(&self) -> String { format!("End({:?})", self.0) }
+    fn unit(&self) { }
+}
diff --git a/src/test/ui/nll/issue-54556-used-vs-unused-tails.stderr b/src/test/ui/nll/issue-54556-used-vs-unused-tails.stderr
new file mode 100644
index 00000000000..c75707b2aee
--- /dev/null
+++ b/src/test/ui/nll/issue-54556-used-vs-unused-tails.stderr
@@ -0,0 +1,86 @@
+error[E0597]: `_t1` does not live long enough
+  --> $DIR/issue-54556-used-vs-unused-tails.rs:10:56
+   |
+LL |     {              let mut _t1 = D(Box::new("t1")); D(&_t1).end()    } ; // suggest `;`
+   |                                                        ^^^           - - borrowed value needs to live until here
+   |                                                        |             |
+   |                                                        |             `_t1` dropped here while still borrowed
+   |                                                        borrowed value does not live long enough
+
+error[E0597]: `_t1` does not live long enough
+  --> $DIR/issue-54556-used-vs-unused-tails.rs:12:56
+   |
+LL |     {            { let mut _t1 = D(Box::new("t1")); D(&_t1).end() }  } ; // suggest `;`
+   |                                                        ^^^        -    - borrowed value needs to live until here
+   |                                                        |          |
+   |                                                        |          `_t1` dropped here while still borrowed
+   |                                                        borrowed value does not live long enough
+
+error[E0597]: `_t1` does not live long enough
+  --> $DIR/issue-54556-used-vs-unused-tails.rs:14:56
+   |
+LL |     {            { let mut _t1 = D(Box::new("t1")); D(&_t1).end() }; }   // suggest `;`
+   |                                                        ^^^        -- borrowed value needs to live until here
+   |                                                        |          |
+   |                                                        |          `_t1` dropped here while still borrowed
+   |                                                        borrowed value does not live long enough
+
+error[E0597]: `_t1` does not live long enough
+  --> $DIR/issue-54556-used-vs-unused-tails.rs:16:56
+   |
+LL |     let _ =      { let mut _t1 = D(Box::new("t1")); D(&_t1).end()    } ; // suggest `;`
+   |                                                        ^^^           - - borrowed value needs to live until here
+   |                                                        |             |
+   |                                                        |             `_t1` dropped here while still borrowed
+   |                                                        borrowed value does not live long enough
+
+error[E0597]: `_t1` does not live long enough
+  --> $DIR/issue-54556-used-vs-unused-tails.rs:18:56
+   |
+LL |     let _u =     { let mut _t1 = D(Box::new("t1")); D(&_t1).unit()   } ; // suggest `;`
+   |                                                        ^^^           - - borrowed value needs to live until here
+   |                                                        |             |
+   |                                                        |             `_t1` dropped here while still borrowed
+   |                                                        borrowed value does not live long enough
+
+error[E0597]: `_t1` does not live long enough
+  --> $DIR/issue-54556-used-vs-unused-tails.rs:20:56
+   |
+LL |     let _x =     { let mut _t1 = D(Box::new("t1")); D(&_t1).end()    } ; // `let x = ...; x`
+   |                                                        ^^^           - - borrowed value needs to live until here
+   |                                                        |             |
+   |                                                        |             `_t1` dropped here while still borrowed
+   |                                                        borrowed value does not live long enough
+
+error[E0597]: `_t1` does not live long enough
+  --> $DIR/issue-54556-used-vs-unused-tails.rs:24:56
+   |
+LL |     _y =         { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } ; // `let x = ...; x`
+   |                                                        ^^^        - - borrowed value needs to live until here
+   |                                                        |          |
+   |                                                        |          `_t1` dropped here while still borrowed
+   |                                                        borrowed value does not live long enough
+
+error[E0597]: `_t1` does not live long enough
+  --> $DIR/issue-54556-used-vs-unused-tails.rs:30:56
+   |
+LL | fn f_local_ref() { let mut _t1 = D(Box::new("t1")); D(&_t1).unit()   }  // suggest `;`
+   |                                                        ^^^           - `_t1` dropped here while still borrowed
+   |                                                        |
+   |                                                        borrowed value does not live long enough
+   |
+   = note: values in a scope are dropped in the opposite order they are created
+
+error[E0597]: `_t1` does not live long enough
+  --> $DIR/issue-54556-used-vs-unused-tails.rs:32:56
+   |
+LL | fn f() -> String { let mut _t1 = D(Box::new("t1")); D(&_t1).end()   }   // `let x = ...; x`
+   |                                                        ^^^          - `_t1` dropped here while still borrowed
+   |                                                        |
+   |                                                        borrowed value does not live long enough
+   |
+   = note: values in a scope are dropped in the opposite order they are created
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/src/test/ui/nll/issue-54556-wrap-it-up.nll.stderr b/src/test/ui/nll/issue-54556-wrap-it-up.nll.stderr
new file mode 100644
index 00000000000..a13e59fa48b
--- /dev/null
+++ b/src/test/ui/nll/issue-54556-wrap-it-up.nll.stderr
@@ -0,0 +1,14 @@
+error[E0506]: cannot assign to `x` because it is borrowed
+  --> $DIR/issue-54556-wrap-it-up.rs:27:5
+   |
+LL |     let wrap = Wrap { p: &mut x };
+   |                          ------ borrow of `x` occurs here
+...
+LL |     x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
+   |     ^^^^^ assignment to borrowed `x` occurs here
+LL | }
+   | - borrow might be used here, when `foo` is dropped and runs the destructor for type `Foo<'_>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0506`.
diff --git a/src/test/ui/nll/issue-54556-wrap-it-up.rs b/src/test/ui/nll/issue-54556-wrap-it-up.rs
new file mode 100644
index 00000000000..11dbef0d864
--- /dev/null
+++ b/src/test/ui/nll/issue-54556-wrap-it-up.rs
@@ -0,0 +1,28 @@
+// This is testing how the diagnostic from issue #54556 behaves when
+// the destructor code is attached to a place held in a field of the
+// temporary being dropped.
+//
+// Eventually it would be nice if the diagnostic would actually report
+// that specific place and its type that implements the `Drop` trait.
+// But for the short term, it is acceptable to just print out the
+// whole type of the temporary.
+
+#![allow(warnings)]
+
+struct Wrap<'p> { p: &'p mut i32 }
+
+impl<'p> Drop for Wrap<'p> {
+    fn drop(&mut self) {
+        *self.p += 1;
+    }
+}
+
+struct Foo<'p> { a: String, b: Wrap<'p> }
+
+fn main() {
+    let mut x = 0;
+    let wrap = Wrap { p: &mut x };
+    let s = String::from("str");
+    let foo = Foo { a: s, b: wrap };
+    x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
+}
diff --git a/src/test/ui/nll/issue-54556-wrap-it-up.stderr b/src/test/ui/nll/issue-54556-wrap-it-up.stderr
new file mode 100644
index 00000000000..a0c19b96387
--- /dev/null
+++ b/src/test/ui/nll/issue-54556-wrap-it-up.stderr
@@ -0,0 +1,12 @@
+error[E0506]: cannot assign to `x` because it is borrowed
+  --> $DIR/issue-54556-wrap-it-up.rs:27:5
+   |
+LL |     let wrap = Wrap { p: &mut x };
+   |                               - borrow of `x` occurs here
+...
+LL |     x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
+   |     ^^^^^ assignment to borrowed `x` occurs here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0506`.