Skip to content

Commit b9af36e

Browse files
committed
Clarify/fix const-unstable semantics in the presence of multiple unstable features
1 parent d9915da commit b9af36e

File tree

8 files changed

+50
-49
lines changed

8 files changed

+50
-49
lines changed

compiler/rustc_attr/src/builtin.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ impl Stability {
9696
}
9797

9898
/// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes.
99+
/// For details see [the dev guide](https://rustc-dev-guide.rust-lang.org/stability.html#rustc_const_unstable).
99100
#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)]
100101
#[derive(HashStable_Generic)]
101102
pub struct ConstStability {
@@ -115,8 +116,8 @@ impl ConstStability {
115116
self.level.is_const_stable()
116117
}
117118

118-
pub fn is_implicit(&self) -> bool {
119-
self.level.is_implicit()
119+
pub fn is_implicit_unstable(&self) -> bool {
120+
self.level.is_implicit_unstable()
120121
}
121122

122123
pub fn unstable_features(&self) -> impl Iterator<Item = Symbol> + use<'_> {
@@ -154,7 +155,6 @@ pub enum StabilityLevel {
154155
}
155156

156157
/// The available const-stability levels for const functions.
157-
/// For details see [#131349](https://github.com/rust-lang/rust/pull/131349).
158158
#[derive(Encodable, Decodable, PartialEq, Clone, Debug, Eq, Hash)]
159159
#[derive(HashStable_Generic)]
160160
pub enum ConstStabilityLevel {
@@ -169,7 +169,7 @@ pub enum ConstStabilityLevel {
169169
},
170170
/// For functions with no explicit const-stability attribute that require checking recursive
171171
/// const stability. This is either an unmarked const fn or a `const_stable_indirect` intrinsic.
172-
Implicit,
172+
ImplicitUnstable,
173173
}
174174

175175
/// Rust release in which a feature is stabilized.
@@ -224,15 +224,15 @@ impl StabilityLevel {
224224

225225
impl ConstStabilityLevel {
226226
pub fn is_const_unstable(&self) -> bool {
227-
matches!(self, ConstStabilityLevel::Unstable { .. })
227+
matches!(self, ConstStabilityLevel::Unstable { .. } | ConstStabilityLevel::ImplicitUnstable)
228228
}
229229

230230
pub fn is_const_stable(&self) -> bool {
231231
matches!(self, ConstStabilityLevel::Stable { .. })
232232
}
233233

234-
pub fn is_implicit(&self) -> bool {
235-
matches!(self, ConstStabilityLevel::Implicit)
234+
pub fn is_implicit_unstable(&self) -> bool {
235+
matches!(self, ConstStabilityLevel::ImplicitUnstable)
236236
}
237237

238238
pub fn unstable_features(&self) -> impl Iterator<Item = Symbol> + use<'_> {
@@ -428,10 +428,10 @@ pub fn find_const_stability(
428428
// record this, and so that's what we do for all `const fn` in a staged_api crate.
429429
if is_const_fn || const_stable_indirect.is_some() {
430430
stab_spans = ConstStabilitySpans(smallvec![(
431-
ConstStabilityLevel::Implicit,
431+
ConstStabilityLevel::ImplicitUnstable,
432432
const_stable_indirect.unwrap_or(DUMMY_SP)
433433
)]);
434-
ConstStabilityLevel::Implicit
434+
ConstStabilityLevel::ImplicitUnstable
435435
} else {
436436
return None;
437437
}

compiler/rustc_const_eval/src/check_consts/check.rs

+21-26
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ use rustc_span::{Span, Symbol, sym};
2323
use rustc_trait_selection::traits::{
2424
Obligation, ObligationCause, ObligationCauseCode, ObligationCtxt,
2525
};
26-
use smallvec::SmallVec;
2726
use tracing::{debug, instrument, trace};
2827

2928
use super::ops::{self, NonConstOp, Status};
@@ -761,7 +760,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
761760
// Non-const intrinsic.
762761
self.check_op(ops::IntrinsicNonConst { name: intrinsic.name });
763762
}
764-
Some(ConstStability { level: ConstStabilityLevel::Implicit, .. }) => {
763+
Some(ConstStability {
764+
level: ConstStabilityLevel::ImplicitUnstable,
765+
..
766+
}) => {
765767
// Intrinsic does not need a separate feature gate (we rely on the
766768
// regular stability checker). However, we have to worry about recursive
767769
// const stability.
@@ -815,7 +817,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
815817
Some(ConstStability { level: ConstStabilityLevel::Stable { .. }, .. }) => {
816818
// All good.
817819
}
818-
None | Some(ConstStability { level: ConstStabilityLevel::Implicit, .. }) => {
820+
None
821+
| Some(ConstStability {
822+
level: ConstStabilityLevel::ImplicitUnstable, ..
823+
}) => {
819824
// This doesn't need a separate const-stability check -- const-stability equals
820825
// regular stability, and regular stability is checked separately.
821826
// However, we *do* have to worry about *recursive* const stability.
@@ -838,43 +843,33 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
838843

839844
// We only honor `span.allows_unstable` aka `#[allow_internal_unstable]` if
840845
// the callee is safe to expose, to avoid bypassing recursive stability.
841-
let is_allowed_unstable = |u: &rustc_attr::Unstability| {
842-
callee_safe_to_expose_on_stable
843-
&& (self.span.allows_unstable(u.feature)
844-
|| u.implied_by.is_some_and(|f| self.span.allows_unstable(f)))
845-
};
846-
let mut needs_check =
847-
unstables.iter().filter(|u| !is_allowed_unstable(u)).peekable();
848-
if needs_check.peek().is_none() {
846+
if callee_safe_to_expose_on_stable
847+
&& unstables.iter().all(|u| {
848+
self.span.allows_unstable(u.feature)
849+
|| u.implied_by.is_some_and(|f| self.span.allows_unstable(f))
850+
})
851+
{
849852
return;
850853
}
851854

852855
// We can't use `check_op` to check whether the feature is enabled because
853856
// the logic is a bit different than elsewhere: local functions don't need
854857
// the feature gate, and there might be an "implied" gate that also suffices
855858
// to allow this.
856-
let is_feature_enabled = |u: &rustc_attr::Unstability| {
857-
callee.is_local()
858-
|| tcx.features().enabled(u.feature)
859-
|| u.implied_by.is_some_and(|f| tcx.features().enabled(f))
860-
};
859+
let features_enabled = callee.is_local()
860+
|| unstables.iter().all(|u| {
861+
tcx.features().enabled(u.feature)
862+
|| u.implied_by.is_some_and(|f| tcx.features().enabled(f))
863+
});
861864
// We do *not* honor this if we are in the "danger zone": we have to enforce
862865
// recursive const-stability and the callee is not safe-to-expose. In that
863866
// case we need `check_op` to do the check.
864867
let danger_zone = !callee_safe_to_expose_on_stable
865868
&& self.enforce_recursive_const_stability();
866-
let missing_features: SmallVec<[_; 1]> = if danger_zone {
867-
needs_check.map(|u| u.into()).collect()
868-
} else {
869-
needs_check
870-
.filter(|u| !is_feature_enabled(u))
871-
.map(|u| u.into())
872-
.collect()
873-
};
874-
if !missing_features.is_empty() {
869+
if danger_zone || !features_enabled {
875870
self.check_op(ops::FnCallUnstable {
876871
def_id: callee,
877-
features: missing_features,
872+
features: unstables.iter().map(|u| u.into()).collect(),
878873
reason: *reason,
879874
safe_to_expose_on_stable: callee_safe_to_expose_on_stable,
880875
});

compiler/rustc_const_eval/src/check_consts/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ pub fn is_safe_to_expose_on_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> b
116116
Some(stab) => {
117117
// We consider things safe-to-expose if they are stable, if they don't have any explicit
118118
// const stability attribute, or if they are marked as `const_stable_indirect`.
119-
stab.is_const_stable() || stab.is_implicit() || stab.const_stable_indirect
119+
stab.is_const_stable() || stab.is_implicit_unstable() || stab.const_stable_indirect
120120
}
121121
}
122122
}

compiler/rustc_const_eval/src/check_consts/ops.rs

+14-6
Original file line numberDiff line numberDiff line change
@@ -325,15 +325,19 @@ impl<'tcx> NonConstOp<'tcx> for FnCallUnstable {
325325
}
326326

327327
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
328+
// Only report the features that aren't already enabled.
329+
let missing_features = Vec::from_iter(
330+
self.features.iter().filter(|d| !ccx.tcx.features().enabled(d.feature)).copied(),
331+
);
328332
ccx.dcx().create_err(errors::UnstableConstFn {
329333
span,
330334
def_path: ccx.tcx.def_path_str(self.def_id),
331-
features: stability::unstable_message(&self.features, self.reason.to_opt_reason())
335+
features: stability::unstable_message(&missing_features, self.reason.to_opt_reason())
332336
.hide_features_on_nightly(&ccx.tcx.sess),
333-
issues: stability::unstable_issues(&self.features),
337+
issues: stability::unstable_issues(&missing_features),
334338
nightly_subdiags: stability::unstable_nightly_subdiags(
335339
&ccx.tcx.sess,
336-
&self.features,
340+
&missing_features,
337341
None,
338342
),
339343
})
@@ -377,15 +381,19 @@ impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable {
377381
}
378382

379383
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
384+
// Only report the features that aren't already enabled.
385+
let missing_features = Vec::from_iter(
386+
self.features.iter().filter(|d| !ccx.tcx.features().enabled(d.feature)).copied(),
387+
);
380388
ccx.dcx().create_err(errors::UnstableIntrinsic {
381389
span,
382390
name: self.name,
383-
features: stability::unstable_message(&self.features, self.reason.to_opt_reason())
391+
features: stability::unstable_message(&missing_features, self.reason.to_opt_reason())
384392
.hide_features_on_nightly(&ccx.tcx.sess),
385-
issues: stability::unstable_issues(&self.features),
393+
issues: stability::unstable_issues(&missing_features),
386394
nightly_subdiags: stability::unstable_nightly_subdiags(
387395
&ccx.tcx.sess,
388-
&self.features,
396+
&missing_features,
389397
None,
390398
),
391399
})

compiler/rustc_middle/src/middle/stability.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ pub enum EvalResult {
362362
}
363363

364364
/// An instance of a disabled feature required for an unstable item
365-
#[derive(Debug)]
365+
#[derive(Clone, Copy, Debug)]
366366
pub struct EvalDenial {
367367
pub feature: Symbol,
368368
pub issue: Option<NonZero<u32>>,

compiler/rustc_passes/src/stability.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,7 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> {
600600
let is_stable =
601601
self.tcx.lookup_stability(def_id).is_some_and(|stability| stability.level.is_stable());
602602
let missing_const_stability_attribute =
603-
self.tcx.lookup_const_stability(def_id).is_none_or(|s| s.is_implicit());
603+
self.tcx.lookup_const_stability(def_id).is_none_or(|s| s.is_implicit_unstable());
604604

605605
if is_const && is_stable && missing_const_stability_attribute {
606606
let descr = self.tcx.def_descr(def_id.to_def_id());

src/librustdoc/html/format.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -1700,14 +1700,12 @@ pub(crate) fn print_constness_with_space(
17001700
| (_, None)
17011701
// ...or when const unstable, but overall unstable too...
17021702
| (None, Some(ConstStability { level: ConstStabilityLevel::Unstable { .. }, .. }))
1703-
// ...or when a const stability level was not explicitly provided, and overall unstable
1704-
| (None, Some(ConstStability { level: ConstStabilityLevel::Implicit, .. })) => {
1703+
| (None, Some(ConstStability { level: ConstStabilityLevel::ImplicitUnstable, .. })) => {
17051704
"const "
17061705
}
17071706
// const unstable (and overall stable)...
17081707
(Some(_), Some(ConstStability { level: ConstStabilityLevel::Unstable { .. }, .. }))
1709-
// ...or without an explicit const stability level (and overall stable)
1710-
| (Some(_), Some(ConstStability { level: ConstStabilityLevel::Implicit, .. })) => "",
1708+
| (Some(_), Some(ConstStability { level: ConstStabilityLevel::ImplicitUnstable, .. })) => "",
17111709
},
17121710
// not const
17131711
hir::Constness::NotConst => "",

src/librustdoc/html/render/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1041,7 +1041,7 @@ fn render_stability_since_raw_with_extra(
10411041
Some((String::from("const unstable"), format!("const: {unstable}")))
10421042
}
10431043
}
1044-
Some(ConstStability { level: ConstStabilityLevel::Implicit, .. }) => {
1044+
Some(ConstStability { level: ConstStabilityLevel::ImplicitUnstable, .. }) => {
10451045
// No explicit const-stability annotation was provided. Treat it as const-unstable.
10461046
if stable_version.is_none() {
10471047
None

0 commit comments

Comments
 (0)