Skip to content

Commit 369d135

Browse files
Rollup merge of #135003 - RalfJung:deprecate-allowed-through-unstable, r=davidtwco
deprecate `std::intrinsics::transmute` etc, use `std::mem::*` instead The `rustc_allowed_through_unstable_modules` attribute lets users call `std::mem::transmute` as `std::intrinsics::transmute`. The former is a reexport of the latter, and for a long time we didn't properly check stability for reexports, so making this a hard error now would be a breaking change for little gain. But at the same time, `std::intrinsics::transmute` is not the intended path for this function, so I think it is a good idea to show a deprecation warning when that path is used. This PR implements that, for all the functions in `std::intrinsics` that carry the attribute. I assume this will need ``@rust-lang/libs-api`` FCP.
2 parents 4c26dc5 + 7ae494a commit 369d135

File tree

20 files changed

+229
-108
lines changed

20 files changed

+229
-108
lines changed

compiler/rustc_ast/src/attr/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,8 @@ impl MetaItemLit {
723723
pub trait AttributeExt: Debug {
724724
fn id(&self) -> AttrId;
725725

726+
/// For a single-segment attribute (i.e., `#[attr]` and not `#[path::atrr]`),
727+
/// return the name of the attribute, else return the empty identifier.
726728
fn name_or_empty(&self) -> Symbol {
727729
self.ident().unwrap_or_else(Ident::empty).name
728730
}

compiler/rustc_attr_data_structures/src/stability.rs

+12-3
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,16 @@ impl PartialConstStability {
101101
}
102102
}
103103

104+
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
105+
#[derive(HashStable_Generic)]
106+
pub enum AllowedThroughUnstableModules {
107+
/// This does not get a deprecation warning. We still generally would prefer people to use the
108+
/// fully stable path, and a warning will likely be emitted in the future.
109+
WithoutDeprecation,
110+
/// Emit the given deprecation warning.
111+
WithDeprecation(Symbol),
112+
}
113+
104114
/// The available stability levels.
105115
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
106116
#[derive(HashStable_Generic)]
@@ -137,9 +147,8 @@ pub enum StabilityLevel {
137147
Stable {
138148
/// Rust release which stabilized this feature.
139149
since: StableSince,
140-
/// Is this item allowed to be referred to on stable, despite being contained in unstable
141-
/// modules?
142-
allowed_through_unstable_modules: bool,
150+
/// This is `Some` if this item allowed to be referred to on stable via unstable modules.
151+
allowed_through_unstable_modules: Option<AllowedThroughUnstableModules>,
143152
},
144153
}
145154

compiler/rustc_attr_parsing/src/attributes/stability.rs

+13-8
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use rustc_ast::MetaItem;
66
use rustc_ast::attr::AttributeExt;
77
use rustc_ast_pretty::pprust;
88
use rustc_attr_data_structures::{
9-
ConstStability, DefaultBodyStability, Stability, StabilityLevel, StableSince, UnstableReason,
10-
VERSION_PLACEHOLDER,
9+
AllowedThroughUnstableModules, ConstStability, DefaultBodyStability, Stability, StabilityLevel,
10+
StableSince, UnstableReason, VERSION_PLACEHOLDER,
1111
};
1212
use rustc_errors::ErrorGuaranteed;
1313
use rustc_session::Session;
@@ -24,11 +24,16 @@ pub fn find_stability(
2424
item_sp: Span,
2525
) -> Option<(Stability, Span)> {
2626
let mut stab: Option<(Stability, Span)> = None;
27-
let mut allowed_through_unstable_modules = false;
27+
let mut allowed_through_unstable_modules = None;
2828

2929
for attr in attrs {
3030
match attr.name_or_empty() {
31-
sym::rustc_allowed_through_unstable_modules => allowed_through_unstable_modules = true,
31+
sym::rustc_allowed_through_unstable_modules => {
32+
allowed_through_unstable_modules = Some(match attr.value_str() {
33+
Some(msg) => AllowedThroughUnstableModules::WithDeprecation(msg),
34+
None => AllowedThroughUnstableModules::WithoutDeprecation,
35+
})
36+
}
3237
sym::unstable => {
3338
if stab.is_some() {
3439
sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
@@ -56,15 +61,15 @@ pub fn find_stability(
5661
}
5762
}
5863

59-
if allowed_through_unstable_modules {
64+
if let Some(allowed_through_unstable_modules) = allowed_through_unstable_modules {
6065
match &mut stab {
6166
Some((
6267
Stability {
63-
level: StabilityLevel::Stable { allowed_through_unstable_modules, .. },
68+
level: StabilityLevel::Stable { allowed_through_unstable_modules: in_stab, .. },
6469
..
6570
},
6671
_,
67-
)) => *allowed_through_unstable_modules = true,
72+
)) => *in_stab = Some(allowed_through_unstable_modules),
6873
_ => {
6974
sess.dcx()
7075
.emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp });
@@ -283,7 +288,7 @@ fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol,
283288

284289
match feature {
285290
Ok(feature) => {
286-
let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: false };
291+
let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: None };
287292
Some((feature, level))
288293
}
289294
Err(ErrorGuaranteed { .. }) => None,

compiler/rustc_feature/src/builtin_attrs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -623,7 +623,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
623623
EncodeCrossCrate::No, "allow_internal_unsafe side-steps the unsafe_code lint",
624624
),
625625
rustc_attr!(
626-
rustc_allowed_through_unstable_modules, Normal, template!(Word),
626+
rustc_allowed_through_unstable_modules, Normal, template!(Word, NameValueStr: "deprecation message"),
627627
WarnFollowing, EncodeCrossCrate::No,
628628
"rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \
629629
through unstable paths"

compiler/rustc_middle/src/middle/stability.rs

+12-9
Original file line numberDiff line numberDiff line change
@@ -249,9 +249,18 @@ fn late_report_deprecation(
249249
return;
250250
}
251251

252+
let is_in_effect = depr.is_in_effect();
253+
let lint = deprecation_lint(is_in_effect);
254+
255+
// Calculating message for lint involves calling `self.def_path_str`,
256+
// which will by default invoke the expensive `visible_parent_map` query.
257+
// Skip all that work if the lint is allowed anyway.
258+
if tcx.lint_level_at_node(lint, hir_id).0 == Level::Allow {
259+
return;
260+
}
261+
252262
let def_path = with_no_trimmed_paths!(tcx.def_path_str(def_id));
253263
let def_kind = tcx.def_descr(def_id);
254-
let is_in_effect = depr.is_in_effect();
255264

256265
let method_span = method_span.unwrap_or(span);
257266
let suggestion =
@@ -267,7 +276,7 @@ fn late_report_deprecation(
267276
note: depr.note,
268277
since_kind: deprecated_since_kind(is_in_effect, depr.since),
269278
};
270-
tcx.emit_node_span_lint(deprecation_lint(is_in_effect), hir_id, method_span, diag);
279+
tcx.emit_node_span_lint(lint, hir_id, method_span, diag);
271280
}
272281

273282
/// Result of `TyCtxt::eval_stability`.
@@ -377,13 +386,7 @@ impl<'tcx> TyCtxt<'tcx> {
377386
// hierarchy.
378387
let depr_attr = &depr_entry.attr;
379388
if !skip || depr_attr.is_since_rustc_version() {
380-
// Calculating message for lint involves calling `self.def_path_str`.
381-
// Which by default to calculate visible path will invoke expensive `visible_parent_map` query.
382-
// So we skip message calculation altogether, if lint is allowed.
383-
let lint = deprecation_lint(depr_attr.is_in_effect());
384-
if self.lint_level_at_node(lint, id).0 != Level::Allow {
385-
late_report_deprecation(self, depr_attr, span, method_span, id, def_id);
386-
}
389+
late_report_deprecation(self, depr_attr, span, method_span, id, def_id);
387390
}
388391
};
389392
}

compiler/rustc_passes/src/stability.rs

+97-39
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use std::mem::replace;
55
use std::num::NonZero;
66

77
use rustc_attr_parsing::{
8-
self as attr, ConstStability, DeprecatedSince, Stability, StabilityLevel, StableSince,
9-
UnstableReason, VERSION_PLACEHOLDER,
8+
self as attr, AllowedThroughUnstableModules, ConstStability, DeprecatedSince, Stability,
9+
StabilityLevel, StableSince, UnstableReason, VERSION_PLACEHOLDER,
1010
};
1111
use rustc_data_structures::fx::FxIndexMap;
1212
use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet};
@@ -20,11 +20,16 @@ use rustc_hir::{FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant};
2020
use rustc_middle::hir::nested_filter;
2121
use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures};
2222
use rustc_middle::middle::privacy::EffectiveVisibilities;
23-
use rustc_middle::middle::stability::{AllowUnstable, DeprecationEntry, Index};
23+
use rustc_middle::middle::stability::{
24+
AllowUnstable, Deprecated, DeprecationEntry, EvalResult, Index,
25+
};
2426
use rustc_middle::query::Providers;
2527
use rustc_middle::ty::TyCtxt;
28+
use rustc_middle::ty::print::with_no_trimmed_paths;
2629
use rustc_session::lint;
27-
use rustc_session::lint::builtin::{INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED};
30+
use rustc_session::lint::builtin::{
31+
DEPRECATED, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED,
32+
};
2833
use rustc_span::{Span, Symbol, sym};
2934
use tracing::{debug, info};
3035

