Skip to content

Commit

Permalink
Auto merge of #69079 - CAD97:layout-of-ptr, r=RalfJung
Browse files Browse the repository at this point in the history
Allow calculating the layout behind a pointer

There was some discussion around allowing this previously.

This does make the requirement for raw pointers to have valid metadata exposed as part of the std API (as a safety invariant, not validity invariant), though I think this is not strictly necessarily required as of current. cc @rust-lang/wg-unsafe-code-guidelines

Naming is hard; I picked the best "obvious" name I could come up with.

If it's agreed that this is actually a desired API surface, I'll file a tracking issue and update the attributes.
  • Loading branch information
bors committed Mar 22, 2020
2 parents 1902d1e + dd973d1 commit d1e81ef
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 8 deletions.
36 changes: 36 additions & 0 deletions src/libcore/alloc.rs
Expand Up @@ -140,6 +140,42 @@ impl Layout {
unsafe { Layout::from_size_align_unchecked(size, align) }
}

/// Produces layout describing a record that could be used to
/// allocate backing structure for `T` (which could be a trait
/// or other unsized type like a slice).
///
/// # Safety
///
/// This function is only safe to call if the following conditions hold:
///
/// - If `T` is `Sized`, this function is always safe to call.
/// - If the unsized tail of `T` is:
/// - a [slice], then the length of the slice tail must be an intialized
/// integer, and the size of the *entire value*
/// (dynamic tail length + statically sized prefix) must fit in `isize`.
/// - a [trait object], then the vtable part of the pointer must point
/// to a valid vtable acquired by an unsizing coersion, and the size
/// of the *entire value* (dynamic tail length + statically sized prefix)
/// must fit in `isize`.
/// - an (unstable) [extern type], then this function is always safe to
/// call, but may panic or otherwise return the wrong value, as the
/// extern type's layout is not known. This is the same behavior as
/// [`Layout::for_value`] on a reference to an extern type tail.
/// - otherwise, it is conservatively not allowed to call this function.
///
/// [slice]: ../../std/primitive.slice.html
/// [trait object]: ../../book/ch17-02-trait-objects.html
/// [extern type]: ../../unstable-book/language-features/extern-types.html
#[inline]
#[cfg(not(bootstrap))]
#[unstable(feature = "layout_for_ptr", issue = "69835")]
pub unsafe fn for_value_raw<T: ?Sized>(t: *const T) -> Self {
let (size, align) = (mem::size_of_val_raw(t), mem::align_of_val_raw(t));
// See rationale in `new` for why this is using an unsafe variant below
debug_assert!(Layout::from_size_align(size, align).is_ok());
Layout::from_size_align_unchecked(size, align)
}

