Auto merge of #106180 - RalfJung:dereferenceable-generators, r=nbdd0121

make &mut !Unpin not dereferenceable, and Box<!Unpin> not noalias

See https://github.com/rust-lang/unsafe-code-guidelines/issues/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 https://github.com/rust-lang/rust/pull/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 https://github.com/rust-lang/miri/issues/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.
This commit is contained in:
bors 2023-02-07 03:35:10 +00:00
commit dffea43fc1
10 changed files with 248 additions and 222 deletions

View file

@ -1439,21 +1439,12 @@ impl<V: Idx> fmt::Debug for LayoutS<V> {
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum PointerKind { pub enum PointerKind {
/// Most general case, we know no restrictions to tell LLVM. /// Shared reference. `frozen` indicates the absence of any `UnsafeCell`.
SharedMutable, SharedRef { frozen: bool },
/// Mutable reference. `unpin` indicates the absence of any pinned data.
/// `&T` where `T` contains no `UnsafeCell`, is `dereferenceable`, `noalias` and `readonly`. MutableRef { unpin: bool },
Frozen, /// Box. `unpin` indicates the absence of any pinned data.
Box { unpin: bool },
/// `&mut T` which is `dereferenceable` and `noalias` but not `readonly`.
UniqueBorrowed,
/// `&mut !Unpin`, which is `dereferenceable` but neither `noalias` nor `readonly`.
UniqueBorrowedPinned,
/// `Box<T>`, which is `noalias` (even on return types, unlike the above) but neither `readonly`
/// nor `dereferenceable`.
UniqueOwned,
} }
/// Note that this information is advisory only, and backends are free to ignore it. /// Note that this information is advisory only, and backends are free to ignore it.

View file

@ -818,125 +818,114 @@ where
let tcx = cx.tcx(); let tcx = cx.tcx();
let param_env = cx.param_env(); let param_env = cx.param_env();
let pointee_info = let pointee_info = match *this.ty.kind() {
match *this.ty.kind() { ty::RawPtr(mt) if offset.bytes() == 0 => {
ty::RawPtr(mt) if offset.bytes() == 0 => { tcx.layout_of(param_env.and(mt.ty)).ok().map(|layout| PointeeInfo {
tcx.layout_of(param_env.and(mt.ty)).ok().map(|layout| PointeeInfo { size: layout.size,
size: layout.size, align: layout.align.abi,
align: layout.align.abi, safe: None,
safe: None, })
}) }
ty::FnPtr(fn_sig) if offset.bytes() == 0 => {
tcx.layout_of(param_env.and(tcx.mk_fn_ptr(fn_sig))).ok().map(|layout| PointeeInfo {
size: layout.size,
align: layout.align.abi,
safe: None,
})
}
ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
// Use conservative pointer kind if not optimizing. This saves us the
// Freeze/Unpin queries, and can save time in the codegen backend (noalias
// attributes in LLVM have compile-time cost even in unoptimized builds).
let optimize = tcx.sess.opts.optimize != OptLevel::No;
let kind = match mt {
hir::Mutability::Not => PointerKind::SharedRef {
frozen: optimize && ty.is_freeze(tcx, cx.param_env()),
},
hir::Mutability::Mut => PointerKind::MutableRef {
unpin: optimize && ty.is_unpin(tcx, cx.param_env()),
},
};
tcx.layout_of(param_env.and(ty)).ok().map(|layout| PointeeInfo {
size: layout.size,
align: layout.align.abi,
safe: Some(kind),
})
}
_ => {
let mut data_variant = match this.variants {
// Within the discriminant field, only the niche itself is
// always initialized, so we only check for a pointer at its
// offset.
//
// If the niche is a pointer, it's either valid (according
// to its type), or null (which the niche field's scalar
// validity range encodes). This allows using
// `dereferenceable_or_null` for e.g., `Option<&T>`, and
// this will continue to work as long as we don't start
// using more niches than just null (e.g., the first page of
// the address space, or unaligned pointers).
Variants::Multiple {
tag_encoding: TagEncoding::Niche { untagged_variant, .. },
tag_field,
..
} if this.fields.offset(tag_field) == offset => {
Some(this.for_variant(cx, untagged_variant))
}
_ => Some(this),
};
if let Some(variant) = data_variant {
// We're not interested in any unions.
if let FieldsShape::Union(_) = variant.fields {
data_variant = None;
}
} }
ty::FnPtr(fn_sig) if offset.bytes() == 0 => {
tcx.layout_of(param_env.and(tcx.mk_fn_ptr(fn_sig))).ok().map(|layout| { let mut result = None;
PointeeInfo { size: layout.size, align: layout.align.abi, safe: None }
}) if let Some(variant) = data_variant {
} // FIXME(erikdesjardins): handle non-default addrspace ptr sizes
ty::Ref(_, ty, mt) if offset.bytes() == 0 => { // (requires passing in the expected address space from the caller)
let kind = if tcx.sess.opts.optimize == OptLevel::No { let ptr_end = offset + Pointer(AddressSpace::DATA).size(cx);
// Use conservative pointer kind if not optimizing. This saves us the for i in 0..variant.fields.count() {
// Freeze/Unpin queries, and can save time in the codegen backend (noalias let field_start = variant.fields.offset(i);
// attributes in LLVM have compile-time cost even in unoptimized builds). if field_start <= offset {
PointerKind::SharedMutable let field = variant.field(cx, i);
} else { result = field.to_result().ok().and_then(|field| {
match mt { if ptr_end <= field_start + field.size {
hir::Mutability::Not => { // We found the right field, look inside it.
if ty.is_freeze(tcx, cx.param_env()) { let field_info =
PointerKind::Frozen field.pointee_info_at(cx, offset - field_start);
field_info
} else { } else {
PointerKind::SharedMutable None
}
}
hir::Mutability::Mut => {
// References to self-referential structures should not be considered
// noalias, as another pointer to the structure can be obtained, that
// is not based-on the original reference. We consider all !Unpin
// types to be potentially self-referential here.
if ty.is_unpin(tcx, cx.param_env()) {
PointerKind::UniqueBorrowed
} else {
PointerKind::UniqueBorrowedPinned
} }
});
if result.is_some() {
break;
} }
} }
}; }
tcx.layout_of(param_env.and(ty)).ok().map(|layout| PointeeInfo {
size: layout.size,
align: layout.align.abi,
safe: Some(kind),
})
} }
_ => { // FIXME(eddyb) This should be for `ptr::Unique<T>`, not `Box<T>`.
let mut data_variant = match this.variants { if let Some(ref mut pointee) = result {
// Within the discriminant field, only the niche itself is if let ty::Adt(def, _) = this.ty.kind() {
// always initialized, so we only check for a pointer at its if def.is_box() && offset.bytes() == 0 {
// offset. let optimize = tcx.sess.opts.optimize != OptLevel::No;
// pointee.safe = Some(PointerKind::Box {
// If the niche is a pointer, it's either valid (according unpin: optimize && this.ty.boxed_ty().is_unpin(tcx, cx.param_env()),
// to its type), or null (which the niche field's scalar });
// validity range encodes). This allows using
// `dereferenceable_or_null` for e.g., `Option<&T>`, and
// this will continue to work as long as we don't start
// using more niches than just null (e.g., the first page of
// the address space, or unaligned pointers).
Variants::Multiple {
tag_encoding: TagEncoding::Niche { untagged_variant, .. },
tag_field,
..
} if this.fields.offset(tag_field) == offset => {
Some(this.for_variant(cx, untagged_variant))
}
_ => Some(this),
};
if let Some(variant) = data_variant {
// We're not interested in any unions.
if let FieldsShape::Union(_) = variant.fields {
data_variant = None;
} }
} }
let mut result = None;
if let Some(variant) = data_variant {
// FIXME(erikdesjardins): handle non-default addrspace ptr sizes
// (requires passing in the expected address space from the caller)
let ptr_end = offset + Pointer(AddressSpace::DATA).size(cx);
for i in 0..variant.fields.count() {
let field_start = variant.fields.offset(i);
if field_start <= offset {
let field = variant.field(cx, i);
result = field.to_result().ok().and_then(|field| {
if ptr_end <= field_start + field.size {
// We found the right field, look inside it.
let field_info =
field.pointee_info_at(cx, offset - field_start);
field_info
} else {
None
}
});
if result.is_some() {
break;
}
}
}
}
// FIXME(eddyb) This should be for `ptr::Unique<T>`, not `Box<T>`.
if let Some(ref mut pointee) = result {
if let ty::Adt(def, _) = this.ty.kind() {
if def.is_box() && offset.bytes() == 0 {
pointee.safe = Some(PointerKind::UniqueOwned);
}
}
}
result
} }
};
result
}
};
debug!( debug!(
"pointee_info_at (offset={:?}, type kind: {:?}) => {:?}", "pointee_info_at (offset={:?}, type kind: {:?}) => {:?}",

View file

@ -254,15 +254,18 @@ fn adjust_for_rust_scalar<'tcx>(
if let Some(kind) = pointee.safe { if let Some(kind) = pointee.safe {
attrs.pointee_align = Some(pointee.align); attrs.pointee_align = Some(pointee.align);
// `Box` (`UniqueBorrowed`) are not necessarily dereferenceable // `Box` are not necessarily dereferenceable for the entire duration of the function as
// for the entire duration of the function as they can be deallocated // they can be deallocated at any time. Same for non-frozen shared references (see
// at any time. Same for shared mutable references. If LLVM had a // <https://github.com/rust-lang/rust/pull/98017>), and for mutable references to
// way to say "dereferenceable on entry" we could use it here. // potentially self-referential types (see
// <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>). If LLVM had a way
// to say "dereferenceable on entry" we could use it here.
attrs.pointee_size = match kind { attrs.pointee_size = match kind {
PointerKind::UniqueBorrowed PointerKind::Box { .. }
| PointerKind::UniqueBorrowedPinned | PointerKind::SharedRef { frozen: false }
| PointerKind::Frozen => pointee.size, | PointerKind::MutableRef { unpin: false } => Size::ZERO,
PointerKind::SharedMutable | PointerKind::UniqueOwned => Size::ZERO, PointerKind::SharedRef { frozen: true }
| PointerKind::MutableRef { unpin: true } => pointee.size,
}; };
// The aliasing rules for `Box<T>` are still not decided, but currently we emit // The aliasing rules for `Box<T>` are still not decided, but currently we emit
@ -275,18 +278,16 @@ fn adjust_for_rust_scalar<'tcx>(
// versions at all anymore. We still support turning it off using -Zmutable-noalias. // versions at all anymore. We still support turning it off using -Zmutable-noalias.
let noalias_mut_ref = cx.tcx.sess.opts.unstable_opts.mutable_noalias; let noalias_mut_ref = cx.tcx.sess.opts.unstable_opts.mutable_noalias;
// `&mut` pointer parameters never alias other parameters, // `&T` where `T` contains no `UnsafeCell<U>` is immutable, and can be marked as both
// or mutable global data // `readonly` and `noalias`, as LLVM's definition of `noalias` is based solely on memory
// dependencies rather than pointer equality. However this only applies to arguments,
// not return values.
// //
// `&T` where `T` contains no `UnsafeCell<U>` is immutable, // `&mut T` and `Box<T>` where `T: Unpin` are unique and hence `noalias`.
// and can be marked as both `readonly` and `noalias`, as
// LLVM's definition of `noalias` is based solely on memory
// dependencies rather than pointer equality
let no_alias = match kind { let no_alias = match kind {
PointerKind::SharedMutable | PointerKind::UniqueBorrowedPinned => false, PointerKind::SharedRef { frozen } => frozen,
PointerKind::UniqueBorrowed => noalias_mut_ref, PointerKind::MutableRef { unpin } => unpin && noalias_mut_ref,
PointerKind::UniqueOwned => noalias_for_box, PointerKind::Box { unpin } => unpin && noalias_for_box,
PointerKind::Frozen => true,
}; };
// We can never add `noalias` in return position; that LLVM attribute has some very surprising semantics // We can never add `noalias` in return position; that LLVM attribute has some very surprising semantics
// (see <https://github.com/rust-lang/unsafe-code-guidelines/issues/385#issuecomment-1368055745>). // (see <https://github.com/rust-lang/unsafe-code-guidelines/issues/385#issuecomment-1368055745>).
@ -294,7 +295,7 @@ fn adjust_for_rust_scalar<'tcx>(
attrs.set(ArgAttribute::NoAlias); attrs.set(ArgAttribute::NoAlias);
} }
if kind == PointerKind::Frozen && !is_return { if matches!(kind, PointerKind::SharedRef { frozen: true }) && !is_return {
attrs.set(ArgAttribute::ReadOnly); attrs.set(ArgAttribute::ReadOnly);
} }
} }