@@ -874,42 +879,95 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
874879
},
875880
);
876881

877-
let is_allowed_through_unstable_modules = |def_id| {
878-
self.tcx.lookup_stability(def_id).is_some_and(|stab| match stab.level {
879-
StabilityLevel::Stable { allowed_through_unstable_modules, .. } => {
880-
allowed_through_unstable_modules
882+
if item_is_allowed {
883+
// The item itself is allowed; check whether the path there is also allowed.
884+
let is_allowed_through_unstable_modules: Option<AllowedThroughUnstableModules> =
885+
self.tcx.lookup_stability(def_id).and_then(|stab| match stab.level {
886+
StabilityLevel::Stable { allowed_through_unstable_modules, .. } => {
887+
allowed_through_unstable_modules
888+
}
889+
_ => None,
890+
});
891+
892+
if is_allowed_through_unstable_modules.is_none() {
893+
// Check parent modules stability as well if the item the path refers to is itself
894+
// stable. We only emit warnings for unstable path segments if the item is stable
895+
// or allowed because stability is often inherited, so the most common case is that
896+
// both the segments and the item are unstable behind the same feature flag.
897+
//
898+
// We check here rather than in `visit_path_segment` to prevent visiting the last
899+
// path segment twice
900+
//
901+
// We include special cases via #[rustc_allowed_through_unstable_modules] for items
902+
// that were accidentally stabilized through unstable paths before this check was
903+
// added, such as `core::intrinsics::transmute`
904+
let parents = path.segments.iter().rev().skip(1);
905+
for path_segment in parents {
906+
if let Some(def_id) = path_segment.res.opt_def_id() {
907+
// use `None` for id to prevent deprecation check
908+
self.tcx.check_stability_allow_unstable(
909+
def_id,
910+
None,
911+
path.span,
912+
None,
913+
if is_unstable_reexport(self.tcx, id) {
914+
AllowUnstable::Yes
915+
} else {
916+
AllowUnstable::No
917+
},
918+
);
919+
}
881920
}
882-
_ => false,
883-
})
884-
};
885-
886-
if item_is_allowed && !is_allowed_through_unstable_modules(def_id) {
887-
// Check parent modules stability as well if the item the path refers to is itself
888-
// stable. We only emit warnings for unstable path segments if the item is stable
889-
// or allowed because stability is often inherited, so the most common case is that
890-
// both the segments and the item are unstable behind the same feature flag.
891-
//
892-
// We check here rather than in `visit_path_segment` to prevent visiting the last
893-
// path segment twice
894-
//
895-
// We include special cases via #[rustc_allowed_through_unstable_modules] for items
896-
// that were accidentally stabilized through unstable paths before this check was
897-
// added, such as `core::intrinsics::transmute`
898-
let parents = path.segments.iter().rev().skip(1);
899-
for path_segment in parents {
900-
if let Some(def_id) = path_segment.res.opt_def_id() {
901-
// use `None` for id to prevent deprecation check
902-
self.tcx.check_stability_allow_unstable(
903-
def_id,
904-
None,
905-
path.span,
906-
None,
907-
if is_unstable_reexport(self.tcx, id) {
908-
AllowUnstable::Yes
909-
} else {
910-
AllowUnstable::No
911-
},
912-
);
921+
} else if let Some(AllowedThroughUnstableModules::WithDeprecation(deprecation)) =
922+
is_allowed_through_unstable_modules
923+
{
924+
// Similar to above, but we cannot use `check_stability_allow_unstable` as that would
925+
// immediately show the stability error. We just want to know the result and disaplay
926+
// our own kind of error.
927+
let parents = path.segments.iter().rev().skip(1);
928+
for path_segment in parents {
929+
if let Some(def_id) = path_segment.res.opt_def_id() {
930+
// use `None` for id to prevent deprecation check
931+
let eval_result = self.tcx.eval_stability_allow_unstable(
932+
def_id,
933+
None,
934+
path.span,
935+
None,
936+
if is_unstable_reexport(self.tcx, id) {
937+
AllowUnstable::Yes
938+
} else {
939+
AllowUnstable::No
940+
},
941+
);
942+
let is_allowed = matches!(eval_result, EvalResult::Allow);
943+
if !is_allowed {
944+
// Calculating message for lint involves calling `self.def_path_str`,
945+
// which will by default invoke the expensive `visible_parent_map` query.
946+
// Skip all that work if the lint is allowed anyway.
947+
if self.tcx.lint_level_at_node(DEPRECATED, id).0
948+
== lint::Level::Allow
949+
{
950+
return;
951+
}
952+
// Show a deprecation message.
953+
let def_path =
954+
with_no_trimmed_paths!(self.tcx.def_path_str(def_id));
955+
let def_kind = self.tcx.def_descr(def_id);
956+
let diag = Deprecated {
957+
sub: None,
958+
kind: def_kind.to_owned(),
959+
path: def_path,
960+
note: Some(deprecation),
961+
since_kind: lint::DeprecatedSinceKind::InEffect,
962+
};
963+
self.tcx.emit_node_span_lint(
964+
DEPRECATED,
965+
id,
966+
method_span.unwrap_or(path.span),
967+
diag,
968+
);
969+
}
970+
}
913971
}
914972
}
915973
}

library/core/src/intrinsics/mod.rs

+20-4
Original file line numberDiff line numberDiff line change
@@ -1897,7 +1897,11 @@ pub const fn forget<T: ?Sized>(_: T) {
18971897
/// }
18981898
/// ```
18991899
#[stable(feature = "rust1", since = "1.0.0")]
1900-
#[rustc_allowed_through_unstable_modules]
1900+
#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)]
1901+
#[cfg_attr(
1902+
not(bootstrap),
1903+
rustc_allowed_through_unstable_modules = "import this function via `std::mem` instead"
1904+
)]
19011905
#[rustc_const_stable(feature = "const_transmute", since = "1.56.0")]
19021906
#[rustc_diagnostic_item = "transmute"]
19031907
#[rustc_nounwind]
@@ -4325,7 +4329,11 @@ pub const fn ptr_metadata<P: ptr::Pointee<Metadata = M> + ?Sized, M>(_ptr: *cons
43254329
/// [`Vec::append`]: ../../std/vec/struct.Vec.html#method.append
43264330
#[doc(alias = "memcpy")]
43274331
#[stable(feature = "rust1", since = "1.0.0")]
4328-
#[rustc_allowed_through_unstable_modules]
4332+
#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)]
4333+
#[cfg_attr(
4334+
not(bootstrap),
4335+
rustc_allowed_through_unstable_modules = "import this function via `std::mem` instead"
4336+
)]
43294337
#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")]
43304338
#[inline(always)]
43314339
#[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
44294437
/// ```
44304438
#[doc(alias = "memmove")]
44314439
#[stable(feature = "rust1", since = "1.0.0")]
4432-
#[rustc_allowed_through_unstable_modules]
4440+
#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)]
4441+
#[cfg_attr(
4442+
not(bootstrap),
4443+
rustc_allowed_through_unstable_modules = "import this function via `std::mem` instead"
4444+
)]
44334445
#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")]
44344446
#[inline(always)]
44354447
#[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) {
45124524
/// ```
45134525
#[doc(alias = "memset")]
45144526
#[stable(feature = "rust1", since = "1.0.0")]
4515-
#[rustc_allowed_through_unstable_modules]
4527+
#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)]
4528+
#[cfg_attr(
4529+
not(bootstrap),
4530+
rustc_allowed_through_unstable_modules = "import this function via `std::mem` instead"
4531+
)]
45164532
#[rustc_const_stable(feature = "const_ptr_write", since = "1.83.0")]
45174533
#[inline(always)]
45184534
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces

