mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-24 12:54:00 +00:00
Rollup merge of #92274 - woppopo:const_deallocate, r=oli-obk
Add `intrinsics::const_deallocate` Tracking issue: #79597 Related: #91884 This allows deallocation of a memory allocated by `intrinsics::const_allocate`. At the moment, this can be only used to reduce memory usage, but in the future this may be useful to detect memory leaks (If an allocated memory remains after evaluation, raise an error...?).
This commit is contained in:
commit
9e86a434a7
@ -369,6 +369,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
}
|
||||
}
|
||||
|
||||
sym::const_allocate => {
|
||||
// returns a null pointer at runtime.
|
||||
bx.const_null(bx.type_i8p())
|
||||
}
|
||||
|
||||
sym::const_deallocate => {
|
||||
// nop at runtime.
|
||||
return;
|
||||
}
|
||||
|
||||
// This requires that atomic intrinsics follow a specific naming pattern:
|
||||
// "atomic_<operation>[_<ordering>]", and no ordering means SeqCst
|
||||
name if name_str.starts_with("atomic_") => {
|
||||
|
@ -347,6 +347,33 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
||||
)?;
|
||||
ecx.write_pointer(ptr, dest)?;
|
||||
}
|
||||
sym::const_deallocate => {
|
||||
let ptr = ecx.read_pointer(&args[0])?;
|
||||
let size = ecx.read_scalar(&args[1])?.to_machine_usize(ecx)?;
|
||||
let align = ecx.read_scalar(&args[2])?.to_machine_usize(ecx)?;
|
||||
|
||||
let size = Size::from_bytes(size);
|
||||
let align = match Align::from_bytes(align) {
|
||||
Ok(a) => a,
|
||||
Err(err) => throw_ub_format!("align has to be a power of 2, {}", err),
|
||||
};
|
||||
|
||||
// If an allocation is created in an another const,
|
||||
// we don't deallocate it.
|
||||
let (alloc_id, _, _) = ecx.memory.ptr_get_alloc(ptr)?;
|
||||
let is_allocated_in_another_const = matches!(
|
||||
ecx.tcx.get_global_alloc(alloc_id),
|
||||
Some(interpret::GlobalAlloc::Memory(_))
|
||||
);
|
||||
|
||||
if !is_allocated_in_another_const {
|
||||
ecx.memory.deallocate(
|
||||
ptr,
|
||||
Some((size, align)),
|
||||
interpret::MemoryKind::Machine(MemoryKind::Heap),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(ConstEvalErrKind::NeedsRfc(format!(
|
||||
"calling intrinsic `{}`",
|
||||
|
@ -461,6 +461,7 @@ symbols! {
|
||||
const_async_blocks,
|
||||
const_compare_raw_pointers,
|
||||
const_constructor,
|
||||
const_deallocate,
|
||||
const_eval_limit,
|
||||
const_eval_select,
|
||||
const_eval_select_ct,
|
||||
|
@ -297,6 +297,11 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
|
||||
sym::const_allocate => {
|
||||
(0, vec![tcx.types.usize, tcx.types.usize], tcx.mk_mut_ptr(tcx.types.u8))
|
||||
}
|
||||
sym::const_deallocate => (
|
||||
0,
|
||||
vec![tcx.mk_mut_ptr(tcx.types.u8), tcx.types.usize, tcx.types.usize],
|
||||
tcx.mk_unit(),
|
||||
),
|
||||
|
||||
sym::ptr_offset_from => {
|
||||
(1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.isize)
|
||||
|
@ -1914,10 +1914,31 @@ extern "rust-intrinsic" {
|
||||
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
|
||||
pub fn ptr_guaranteed_ne<T>(ptr: *const T, other: *const T) -> bool;
|
||||
|
||||
/// Allocate at compile time. Should not be called at runtime.
|
||||
/// Allocates a block of memory at compile time.
|
||||
/// At runtime, just returns a null pointer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The `align` argument must be a power of two.
|
||||
/// - At compile time, a compile error occurs if this constraint is violated.
|
||||
/// - At runtime, it is not checked.
|
||||
#[rustc_const_unstable(feature = "const_heap", issue = "79597")]
|
||||
pub fn const_allocate(size: usize, align: usize) -> *mut u8;
|
||||
|
||||
/// Deallocates a memory which allocated by `intrinsics::const_allocate` at compile time.
|
||||
/// At runtime, does nothing.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The `align` argument must be a power of two.
|
||||
/// - At compile time, a compile error occurs if this constraint is violated.
|
||||
/// - At runtime, it is not checked.
|
||||
/// - If the `ptr` is created in an another const, this intrinsic doesn't deallocate it.
|
||||
/// - If the `ptr` is pointing to a local variable, this intrinsic doesn't deallocate it.
|
||||
#[rustc_const_unstable(feature = "const_heap", issue = "79597")]
|
||||
#[cfg(not(bootstrap))]
|
||||
pub fn const_deallocate(ptr: *mut u8, size: usize, align: usize);
|
||||
|
||||
/// Determines whether the raw bytes of the two values are equal.
|
||||
///
|
||||
/// This is particularly handy for arrays, since it allows things like just
|
||||
|
@ -80,3 +80,25 @@ fn test_hints_in_const_contexts() {
|
||||
assert!(42u32 == core::hint::black_box(42u32));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(bootstrap))]
|
||||
#[test]
|
||||
fn test_const_allocate_at_runtime() {
|
||||
use core::intrinsics::const_allocate;
|
||||
unsafe {
|
||||
assert!(const_allocate(4, 4).is_null());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(bootstrap))]
|
||||
#[test]
|
||||
fn test_const_deallocate_at_runtime() {
|
||||
use core::intrinsics::const_deallocate;
|
||||
const X: &u32 = &42u32;
|
||||
let x = &0u32;
|
||||
unsafe {
|
||||
const_deallocate(X as *const _ as *mut u8, 4, 4); // nop
|
||||
const_deallocate(x as *const _ as *mut u8, 4, 4); // nop
|
||||
const_deallocate(core::ptr::null_mut(), 1, 1); // nop
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
#![feature(const_bool_to_option)]
|
||||
#![feature(const_cell_into_inner)]
|
||||
#![feature(const_convert)]
|
||||
#![feature(const_heap)]
|
||||
#![feature(const_maybe_uninit_as_mut_ptr)]
|
||||
#![feature(const_maybe_uninit_assume_init)]
|
||||
#![feature(const_maybe_uninit_assume_init_read)]
|
||||
|
@ -6,11 +6,10 @@ use std::intrinsics;
|
||||
const FOO: i32 = foo();
|
||||
const fn foo() -> i32 {
|
||||
unsafe {
|
||||
let _ = intrinsics::const_allocate(4, 3) as * mut i32;
|
||||
let _ = intrinsics::const_allocate(4, 3) as *mut i32;
|
||||
//~^ error: evaluation of constant value failed
|
||||
}
|
||||
1
|
||||
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -4,7 +4,7 @@ error[E0080]: evaluation of constant value failed
|
||||
LL | const FOO: i32 = foo();
|
||||
| ----- inside `FOO` at $DIR/alloc_intrinsic_errors.rs:6:18
|
||||
...
|
||||
LL | let _ = intrinsics::const_allocate(4, 3) as * mut i32;
|
||||
LL | let _ = intrinsics::const_allocate(4, 3) as *mut i32;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| align has to be a power of 2, `3` is not a power of 2
|
||||
|
@ -0,0 +1,16 @@
|
||||
// run-pass
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(const_heap)]
|
||||
#![feature(inline_const)]
|
||||
|
||||
use std::intrinsics;
|
||||
|
||||
struct ZST;
|
||||
|
||||
fn main() {
|
||||
const {
|
||||
unsafe {
|
||||
let _ = intrinsics::const_allocate(0, 0) as *mut ZST;
|
||||
}
|
||||
}
|
||||
}
|
36
src/test/ui/consts/const-eval/heap/dealloc_intrinsic.rs
Normal file
36
src/test/ui/consts/const-eval/heap/dealloc_intrinsic.rs
Normal file
@ -0,0 +1,36 @@
|
||||
// run-pass
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(const_heap)]
|
||||
#![feature(const_mut_refs)]
|
||||
|
||||
use std::intrinsics;
|
||||
|
||||
const _X: () = unsafe {
|
||||
let ptr = intrinsics::const_allocate(4, 4);
|
||||
intrinsics::const_deallocate(ptr, 4, 4);
|
||||
};
|
||||
|
||||
const Y: &u32 = unsafe {
|
||||
let ptr = intrinsics::const_allocate(4, 4) as *mut u32;
|
||||
*ptr = 42;
|
||||
&*ptr
|
||||
};
|
||||
|
||||
const Z: &u32 = &42;
|
||||
|
||||
const _Z: () = unsafe {
|
||||
let ptr1 = Y as *const _ as *mut u8;
|
||||
intrinsics::const_deallocate(ptr1, 4, 4); // nop
|
||||
intrinsics::const_deallocate(ptr1, 2, 4); // nop
|
||||
intrinsics::const_deallocate(ptr1, 4, 2); // nop
|
||||
|
||||
let ptr2 = Z as *const _ as *mut u8;
|
||||
intrinsics::const_deallocate(ptr2, 4, 4); // nop
|
||||
intrinsics::const_deallocate(ptr2, 2, 4); // nop
|
||||
intrinsics::const_deallocate(ptr2, 4, 2); // nop
|
||||
};
|
||||
|
||||
fn main() {
|
||||
assert_eq!(*Y, 42);
|
||||
assert_eq!(*Z, 42);
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(const_heap)]
|
||||
#![feature(const_mut_refs)]
|
||||
|
||||
use std::intrinsics;
|
||||
|
||||
const _X: &'static u8 = unsafe {
|
||||
let ptr = intrinsics::const_allocate(4, 4);
|
||||
intrinsics::const_deallocate(ptr, 4, 4);
|
||||
&*ptr
|
||||
//~^ error: evaluation of constant value failed
|
||||
};
|
||||
|
||||
const _Y: u8 = unsafe {
|
||||
let ptr = intrinsics::const_allocate(4, 4);
|
||||
let reference = &*ptr;
|
||||
intrinsics::const_deallocate(ptr, 4, 4);
|
||||
*reference
|
||||
//~^ error: evaluation of constant value failed
|
||||
};
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,15 @@
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/dealloc_intrinsic_dangling.rs:10:5
|
||||
|
|
||||
LL | &*ptr
|
||||
| ^^^^^ pointer to alloc2 was dereferenced after this allocation got freed
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/dealloc_intrinsic_dangling.rs:18:5
|
||||
|
|
||||
LL | *reference
|
||||
| ^^^^^^^^^^ pointer to alloc4 was dereferenced after this allocation got freed
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0080`.
|
@ -0,0 +1,13 @@
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(const_heap)]
|
||||
|
||||
use std::intrinsics;
|
||||
|
||||
const _X: () = unsafe {
|
||||
let ptr = intrinsics::const_allocate(4, 4);
|
||||
intrinsics::const_deallocate(ptr, 4, 4);
|
||||
intrinsics::const_deallocate(ptr, 4, 4);
|
||||
//~^ error: evaluation of constant value failed
|
||||
};
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,9 @@
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/dealloc_intrinsic_duplicate.rs:9:5
|
||||
|
|
||||
LL | intrinsics::const_deallocate(ptr, 4, 4);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pointer to alloc2 was dereferenced after this allocation got freed
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0080`.
|
@ -0,0 +1,29 @@
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(const_heap)]
|
||||
|
||||
use std::intrinsics;
|
||||
|
||||
const _X: () = unsafe {
|
||||
let ptr = intrinsics::const_allocate(4, 4);
|
||||
intrinsics::const_deallocate(ptr, 4, 2);
|
||||
//~^ error: evaluation of constant value failed
|
||||
};
|
||||
const _Y: () = unsafe {
|
||||
let ptr = intrinsics::const_allocate(4, 4);
|
||||
intrinsics::const_deallocate(ptr, 2, 4);
|
||||
//~^ error: evaluation of constant value failed
|
||||
};
|
||||
|
||||
const _Z: () = unsafe {
|
||||
let ptr = intrinsics::const_allocate(4, 4);
|
||||
intrinsics::const_deallocate(ptr, 3, 4);
|
||||
//~^ error: evaluation of constant value failed
|
||||
};
|
||||
|
||||
const _W: () = unsafe {
|
||||
let ptr = intrinsics::const_allocate(4, 4);
|
||||
intrinsics::const_deallocate(ptr, 4, 3);
|
||||
//~^ error: evaluation of constant value failed
|
||||
};
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,27 @@
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/dealloc_intrinsic_incorrect_layout.rs:8:5
|
||||
|
|
||||
LL | intrinsics::const_deallocate(ptr, 4, 2);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect layout on deallocation: alloc2 has size 4 and alignment 4, but gave size 4 and alignment 2
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/dealloc_intrinsic_incorrect_layout.rs:13:5
|
||||
|
|
||||
LL | intrinsics::const_deallocate(ptr, 2, 4);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect layout on deallocation: alloc4 has size 4 and alignment 4, but gave size 2 and alignment 4
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/dealloc_intrinsic_incorrect_layout.rs:19:5
|
||||
|
|
||||
LL | intrinsics::const_deallocate(ptr, 3, 4);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect layout on deallocation: alloc6 has size 4 and alignment 4, but gave size 3 and alignment 4
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/dealloc_intrinsic_incorrect_layout.rs:25:5
|
||||
|
|
||||
LL | intrinsics::const_deallocate(ptr, 4, 3);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ align has to be a power of 2, `3` is not a power of 2
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0080`.
|
@ -0,0 +1,17 @@
|
||||
// run-pass
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(const_heap)]
|
||||
#![feature(inline_const)]
|
||||
|
||||
use std::intrinsics;
|
||||
|
||||
fn main() {
|
||||
const {
|
||||
unsafe {
|
||||
let ptr1 = intrinsics::const_allocate(0, 0);
|
||||
let ptr2 = intrinsics::const_allocate(0, 0);
|
||||
intrinsics::const_deallocate(ptr1, 0, 0);
|
||||
intrinsics::const_deallocate(ptr2, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user