Auto merge of #135540 - GuillaumeGomez:rollup-40lfb7l, r=GuillaumeGomez

Rollup of 6 pull requests

Successful merges:

 - #132654 (std: lazily allocate the main thread handle)
 - #135003 (deprecate `std::intrinsics::transmute` etc, use `std::mem::*` instead)
 - #135428 (rustdoc: Remove `AttributesExt` trait magic that added needless complexity)
 - #135498 (Prefer lower `TraitUpcasting` candidates in selection)
 - #135507 (TRPL: incorporate all backward-compatible Edition changes)
 - #135529 (remove outdated FIXME)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-01-15 18:26:54 +00:00
commit 419b3e2d3e
39 changed files with 604 additions and 464 deletions

View File

@ -723,6 +723,8 @@ impl MetaItemLit {
pub trait AttributeExt: Debug {
fn id(&self) -> AttrId;
/// For a single-segment attribute (i.e., `#[attr]` and not `#[path::atrr]`),
/// return the name of the attribute, else return the empty identifier.
fn name_or_empty(&self) -> Symbol {
self.ident().unwrap_or_else(Ident::empty).name
}

View File

@ -101,6 +101,16 @@ impl PartialConstStability {
}
}
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
#[derive(HashStable_Generic)]
pub enum AllowedThroughUnstableModules {
/// This does not get a deprecation warning. We still generally would prefer people to use the
/// fully stable path, and a warning will likely be emitted in the future.
WithoutDeprecation,
/// Emit the given deprecation warning.
WithDeprecation(Symbol),
}
/// The available stability levels.
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
#[derive(HashStable_Generic)]
@ -137,9 +147,8 @@ pub enum StabilityLevel {
Stable {
/// Rust release which stabilized this feature.
since: StableSince,
/// Is this item allowed to be referred to on stable, despite being contained in unstable
/// modules?
allowed_through_unstable_modules: bool,
/// This is `Some` if this item allowed to be referred to on stable via unstable modules.
allowed_through_unstable_modules: Option<AllowedThroughUnstableModules>,
},
}

View File

@ -6,8 +6,8 @@ use rustc_ast::MetaItem;
use rustc_ast::attr::AttributeExt;
use rustc_ast_pretty::pprust;
use rustc_attr_data_structures::{
ConstStability, DefaultBodyStability, Stability, StabilityLevel, StableSince, UnstableReason,
VERSION_PLACEHOLDER,
AllowedThroughUnstableModules, ConstStability, DefaultBodyStability, Stability, StabilityLevel,
StableSince, UnstableReason, VERSION_PLACEHOLDER,
};
use rustc_errors::ErrorGuaranteed;
use rustc_session::Session;
@ -24,11 +24,16 @@ pub fn find_stability(
item_sp: Span,
) -> Option<(Stability, Span)> {
let mut stab: Option<(Stability, Span)> = None;
let mut allowed_through_unstable_modules = false;
let mut allowed_through_unstable_modules = None;
for attr in attrs {
match attr.name_or_empty() {
sym::rustc_allowed_through_unstable_modules => allowed_through_unstable_modules = true,
sym::rustc_allowed_through_unstable_modules => {
allowed_through_unstable_modules = Some(match attr.value_str() {
Some(msg) => AllowedThroughUnstableModules::WithDeprecation(msg),
None => AllowedThroughUnstableModules::WithoutDeprecation,
})
}
sym::unstable => {
if stab.is_some() {
sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
@ -56,15 +61,15 @@ pub fn find_stability(
}
}
if allowed_through_unstable_modules {
if let Some(allowed_through_unstable_modules) = allowed_through_unstable_modules {
match &mut stab {
Some((
Stability {
level: StabilityLevel::Stable { allowed_through_unstable_modules, .. },
level: StabilityLevel::Stable { allowed_through_unstable_modules: in_stab, .. },
..
},
_,
)) => *allowed_through_unstable_modules = true,
)) => *in_stab = Some(allowed_through_unstable_modules),
_ => {
sess.dcx()
.emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp });
@ -283,7 +288,7 @@ fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol,
match feature {
Ok(feature) => {
let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: false };
let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: None };
Some((feature, level))
}
Err(ErrorGuaranteed { .. }) => None,

View File

@ -623,7 +623,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
EncodeCrossCrate::No, "allow_internal_unsafe side-steps the unsafe_code lint",
),
rustc_attr!(
rustc_allowed_through_unstable_modules, Normal, template!(Word),
rustc_allowed_through_unstable_modules, Normal, template!(Word, NameValueStr: "deprecation message"),
WarnFollowing, EncodeCrossCrate::No,
"rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \
through unstable paths"

View File

@ -249,9 +249,18 @@ fn late_report_deprecation(
return;
}
let is_in_effect = depr.is_in_effect();
let lint = deprecation_lint(is_in_effect);
// Calculating message for lint involves calling `self.def_path_str`,
// which will by default invoke the expensive `visible_parent_map` query.
// Skip all that work if the lint is allowed anyway.
if tcx.lint_level_at_node(lint, hir_id).0 == Level::Allow {
return;
}
let def_path = with_no_trimmed_paths!(tcx.def_path_str(def_id));
let def_kind = tcx.def_descr(def_id);
let is_in_effect = depr.is_in_effect();
let method_span = method_span.unwrap_or(span);
let suggestion =
@ -267,7 +276,7 @@ fn late_report_deprecation(
note: depr.note,
since_kind: deprecated_since_kind(is_in_effect, depr.since),
};
tcx.emit_node_span_lint(deprecation_lint(is_in_effect), hir_id, method_span, diag);
tcx.emit_node_span_lint(lint, hir_id, method_span, diag);
}
/// Result of `TyCtxt::eval_stability`.
@ -377,13 +386,7 @@ impl<'tcx> TyCtxt<'tcx> {
// hierarchy.
let depr_attr = &depr_entry.attr;
if !skip || depr_attr.is_since_rustc_version() {
// Calculating message for lint involves calling `self.def_path_str`.
// Which by default to calculate visible path will invoke expensive `visible_parent_map` query.
// So we skip message calculation altogether, if lint is allowed.
let lint = deprecation_lint(depr_attr.is_in_effect());
if self.lint_level_at_node(lint, id).0 != Level::Allow {
late_report_deprecation(self, depr_attr, span, method_span, id, def_id);
}
late_report_deprecation(self, depr_attr, span, method_span, id, def_id);
}
};
}

View File

@ -741,12 +741,14 @@ where
a_data.principal(),
));
} else if let Some(a_principal) = a_data.principal() {
for new_a_principal in
elaborate::supertraits(self.cx(), a_principal.with_self_ty(cx, a_ty)).skip(1)
for (idx, new_a_principal) in
elaborate::supertraits(self.cx(), a_principal.with_self_ty(cx, a_ty))
.enumerate()
.skip(1)
{
responses.extend(self.consider_builtin_upcast_to_principal(
goal,
CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting),
CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting(idx)),
a_data,
a_region,
b_data,

View File

@ -5,8 +5,8 @@ use std::mem::replace;
use std::num::NonZero;
use rustc_attr_parsing::{
self as attr, ConstStability, DeprecatedSince, Stability, StabilityLevel, StableSince,
UnstableReason, VERSION_PLACEHOLDER,
self as attr, AllowedThroughUnstableModules, ConstStability, DeprecatedSince, Stability,
StabilityLevel, StableSince, UnstableReason, VERSION_PLACEHOLDER,
};
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet};
@ -20,11 +20,16 @@ use rustc_hir::{FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant};
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures};
use rustc_middle::middle::privacy::EffectiveVisibilities;
use rustc_middle::middle::stability::{AllowUnstable, DeprecationEntry, Index};
use rustc_middle::middle::stability::{
AllowUnstable, Deprecated, DeprecationEntry, EvalResult, Index,
};
use rustc_middle::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_session::lint;
use rustc_session::lint::builtin::{INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED};
use rustc_session::lint::builtin::{
DEPRECATED, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED,
};
use rustc_span::{Span, Symbol, sym};
use tracing::{debug, info};
@ -874,42 +879,95 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
},
);
let is_allowed_through_unstable_modules = |def_id| {
self.tcx.lookup_stability(def_id).is_some_and(|stab| match stab.level {
StabilityLevel::Stable { allowed_through_unstable_modules, .. } => {
allowed_through_unstable_modules
}
_ => false,
})
};
if item_is_allowed {
// The item itself is allowed; check whether the path there is also allowed.
let is_allowed_through_unstable_modules: Option<AllowedThroughUnstableModules> =
self.tcx.lookup_stability(def_id).and_then(|stab| match stab.level {
StabilityLevel::Stable { allowed_through_unstable_modules, .. } => {
allowed_through_unstable_modules
}
_ => None,
});
if item_is_allowed && !is_allowed_through_unstable_modules(def_id) {
// Check parent modules stability as well if the item the path refers to is itself
// stable. We only emit warnings for unstable path segments if the item is stable
// or allowed because stability is often inherited, so the most common case is that
// both the segments and the item are unstable behind the same feature flag.
//
// We check here rather than in `visit_path_segment` to prevent visiting the last
// path segment twice
//
// We include special cases via #[rustc_allowed_through_unstable_modules] for items
// that were accidentally stabilized through unstable paths before this check was
// added, such as `core::intrinsics::transmute`
let parents = path.segments.iter().rev().skip(1);
for path_segment in parents {
if let Some(def_id) = path_segment.res.opt_def_id() {
// use `None` for id to prevent deprecation check
self.tcx.check_stability_allow_unstable(
def_id,
None,
path.span,
None,
if is_unstable_reexport(self.tcx, id) {
AllowUnstable::Yes
} else {
AllowUnstable::No
},
);
if is_allowed_through_unstable_modules.is_none() {
// Check parent modules stability as well if the item the path refers to is itself
// stable. We only emit warnings for unstable path segments if the item is stable
// or allowed because stability is often inherited, so the most common case is that
// both the segments and the item are unstable behind the same feature flag.
//
// We check here rather than in `visit_path_segment` to prevent visiting the last
// path segment twice
//
// We include special cases via #[rustc_allowed_through_unstable_modules] for items
// that were accidentally stabilized through unstable paths before this check was
// added, such as `core::intrinsics::transmute`
let parents = path.segments.iter().rev().skip(1);
for path_segment in parents {
if let Some(def_id) = path_segment.res.opt_def_id() {
// use `None` for id to prevent deprecation check
self.tcx.check_stability_allow_unstable(
def_id,
None,
path.span,
None,
if is_unstable_reexport(self.tcx, id) {
AllowUnstable::Yes
} else {
AllowUnstable::No
},
);
}
}
} else if let Some(AllowedThroughUnstableModules::WithDeprecation(deprecation)) =
is_allowed_through_unstable_modules
{
// Similar to above, but we cannot use `check_stability_allow_unstable` as that would
// immediately show the stability error. We just want to know the result and disaplay
// our own kind of error.
let parents = path.segments.iter().rev().skip(1);
for path_segment in parents {
if let Some(def_id) = path_segment.res.opt_def_id() {
// use `None` for id to prevent deprecation check
let eval_result = self.tcx.eval_stability_allow_unstable(
def_id,
None,
path.span,
None,
if is_unstable_reexport(self.tcx, id) {
AllowUnstable::Yes
} else {
AllowUnstable::No
},
);
let is_allowed = matches!(eval_result, EvalResult::Allow);
if !is_allowed {
// Calculating message for lint involves calling `self.def_path_str`,
// which will by default invoke the expensive `visible_parent_map` query.
// Skip all that work if the lint is allowed anyway.
if self.tcx.lint_level_at_node(DEPRECATED, id).0
== lint::Level::Allow
{
return;
}
// Show a deprecation message.
let def_path =
with_no_trimmed_paths!(self.tcx.def_path_str(def_id));
let def_kind = self.tcx.def_descr(def_id);
let diag = Deprecated {
sub: None,
kind: def_kind.to_owned(),
path: def_path,
note: Some(deprecation),
since_kind: lint::DeprecatedSinceKind::InEffect,
};
self.tcx.emit_node_span_lint(
DEPRECATED,
id,
method_span.unwrap_or(path.span),
diag,
);
}
}
}
}
}

View File

@ -117,6 +117,10 @@ fn candidate_should_be_dropped_in_favor_of<'tcx>(
CandidateSource::BuiltinImpl(BuiltinImplSource::Object(a)),
CandidateSource::BuiltinImpl(BuiltinImplSource::Object(b)),
) => a >= b,
(
CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting(a)),
CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting(b)),
) => a >= b,
// Prefer dyn candidates over non-dyn candidates. This is necessary to
// handle the unsoundness between `impl<T: ?Sized> Any for T` and `dyn Any: Any`.
(

View File

@ -1090,7 +1090,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
)?
.expect("did not expect ambiguity during confirmation");
Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting, nested))
Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting(idx), nested))
}
fn confirm_builtin_unsize_candidate(

View File

@ -1895,6 +1895,18 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
Some(None) => {}
None => return None,
}
// Same for upcasting.
let upcast_bound = candidates
.iter()
.filter_map(|c| {
if let TraitUpcastingUnsizeCandidate(i) = c.candidate { Some(i) } else { None }
})
.try_reduce(|c1, c2| if has_non_region_infer { None } else { Some(c1.min(c2)) });
match upcast_bound {
Some(Some(index)) => return Some(TraitUpcastingUnsizeCandidate(index)),
Some(None) => {}
None => return None,
}
// Finally, handle overlapping user-written impls.
let impls = candidates.iter().filter_map(|c| {

View File

@ -177,8 +177,9 @@ pub enum BuiltinImplSource {
/// A built-in implementation of `Upcast` for trait objects to other trait objects.
///
/// This can be removed when `feature(dyn_upcasting)` is stabilized, since we only
/// use it to detect when upcasting traits in hir typeck.
TraitUpcasting,
/// use it to detect when upcasting traits in hir typeck. The index is only used
/// for winnowing.
TraitUpcasting(usize),
/// Unsizing a tuple like `(A, B, ..., X)` to `(A, B, ..., Y)` if `X` unsizes to `Y`.
///
/// This can be removed when `feature(tuple_unsizing)` is stabilized, since we only

View File

@ -1897,7 +1897,11 @@ pub const fn forget<T: ?Sized>(_: T) {
/// }
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_allowed_through_unstable_modules]
#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)]
#[cfg_attr(
not(bootstrap),
rustc_allowed_through_unstable_modules = "import this function via `std::mem` instead"
)]
#[rustc_const_stable(feature = "const_transmute", since = "1.56.0")]
#[rustc_diagnostic_item = "transmute"]
#[rustc_nounwind]
@ -4325,7 +4329,11 @@ pub const fn ptr_metadata<P: ptr::Pointee<Metadata = M> + ?Sized, M>(_ptr: *cons
/// [`Vec::append`]: ../../std/vec/struct.Vec.html#method.append
#[doc(alias = "memcpy")]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_allowed_through_unstable_modules]
#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)]
#[cfg_attr(
not(bootstrap),
rustc_allowed_through_unstable_modules = "import this function via `std::mem` instead"
)]
#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")]
#[inline(always)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
@ -4429,7 +4437,11 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
/// ```
#[doc(alias = "memmove")]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_allowed_through_unstable_modules]
#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)]
#[cfg_attr(
not(bootstrap),
rustc_allowed_through_unstable_modules = "import this function via `std::mem` instead"
)]
#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")]
#[inline(always)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
@ -4512,7 +4524,11 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
/// ```
#[doc(alias = "memset")]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_allowed_through_unstable_modules]
#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)]
#[cfg_attr(
not(bootstrap),
rustc_allowed_through_unstable_modules = "import this function via `std::mem` instead"
)]
#[rustc_const_stable(feature = "const_ptr_write", since = "1.83.0")]
#[inline(always)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces

