Skip to content

Commit 597256c

Browse files
authored
Unrolled build for rust-lang#132541
Rollup merge of rust-lang#132541 - RalfJung:const-stable-extern-crate, r=compiler-errors Proper support for cross-crate recursive const stability checks ~~Stacked on top of rust-lang#132492; only the last three commits are new.~~ In a crate without `staged_api` but with `-Zforce-unstable-if-unmarked`, we now subject all functions marked with `#[rustc_const_stable_indirect]` to recursive const stability checks. We require an opt-in so that by default, a crate can be built with `-Zforce-unstable-if-unmarked` and use nightly features as usual. This property is recorded in the crate metadata so when a `staged_api` crate calls such a function, it sees the `#[rustc_const_stable_indirect]` and allows it to be exposed on stable. This, finally, will let us expose `const fn` from hashbrown on stable. The second commit makes const stability more like regular stability: via `check_missing_const_stability`, we ensure that all publicly reachable functions have a const stability attribute -- both in `staged_api` crates and `-Zforce-unstable-if-unmarked` crates. To achieve this, we move around the stability computation so that const stability is computed after regular stability is done. This lets us access the final result of the regular stability computation, which we use so that `const fn` can inherit the regular stability (but only if that is "unstable"). Fortunately, this lets us get rid of an `Option` in `ConstStability`. This is the last PR that I have planned in this series. r? `@compiler-errors`
2 parents 6503543 + 3780496 commit 597256c

18 files changed

+340
-205
lines changed

compiler/rustc_attr/src/builtin.rs

+32-36
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ use rustc_session::lint::BuiltinLintDiag;
1616
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
1717
use rustc_session::parse::feature_err;
1818
use rustc_session::{RustcVersion, Session};
19+
use rustc_span::Span;
1920
use rustc_span::hygiene::Transparency;
2021
use rustc_span::symbol::{Symbol, kw, sym};
21-
use rustc_span::{DUMMY_SP, Span};
2222

