Skip to content

Commit 79ccd0e

Browse files
committed
Auto merge of #106180 - RalfJung:dereferenceable-generators, r=nbdd0121
make &mut !Unpin not dereferenceable, and Box<!Unpin> not noalias See rust-lang/unsafe-code-guidelines#381 and [this LLVM discussion](https://discourse.llvm.org/t/interaction-of-noalias-and-dereferenceable/66979). The exact semantics of how `noalias` and `dereferenceable` interact are unclear, and `@comex` found a case of LLVM actually exploiting that ambiguity for optimizations. I think for now we should treat LLVM `dereferenceable` as implying a "fake read" to happen immediately at the top of the function (standing in for the spurious reads that LLVM might introduce), and that fake read is subject to all the usual `noalias` restrictions. This means we cannot put `dereferenceable` on `&mut !Unpin` references as those references can alias with other references that are being read and written inside the function (e.g. for self-referential generators), meaning the fake read introduces aliasing conflicts with those other accesses. For `&` this is already not a problem due to rust-lang/rust#98017 which removed the `dereferenceable` attribute for other reasons. Regular `&mut Unpin` references are unaffected, so I hope the impact of this is going to be tiny. The first commit does some refactoring of the `PointerKind` enum since I found the old code very confusing each time I had to touch it. It doesn't change behavior. Fixes #2714 EDIT: Turns out our `Box<!Unpin>` treatment was incorrect, too, so the PR also fixes that now (in codegen and Miri): we do not put `noalias` on these boxes any more.
2 parents 313a19e + c9f136b commit 79ccd0e

File tree

5 files changed

+95
-76
lines changed

5 files changed

+95
-76
lines changed

src/borrow_tracker/stacked_borrows/mod.rs

