Skip to content

Commit addf931

Browse files
committed
move Option::as_slice to intrinsic
1 parent 8645296 commit addf931

File tree

2 files changed

+42
-72
lines changed

2 files changed

+42
-72
lines changed

core/src/intrinsics.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2214,6 +2214,12 @@ extern "rust-intrinsic" {
22142214
where
22152215
G: FnOnce<ARG, Output = RET>,
22162216
F: FnOnce<ARG, Output = RET>;
2217+
2218+
#[cfg(not(bootstrap))]
2219+
/// This method creates a pointer to any `Some` value. If the argument is
2220+
/// `None`, an invalid within-bounds pointer (that is still acceptable for
2221+
/// constructing an empty slice) is returned.
2222+
pub fn option_payload_ptr<T>(arg: *const Option<T>) -> *const T;
22172223
}
22182224

22192225
// Some functions are defined here because they accidentally got made

core/src/option.rs

Lines changed: 36 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,7 @@ use crate::{
559559
/// The `Option` type. See [the module level documentation](self) for more.
560560
#[derive(Copy, PartialOrd, Eq, Ord, Debug, Hash)]
561561
#[rustc_diagnostic_item = "Option"]
562+
#[cfg_attr(not(bootstrap), lang = "Option")]
562563
#[stable(feature = "rust1", since = "1.0.0")]
563564
pub enum Option<T> {
564565
/// No value.
@@ -735,48 +736,6 @@ impl<T> Option<T> {
735736
}
736737
}
737738

738-
/// This is a guess at how many bytes into the option the payload can be found.
739-
///
740-
/// For niche-optimized types it's correct because it's pigeon-holed to only
741-
/// one possible place. For other types, it's usually correct today, but
742-
/// tweaks to the layout algorithm (particularly expansions of
743-
/// `-Z randomize-layout`) might make it incorrect at any point.
744-
///
745-
/// It's guaranteed to be a multiple of alignment (so will always give a
746-
/// correctly-aligned location) and to be within the allocated object, so
747-
/// is valid to use with `offset` and to use for a zero-sized read.
748-
///
749-
/// FIXME: This is a horrible hack, but allows a nice optimization. It should
750-
/// be replaced with `offset_of!` once that works on enum variants.
751-
const SOME_BYTE_OFFSET_GUESS: isize = {
752-
let some_uninit = Some(mem::MaybeUninit::<T>::uninit());
753-
let payload_ref = some_uninit.as_ref().unwrap();
754-
// SAFETY: `as_ref` gives an address inside the existing `Option`,
755-
// so both pointers are derived from the same thing and the result
756-
// cannot overflow an `isize`.
757-
let offset = unsafe { <*const _>::byte_offset_from(payload_ref, &some_uninit) };
758-
759-
// The offset is into the object, so it's guaranteed to be non-negative.
760-
assert!(offset >= 0);
761-
762-
// The payload and the overall option are aligned,
763-
// so the offset will be a multiple of the alignment too.
764-
assert!((offset as usize) % mem::align_of::<T>() == 0);
765-
766-
let max_offset = mem::size_of::<Self>() - mem::size_of::<T>();
767-
if offset as usize <= max_offset {
768-
// There's enough space after this offset for a `T` to exist without
769-
// overflowing the bounds of the object, so let's try it.
770-
offset
771-
} else {
772-
// The offset guess is definitely wrong, so use the address
773-
// of the original option since we have it already.
774-
// This also correctly handles the case of layout-optimized enums
775-
// where `max_offset == 0` and thus this is the only possibility.
776-
0
777-
}
778-
};
779-
780739
/// Returns a slice of the contained value, if any. If this is `None`, an
781740
/// empty slice is returned. This can be useful to have a single type of
782741
/// iterator over an `Option` or slice.
@@ -809,28 +768,29 @@ impl<T> Option<T> {
809768
#[must_use]
810769
#[unstable(feature = "option_as_slice", issue = "108545")]
811770
pub fn as_slice(&self) -> &[T] {
812-
let payload_ptr: *const T =
813-
// The goal here is that both arms here are calculating exactly
814-
// the same pointer, and thus it'll be folded away when the guessed
815-
// offset is correct, but if the guess is wrong for some reason
816-
// it'll at least still be sound, just no longer optimal.
817-
if let Some(payload) = self {
818-
payload
819-
} else {
820-
let self_ptr: *const Self = self;
821-
// SAFETY: `SOME_BYTE_OFFSET_GUESS` guarantees that its value is
822-
// such that this will be in-bounds of the object.
823-
unsafe { self_ptr.byte_offset(Self::SOME_BYTE_OFFSET_GUESS).cast() }
824-
};
825-
let len = usize::from(self.is_some());
771+
#[cfg(bootstrap)]
772+
match self {
773+
Some(value) => slice::from_ref(value),
774+
None => &[],
775+
}
826776

777+
#[cfg(not(bootstrap))]
827778
// SAFETY: When the `Option` is `Some`, we're using the actual pointer
828779
// to the payload, with a length of 1, so this is equivalent to
829780
// `slice::from_ref`, and thus is safe.
830781
// When the `Option` is `None`, the length used is 0, so to be safe it
831782
// just needs to be aligned, which it is because `&self` is aligned and
832783
// the offset used is a multiple of alignment.
833-
unsafe { slice::from_raw_parts(payload_ptr, len) }
784+
//
785+
// In the new version, the intrinsic always returns a pointer to an
786+
// in-bounds and correctly aligned position for a `T` (even if in the
787+
// `None` case it's just padding).
788+
unsafe {
789+
slice::from_raw_parts(
790+
crate::intrinsics::option_payload_ptr(crate::ptr::from_ref(self)),
791+
usize::from(self.is_some()),
792+
)
793+
}
834794
}
835795

836796
/// Returns a mutable slice of the contained value, if any. If this is
@@ -875,28 +835,32 @@ impl<T> Option<T> {
875835
#[must_use]
876836
#[unstable(feature = "option_as_slice", issue = "108545")]
877837
pub fn as_mut_slice(&mut self) -> &mut [T] {
878-
let payload_ptr: *mut T =
879-
// The goal here is that both arms here are calculating exactly
880-
// the same pointer, and thus it'll be folded away when the guessed
881-
// offset is correct, but if the guess is wrong for some reason
882-
// it'll at least still be sound, just no longer optimal.
883-
if let Some(payload) = self {
884-
payload
885-
} else {
886-
let self_ptr: *mut Self = self;
887-
// SAFETY: `SOME_BYTE_OFFSET_GUESS` guarantees that its value is
888-
// such that this will be in-bounds of the object.
889-
unsafe { self_ptr.byte_offset(Self::SOME_BYTE_OFFSET_GUESS).cast() }
890-
};
891-
let len = usize::from(self.is_some());
838+
#[cfg(bootstrap)]
839+
match self {
840+
Some(value) => slice::from_mut(value),
841+
None => &mut [],
842+
}
892843

844+
#[cfg(not(bootstrap))]
893845
// SAFETY: When the `Option` is `Some`, we're using the actual pointer
894846
// to the payload, with a length of 1, so this is equivalent to
895847
// `slice::from_mut`, and thus is safe.
896848
// When the `Option` is `None`, the length used is 0, so to be safe it
897849
// just needs to be aligned, which it is because `&self` is aligned and
898850
// the offset used is a multiple of alignment.
899-
unsafe { slice::from_raw_parts_mut(payload_ptr, len) }
851+
//
852+
// In the new version, the intrinsic creates a `*const T` from a
853+
// mutable reference so it is safe to cast back to a mutable pointer
854+
// here. As with `as_slice`, the intrinsic always returns a pointer to
855+
// an in-bounds and correctly aligned position for a `T` (even if in
856+
// the `None` case it's just padding).
857+
unsafe {
858+
slice::from_raw_parts_mut(
859+
crate::intrinsics::option_payload_ptr(crate::ptr::from_mut(self).cast_const())
860+
.cast_mut(),
861+
usize::from(self.is_some()),
862+
)
863+
}
900864
}
901865

902866
/////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)