Skip to content

Commit 1ef1687

Browse files
committed
also do not add noalias on not-Unpin Box
1 parent ea541bc commit 1ef1687

File tree

6 files changed

+180
-116
lines changed

6 files changed

+180
-116
lines changed

compiler/rustc_abi/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1443,8 +1443,8 @@ pub enum PointerKind {
14431443
SharedRef { frozen: bool },
14441444
/// Mutable reference. `unpin` indicates the absence of any pinned data.
14451445
MutableRef { unpin: bool },
1446-
/// Box.
1447-
Box,
1446+
/// Box. `unpin` indicates the absence of any pinned data.
1447+
Box { unpin: bool },
14481448
}
14491449

14501450
/// Note that this information is advisory only, and backends are free to ignore it.

compiler/rustc_middle/src/ty/layout.rs

+95-91
Original file line numberDiff line numberDiff line change
@@ -818,110 +818,114 @@ where
818818
let tcx = cx.tcx();
819819
let param_env = cx.param_env();
820820

821-
let pointee_info =
822-
match *this.ty.kind() {
823-
ty::RawPtr(mt) if offset.bytes() == 0 => {
824-
tcx.layout_of(param_env.and(mt.ty)).ok().map(|layout| PointeeInfo {
825-
size: layout.size,
826-
align: layout.align.abi,
827-
safe: None,
828-
})
829-
}
830-
ty::FnPtr(fn_sig) if offset.bytes() == 0 => {
831-
tcx.layout_of(param_env.and(tcx.mk_fn_ptr(fn_sig))).ok().map(|layout| {
832-
PointeeInfo { size: layout.size, align: layout.align.abi, safe: None }
833-
})
834-
}
835-
ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
836-
// Use conservative pointer kind if not optimizing. This saves us the
837-
// Freeze/Unpin queries, and can save time in the codegen backend (noalias
838-
// attributes in LLVM have compile-time cost even in unoptimized builds).
839-
let optimize = tcx.sess.opts.optimize != OptLevel::No;
840-
let kind = match mt {
841-
hir::Mutability::Not => PointerKind::SharedRef {
842-
frozen: optimize && ty.is_freeze(tcx, cx.param_env()),
843-
},
844-
hir::Mutability::Mut => PointerKind::MutableRef {
845-
unpin: optimize && ty.is_unpin(tcx, cx.param_env()),
846-
},
847-
};
821+
let pointee_info = match *this.ty.kind() {
822+
ty::RawPtr(mt) if offset.bytes() == 0 => {
823+
tcx.layout_of(param_env.and(mt.ty)).ok().map(|layout| PointeeInfo {
824+
size: layout.size,
825+
align: layout.align.abi,
826+
safe: None,
827+
})
828+
}
829+
ty::FnPtr(fn_sig) if offset.bytes() == 0 => {
830+
tcx.layout_of(param_env.and(tcx.mk_fn_ptr(fn_sig))).ok().map(|layout| PointeeInfo {
831+
size: layout.size,
832+
align: layout.align.abi,
833+
safe: None,
834+
})
835+
}
836+
ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
837+
// Use conservative pointer kind if not optimizing. This saves us the
838+
// Freeze/Unpin queries, and can save time in the codegen backend (noalias
839+
// attributes in LLVM have compile-time cost even in unoptimized builds).
840+
let optimize = tcx.sess.opts.optimize != OptLevel::No;
841+
let kind = match mt {
842+
hir::Mutability::Not => PointerKind::SharedRef {
843+
frozen: optimize && ty.is_freeze(tcx, cx.param_env()),
844+
},
845+
hir::Mutability::Mut => PointerKind::MutableRef {
846+
unpin: optimize && ty.is_unpin(tcx, cx.param_env()),
847+
},
848+
};
848849

849-
tcx.layout_of(param_env.and(ty)).ok().map(|layout| PointeeInfo {
850-
size: layout.size,
851-
align: layout.align.abi,
852-
safe: Some(kind),
853-
})
854-
}
850+
tcx.layout_of(param_env.and(ty)).ok().map(|layout| PointeeInfo {
851+
size: layout.size,
852+
align: layout.align.abi,
853+
safe: Some(kind),
854+
})
855+
}
855856

