diff options
| author | bors <bors@rust-lang.org> | 2025-01-28 20:15:51 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2025-01-28 20:15:51 +0000 |
| commit | bf1b174e7d0922fc549071b751ab96fc9ee9f6fe (patch) | |
| tree | e98cf327b1b995ead0c48fdb52669a22a5af86d6 /compiler/rustc_const_eval | |
| parent | 77a455303bf08da8eef6236b2b4422a77cd25c42 (diff) | |
| parent | 9715fea5d52a521a62c7165fbd85ed6559db2b52 (diff) | |
| download | rust-bf1b174e7d0922fc549071b751ab96fc9ee9f6fe.tar.gz rust-bf1b174e7d0922fc549071b751ab96fc9ee9f6fe.zip | |
Auto merge of #136203 - matthiaskrgr:rollup-1k0f44l, r=matthiaskrgr
Rollup of 9 pull requests Successful merges: - #135869 (Make docs for AtomicUsize::from_mut platform-independent) - #135892 (-Znext-solver: "normalize" signature before checking it mentions self in `deduce_closure_signature`) - #136055 (Implement MIR const trait stability checks) - #136066 (Pass spans to `perform_locally_in_new_solver`) - #136071 ([Clippy] Add vec_reserve & vecdeque_reserve diagnostic items) - #136124 (Arbitrary self types v2: explain test.) - #136149 (Flip the `rustc-rayon`/`indexmap` dependency order) - #136173 (Update comments and sort target_arch in c_char_definition) - #136178 (Update username in build helper example) r? `@ghost` `@rustbot` modify labels: rollup
Diffstat (limited to 'compiler/rustc_const_eval')
| -rw-r--r-- | compiler/rustc_const_eval/messages.ftl | 3 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/check_consts/check.rs | 168 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/check_consts/mod.rs | 20 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/check_consts/ops.rs | 27 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/errors.rs | 12 |
5 files changed, 120 insertions, 110 deletions
diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 485c8696342..d600d223bff 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -403,7 +403,7 @@ const_eval_uninhabited_enum_variant_read = const_eval_uninhabited_enum_variant_written = writing discriminant of an uninhabited enum variant -const_eval_unmarked_const_fn_exposed = `{$def_path}` cannot be (indirectly) exposed to stable +const_eval_unmarked_const_item_exposed = `{$def_path}` cannot be (indirectly) exposed to stable .help = either mark the callee as `#[rustc_const_stable_indirect]`, or the caller as `#[rustc_const_unstable]` const_eval_unmarked_intrinsic_exposed = intrinsic `{$def_path}` cannot be (indirectly) exposed to stable .help = mark the caller as `#[rustc_const_unstable]`, or mark the intrinsic `#[rustc_intrinsic_const_stable_indirect]` (but this requires team approval) @@ -414,6 +414,7 @@ const_eval_unreachable_unwind = const_eval_unsized_local = unsized locals are not supported const_eval_unstable_const_fn = `{$def_path}` is not yet stable as a const fn +const_eval_unstable_const_trait = `{$def_path}` is not yet stable as a const trait const_eval_unstable_in_stable_exposed = const function that might be (indirectly) exposed to stable cannot use `#[feature({$gate})]` .is_function_call = mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 83850aef301..16ead1b9785 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -8,6 +8,7 @@ use std::ops::Deref; use rustc_attr_parsing::{ConstStability, StabilityLevel}; use rustc_errors::{Diag, ErrorGuaranteed}; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem}; use rustc_index::bit_set::DenseBitSet; @@ -29,7 +30,7 @@ use super::ops::{self, NonConstOp, Status}; use super::qualifs::{self, HasMutInterior, NeedsDrop, NeedsNonConstDrop}; use super::resolver::FlowSensitiveAnalysis; use super::{ConstCx, Qualif}; -use crate::check_consts::is_safe_to_expose_on_stable_const_fn; +use crate::check_consts::is_fn_or_trait_safe_to_expose_on_stable; use crate::errors; type QualifResults<'mir, 'tcx, Q> = @@ -470,6 +471,88 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { self.tcx.crate_level_attribute_injection_span(self.tcx.local_def_id_to_hir_id(id)) }) } + + /// Check the const stability of the given item (fn or trait). + fn check_callee_stability(&mut self, def_id: DefId) { + match self.tcx.lookup_const_stability(def_id) { + Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => { + // All good. + } + None => { + // This doesn't need a separate const-stability check -- const-stability equals + // regular stability, and regular stability is checked separately. + // However, we *do* have to worry about *recursive* const stability. + if self.enforce_recursive_const_stability() + && !is_fn_or_trait_safe_to_expose_on_stable(self.tcx, def_id) + { + self.dcx().emit_err(errors::UnmarkedConstItemExposed { + span: self.span, + def_path: self.tcx.def_path_str(def_id), + }); + } + } + Some(ConstStability { + level: StabilityLevel::Unstable { implied_by: implied_feature, issue, .. }, + feature, + .. + }) => { + // An unstable const fn/trait with a feature gate. + let callee_safe_to_expose_on_stable = + is_fn_or_trait_safe_to_expose_on_stable(self.tcx, def_id); + + // We only honor `span.allows_unstable` aka `#[allow_internal_unstable]` if + // the callee is safe to expose, to avoid bypassing recursive stability. + // This is not ideal since it means the user sees an error, not the macro + // author, but that's also the case if one forgets to set + // `#[allow_internal_unstable]` in the first place. Note that this cannot be + // integrated in the check below since we want to enforce + // `callee_safe_to_expose_on_stable` even if + // `!self.enforce_recursive_const_stability()`. + if (self.span.allows_unstable(feature) + || implied_feature.is_some_and(|f| self.span.allows_unstable(f))) + && callee_safe_to_expose_on_stable + { + return; + } + + // We can't use `check_op` to check whether the feature is enabled because + // the logic is a bit different than elsewhere: local functions don't need + // the feature gate, and there might be an "implied" gate that also suffices + // to allow this. + let feature_enabled = def_id.is_local() + || self.tcx.features().enabled(feature) + || implied_feature.is_some_and(|f| self.tcx.features().enabled(f)) + || { + // When we're compiling the compiler itself we may pull in + // crates from crates.io, but those crates may depend on other + // crates also pulled in from crates.io. We want to ideally be + // able to compile everything without requiring upstream + // modifications, so in the case that this looks like a + // `rustc_private` crate (e.g., a compiler crate) and we also have + // the `-Z force-unstable-if-unmarked` flag present (we're + // compiling a compiler crate), then let this missing feature + // annotation slide. + // This matches what we do in `eval_stability_allow_unstable` for + // regular stability. + feature == sym::rustc_private + && issue == NonZero::new(27812) + && self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked + }; + // Even if the feature is enabled, we still need check_op to double-check + // this if the callee is not safe to expose on stable. + if !feature_enabled || !callee_safe_to_expose_on_stable { + self.check_op(ops::CallUnstable { + def_id, + feature, + feature_enabled, + safe_to_expose_on_stable: callee_safe_to_expose_on_stable, + suggestion_span: self.crate_inject_span(), + is_function_call: self.tcx.def_kind(def_id) != DefKind::Trait, + }); + } + } + } + } } impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { @@ -733,8 +816,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { span: *fn_span, call_source, }); - // FIXME(const_trait_impl): do a more fine-grained check whether this - // particular trait can be const-stably called. + self.check_callee_stability(trait_did); } else { // Not even a const trait. self.check_op(ops::FnCallNonConst { @@ -810,7 +892,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // fallback body is safe to expose on stable. let is_const_stable = intrinsic.const_stable || (!intrinsic.must_be_overridden - && is_safe_to_expose_on_stable_const_fn(tcx, callee)); + && is_fn_or_trait_safe_to_expose_on_stable(tcx, callee)); match tcx.lookup_const_stability(callee) { None => { // This doesn't need a separate const-stability check -- const-stability equals @@ -859,83 +941,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } // Finally, stability for regular function calls -- this is the big one. - match tcx.lookup_const_stability(callee) { - Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => { - // All good. - } - None => { - // This doesn't need a separate const-stability check -- const-stability equals - // regular stability, and regular stability is checked separately. - // However, we *do* have to worry about *recursive* const stability. - if self.enforce_recursive_const_stability() - && !is_safe_to_expose_on_stable_const_fn(tcx, callee) - { - self.dcx().emit_err(errors::UnmarkedConstFnExposed { - span: self.span, - def_path: self.tcx.def_path_str(callee), - }); - } - } - Some(ConstStability { - level: StabilityLevel::Unstable { implied_by: implied_feature, issue, .. }, - feature, - .. - }) => { - // An unstable const fn with a feature gate. - let callee_safe_to_expose_on_stable = - is_safe_to_expose_on_stable_const_fn(tcx, callee); - - // We only honor `span.allows_unstable` aka `#[allow_internal_unstable]` if - // the callee is safe to expose, to avoid bypassing recursive stability. - // This is not ideal since it means the user sees an error, not the macro - // author, but that's also the case if one forgets to set - // `#[allow_internal_unstable]` in the first place. Note that this cannot be - // integrated in the check below since we want to enforce - // `callee_safe_to_expose_on_stable` even if - // `!self.enforce_recursive_const_stability()`. - if (self.span.allows_unstable(feature) - || implied_feature.is_some_and(|f| self.span.allows_unstable(f))) - && callee_safe_to_expose_on_stable - { - return; - } - - // We can't use `check_op` to check whether the feature is enabled because - // the logic is a bit different than elsewhere: local functions don't need - // the feature gate, and there might be an "implied" gate that also suffices - // to allow this. - let feature_enabled = callee.is_local() - || tcx.features().enabled(feature) - || implied_feature.is_some_and(|f| tcx.features().enabled(f)) - || { - // When we're compiling the compiler itself we may pull in - // crates from crates.io, but those crates may depend on other - // crates also pulled in from crates.io. We want to ideally be - // able to compile everything without requiring upstream - // modifications, so in the case that this looks like a - // `rustc_private` crate (e.g., a compiler crate) and we also have - // the `-Z force-unstable-if-unmarked` flag present (we're - // compiling a compiler crate), then let this missing feature - // annotation slide. - // This matches what we do in `eval_stability_allow_unstable` for - // regular stability. - feature == sym::rustc_private - && issue == NonZero::new(27812) - && tcx.sess.opts.unstable_opts.force_unstable_if_unmarked - }; - // Even if the feature is enabled, we still need check_op to double-check - // this if the callee is not safe to expose on stable. - if !feature_enabled || !callee_safe_to_expose_on_stable { - self.check_op(ops::FnCallUnstable { - def_id: callee, - feature, - feature_enabled, - safe_to_expose_on_stable: callee_safe_to_expose_on_stable, - suggestion_span: self.crate_inject_span(), - }); - } - } - } + self.check_callee_stability(callee); } // Forbid all `Drop` terminators unless the place being dropped is a local with no diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs index ab68691f1b9..bfa0a0319c3 100644 --- a/compiler/rustc_const_eval/src/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/check_consts/mod.rs @@ -56,7 +56,7 @@ impl<'mir, 'tcx> ConstCx<'mir, 'tcx> { self.const_kind == Some(hir::ConstContext::ConstFn) && (self.tcx.features().staged_api() || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked) - && is_safe_to_expose_on_stable_const_fn(self.tcx, self.def_id().to_def_id()) + && is_fn_or_trait_safe_to_expose_on_stable(self.tcx, self.def_id().to_def_id()) } fn is_async(&self) -> bool { @@ -84,28 +84,14 @@ pub fn rustc_allow_const_fn_unstable( attr::rustc_allow_const_fn_unstable(tcx.sess, attrs).any(|name| name == feature_gate) } -/// Returns `true` if the given `const fn` is "safe to expose on stable". -/// -/// Panics if the given `DefId` does not refer to a `const fn`. +/// Returns `true` if the given `def_id` (trait or function) is "safe to expose on stable". /// /// This is relevant within a `staged_api` crate. Unlike with normal features, the use of unstable /// const features *recursively* taints the functions that use them. This is to avoid accidentally /// exposing e.g. the implementation of an unstable const intrinsic on stable. So we partition the /// world into two functions: those that are safe to expose on stable (and hence may not use /// unstable features, not even recursively), and those that are not. -pub fn is_safe_to_expose_on_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - // A default body in a `#[const_trait]` is not const-stable because const trait fns currently - // cannot be const-stable. These functions can't be called from anything stable, so we shouldn't - // restrict them to only call const-stable functions. - if tcx.is_const_default_method(def_id) { - // FIXME(const_trait_impl): we have to eventually allow some of these if these things can ever be stable. - // They should probably behave like regular `const fn` for that... - return false; - } - - // Const-stability is only relevant for `const fn`. - assert!(tcx.is_const_fn(def_id)); - +pub fn is_fn_or_trait_safe_to_expose_on_stable(tcx: TyCtxt<'_>, def_id: DefId) -> bool { match tcx.lookup_const_stability(def_id) { None => { // In a `staged_api` crate, we do enforce recursive const stability for all unmarked diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 3c83a7b92cd..7756e51c4c5 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -377,11 +377,11 @@ fn build_error_for_const_call<'tcx>( err } -/// A call to an `#[unstable]` const fn or `#[rustc_const_unstable]` function. +/// A call to an `#[unstable]` const fn, `#[rustc_const_unstable]` function or trait. /// -/// Contains the name of the feature that would allow the use of this function. +/// Contains the name of the feature that would allow the use of this function/trait. #[derive(Debug)] -pub(crate) struct FnCallUnstable { +pub(crate) struct CallUnstable { pub def_id: DefId, pub feature: Symbol, /// If this is true, then the feature is enabled, but we need to still check if it is safe to @@ -389,24 +389,33 @@ pub(crate) struct FnCallUnstable { pub feature_enabled: bool, pub safe_to_expose_on_stable: bool, pub suggestion_span: Option<Span>, + /// true if `def_id` is the function we are calling, false if `def_id` is an unstable trait. + pub is_function_call: bool, } -impl<'tcx> NonConstOp<'tcx> for FnCallUnstable { +impl<'tcx> NonConstOp<'tcx> for CallUnstable { fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable { gate: self.feature, gate_already_checked: self.feature_enabled, safe_to_expose_on_stable: self.safe_to_expose_on_stable, - is_function_call: true, + is_function_call: self.is_function_call, } } fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { assert!(!self.feature_enabled); - let mut err = ccx.dcx().create_err(errors::UnstableConstFn { - span, - def_path: ccx.tcx.def_path_str(self.def_id), - }); + let mut err = if self.is_function_call { + ccx.dcx().create_err(errors::UnstableConstFn { + span, + def_path: ccx.tcx.def_path_str(self.def_id), + }) + } else { + ccx.dcx().create_err(errors::UnstableConstTrait { + span, + def_path: ccx.tcx.def_path_str(self.def_id), + }) + }; // FIXME: make this translatable let msg = format!("add `#![feature({})]` to the crate attributes to enable", self.feature); #[allow(rustc::untranslatable_diagnostic)] diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 1ee9214c4b2..a2635885098 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -122,6 +122,14 @@ pub(crate) struct UnstableConstFn { } #[derive(Diagnostic)] +#[diag(const_eval_unstable_const_trait)] +pub(crate) struct UnstableConstTrait { + #[primary_span] + pub span: Span, + pub def_path: String, +} + +#[derive(Diagnostic)] #[diag(const_eval_unstable_intrinsic)] pub(crate) struct UnstableIntrinsic { #[primary_span] @@ -139,9 +147,9 @@ pub(crate) struct UnstableIntrinsic { } #[derive(Diagnostic)] -#[diag(const_eval_unmarked_const_fn_exposed)] +#[diag(const_eval_unmarked_const_item_exposed)] #[help] -pub(crate) struct UnmarkedConstFnExposed { +pub(crate) struct UnmarkedConstItemExposed { #[primary_span] pub span: Span, pub def_path: String, |