+31-12
Original file line numberDiff line numberDiff line change
@@ -81,21 +81,18 @@ impl NewPermission {
8181
protector: None,
8282
}
8383
} else if pointee.is_unpin(*cx.tcx, cx.param_env()) {
84-
// A regular full mutable reference.
84+
// A regular full mutable reference. On `FnEntry` this is `noalias` and `dereferenceable`.
8585
NewPermission::Uniform {
8686
perm: Permission::Unique,
8787
access: Some(AccessKind::Write),
8888
protector,
8989
}
9090
} else {
91+
// `!Unpin` dereferences do not get `noalias` nor `dereferenceable`.
9192
NewPermission::Uniform {
9293
perm: Permission::SharedReadWrite,
93-
// FIXME: We emit `dereferenceable` for `!Unpin` mutable references, so we
94-
// should do fake accesses here. But then we run into
95-
// <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>, so for now
96-
// we don't do that.
9794
access: None,
98-
protector,
95+
protector: None,
9996
}
10097
}
10198
}
@@ -109,6 +106,7 @@ impl NewPermission {
109106
}
110107
}
111108
ty::Ref(_, _pointee, Mutability::Not) => {
109+
// Shared references. If frozen, these get `noalias` and `dereferenceable`; otherwise neither.
112110
NewPermission::FreezeSensitive {
113111
freeze_perm: Permission::SharedReadOnly,
114112
freeze_access: Some(AccessKind::Read),
@@ -137,6 +135,32 @@ impl NewPermission {
137135
}
138136
}
139137

138+
fn from_box_ty<'tcx>(
139+
ty: Ty<'tcx>,
140+
kind: RetagKind,
141+
cx: &crate::MiriInterpCx<'_, 'tcx>,
142+
) -> Self {
143+
// `ty` is not the `Box` but the field of the Box with this pointer (due to allocator handling).
144+
let pointee = ty.builtin_deref(true).unwrap().ty;
145+
if pointee.is_unpin(*cx.tcx, cx.param_env()) {
146+
// A regular box. On `FnEntry` this is `noalias`, but not `dereferenceable` (hence only
147+
// a weak protector).
148+
NewPermission::Uniform {
149+
perm: Permission::Unique,
150+
access: Some(AccessKind::Write),
151+
protector: (kind == RetagKind::FnEntry)
152+
.then_some(ProtectorKind::WeakProtector),
153+
}
154+
} else {
155+
// `!Unpin` boxes do not get `noalias` nor `dereferenceable`.
156+
NewPermission::Uniform {
157+
perm: Permission::SharedReadWrite,
158+
access: None,
159+
protector: None,
160+
}
161+
}
162+
}
163+
140164
fn protector(&self) -> Option<ProtectorKind> {
141165
match self {
142166
NewPermission::Uniform { protector, .. } => *protector,
@@ -916,12 +940,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
916940

917941
fn visit_box(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
918942
// Boxes get a weak protectors, since they may be deallocated.
919-
let new_perm = NewPermission::Uniform {
920-
perm: Permission::Unique,
921-
access: Some(AccessKind::Write),
922-
protector: (self.kind == RetagKind::FnEntry)
923-
.then_some(ProtectorKind::WeakProtector),
924-
};
943+
let new_perm = NewPermission::from_box_ty(place.layout.ty, self.kind, self.ecx);
925944
self.retag_ptr_inplace(place, new_perm, self.retag_cause)
926945
}
927946

tests/fail/stacked_borrows/deallocate_against_protector2.rs

-16
This file was deleted.

tests/fail/stacked_borrows/deallocate_against_protector2.stderr

-38
This file was deleted.

tests/pass/stacked-borrows/future-self-referential.rs

+44-10
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@ impl Future for Delay {
2626
}
2727
}
2828

29+
fn mk_waker() -> Waker {
30+
use std::sync::Arc;
31+
32+
struct MyWaker;
33+
impl Wake for MyWaker {
34+
fn wake(self: Arc<Self>) {
35+
unimplemented!()
36+
}
37+
}
38+
39+
Waker::from(Arc::new(MyWaker))
40+
}
41+
2942
async fn do_stuff() {
3043
(&mut Delay::new(1)).await;
3144
}
@@ -73,16 +86,7 @@ impl Future for DoStuff {
7386
}
7487

7588
fn run_fut<T>(fut: impl Future<Output = T>) -> T {
76-
use std::sync::Arc;
77-
78-
struct MyWaker;
79-
impl Wake for MyWaker {
80-
fn wake(self: Arc<Self>) {
81-
unimplemented!()
82-
}
83-
}
84-
85-
let waker = Waker::from(Arc::new(MyWaker));
89+
let waker = mk_waker();
8690
let mut context = Context::from_waker(&waker);
8791

8892
let mut pinned = pin!(fut);
@@ -94,7 +98,37 @@ fn run_fut<T>(fut: impl Future<Output = T>) -> T {
9498
}
9599
}
96100

101+
fn self_referential_box() {
102+
let waker = mk_waker();
103+
let cx = &mut Context::from_waker(&waker);
104+
105+
async fn my_fut() -> i32 {
106+
let val = 10;
107+
let val_ref = &val;
108+
109+
let _ = Delay::new(1).await;
110+
111+
*val_ref
112+
}
113+
114+
fn box_poll<F: Future>(
115+
mut f: Pin<Box<F>>,
116+
cx: &mut Context<'_>,
117+
) -> (Pin<Box<F>>, Poll<F::Output>) {
118+
let p = f.as_mut().poll(cx);
119+
(f, p)
120+
}
121+
122+
let my_fut = Box::pin(my_fut());
123+
let (my_fut, p1) = box_poll(my_fut, cx);
124+
assert!(p1.is_pending());
125+
let (my_fut, p2) = box_poll(my_fut, cx);
126+
assert!(p2.is_ready());
127+
drop(my_fut);
128+
}
129+
97130
fn main() {
98131
run_fut(do_stuff());
99132
run_fut(DoStuff::new());
133+
self_referential_box();
100134
}

tests/pass/stacked-borrows/stacked-borrows.rs

+20
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ fn main() {
1919
array_casts();
2020
mut_below_shr();
2121
wide_raw_ptr_in_tuple();
22+
not_unpin_not_protected();
2223
}
2324

2425
// Make sure that reading from an `&mut` does, like reborrowing to `&`,
@@ -219,3 +220,22 @@ fn wide_raw_ptr_in_tuple() {
219220
// Make sure the fn ptr part of the vtable is still fine.
220221
r.type_id();
221222
}
223+
224+
fn not_unpin_not_protected() {
225+
// `&mut !Unpin`, at least for now, does not get `noalias` nor `dereferenceable`, so we also
226+
// don't add protectors. (We could, but until we have a better idea for where we want to go with
227+
// the self-referntial-generator situation, it does not seem worth the potential trouble.)
228+
use std::marker::PhantomPinned;
229+
230+
pub struct NotUnpin(i32, PhantomPinned);
231+
232+
fn inner(x: &mut NotUnpin, f: fn(&mut NotUnpin)) {
233+
// `f` may mutate, but it may not deallocate!
234+
f(x)
235+
}
236+
237+
inner(Box::leak(Box::new(NotUnpin(0, PhantomPinned))), |x| {
238+
let raw = x as *mut _;
239+
drop(unsafe { Box::from_raw(raw) });
240+
});
241+
}

0 commit comments

Comments
 (0)