diff --git a/src/stacked_borrows.rs b/src/stacked_borrows.rs index ab0c3f8994c5..56688ca10f49 100644 --- a/src/stacked_borrows.rs +++ b/src/stacked_borrows.rs @@ -167,13 +167,15 @@ impl<'tcx> Stack { behind a barrier", bor)) } (BorStackItem::Uniq(itm_t), Borrow::Uniq(bor_t)) if itm_t == bor_t => { - // Found matching unique item. + // Found matching unique item. This is *always* required to use a `Uniq`: + // The item must still be on the stack. if !is_write { - // As a special case, if we are reading and since we *did* find the `Uniq`, - // we try to pop less: We are happy with making a `Shr` or `Frz` active; - // that one will not mind concurrent reads. + // As a special case, if we are reading, let us see if it would be + // beneficial to pretend we are a raw pointer instead. If + // raw pointers are allowed to read while popping *less* than we + // would have to pop, there is no reason not to let them do this. match self.reactivatable(Borrow::default(), is_write) { - // If we got something better that `idx`, use that + // If we got something better (popping less) that `idx`, use that Ok(None) => return Ok(None), Ok(Some(shr_idx)) if shr_idx <= idx => return Ok(Some(shr_idx)), // Otherwise just go on. @@ -329,6 +331,8 @@ impl<'tcx> Stacks { ))) } // Sometimes we also need to be frozen. + // In this case we *both* push `Shr` and then freeze. This means that a `&mut` + // to `*const` to `*mut` cast through `&` actually works. if frozen { // Even shared refs can have uniq tags (after transmute). That's not an error // but they do not get any freezing benefits. diff --git a/tests/compile-fail/stacked_borrows/illegal_read4.rs b/tests/compile-fail/stacked_borrows/illegal_read4.rs new file mode 100644 index 000000000000..c86ec1286daa --- /dev/null +++ b/tests/compile-fail/stacked_borrows/illegal_read4.rs @@ -0,0 +1,9 @@ +// Using a raw invalidates derived `&mut` even for reading. +fn main() { + let mut x = 2; + let xref1 = &mut x; + let xraw = xref1 as *mut _; + let xref2 = unsafe { &mut *xraw }; + let _val = unsafe { *xraw }; // use the raw again, this invalidates xref2 *even* with the special read except for uniq refs + let _illegal = *xref2; //~ ERROR does not exist on the stack +} diff --git a/tests/run-pass/stacked-borrows.rs b/tests/run-pass/stacked-borrows.rs index 00b21c746c26..7b7a7c9be203 100644 --- a/tests/run-pass/stacked-borrows.rs +++ b/tests/run-pass/stacked-borrows.rs @@ -6,6 +6,7 @@ fn main() { ref_raw_int_raw(); mut_shr_raw(); mut_raw_then_mut_shr(); + mut_raw_mut(); } // Deref a raw ptr to access a field of a large struct, where the field @@ -76,3 +77,25 @@ fn mut_raw_then_mut_shr() { } assert_eq!(x, 4); } + +// Ensure that if we derive from a mut a raw, and then from that a mut, +// and then read through the original mut, that does not invalidate the raw. +// This shows that the read-exception for `&mut` applies even if the `Shr` item +// on the stack is not at the top. +fn mut_raw_mut() { + let mut x = 2; + { + let xref1 = &mut x; + let xraw = xref1 as *mut _; + let _xref2 = unsafe { &mut *xraw }; + let _val = *xref1; + unsafe { *xraw = 4; } + // we can now use both xraw and xref1, for reading + assert_eq!(*xref1, 4); + assert_eq!(unsafe { *xraw }, 4); + assert_eq!(*xref1, 4); + assert_eq!(unsafe { *xraw }, 4); + // we cannot use xref2; see `compile-fail/stacked-borows/illegal_read4.rs` + } + assert_eq!(x, 4); +}