2323
use crate::fluent_generated;
2424
use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
@@ -92,9 +92,7 @@ impl Stability {
9292
#[derive(HashStable_Generic)]
9393
pub struct ConstStability {
9494
pub level: StabilityLevel,
95-
/// This can be `None` for functions that do not have an explicit const feature.
96-
/// We still track them for recursive const stability checks.
97-
pub feature: Option<Symbol>,
95+
pub feature: Symbol,
9896
/// This is true iff the `const_stable_indirect` attribute is present.
9997
pub const_stable_indirect: bool,
10098
/// whether the function has a `#[rustc_promotable]` attribute
@@ -272,22 +270,19 @@ pub fn find_stability(
272270

273271
/// Collects stability info from `rustc_const_stable`/`rustc_const_unstable`/`rustc_promotable`
274272
/// attributes in `attrs`. Returns `None` if no stability attributes are found.
275-
///
276-
/// `is_const_fn` indicates whether this is a function marked as `const`.
277273
pub fn find_const_stability(
278274
sess: &Session,
279275
attrs: &[Attribute],
280276
item_sp: Span,
281-
is_const_fn: bool,
282277
) -> Option<(ConstStability, Span)> {
283278
let mut const_stab: Option<(ConstStability, Span)> = None;
284279
let mut promotable = false;
285-
let mut const_stable_indirect = None;
280+
let mut const_stable_indirect = false;
286281

287282
for attr in attrs {
288283
match attr.name_or_empty() {
289284
sym::rustc_promotable => promotable = true,
290-
sym::rustc_const_stable_indirect => const_stable_indirect = Some(attr.span),
285+
sym::rustc_const_stable_indirect => const_stable_indirect = true,
291286
sym::rustc_const_unstable => {
292287
if const_stab.is_some() {
293288
sess.dcx()
@@ -299,7 +294,7 @@ pub fn find_const_stability(
299294
const_stab = Some((
300295
ConstStability {
301296
level,
302-
feature: Some(feature),
297+
feature,
303298
const_stable_indirect: false,
304299
promotable: false,
305300
},
@@ -317,7 +312,7 @@ pub fn find_const_stability(
317312
const_stab = Some((
318313
ConstStability {
319314
level,
320-
feature: Some(feature),
315+
feature,
321316
const_stable_indirect: false,
322317
promotable: false,
323318
},
@@ -340,7 +335,7 @@ pub fn find_const_stability(
340335
}
341336
}
342337
}
343-
if const_stable_indirect.is_some() {
338+
if const_stable_indirect {
344339
match &mut const_stab {
345340
Some((stab, _)) => {
346341
if stab.is_const_unstable() {
@@ -351,36 +346,37 @@ pub fn find_const_stability(
351346
})
352347
}
353348
}
354-
_ => {}
349+
_ => {
350+
// This function has no const stability attribute, but has `const_stable_indirect`.
351+
// We ignore that; unmarked functions are subject to recursive const stability
352+
// checks by default so we do carry out the user's intent.
353+
}
355354
}
356355
}
357-
// Make sure if `const_stable_indirect` is present, that is recorded. Also make sure all `const
358-
// fn` get *some* marker, since we are a staged_api crate and therefore will do recursive const
359-
// stability checks for them. We need to do this because the default for whether an unmarked
360-
// function enforces recursive stability differs between staged-api crates and force-unmarked
361-
// crates: in force-unmarked crates, only functions *explicitly* marked `const_stable_indirect`
362-
// enforce recursive stability. Therefore when `lookup_const_stability` is `None`, we have to
363-
// assume the function does not have recursive stability. All functions that *do* have recursive
364-
// stability must explicitly record this, and so that's what we do for all `const fn` in a
365-
// staged_api crate.
366-
if (is_const_fn || const_stable_indirect.is_some()) && const_stab.is_none() {
367-
let c = ConstStability {
368-
feature: None,
369-
const_stable_indirect: const_stable_indirect.is_some(),
370-
promotable: false,
371-
level: StabilityLevel::Unstable {
372-
reason: UnstableReason::Default,
373-
issue: None,
374-
is_soft: false,
375-
implied_by: None,
376-
},
377-
};
378-
const_stab = Some((c, const_stable_indirect.unwrap_or(DUMMY_SP)));
379-
}
380356

381357
const_stab
382358
}
383359

360+
/// Calculates the const stability for a const function in a `-Zforce-unstable-if-unmarked` crate
361+
/// without the `staged_api` feature.
362+
pub fn unmarked_crate_const_stab(
363+
_sess: &Session,
364+
attrs: &[Attribute],
365+
regular_stab: Stability,
366+
) -> ConstStability {
367+
assert!(regular_stab.level.is_unstable());
368+
// The only attribute that matters here is `rustc_const_stable_indirect`.
369+
// We enforce recursive const stability rules for those functions.
370+
let const_stable_indirect =
371+
attrs.iter().any(|a| a.name_or_empty() == sym::rustc_const_stable_indirect);
372+
ConstStability {
373+
feature: regular_stab.feature,
374+
const_stable_indirect,
375+
promotable: false,
376+
level: regular_stab.level,
377+
}
378+
}
379+
384380
/// Collects stability info from `rustc_default_body_unstable` attributes in `attrs`.
385381
/// Returns `None` if no stability attributes are found.
386382
pub fn find_body_stability(

compiler/rustc_const_eval/src/check_consts/check.rs

+32-13
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use std::assert_matches::assert_matches;
44
use std::borrow::Cow;
55
use std::mem;
6+
use std::num::NonZero;
67
use std::ops::Deref;
78

89
use rustc_attr::{ConstStability, StabilityLevel};
@@ -709,24 +710,26 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
709710

710711
// Intrinsics are language primitives, not regular calls, so treat them separately.
711712
if let Some(intrinsic) = tcx.intrinsic(callee) {
713+
if !tcx.is_const_fn(callee) {
714+
// Non-const intrinsic.
715+
self.check_op(ops::IntrinsicNonConst { name: intrinsic.name });
716+
// If we allowed this, we're in miri-unleashed mode, so we might
717+
// as well skip the remaining checks.
718+
return;
719+
}
712720
// We use `intrinsic.const_stable` to determine if this can be safely exposed to
713721
// stable code, rather than `const_stable_indirect`. This is to make
714722
// `#[rustc_const_stable_indirect]` an attribute that is always safe to add.
715723
// We also ask is_safe_to_expose_on_stable_const_fn; this determines whether the intrinsic
716724
// fallback body is safe to expose on stable.
717725
let is_const_stable = intrinsic.const_stable
718726
|| (!intrinsic.must_be_overridden
719-
&& tcx.is_const_fn(callee)
720727
&& is_safe_to_expose_on_stable_const_fn(tcx, callee));
721728
match tcx.lookup_const_stability(callee) {
722729
None => {
723-
// Non-const intrinsic.
724-
self.check_op(ops::IntrinsicNonConst { name: intrinsic.name });
725-
}
726-
Some(ConstStability { feature: None, .. }) => {
727-
// Intrinsic does not need a separate feature gate (we rely on the
728-
// regular stability checker). However, we have to worry about recursive
729-
// const stability.
730+
// This doesn't need a separate const-stability check -- const-stability equals
731+
// regular stability, and regular stability is checked separately.
732+
// However, we *do* have to worry about *recursive* const stability.
730733
if !is_const_stable && self.enforce_recursive_const_stability() {
731734
self.dcx().emit_err(errors::UnmarkedIntrinsicExposed {
732735
span: self.span,
@@ -735,8 +738,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
735738
}
736739
}
737740
Some(ConstStability {
738-
feature: Some(feature),
739741
level: StabilityLevel::Unstable { .. },
742+
feature,
740743
..
741744
}) => {
742745
self.check_op(ops::IntrinsicUnstable {
@@ -773,7 +776,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
773776
Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => {
774777
// All good.
775778
}
776-
None | Some(ConstStability { feature: None, .. }) => {
779+
None => {
777780
// This doesn't need a separate const-stability check -- const-stability equals
778781
// regular stability, and regular stability is checked separately.
779782
// However, we *do* have to worry about *recursive* const stability.
@@ -787,8 +790,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
787790
}
788791
}
789792
Some(ConstStability {
790-
feature: Some(feature),
791-
level: StabilityLevel::Unstable { implied_by: implied_feature, .. },
793+
level: StabilityLevel::Unstable { implied_by: implied_feature, issue, .. },
794+
feature,
792795
..
793796
}) => {
794797
// An unstable const fn with a feature gate.
@@ -810,7 +813,23 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
810813
// to allow this.
811814
let feature_enabled = callee.is_local()
812815
|| tcx.features().enabled(feature)
813-
|| implied_feature.is_some_and(|f| tcx.features().enabled(f));
816+
|| implied_feature.is_some_and(|f| tcx.features().enabled(f))
817+
|| {
818+
// When we're compiling the compiler itself we may pull in
819+
// crates from crates.io, but those crates may depend on other
820+
// crates also pulled in from crates.io. We want to ideally be
821+
// able to compile everything without requiring upstream
822+
// modifications, so in the case that this looks like a
823+
// `rustc_private` crate (e.g., a compiler crate) and we also have
824+
// the `-Z force-unstable-if-unmarked` flag present (we're
825+
// compiling a compiler crate), then let this missing feature
826+
// annotation slide.
827+
// This matches what we do in `eval_stability_allow_unstable` for
828+
// regular stability.
829+
feature == sym::rustc_private
830+
&& issue == NonZero::new(27812)
831+
&& self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked
832+
};
814833
// We do *not* honor this if we are in the "danger zone": we have to enforce
815834
// recursive const-stability and the callee is not safe-to-expose. In that
816835
// case we need `check_op` to do the check.

compiler/rustc_const_eval/src/check_consts/mod.rs

+11-9
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,11 @@ impl<'mir, 'tcx> ConstCx<'mir, 'tcx> {
5353
}
5454

5555
pub fn enforce_recursive_const_stability(&self) -> bool {
56-
// We can skip this if `staged_api` is not enabled, since in such crates
57-
// `lookup_const_stability` will always be `None`.
56+
// We can skip this if neither `staged_api` nor `-Zforce-unstable-if-unmarked` are enabled,
57+
// since in such crates `lookup_const_stability` will always be `None`.
5858
self.const_kind == Some(hir::ConstContext::ConstFn)
59-
&& self.tcx.features().staged_api()
59+
&& (self.tcx.features().staged_api()
60+
|| self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked)
6061
&& is_safe_to_expose_on_stable_const_fn(self.tcx, self.def_id().to_def_id())
6162
}
6263

@@ -109,14 +110,15 @@ pub fn is_safe_to_expose_on_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> b
109110

110111
match tcx.lookup_const_stability(def_id) {
111112
None => {
112-
// Only marked functions can be trusted. Note that this may be a function in a
113-
// non-staged-API crate where no recursive checks were done!
114-
false
113+
// In a `staged_api` crate, we do enforce recursive const stability for all unmarked
114+
// functions, so we can trust local functions. But in another crate we don't know which
115+
// rules were applied, so we can't trust that.
116+
def_id.is_local() && tcx.features().staged_api()
115117
}
116118
Some(stab) => {
117-
// We consider things safe-to-expose if they are stable, if they don't have any explicit
118-
// const stability attribute, or if they are marked as `const_stable_indirect`.
119-
stab.is_const_stable() || stab.feature.is_none() || stab.const_stable_indirect
119+
// We consider things safe-to-expose if they are stable or if they are marked as
120+
// `const_stable_indirect`.
121+
stab.is_const_stable() || stab.const_stable_indirect
120122
}
121123
}
122124
}

compiler/rustc_expand/src/base.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -866,9 +866,7 @@ impl SyntaxExtension {
866866
})
867867
.unwrap_or_else(|| (None, helper_attrs));
868868
let stability = attr::find_stability(sess, attrs, span);
869-
// We set `is_const_fn` false to avoid getting any implicit const stability.
870-
let const_stability =
871-
attr::find_const_stability(sess, attrs, span, /* is_const_fn */ false);
869+
let const_stability = attr::find_const_stability(sess, attrs, span);
872870
let body_stability = attr::find_body_stability(sess, attrs);
873871
if let Some((_, sp)) = const_stability {
874872
sess.dcx().emit_err(errors::MacroConstStability {

0 commit comments

Comments
 (0)