mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-06 20:28:33 +00:00
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:
commit
419b3e2d3e
@ -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
|
||||
}
|
||||
|
@ -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>,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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`.
|
||||
(
|
||||
|
@ -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(
|
||||
|
@ -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| {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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(¤t))
|
||||
}
|
||||
} 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`.
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
});
|
||||
|
@ -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
|
||||
|
@ -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`.
|
||||
|
@ -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`
|
||||
|
@ -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`.
|
||||
|
@ -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 {}
|
||||
}
|
||||
|
29
tests/ui/traits/trait-upcasting/prefer-lower-candidates.rs
Normal file
29
tests/ui/traits/trait-upcasting/prefer-lower-candidates.rs
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user