Skip to content
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: 3 additions & 1 deletion compiler/rustc_codegen_ssa/src/back/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,9 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
file.set_sub_architecture(sub_architecture);
if sess.target.is_like_darwin {
if macho_is_arm64e(&sess.target) {
file.set_macho_cpu_subtype(object::macho::CPU_SUBTYPE_ARM64E);
file.set_macho_cpu_subtype(
object::macho::CPU_SUBTYPE_ARM64E | object::macho::CPU_SUBTYPE_PTRAUTH_ABI,
);
}

file.set_macho_build_version(macho_object_build_version_for_target(sess))
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