Skip to content

Commit bc6c3b0

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

File tree

8 files changed

+47
-49
lines changed

8 files changed

+47
-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<'_> {
@@ -429,10 +429,10 @@ pub fn find_const_stability(
429429
// record this, and so that's what we do for all `const fn` in a staged_api crate.
430430
if is_const_fn || const_stable_indirect.is_some() {
431431
stab_spans = ConstStabilitySpans(smallvec![(
432-
ConstStabilityLevel::Implicit,
432+
ConstStabilityLevel::ImplicitUnstable,
433433
const_stable_indirect.unwrap_or(DUMMY_SP)
434434
)]);
435-
ConstStabilityLevel::Implicit
435+
ConstStabilityLevel::ImplicitUnstable
436436
} else {
437437
return None;
438438
}

compiler/rustc_const_eval/src/check_consts/check.rs

+18-26
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ use rustc_mir_dataflow::storage::always_storage_live_locals;
2323
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
2424
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
2525
use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt};
26-
use smallvec::SmallVec;
2726
use tracing::{debug, instrument, trace};
2827

2928
use super::ops::{self, NonConstOp, Status};
@@ -720,7 +719,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
720719
self.check_op(ops::IntrinsicNonConst { name: intrinsic.name });
721720
}
722721
Some(ConstStability {
723-
level: ConstStabilityLevel::Implicit,
722+
level: ConstStabilityLevel::ImplicitUnstable,
724723
const_stable_indirect,
725724
..
726725
}) => {
@@ -776,7 +775,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
776775
Some(ConstStability { level: ConstStabilityLevel::Stable { .. }, .. }) => {
777776
// All good.
778777
}
779-
None | Some(ConstStability { level: ConstStabilityLevel::Implicit, .. }) => {
778+
None
779+
| Some(ConstStability {
780+
level: ConstStabilityLevel::ImplicitUnstable, ..
781+
}) => {
780782
// This doesn't need a separate const-stability check -- const-stability equals
781783
// regular stability, and regular stability is checked separately.
782784
// However, we *do* have to worry about *recursive* const stability.
@@ -799,43 +801,33 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
799801

800802
// We only honor `span.allows_unstable` aka `#[allow_internal_unstable]` if
801803
// the callee is safe to expose, to avoid bypassing recursive stability.
802-
let is_allowed_unstable = |u: &rustc_attr::Unstability| {
803-
callee_safe_to_expose_on_stable
804-
&& (self.span.allows_unstable(u.feature)
805-
|| u.implied_by.is_some_and(|f| self.span.allows_unstable(f)))
806-
};
807-
let mut needs_check =
808-
unstables.iter().filter(|u| !is_allowed_unstable(u)).peekable();
809-
if needs_check.peek().is_none() {
804+
if callee_safe_to_expose_on_stable
805+
&& unstables.iter().all(|u| {
806+
self.span.allows_unstable(u.feature)
807+
|| u.implied_by.is_some_and(|f| self.span.allows_unstable(f))
808+
})
809+
{
810810
return;
811811
}
812812

813813
// We can't use `check_op` to check whether the feature is enabled because
814814
// the logic is a bit different than elsewhere: local functions don't need
815815
// the feature gate, and there might be an "implied" gate that also suffices
816816
// to allow this.
817-
let is_feature_enabled = |u: &rustc_attr::Unstability| {
818-
callee.is_local()
819-
|| tcx.features().enabled(u.feature)
820-
|| u.implied_by.is_some_and(|f| tcx.features().enabled(f))
821-
};
817+
let features_enabled = callee.is_local()
818+
|| unstables.iter().all(|u| {
819+
tcx.features().enabled(u.feature)
820+
|| u.implied_by.is_some_and(|f| tcx.features().enabled(f))
821+
});
822822
// We do *not* honor this if we are in the "danger zone": we have to enforce
823823
// recursive const-stability and the callee is not safe-to-expose. In that
824824
// case we need `check_op` to do the check.
825825
let danger_zone = !callee_safe_to_expose_on_stable
826826
&& self.enforce_recursive_const_stability();
827-
let missing_features: SmallVec<[_; 1]> = if danger_zone {
828-
needs_check.map(|u| u.into()).collect()
829-
} else {
830-
needs_check
831-
.filter(|u| !is_feature_enabled(u))
832-
.map(|u| u.into())
833-
.collect()
834-
};
835-
if !missing_features.is_empty() {
827+
if danger_zone || !features_enabled {
836828
self.check_op(ops::FnCallUnstable {
837829
def_id: callee,
838-
features: missing_features,
830+
features: unstables.iter().map(|u| u.into()).collect(),
839831
reason: *reason,
840832
safe_to_expose_on_stable: callee_safe_to_expose_on_stable,
841833
});

compiler/rustc_const_eval/src/check_consts/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ pub fn is_safe_to_expose_on_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> b
123123
Some(stab) => {
124124
// We consider things safe-to-expose if they are stable, if they don't have any explicit
125125
// const stability attribute, or if they are marked as `const_stable_indirect`.
126-
stab.is_const_stable() || stab.is_implicit() || stab.const_stable_indirect
126+
stab.is_const_stable() || stab.is_implicit_unstable() || stab.const_stable_indirect
127127
}
128128
}
129129
}

compiler/rustc_const_eval/src/check_consts/ops.rs

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

328328
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
329+
// Only report the features that aren't already enabled.
330+
let missing_features = Vec::from_iter(
331+
self.features.iter().filter(|d| !ccx.tcx.features().enabled(d.feature)).copied(),
332+
);
329333
ccx.dcx().create_err(errors::UnstableConstFn {
330334
span,
331335
def_path: ccx.tcx.def_path_str(self.def_id),
332-
features: stability::unstable_message(&self.features, self.reason.to_opt_reason())
336+
features: stability::unstable_message(&missing_features, self.reason.to_opt_reason())
333337
.hide_features_on_nightly(&ccx.tcx.sess),
334-
issues: stability::unstable_issues(&self.features),
338+
issues: stability::unstable_issues(&missing_features),
335339
nightly_subdiags: stability::unstable_nightly_subdiags(
336340
&ccx.tcx.sess,
337-
&self.features,
341+
&missing_features,
338342
None,
339343
),
340344
})
@@ -378,15 +382,19 @@ impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable {
378382
}
379383

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

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
@@ -616,7 +616,7 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> {
616616
let is_stable =
617617
self.tcx.lookup_stability(def_id).is_some_and(|stability| stability.level.is_stable());
618618
let missing_const_stability_attribute =
619-
self.tcx.lookup_const_stability(def_id).is_none_or(|s| s.is_implicit());
619+
self.tcx.lookup_const_stability(def_id).is_none_or(|s| s.is_implicit_unstable());
620620

621621
if is_const && is_stable && missing_const_stability_attribute {
622622
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
@@ -1701,14 +1701,12 @@ pub(crate) fn print_constness_with_space(
17011701
| (_, None)
17021702
// ...or when const unstable, but overall unstable too...
17031703
| (None, Some(ConstStability { level: ConstStabilityLevel::Unstable { .. }, .. }))
1704-
// ...or when a const stability level was not explicitly provided, and overall unstable
1705-
| (None, Some(ConstStability { level: ConstStabilityLevel::Implicit, .. })) => {
1704+
| (None, Some(ConstStability { level: ConstStabilityLevel::ImplicitUnstable, .. })) => {
17061705
"const "
17071706
}
17081707
// const unstable (and overall stable)...
17091708
(Some(_), Some(ConstStability { level: ConstStabilityLevel::Unstable { .. }, .. }))
1710-
// ...or without an explicit const stability level (and overall stable)
1711-
| (Some(_), Some(ConstStability { level: ConstStabilityLevel::Implicit, .. })) => "",
1709+
| (Some(_), Some(ConstStability { level: ConstStabilityLevel::ImplicitUnstable, .. })) => "",
17121710
},
17131711
// not const
17141712
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)