View File

@ -258,31 +258,34 @@ fn default_hook(info: &PanicHookInfo<'_>) {
let location = info.location().unwrap();
let msg = payload_as_str(info.payload());
let thread = thread::try_current();
let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unnamed>");
let write = #[optimize(size)]
|err: &mut dyn crate::io::Write| {
// Use a lock to prevent mixed output in multithreading context.
// Some platforms also require it when printing a backtrace, like `SymFromAddr` on Windows.
let mut lock = backtrace::lock();
// Try to write the panic message to a buffer first to prevent other concurrent outputs
// interleaving with it.
let mut buffer = [0u8; 512];
let mut cursor = crate::io::Cursor::new(&mut buffer[..]);
let write_msg = |dst: &mut dyn crate::io::Write| {
// We add a newline to ensure the panic message appears at the start of a line.
writeln!(dst, "\nthread '{name}' panicked at {location}:\n{msg}")
};
thread::with_current_name(|name| {
let name = name.unwrap_or("<unnamed>");
if write_msg(&mut cursor).is_ok() {
let pos = cursor.position() as usize;
let _ = err.write_all(&buffer[0..pos]);
} else {
// The message did not fit into the buffer, write it directly instead.
let _ = write_msg(err);
};
// Try to write the panic message to a buffer first to prevent other concurrent outputs
// interleaving with it.
let mut buffer = [0u8; 512];
let mut cursor = crate::io::Cursor::new(&mut buffer[..]);
let write_msg = |dst: &mut dyn crate::io::Write| {
// We add a newline to ensure the panic message appears at the start of a line.
writeln!(dst, "\nthread '{name}' panicked at {location}:\n{msg}")
};
if write_msg(&mut cursor).is_ok() {
let pos = cursor.position() as usize;
let _ = err.write_all(&buffer[0..pos]);
} else {
// The message did not fit into the buffer, write it directly instead.
let _ = write_msg(err);
};
});
static FIRST_PANIC: AtomicBool = AtomicBool::new(true);

View File

@ -23,7 +23,7 @@ pub use core::panicking::{panic_display, panic_fmt};
#[rustfmt::skip]
use crate::any::Any;
use crate::sync::Once;
use crate::thread::{self, Thread};
use crate::thread::{self, main_thread};
use crate::{mem, panic, sys};
// Prints to the "panic output", depending on the platform this may be:
@ -102,24 +102,9 @@ unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
sys::init(argc, argv, sigpipe)
};
// Set up the current thread handle to give it the right name.
//
// When code running before main uses `ReentrantLock` (for example by
// using `println!`), the thread ID can become initialized before we
// create this handle. Since `set_current` fails when the ID of the
// handle does not match the current ID, we should attempt to use the
// current thread ID here instead of unconditionally creating a new
// one. Also see #130210.
let thread = unsafe { Thread::new_main(thread::current_id()) };
if let Err(_thread) = thread::set_current(thread) {
// `thread::current` will create a new handle if none has been set yet.
// Thus, if someone uses it before main, this call will fail. That's a
// bad idea though, as we then cannot set the main thread name here.
//
// FIXME: detect the main thread in `thread::current` and use the
// correct name there.
rtabort!("code running before main must not use thread::current");
}
// Remember the main thread ID to give it the correct name.
// SAFETY: this is the only time and place where we call this function.
unsafe { main_thread::set(thread::current_id()) };
}
/// Clean up the thread-local runtime state. This *should* be run after all other