View file

@ -81,21 +81,18 @@ impl NewPermission {
protector: None, protector: None,
} }
} else if pointee.is_unpin(*cx.tcx, cx.param_env()) { } else if pointee.is_unpin(*cx.tcx, cx.param_env()) {
// A regular full mutable reference. // A regular full mutable reference. On `FnEntry` this is `noalias` and `dereferenceable`.
NewPermission::Uniform { NewPermission::Uniform {
perm: Permission::Unique, perm: Permission::Unique,
access: Some(AccessKind::Write), access: Some(AccessKind::Write),
protector, protector,
} }
} else { } else {
// `!Unpin` dereferences do not get `noalias` nor `dereferenceable`.
NewPermission::Uniform { NewPermission::Uniform {
perm: Permission::SharedReadWrite, perm: Permission::SharedReadWrite,
// FIXME: We emit `dereferenceable` for `!Unpin` mutable references, so we
// should do fake accesses here. But then we run into
// <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>, so for now
// we don't do that.
access: None, access: None,
protector, protector: None,
} }
} }
} }
@ -109,6 +106,7 @@ impl NewPermission {
} }
} }
ty::Ref(_, _pointee, Mutability::Not) => { ty::Ref(_, _pointee, Mutability::Not) => {
// Shared references. If frozen, these get `noalias` and `dereferenceable`; otherwise neither.
NewPermission::FreezeSensitive { NewPermission::FreezeSensitive {
freeze_perm: Permission::SharedReadOnly, freeze_perm: Permission::SharedReadOnly,
freeze_access: Some(AccessKind::Read), freeze_access: Some(AccessKind::Read),
@ -137,6 +135,32 @@ impl NewPermission {
} }
} }
fn from_box_ty<'tcx>(
ty: Ty<'tcx>,
kind: RetagKind,
cx: &crate::MiriInterpCx<'_, 'tcx>,
) -> Self {
// `ty` is not the `Box` but the field of the Box with this pointer (due to allocator handling).
let pointee = ty.builtin_deref(true).unwrap().ty;
if pointee.is_unpin(*cx.tcx, cx.param_env()) {
// A regular box. On `FnEntry` this is `noalias`, but not `dereferenceable` (hence only
// a weak protector).
NewPermission::Uniform {
perm: Permission::Unique,
access: Some(AccessKind::Write),
protector: (kind == RetagKind::FnEntry)
.then_some(ProtectorKind::WeakProtector),
}
} else {
// `!Unpin` boxes do not get `noalias` nor `dereferenceable`.
NewPermission::Uniform {
perm: Permission::SharedReadWrite,
access: None,
protector: None,
}
}
}
fn protector(&self) -> Option<ProtectorKind> { fn protector(&self) -> Option<ProtectorKind> {
match self { match self {
NewPermission::Uniform { protector, .. } => *protector, NewPermission::Uniform { protector, .. } => *protector,
@ -916,12 +940,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
fn visit_box(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> { fn visit_box(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
// Boxes get a weak protectors, since they may be deallocated. // Boxes get a weak protectors, since they may be deallocated.
let new_perm = NewPermission::Uniform { let new_perm = NewPermission::from_box_ty(place.layout.ty, self.kind, self.ecx);
perm: Permission::Unique,
access: Some(AccessKind::Write),
protector: (self.kind == RetagKind::FnEntry)
.then_some(ProtectorKind::WeakProtector),
};
self.retag_ptr_inplace(place, new_perm, self.retag_cause) self.retag_ptr_inplace(place, new_perm, self.retag_cause)
} }

View file

@ -1,16 +0,0 @@
//@error-pattern: /deallocating while item \[SharedReadWrite for .*\] is strongly protected/
use std::marker::PhantomPinned;
pub struct NotUnpin(i32, PhantomPinned);
fn inner(x: &mut NotUnpin, f: fn(&mut NotUnpin)) {
// `f` may mutate, but it may not deallocate!
f(x)
}
fn main() {
inner(Box::leak(Box::new(NotUnpin(0, PhantomPinned))), |x| {
let raw = x as *mut _;
drop(unsafe { Box::from_raw(raw) });
});
}

View file

@ -1,38 +0,0 @@
error: Undefined Behavior: deallocating while item [SharedReadWrite for <TAG>] is strongly protected by call ID
--> RUSTLIB/alloc/src/alloc.rs:LL:CC
|
LL | unsafe { __rust_dealloc(ptr, layout.size(), layout.align()) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ deallocating while item [SharedReadWrite for <TAG>] is strongly protected by call ID
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
= note: BACKTRACE:
= note: inside `std::alloc::dealloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
= note: inside `<std::alloc::Global as std::alloc::Allocator>::deallocate` at RUSTLIB/alloc/src/alloc.rs:LL:CC
= note: inside `alloc::alloc::box_free::<NotUnpin, std::alloc::Global>` at RUSTLIB/alloc/src/alloc.rs:LL:CC
= note: inside `std::ptr::drop_in_place::<std::boxed::Box<NotUnpin>> - shim(Some(std::boxed::Box<NotUnpin>))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC
= note: inside `std::mem::drop::<std::boxed::Box<NotUnpin>>` at RUSTLIB/core/src/mem/mod.rs:LL:CC
note: inside closure
--> $DIR/deallocate_against_protector2.rs:LL:CC
|
LL | drop(unsafe { Box::from_raw(raw) });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: inside `<[closure@$DIR/deallocate_against_protector2.rs:LL:CC] as std::ops::FnOnce<(&mut NotUnpin,)>>::call_once - shim` at RUSTLIB/core/src/ops/function.rs:LL:CC
note: inside `inner`
--> $DIR/deallocate_against_protector2.rs:LL:CC
|
LL | f(x)
| ^^^^
note: inside `main`
--> $DIR/deallocate_against_protector2.rs:LL:CC
|
LL | / inner(Box::leak(Box::new(NotUnpin(0, PhantomPinned))), |x| {
LL | | let raw = x as *mut _;
LL | | drop(unsafe { Box::from_raw(raw) });
LL | | });
| |______^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to previous error

View file

@ -26,6 +26,19 @@ impl Future for Delay {
} }
} }
fn mk_waker() -> Waker {
use std::sync::Arc;
struct MyWaker;
impl Wake for MyWaker {
fn wake(self: Arc<Self>) {
unimplemented!()
}
}
Waker::from(Arc::new(MyWaker))
}
async fn do_stuff() { async fn do_stuff() {
(&mut Delay::new(1)).await; (&mut Delay::new(1)).await;
} }
@ -73,16 +86,7 @@ impl Future for DoStuff {
} }
fn run_fut<T>(fut: impl Future<Output = T>) -> T { fn run_fut<T>(fut: impl Future<Output = T>) -> T {
use std::sync::Arc; let waker = mk_waker();
struct MyWaker;
impl Wake for MyWaker {
fn wake(self: Arc<Self>) {
unimplemented!()
}
}
let waker = Waker::from(Arc::new(MyWaker));
let mut context = Context::from_waker(&waker); let mut context = Context::from_waker(&waker);
let mut pinned = pin!(fut); let mut pinned = pin!(fut);
@ -94,7 +98,37 @@ fn run_fut<T>(fut: impl Future<Output = T>) -> T {
} }
} }
fn self_referential_box() {
let waker = mk_waker();
let cx = &mut Context::from_waker(&waker);
async fn my_fut() -> i32 {
let val = 10;
let val_ref = &val;
let _ = Delay::new(1).await;
*val_ref
}
fn box_poll<F: Future>(
mut f: Pin<Box<F>>,
cx: &mut Context<'_>,
) -> (Pin<Box<F>>, Poll<F::Output>) {
let p = f.as_mut().poll(cx);
(f, p)
}
let my_fut = Box::pin(my_fut());
let (my_fut, p1) = box_poll(my_fut, cx);
assert!(p1.is_pending());
let (my_fut, p2) = box_poll(my_fut, cx);
assert!(p2.is_ready());
drop(my_fut);
}
fn main() { fn main() {
run_fut(do_stuff()); run_fut(do_stuff());
run_fut(DoStuff::new()); run_fut(DoStuff::new());
self_referential_box();
} }

View file

@ -19,6 +19,7 @@ fn main() {
array_casts(); array_casts();
mut_below_shr(); mut_below_shr();
wide_raw_ptr_in_tuple(); wide_raw_ptr_in_tuple();
not_unpin_not_protected();
} }
// Make sure that reading from an `&mut` does, like reborrowing to `&`, // Make sure that reading from an `&mut` does, like reborrowing to `&`,
@ -219,3 +220,22 @@ fn wide_raw_ptr_in_tuple() {
// Make sure the fn ptr part of the vtable is still fine. // Make sure the fn ptr part of the vtable is still fine.
r.type_id(); r.type_id();
} }
fn not_unpin_not_protected() {
// `&mut !Unpin`, at least for now, does not get `noalias` nor `dereferenceable`, so we also
// don't add protectors. (We could, but until we have a better idea for where we want to go with
// the self-referntial-generator situation, it does not seem worth the potential trouble.)
use std::marker::PhantomPinned;
pub struct NotUnpin(i32, PhantomPinned);
fn inner(x: &mut NotUnpin, f: fn(&mut NotUnpin)) {
// `f` may mutate, but it may not deallocate!
f(x)
}
inner(Box::leak(Box::new(NotUnpin(0, PhantomPinned))), |x| {
let raw = x as *mut _;
drop(unsafe { Box::from_raw(raw) });
});
}