/// Creates a `NonNull` that is dangling, but well-aligned for this Layout.
///
/// Note that the pointer value may potentially represent a valid pointer,
Expand Down
11 changes: 11 additions & 0 deletions src/libcore/intrinsics.rs
Expand Up @@ -980,13 +980,24 @@ extern "rust-intrinsic" {
///
/// The stabilized version of this intrinsic is
/// [`std::mem::size_of_val`](../../std/mem/fn.size_of_val.html).
#[cfg(bootstrap)]
pub fn size_of_val<T: ?Sized>(_: &T) -> usize;
/// The minimum alignment of the type of the value that `val` points to.
///
/// The stabilized version of this intrinsic is
/// [`std::mem::min_align_of_val`](../../std/mem/fn.min_align_of_val.html).
#[cfg(bootstrap)]
pub fn min_align_of_val<T: ?Sized>(_: &T) -> usize;

/// The size of the referenced value in bytes.
///
/// The stabilized version of this intrinsic is
/// [`std::mem::size_of_val`](../../std/mem/fn.size_of_val.html).
#[cfg(not(bootstrap))]
pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
#[cfg(not(bootstrap))]
pub fn min_align_of_val<T: ?Sized>(_: *const T) -> usize;

/// Gets a static string slice containing the name of a type.
///
/// The stabilized version of this intrinsic is
Expand Down
92 changes: 92 additions & 0 deletions src/libcore/mem/mod.rs
Expand Up @@ -336,6 +336,54 @@ pub fn size_of_val<T: ?Sized>(val: &T) -> usize {
intrinsics::size_of_val(val)
}

/// Returns the size of the pointed-to value in bytes.
///
/// This is usually the same as `size_of::<T>()`. However, when `T` *has* no
/// statically-known size, e.g., a slice [`[T]`][slice] or a [trait object],
/// then `size_of_val_raw` can be used to get the dynamically-known size.
///
/// # Safety
///
/// This function is only safe to call if the following conditions hold:
///
/// - If `T` is `Sized`, this function is always safe to call.
/// - If the unsized tail of `T` is:
/// - a [slice], then the length of the slice tail must be an intialized
/// integer, and the size of the *entire value*
/// (dynamic tail length + statically sized prefix) must fit in `isize`.
/// - a [trait object], then the vtable part of the pointer must point
/// to a valid vtable acquired by an unsizing coersion, and the size
/// of the *entire value* (dynamic tail length + statically sized prefix)
/// must fit in `isize`.
/// - an (unstable) [extern type], then this function is always safe to
/// call, but may panic or otherwise return the wrong value, as the
/// extern type's layout is not known. This is the same behavior as
/// [`size_of_val`] on a reference to an extern type tail.
/// - otherwise, it is conservatively not allowed to call this function.
///
/// [slice]: ../../std/primitive.slice.html
/// [trait object]: ../../book/ch17-02-trait-objects.html
/// [extern type]: ../../unstable-book/language-features/extern-types.html
///
/// # Examples
///
/// ```
/// #![feature(layout_for_ptr)]
/// use std::mem;
///
/// assert_eq!(4, mem::size_of_val(&5i32));
///
/// let x: [u8; 13] = [0; 13];
/// let y: &[u8] = &x;
/// assert_eq!(13, unsafe { mem::size_of_val_raw(y) });
/// ```
#[inline]
#[cfg(not(bootstrap))]
#[unstable(feature = "layout_for_ptr", issue = "69835")]
pub unsafe fn size_of_val_raw<T: ?Sized>(val: *const T) -> usize {
intrinsics::size_of_val(val)
}

/// Returns the [ABI]-required minimum alignment of a type.
///
/// Every reference to a value of the type `T` must be a multiple of this number.
Expand Down Expand Up @@ -423,6 +471,50 @@ pub fn align_of_val<T: ?Sized>(val: &T) -> usize {
min_align_of_val(val)
}

/// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to.
///
/// Every reference to a value of the type `T` must be a multiple of this number.
///
/// [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface
///
/// # Safety
///
/// This function is only safe to call if the following conditions hold:
///
/// - If `T` is `Sized`, this function is always safe to call.
/// - If the unsized tail of `T` is:
/// - a [slice], then the length of the slice tail must be an intialized
/// integer, and the size of the *entire value*
/// (dynamic tail length + statically sized prefix) must fit in `isize`.
/// - a [trait object], then the vtable part of the pointer must point
/// to a valid vtable acquired by an unsizing coersion, and the size
/// of the *entire value* (dynamic tail length + statically sized prefix)
/// must fit in `isize`.
/// - an (unstable) [extern type], then this function is always safe to
/// call, but may panic or otherwise return the wrong value, as the
/// extern type's layout is not known. This is the same behavior as
/// [`align_of_val`] on a reference to an extern type tail.
/// - otherwise, it is conservatively not allowed to call this function.
///
/// [slice]: ../../std/primitive.slice.html
/// [trait object]: ../../book/ch17-02-trait-objects.html
/// [extern type]: ../../unstable-book/language-features/extern-types.html
///
/// # Examples
///
/// ```
/// #![feature(layout_for_ptr)]
/// use std::mem;
///
/// assert_eq!(4, unsafe { mem::align_of_val_raw(&5i32) });
/// ```
#[inline]
#[cfg(not(bootstrap))]
#[unstable(feature = "layout_for_ptr", issue = "69835")]
pub unsafe fn align_of_val_raw<T: ?Sized>(val: *const T) -> usize {
intrinsics::min_align_of_val(val)
}

/// Returns `true` if dropping values of type `T` matters.
///
/// This is purely an optimization hint, and may be implemented conservatively:
Expand Down
11 changes: 3 additions & 8 deletions src/librustc_typeck/check/intrinsic.rs
Expand Up @@ -137,14 +137,9 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
let (n_tps, inputs, output) = match &name[..] {
"breakpoint" => (0, Vec::new(), tcx.mk_unit()),
"size_of" | "pref_align_of" | "min_align_of" => (1, Vec::new(), tcx.types.usize),
"size_of_val" | "min_align_of_val" => (
1,
vec![tcx.mk_imm_ref(
tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(0))),
param(0),
)],
tcx.types.usize,
),
"size_of_val" | "min_align_of_val" => {
(1, vec![tcx.mk_imm_ptr(param(0))], tcx.types.usize)
}
"rustc_peek" => (1, vec![param(0)], param(0)),
"caller_location" => (0, vec![], tcx.caller_location_ty()),
"assert_inhabited" | "assert_zero_valid" | "assert_uninit_valid" => {
Expand Down

0 comments on commit d1e81ef

Please sign in to comment.