Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion compiler/rustc_middle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
#![feature(min_specialization)]
#![feature(negative_impls)]
#![feature(never_type)]
#![feature(ptr_alignment_type)]
#![feature(range_bounds_is_empty)]
#![feature(rustc_attrs)]
#![feature(sized_hierarchy)]
Expand Down
8 changes: 0 additions & 8 deletions compiler/rustc_middle/src/query/erase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,6 @@ impl<T> Erasable for &'_ [T] {
type Storage = [u8; size_of::<&'_ [()]>()];
}

impl<T> Erasable for &'_ ty::List<T> {
type Storage = [u8; size_of::<&'_ ty::List<()>>()];
}

impl<T> Erasable for &'_ ty::ListWithCachedTypeInfo<T> {
type Storage = [u8; size_of::<&'_ ty::ListWithCachedTypeInfo<()>>()];
}

impl<T> Erasable for Result<&'_ T, traits::query::NoSolution> {
type Storage = [u8; size_of::<Result<&'_ (), traits::query::NoSolution>>()];
}
Expand Down
71 changes: 29 additions & 42 deletions compiler/rustc_middle/src/ty/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::{fmt, iter, mem, ptr, slice};

use rustc_data_structures::aligned::{Aligned, align_of};
use rustc_data_structures::sync::DynSync;
use rustc_data_structures::aligned::align_of;
use rustc_serialize::{Encodable, Encoder};
use rustc_type_ir::FlagComputation;

Expand Down Expand Up @@ -36,14 +35,6 @@ pub type List<T> = RawList<(), T>;
/// [`Hash`] and [`Encodable`].
#[repr(C)]
pub struct RawList<H, T> {
skel: ListSkeleton<H, T>,
opaque: OpaqueListContents,
}

/// A [`RawList`] without the unsized tail. This type is used for layout computation
/// and constructing empty lists.
#[repr(C)]
struct ListSkeleton<H, T> {
header: H,
len: usize,
/// Although this claims to be a zero-length array, in practice `len`
Expand All @@ -57,16 +48,10 @@ impl<T> Default for &List<T> {
}
}

unsafe extern "C" {
/// A dummy type used to force `List` to be unsized while not requiring
/// references to it be wide pointers.
type OpaqueListContents;
}

impl<H, T> RawList<H, T> {
#[inline(always)]
pub fn len(&self) -> usize {
self.skel.len
self.len
}

#[inline(always)]
Expand Down Expand Up @@ -97,18 +82,18 @@ impl<H, T> RawList<H, T> {
assert!(!slice.is_empty());

let (layout, _offset) =
Layout::new::<ListSkeleton<H, T>>().extend(Layout::for_value::<[T]>(slice)).unwrap();
Layout::new::<RawList<H, T>>().extend(Layout::for_value::<[T]>(slice)).unwrap();

let mem = arena.dropless.alloc_raw(layout) as *mut RawList<H, T>;
unsafe {
// Write the header
(&raw mut (*mem).skel.header).write(header);
(&raw mut (*mem).header).write(header);

// Write the length
(&raw mut (*mem).skel.len).write(slice.len());
(&raw mut (*mem).len).write(slice.len());

// Write the elements
(&raw mut (*mem).skel.data)
(&raw mut (*mem).data)
.cast::<T>()
.copy_from_nonoverlapping(slice.as_ptr(), slice.len());

Expand Down Expand Up @@ -152,8 +137,8 @@ macro_rules! impl_list_empty {
#[repr(align(64))]
struct MaxAlign;

static EMPTY: ListSkeleton<$header_ty, MaxAlign> =
ListSkeleton { header: $header_init, len: 0, data: [] };
static EMPTY: RawList<$header_ty, MaxAlign> =
RawList { header: $header_init, len: 0, data: [] };

assert!(align_of::<T>() <= align_of::<MaxAlign>());

Expand Down Expand Up @@ -237,12 +222,12 @@ impl<H, T> Deref for RawList<H, T> {
impl<H, T> AsRef<[T]> for RawList<H, T> {
#[inline(always)]
fn as_ref(&self) -> &[T] {
let data_ptr = (&raw const self.skel.data).cast::<T>();
let data_ptr = (&raw const self.data).cast::<T>();
// SAFETY: `data_ptr` has the same provenance as `self` and can therefore
// access the `self.skel.len` elements stored at `self.skel.data`.
// Note that we specifically don't reborrow `&self.skel.data`, because that
// access the `self.len` elements stored at `self.data`.
// Note that we specifically don't reborrow `&self.data`, because that
// would give us a pointer with provenance over 0 bytes.
Comment on lines 226 to 229
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this provenance still work when the compiler thinks it does know the size? i.e. &self might have more limited range than when it was unsized.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know much about provenance, so I don't even know how a change here would manifest in practice.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It depends on the model. Stacked borrows rejects this, but tree borrows accepts it. It's safer to avoid this pattern - you can create a OpaqueListRef<'a> that contains a *const List and PhantomData<&'a List> to replace the reference and stay in raw pointer land where this is definitely okay.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For cross-reference, I also brought up this question on zulip. At the very least, with or without this change, we should add a comment mentioning rust-lang/unsafe-code-guidelines#256.

unsafe { slice::from_raw_parts(data_ptr, self.skel.len) }
unsafe { slice::from_raw_parts(data_ptr, self.len) }
}
}

Expand All @@ -257,32 +242,19 @@ impl<'a, H, T: Copy> IntoIterator for &'a RawList<H, T> {

unsafe impl<H: Sync, T: Sync> Sync for RawList<H, T> {}

// We need this since `List` uses extern type `OpaqueListContents`.
unsafe impl<H: DynSync, T: DynSync> DynSync for RawList<H, T> {}

// Safety:
// Layouts of `ListSkeleton<H, T>` and `RawList<H, T>` are the same, modulo opaque tail,
// thus aligns of `ListSkeleton<H, T>` and `RawList<H, T>` must be the same.
unsafe impl<H, T> Aligned for RawList<H, T> {
#[cfg(bootstrap)]
const ALIGN: ptr::Alignment = align_of::<ListSkeleton<H, T>>();
#[cfg(not(bootstrap))]
const ALIGN: mem::Alignment = align_of::<ListSkeleton<H, T>>();
}

/// A [`List`] that additionally stores type information inline to speed up
/// [`TypeVisitableExt`](super::TypeVisitableExt) operations.
pub type ListWithCachedTypeInfo<T> = RawList<TypeInfo, T>;

impl<T> ListWithCachedTypeInfo<T> {
#[inline(always)]
pub fn flags(&self) -> TypeFlags {
self.skel.header.flags
self.header.flags
}

#[inline(always)]
pub fn outer_exclusive_binder(&self) -> DebruijnIndex {
self.skel.header.outer_exclusive_binder
self.header.outer_exclusive_binder
}
}

Expand Down Expand Up @@ -310,3 +282,18 @@ impl<'tcx> From<FlagComputation<TyCtxt<'tcx>>> for TypeInfo {
}
}
}

#[cfg(target_pointer_width = "64")]
mod size_asserts {
use rustc_data_structures::static_assert_size;

use super::*;
// tidy-alphabetical-start
static_assert_size!(&List<u32>, 8);
static_assert_size!(&RawList<u8, u32>, 8);
static_assert_size!(List<u8>, 8);
static_assert_size!(List<u32>, 8);
static_assert_size!(RawList<u8, u32>, 16);
static_assert_size!(RawList<usize, u32>, 16);
// tidy-alphabetical-end
}
Loading