Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ptr::read_move #113066

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_hir_analysis/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
sym::unlikely => (0, vec![tcx.types.bool], tcx.types.bool),

sym::read_via_copy => (1, vec![tcx.mk_imm_ptr(param(0))], param(0)),
sym::read_via_move => (1, vec![tcx.mk_imm_ptr(param(0))], param(0)),
sym::write_via_move => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()),

sym::discriminant_value => {
Expand Down
9 changes: 7 additions & 2 deletions compiler/rustc_mir_transform/src/lower_intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
terminator.kind = TerminatorKind::Goto { target };
}
}
sym::read_via_copy => {
sym::read_via_copy | sym::read_via_move => {
let [arg] = args.as_slice() else {
span_bug!(terminator.source_info.span, "Wrong number of arguments");
};
Expand All @@ -183,11 +183,16 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
TerminatorKind::Unreachable
}
Some(target) => {
let operand = if intrinsic_name == sym::read_via_copy {
Operand::Copy(derefed_place)
} else {
Operand::Move(derefed_place)
};
block.statements.push(Statement {
source_info: terminator.source_info,
kind: StatementKind::Assign(Box::new((
*destination,
Rvalue::Use(Operand::Copy(derefed_place)),
Rvalue::Use(operand),
))),
});
TerminatorKind::Goto { target }
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 @@ -1182,6 +1182,7 @@ symbols! {
read_struct,
read_struct_field,
read_via_copy,
read_via_move,
readonly,
realloc,
reason,
Expand Down
10 changes: 10 additions & 0 deletions library/core/src/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2272,6 +2272,16 @@ extern "rust-intrinsic" {
#[rustc_nounwind]
pub fn read_via_copy<T>(ptr: *const T) -> T;

/// This is an implementation detail of [`crate::ptr::read_move`] and should
/// not be used anywhere else.
///
/// This intrinsic can *only* be called where the pointer is a local without
/// projections (`read_via_move(ptr)`, not `read_via_move(*ptr)`) so that it
/// trivially obeys runtime-MIR rules about derefs in operands.
#[rustc_nounwind]
#[cfg(not(bootstrap))]
pub fn read_via_move<T>(ptr: *mut T) -> T;
newpavlov marked this conversation as resolved.
Show resolved Hide resolved

/// This is an implementation detail of [`crate::ptr::write`] and should
/// not be used anywhere else. See its comments for why this exists.
///
Expand Down
32 changes: 32 additions & 0 deletions library/core/src/ptr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1180,6 +1180,38 @@ pub const unsafe fn read<T>(src: *const T) -> T {
}
}

/// Reads the value from `src` by moving it.
///
/// # Safety
///
/// Behavior is undefined if any of the following conditions are violated:
///
/// * `src` must be [valid] for reads.
///
/// * `src` must be properly aligned.
///
/// * `src` must point to a properly initialized value of type `T`.
///
/// * `src` must not be read or written while the return value exists.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am ignorant about MIR move semantics.

Does it apply to T: Copy too? How does that interact with freeing the allocation from which the value has been read?

///
/// Note that even if `T` has size `0`, the pointer must be non-null and properly aligned.
///
/// [valid]: self#safety
#[inline]
#[rustc_const_unstable(feature = "read_move", issue = "none")]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[cfg(not(bootstrap))]
pub const unsafe fn read_move<T>(src: *mut T) -> T {
// SAFETY: the caller must guarantee that `src` is valid for reads.
unsafe {
assert_unsafe_precondition!(
"ptr::read_move requires that the pointer argument is aligned and non-null",
[T](src: *mut T) => is_aligned_and_not_null(src)
);
crate::intrinsics::read_via_move(src)
}
}

/// Reads the value from `src` without moving it. This leaves the
/// memory in `src` unchanged.
///
Expand Down