From 991a296ce7617443c021c164644e2791890849bc Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 28 Oct 2021 17:15:25 +0300 Subject: [PATCH 1/3] Make `core::slice::from_raw_parts[_mut]` const --- library/core/src/lib.rs | 1 + library/core/src/slice/raw.rs | 55 ++++++++++++++++++++++++++--------- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 36496193d0370..da537262f6bd5 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -105,6 +105,7 @@ #![feature(const_caller_location)] #![feature(const_cell_into_inner)] #![feature(const_discriminant)] +#![cfg_attr(not(bootstrap), feature(const_eval_select))] #![feature(const_float_bits_conv)] #![feature(const_float_classify)] #![feature(const_fmt_arguments_new)] diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs index ad38aaf9f8300..6653c998fa2ba 100644 --- a/library/core/src/slice/raw.rs +++ b/library/core/src/slice/raw.rs @@ -1,8 +1,6 @@ //! Free functions to create `&[T]` and `&mut [T]`. use crate::array; -use crate::intrinsics::is_aligned_and_not_null; -use crate::mem; use crate::ptr; /// Forms a slice from a pointer and a length. @@ -85,12 +83,10 @@ use crate::ptr; /// [`NonNull::dangling()`]: ptr::NonNull::dangling #[inline] #[stable(feature = "rust1", since = "1.0.0")] -pub unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] { - debug_assert!(is_aligned_and_not_null(data), "attempt to create unaligned or null slice"); - debug_assert!( - mem::size_of::().saturating_mul(len) <= isize::MAX as usize, - "attempt to create slice covering at least half the address space" - ); +#[rustc_const_unstable(feature = "const_slice_from_raw_parts", issue = "none")] +pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] { + debug_check_data_len(data, len); + // SAFETY: the caller must uphold the safety contract for `from_raw_parts`. unsafe { &*ptr::slice_from_raw_parts(data, len) } } @@ -126,16 +122,47 @@ pub unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] { /// [`NonNull::dangling()`]: ptr::NonNull::dangling #[inline] #[stable(feature = "rust1", since = "1.0.0")] -pub unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] { - debug_assert!(is_aligned_and_not_null(data), "attempt to create unaligned or null slice"); - debug_assert!( - mem::size_of::().saturating_mul(len) <= isize::MAX as usize, - "attempt to create slice covering at least half the address space" - ); +#[rustc_const_unstable(feature = "const_slice_from_raw_parts", issue = "none")] +pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] { + debug_check_data_len(data as _, len); + // SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`. unsafe { &mut *ptr::slice_from_raw_parts_mut(data, len) } } +// In debug builds checks that `data` pointer is aligned and non-null and that slice with given `len` would cover less than half the address space +#[cfg(all(not(bootstrap), debug_assertions))] +#[unstable(feature = "const_slice_from_raw_parts", issue = "none")] +#[rustc_const_unstable(feature = "const_slice_from_raw_parts", issue = "none")] +const fn debug_check_data_len(data: *const T, len: usize) { + fn rt_check(data: *const T) { + use crate::intrinsics::is_aligned_and_not_null; + + assert!(is_aligned_and_not_null(data), "attempt to create unaligned or null slice"); + } + + const fn ctfe_check(_data: *const T) { + // It's impossible to check alignment in const fn. + // + // CTFE engine checks that the pointer is aligned and not null. + } + + // SAFETY: + // - `calling from_raw_parts[_mut]` with arguments that fail to fulfil checks made here is UB, so unless UB is already triggered this is noop + // - CTFE makes the same checks as `rt_check`, so behavior change is not observable due to compilation error + unsafe { + crate::intrinsics::const_eval_select((data,), ctfe_check, rt_check); + } + + assert!( + crate::mem::size_of::().saturating_mul(len) <= isize::MAX as usize, + "attempt to create slice covering at least half the address space" + ); +} + +#[cfg(not(all(not(bootstrap), debug_assertions)))] +const fn debug_check_data_len(_data: *const T, _len: usize) {} + /// Converts a reference to T into a slice of length 1 (without copying). #[stable(feature = "from_ref", since = "1.28.0")] #[rustc_const_unstable(feature = "const_slice_from_ref", issue = "90206")] From 878ac10fe13761664da2d205c64e2a398f469601 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 29 Oct 2021 22:45:10 +0300 Subject: [PATCH 2/3] Use proper issue number for `feature(const_slice_from_raw_parts)` --- library/core/src/slice/raw.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs index 6653c998fa2ba..edae555206fbe 100644 --- a/library/core/src/slice/raw.rs +++ b/library/core/src/slice/raw.rs @@ -83,7 +83,7 @@ use crate::ptr; /// [`NonNull::dangling()`]: ptr::NonNull::dangling #[inline] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_slice_from_raw_parts", issue = "none")] +#[rustc_const_unstable(feature = "const_slice_from_raw_parts", issue = "67456")] pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] { debug_check_data_len(data, len); @@ -122,7 +122,7 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] /// [`NonNull::dangling()`]: ptr::NonNull::dangling #[inline] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_slice_from_raw_parts", issue = "none")] +#[rustc_const_unstable(feature = "const_slice_from_raw_parts", issue = "67456")] pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] { debug_check_data_len(data as _, len); @@ -132,8 +132,8 @@ pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a m // In debug builds checks that `data` pointer is aligned and non-null and that slice with given `len` would cover less than half the address space #[cfg(all(not(bootstrap), debug_assertions))] -#[unstable(feature = "const_slice_from_raw_parts", issue = "none")] -#[rustc_const_unstable(feature = "const_slice_from_raw_parts", issue = "none")] +#[unstable(feature = "const_slice_from_raw_parts", issue = "67456")] +#[rustc_const_unstable(feature = "const_slice_from_raw_parts", issue = "67456")] const fn debug_check_data_len(data: *const T, len: usize) { fn rt_check(data: *const T) { use crate::intrinsics::is_aligned_and_not_null; From afaa54a99d75bc6287d835b1ee33e1b15bda462e Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 29 Oct 2021 23:45:09 +0300 Subject: [PATCH 3/3] Apply changes proposed in the review --- library/core/src/slice/raw.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs index edae555206fbe..81bb16d54015e 100644 --- a/library/core/src/slice/raw.rs +++ b/library/core/src/slice/raw.rs @@ -141,17 +141,18 @@ const fn debug_check_data_len(data: *const T, len: usize) { assert!(is_aligned_and_not_null(data), "attempt to create unaligned or null slice"); } - const fn ctfe_check(_data: *const T) { - // It's impossible to check alignment in const fn. - // - // CTFE engine checks that the pointer is aligned and not null. - } + const fn noop(_: *const T) {} // SAFETY: - // - `calling from_raw_parts[_mut]` with arguments that fail to fulfil checks made here is UB, so unless UB is already triggered this is noop - // - CTFE makes the same checks as `rt_check`, so behavior change is not observable due to compilation error + // + // `rt_check` is just a debug assert to hint users that they are causing UB, + // it is not required for safety (the safety must be guatanteed by + // the `from_raw_parts[_mut]` caller). + // + // Since the checks are not required, we ignore them in CTFE as they can't + // be done there (alignment does not make much sense there). unsafe { - crate::intrinsics::const_eval_select((data,), ctfe_check, rt_check); + crate::intrinsics::const_eval_select((data,), noop, rt_check); } assert!(