Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 35 additions & 21 deletions compiler/rustc_const_eval/src/interpret/validity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use std::borrow::Cow;
use std::fmt::Write;
use std::hash::Hash;
use std::mem;
use std::num::NonZero;

use either::{Left, Right};
Expand Down Expand Up @@ -288,6 +289,7 @@ struct ValidityVisitor<'rt, 'tcx, M: Machine<'tcx>> {
/// If this is `Some`, then `reset_provenance_and_padding` must be true (but not vice versa:
/// we might not track data vs padding bytes if the operand isn't stored in memory anyway).
data_bytes: Option<RangeSet>,
may_dangle: bool,
}

impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
Expand Down Expand Up @@ -503,27 +505,29 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
// alignment and size determined by the layout (size will be 0,
// alignment should take attributes into account).
.unwrap_or_else(|| (place.layout.size, place.layout.align.abi));
// Direct call to `check_ptr_access_align` checks alignment even on CTFE machines.
try_validation!(
self.ecx.check_ptr_access(
place.ptr(),
size,
CheckInAllocMsg::Dereferenceable, // will anyway be replaced by validity message
),
self.path,
Ub(DanglingIntPointer { addr: 0, .. }) => NullPtr { ptr_kind, maybe: false },
Ub(DanglingIntPointer { addr: i, .. }) => DanglingPtrNoProvenance {
ptr_kind,
// FIXME this says "null pointer" when null but we need translate
pointer: format!("{}", Pointer::<Option<AllocId>>::without_provenance(i))
},
Ub(PointerOutOfBounds { .. }) => DanglingPtrOutOfBounds {
ptr_kind
},
Ub(PointerUseAfterFree(..)) => DanglingPtrUseAfterFree {
ptr_kind,
},
);
if !self.may_dangle {
// Direct call to `check_ptr_access_align` checks alignment even on CTFE machines.
try_validation!(
self.ecx.check_ptr_access(
place.ptr(),
size,
CheckInAllocMsg::Dereferenceable, // will anyway be replaced by validity message
),
self.path,
Ub(DanglingIntPointer { addr: 0, .. }) => NullPtr { ptr_kind, maybe: false },
Ub(DanglingIntPointer { addr: i, .. }) => DanglingPtrNoProvenance {
ptr_kind,
// FIXME this says "null pointer" when null but we need translate
pointer: format!("{}", Pointer::<Option<AllocId>>::without_provenance(i))
},
Ub(PointerOutOfBounds { .. }) => DanglingPtrOutOfBounds {
ptr_kind
},
Ub(PointerUseAfterFree(..)) => DanglingPtrUseAfterFree {
ptr_kind,
},
);
}
try_validation!(
self.ecx.check_ptr_align(
place.ptr(),
Expand All @@ -536,6 +540,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
found_bytes: has.bytes()
},
);

// Make sure this is non-null. We checked dereferenceability above, but if `size` is zero
// that does not imply non-null.
let scalar = Scalar::from_maybe_pointer(place.ptr(), self.ecx);
Expand Down Expand Up @@ -1269,6 +1274,14 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
ty::PatternKind::Or(_patterns) => {}
}
}
ty::Adt(adt, _) if adt.is_maybe_dangling() => {
let could_dangle = mem::replace(&mut self.may_dangle, true);

let inner = self.ecx.project_field(val, FieldIdx::ZERO)?;
self.visit_value(&inner)?;

self.may_dangle = could_dangle;
}
_ => {
// default handler
try_validation!(
Expand Down Expand Up @@ -1354,6 +1367,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
ecx,
reset_provenance_and_padding,
data_bytes: reset_padding.then_some(RangeSet(Vec::new())),
may_dangle: false,
};
v.visit_value(val)?;
v.reset_padding(val)?;
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,8 @@ language_item_table! {

PhantomData, sym::phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1);

ManuallyDrop, sym::manually_drop, manually_drop, Target::Struct, GenericRequirement::None;
ManuallyDrop, sym::manually_drop, manually_drop, Target::Struct, GenericRequirement::Exact(1);
MaybeDangling, sym::maybe_dangling, maybe_dangling, Target::Struct, GenericRequirement::Exact(1);
BikeshedGuaranteedNoDrop, sym::bikeshed_guaranteed_no_drop, bikeshed_guaranteed_no_drop, Target::Trait, GenericRequirement::Exact(0);

MaybeUninit, sym::maybe_uninit, maybe_uninit, Target::Union, GenericRequirement::None;
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_middle/src/ty/adt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ bitflags::bitflags! {
const IS_PIN = 1 << 11;
/// Indicates whether the type is `#[pin_project]`.
const IS_PIN_PROJECT = 1 << 12;
/// Indicates whether the type is `MaybeDangling<_>`.
const IS_MAYBE_DANGLING = 1 << 13;
}
}
rustc_data_structures::external_bitflags_debug! { AdtFlags }
Expand Down Expand Up @@ -315,6 +317,9 @@ impl AdtDefData {
if tcx.is_lang_item(did, LangItem::ManuallyDrop) {
flags |= AdtFlags::IS_MANUALLY_DROP;
}
if tcx.is_lang_item(did, LangItem::MaybeDangling) {
flags |= AdtFlags::IS_MAYBE_DANGLING;
}
if tcx.is_lang_item(did, LangItem::UnsafeCell) {
flags |= AdtFlags::IS_UNSAFE_CELL;
}
Expand Down Expand Up @@ -439,6 +444,12 @@ impl<'tcx> AdtDef<'tcx> {
self.flags().contains(AdtFlags::IS_MANUALLY_DROP)
}

/// Returns `true` if this is `MaybeDangling<T>`.
#[inline]
pub fn is_maybe_dangling(self) -> bool {
self.flags().contains(AdtFlags::IS_MAYBE_DANGLING)
}

/// Returns `true` if this is `Pin<T>`.
#[inline]
pub fn is_pin(self) -> bool {
Expand Down
8 changes: 7 additions & 1 deletion compiler/rustc_middle/src/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,12 @@ where
})
}

