Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rollup of 7 pull requests #91727

Closed
wants to merge 14 commits into from
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
53 changes: 29 additions & 24 deletions compiler/rustc_data_structures/src/functor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,38 +34,43 @@ impl<T> IdFunctor for Vec<T> {
type Inner = T;

#[inline]
fn try_map_id<F, E>(mut self, mut f: F) -> Result<Self, E>
fn try_map_id<F, E>(self, mut f: F) -> Result<Self, E>
where
F: FnMut(Self::Inner) -> Result<Self::Inner, E>,
{
// FIXME: We don't really care about panics here and leak
// far more than we should, but that should be fine for now.
let len = self.len();
unsafe {
self.set_len(0);
let start = self.as_mut_ptr();
for i in 0..len {
let p = start.add(i);
match f(p.read()) {
Ok(val) => p.write(val),
Err(err) => {
// drop all other elements in self
// (current element was "moved" into the call to f)
for j in (0..i).chain(i + 1..len) {
start.add(j).drop_in_place();
}
struct HoleVec<T> {
vec: Vec<mem::ManuallyDrop<T>>,
hole: Option<usize>,
}

// returning will drop self, releasing the allocation
// (len is 0 so elements will not be re-dropped)
return Err(err);
impl<T> Drop for HoleVec<T> {
fn drop(&mut self) {
unsafe {
for (index, slot) in self.vec.iter_mut().enumerate() {
if self.hole != Some(index) {
mem::ManuallyDrop::drop(slot);
}
}
}
}
// Even if we encountered an error, set the len back
// so we don't leak memory.
self.set_len(len);
}
Ok(self)

unsafe {
let (ptr, length, capacity) = self.into_raw_parts();
let vec = Vec::from_raw_parts(ptr.cast(), length, capacity);
let mut hole_vec = HoleVec { vec, hole: None };

for (index, slot) in hole_vec.vec.iter_mut().enumerate() {
hole_vec.hole = Some(index);
let original = mem::ManuallyDrop::take(slot);
let mapped = f(original)?;
*slot = mem::ManuallyDrop::new(mapped);
hole_vec.hole = None;
}

mem::forget(hole_vec);
Ok(Vec::from_raw_parts(ptr, length, capacity))
}
}
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_data_structures/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#![feature(once_cell)]
#![feature(test)]
#![feature(thread_id_value)]
#![feature(vec_into_raw_parts)]
#![allow(rustc::default_hash_types)]
#![deny(unaligned_references)]

Expand Down
140 changes: 89 additions & 51 deletions compiler/rustc_middle/src/ty/list.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use crate::arena::Arena;

use rustc_serialize::{Encodable, Encoder};

use std::alloc::Layout;
use std::cmp::Ordering;
use std::fmt;
Expand All @@ -12,49 +10,69 @@ use std::ops::Deref;
use std::ptr;
use std::slice;

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

/// A wrapper for slices with the additional invariant
/// that the slice is interned and no other slice with
/// the same contents can exist in the same context.
/// This means we can use pointer for both
/// equality comparisons and hashing.
///
/// Unlike slices, the types contained in `List` are expected to be `Copy`
/// and iterating over a `List` returns `T` instead of a reference.
///
/// Note: `Slice` was already taken by the `Ty`.
/// `List<T>` is a bit like `&[T]`, but with some critical differences.
/// - IMPORTANT: Every `List<T>` is *required* to have unique contents. The
/// type's correctness relies on this, *but it does not enforce it*.
/// Therefore, any code that creates a `List<T>` must ensure uniqueness
/// itself. In practice this is achieved by interning.
/// - The length is stored within the `List<T>`, so `&List<Ty>` is a thin
/// pointer.
/// - Because of this, you cannot get a `List<T>` that is a sub-list of another
/// `List<T>`. You can get a sub-slice `&[T]`, however.
/// - `List<T>` can be used with `CopyTaggedPtr`, which is useful within
/// structs whose size must be minimized.
/// - Because of the uniqueness assumption, we can use the address of a
/// `List<T>` for faster equality comparisons and hashing.
/// - `T` must be `Copy`. This lets `List<T>` be stored in a dropless arena and
/// iterators return a `T` rather than a `&T`.
/// - `T` must not be zero-sized.
#[repr(C)]
pub struct List<T> {
len: usize,

/// Although this claims to be a zero-length array, in practice `len`
/// elements are actually present.
data: [T; 0],

opaque: OpaqueListContents,
}

unsafe impl<'a, T: 'a> rustc_data_structures::tagged_ptr::Pointer for &'a List<T> {
const BITS: usize = std::mem::align_of::<usize>().trailing_zeros() as usize;
#[inline]
fn into_usize(self) -> usize {
self as *const List<T> as usize
}
#[inline]
unsafe fn from_usize(ptr: usize) -> Self {
&*(ptr as *const List<T>)
}
unsafe fn with_ref<R, F: FnOnce(&Self) -> R>(ptr: usize, f: F) -> R {
// Self: Copy so this is fine
let ptr = Self::from_usize(ptr);
f(&ptr)
}
extern "C" {
/// A dummy type used to force `List` to be unsized while not requiring
/// references to it be wide pointers.
type OpaqueListContents;
}

unsafe impl<T: Sync> Sync for List<T> {}
impl<T> List<T> {
/// Returns a reference to the (unique, static) empty list.
#[inline(always)]
pub fn empty<'a>() -> &'a List<T> {
#[repr(align(64))]
struct MaxAlign;

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

#[repr(C)]
struct InOrder<T, U>(T, U);

// The empty slice is static and contains a single `0` usize (for the
// length) that is 64-byte aligned, thus featuring the necessary
// trailing padding for elements with up to 64-byte alignment.
static EMPTY_SLICE: InOrder<usize, MaxAlign> = InOrder(0, MaxAlign);
unsafe { &*(&EMPTY_SLICE as *const _ as *const List<T>) }
}
}

impl<T: Copy> List<T> {
/// Allocates a list from `arena` and copies the contents of `slice` into it.
///
/// WARNING: the contents *must be unique*, such that no list with these
/// contents has been previously created. If not, operations such as `eq`
/// and `hash` might give incorrect results.
///
/// Panics if `T` is `Drop`, or `T` is zero-sized, or the slice is empty
/// (because the empty list exists statically, and is available via
/// `empty()`).
#[inline]
pub(super) fn from_arena<'tcx>(arena: &'tcx Arena<'tcx>, slice: &[T]) -> &'tcx List<T> {
assert!(!mem::needs_drop::<T>());
Expand All @@ -73,7 +91,7 @@ impl<T: Copy> List<T> {
.cast::<T>()
.copy_from_nonoverlapping(slice.as_ptr(), slice.len());

&mut *mem
&*mem
}
}

Expand Down Expand Up @@ -107,11 +125,24 @@ impl<S: Encoder, T: Encodable<S>> Encodable<S> for &List<T> {
}
}

impl<T: PartialEq> PartialEq for List<T> {
#[inline]
fn eq(&self, other: &List<T>) -> bool {
// Pointer equality implies list equality (due to the unique contents
// assumption).
ptr::eq(self, other)
}
}

impl<T: Eq> Eq for List<T> {}

impl<T> Ord for List<T>
where
T: Ord,
{
fn cmp(&self, other: &List<T>) -> Ordering {
// Pointer equality implies list equality (due to the unique contents
// assumption), but the contents must be compared otherwise.
if self == other { Ordering::Equal } else { <[T] as Ord>::cmp(&**self, &**other) }
}
}
Expand All @@ -121,6 +152,8 @@ where
T: PartialOrd,
{
fn partial_cmp(&self, other: &List<T>) -> Option<Ordering> {
// Pointer equality implies list equality (due to the unique contents
// assumption), but the contents must be compared otherwise.
if self == other {
Some(Ordering::Equal)
} else {
Expand All @@ -129,17 +162,11 @@ where
}
}

impl<T: PartialEq> PartialEq for List<T> {
#[inline]
fn eq(&self, other: &List<T>) -> bool {
ptr::eq(self, other)
}
}
impl<T: Eq> Eq for List<T> {}

impl<T> Hash for List<T> {
#[inline]
fn hash<H: Hasher>(&self, s: &mut H) {
// Pointer hashing is sufficient (due to the unique contents
// assumption).
(self as *const List<T>).hash(s)
}
}
Expand Down Expand Up @@ -168,13 +195,24 @@ impl<'a, T: Copy> IntoIterator for &'a List<T> {
}
}

impl<T> List<T> {
#[inline(always)]
pub fn empty<'a>() -> &'a List<T> {
#[repr(align(64), C)]
struct EmptySlice([u8; 64]);
static EMPTY_SLICE: EmptySlice = EmptySlice([0; 64]);
assert!(mem::align_of::<T>() <= 64);
unsafe { &*(&EMPTY_SLICE as *const _ as *const List<T>) }
unsafe impl<T: Sync> Sync for List<T> {}

unsafe impl<'a, T: 'a> rustc_data_structures::tagged_ptr::Pointer for &'a List<T> {
const BITS: usize = std::mem::align_of::<usize>().trailing_zeros() as usize;

#[inline]
fn into_usize(self) -> usize {
self as *const List<T> as usize
}

#[inline]
unsafe fn from_usize(ptr: usize) -> &'a List<T> {
&*(ptr as *const List<T>)
}

unsafe fn with_ref<R, F: FnOnce(&Self) -> R>(ptr: usize, f: F) -> R {
// `Self` is `&'a List<T>` which impls `Copy`, so this is fine.
let ptr = Self::from_usize(ptr);
f(&ptr)
}
}
5 changes: 5 additions & 0 deletions compiler/rustc_mir_transform/src/check_packed_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> {
a misaligned reference is undefined behavior (even if that \
reference is never dereferenced)",
)
.help(
"copy the field contents to a local variable, or replace the \
reference with a raw pointer and use `read_unaligned`/`write_unaligned` \
(loads and stores via `*p` must be properly aligned even when using raw pointers)"
)
.emit()
},
);
Expand Down
26 changes: 13 additions & 13 deletions src/librustdoc/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,19 @@ impl Options {
return Err(0);
}

let color = config::parse_color(matches);
let config::JsonConfig { json_rendered, json_unused_externs, .. } =
config::parse_json(matches);
let error_format = config::parse_error_format(matches, color, json_rendered);

let codegen_options = CodegenOptions::build(matches, error_format);
let debugging_opts = DebuggingOptions::build(matches, error_format);

let diag = new_handler(error_format, None, &debugging_opts);

// check for deprecated options
check_deprecated_options(matches, &diag);

if matches.opt_strs("passes") == ["list"] {
println!("Available passes for running rustdoc:");
for pass in passes::PASSES {
Expand Down Expand Up @@ -359,19 +372,6 @@ impl Options {
return Err(0);
}

let color = config::parse_color(matches);
let config::JsonConfig { json_rendered, json_unused_externs, .. } =
config::parse_json(matches);
let error_format = config::parse_error_format(matches, color, json_rendered);

let codegen_options = CodegenOptions::build(matches, error_format);
let debugging_opts = DebuggingOptions::build(matches, error_format);

let diag = new_handler(error_format, None, &debugging_opts);

// check for deprecated options
check_deprecated_options(matches, &diag);

let mut emit = Vec::new();
for list in matches.opt_strs("emit") {
for kind in list.split(',') {
Expand Down
1 change: 1 addition & 0 deletions src/librustdoc/html/render/print_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,7 @@ fn item_typedef(
// associated items from the aliased type (see discussion in #32077), but
// we need #14072 to make sense of the generics.
render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
document_type_layout(w, cx, def_id);
}

fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Union) {
Expand Down
3 changes: 3 additions & 0 deletions src/test/rustdoc-ui/issue-91713.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// check-pass
// compile-flags: --passes list
// error-pattern: the `passes` flag is deprecated
4 changes: 4 additions & 0 deletions src/test/rustdoc-ui/issue-91713.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
warning: the `passes` flag is deprecated
|
= note: see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information

31 changes: 31 additions & 0 deletions src/test/rustdoc-ui/issue-91713.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Available passes for running rustdoc:
check_doc_test_visibility - run various visibility-related lints on doctests
strip-hidden - strips all `#[doc(hidden)]` items from the output
unindent-comments - removes excess indentation on comments in order for markdown to like it
strip-private - strips all private items from a crate which cannot be seen externally, implies strip-priv-imports
strip-priv-imports - strips all private import statements (`use`, `extern crate`) from a crate
propagate-doc-cfg - propagates `#[doc(cfg(...))]` to child items
collect-intra-doc-links - resolves intra-doc links
check-code-block-syntax - validates syntax inside Rust code blocks
collect-trait-impls - retrieves trait impls for items in the crate
calculate-doc-coverage - counts the number of items with and without documentation
check-invalid-html-tags - detects invalid HTML tags in doc comments
check-bare-urls - detects URLs that are not hyperlinks

Default passes for rustdoc:
collect-trait-impls
unindent-comments
check_doc_test_visibility
strip-hidden (when not --document-hidden-items)
strip-private (when not --document-private-items)
strip-priv-imports (when --document-private-items)
collect-intra-doc-links
check-code-block-syntax
check-invalid-html-tags
propagate-doc-cfg
check-bare-urls

Passes run with `--show-coverage`:
strip-hidden (when not --document-hidden-items)
strip-private (when not --document-private-items)
calculate-doc-coverage
Loading