src/librustdoc/clean/types.rs

+11-5
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ use std::{fmt, iter};
66

77
use arrayvec::ArrayVec;
88
use rustc_abi::{ExternAbi, VariantIdx};
9-
use rustc_attr_parsing::{ConstStability, Deprecation, Stability, StableSince};
9+
use rustc_attr_parsing::{
10+
AllowedThroughUnstableModules, ConstStability, Deprecation, Stability, StableSince,
11+
};
1012
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
1113
use rustc_hir::def::{CtorKind, DefKind, Res};
1214
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId};
@@ -406,15 +408,19 @@ impl Item {
406408
// were never supposed to work at all.
407409
let stab = self.stability(tcx)?;
408410
if let rustc_attr_parsing::StabilityLevel::Stable {
409-
allowed_through_unstable_modules: true,
411+
allowed_through_unstable_modules: Some(note),
410412
..
411413
} = stab.level
412414
{
415+
let note = match note {
416+
AllowedThroughUnstableModules::WithDeprecation(note) => Some(note),
417+
// FIXME: Would be better to say *something* here about the *path* being
418+
// deprecated rather than the item.
419+
AllowedThroughUnstableModules::WithoutDeprecation => None,
420+
};
413421
Some(Deprecation {
414-
// FIXME(#131676, #135003): when a note is added to this stability tag,
415-
// translate it here
416422
since: rustc_attr_parsing::DeprecatedSince::Unspecified,
417-
note: None,
423+
note,
418424
suggestion: None,
419425
})
420426
} else {

0 commit comments

Comments
 (0)