View file

@ -29,6 +29,12 @@ pub fn borrow(x: &i32) -> &i32 {
x x
} }
// CHECK: align 4 {{i32\*|ptr}} @borrow_mut({{i32\*|ptr}} align 4 %x)
#[no_mangle]
pub fn borrow_mut(x: &mut i32) -> &mut i32 {
x
}
// CHECK-LABEL: @borrow_call // CHECK-LABEL: @borrow_call
#[no_mangle] #[no_mangle]
pub fn borrow_call(x: &i32, f: fn(&i32) -> &i32) -> &i32 { pub fn borrow_call(x: &i32, f: fn(&i32) -> &i32) -> &i32 {

View file

@ -85,6 +85,12 @@ pub fn option_nonzero_int(x: Option<NonZeroU64>) -> Option<NonZeroU64> {
pub fn readonly_borrow(_: &i32) { pub fn readonly_borrow(_: &i32) {
} }
// CHECK: noundef align 4 dereferenceable(4) {{i32\*|ptr}} @readonly_borrow_ret()
#[no_mangle]
pub fn readonly_borrow_ret() -> &'static i32 {
loop {}
}
// CHECK: @static_borrow({{i32\*|ptr}} noalias noundef readonly align 4 dereferenceable(4) %_1) // CHECK: @static_borrow({{i32\*|ptr}} noalias noundef readonly align 4 dereferenceable(4) %_1)
// static borrow may be captured // static borrow may be captured
#[no_mangle] #[no_mangle]
@ -115,9 +121,17 @@ pub fn mutable_unsafe_borrow(_: &mut UnsafeInner) {
pub fn mutable_borrow(_: &mut i32) { pub fn mutable_borrow(_: &mut i32) {
} }
// CHECK: noundef align 4 dereferenceable(4) {{i32\*|ptr}} @mutable_borrow_ret()
#[no_mangle] #[no_mangle]
// CHECK: @mutable_notunpin_borrow({{i32\*|ptr}} noundef align 4 dereferenceable(4) %_1) pub fn mutable_borrow_ret() -> &'static mut i32 {
loop {}
}
#[no_mangle]
// CHECK: @mutable_notunpin_borrow({{i32\*|ptr}} noundef nonnull align 4 %_1)
// This one is *not* `noalias` because it might be self-referential. // This one is *not* `noalias` because it might be self-referential.
// It is also not `dereferenceable` due to
// <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>.
pub fn mutable_notunpin_borrow(_: &mut NotUnpin) { pub fn mutable_notunpin_borrow(_: &mut NotUnpin) {
} }
@ -167,6 +181,12 @@ pub fn _box(x: Box<i32>) -> Box<i32> {
x x
} }
// CHECK: noundef nonnull align 4 {{i32\*|ptr}} @notunpin_box({{i32\*|ptr}} noundef nonnull align 4 %x)
#[no_mangle]
pub fn notunpin_box(x: Box<NotUnpin>) -> Box<NotUnpin> {
x
}
// CHECK: @struct_return({{%S\*|ptr}} noalias nocapture noundef sret(%S) dereferenceable(32){{( %0)?}}) // CHECK: @struct_return({{%S\*|ptr}} noalias nocapture noundef sret(%S) dereferenceable(32){{( %0)?}})
#[no_mangle] #[no_mangle]
pub fn struct_return() -> S { pub fn struct_return() -> S {
@ -233,12 +253,12 @@ pub fn trait_raw(_: *const dyn Drop) {
// CHECK: @trait_box({{\{\}\*|ptr}} noalias noundef nonnull align 1{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}}) // CHECK: @trait_box({{\{\}\*|ptr}} noalias noundef nonnull align 1{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
#[no_mangle] #[no_mangle]
pub fn trait_box(_: Box<dyn Drop>) { pub fn trait_box(_: Box<dyn Drop + Unpin>) {
} }
// CHECK: { {{i8\*|ptr}}, {{i8\*|ptr}} } @trait_option({{i8\*|ptr}} noalias noundef align 1 %x.0, {{i8\*|ptr}} %x.1) // CHECK: { {{i8\*|ptr}}, {{i8\*|ptr}} } @trait_option({{i8\*|ptr}} noalias noundef align 1 %x.0, {{i8\*|ptr}} %x.1)
#[no_mangle] #[no_mangle]
pub fn trait_option(x: Option<Box<dyn Drop>>) -> Option<Box<dyn Drop>> { pub fn trait_option(x: Option<Box<dyn Drop + Unpin>>) -> Option<Box<dyn Drop + Unpin>> {
x x
} }