Change:
I tried this code: I expected to see this happen: it runs cleanly under Miri. Instead, this happened: The issue is in this construct: By the SB model, moving a (struct directly containing) retags that as the live unique owner of the referenced allocation. Thus passing to invalidates the duplicated () we just retrieved. The correct way to write this is with : Tested against 1.81.0-nightly (2024-07-09 ). This also catches , which is implemented using . IIRC the LLVM backend is currently not informed about this instance of this UB, as the relevant attributes are only when the argument passing ABI is or , which it is not in this case. Additionally, Miri's SB implementation does not complain when the box is , (conjecture: since is not ). cc — another case of instead of in std. This was randomly spotted. It might be worth it to do a grep for to try to spot further such mistakes. 's docs call this out as incorrect, but it's still quite easy to overlook.
Good catch! That is odd. Miri uses to determine the size to use for retags, so should not make a difference.
Just searched for using the GitHub search. The only other suspect snippet is already marked as kinda iffy: and somewhat amusingly calls out this exact issue in comments as well: The reason I assigned was conjecture based on likely old information (Miri only checking cases where we passed constant assertions to LLVM by default), but it is the observed behavior. The second potential reason now coming to mind is that is , thus getting raw pointer retags instead of unique retags.
Ah yes that would explain it.
I'm putting up a PR to fix just the instance reported here, but I would like to see a larger effort to replace all uses of .
In compiler, libs, or both? All usages of or just some particular ones?
Note that I am not a libs reviewer, so before you submit a large PR you should check in with one of these lovely people or post in Zulilp: I would prefer a simple policy that we do not use . All uses of it can (with varying amounts of typing) be replaced with . I don't think it's worth the brain cycles to try to think about whether a certain use of follows the pattern that was reported here. The compiler is much less important here; nobody is running the compiler in Miri and this hazard is specific to an aspect of Stacked Borrows that is not actively used in optimizations the current toolchain does. For the library, being clean with respect to Stacked Borrows is an important quality-of-implementation issue. The same concern does not apply to the compiler. Note that if you run the example program in this issue with there is no UB report, and that is a deliberate aspect of Tree Borrows. But that is just (the general issue is described in ); the same property that makes this well-defined in Tree Borrows also removes some optimizations that people instinctively believe are valid.

Discussion:
use crate::io::{ self, ErrorKind, IntoInnerError, IoSlice, Seek, SeekFrom, Write, DEFAULT_BUF_SIZE, }; <del> use crate::mem; </del> <ins> use crate::mem::{self, ManuallyDrop}; </ins> use crate::ptr; /// Wraps a writer and buffers its output.

/// assert_eq!(&buffered_data.unwrap(), b"ata"); /// ``` #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] <del> pub fn into_parts(mut self) -> (W, Result<Vec<u8>, WriterPanicked>) { let buf = mem::take(&mut self.buf); let buf = if !self.panicked { Ok(buf) } else { Err(WriterPanicked { buf }) }; </del> <ins> pub fn into_parts(self) -> (W, Result<Vec<u8>, WriterPanicked>) { let mut this = ManuallyDrop::new(self); let buf = mem::take(&mut this.buf); let buf = if !this.panicked { Ok(buf) } else { Err(WriterPanicked { buf }) }; </ins> <del> // SAFETY: forget(self) prevents double dropping inner let inner = unsafe { ptr::read(&self.inner) }; mem::forget(self); </del> <ins> // SAFETY: double-drops are prevented by putting `this` in a ManuallyDrop that is never dropped let inner = unsafe { ptr::read(&this.inner) }; </ins> (inner, buf) }

// But we initialized the whole buffer! assert_eq!(reader.initialized(), reader.capacity()); } <ins> /// This is a regression test for https://github.com/rust-lang/rust/issues/127584. #[test] fn bufwriter_aliasing() { use crate::io::{BufWriter, Cursor}; let mut v = vec![0; 1024]; let c = Cursor::new(&mut v); let w = BufWriter::new(Box::new(c)); let _ = w.into_parts(); } </ins>