Skip to content

Commit d1e81ef

Browse files
committed
Auto merge of #69079 - CAD97:layout-of-ptr, r=RalfJung
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.
2 parents 1902d1e + dd973d1 commit d1e81ef

File tree

4 files changed

+142
-8
lines changed

4 files changed

+142
-8
lines changed

src/libcore/alloc.rs

+36
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,42 @@ impl Layout {
140140
unsafe { Layout::from_size_align_unchecked(size, align) }
141141
}
142142

143+
/// Produces layout describing a record that could be used to
144+
/// allocate backing structure for `T` (which could be a trait
145+
/// or other unsized type like a slice).
146+
///
147+
/// # Safety
148+
///
149+
/// This function is only safe to call if the following conditions hold:
150+
///
151+
/// - If `T` is `Sized`, this function is always safe to call.
152+
/// - If the unsized tail of `T` is:
153+
/// - a [slice], then the length of the slice tail must be an intialized
154+
/// integer, and the size of the *entire value*
155+
/// (dynamic tail length + statically sized prefix) must fit in `isize`.
156+
/// - a [trait object], then the vtable part of the pointer must point
157+
/// to a valid vtable acquired by an unsizing coersion, and the size
158+
/// of the *entire value* (dynamic tail length + statically sized prefix)
159+
/// must fit in `isize`.
160+
/// - an (unstable) [extern type], then this function is always safe to
161+
/// call, but may panic or otherwise return the wrong value, as the
162+
/// extern type's layout is not known. This is the same behavior as
163+
/// [`Layout::for_value`] on a reference to an extern type tail.
164+
/// - otherwise, it is conservatively not allowed to call this function.
165+
///
166+
/// [slice]: ../../std/primitive.slice.html
167+
/// [trait object]: ../../book/ch17-02-trait-objects.html
168+
/// [extern type]: ../../unstable-book/language-features/extern-types.html
169+
#[inline]
170+
#[cfg(not(bootstrap))]
171+
#[unstable(feature = "layout_for_ptr", issue = "69835")]
172+
pub unsafe fn for_value_raw<T: ?Sized>(t: *const T) -> Self {
173+
let (size, align) = (mem::size_of_val_raw(t), mem::align_of_val_raw(t));
174+
// See rationale in `new` for why this is using an unsafe variant below
175+
debug_assert!(Layout::from_size_align(size, align).is_ok());
176+
Layout::from_size_align_unchecked(size, align)
177+
}
178+
143179
/// Creates a `NonNull` that is dangling, but well-aligned for this Layout.
144180
///
145181
/// Note that the pointer value may potentially represent a valid pointer,

src/libcore/intrinsics.rs