856-
_ => {
857-
let mut data_variant = match this.variants {
858-
// Within the discriminant field, only the niche itself is
859-
// always initialized, so we only check for a pointer at its
860-
// offset.
861-
//
862-
// If the niche is a pointer, it's either valid (according
863-
// to its type), or null (which the niche field's scalar
864-
// validity range encodes). This allows using
865-
// `dereferenceable_or_null` for e.g., `Option<&T>`, and
866-
// this will continue to work as long as we don't start
867-
// using more niches than just null (e.g., the first page of
868-
// the address space, or unaligned pointers).
869-
Variants::Multiple {
870-
tag_encoding: TagEncoding::Niche { untagged_variant, .. },
871-
tag_field,
872-
..
873-
} if this.fields.offset(tag_field) == offset => {
874-
Some(this.for_variant(cx, untagged_variant))
875-
}
876-
_ => Some(this),
877-
};
857+
_ => {
858+
let mut data_variant = match this.variants {
859+
// Within the discriminant field, only the niche itself is
860+
// always initialized, so we only check for a pointer at its
861+
// offset.
862+
//
863+
// If the niche is a pointer, it's either valid (according
864+
// to its type), or null (which the niche field's scalar
865+
// validity range encodes). This allows using
866+
// `dereferenceable_or_null` for e.g., `Option<&T>`, and
867+
// this will continue to work as long as we don't start
868+
// using more niches than just null (e.g., the first page of
869+
// the address space, or unaligned pointers).
870+
Variants::Multiple {
871+
tag_encoding: TagEncoding::Niche { untagged_variant, .. },
872+
tag_field,
873+
..
874+
} if this.fields.offset(tag_field) == offset => {
875+
Some(this.for_variant(cx, untagged_variant))
876+
}
877+
_ => Some(this),
878+
};
878879

879-
if let Some(variant) = data_variant {
880-
// We're not interested in any unions.
881-
if let FieldsShape::Union(_) = variant.fields {
882-
data_variant = None;
883-
}
880+
if let Some(variant) = data_variant {
881+
// We're not interested in any unions.
882+
if let FieldsShape::Union(_) = variant.fields {
883+
data_variant = None;
884884
}
885+
}
885886

886-
let mut result = None;
887-
888-
if let Some(variant) = data_variant {
889-
// FIXME(erikdesjardins): handle non-default addrspace ptr sizes
890-
// (requires passing in the expected address space from the caller)
891-
let ptr_end = offset + Pointer(AddressSpace::DATA).size(cx);
892-
for i in 0..variant.fields.count() {
893-
let field_start = variant.fields.offset(i);
894-
if field_start <= offset {
895-
let field = variant.field(cx, i);
896-
result = field.to_result().ok().and_then(|field| {
897-
if ptr_end <= field_start + field.size {
898-
// We found the right field, look inside it.
899-
let field_info =
900-
field.pointee_info_at(cx, offset - field_start);
901-
field_info
902-
} else {
903-
None
904-
}
905-
});
906-
if result.is_some() {
907-
break;
887+
let mut result = None;
888+
889+
if let Some(variant) = data_variant {
890+
// FIXME(erikdesjardins): handle non-default addrspace ptr sizes
891+
// (requires passing in the expected address space from the caller)
892+
let ptr_end = offset + Pointer(AddressSpace::DATA).size(cx);
893+
for i in 0..variant.fields.count() {
894+
let field_start = variant.fields.offset(i);
895+
if field_start <= offset {
896+
let field = variant.field(cx, i);
897+
result = field.to_result().ok().and_then(|field| {
898+
if ptr_end <= field_start + field.size {
899+
// We found the right field, look inside it.
900+
let field_info =
901+
field.pointee_info_at(cx, offset - field_start);
902+
field_info
903+
} else {
904+
None
908905
}
906+
});
907+
if result.is_some() {
908+
break;
909909
}
910910
}
911911
}
912+
}
912913

913-
// FIXME(eddyb) This should be for `ptr::Unique<T>`, not `Box<T>`.
914-
if let Some(ref mut pointee) = result {
915-
if let ty::Adt(def, _) = this.ty.kind() {
916-
if def.is_box() && offset.bytes() == 0 {
917-
pointee.safe = Some(PointerKind::Box);
918-
}
914+
// FIXME(eddyb) This should be for `ptr::Unique<T>`, not `Box<T>`.
915+
if let Some(ref mut pointee) = result {
916+
if let ty::Adt(def, _) = this.ty.kind() {
917+
if def.is_box() && offset.bytes() == 0 {
918+
let optimize = tcx.sess.opts.optimize != OptLevel::No;
919+
pointee.safe = Some(PointerKind::Box {
920+
unpin: optimize && this.ty.boxed_ty().is_unpin(tcx, cx.param_env()),
921+
});
919922
}
920923
}
921-
922-
result
923924
}
924-
};
925+
926+
result
927+
}
928+
};
925929

926930
debug!(
927931
"pointee_info_at (offset={:?}, type kind: {:?}) => {:?}",

compiler/rustc_ty_utils/src/abi.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ fn adjust_for_rust_scalar<'tcx>(
261261
// <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>). If LLVM had a way
262262
// to say "dereferenceable on entry" we could use it here.
263263
attrs.pointee_size = match kind {
264-
PointerKind::Box
264+
PointerKind::Box { .. }
265265
| PointerKind::SharedRef { frozen: false }
266266
| PointerKind::MutableRef { unpin: false } => Size::ZERO,
267267
PointerKind::SharedRef { frozen: true }
@@ -278,17 +278,16 @@ fn adjust_for_rust_scalar<'tcx>(
278278
// versions at all anymore. We still support turning it off using -Zmutable-noalias.
279279
let noalias_mut_ref = cx.tcx.sess.opts.unstable_opts.mutable_noalias;
280280

281-
// `&mut` pointer parameters never alias other parameters,
282-
// or mutable global data
283-
//
284281
// `&T` where `T` contains no `UnsafeCell<U>` is immutable, and can be marked as both
285282
// `readonly` and `noalias`, as LLVM's definition of `noalias` is based solely on memory
286283
// dependencies rather than pointer equality. However this only applies to arguments,
287284
// not return values.
285+
//
286+
// `&mut T` and `Box<T>` where `T: Unpin` are unique and hence `noalias`.
288287
let no_alias = match kind {
289288
PointerKind::SharedRef { frozen } => frozen,
290289
PointerKind::MutableRef { unpin } => unpin && noalias_mut_ref,
291-
PointerKind::Box => noalias_for_box,
290+
PointerKind::Box { unpin } => unpin && noalias_for_box,
292291
};
293292
// We can never add `noalias` in return position; that LLVM attribute has some very surprising semantics
294293
// (see <https://github.com/rust-lang/unsafe-code-guidelines/issues/385#issuecomment-1368055745>).

src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs

+27-6
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,32 @@ impl NewPermission {
135135
}
136136
}
137137

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+
138164
fn protector(&self) -> Option<ProtectorKind> {
139165
match self {
140166
NewPermission::Uniform { protector, .. } => *protector,
@@ -914,12 +940,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
914940

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

src/tools/miri/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/codegen/function-arguments.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,12 @@ pub fn _box(x: Box<i32>) -> Box<i32> {
181181
x
182182
}
183183

184+
// CHECK: noundef nonnull align 4 {{i32\*|ptr}} @notunpin_box({{i32\*|ptr}} noundef nonnull align 4 %x)
185+
#[no_mangle]
186+
pub fn notunpin_box(x: Box<NotUnpin>) -> Box<NotUnpin> {
187+
x
188+
}
189+
184190
// CHECK: @struct_return({{%S\*|ptr}} noalias nocapture noundef sret(%S) dereferenceable(32){{( %0)?}})
185191
#[no_mangle]
186192
pub fn struct_return() -> S {
@@ -247,12 +253,12 @@ pub fn trait_raw(_: *const dyn Drop) {
247253

248254
// CHECK: @trait_box({{\{\}\*|ptr}} noalias noundef nonnull align 1{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
249255
#[no_mangle]
250-
pub fn trait_box(_: Box<dyn Drop>) {
256+
pub fn trait_box(_: Box<dyn Drop + Unpin>) {
251257
}
252258

253259
// CHECK: { {{i8\*|ptr}}, {{i8\*|ptr}} } @trait_option({{i8\*|ptr}} noalias noundef align 1 %x.0, {{i8\*|ptr}} %x.1)
254260
#[no_mangle]
255-
pub fn trait_option(x: Option<Box<dyn Drop>>) -> Option<Box<dyn Drop>> {
261+
pub fn trait_option(x: Option<Box<dyn Drop + Unpin>>) -> Option<Box<dyn Drop + Unpin>> {
256262
x
257263
}
258264

0 commit comments

Comments
 (0)