mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-08 05:08:40 +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 {
|
pub trait AttributeExt: Debug {
|
||||||
fn id(&self) -> AttrId;
|
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 {
|
fn name_or_empty(&self) -> Symbol {
|
||||||
self.ident().unwrap_or_else(Ident::empty).name
|
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.
|
/// The available stability levels.
|
||||||
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
|
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
|
||||||
#[derive(HashStable_Generic)]
|
#[derive(HashStable_Generic)]
|
||||||
@ -137,9 +147,8 @@ pub enum StabilityLevel {
|
|||||||
Stable {
|
Stable {
|
||||||
/// Rust release which stabilized this feature.
|
/// Rust release which stabilized this feature.
|
||||||
since: StableSince,
|
since: StableSince,
|
||||||
/// Is this item allowed to be referred to on stable, despite being contained in unstable
|
/// This is `Some` if this item allowed to be referred to on stable via unstable modules.
|
||||||
/// modules?
|
allowed_through_unstable_modules: Option<AllowedThroughUnstableModules>,
|
||||||
allowed_through_unstable_modules: bool,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@ use rustc_ast::MetaItem;
|
|||||||
use rustc_ast::attr::AttributeExt;
|
use rustc_ast::attr::AttributeExt;
|
||||||
use rustc_ast_pretty::pprust;
|
use rustc_ast_pretty::pprust;
|
||||||
use rustc_attr_data_structures::{
|
use rustc_attr_data_structures::{
|
||||||
ConstStability, DefaultBodyStability, Stability, StabilityLevel, StableSince, UnstableReason,
|
AllowedThroughUnstableModules, ConstStability, DefaultBodyStability, Stability, StabilityLevel,
|
||||||
VERSION_PLACEHOLDER,
|
StableSince, UnstableReason, VERSION_PLACEHOLDER,
|
||||||
};
|
};
|
||||||
use rustc_errors::ErrorGuaranteed;
|
use rustc_errors::ErrorGuaranteed;
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
@ -24,11 +24,16 @@ pub fn find_stability(
|
|||||||
item_sp: Span,
|
item_sp: Span,
|
||||||
) -> Option<(Stability, Span)> {
|
) -> Option<(Stability, Span)> {
|
||||||
let mut stab: Option<(Stability, Span)> = None;
|
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 {
|
for attr in attrs {
|
||||||
match attr.name_or_empty() {
|
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 => {
|
sym::unstable => {
|
||||||
if stab.is_some() {
|
if stab.is_some() {
|
||||||
sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
|
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 {
|
match &mut stab {
|
||||||
Some((
|
Some((
|
||||||
Stability {
|
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()
|
sess.dcx()
|
||||||
.emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp });
|
.emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp });
|
||||||
@ -283,7 +288,7 @@ fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol,
|
|||||||
|
|
||||||
match feature {
|
match feature {
|
||||||
Ok(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))
|
Some((feature, level))
|
||||||
}
|
}
|
||||||
Err(ErrorGuaranteed { .. }) => None,
|
Err(ErrorGuaranteed { .. }) => None,
|
||||||
|
@ -623,7 +623,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||||||
EncodeCrossCrate::No, "allow_internal_unsafe side-steps the unsafe_code lint",
|
EncodeCrossCrate::No, "allow_internal_unsafe side-steps the unsafe_code lint",
|
||||||
),
|
),
|
||||||
rustc_attr!(
|
rustc_attr!(
|
||||||
rustc_allowed_through_unstable_modules, Normal, template!(Word),
|
rustc_allowed_through_unstable_modules, Normal, template!(Word, NameValueStr: "deprecation message"),
|
||||||
WarnFollowing, EncodeCrossCrate::No,
|
WarnFollowing, EncodeCrossCrate::No,
|
||||||
"rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \
|
"rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \
|
||||||
through unstable paths"
|
through unstable paths"
|
||||||
|
@ -249,9 +249,18 @@ fn late_report_deprecation(
|
|||||||
return;
|
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_path = with_no_trimmed_paths!(tcx.def_path_str(def_id));
|
||||||
let def_kind = tcx.def_descr(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 method_span = method_span.unwrap_or(span);
|
||||||
let suggestion =
|
let suggestion =
|
||||||
@ -267,7 +276,7 @@ fn late_report_deprecation(
|
|||||||
note: depr.note,
|
note: depr.note,
|
||||||
since_kind: deprecated_since_kind(is_in_effect, depr.since),
|
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`.
|
/// Result of `TyCtxt::eval_stability`.
|
||||||
@ -377,13 +386,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||||||
// hierarchy.
|
// hierarchy.
|
||||||
let depr_attr = &depr_entry.attr;
|
let depr_attr = &depr_entry.attr;
|
||||||
if !skip || depr_attr.is_since_rustc_version() {
|
if !skip || depr_attr.is_since_rustc_version() {
|
||||||
// Calculating message for lint involves calling `self.def_path_str`.
|
late_report_deprecation(self, depr_attr, span, method_span, id, def_id);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -741,12 +741,14 @@ where
|
|||||||
a_data.principal(),
|
a_data.principal(),
|
||||||
));
|
));
|
||||||
} else if let Some(a_principal) = a_data.principal() {
|
} else if let Some(a_principal) = a_data.principal() {
|
||||||
for new_a_principal in
|
for (idx, new_a_principal) in
|
||||||
elaborate::supertraits(self.cx(), a_principal.with_self_ty(cx, a_ty)).skip(1)
|
elaborate::supertraits(self.cx(), a_principal.with_self_ty(cx, a_ty))
|
||||||
|
.enumerate()
|
||||||
|
.skip(1)
|
||||||
{
|
{
|
||||||
responses.extend(self.consider_builtin_upcast_to_principal(
|
responses.extend(self.consider_builtin_upcast_to_principal(
|
||||||
goal,
|
goal,
|
||||||
CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting),
|
CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting(idx)),
|
||||||
a_data,
|
a_data,
|
||||||
a_region,
|
a_region,
|
||||||
b_data,
|
b_data,
|
||||||
|
@ -5,8 +5,8 @@ use std::mem::replace;
|
|||||||
use std::num::NonZero;
|
use std::num::NonZero;
|
||||||
|
|
||||||
use rustc_attr_parsing::{
|
use rustc_attr_parsing::{
|
||||||
self as attr, ConstStability, DeprecatedSince, Stability, StabilityLevel, StableSince,
|
self as attr, AllowedThroughUnstableModules, ConstStability, DeprecatedSince, Stability,
|
||||||
UnstableReason, VERSION_PLACEHOLDER,
|
StabilityLevel, StableSince, UnstableReason, VERSION_PLACEHOLDER,
|
||||||
};
|
};
|
||||||
use rustc_data_structures::fx::FxIndexMap;
|
use rustc_data_structures::fx::FxIndexMap;
|
||||||
use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet};
|
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::hir::nested_filter;
|
||||||
use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures};
|
use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures};
|
||||||
use rustc_middle::middle::privacy::EffectiveVisibilities;
|
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::query::Providers;
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::TyCtxt;
|
||||||
|
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||||
use rustc_session::lint;
|
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 rustc_span::{Span, Symbol, sym};
|
||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
|
|
||||||
@ -874,42 +879,95 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let is_allowed_through_unstable_modules = |def_id| {
|
if item_is_allowed {
|
||||||
self.tcx.lookup_stability(def_id).is_some_and(|stab| match stab.level {
|
// The item itself is allowed; check whether the path there is also allowed.
|
||||||
StabilityLevel::Stable { allowed_through_unstable_modules, .. } => {
|
let is_allowed_through_unstable_modules: Option<AllowedThroughUnstableModules> =
|
||||||
allowed_through_unstable_modules
|
self.tcx.lookup_stability(def_id).and_then(|stab| match stab.level {
|
||||||
}
|
StabilityLevel::Stable { allowed_through_unstable_modules, .. } => {
|
||||||
_ => false,
|
allowed_through_unstable_modules
|
||||||
})
|
}
|
||||||
};
|
_ => None,
|
||||||
|
});
|
||||||
|
|
||||||
if item_is_allowed && !is_allowed_through_unstable_modules(def_id) {
|
if is_allowed_through_unstable_modules.is_none() {
|
||||||
// Check parent modules stability as well if the item the path refers to is itself
|
// 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
|
// 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
|
// 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.
|
// 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
|
// We check here rather than in `visit_path_segment` to prevent visiting the last
|
||||||
// path segment twice
|
// path segment twice
|
||||||
//
|
//
|
||||||
// We include special cases via #[rustc_allowed_through_unstable_modules] for items
|
// We include special cases via #[rustc_allowed_through_unstable_modules] for items
|
||||||
// that were accidentally stabilized through unstable paths before this check was
|
// that were accidentally stabilized through unstable paths before this check was
|
||||||
// added, such as `core::intrinsics::transmute`
|
// added, such as `core::intrinsics::transmute`
|
||||||
let parents = path.segments.iter().rev().skip(1);
|
let parents = path.segments.iter().rev().skip(1);
|
||||||
for path_segment in parents {
|
for path_segment in parents {
|
||||||
if let Some(def_id) = path_segment.res.opt_def_id() {
|
if let Some(def_id) = path_segment.res.opt_def_id() {
|
||||||
// use `None` for id to prevent deprecation check
|
// use `None` for id to prevent deprecation check
|
||||||
self.tcx.check_stability_allow_unstable(
|
self.tcx.check_stability_allow_unstable(
|
||||||
def_id,
|
def_id,
|
||||||
None,
|
None,
|
||||||
path.span,
|
path.span,
|
||||||
None,
|
None,
|
||||||
if is_unstable_reexport(self.tcx, id) {
|
if is_unstable_reexport(self.tcx, id) {
|
||||||
AllowUnstable::Yes
|
AllowUnstable::Yes
|
||||||
} else {
|
} else {
|
||||||
AllowUnstable::No
|
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(a)),
|
||||||
CandidateSource::BuiltinImpl(BuiltinImplSource::Object(b)),
|
CandidateSource::BuiltinImpl(BuiltinImplSource::Object(b)),
|
||||||
) => a >= 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
|
// 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`.
|
// 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");
|
.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(
|
fn confirm_builtin_unsize_candidate(
|
||||||
|
@ -1895,6 +1895,18 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
|||||||
Some(None) => {}
|
Some(None) => {}
|
||||||
None => return 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.
|
// Finally, handle overlapping user-written impls.
|
||||||
let impls = candidates.iter().filter_map(|c| {
|
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.
|
/// 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
|
/// This can be removed when `feature(dyn_upcasting)` is stabilized, since we only
|
||||||
/// use it to detect when upcasting traits in hir typeck.
|
/// use it to detect when upcasting traits in hir typeck. The index is only used
|
||||||
TraitUpcasting,
|
/// for winnowing.
|
||||||
|
TraitUpcasting(usize),
|
||||||
/// Unsizing a tuple like `(A, B, ..., X)` to `(A, B, ..., Y)` if `X` unsizes to `Y`.
|
/// 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
|
/// 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")]
|
#[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_const_stable(feature = "const_transmute", since = "1.56.0")]
|
||||||
#[rustc_diagnostic_item = "transmute"]
|
#[rustc_diagnostic_item = "transmute"]
|
||||||
#[rustc_nounwind]
|
#[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
|
/// [`Vec::append`]: ../../std/vec/struct.Vec.html#method.append
|
||||||
#[doc(alias = "memcpy")]
|
#[doc(alias = "memcpy")]
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[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")]
|
#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
#[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")]
|
#[doc(alias = "memmove")]
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[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")]
|
#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
#[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")]
|
#[doc(alias = "memset")]
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[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")]
|
#[rustc_const_stable(feature = "const_ptr_write", since = "1.83.0")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
#[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 location = info.location().unwrap();
|
||||||
|
|
||||||
let msg = payload_as_str(info.payload());
|
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)]
|
let write = #[optimize(size)]
|
||||||
|err: &mut dyn crate::io::Write| {
|
|err: &mut dyn crate::io::Write| {
|
||||||
// Use a lock to prevent mixed output in multithreading context.
|
// Use a lock to prevent mixed output in multithreading context.
|
||||||
// Some platforms also require it when printing a backtrace, like `SymFromAddr` on Windows.
|
// Some platforms also require it when printing a backtrace, like `SymFromAddr` on Windows.
|
||||||
let mut lock = backtrace::lock();
|
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| {
|
thread::with_current_name(|name| {
|
||||||
// We add a newline to ensure the panic message appears at the start of a line.
|
let name = name.unwrap_or("<unnamed>");
|
||||||
writeln!(dst, "\nthread '{name}' panicked at {location}:\n{msg}")
|
|
||||||
};
|
|
||||||
|
|
||||||
if write_msg(&mut cursor).is_ok() {
|
// Try to write the panic message to a buffer first to prevent other concurrent outputs
|
||||||
let pos = cursor.position() as usize;
|
// interleaving with it.
|
||||||
let _ = err.write_all(&buffer[0..pos]);
|
let mut buffer = [0u8; 512];
|
||||||
} else {
|
let mut cursor = crate::io::Cursor::new(&mut buffer[..]);
|
||||||
// The message did not fit into the buffer, write it directly instead.
|
|
||||||
let _ = write_msg(err);
|
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);
|
static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ pub use core::panicking::{panic_display, panic_fmt};
|
|||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
use crate::any::Any;
|
use crate::any::Any;
|
||||||
use crate::sync::Once;
|
use crate::sync::Once;
|
||||||
use crate::thread::{self, Thread};
|
use crate::thread::{self, main_thread};
|
||||||
use crate::{mem, panic, sys};
|
use crate::{mem, panic, sys};
|
||||||
|
|
||||||
// Prints to the "panic output", depending on the platform this may be:
|
// 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)
|
sys::init(argc, argv, sigpipe)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set up the current thread handle to give it the right name.
|
// Remember the main thread ID to give it the correct name.
|
||||||
//
|
// SAFETY: this is the only time and place where we call this function.
|
||||||
// When code running before main uses `ReentrantLock` (for example by
|
unsafe { main_thread::set(thread::current_id()) };
|
||||||
// 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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clean up the thread-local runtime state. This *should* be run after all other
|
/// 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
|
// If the faulting address is within the guard page, then we print a
|
||||||
// message saying so and abort.
|
// message saying so and abort.
|
||||||
if start <= addr && addr < end {
|
if start <= addr && addr < end {
|
||||||
rtprintpanic!(
|
thread::with_current_name(|name| {
|
||||||
"\nthread '{}' has overflowed its stack\n",
|
let name = name.unwrap_or("<unknown>");
|
||||||
thread::current().name().unwrap_or("<unknown>")
|
rtprintpanic!("\nthread '{name}' has overflowed its stack\n");
|
||||||
);
|
});
|
||||||
|
|
||||||
rtabort!("stack overflow");
|
rtabort!("stack overflow");
|
||||||
} else {
|
} else {
|
||||||
// Unregister ourselves by reverting back to the default behavior.
|
// 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;
|
let code = rec.ExceptionCode;
|
||||||
|
|
||||||
if code == c::EXCEPTION_STACK_OVERFLOW {
|
if code == c::EXCEPTION_STACK_OVERFLOW {
|
||||||
rtprintpanic!(
|
thread::with_current_name(|name| {
|
||||||
"\nthread '{}' has overflowed its stack\n",
|
let name = name.unwrap_or("<unknown>");
|
||||||
thread::current().name().unwrap_or("<unknown>")
|
rtprintpanic!("\nthread '{name}' has overflowed its stack\n");
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
c::EXCEPTION_CONTINUE_SEARCH
|
c::EXCEPTION_CONTINUE_SEARCH
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ local_pointer! {
|
|||||||
///
|
///
|
||||||
/// We store the thread ID so that it never gets destroyed during the lifetime
|
/// 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.
|
/// of a thread, either using `#[thread_local]` or multiple `local_pointer!`s.
|
||||||
mod id {
|
pub(super) mod id {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
@ -27,7 +27,7 @@ mod id {
|
|||||||
|
|
||||||
pub(super) const CHEAP: bool = true;
|
pub(super) const CHEAP: bool = true;
|
||||||
|
|
||||||
pub(super) fn get() -> Option<ThreadId> {
|
pub(crate) fn get() -> Option<ThreadId> {
|
||||||
ID.get()
|
ID.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ mod id {
|
|||||||
|
|
||||||
pub(super) const CHEAP: bool = false;
|
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 id0 = ID0.get().addr() as u64;
|
||||||
let id16 = ID16.get().addr() as u64;
|
let id16 = ID16.get().addr() as u64;
|
||||||
let id32 = ID32.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) const CHEAP: bool = false;
|
||||||
|
|
||||||
pub(super) fn get() -> Option<ThreadId> {
|
pub(crate) fn get() -> Option<ThreadId> {
|
||||||
let id0 = ID0.get().addr() as u64;
|
let id0 = ID0.get().addr() as u64;
|
||||||
let id32 = ID32.get().addr() as u64;
|
let id32 = ID32.get().addr() as u64;
|
||||||
ThreadId::from_u64((id32 << 32) + id0)
|
ThreadId::from_u64((id32 << 32) + id0)
|
||||||
@ -85,7 +85,7 @@ mod id {
|
|||||||
|
|
||||||
pub(super) const CHEAP: bool = true;
|
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;
|
let id = ID.get().addr() as u64;
|
||||||
ThreadId::from_u64(id)
|
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
|
/// 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.
|
/// 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 {
|
if CURRENT.get() != NONE {
|
||||||
return Err(thread);
|
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
|
// to retrieve it from the current thread handle, which will only take one
|
||||||
// TLS access.
|
// TLS access.
|
||||||
if !id::CHEAP {
|
if !id::CHEAP {
|
||||||
let current = CURRENT.get();
|
if let Some(id) = try_with_current(|t| t.map(|t| t.id())) {
|
||||||
if current > DESTROYED {
|
return id;
|
||||||
unsafe {
|
|
||||||
let current = ManuallyDrop::new(Thread::from_raw(current));
|
|
||||||
return current.id();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
id::get_or_init()
|
id::get_or_init()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a handle to the thread that invokes it, if the handle has been initialized.
|
/// Gets a reference to the handle of the thread that invokes it, if the handle
|
||||||
pub(crate) fn try_current() -> Option<Thread> {
|
/// has been initialized.
|
||||||
|
pub(super) fn try_with_current<F, R>(f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(Option<&Thread>) -> R,
|
||||||
|
{
|
||||||
let current = CURRENT.get();
|
let current = CURRENT.get();
|
||||||
if current > DESTROYED {
|
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 {
|
unsafe {
|
||||||
let current = ManuallyDrop::new(Thread::from_raw(current));
|
let current = ManuallyDrop::new(Thread::from_raw(current));
|
||||||
Some((*current).clone())
|
f(Some(¤t))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
f(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,7 +179,7 @@ pub(crate) fn current_or_unnamed() -> Thread {
|
|||||||
(*current).clone()
|
(*current).clone()
|
||||||
}
|
}
|
||||||
} else if current == DESTROYED {
|
} else if current == DESTROYED {
|
||||||
Thread::new_unnamed(id::get_or_init())
|
Thread::new(id::get_or_init(), None)
|
||||||
} else {
|
} else {
|
||||||
init_current(current)
|
init_current(current)
|
||||||
}
|
}
|
||||||
@ -221,7 +224,7 @@ fn init_current(current: *mut ()) -> Thread {
|
|||||||
CURRENT.set(BUSY);
|
CURRENT.set(BUSY);
|
||||||
// If the thread ID was initialized already, use it.
|
// If the thread ID was initialized already, use it.
|
||||||
let id = id::get_or_init();
|
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
|
// Make sure that `crate::rt::thread_cleanup` will be run, which will
|
||||||
// call `drop_current`.
|
// call `drop_current`.
|
||||||
|
@ -158,12 +158,9 @@
|
|||||||
#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))]
|
#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
use core::cell::SyncUnsafeCell;
|
|
||||||
use core::ffi::CStr;
|
|
||||||
use core::mem::MaybeUninit;
|
|
||||||
|
|
||||||
use crate::any::Any;
|
use crate::any::Any;
|
||||||
use crate::cell::UnsafeCell;
|
use crate::cell::UnsafeCell;
|
||||||
|
use crate::ffi::CStr;
|
||||||
use crate::marker::PhantomData;
|
use crate::marker::PhantomData;
|
||||||
use crate::mem::{self, ManuallyDrop, forget};
|
use crate::mem::{self, ManuallyDrop, forget};
|
||||||
use crate::num::NonZero;
|
use crate::num::NonZero;
|
||||||
@ -186,7 +183,8 @@ mod current;
|
|||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
pub use current::current;
|
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;
|
mod spawnhook;
|
||||||
|
|
||||||
@ -501,10 +499,7 @@ impl Builder {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let id = ThreadId::new();
|
let id = ThreadId::new();
|
||||||
let my_thread = match name {
|
let my_thread = Thread::new(id, name);
|
||||||
Some(name) => Thread::new(id, name),
|
|
||||||
None => Thread::new_unnamed(id),
|
|
||||||
};
|
|
||||||
|
|
||||||
let hooks = if no_hooks {
|
let hooks = if no_hooks {
|
||||||
spawnhook::ChildSpawnHooks::default()
|
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> {
|
fn from_u64(v: u64) -> Option<ThreadId> {
|
||||||
NonZero::new(v).map(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.
|
// This module ensures private fields are kept private, which is necessary to enforce the safety requirements.
|
||||||
mod thread_name_string {
|
mod thread_name_string {
|
||||||
use core::str;
|
|
||||||
|
|
||||||
use crate::ffi::{CStr, CString};
|
use crate::ffi::{CStr, CString};
|
||||||
|
use crate::str;
|
||||||
|
|
||||||
/// Like a `String` it's guaranteed UTF-8 and like a `CString` it's null terminated.
|
/// Like a `String` it's guaranteed UTF-8 and like a `CString` it's null terminated.
|
||||||
pub(crate) struct ThreadNameString {
|
pub(crate) struct ThreadNameString {
|
||||||
inner: CString,
|
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 {
|
impl From<String> for ThreadNameString {
|
||||||
fn from(s: String) -> Self {
|
fn from(s: String) -> Self {
|
||||||
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>)> =
|
use thread_name_string::ThreadNameString;
|
||||||
SyncUnsafeCell::new((MaybeUninit::uninit(), MaybeUninit::uninit()));
|
|
||||||
|
|
||||||
/// The internal representation of a `Thread` that is not the main thread.
|
/// Store the ID of the main thread.
|
||||||
struct OtherInner {
|
///
|
||||||
|
/// 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>,
|
name: Option<ThreadNameString>,
|
||||||
id: ThreadId,
|
id: ThreadId,
|
||||||
parker: Parker,
|
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 {
|
impl Inner {
|
||||||
fn id(&self) -> ThreadId {
|
fn parker(self: Pin<&Self>) -> Pin<&Parker> {
|
||||||
match self {
|
unsafe { Pin::map_unchecked(self, |inner| &inner.parker) }
|
||||||
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)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1390,47 +1412,21 @@ impl Inner {
|
|||||||
/// docs of [`Builder`] and [`spawn`] for more details.
|
/// docs of [`Builder`] and [`spawn`] for more details.
|
||||||
///
|
///
|
||||||
/// [`thread::current`]: current::current
|
/// [`thread::current`]: current::current
|
||||||
pub struct Thread(Inner);
|
pub struct Thread {
|
||||||
|
inner: Pin<Arc<Inner>>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Thread {
|
impl Thread {
|
||||||
/// Used only internally to construct a thread object without spawning.
|
pub(crate) fn new(id: ThreadId, name: Option<String>) -> Thread {
|
||||||
pub(crate) fn new(id: ThreadId, name: String) -> Thread {
|
let name = name.map(ThreadNameString::from);
|
||||||
Self::new_inner(id, Some(ThreadNameString::from(name)))
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
// We have to use `unsafe` here to construct the `Parker` in-place,
|
||||||
// which is required for the UNIX implementation.
|
// which is required for the UNIX implementation.
|
||||||
//
|
//
|
||||||
// SAFETY: We pin the Arc immediately after creation, so its address never
|
// SAFETY: We pin the Arc immediately after creation, so its address never
|
||||||
// changes.
|
// changes.
|
||||||
let inner = unsafe {
|
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();
|
let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr();
|
||||||
(&raw mut (*ptr).name).write(name);
|
(&raw mut (*ptr).name).write(name);
|
||||||
(&raw mut (*ptr).id).write(id);
|
(&raw mut (*ptr).id).write(id);
|
||||||
@ -1438,7 +1434,7 @@ impl Thread {
|
|||||||
Pin::new_unchecked(arc.assume_init())
|
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
|
/// Like the public [`park`], but callable on any handle. This is used to
|
||||||
@ -1447,7 +1443,7 @@ impl Thread {
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
/// May only be called from the thread to which this handle belongs.
|
/// May only be called from the thread to which this handle belongs.
|
||||||
pub(crate) unsafe fn park(&self) {
|
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
|
/// Like the public [`park_timeout`], but callable on any handle. This is
|
||||||
@ -1456,7 +1452,7 @@ impl Thread {
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
/// May only be called from the thread to which this handle belongs.
|
/// May only be called from the thread to which this handle belongs.
|
||||||
pub(crate) unsafe fn park_timeout(&self, dur: Duration) {
|
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.
|
/// 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")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn unpark(&self) {
|
pub fn unpark(&self) {
|
||||||
self.0.parker().unpark();
|
self.inner.as_ref().parker().unpark();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the thread's unique identifier.
|
/// Gets the thread's unique identifier.
|
||||||
@ -1512,7 +1508,7 @@ impl Thread {
|
|||||||
#[stable(feature = "thread_id", since = "1.19.0")]
|
#[stable(feature = "thread_id", since = "1.19.0")]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn id(&self) -> ThreadId {
|
pub fn id(&self) -> ThreadId {
|
||||||
self.0.id()
|
self.inner.id
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the thread's name.
|
/// Gets the thread's name.
|
||||||
@ -1555,11 +1551,13 @@ impl Thread {
|
|||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn name(&self) -> Option<&str> {
|
pub fn name(&self) -> Option<&str> {
|
||||||
self.0.name()
|
if let Some(name) = &self.inner.name {
|
||||||
}
|
Some(name.as_str())
|
||||||
|
} else if main_thread::get() == Some(self.inner.id) {
|
||||||
fn cname(&self) -> Option<&CStr> {
|
Some("main")
|
||||||
self.0.cname()
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consumes the `Thread`, returning a raw pointer.
|
/// Consumes the `Thread`, returning a raw pointer.
|
||||||
@ -1583,7 +1581,9 @@ impl Thread {
|
|||||||
/// ```
|
/// ```
|
||||||
#[unstable(feature = "thread_raw", issue = "97523")]
|
#[unstable(feature = "thread_raw", issue = "97523")]
|
||||||
pub fn into_raw(self) -> *const () {
|
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.
|
/// Constructs a `Thread` from a raw pointer.
|
||||||
@ -1605,7 +1605,17 @@ impl Thread {
|
|||||||
#[unstable(feature = "thread_raw", issue = "97523")]
|
#[unstable(feature = "thread_raw", issue = "97523")]
|
||||||
pub unsafe fn from_raw(ptr: *const ()) -> Thread {
|
pub unsafe fn from_raw(ptr: *const ()) -> Thread {
|
||||||
// Safety: Upheld by caller.
|
// 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
|
// 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`!
|
// adding a build step in `src/bootstrap/code/builder/mod.rs`!
|
||||||
// NOTE: Make sure to add the corresponding submodule when adding a new book.
|
// 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!(
|
book!(
|
||||||
CargoBook, "src/tools/cargo/src/doc", "cargo", &[];
|
CargoBook, "src/tools/cargo/src/doc", "cargo", &[];
|
||||||
ClippyBook, "src/tools/clippy/book", "clippy", &[];
|
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 thin_vec::{ThinVec, thin_vec};
|
||||||
use tracing::{debug, trace};
|
use tracing::{debug, trace};
|
||||||
|
|
||||||
use super::Item;
|
use super::{Item, extract_cfg_from_attrs};
|
||||||
use crate::clean::{
|
use crate::clean::{
|
||||||
self, Attributes, AttributesExt, ImplKind, ItemId, Type, clean_bound_vars, clean_generics,
|
self, Attributes, ImplKind, ItemId, Type, clean_bound_vars, clean_generics, clean_impl_item,
|
||||||
clean_impl_item, clean_middle_assoc_item, clean_middle_field, clean_middle_ty,
|
clean_middle_assoc_item, clean_middle_field, clean_middle_ty, clean_poly_fn_sig,
|
||||||
clean_poly_fn_sig, clean_trait_ref_with_constraints, clean_ty, clean_ty_alias_inner_type,
|
clean_trait_ref_with_constraints, clean_ty, clean_ty_alias_inner_type, clean_ty_generics,
|
||||||
clean_ty_generics, clean_variant_def, utils,
|
clean_variant_def, utils,
|
||||||
};
|
};
|
||||||
use crate::core::DocContext;
|
use crate::core::DocContext;
|
||||||
use crate::formats::item_type::ItemType;
|
use crate::formats::item_type::ItemType;
|
||||||
@ -408,10 +408,13 @@ pub(crate) fn merge_attrs(
|
|||||||
} else {
|
} else {
|
||||||
Attributes::from_hir(&both)
|
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 {
|
} 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
|
// 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
|
// 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.
|
// matter), for non-inlined re-exports see #85043.
|
||||||
let is_inline = inline::load_attrs(cx, import_id.to_def_id())
|
let is_inline = hir_attr_lists(inline::load_attrs(cx, import_id.to_def_id()), sym::doc)
|
||||||
.lists(sym::doc)
|
|
||||||
.get_word_attr(sym::inline)
|
.get_word_attr(sym::inline)
|
||||||
.is_some()
|
.is_some()
|
||||||
|| (is_glob_import(cx.tcx, import_id)
|
|| (is_glob_import(cx.tcx, import_id)
|
||||||
@ -199,8 +198,14 @@ fn generate_item_with_correct_attrs(
|
|||||||
// We only keep the item's attributes.
|
// We only keep the item's attributes.
|
||||||
target_attrs.iter().map(|attr| (Cow::Borrowed(attr), None)).collect()
|
target_attrs.iter().map(|attr| (Cow::Borrowed(attr), None)).collect()
|
||||||
};
|
};
|
||||||
|
let cfg = extract_cfg_from_attrs(
|
||||||
let cfg = attrs.cfg(cx.tcx, &cx.cache.hidden_cfg);
|
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 attrs = Attributes::from_hir_iter(attrs.iter().map(|(attr, did)| (&**attr, *did)), false);
|
||||||
|
|
||||||
let name = renamed.or(Some(name));
|
let name = renamed.or(Some(name));
|
||||||
@ -979,13 +984,14 @@ fn clean_proc_macro<'tcx>(
|
|||||||
) -> ItemKind {
|
) -> ItemKind {
|
||||||
let attrs = cx.tcx.hir().attrs(item.hir_id());
|
let attrs = cx.tcx.hir().attrs(item.hir_id());
|
||||||
if kind == MacroKind::Derive
|
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;
|
*name = derive_name.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut helpers = Vec::new();
|
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) {
|
if !mi.has_name(sym::attributes) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -2985,7 +2991,7 @@ fn clean_use_statement_inner<'tcx>(
|
|||||||
|
|
||||||
let visibility = cx.tcx.visibility(import.owner_id);
|
let visibility = cx.tcx.visibility(import.owner_id);
|
||||||
let attrs = cx.tcx.hir().attrs(import.hir_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 pub_underscore = visibility.is_public() && name == kw::Underscore;
|
||||||
let current_mod = cx.tcx.parent_module_from_def_id(import.owner_id.def_id);
|
let current_mod = cx.tcx.parent_module_from_def_id(import.owner_id.def_id);
|
||||||
let import_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::hash::Hash;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::{Arc, OnceLock as OnceCell};
|
use std::sync::{Arc, OnceLock as OnceCell};
|
||||||
@ -6,7 +5,9 @@ use std::{fmt, iter};
|
|||||||
|
|
||||||
use arrayvec::ArrayVec;
|
use arrayvec::ArrayVec;
|
||||||
use rustc_abi::{ExternAbi, VariantIdx};
|
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_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
|
||||||
use rustc_hir::def::{CtorKind, DefKind, Res};
|
use rustc_hir::def::{CtorKind, DefKind, Res};
|
||||||
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId};
|
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId};
|
||||||
@ -406,15 +407,19 @@ impl Item {
|
|||||||
// were never supposed to work at all.
|
// were never supposed to work at all.
|
||||||
let stab = self.stability(tcx)?;
|
let stab = self.stability(tcx)?;
|
||||||
if let rustc_attr_parsing::StabilityLevel::Stable {
|
if let rustc_attr_parsing::StabilityLevel::Stable {
|
||||||
allowed_through_unstable_modules: true,
|
allowed_through_unstable_modules: Some(note),
|
||||||
..
|
..
|
||||||
} = stab.level
|
} = 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 {
|
Some(Deprecation {
|
||||||
// FIXME(#131676, #135003): when a note is added to this stability tag,
|
|
||||||
// translate it here
|
|
||||||
since: rustc_attr_parsing::DeprecatedSince::Unspecified,
|
since: rustc_attr_parsing::DeprecatedSince::Unspecified,
|
||||||
note: None,
|
note,
|
||||||
suggestion: None,
|
suggestion: None,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@ -479,7 +484,7 @@ impl Item {
|
|||||||
name,
|
name,
|
||||||
kind,
|
kind,
|
||||||
Attributes::from_hir(hir_attrs),
|
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) span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait AttributesExt {
|
pub(crate) fn hir_attr_lists<'a, I: IntoIterator<Item = &'a hir::Attribute>>(
|
||||||
type AttributeIterator<'a>: Iterator<Item = ast::MetaItemInner>
|
attrs: I,
|
||||||
where
|
name: Symbol,
|
||||||
Self: 'a;
|
) -> impl Iterator<Item = ast::MetaItemInner> + use<'a, I> {
|
||||||
type Attributes<'a>: Iterator<Item = &'a hir::Attribute>
|
attrs
|
||||||
where
|
.into_iter()
|
||||||
Self: 'a;
|
.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 single<T: IntoIterator>(it: T) -> Option<T::Item> {
|
||||||
|
let mut iter = it.into_iter();
|
||||||
fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet<Cfg>) -> Option<Arc<Cfg>> {
|
let item = iter.next()?;
|
||||||
let sess = tcx.sess;
|
if iter.next().is_some() {
|
||||||
let doc_cfg_active = tcx.features().doc_cfg();
|
return None;
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
Some(item)
|
||||||
|
}
|
||||||
|
|
||||||
let mut cfg = if doc_cfg_active || doc_auto_cfg_active {
|
let mut cfg = if doc_cfg_active || doc_auto_cfg_active {
|
||||||
let mut doc_cfg = self
|
let mut doc_cfg = attrs
|
||||||
.iter()
|
.clone()
|
||||||
.filter(|attr| attr.has_name(sym::doc))
|
.filter(|attr| attr.has_name(sym::doc))
|
||||||
.flat_map(|attr| attr.meta_item_list().unwrap_or_default())
|
.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))
|
.filter(|attr| attr.has_name(sym::cfg))
|
||||||
.peekable();
|
.filter_map(|attr| single(attr.meta_item_list()?))
|
||||||
if doc_cfg.peek().is_some() && doc_cfg_active {
|
.filter_map(|attr| Cfg::parse_without(attr.meta_item()?, hidden_cfg).ok().flatten())
|
||||||
doc_cfg
|
.fold(Cfg::True, |cfg, new_cfg| cfg & new_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
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Cfg::True
|
Cfg::True
|
||||||
};
|
}
|
||||||
|
} else {
|
||||||
|
Cfg::True
|
||||||
|
};
|
||||||
|
|
||||||
for attr in self.iter() {
|
for attr in attrs.clone() {
|
||||||
// #[doc]
|
// #[doc]
|
||||||
if attr.doc_str().is_none() && attr.has_name(sym::doc) {
|
if attr.doc_str().is_none() && attr.has_name(sym::doc) {
|
||||||
// #[doc(...)]
|
// #[doc(...)]
|
||||||
if let Some(list) = attr.meta_item_list() {
|
if let Some(list) = attr.meta_item_list() {
|
||||||
for item in list {
|
for item in list {
|
||||||
// #[doc(hidden)]
|
// #[doc(hidden)]
|
||||||
if !item.has_name(sym::cfg) {
|
if !item.has_name(sym::cfg) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// #[doc(cfg(...))]
|
// #[doc(cfg(...))]
|
||||||
if let Some(cfg_mi) = item
|
if let Some(cfg_mi) = item
|
||||||
.meta_item()
|
.meta_item()
|
||||||
.and_then(|item| rustc_expand::config::parse_cfg(item, sess))
|
.and_then(|item| rustc_expand::config::parse_cfg(item, sess))
|
||||||
{
|
{
|
||||||
match Cfg::parse(cfg_mi) {
|
match Cfg::parse(cfg_mi) {
|
||||||
Ok(new_cfg) => cfg &= new_cfg,
|
Ok(new_cfg) => cfg &= new_cfg,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
sess.dcx().span_err(e.span, e.msg);
|
sess.dcx().span_err(e.span, e.msg);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// treat #[target_feature(enable = "feat")] attributes as if they were
|
// treat #[target_feature(enable = "feat")] attributes as if they were
|
||||||
// #[doc(cfg(target_feature = "feat"))] attributes as well
|
// #[doc(cfg(target_feature = "feat"))] attributes as well
|
||||||
for attr in self.lists(sym::target_feature) {
|
for attr in hir_attr_lists(attrs, sym::target_feature) {
|
||||||
if attr.has_name(sym::enable) {
|
if attr.has_name(sym::enable) {
|
||||||
if attr.value_str().is_some() {
|
if attr.value_str().is_some() {
|
||||||
// Clone `enable = "feat"`, change to `target_feature = "feat"`.
|
// Clone `enable = "feat"`, change to `target_feature = "feat"`.
|
||||||
// Unwrap is safe because `value_str` succeeded above.
|
// Unwrap is safe because `value_str` succeeded above.
|
||||||
let mut meta = attr.meta_item().unwrap().clone();
|
let mut meta = attr.meta_item().unwrap().clone();
|
||||||
meta.path = ast::Path::from_ident(Ident::with_dummy_span(sym::target_feature));
|
meta.path = ast::Path::from_ident(Ident::with_dummy_span(sym::target_feature));
|
||||||
|
|
||||||
if let Ok(feat_cfg) = Cfg::parse(&ast::MetaItemInner::MetaItem(meta)) {
|
if let Ok(feat_cfg) = Cfg::parse(&ast::MetaItemInner::MetaItem(meta)) {
|
||||||
cfg &= feat_cfg;
|
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<'_> {
|
if cfg == Cfg::True { None } else { Some(Arc::new(cfg)) }
|
||||||
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,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait NestedAttributesExt {
|
pub(crate) trait NestedAttributesExt {
|
||||||
@ -1196,7 +1161,7 @@ pub(crate) struct Attributes {
|
|||||||
|
|
||||||
impl Attributes {
|
impl Attributes {
|
||||||
pub(crate) fn lists(&self, name: Symbol) -> impl Iterator<Item = ast::MetaItemInner> + '_ {
|
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 {
|
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]> {
|
pub(crate) fn get_doc_aliases(&self) -> Box<[Symbol]> {
|
||||||
let mut aliases = FxIndexSet::default();
|
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() {
|
if let Some(values) = attr.meta_item_list() {
|
||||||
for l in values {
|
for l in values {
|
||||||
if let Some(lit) = l.lit()
|
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 rustc_span::{BytePos, DUMMY_SP, FileName, Pos, Span};
|
||||||
|
|
||||||
use super::{DocTestVisitor, ScrapedDocTest};
|
use super::{DocTestVisitor, ScrapedDocTest};
|
||||||
use crate::clean::Attributes;
|
use crate::clean::{Attributes, extract_cfg_from_attrs};
|
||||||
use crate::clean::types::AttributesExt;
|
|
||||||
use crate::html::markdown::{self, ErrorCodes, LangString, MdRelLine};
|
use crate::html::markdown::{self, ErrorCodes, LangString, MdRelLine};
|
||||||
|
|
||||||
struct RustCollector {
|
struct RustCollector {
|
||||||
@ -97,7 +96,9 @@ impl HirCollector<'_> {
|
|||||||
nested: F,
|
nested: F,
|
||||||
) {
|
) {
|
||||||
let ast_attrs = self.tcx.hir().attrs(self.tcx.local_def_id_to_hir_id(def_id));
|
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())) {
|
if !cfg.matches(&self.tcx.sess.psess, Some(self.tcx.features())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -316,7 +316,7 @@ impl DocFolder for CacheBuilder<'_, '_> {
|
|||||||
|
|
||||||
let skip_because_unstable = matches!(
|
let skip_because_unstable = matches!(
|
||||||
item.stability.map(|stab| stab.level),
|
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 {
|
if (!self.cache.stripped_mod && !skip_because_unstable) || self.is_json_output {
|
||||||
|
@ -119,7 +119,7 @@ fn merge_stability(
|
|||||||
parent_stability: Option<Stability>,
|
parent_stability: Option<Stability>,
|
||||||
) -> Option<Stability> {
|
) -> Option<Stability> {
|
||||||
if let Some(own_stab) = own_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
|
own_stab.level
|
||||||
&& let Some(parent_stab) = parent_stability
|
&& let Some(parent_stab) = parent_stability
|
||||||
&& (parent_stab.is_unstable()
|
&& (parent_stab.is_unstable()
|
||||||
@ -127,12 +127,12 @@ fn merge_stability(
|
|||||||
{
|
{
|
||||||
parent_stability
|
parent_stability
|
||||||
} else if let Some(mut own_stab) = own_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
|
own_stab.level
|
||||||
&& parent_stability.is_some_and(|stab| stab.is_stable())
|
&& parent_stability.is_some_and(|stab| stab.is_stable())
|
||||||
{
|
{
|
||||||
// this property does not apply transitively through re-exports
|
// 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)
|
Some(own_stab)
|
||||||
} else {
|
} else {
|
||||||
own_stability
|
own_stability
|
||||||
|
@ -19,7 +19,7 @@ use tracing::debug;
|
|||||||
|
|
||||||
use crate::clean::cfg::Cfg;
|
use crate::clean::cfg::Cfg;
|
||||||
use crate::clean::utils::{inherits_doc_hidden, should_ignore_res};
|
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;
|
use crate::core;
|
||||||
|
|
||||||
/// This module is used to store stuff from Rust's AST in a more convenient
|
/// 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 document_hidden = self.cx.render_options.document_hidden;
|
||||||
let use_attrs = tcx.hir().attrs(tcx.local_def_id_to_hir_id(def_id));
|
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.
|
// 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)
|
let is_no_inline = hir_attr_lists(use_attrs, sym::doc).has_word(sym::no_inline)
|
||||||
|| (document_hidden && use_attrs.lists(sym::doc).has_word(sym::hidden));
|
|| (document_hidden && hir_attr_lists(use_attrs, sym::doc).has_word(sym::hidden));
|
||||||
|
|
||||||
if is_no_inline {
|
if is_no_inline {
|
||||||
return false;
|
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)
|
if let Some(stability) = cx.tcx.lookup_stability(def_id)
|
||||||
&& let StabilityLevel::Stable {
|
&& let StabilityLevel::Stable {
|
||||||
since,
|
since,
|
||||||
allowed_through_unstable_modules: false,
|
allowed_through_unstable_modules: None,
|
||||||
} = stability.level
|
} = stability.level
|
||||||
{
|
{
|
||||||
let stable = match since {
|
let stable = match since {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//@aux-build:proc_macro_derive.rs
|
//@aux-build:proc_macro_derive.rs
|
||||||
|
|
||||||
#![warn(clippy::std_instead_of_core)]
|
#![warn(clippy::std_instead_of_core)]
|
||||||
#![allow(unused_imports)]
|
#![allow(unused_imports, deprecated)]
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//@aux-build:proc_macro_derive.rs
|
//@aux-build:proc_macro_derive.rs
|
||||||
|
|
||||||
#![warn(clippy::std_instead_of_core)]
|
#![warn(clippy::std_instead_of_core)]
|
||||||
#![allow(unused_imports)]
|
#![allow(unused_imports, deprecated)]
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
|
@ -24,31 +24,31 @@ fn my_vec() -> MyVec<i32> {
|
|||||||
#[warn(clippy::useless_transmute)]
|
#[warn(clippy::useless_transmute)]
|
||||||
unsafe fn _generic<'a, T, U: 'a>(t: &'a T) {
|
unsafe fn _generic<'a, T, U: 'a>(t: &'a T) {
|
||||||
// FIXME: should lint
|
// 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
|
//~^ ERROR: transmute from a reference to a pointer
|
||||||
//~| NOTE: `-D clippy::useless-transmute` implied by `-D warnings`
|
//~| 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
|
//~^ 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
|
//~^ ERROR: transmute from a reference to a pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
#[warn(clippy::useless_transmute)]
|
#[warn(clippy::useless_transmute)]
|
||||||
fn useless() {
|
fn useless() {
|
||||||
unsafe {
|
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
|
//~^ ERROR: transmute from a type (`std::vec::Vec<i32>`) to itself
|
||||||
|
|
||||||
let _: Vec<i32> = core::mem::transmute(my_vec());
|
let _: Vec<i32> = core::mem::transmute(my_vec());
|
||||||
//~^ ERROR: transmute from a type (`std::vec::Vec<i32>`) to itself
|
//~^ 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
|
//~^ ERROR: transmute from a type (`std::vec::Vec<i32>`) to itself
|
||||||
|
|
||||||
let _: Vec<i32> = std::mem::transmute(my_vec());
|
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;
|
let int_mut_ptr: *mut Usize = &mut int as *mut Usize;
|
||||||
|
|
||||||
unsafe {
|
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 (
|
//~^ ERROR: transmute from a type (`*const Usize`) to the type that it points to (
|
||||||
//~| NOTE: `-D clippy::crosspointer-transmute` implied by `-D warnings`
|
//~| 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
|
//~^ 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
|
//~^ 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
|
//~^ 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
|
error: transmute from a reference to a pointer
|
||||||
--> tests/ui/transmute.rs:31:23
|
--> tests/ui/transmute.rs:31:23
|
||||||
|
|
|
|
||||||
LL | let _: *const T = core::intrinsics::transmute(t);
|
LL | let _: *const T = core::mem::transmute(t);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T`
|
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T`
|
||||||
|
|
|
|
||||||
= note: `-D clippy::useless-transmute` implied by `-D warnings`
|
= note: `-D clippy::useless-transmute` implied by `-D warnings`
|
||||||
= help: to override `-D warnings` add `#[allow(clippy::useless_transmute)]`
|
= 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
|
error: transmute from a reference to a pointer
|
||||||
--> tests/ui/transmute.rs:35:21
|
--> tests/ui/transmute.rs:35:21
|
||||||
|
|
|
|
||||||
LL | let _: *mut T = core::intrinsics::transmute(t);
|
LL | let _: *mut T = core::mem::transmute(t);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T`
|
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T`
|
||||||
|
|
||||||
error: transmute from a reference to a pointer
|
error: transmute from a reference to a pointer
|
||||||
--> tests/ui/transmute.rs:38:23
|
--> tests/ui/transmute.rs:38:23
|
||||||
|
|
|
|
||||||
LL | let _: *const U = core::intrinsics::transmute(t);
|
LL | let _: *const U = core::mem::transmute(t);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U`
|
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U`
|
||||||
|
|
||||||
error: transmute from a type (`std::vec::Vec<i32>`) to itself
|
error: transmute from a type (`std::vec::Vec<i32>`) to itself
|
||||||
--> tests/ui/transmute.rs:45:27
|
--> 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
|
error: transmute from a type (`std::vec::Vec<i32>`) to itself
|
||||||
--> tests/ui/transmute.rs:48:27
|
--> 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
|
error: transmute from a type (`std::vec::Vec<i32>`) to itself
|
||||||
--> tests/ui/transmute.rs:51:27
|
--> 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
|
error: transmute from a type (`std::vec::Vec<i32>`) to itself
|
||||||
--> tests/ui/transmute.rs:54:27
|
--> 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`)
|
error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`)
|
||||||
--> tests/ui/transmute.rs:97:24
|
--> 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`
|
= note: `-D clippy::crosspointer-transmute` implied by `-D warnings`
|
||||||
= help: to override `-D warnings` add `#[allow(clippy::crosspointer_transmute)]`
|
= 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`)
|
error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`)
|
||||||
--> tests/ui/transmute.rs:101:24
|
--> 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`)
|
error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`)
|
||||||
--> tests/ui/transmute.rs:104:31
|
--> 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`)
|
error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`)
|
||||||
--> tests/ui/transmute.rs:107:29
|
--> 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`
|
error: transmute from a `u8` to a `bool`
|
||||||
--> tests/ui/transmute.rs:114:28
|
--> tests/ui/transmute.rs:114:28
|
||||||
|
@ -12,15 +12,15 @@
|
|||||||
// cdb-check:join_handle,d [Type: std::thread::JoinHandle<tuple$<> >]
|
// cdb-check:join_handle,d [Type: std::thread::JoinHandle<tuple$<> >]
|
||||||
// cdb-check: [...] __0 [Type: std::thread::JoinInner<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:t,d : [...] [Type: std::thread::Thread *]
|
||||||
// cdb-check: [...] __0 : Other [Type: enum2$<std::thread::Inner>]
|
// cdb-check:[...] inner [...][Type: core::pin::Pin<alloc::sync::Arc<std::thread::Inner,alloc::alloc::Global> >]
|
||||||
// cdb-check: [...] __0 [Type: core::pin::Pin<alloc::sync::Arc<std::thread::OtherInner,[...]> >]
|
|
||||||
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn main() {
|
fn main()
|
||||||
|
{
|
||||||
let join_handle = thread::spawn(|| {
|
let join_handle = thread::spawn(|| {
|
||||||
println!("Initialize a thread");
|
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
|
// Known accidental stabilizations with known users
|
||||||
// fully stable @ core::mem::transmute
|
// fully stable @ core::mem::transmute
|
||||||
use core::intrinsics::transmute; // depended upon by rand_core
|
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
|
= 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
|
= 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`.
|
For more information about this error, try `rustc --explain E0658`.
|
||||||
|
@ -6,4 +6,5 @@
|
|||||||
extern crate allowed_through_unstable_core;
|
extern crate allowed_through_unstable_core;
|
||||||
|
|
||||||
use allowed_through_unstable_core::unstable_module::OldStableTraitAllowedThoughUnstable;
|
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`
|
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`
|
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;
|
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
|
= 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
|
= 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`.
|
For more information about this error, try `rustc --explain E0658`.
|
||||||
|
@ -9,6 +9,10 @@ pub mod unstable_module {
|
|||||||
#[rustc_allowed_through_unstable_modules]
|
#[rustc_allowed_through_unstable_modules]
|
||||||
pub trait OldStableTraitAllowedThoughUnstable {}
|
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")]
|
#[stable(feature = "stable_test_feature", since = "1.2.0")]
|
||||||
pub trait NewStableTraitNotAllowedThroughUnstable {}
|
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