+11
Original file line numberDiff line numberDiff line change
@@ -980,13 +980,24 @@ extern "rust-intrinsic" {
980980
///
981981
/// The stabilized version of this intrinsic is
982982
/// [`std::mem::size_of_val`](../../std/mem/fn.size_of_val.html).
983+
#[cfg(bootstrap)]
983984
pub fn size_of_val<T: ?Sized>(_: &T) -> usize;
984985
/// The minimum alignment of the type of the value that `val` points to.
985986
///
986987
/// The stabilized version of this intrinsic is
987988
/// [`std::mem::min_align_of_val`](../../std/mem/fn.min_align_of_val.html).
989+
#[cfg(bootstrap)]
988990
pub fn min_align_of_val<T: ?Sized>(_: &T) -> usize;
989991

992+
/// The size of the referenced value in bytes.
993+
///
994+
/// The stabilized version of this intrinsic is
995+
/// [`std::mem::size_of_val`](../../std/mem/fn.size_of_val.html).
996+
#[cfg(not(bootstrap))]
997+
pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
998+
#[cfg(not(bootstrap))]
999+
pub fn min_align_of_val<T: ?Sized>(_: *const T) -> usize;
1000+
9901001
/// Gets a static string slice containing the name of a type.
9911002
///
9921003
/// The stabilized version of this intrinsic is

src/libcore/mem/mod.rs

+92
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,54 @@ pub fn size_of_val<T: ?Sized>(val: &T) -> usize {
336336
intrinsics::size_of_val(val)
337337
}
338338

339+
/// Returns the size of the pointed-to value in bytes.
340+
///
341+
/// This is usually the same as `size_of::<T>()`. However, when `T` *has* no
342+
/// statically-known size, e.g., a slice [`[T]`][slice] or a [trait object],
343+
/// then `size_of_val_raw` can be used to get the dynamically-known size.
344+
///
345+
/// # Safety
346+
///
347+
/// This function is only safe to call if the following conditions hold:
348+
///
349+
/// - If `T` is `Sized`, this function is always safe to call.
350+
/// - If the unsized tail of `T` is:
351+
/// - a [slice], then the length of the slice tail must be an intialized
352+
/// integer, and the size of the *entire value*
353+
/// (dynamic tail length + statically sized prefix) must fit in `isize`.
354+
/// - a [trait object], then the vtable part of the pointer must point
355+
/// to a valid vtable acquired by an unsizing coersion, and the size
356+
/// of the *entire value* (dynamic tail length + statically sized prefix)
357+
/// must fit in `isize`.
358+
/// - an (unstable) [extern type], then this function is always safe to
359+
/// call, but may panic or otherwise return the wrong value, as the
360+
/// extern type's layout is not known. This is the same behavior as
361+
/// [`size_of_val`] on a reference to an extern type tail.
362+
/// - otherwise, it is conservatively not allowed to call this function.
363+
///
364+
/// [slice]: ../../std/primitive.slice.html
365+
/// [trait object]: ../../book/ch17-02-trait-objects.html
366+
/// [extern type]: ../../unstable-book/language-features/extern-types.html
367+
///
368+
/// # Examples
369+
///
370+
/// ```
371+
/// #![feature(layout_for_ptr)]
372+
/// use std::mem;
373+
///
374+
/// assert_eq!(4, mem::size_of_val(&5i32));
375+
///
376+
/// let x: [u8; 13] = [0; 13];
377+
/// let y: &[u8] = &x;
378+
/// assert_eq!(13, unsafe { mem::size_of_val_raw(y) });
379+
/// ```
380+
#[inline]
381+
#[cfg(not(bootstrap))]
382+
#[unstable(feature = "layout_for_ptr", issue = "69835")]
383+
pub unsafe fn size_of_val_raw<T: ?Sized>(val: *const T) -> usize {
384+
intrinsics::size_of_val(val)
385+
}
386+
339387
/// Returns the [ABI]-required minimum alignment of a type.
340388
///
341389
/// Every reference to a value of the type `T` must be a multiple of this number.
@@ -423,6 +471,50 @@ pub fn align_of_val<T: ?Sized>(val: &T) -> usize {
423471
min_align_of_val(val)
424472
}
425473

474+
/// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to.
475+
///
476+
/// Every reference to a value of the type `T` must be a multiple of this number.
477+
///
478+
/// [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface
479+
///
480+
/// # Safety
481+
///
482+
/// This function is only safe to call if the following conditions hold:
483+
///
484+
/// - If `T` is `Sized`, this function is always safe to call.
485+
/// - If the unsized tail of `T` is:
486+
/// - a [slice], then the length of the slice tail must be an intialized
487+
/// integer, and the size of the *entire value*
488+
/// (dynamic tail length + statically sized prefix) must fit in `isize`.
489+
/// - a [trait object], then the vtable part of the pointer must point
490+
/// to a valid vtable acquired by an unsizing coersion, and the size
491+
/// of the *entire value* (dynamic tail length + statically sized prefix)
492+
/// must fit in `isize`.
493+
/// - an (unstable) [extern type], then this function is always safe to
494+
/// call, but may panic or otherwise return the wrong value, as the
495+
/// extern type's layout is not known. This is the same behavior as
496+
/// [`align_of_val`] on a reference to an extern type tail.
497+
/// - otherwise, it is conservatively not allowed to call this function.
498+
///
499+
/// [slice]: ../../std/primitive.slice.html
500+
/// [trait object]: ../../book/ch17-02-trait-objects.html
501+
/// [extern type]: ../../unstable-book/language-features/extern-types.html
502+
///
503+
/// # Examples
504+
///
505+
/// ```
506+
/// #![feature(layout_for_ptr)]
507+
/// use std::mem;
508+
///
509+
/// assert_eq!(4, unsafe { mem::align_of_val_raw(&5i32) });
510+
/// ```
511+
#[inline]
512+
#[cfg(not(bootstrap))]
513+
#[unstable(feature = "layout_for_ptr", issue = "69835")]
514+
pub unsafe fn align_of_val_raw<T: ?Sized>(val: *const T) -> usize {
515+
intrinsics::min_align_of_val(val)
516+
}
517+
426518
/// Returns `true` if dropping values of type `T` matters.
427519
///
428520
/// This is purely an optimization hint, and may be implemented conservatively:

src/librustc_typeck/check/intrinsic.rs

+3-8
Original file line numberDiff line numberDiff line change
@@ -137,14 +137,9 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
137137
let (n_tps, inputs, output) = match &name[..] {
138138
"breakpoint" => (0, Vec::new(), tcx.mk_unit()),
139139
"size_of" | "pref_align_of" | "min_align_of" => (1, Vec::new(), tcx.types.usize),
140-
"size_of_val" | "min_align_of_val" => (
141-
1,
142-
vec![tcx.mk_imm_ref(
143-
tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(0))),
144-
param(0),
145-
)],
146-
tcx.types.usize,
147-
),
140+
"size_of_val" | "min_align_of_val" => {
141+
(1, vec![tcx.mk_imm_ptr(param(0))], tcx.types.usize)
142+
}
148143
"rustc_peek" => (1, vec![param(0)], param(0)),
149144
"caller_location" => (0, vec![], tcx.caller_location_ty()),
150145
"assert_inhabited" | "assert_zero_valid" | "assert_uninit_valid" => {

0 commit comments

Comments
 (0)