ty::Adt(adt_def, ..) if adt_def.is_maybe_dangling() => {
// FIXME: what is the exact effect of maybe dangling?
Self::ty_and_layout_pointee_info_at(this.field(cx, 0), cx, offset)
.map(|info| PointeeInfo { safe: None, ..info })
}

_ => {
let mut data_variant = match &this.variants {
// Within the discriminant field, only the niche itself is
Expand Down Expand Up @@ -1091,7 +1097,7 @@ where
}
}
Variants::Multiple { .. } => None,
_ => Some(this),
Variants::Empty | Variants::Single { .. } => Some(this),
};

if let Some(variant) = data_variant
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1409,6 +1409,7 @@ symbols! {
maxnumf128,
may_dangle,
may_unwind,
maybe_dangling,
maybe_uninit,
maybe_uninit_uninit,
maybe_uninit_zeroed,
Expand Down
5 changes: 1 addition & 4 deletions library/core/src/mem/maybe_dangling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ use crate::{mem, ptr};

/// Allows wrapped [references] and [boxes] to dangle.
///
/// <section class="warning">
/// This type is not properly implemented yet, and the documentation below is thus not accurate.
/// </section>
///
/// That is, if a reference (or a `Box`) is wrapped in `MaybeDangling` (including when in a
/// (nested) field of a compound type wrapped in `MaybeDangling`), it does not have to follow
/// pointer aliasing rules or be dereferenceable.
Expand Down Expand Up @@ -72,6 +68,7 @@ use crate::{mem, ptr};
#[repr(transparent)]
#[rustc_pub_transparent]
#[derive(Debug, Copy, Clone, Default)]
#[lang = "maybe_dangling"]
pub struct MaybeDangling<P: ?Sized>(P);

impl<P: ?Sized> MaybeDangling<P> {
Expand Down
1 change: 1 addition & 0 deletions library/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@
#![feature(hint_must_use)]
#![feature(int_from_ascii)]
#![feature(ip)]
#![feature(maybe_dangling)]
#![feature(maybe_uninit_array_assume_init)]
#![feature(panic_can_unwind)]
#![feature(panic_internals)]
Expand Down
25 changes: 2 additions & 23 deletions library/std/src/thread/lifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use super::thread::Thread;
use super::{Result, spawnhook};
use crate::cell::UnsafeCell;
use crate::marker::PhantomData;
use crate::mem::{ManuallyDrop, MaybeUninit};
use crate::mem::MaybeDangling;
use crate::sync::Arc;
use crate::sync::atomic::{Atomic, AtomicUsize, Ordering};
use crate::sys::{AsInner, IntoInner, thread as imp};
Expand Down Expand Up @@ -57,29 +57,8 @@ where
Arc::new(Packet { scope: scope_data, result: UnsafeCell::new(None), _marker: PhantomData });
let their_packet = my_packet.clone();

// Pass `f` in `MaybeUninit` because actually that closure might *run longer than the lifetime of `F`*.
// Pass `f` in `MaybeDangling` because actually that closure might *run longer than the lifetime of `F`*.
// See <https://github.com/rust-lang/rust/issues/101983> for more details.
// To prevent leaks we use a wrapper that drops its contents.
#[repr(transparent)]
struct MaybeDangling<T>(MaybeUninit<T>);
impl<T> MaybeDangling<T> {
fn new(x: T) -> Self {
MaybeDangling(MaybeUninit::new(x))
}
fn into_inner(self) -> T {
// Make sure we don't drop.
let this = ManuallyDrop::new(self);
// SAFETY: we are always initialized.
unsafe { this.0.assume_init_read() }
}
}
impl<T> Drop for MaybeDangling<T> {
fn drop(&mut self) {
// SAFETY: we are always initialized.
unsafe { self.0.assume_init_drop() };
}
}

let f = MaybeDangling::new(f);

// The entrypoint of the Rust thread, after platform-specific thread
Expand Down
28 changes: 20 additions & 8 deletions src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -889,7 +889,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
RetagKind::FnEntry => RetagCause::FnEntry,
RetagKind::Default | RetagKind::Raw => RetagCause::Normal,
};
let mut visitor = RetagVisitor { ecx: this, kind, retag_cause, in_field: false };
let mut visitor =
RetagVisitor { ecx: this, kind, retag_cause, in_field: false, may_dangle: false };
return visitor.visit_value(place);

// The actual visitor.
Expand All @@ -898,6 +899,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
kind: RetagKind,
retag_cause: RetagCause,
in_field: bool,
may_dangle: bool,
}
impl<'ecx, 'tcx> RetagVisitor<'ecx, 'tcx> {
#[inline(always)] // yes this helps in our benchmarks
Expand All @@ -906,13 +908,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
place: &PlaceTy<'tcx>,
new_perm: NewPermission,
) -> InterpResult<'tcx> {
let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?;
let val = self.ecx.sb_retag_reference(
&val,
new_perm,
RetagInfo { cause: self.retag_cause, in_field: self.in_field },
)?;
self.ecx.write_immediate(*val, place)?;
if !self.may_dangle {
let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?;
let val = self.ecx.sb_retag_reference(
&val,
new_perm,
RetagInfo { cause: self.retag_cause, in_field: self.in_field },
)?;
self.ecx.write_immediate(*val, place)?;
}

