Skip to content

Commit ce40196

Browse files
committed
Auto merge of rust-lang#132992 - RalfJung:check-consts-feature-gate, r=compiler-errors
check_consts: fix error requesting feature gate when that gate is not actually needed When working on rust-lang/hashbrown#586 I noticed that the compiler asks for the `rustc_private` feature to be enabled if one forgets to set `rustc_const_stable_indirect` on a function -- but enabling `rustc_private` would not actually help. This fixes the diagnostics. r? `@compiler-errors`
2 parents 76fd471 + 9760983 commit ce40196

File tree

3 files changed

+37
-13
lines changed

3 files changed

+37
-13
lines changed

Diff for: compiler/rustc_const_eval/src/check_consts/check.rs

+23-10
Original file line numberDiff line numberDiff line change
@@ -272,9 +272,18 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
272272
/// context.
273273
pub fn check_op_spanned<O: NonConstOp<'tcx>>(&mut self, op: O, span: Span) {
274274
let gate = match op.status_in_item(self.ccx) {
275-
Status::Unstable { gate, safe_to_expose_on_stable, is_function_call }
276-
if self.tcx.features().enabled(gate) =>
277-
{
275+
Status::Unstable {
276+
gate,
277+
safe_to_expose_on_stable,
278+
is_function_call,
279+
gate_already_checked,
280+
} if gate_already_checked || self.tcx.features().enabled(gate) => {
281+
if gate_already_checked {
282+
assert!(
283+
!safe_to_expose_on_stable,
284+
"setting `gate_already_checked` without `safe_to_expose_on_stable` makes no sense"
285+
);
286+
}
278287
// Generally this is allowed since the feature gate is enabled -- except
279288
// if this function wants to be safe-to-expose-on-stable.
280289
if !safe_to_expose_on_stable
@@ -745,7 +754,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
745754
self.check_op(ops::IntrinsicUnstable {
746755
name: intrinsic.name,
747756
feature,
748-
const_stable: is_const_stable,
757+
const_stable_indirect: is_const_stable,
749758
});
750759
}
751760
Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => {
@@ -800,6 +809,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
800809

801810
// We only honor `span.allows_unstable` aka `#[allow_internal_unstable]` if
802811
// the callee is safe to expose, to avoid bypassing recursive stability.
812+
// This is not ideal since it means the user sees an error, not the macro
813+
// author, but that's also the case if one forgets to set
814+
// `#[allow_internal_unstable]` in the first place. Note that this cannot be
815+
// integrated in the check below since we want to enforce
816+
// `callee_safe_to_expose_on_stable` even if
817+
// `!self.enforce_recursive_const_stability()`.
803818
if (self.span.allows_unstable(feature)
804819
|| implied_feature.is_some_and(|f| self.span.allows_unstable(f)))
805820
&& callee_safe_to_expose_on_stable
@@ -830,15 +845,13 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
830845
&& issue == NonZero::new(27812)
831846
&& self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked
832847
};
833-
// We do *not* honor this if we are in the "danger zone": we have to enforce
834-
// recursive const-stability and the callee is not safe-to-expose. In that
835-
// case we need `check_op` to do the check.
836-
let danger_zone = !callee_safe_to_expose_on_stable
837-
&& self.enforce_recursive_const_stability();
838-
if danger_zone || !feature_enabled {
848+
// Even if the feature is enabled, we still need check_op to double-check
849+
// this if the callee is not safe to expose on stable.
850+
if !feature_enabled || !callee_safe_to_expose_on_stable {
839851
self.check_op(ops::FnCallUnstable {
840852
def_id: callee,
841853
feature,
854+
feature_enabled,
842855
safe_to_expose_on_stable: callee_safe_to_expose_on_stable,
843856
});
844857
}

Diff for: compiler/rustc_const_eval/src/check_consts/ops.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ pub enum Status {
2828
Unstable {
2929
/// The feature that must be enabled to use this operation.
3030
gate: Symbol,
31+
/// Whether the feature gate was already checked (because the logic is a bit more
32+
/// complicated than just checking a single gate).
33+
gate_already_checked: bool,
3134
/// Whether it is allowed to use this operation from stable `const fn`.
3235
/// This will usually be `false`.
3336
safe_to_expose_on_stable: bool,
@@ -82,6 +85,7 @@ impl<'tcx> NonConstOp<'tcx> for ConditionallyConstCall<'tcx> {
8285
// We use the `const_trait_impl` gate for all conditionally-const calls.
8386
Status::Unstable {
8487
gate: sym::const_trait_impl,
88+
gate_already_checked: false,
8589
safe_to_expose_on_stable: false,
8690
// We don't want the "mark the callee as `#[rustc_const_stable_indirect]`" hint
8791
is_function_call: false,
@@ -330,19 +334,24 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
330334
pub(crate) struct FnCallUnstable {
331335
pub def_id: DefId,
332336
pub feature: Symbol,
337+
/// If this is true, then the feature is enabled, but we need to still check if it is safe to
338+
/// expose on stable.
339+
pub feature_enabled: bool,
333340
pub safe_to_expose_on_stable: bool,
334341
}
335342

336343
impl<'tcx> NonConstOp<'tcx> for FnCallUnstable {
337344
fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
338345
Status::Unstable {
339346
gate: self.feature,
347+
gate_already_checked: self.feature_enabled,
340348
safe_to_expose_on_stable: self.safe_to_expose_on_stable,
341349
is_function_call: true,
342350
}
343351
}
344352

345353
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
354+
assert!(!self.feature_enabled);
346355
let mut err = ccx.dcx().create_err(errors::UnstableConstFn {
347356
span,
348357
def_path: ccx.tcx.def_path_str(self.def_id),
@@ -376,14 +385,15 @@ impl<'tcx> NonConstOp<'tcx> for IntrinsicNonConst {
376385
pub(crate) struct IntrinsicUnstable {
377386
pub name: Symbol,
378387
pub feature: Symbol,
379-
pub const_stable: bool,
388+
pub const_stable_indirect: bool,
380389
}
381390

382391
impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable {
383392
fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
384393
Status::Unstable {
385394
gate: self.feature,
386-
safe_to_expose_on_stable: self.const_stable,
395+
gate_already_checked: false,
396+
safe_to_expose_on_stable: self.const_stable_indirect,
387397
// We do *not* want to suggest to mark the intrinsic as `const_stable_indirect`,
388398
// that's not a trivial change!
389399
is_function_call: false,
@@ -410,6 +420,7 @@ impl<'tcx> NonConstOp<'tcx> for Coroutine {
410420
{
411421
Status::Unstable {
412422
gate: sym::const_async_blocks,
423+
gate_already_checked: false,
413424
safe_to_expose_on_stable: false,
414425
is_function_call: false,
415426
}

Diff for: tests/ui/consts/min_const_fn/recursive_const_stab_unstable_if_unmarked.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//@ compile-flags: -Zforce-unstable-if-unmarked
22
//@ edition: 2021
3-
#![feature(const_async_blocks, rustc_attrs, rustc_private)]
3+
#![feature(const_async_blocks, rustc_attrs)]
44

55
pub const fn not_stably_const() {
66
// We need to do something const-unstable here.

0 commit comments

Comments
 (0)