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
3 changes: 3 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/doc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,9 @@ impl DocParser {
for i in items.mixed() {
match i {
MetaItemOrLitParser::MetaItemParser(mip) => {
if self.nb_doc_attrs == 0 {
self.attribute.first_span = cx.attr_span;
}
self.nb_doc_attrs += 1;
self.parse_single_doc_attr_item(cx, mip);
}
Expand Down
17 changes: 9 additions & 8 deletions compiler/rustc_attr_parsing/src/target_checking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,15 +292,16 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
// in where clauses. After that, this function would become useless.
let spans = attrs
.into_iter()
// FIXME: We shouldn't need to special-case `doc`!
.filter(|attr| {
matches!(
attr,
Attribute::Parsed(AttributeKind::DocComment { .. } | AttributeKind::Doc(_))
| Attribute::Unparsed(_)
)
.filter_map(|attr| {
match attr {
Attribute::Parsed(AttributeKind::DocComment { span, .. }) => Some(*span),
// FIXME: We shouldn't need to special-case `doc`!
Attribute::Parsed(AttributeKind::Doc(attr)) => Some(attr.first_span),
// Checked during attribute parsing target checking
Attribute::Parsed(_) => None,
Attribute::Unparsed(attr) => Some(attr.span),
}
})
.map(|attr| attr.span())
.collect::<Vec<_>>();
if !spans.is_empty() {
self.dcx()
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_hir/src/attrs/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,8 @@ pub struct CfgHideShow {

#[derive(Clone, Debug, Default, HashStable_Generic, Decodable, PrintAttribute)]
pub struct DocAttribute {
pub first_span: Span,

pub aliases: FxIndexMap<Symbol, Span>,
pub hidden: Option<Span>,
// Because we need to emit the error if there is more than one `inline` attribute on an item
Expand Down Expand Up @@ -581,6 +583,7 @@ pub struct DocAttribute {
impl<E: rustc_span::SpanEncoder> rustc_serialize::Encodable<E> for DocAttribute {
fn encode(&self, encoder: &mut E) {
let DocAttribute {
first_span,
aliases,
hidden,
inline,
Expand All @@ -603,6 +606,7 @@ impl<E: rustc_span::SpanEncoder> rustc_serialize::Encodable<E> for DocAttribute
test_attrs,
no_crate_inject,
} = self;
rustc_serialize::Encodable::<E>::encode(first_span, encoder);
rustc_serialize::Encodable::<E>::encode(aliases, encoder);
rustc_serialize::Encodable::<E>::encode(hidden, encoder);

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1027,6 +1027,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
/// [`check_doc_inline`]: Self::check_doc_inline
fn check_doc_attrs(&self, attr: &DocAttribute, hir_id: HirId, target: Target) {
let DocAttribute {
first_span: _,
aliases,
// valid pretty much anywhere, not checked here?
// FIXME: should we?
Expand Down
69 changes: 47 additions & 22 deletions library/core/src/array/drain.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::marker::{Destruct, PhantomData};
use crate::mem::{ManuallyDrop, SizedTypeProperties, conjure_zst};
use crate::ptr::{NonNull, drop_in_place, from_raw_parts_mut, null_mut};
use crate::mem::{ManuallyDrop, SizedTypeProperties, conjure_zst, transmute};
use crate::ptr::{NonNull, drop_in_place, from_raw_parts_mut, without_provenance_mut};

impl<'l, 'f, T, U, const N: usize, F: FnMut(T) -> U> Drain<'l, 'f, T, N, F> {
impl<'l, 'f, T, U, F: FnMut(T) -> U> Drain<'l, 'f, T, F> {
/// This function returns a function that lets you index the given array in const.
/// As implemented it can optimize better than iterators, and can be constified.
/// It acts like a sort of guard (owns the array) and iterator combined, which can be implemented
Expand All @@ -14,43 +14,46 @@ impl<'l, 'f, T, U, const N: usize, F: FnMut(T) -> U> Drain<'l, 'f, T, N, F> {
/// This will also not actually store the array.
///
/// SAFETY: must only be called `N` times. Thou shalt not drop the array either.
// FIXME(const-hack): this is a hack for `let guard = Guard(array); |i| f(guard[i])`.
#[rustc_const_unstable(feature = "array_try_map", issue = "79711")]
pub(super) const unsafe fn new(array: &'l mut ManuallyDrop<[T; N]>, f: &'f mut F) -> Self {
pub(super) const unsafe fn new<const N: usize>(
array: &'l mut ManuallyDrop<[T; N]>,
f: &'f mut F,
) -> Self {
// dont drop the array, transfers "ownership" to Self
let ptr: NonNull<T> = NonNull::from_mut(array).cast();
// SAFETY:
// Adding `slice.len()` to the starting pointer gives a pointer
// at the end of `slice`. `end` will never be dereferenced, only checked
// for direct pointer equality with `ptr` to check if the drainer is done.
unsafe {
let end = if T::IS_ZST { null_mut() } else { ptr.as_ptr().add(N) };
Self { ptr, end, f, l: PhantomData }
let end_or_len =
if T::IS_ZST { without_provenance_mut(N) } else { ptr.as_ptr().add(N) };
Self { ptr, end_or_len, f, l: PhantomData }
}
}
}

/// See [`Drain::new`]; this is our fake iterator.
#[unstable(feature = "array_try_map", issue = "79711")]
pub(super) struct Drain<'l, 'f, T, const N: usize, F> {
// FIXME(const-hack): This is essentially a slice::IterMut<'static>, replace when possible.
pub(super) struct Drain<'l, 'f, T, F> {
// FIXME(const-hack): This is a slice::IterMut<'l>, replace when possible.
/// The pointer to the next element to return, or the past-the-end location
/// if the drainer is empty.
///
/// This address will be used for all ZST elements, never changed.
/// As we "own" this array, we dont need to store any lifetime.
ptr: NonNull<T>,
/// For non-ZSTs, the non-null pointer to the past-the-end element.
/// For ZSTs, this is null.
end: *mut T,
/// For ZSTs, this is the number of unprocessed items.
end_or_len: *mut T,

f: &'f mut F,
l: PhantomData<&'l mut [T; N]>,
l: PhantomData<&'l mut [T]>,
}

#[rustc_const_unstable(feature = "array_try_map", issue = "79711")]
#[unstable(feature = "array_try_map", issue = "79711")]
impl<T, U, const N: usize, F> const FnOnce<(usize,)> for &mut Drain<'_, '_, T, N, F>
impl<T, U, F> const FnOnce<(usize,)> for &mut Drain<'_, '_, T, F>
where
F: [const] FnMut(T) -> U,
{
Expand All @@ -63,7 +66,7 @@ where
}
#[rustc_const_unstable(feature = "array_try_map", issue = "79711")]
#[unstable(feature = "array_try_map", issue = "79711")]
impl<T, U, const N: usize, F> const FnMut<(usize,)> for &mut Drain<'_, '_, T, N, F>
impl<T, U, F> const FnMut<(usize,)> for &mut Drain<'_, '_, T, F>
where
F: [const] FnMut(T) -> U,
{
Expand All @@ -73,6 +76,16 @@ where
(_ /* ignore argument */,): (usize,),
) -> Self::Output {
if T::IS_ZST {
#[expect(ptr_to_integer_transmute_in_consts)]
// SAFETY:
// This is equivalent to `self.end_or_len.addr`, but that's not
// available in `const`. `self.end_or_len` doesn't have provenance,
// so transmuting is fine.
let len = unsafe { transmute::<*mut T, usize>(self.end_or_len) };
// SAFETY:
// The caller guarantees that this is never called more than N times
// (see `Drain::new`), hence this cannot underflow.
self.end_or_len = without_provenance_mut(unsafe { len.unchecked_sub(1) });
// its UB to call this more than N times, so returning more ZSTs is valid.
// SAFETY: its a ZST? we conjur.
(self.f)(unsafe { conjure_zst::<T>() })
Expand All @@ -88,20 +101,32 @@ where
}
#[rustc_const_unstable(feature = "array_try_map", issue = "79711")]
#[unstable(feature = "array_try_map", issue = "79711")]
impl<T: [const] Destruct, const N: usize, F> const Drop for Drain<'_, '_, T, N, F> {
impl<T: [const] Destruct, F> const Drop for Drain<'_, '_, T, F> {
fn drop(&mut self) {
if !T::IS_ZST {
let slice = if T::IS_ZST {
from_raw_parts_mut::<[T]>(
self.ptr.as_ptr(),
#[expect(ptr_to_integer_transmute_in_consts)]
// SAFETY:
// This is equivalent to `self.end_or_len.addr`, but that's not
// available in `const`. `self.end_or_len` doesn't have provenance,
// so transmuting is fine.
unsafe {
transmute::<*mut T, usize>(self.end_or_len)
},
)
} else {
// SAFETY: we cant read more than N elements
let slice = unsafe {
unsafe {
from_raw_parts_mut::<[T]>(
self.ptr.as_ptr(),
// SAFETY: `start <= end`
self.end.offset_from_unsigned(self.ptr.as_ptr()),
self.end_or_len.offset_from_unsigned(self.ptr.as_ptr()),
)
};
}
};

// SAFETY: By the type invariant, we're allowed to drop all these. (we own it, after all)
unsafe { drop_in_place(slice) }
}
// SAFETY: By the type invariant, we're allowed to drop all these. (we own it, after all)
unsafe { drop_in_place(slice) }
}
}
27 changes: 27 additions & 0 deletions library/coretests/tests/array.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use core::cell::Cell;
use core::num::NonZero;
use core::sync::atomic::{AtomicUsize, Ordering};
use core::{array, assert_eq};
use std::sync::ReentrantLock;

#[test]
fn array_from_ref() {
Expand Down Expand Up @@ -718,6 +720,31 @@ fn array_map_drops_unmapped_elements_on_panic() {
}
}

#[cfg(not(panic = "abort"))]
#[test]
fn array_map_drops_unmapped_zst_elements_on_panic() {
static DROPPED: ReentrantLock<Cell<usize>> = ReentrantLock::new(Cell::new(0));

struct ZstDrop;
impl Drop for ZstDrop {
fn drop(&mut self) {
DROPPED.lock().update(|x| x + 1);
}
}

let dropped = DROPPED.lock();
dropped.set(0);
let array = [const { ZstDrop }; 5];
let success = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let _ = array.map(|x| {
drop(x);
assert_eq!(dropped.get(), 1);
});
}));
assert!(success.is_err());
assert_eq!(dropped.get(), 5);
}

// This covers the `PartialEq::<[T]>::eq` impl for `[T; N]` when it returns false.
#[test]
fn array_eq() {
Expand Down
1 change: 1 addition & 0 deletions library/coretests/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
#![feature(pointer_is_aligned_to)]
#![feature(portable_simd)]
#![feature(ptr_metadata)]
#![feature(reentrant_lock)]
#![feature(result_option_map_or_default)]
#![feature(rustc_attrs)]
#![feature(signed_bigint_helpers)]
Expand Down
1 change: 1 addition & 0 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2780,6 +2780,7 @@ fn add_without_unwanted_attributes<'hir>(
hir::Attribute::Parsed(AttributeKind::Doc(box d)) => {
// Remove attributes from `normal` that should not be inherited by `use` re-export.
let DocAttribute {
first_span: _,
aliases,
hidden,
inline,
Expand Down
1 change: 1 addition & 0 deletions src/librustdoc/json/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -960,6 +960,7 @@ fn maybe_from_hir_attr(attr: &hir::Attribute, item_id: ItemId, tcx: TyCtxt<'_>)
}

let DocAttribute {
first_span: _,
aliases,
hidden,
inline,
Expand Down
34 changes: 34 additions & 0 deletions tests/ui/attributes/where-doc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#![feature(where_clause_attrs)]
#![allow(invalid_doc_attributes)]

fn test()
where
#[doc(alias = ":(")]
//~^ ERROR most attributes are not supported in `where` clauses
//~| ERROR `#[doc(alias = "...")]` isn't allowed on where predicate
():,

#[doc(hidden)]
//~^ ERROR most attributes are not supported in `where` clauses
():,

#[doc = ""]
//~^ ERROR most attributes are not supported in `where` clauses
():,

// == That the doc attributes below don't trigger the error is a bug
#[doc()]
():,

#[doc(5)]
():,

#[doc]
():,

#[doc = 5]
():,

{ }

fn main() {}
32 changes: 32 additions & 0 deletions tests/ui/attributes/where-doc.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
error: most attributes are not supported in `where` clauses
--> $DIR/where-doc.rs:6:1
|
LL | #[doc(alias = ":(")]
| ^^^^^^^^^^^^^^^^^^^^
|
= help: only `#[cfg]` and `#[cfg_attr]` are supported

error: most attributes are not supported in `where` clauses
--> $DIR/where-doc.rs:11:1
|
LL | #[doc(hidden)]
| ^^^^^^^^^^^^^^
|
= help: only `#[cfg]` and `#[cfg_attr]` are supported

error: most attributes are not supported in `where` clauses
--> $DIR/where-doc.rs:15:1
|
LL | #[doc = ""]
| ^^^^^^^^^^^
|
= help: only `#[cfg]` and `#[cfg_attr]` are supported

error: `#[doc(alias = "...")]` isn't allowed on where predicate
--> $DIR/where-doc.rs:6:15
|
LL | #[doc(alias = ":(")]
| ^^^^

error: aborting due to 4 previous errors

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
//@ check-pass

// One of the minimizations from trait-system-refactor-initiative#257 which ended up
// getting fixed by #153614. It seems somewhat likely that this is somewhat
// accidental by changing the exact shape of the cycle, causing us to avoid the
// underlying issue of trait-system-refactor-initiative#257.

pub trait Field: Sized + HasUnderlier<Underlier: PackScalar<Self>> {}
pub trait PackScalar<F>: 'static + UnderlierType {
type Packed;
}
trait PackedField {
type Scalar;
}
pub trait UnderlierType {}
pub trait HasUnderlier {
type Underlier;
}
impl<U: UnderlierType> HasUnderlier for U {
type Underlier = U;
}
impl UnderlierType for u8 {}
struct MyField;
impl Field for MyField {}
impl HasUnderlier for MyField {
type Underlier = u8;
}
impl<F> PackScalar<F> for u8
where
F: Field,
{
type Packed = PackedPrimitiveType<F>;
}
pub struct PackedPrimitiveType<Scalar: Field>(Scalar);
impl<Scalar> PackedField for PackedPrimitiveType<Scalar>
where
Scalar: Field,
{
type Scalar = Scalar;
}

pub trait PackedTransformationFactory<OP> {}
trait TaggedPackedTransformationFactory<OP>: PackedField<Scalar: Field> {}
impl<OP> PackedTransformationFactory<OP> for PackedPrimitiveType<MyField> where
Self: TaggedPackedTransformationFactory<OP>
{
}
fn main() {}
Loading