Change:
Similar to I tried this code: I expected to see this happen: Miri does not detect UB Instead, this happened: Miri detects UB (experimentally) <!-- If you're using the stable version of the compiler, you should also check if the bug also exists in the beta or nightly versions. --: <!-- Include a backtrace in the code block by setting in your environment. E.g. . --<details<summaryBacktrace</summary<p</p</details
cc
FWIW, I have now detected this bug through the tests in and .
Looks like the problem is in this line: re-borrows an on the entire slice, thus invalidation previously created pointers. So this is yet another instance of
Actually, just to be sure, are we interested in patching out all such instances of this in std/alloc/core? I know I personally would like the standard library to be clean against Miri with so that I can check libraries for stacked borrows issues. I just did a quick skim now that I understand the rules and I think I can spot a few other issues, so I'm not sure if these were deliberately left in, or if I'm just the first person to be looking this carefully.
So far we fixed various issues of this sort that people noticed, but there was never a concerted effort to find all of them. So they were not deliberately left in, just not found yet. Whether we want to fix them all is up to who ultimately decide about the std library implementation.
So far the fixes have been localized enough that it's been easy to take them (eg ). If anything comes up that requires drastic changes to fix then there might be a conversation about it, but for simple ones that don't increase maintenance burden I think they'll be happily accepted.

Discussion:
F: FnMut(&T, &T) -> bool, { let len = v.len(); <del> // SAFETY: The unsafe operations below involves indexing without a bound check (`get_unchecked` and `get_unchecked_mut`) // and copying memory (`ptr::copy_nonoverlapping`). </del> <ins> // SAFETY: The unsafe operations below involves indexing without a bounds check (by offsetting a // pointer) and copying memory (`ptr::copy_nonoverlapping`). </ins> // // a. Indexing: //  1. We checked the size of the array to >=2.

// operation panics, `hole` will get dropped and automatically write the element back // into the slice. let mut tmp = mem::ManuallyDrop::new(ptr::read(v.get_unchecked(0))); <del> let mut hole = CopyOnDrop { src: &mut *tmp, dest: v.get_unchecked_mut(1) }; ptr::copy_nonoverlapping(v.get_unchecked(1), v.get_unchecked_mut(0), 1); </del> <ins> let v = v.as_mut_ptr(); let mut hole = CopyOnDrop { src: &mut *tmp, dest: v.add(1) }; ptr::copy_nonoverlapping(v.add(1), v.add(0), 1); </ins> for i in 2..len { <del> if !is_less(v.get_unchecked(i), &*tmp) { </del> <ins> if !is_less(&*v.add(i), &*tmp) { </ins> break; } // Move `i`-th element one place to the left, thus shifting the hole to the right. <del> ptr::copy_nonoverlapping(v.get_unchecked(i), v.get_unchecked_mut(i - 1), 1); hole.dest = v.get_unchecked_mut(i); </del> <ins> ptr::copy_nonoverlapping(v.add(i), v.add(i - 1), 1); hole.dest = v.add(i); </ins> } // `hole` gets dropped and thus copies `tmp` into the remaining hole in `v`. }

F: FnMut(&T, &T) -> bool, { let len = v.len(); <del> // SAFETY: The unsafe operations below involves indexing without a bound check (`get_unchecked` and `get_unchecked_mut`) // and copying memory (`ptr::copy_nonoverlapping`). </del> <ins> // SAFETY: The unsafe operations below involves indexing without a bound check (by offsetting a // pointer) and copying memory (`ptr::copy_nonoverlapping`). </ins> // // a. Indexing: //  1. We checked the size of the array to >= 2.

// operation panics, `hole` will get dropped and automatically write the element back // into the slice. let mut tmp = mem::ManuallyDrop::new(ptr::read(v.get_unchecked(len - 1))); <del> let mut hole = CopyOnDrop { src: &mut *tmp, dest: v.get_unchecked_mut(len - 2) }; ptr::copy_nonoverlapping(v.get_unchecked(len - 2), v.get_unchecked_mut(len - 1), 1); </del> <ins> let v = v.as_mut_ptr(); let mut hole = CopyOnDrop { src: &mut *tmp, dest: v.add(len - 2) }; ptr::copy_nonoverlapping(v.add(len - 2), v.add(len - 1), 1); </ins> for i in (0..len - 2).rev() { <del> if !is_less(&*tmp, v.get_unchecked(i)) { </del> <ins> if !is_less(&*tmp, &*v.add(i)) { </ins> break; } // Move `i`-th element one place to the right, thus shifting the hole to the left. <del> ptr::copy_nonoverlapping(v.get_unchecked(i), v.get_unchecked_mut(i + 1), 1); hole.dest = v.get_unchecked_mut(i); </del> <ins> ptr::copy_nonoverlapping(v.add(i), v.add(i + 1), 1); hole.dest = v.add(i); </ins> } // `hole` gets dropped and thus copies `tmp` into the remaining hole in `v`. }

if start_l == end_l { // Trace `block_l` elements from the left side. start_l = MaybeUninit::slice_as_mut_ptr(&mut offsets_l); <del> end_l = MaybeUninit::slice_as_mut_ptr(&mut offsets_l); </del> <ins> end_l = start_l; </ins> let mut elem = l; for i in 0..block_l {

if start_r == end_r { // Trace `block_r` elements from the right side. start_r = MaybeUninit::slice_as_mut_ptr(&mut offsets_r); <del> end_r = MaybeUninit::slice_as_mut_ptr(&mut offsets_r); </del> <ins> end_r = start_r; </ins> let mut elem = r; for i in 0..block_r {

// Swap the found pair of out-of-order elements. r -= 1; <del> ptr::swap(v.get_unchecked_mut(l), v.get_unchecked_mut(r)); </del> <ins> let ptr = v.as_mut_ptr(); ptr::swap(ptr.add(l), ptr.add(r)); </ins> l += 1; } }