View File

@ -100,10 +100,11 @@ mod imp {
// If the faulting address is within the guard page, then we print a
// message saying so and abort.
if start <= addr && addr < end {
rtprintpanic!(
"\nthread '{}' has overflowed its stack\n",
thread::current().name().unwrap_or("<unknown>")
);
thread::with_current_name(|name| {
let name = name.unwrap_or("<unknown>");
rtprintpanic!("\nthread '{name}' has overflowed its stack\n");
});
rtabort!("stack overflow");
} else {
// Unregister ourselves by reverting back to the default behavior.

View File

@ -18,10 +18,10 @@ unsafe extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POIN
let code = rec.ExceptionCode;
if code == c::EXCEPTION_STACK_OVERFLOW {
rtprintpanic!(
"\nthread '{}' has overflowed its stack\n",
thread::current().name().unwrap_or("<unknown>")
);
thread::with_current_name(|name| {
let name = name.unwrap_or("<unknown>");
rtprintpanic!("\nthread '{name}' has overflowed its stack\n");
});
}
c::EXCEPTION_CONTINUE_SEARCH
}

View File

@ -15,7 +15,7 @@ local_pointer! {
///
/// We store the thread ID so that it never gets destroyed during the lifetime
/// of a thread, either using `#[thread_local]` or multiple `local_pointer!`s.
mod id {
pub(super) mod id {
use super::*;
cfg_if::cfg_if! {
@ -27,7 +27,7 @@ mod id {
pub(super) const CHEAP: bool = true;
pub(super) fn get() -> Option<ThreadId> {
pub(crate) fn get() -> Option<ThreadId> {
ID.get()
}
@ -44,7 +44,7 @@ mod id {
pub(super) const CHEAP: bool = false;
pub(super) fn get() -> Option<ThreadId> {
pub(crate) fn get() -> Option<ThreadId> {
let id0 = ID0.get().addr() as u64;
let id16 = ID16.get().addr() as u64;
let id32 = ID32.get().addr() as u64;
@ -67,7 +67,7 @@ mod id {
pub(super) const CHEAP: bool = false;
pub(super) fn get() -> Option<ThreadId> {
pub(crate) fn get() -> Option<ThreadId> {
let id0 = ID0.get().addr() as u64;
let id32 = ID32.get().addr() as u64;
ThreadId::from_u64((id32 << 32) + id0)
@ -85,7 +85,7 @@ mod id {
pub(super) const CHEAP: bool = true;
pub(super) fn get() -> Option<ThreadId> {
pub(crate) fn get() -> Option<ThreadId> {
let id = ID.get().addr() as u64;
ThreadId::from_u64(id)
}
@ -112,7 +112,7 @@ mod id {
/// Tries to set the thread handle for the current thread. Fails if a handle was
/// already set or if the thread ID of `thread` would change an already-set ID.
pub(crate) fn set_current(thread: Thread) -> Result<(), Thread> {
pub(super) fn set_current(thread: Thread) -> Result<(), Thread> {
if CURRENT.get() != NONE {
return Err(thread);
}
@ -140,28 +140,31 @@ pub(crate) fn current_id() -> ThreadId {
// to retrieve it from the current thread handle, which will only take one
// TLS access.
if !id::CHEAP {
let current = CURRENT.get();
if current > DESTROYED {
unsafe {
let current = ManuallyDrop::new(Thread::from_raw(current));
return current.id();
}
if let Some(id) = try_with_current(|t| t.map(|t| t.id())) {
return id;
}
}
id::get_or_init()
}
/// Gets a handle to the thread that invokes it, if the handle has been initialized.
pub(crate) fn try_current() -> Option<Thread> {
/// Gets a reference to the handle of the thread that invokes it, if the handle
/// has been initialized.
pub(super) fn try_with_current<F, R>(f: F) -> R
where
F: FnOnce(Option<&Thread>) -> R,
{
let current = CURRENT.get();
if current > DESTROYED {
// SAFETY: `Arc` does not contain interior mutability, so it does not
// matter that the address of the handle might be different depending
// on where this is called.
unsafe {
let current = ManuallyDrop::new(Thread::from_raw(current));
Some((*current).clone())
f(Some(&current))
}
} else {
None
f(None)
}
}
@ -176,7 +179,7 @@ pub(crate) fn current_or_unnamed() -> Thread {
(*current).clone()
}
} else if current == DESTROYED {
Thread::new_unnamed(id::get_or_init())
Thread::new(id::get_or_init(), None)
} else {
init_current(current)
}
@ -221,7 +224,7 @@ fn init_current(current: *mut ()) -> Thread {
CURRENT.set(BUSY);
// If the thread ID was initialized already, use it.
let id = id::get_or_init();
let thread = Thread::new_unnamed(id);
let thread = Thread::new(id, None);
// Make sure that `crate::rt::thread_cleanup` will be run, which will
// call `drop_current`.

View File

@ -158,12 +158,9 @@
#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))]
mod tests;
use core::cell::SyncUnsafeCell;
use core::ffi::CStr;
use core::mem::MaybeUninit;
use crate::any::Any;
use crate::cell::UnsafeCell;
use crate::ffi::CStr;
use crate::marker::PhantomData;
use crate::mem::{self, ManuallyDrop, forget};
use crate::num::NonZero;
@ -186,7 +183,8 @@ mod current;
#[stable(feature = "rust1", since = "1.0.0")]
pub use current::current;
pub(crate) use current::{current_id, current_or_unnamed, drop_current, set_current, try_current};
pub(crate) use current::{current_id, current_or_unnamed, drop_current};
use current::{set_current, try_with_current};
mod spawnhook;
@ -501,10 +499,7 @@ impl Builder {
});
let id = ThreadId::new();
let my_thread = match name {
Some(name) => Thread::new(id, name),
None => Thread::new_unnamed(id),
};
let my_thread = Thread::new(id, name);
let hooks = if no_hooks {
spawnhook::ChildSpawnHooks::default()
@ -1235,7 +1230,7 @@ impl ThreadId {
}
}
#[cfg(not(target_thread_local))]
#[cfg(any(not(target_thread_local), target_has_atomic = "64"))]
fn from_u64(v: u64) -> Option<ThreadId> {
NonZero::new(v).map(ThreadId)
}
@ -1261,29 +1256,14 @@ impl ThreadId {
// This module ensures private fields are kept private, which is necessary to enforce the safety requirements.
mod thread_name_string {
use core::str;
use crate::ffi::{CStr, CString};
use crate::str;
/// Like a `String` it's guaranteed UTF-8 and like a `CString` it's null terminated.
pub(crate) struct ThreadNameString {
inner: CString,
}
impl ThreadNameString {
pub fn as_str(&self) -> &str {
// SAFETY: `self.inner` is only initialised via `String`, which upholds the validity invariant of `str`.
unsafe { str::from_utf8_unchecked(self.inner.to_bytes()) }
}
}
impl core::ops::Deref for ThreadNameString {
type Target = CStr;
fn deref(&self) -> &CStr {
&self.inner
}
}
impl From<String> for ThreadNameString {
fn from(s: String) -> Self {
Self {
@ -1291,82 +1271,124 @@ mod thread_name_string {
}
}
}
impl ThreadNameString {
pub fn as_cstr(&self) -> &CStr {
&self.inner
}
pub fn as_str(&self) -> &str {
// SAFETY: `ThreadNameString` is guaranteed to be UTF-8.
unsafe { str::from_utf8_unchecked(self.inner.to_bytes()) }
}
}
}
pub(crate) use thread_name_string::ThreadNameString;
static MAIN_THREAD_INFO: SyncUnsafeCell<(MaybeUninit<ThreadId>, MaybeUninit<Parker>)> =
SyncUnsafeCell::new((MaybeUninit::uninit(), MaybeUninit::uninit()));
use thread_name_string::ThreadNameString;
/// The internal representation of a `Thread` that is not the main thread.
struct OtherInner {
/// Store the ID of the main thread.
///
/// The thread handle for the main thread is created lazily, and this might even
/// happen pre-main. Since not every platform has a way to identify the main
/// thread when that happens macOS's `pthread_main_np` function being a notable
/// exception we cannot assign it the right name right then. Instead, in our
/// runtime startup code, we remember the thread ID of the main thread (through
/// this modules `set` function) and use it to identify the main thread from then
/// on. This works reliably and has the additional advantage that we can report
/// the right thread name on main even after the thread handle has been destroyed.
/// Note however that this also means that the name reported in pre-main functions
/// will be incorrect, but that's just something we have to live with.
pub(crate) mod main_thread {
cfg_if::cfg_if! {
if #[cfg(target_has_atomic = "64")] {
use super::ThreadId;
use crate::sync::atomic::AtomicU64;
use crate::sync::atomic::Ordering::Relaxed;
static MAIN: AtomicU64 = AtomicU64::new(0);
pub(super) fn get() -> Option<ThreadId> {
ThreadId::from_u64(MAIN.load(Relaxed))
}
/// # Safety
/// May only be called once.
pub(crate) unsafe fn set(id: ThreadId) {
MAIN.store(id.as_u64().get(), Relaxed)
}
} else {
use super::ThreadId;
use crate::mem::MaybeUninit;
use crate::sync::atomic::AtomicBool;
use crate::sync::atomic::Ordering::{Acquire, Release};
static INIT: AtomicBool = AtomicBool::new(false);
static mut MAIN: MaybeUninit<ThreadId> = MaybeUninit::uninit();
pub(super) fn get() -> Option<ThreadId> {
if INIT.load(Acquire) {
Some(unsafe { MAIN.assume_init() })
} else {
None
}
}
/// # Safety
/// May only be called once.
pub(crate) unsafe fn set(id: ThreadId) {
unsafe { MAIN = MaybeUninit::new(id) };
INIT.store(true, Release);
}
}
}
}
/// Run a function with the current thread's name.
///
/// Modulo thread local accesses, this function is safe to call from signal
/// handlers and in similar circumstances where allocations are not possible.
pub(crate) fn with_current_name<F, R>(f: F) -> R
where
F: FnOnce(Option<&str>) -> R,
{
try_with_current(|thread| {
if let Some(thread) = thread {
// If there is a current thread handle, try to use the name stored
// there.
if let Some(name) = &thread.inner.name {
return f(Some(name.as_str()));
} else if Some(thread.inner.id) == main_thread::get() {
// The main thread doesn't store its name in the handle, we must
// identify it through its ID. Since we already have the `Thread`,
// we can retrieve the ID from it instead of going through another
// thread local.
return f(Some("main"));
}
} else if let Some(main) = main_thread::get()
&& let Some(id) = current::id::get()
&& id == main
{
// The main thread doesn't always have a thread handle, we must
// identify it through its ID instead. The checks are ordered so
// that the current ID is only loaded if it is actually needed,
// since loading it from TLS might need multiple expensive accesses.
return f(Some("main"));
}
f(None)
})
}
/// The internal representation of a `Thread` handle
struct Inner {
name: Option<ThreadNameString>,
id: ThreadId,
parker: Parker,
}
/// The internal representation of a `Thread` handle.
#[derive(Clone)]
enum Inner {
/// Represents the main thread. May only be constructed by Thread::new_main.
Main(&'static (ThreadId, Parker)),
/// Represents any other thread.
Other(Pin<Arc<OtherInner>>),
}
impl Inner {
fn id(&self) -> ThreadId {
match self {
Self::Main((thread_id, _)) => *thread_id,
Self::Other(other) => other.id,
}
}
fn cname(&self) -> Option<&CStr> {
match self {
Self::Main(_) => Some(c"main"),
Self::Other(other) => other.name.as_deref(),
}
}
fn name(&self) -> Option<&str> {
match self {
Self::Main(_) => Some("main"),
Self::Other(other) => other.name.as_ref().map(ThreadNameString::as_str),
}
}
fn into_raw(self) -> *const () {
match self {
// Just return the pointer to `MAIN_THREAD_INFO`.
Self::Main(ptr) => crate::ptr::from_ref(ptr).cast(),
Self::Other(arc) => {
// Safety: We only expose an opaque pointer, which maintains the `Pin` invariant.
let inner = unsafe { Pin::into_inner_unchecked(arc) };
Arc::into_raw(inner) as *const ()
}
}
}
/// # Safety
///
/// See [`Thread::from_raw`].
unsafe fn from_raw(ptr: *const ()) -> Self {
// If the pointer is to `MAIN_THREAD_INFO`, we know it is the `Main` variant.
if crate::ptr::eq(ptr.cast(), &MAIN_THREAD_INFO) {
Self::Main(unsafe { &*ptr.cast() })
} else {
// Safety: Upheld by caller
Self::Other(unsafe { Pin::new_unchecked(Arc::from_raw(ptr as *const OtherInner)) })
}
}
fn parker(&self) -> Pin<&Parker> {
match self {
Self::Main((_, parker_ref)) => Pin::static_ref(parker_ref),
Self::Other(inner) => unsafe {
Pin::map_unchecked(inner.as_ref(), |inner| &inner.parker)
},
}
fn parker(self: Pin<&Self>) -> Pin<&Parker> {
unsafe { Pin::map_unchecked(self, |inner| &inner.parker) }
}
}
@ -1390,47 +1412,21 @@ impl Inner {
/// docs of [`Builder`] and [`spawn`] for more details.
///
/// [`thread::current`]: current::current
pub struct Thread(Inner);
pub struct Thread {
inner: Pin<Arc<Inner>>,
}
impl Thread {
/// Used only internally to construct a thread object without spawning.
pub(crate) fn new(id: ThreadId, name: String) -> Thread {
Self::new_inner(id, Some(ThreadNameString::from(name)))
}
pub(crate) fn new(id: ThreadId, name: Option<String>) -> Thread {
let name = name.map(ThreadNameString::from);
pub(crate) fn new_unnamed(id: ThreadId) -> Thread {
Self::new_inner(id, None)
}
/// Used in runtime to construct main thread
///
/// # Safety
///
/// This must only ever be called once, and must be called on the main thread.
pub(crate) unsafe fn new_main(thread_id: ThreadId) -> Thread {
// Safety: As this is only called once and on the main thread, nothing else is accessing MAIN_THREAD_INFO
// as the only other read occurs in `main_thread_info` *after* the main thread has been constructed,
// and this function is the only one that constructs the main thread.
//
// Pre-main thread spawning cannot hit this either, as the caller promises that this is only called on the main thread.
let main_thread_info = unsafe { &mut *MAIN_THREAD_INFO.get() };
unsafe { Parker::new_in_place((&raw mut main_thread_info.1).cast()) };
main_thread_info.0.write(thread_id);
// Store a `'static` ref to the initialised ThreadId and Parker,
// to avoid having to repeatedly prove initialisation.
Self(Inner::Main(unsafe { &*MAIN_THREAD_INFO.get().cast() }))
}
fn new_inner(id: ThreadId, name: Option<ThreadNameString>) -> Thread {
// We have to use `unsafe` here to construct the `Parker` in-place,
// which is required for the UNIX implementation.
//
// SAFETY: We pin the Arc immediately after creation, so its address never
// changes.
let inner = unsafe {
let mut arc = Arc::<OtherInner>::new_uninit();
let mut arc = Arc::<Inner>::new_uninit();
let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr();
(&raw mut (*ptr).name).write(name);
(&raw mut (*ptr).id).write(id);
@ -1438,7 +1434,7 @@ impl Thread {
Pin::new_unchecked(arc.assume_init())
};
Self(Inner::Other(inner))
Thread { inner }
}
/// Like the public [`park`], but callable on any handle. This is used to
@ -1447,7 +1443,7 @@ impl Thread {
/// # Safety
/// May only be called from the thread to which this handle belongs.
pub(crate) unsafe fn park(&self) {
unsafe { self.0.parker().park() }
unsafe { self.inner.as_ref().parker().park() }
}
/// Like the public [`park_timeout`], but callable on any handle. This is
@ -1456,7 +1452,7 @@ impl Thread {
/// # Safety
/// May only be called from the thread to which this handle belongs.
pub(crate) unsafe fn park_timeout(&self, dur: Duration) {
unsafe { self.0.parker().park_timeout(dur) }
unsafe { self.inner.as_ref().parker().park_timeout(dur) }
}
/// Atomically makes the handle's token available if it is not already.
@ -1492,7 +1488,7 @@ impl Thread {
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn unpark(&self) {
self.0.parker().unpark();
self.inner.as_ref().parker().unpark();
}
/// Gets the thread's unique identifier.
@ -1512,7 +1508,7 @@ impl Thread {
#[stable(feature = "thread_id", since = "1.19.0")]
#[must_use]
pub fn id(&self) -> ThreadId {
self.0.id()
self.inner.id
}
/// Gets the thread's name.
@ -1555,11 +1551,13 @@ impl Thread {
#[stable(feature = "rust1", since = "1.0.0")]
#[must_use]
pub fn name(&self) -> Option<&str> {
self.0.name()
}
fn cname(&self) -> Option<&CStr> {
self.0.cname()
if let Some(name) = &self.inner.name {
Some(name.as_str())
} else if main_thread::get() == Some(self.inner.id) {
Some("main")
} else {
None
}
}
/// Consumes the `Thread`, returning a raw pointer.
@ -1583,7 +1581,9 @@ impl Thread {
/// ```
#[unstable(feature = "thread_raw", issue = "97523")]
pub fn into_raw(self) -> *const () {
self.0.into_raw()
// Safety: We only expose an opaque pointer, which maintains the `Pin` invariant.
let inner = unsafe { Pin::into_inner_unchecked(self.inner) };
Arc::into_raw(inner) as *const ()
}
/// Constructs a `Thread` from a raw pointer.
@ -1605,7 +1605,17 @@ impl Thread {
#[unstable(feature = "thread_raw", issue = "97523")]
pub unsafe fn from_raw(ptr: *const ()) -> Thread {
// Safety: Upheld by caller.
unsafe { Thread(Inner::from_raw(ptr)) }
unsafe { Thread { inner: Pin::new_unchecked(Arc::from_raw(ptr as *const Inner)) } }
}
fn cname(&self) -> Option<&CStr> {
if let Some(name) = &self.inner.name {
Some(name.as_cstr())
} else if main_thread::get() == Some(self.inner.id) {
Some(c"main")
} else {
None
}
}
}

View File

@ -65,8 +65,6 @@ macro_rules! book {
// NOTE: When adding a book here, make sure to ALSO build the book by
// adding a build step in `src/bootstrap/code/builder/mod.rs`!
// NOTE: Make sure to add the corresponding submodule when adding a new book.
// FIXME: Make checking for a submodule automatic somehow (maybe by having a list of all submodules
// and checking against it?).
book!(
CargoBook, "src/tools/cargo/src/doc", "cargo", &[];
ClippyBook, "src/tools/clippy/book", "clippy", &[];

@ -1 +1 @@
Subproject commit 5a65e2af063ff701ae858f1f7536ee347b3cfe63
Subproject commit 8a0eee28f769387e543882352b12d956aa1b7c38

View File

@ -17,12 +17,12 @@ use rustc_span::symbol::{Symbol, sym};
use thin_vec::{ThinVec, thin_vec};
use tracing::{debug, trace};
use super::Item;
use super::{Item, extract_cfg_from_attrs};
use crate::clean::{
self, Attributes, AttributesExt, ImplKind, ItemId, Type, clean_bound_vars, clean_generics,
clean_impl_item, clean_middle_assoc_item, clean_middle_field, clean_middle_ty,
clean_poly_fn_sig, clean_trait_ref_with_constraints, clean_ty, clean_ty_alias_inner_type,
clean_ty_generics, clean_variant_def, utils,
self, Attributes, ImplKind, ItemId, Type, clean_bound_vars, clean_generics, clean_impl_item,
clean_middle_assoc_item, clean_middle_field, clean_middle_ty, clean_poly_fn_sig,
clean_trait_ref_with_constraints, clean_ty, clean_ty_alias_inner_type, clean_ty_generics,
clean_variant_def, utils,
};
use crate::core::DocContext;
use crate::formats::item_type::ItemType;
@ -408,10 +408,13 @@ pub(crate) fn merge_attrs(
} else {
Attributes::from_hir(&both)
},
both.cfg(cx.tcx, &cx.cache.hidden_cfg),
extract_cfg_from_attrs(both.iter(), cx.tcx, &cx.cache.hidden_cfg),
)
} else {
(Attributes::from_hir(old_attrs), old_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg))
(
Attributes::from_hir(old_attrs),
extract_cfg_from_attrs(old_attrs.iter(), cx.tcx, &cx.cache.hidden_cfg),
)
}
}

View File

@ -186,8 +186,7 @@ fn generate_item_with_correct_attrs(
// For glob re-exports the item may or may not exist to be re-exported (potentially the cfgs
// on the path up until the glob can be removed, and only cfgs on the globbed item itself
// matter), for non-inlined re-exports see #85043.
let is_inline = inline::load_attrs(cx, import_id.to_def_id())
.lists(sym::doc)
let is_inline = hir_attr_lists(inline::load_attrs(cx, import_id.to_def_id()), sym::doc)
.get_word_attr(sym::inline)
.is_some()
|| (is_glob_import(cx.tcx, import_id)
@ -199,8 +198,14 @@ fn generate_item_with_correct_attrs(
// We only keep the item's attributes.
target_attrs.iter().map(|attr| (Cow::Borrowed(attr), None)).collect()
};
let cfg = attrs.cfg(cx.tcx, &cx.cache.hidden_cfg);
let cfg = extract_cfg_from_attrs(
attrs.iter().map(move |(attr, _)| match attr {
Cow::Borrowed(attr) => *attr,
Cow::Owned(attr) => attr,
}),
cx.tcx,
&cx.cache.hidden_cfg,
);
let attrs = Attributes::from_hir_iter(attrs.iter().map(|(attr, did)| (&**attr, *did)), false);
let name = renamed.or(Some(name));
@ -979,13 +984,14 @@ fn clean_proc_macro<'tcx>(
) -> ItemKind {
let attrs = cx.tcx.hir().attrs(item.hir_id());
if kind == MacroKind::Derive
&& let Some(derive_name) = attrs.lists(sym::proc_macro_derive).find_map(|mi| mi.ident())
&& let Some(derive_name) =
hir_attr_lists(attrs, sym::proc_macro_derive).find_map(|mi| mi.ident())
{
*name = derive_name.name;
}
let mut helpers = Vec::new();
for mi in attrs.lists(sym::proc_macro_derive) {
for mi in hir_attr_lists(attrs, sym::proc_macro_derive) {
if !mi.has_name(sym::attributes) {
continue;
}
@ -2985,7 +2991,7 @@ fn clean_use_statement_inner<'tcx>(
let visibility = cx.tcx.visibility(import.owner_id);
let attrs = cx.tcx.hir().attrs(import.hir_id());
let inline_attr = attrs.lists(sym::doc).get_word_attr(sym::inline);
let inline_attr = hir_attr_lists(attrs, sym::doc).get_word_attr(sym::inline);
let pub_underscore = visibility.is_public() && name == kw::Underscore;
let current_mod = cx.tcx.parent_module_from_def_id(import.owner_id.def_id);
let import_def_id = import.owner_id.def_id;

View File

@ -1,4 +1,3 @@
use std::borrow::Cow;
use std::hash::Hash;
use std::path::PathBuf;
use std::sync::{Arc, OnceLock as OnceCell};
@ -6,7 +5,9 @@ use std::{fmt, iter};
use arrayvec::ArrayVec;
use rustc_abi::{ExternAbi, VariantIdx};
use rustc_attr_parsing::{ConstStability, Deprecation, Stability, StableSince};
use rustc_attr_parsing::{
AllowedThroughUnstableModules, ConstStability, Deprecation, Stability, StableSince,
};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId};
@ -406,15 +407,19 @@ impl Item {
// were never supposed to work at all.
let stab = self.stability(tcx)?;
if let rustc_attr_parsing::StabilityLevel::Stable {
allowed_through_unstable_modules: true,
allowed_through_unstable_modules: Some(note),
..
} = stab.level
{
let note = match note {
AllowedThroughUnstableModules::WithDeprecation(note) => Some(note),
// FIXME: Would be better to say *something* here about the *path* being
// deprecated rather than the item.
AllowedThroughUnstableModules::WithoutDeprecation => None,
};
Some(Deprecation {
// FIXME(#131676, #135003): when a note is added to this stability tag,
// translate it here
since: rustc_attr_parsing::DeprecatedSince::Unspecified,
note: None,
note,
suggestion: None,
})
} else {
@ -479,7 +484,7 @@ impl Item {
name,
kind,
Attributes::from_hir(hir_attrs),
hir_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg),
extract_cfg_from_attrs(hir_attrs.iter(), cx.tcx, &cx.cache.hidden_cfg),
)
}
@ -990,147 +995,107 @@ pub(crate) struct Module {
pub(crate) span: Span,
}
pub(crate) trait AttributesExt {
type AttributeIterator<'a>: Iterator<Item = ast::MetaItemInner>
where
Self: 'a;
type Attributes<'a>: Iterator<Item = &'a hir::Attribute>
where
Self: 'a;
pub(crate) fn hir_attr_lists<'a, I: IntoIterator<Item = &'a hir::Attribute>>(
attrs: I,
name: Symbol,
) -> impl Iterator<Item = ast::MetaItemInner> + use<'a, I> {
attrs
.into_iter()
.filter(move |attr| attr.has_name(name))
.filter_map(ast::attr::AttributeExt::meta_item_list)
.flatten()
}
fn lists(&self, name: Symbol) -> Self::AttributeIterator<'_>;
pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute> + Clone>(
attrs: I,
tcx: TyCtxt<'_>,
hidden_cfg: &FxHashSet<Cfg>,
) -> Option<Arc<Cfg>> {
let sess = tcx.sess;
let doc_cfg_active = tcx.features().doc_cfg();
let doc_auto_cfg_active = tcx.features().doc_auto_cfg();
fn iter(&self) -> Self::Attributes<'_>;
fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet<Cfg>) -> Option<Arc<Cfg>> {
let sess = tcx.sess;
let doc_cfg_active = tcx.features().doc_cfg();
let doc_auto_cfg_active = tcx.features().doc_auto_cfg();
fn single<T: IntoIterator>(it: T) -> Option<T::Item> {
let mut iter = it.into_iter();
let item = iter.next()?;
if iter.next().is_some() {
return None;
}
Some(item)
fn single<T: IntoIterator>(it: T) -> Option<T::Item> {
let mut iter = it.into_iter();
let item = iter.next()?;
if iter.next().is_some() {
return None;
}
Some(item)
}
let mut cfg = if doc_cfg_active || doc_auto_cfg_active {
let mut doc_cfg = self
.iter()
.filter(|attr| attr.has_name(sym::doc))
.flat_map(|attr| attr.meta_item_list().unwrap_or_default())
let mut cfg = if doc_cfg_active || doc_auto_cfg_active {
let mut doc_cfg = attrs
.clone()
.filter(|attr| attr.has_name(sym::doc))
.flat_map(|attr| attr.meta_item_list().unwrap_or_default())
.filter(|attr| attr.has_name(sym::cfg))
.peekable();
if doc_cfg.peek().is_some() && doc_cfg_active {
doc_cfg
.filter_map(|attr| Cfg::parse(&attr).ok())
.fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg)
} else if doc_auto_cfg_active {
// If there is no `doc(cfg())`, then we retrieve the `cfg()` attributes (because
// `doc(cfg())` overrides `cfg()`).
attrs
.clone()
.filter(|attr| attr.has_name(sym::cfg))
.peekable();
if doc_cfg.peek().is_some() && doc_cfg_active {
doc_cfg
.filter_map(|attr| Cfg::parse(&attr).ok())
.fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg)
} else if doc_auto_cfg_active {
// If there is no `doc(cfg())`, then we retrieve the `cfg()` attributes (because
// `doc(cfg())` overrides `cfg()`).
self.iter()
.filter(|attr| attr.has_name(sym::cfg))
.filter_map(|attr| single(attr.meta_item_list()?))
.filter_map(|attr| {
Cfg::parse_without(attr.meta_item()?, hidden_cfg).ok().flatten()
})
.fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg)
} else {
Cfg::True
}
.filter_map(|attr| single(attr.meta_item_list()?))
.filter_map(|attr| Cfg::parse_without(attr.meta_item()?, hidden_cfg).ok().flatten())
.fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg)
} else {
Cfg::True
};
}
} else {
Cfg::True
};
for attr in self.iter() {
// #[doc]
if attr.doc_str().is_none() && attr.has_name(sym::doc) {
// #[doc(...)]
if let Some(list) = attr.meta_item_list() {
for item in list {
// #[doc(hidden)]
if !item.has_name(sym::cfg) {
continue;
}
// #[doc(cfg(...))]
if let Some(cfg_mi) = item
.meta_item()
.and_then(|item| rustc_expand::config::parse_cfg(item, sess))
{
match Cfg::parse(cfg_mi) {
Ok(new_cfg) => cfg &= new_cfg,
Err(e) => {
sess.dcx().span_err(e.span, e.msg);
}
for attr in attrs.clone() {
// #[doc]
if attr.doc_str().is_none() && attr.has_name(sym::doc) {
// #[doc(...)]
if let Some(list) = attr.meta_item_list() {
for item in list {
// #[doc(hidden)]
if !item.has_name(sym::cfg) {
continue;
}
// #[doc(cfg(...))]
if let Some(cfg_mi) = item
.meta_item()
.and_then(|item| rustc_expand::config::parse_cfg(item, sess))
{
match Cfg::parse(cfg_mi) {
Ok(new_cfg) => cfg &= new_cfg,
Err(e) => {
sess.dcx().span_err(e.span, e.msg);
}
}
}
}
}
}
}
// treat #[target_feature(enable = "feat")] attributes as if they were
// #[doc(cfg(target_feature = "feat"))] attributes as well
for attr in self.lists(sym::target_feature) {
if attr.has_name(sym::enable) {
if attr.value_str().is_some() {
// Clone `enable = "feat"`, change to `target_feature = "feat"`.
// Unwrap is safe because `value_str` succeeded above.
let mut meta = attr.meta_item().unwrap().clone();
meta.path = ast::Path::from_ident(Ident::with_dummy_span(sym::target_feature));
// treat #[target_feature(enable = "feat")] attributes as if they were
// #[doc(cfg(target_feature = "feat"))] attributes as well
for attr in hir_attr_lists(attrs, sym::target_feature) {
if attr.has_name(sym::enable) {
if attr.value_str().is_some() {
// Clone `enable = "feat"`, change to `target_feature = "feat"`.
// Unwrap is safe because `value_str` succeeded above.
let mut meta = attr.meta_item().unwrap().clone();
meta.path = ast::Path::from_ident(Ident::with_dummy_span(sym::target_feature));
if let Ok(feat_cfg) = Cfg::parse(&ast::MetaItemInner::MetaItem(meta)) {
cfg &= feat_cfg;
}
if let Ok(feat_cfg) = Cfg::parse(&ast::MetaItemInner::MetaItem(meta)) {
cfg &= feat_cfg;
}
}
}
if cfg == Cfg::True { None } else { Some(Arc::new(cfg)) }
}
}
impl AttributesExt for [hir::Attribute] {
type AttributeIterator<'a> = impl Iterator<Item = ast::MetaItemInner> + 'a;
type Attributes<'a> = impl Iterator<Item = &'a hir::Attribute> + 'a;
fn lists(&self, name: Symbol) -> Self::AttributeIterator<'_> {
self.iter()
.filter(move |attr| attr.has_name(name))
.filter_map(ast::attr::AttributeExt::meta_item_list)
.flatten()
}
fn iter(&self) -> Self::Attributes<'_> {
self.iter()
}
}
impl AttributesExt for [(Cow<'_, hir::Attribute>, Option<DefId>)] {
type AttributeIterator<'a>
= impl Iterator<Item = ast::MetaItemInner> + 'a
where
Self: 'a;
type Attributes<'a>
= impl Iterator<Item = &'a hir::Attribute> + 'a
where
Self: 'a;
fn lists(&self, name: Symbol) -> Self::AttributeIterator<'_> {
AttributesExt::iter(self)
.filter(move |attr| attr.has_name(name))
.filter_map(hir::Attribute::meta_item_list)
.flatten()
}
fn iter(&self) -> Self::Attributes<'_> {
self.iter().map(move |(attr, _)| match attr {
Cow::Borrowed(attr) => *attr,
Cow::Owned(attr) => attr,
})
}
if cfg == Cfg::True { None } else { Some(Arc::new(cfg)) }
}
pub(crate) trait NestedAttributesExt {
@ -1196,7 +1161,7 @@ pub(crate) struct Attributes {
impl Attributes {
pub(crate) fn lists(&self, name: Symbol) -> impl Iterator<Item = ast::MetaItemInner> + '_ {
self.other_attrs.lists(name)
hir_attr_lists(&self.other_attrs[..], name)
}
pub(crate) fn has_doc_flag(&self, flag: Symbol) -> bool {
@ -1263,7 +1228,9 @@ impl Attributes {
pub(crate) fn get_doc_aliases(&self) -> Box<[Symbol]> {
let mut aliases = FxIndexSet::default();
for attr in self.other_attrs.lists(sym::doc).filter(|a| a.has_name(sym::alias)) {
for attr in
hir_attr_lists(&self.other_attrs[..], sym::doc).filter(|a| a.has_name(sym::alias))
{
if let Some(values) = attr.meta_item_list() {
for l in values {
if let Some(lit) = l.lit()

View File

@ -13,8 +13,7 @@ use rustc_span::source_map::SourceMap;
use rustc_span::{BytePos, DUMMY_SP, FileName, Pos, Span};
use super::{DocTestVisitor, ScrapedDocTest};
use crate::clean::Attributes;
use crate::clean::types::AttributesExt;
use crate::clean::{Attributes, extract_cfg_from_attrs};
use crate::html::markdown::{self, ErrorCodes, LangString, MdRelLine};
struct RustCollector {
@ -97,7 +96,9 @@ impl HirCollector<'_> {
nested: F,
) {
let ast_attrs = self.tcx.hir().attrs(self.tcx.local_def_id_to_hir_id(def_id));
if let Some(ref cfg) = ast_attrs.cfg(self.tcx, &FxHashSet::default()) {
if let Some(ref cfg) =
extract_cfg_from_attrs(ast_attrs.iter(), self.tcx, &FxHashSet::default())
{
if !cfg.matches(&self.tcx.sess.psess, Some(self.tcx.features())) {
return;
}

View File

@ -316,7 +316,7 @@ impl DocFolder for CacheBuilder<'_, '_> {
let skip_because_unstable = matches!(
item.stability.map(|stab| stab.level),
Some(StabilityLevel::Stable { allowed_through_unstable_modules: true, .. })
Some(StabilityLevel::Stable { allowed_through_unstable_modules: Some(_), .. })
);
if (!self.cache.stripped_mod && !skip_because_unstable) || self.is_json_output {

View File

@ -119,7 +119,7 @@ fn merge_stability(
parent_stability: Option<Stability>,
) -> Option<Stability> {
if let Some(own_stab) = own_stability
&& let StabilityLevel::Stable { since: own_since, allowed_through_unstable_modules: false } =
&& let StabilityLevel::Stable { since: own_since, allowed_through_unstable_modules: None } =
own_stab.level
&& let Some(parent_stab) = parent_stability
&& (parent_stab.is_unstable()
@ -127,12 +127,12 @@ fn merge_stability(
{
parent_stability
} else if let Some(mut own_stab) = own_stability
&& let StabilityLevel::Stable { since, allowed_through_unstable_modules: true } =
&& let StabilityLevel::Stable { since, allowed_through_unstable_modules: Some(_) } =
own_stab.level
&& parent_stability.is_some_and(|stab| stab.is_stable())
{
// this property does not apply transitively through re-exports
own_stab.level = StabilityLevel::Stable { since, allowed_through_unstable_modules: false };
own_stab.level = StabilityLevel::Stable { since, allowed_through_unstable_modules: None };
Some(own_stab)
} else {
own_stability

View File

@ -19,7 +19,7 @@ use tracing::debug;
use crate::clean::cfg::Cfg;
use crate::clean::utils::{inherits_doc_hidden, should_ignore_res};
use crate::clean::{AttributesExt, NestedAttributesExt, reexport_chain};
use crate::clean::{NestedAttributesExt, hir_attr_lists, reexport_chain};
use crate::core;
/// This module is used to store stuff from Rust's AST in a more convenient
@ -247,8 +247,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
let document_hidden = self.cx.render_options.document_hidden;
let use_attrs = tcx.hir().attrs(tcx.local_def_id_to_hir_id(def_id));
// Don't inline `doc(hidden)` imports so they can be stripped at a later stage.
let is_no_inline = use_attrs.lists(sym::doc).has_word(sym::no_inline)
|| (document_hidden && use_attrs.lists(sym::doc).has_word(sym::hidden));
let is_no_inline = hir_attr_lists(use_attrs, sym::doc).has_word(sym::no_inline)
|| (document_hidden && hir_attr_lists(use_attrs, sym::doc).has_word(sym::hidden));
if is_no_inline {
return false;

View File

@ -180,7 +180,7 @@ fn is_stable(cx: &LateContext<'_>, mut def_id: DefId, msrv: &Msrv) -> bool {
if let Some(stability) = cx.tcx.lookup_stability(def_id)
&& let StabilityLevel::Stable {
since,
allowed_through_unstable_modules: false,
allowed_through_unstable_modules: None,
} = stability.level
{
let stable = match since {

View File

@ -1,7 +1,7 @@
//@aux-build:proc_macro_derive.rs
#![warn(clippy::std_instead_of_core)]
#![allow(unused_imports)]
#![allow(unused_imports, deprecated)]
extern crate alloc;

View File

@ -1,7 +1,7 @@
//@aux-build:proc_macro_derive.rs
#![warn(clippy::std_instead_of_core)]
#![allow(unused_imports)]
#![allow(unused_imports, deprecated)]
extern crate alloc;

View File

@ -24,31 +24,31 @@ fn my_vec() -> MyVec<i32> {
#[warn(clippy::useless_transmute)]
unsafe fn _generic<'a, T, U: 'a>(t: &'a T) {
// FIXME: should lint
// let _: &'a T = core::intrinsics::transmute(t);
// let _: &'a T = core::mem::transmute(t);
let _: &'a U = core::intrinsics::transmute(t);
let _: &'a U = core::mem::transmute(t);
let _: *const T = core::intrinsics::transmute(t);
let _: *const T = core::mem::transmute(t);
//~^ ERROR: transmute from a reference to a pointer
//~| NOTE: `-D clippy::useless-transmute` implied by `-D warnings`
let _: *mut T = core::intrinsics::transmute(t);
let _: *mut T = core::mem::transmute(t);
//~^ ERROR: transmute from a reference to a pointer
let _: *const U = core::intrinsics::transmute(t);
let _: *const U = core::mem::transmute(t);
//~^ ERROR: transmute from a reference to a pointer
}
#[warn(clippy::useless_transmute)]
fn useless() {
unsafe {
let _: Vec<i32> = core::intrinsics::transmute(my_vec());
let _: Vec<i32> = core::mem::transmute(my_vec());
//~^ ERROR: transmute from a type (`std::vec::Vec<i32>`) to itself
let _: Vec<i32> = core::mem::transmute(my_vec());
//~^ ERROR: transmute from a type (`std::vec::Vec<i32>`) to itself
let _: Vec<i32> = std::intrinsics::transmute(my_vec());
let _: Vec<i32> = std::mem::transmute(my_vec());
//~^ ERROR: transmute from a type (`std::vec::Vec<i32>`) to itself
let _: Vec<i32> = std::mem::transmute(my_vec());
@ -94,17 +94,17 @@ fn crosspointer() {
let int_mut_ptr: *mut Usize = &mut int as *mut Usize;
unsafe {
let _: Usize = core::intrinsics::transmute(int_const_ptr);
let _: Usize = core::mem::transmute(int_const_ptr);
//~^ ERROR: transmute from a type (`*const Usize`) to the type that it points to (
//~| NOTE: `-D clippy::crosspointer-transmute` implied by `-D warnings`
let _: Usize = core::intrinsics::transmute(int_mut_ptr);
let _: Usize = core::mem::transmute(int_mut_ptr);
//~^ ERROR: transmute from a type (`*mut Usize`) to the type that it points to (`U
let _: *const Usize = core::intrinsics::transmute(my_int());
let _: *const Usize = core::mem::transmute(my_int());
//~^ ERROR: transmute from a type (`Usize`) to a pointer to that type (`*const Usi
let _: *mut Usize = core::intrinsics::transmute(my_int());
let _: *mut Usize = core::mem::transmute(my_int());
//~^ ERROR: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize
}
}

View File

@ -1,8 +1,8 @@
error: transmute from a reference to a pointer
--> tests/ui/transmute.rs:31:23
|
LL | let _: *const T = core::intrinsics::transmute(t);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T`
LL | let _: *const T = core::mem::transmute(t);
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T`
|
= note: `-D clippy::useless-transmute` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::useless_transmute)]`
@ -10,20 +10,20 @@ LL | let _: *const T = core::intrinsics::transmute(t);
error: transmute from a reference to a pointer
--> tests/ui/transmute.rs:35:21
|
LL | let _: *mut T = core::intrinsics::transmute(t);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T`
LL | let _: *mut T = core::mem::transmute(t);
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T`
error: transmute from a reference to a pointer
--> tests/ui/transmute.rs:38:23
|
LL | let _: *const U = core::intrinsics::transmute(t);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U`
LL | let _: *const U = core::mem::transmute(t);
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U`
error: transmute from a type (`std::vec::Vec<i32>`) to itself
--> tests/ui/transmute.rs:45:27
|
LL | let _: Vec<i32> = core::intrinsics::transmute(my_vec());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | let _: Vec<i32> = core::mem::transmute(my_vec());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: transmute from a type (`std::vec::Vec<i32>`) to itself
--> tests/ui/transmute.rs:48:27
@ -34,8 +34,8 @@ LL | let _: Vec<i32> = core::mem::transmute(my_vec());
error: transmute from a type (`std::vec::Vec<i32>`) to itself
--> tests/ui/transmute.rs:51:27
|
LL | let _: Vec<i32> = std::intrinsics::transmute(my_vec());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | let _: Vec<i32> = std::mem::transmute(my_vec());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: transmute from a type (`std::vec::Vec<i32>`) to itself
--> tests/ui/transmute.rs:54:27
@ -64,8 +64,8 @@ LL | let _: *const usize = std::mem::transmute(1 + 1usize);
error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`)
--> tests/ui/transmute.rs:97:24
|
LL | let _: Usize = core::intrinsics::transmute(int_const_ptr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | let _: Usize = core::mem::transmute(int_const_ptr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::crosspointer-transmute` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::crosspointer_transmute)]`
@ -73,20 +73,20 @@ LL | let _: Usize = core::intrinsics::transmute(int_const_ptr);
error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`)
--> tests/ui/transmute.rs:101:24
|
LL | let _: Usize = core::intrinsics::transmute(int_mut_ptr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | let _: Usize = core::mem::transmute(int_mut_ptr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`)
--> tests/ui/transmute.rs:104:31
|
LL | let _: *const Usize = core::intrinsics::transmute(my_int());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | let _: *const Usize = core::mem::transmute(my_int());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`)
--> tests/ui/transmute.rs:107:29
|
LL | let _: *mut Usize = core::intrinsics::transmute(my_int());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | let _: *mut Usize = core::mem::transmute(my_int());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: transmute from a `u8` to a `bool`
--> tests/ui/transmute.rs:114:28

View File

@ -12,15 +12,15 @@
// cdb-check:join_handle,d [Type: std::thread::JoinHandle<tuple$<> >]
// cdb-check: [...] __0 [Type: std::thread::JoinInner<tuple$<> >]
//
// cdb-command:dx -r3 t,d
// cdb-command:dx t,d
// cdb-check:t,d : [...] [Type: std::thread::Thread *]
// cdb-check: [...] __0 : Other [Type: enum2$<std::thread::Inner>]
// cdb-check: [...] __0 [Type: core::pin::Pin<alloc::sync::Arc<std::thread::OtherInner,[...]> >]
// cdb-check:[...] inner [...][Type: core::pin::Pin<alloc::sync::Arc<std::thread::Inner,alloc::alloc::Global> >]
use std::thread;
#[allow(unused_variables)]
fn main() {
fn main()
{
let join_handle = thread::spawn(|| {
println!("Initialize a thread");
});

View File

@ -8,3 +8,4 @@ use core::unicode::UNICODE_VERSION; //~ ERROR use of unstable library feature `u
// Known accidental stabilizations with known users
// fully stable @ core::mem::transmute
use core::intrinsics::transmute; // depended upon by rand_core
//~^WARN deprecated

View File

@ -7,6 +7,14 @@ LL | use core::unicode::UNICODE_VERSION;
= help: add `#![feature(unicode_internals)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 1 previous error
warning: use of deprecated module `std::intrinsics`: import this function via `std::mem` instead
--> $DIR/accidental-stable-in-unstable.rs:10:23
|
LL | use core::intrinsics::transmute; // depended upon by rand_core
| ^^^^^^^^^
|
= note: `#[warn(deprecated)]` on by default
error: aborting due to 1 previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0658`.

View File

@ -6,4 +6,5 @@
extern crate allowed_through_unstable_core;
use allowed_through_unstable_core::unstable_module::OldStableTraitAllowedThoughUnstable;
use allowed_through_unstable_core::unstable_module::OldStableTraitAllowedThoughUnstableWithDeprecation; //~WARN use of deprecated module `allowed_through_unstable_core::unstable_module`: use the new path instead
use allowed_through_unstable_core::unstable_module::NewStableTraitNotAllowedThroughUnstable; //~ ERROR use of unstable library feature `unstable_test_feature`

View File

@ -1,5 +1,13 @@
warning: use of deprecated module `allowed_through_unstable_core::unstable_module`: use the new path instead
--> $DIR/allowed-through-unstable.rs:9:53
|
LL | use allowed_through_unstable_core::unstable_module::OldStableTraitAllowedThoughUnstableWithDeprecation;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(deprecated)]` on by default
error[E0658]: use of unstable library feature `unstable_test_feature`
--> $DIR/allowed-through-unstable.rs:9:5
--> $DIR/allowed-through-unstable.rs:10:5
|
LL | use allowed_through_unstable_core::unstable_module::NewStableTraitNotAllowedThroughUnstable;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -8,6 +16,6 @@ LL | use allowed_through_unstable_core::unstable_module::NewStableTraitNotAllowe
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 1 previous error
error: aborting due to 1 previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0658`.

View File

@ -9,6 +9,10 @@ pub mod unstable_module {
#[rustc_allowed_through_unstable_modules]
pub trait OldStableTraitAllowedThoughUnstable {}
#[stable(feature = "stable_test_feature", since = "1.2.0")]
#[rustc_allowed_through_unstable_modules = "use the new path instead"]
pub trait OldStableTraitAllowedThoughUnstableWithDeprecation {}
#[stable(feature = "stable_test_feature", since = "1.2.0")]
pub trait NewStableTraitNotAllowedThroughUnstable {}
}

View File

@ -0,0 +1,29 @@
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
//@ check-pass
// Ensure we don't have ambiguity when upcasting to two supertraits
// that are identical modulo normalization.
#![feature(trait_upcasting)]
trait Supertrait<T> {
fn method(&self) {}
}
impl<T> Supertrait<T> for () {}
trait Identity {
type Selff;
}
impl<Selff> Identity for Selff {
type Selff = Selff;
}
trait Trait<P>: Supertrait<()> + Supertrait<<P as Identity>::Selff> {}
impl<P> Trait<P> for () {}
fn main() {
let x: &dyn Trait<()> = &();
let x: &dyn Supertrait<()> = x;
}