interp_ok(())
}
}
Expand Down Expand Up @@ -960,6 +965,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// even if field retagging is not enabled. *shrug*)
self.walk_value(place)?;
}
ty::Adt(adt, _) if adt.is_maybe_dangling() => {
let in_field = mem::replace(&mut self.in_field, true); // remember and restore old value
let may_dangle = mem::replace(&mut self.may_dangle, true); // remember and restore old value
self.walk_value(place)?;
self.may_dangle = may_dangle;
self.in_field = in_field;
}
_ => {
// Not a reference/pointer/box. Recurse.
let in_field = mem::replace(&mut self.in_field, true); // remember and restore old value
Expand Down
7 changes: 7 additions & 0 deletions src/tools/miri/tests/pass/move_manually_drop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use std::mem::ManuallyDrop;

fn main() {
let mut x = ManuallyDrop::new(Box::new(1));
unsafe { ManuallyDrop::drop(&mut x) };
let _x = x; // move
}
22 changes: 22 additions & 0 deletions tests/codegen-llvm/manually_drop_refs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//@ compile-flags: -Copt-level=3 -Zmerge-functions=disabled
#![crate_type = "lib"]

use std::mem::ManuallyDrop;

// CHECK: define noundef nonnull ptr @f(ptr noundef nonnull readnone returned {{(captures\(ret: address, provenance\) )?}}%x) unnamed_addr
#[no_mangle]
pub fn f(x: ManuallyDrop<Box<u8>>) -> ManuallyDrop<Box<u8>> {
x
}

// CHECK: define noundef nonnull ptr @g(ptr noundef nonnull readnone returned {{(captures\(ret: address, provenance\) )?}}%x) unnamed_addr
#[no_mangle]
pub fn g(x: ManuallyDrop<&u8>) -> ManuallyDrop<&u8> {
x
}

// CHECK: define noundef nonnull ptr @h(ptr noundef nonnull readnone returned {{(captures\(ret: address, provenance\) )?}}%x) unnamed_addr
#[no_mangle]
pub fn h(x: ManuallyDrop<&mut u8>) -> ManuallyDrop<&mut u8> {
x
}
Loading