From 23cee742b0ba4f7a98154e30cbf93025f1bf9b32 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Fri, 7 Feb 2025 21:58:08 +0800 Subject: [PATCH] require trait impls to have matching const stabilities as the traits --- .../rustc_const_eval/src/check_consts/mod.rs | 5 ++ compiler/rustc_passes/messages.ftl | 6 ++ compiler/rustc_passes/src/errors.rs | 39 +++++++++++ compiler/rustc_passes/src/stability.rs | 62 ++++++++++++----- library/core/src/ops/arith.rs | 1 + library/core/src/ops/deref.rs | 3 + tests/ui/traits/const-traits/staged-api.rs | 32 +++++++++ .../ui/traits/const-traits/staged-api.stderr | 69 ++++++++++++++++++- 8 files changed, 200 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs index bfa0a0319c344..46f174fb94a96 100644 --- a/compiler/rustc_const_eval/src/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/check_consts/mod.rs @@ -92,6 +92,11 @@ pub fn rustc_allow_const_fn_unstable( /// 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_fn_or_trait_safe_to_expose_on_stable(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + // A default body in a `#[const_trait]` is const-stable when the trait is const-stable. + if tcx.is_const_default_method(def_id) { + return is_fn_or_trait_safe_to_expose_on_stable(tcx, tcx.parent(def_id)); + } + 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_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 978cb7af2427f..4c0fc7cd484fb 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -732,6 +732,12 @@ passes_target_feature_on_statement = .warn = {-passes_previously_accepted} .label = {passes_should_be_applied_to_fn.label} +passes_trait_impl_const_stability_mismatch = const stability on the impl does not match the const stability on the trait +passes_trait_impl_const_stability_mismatch_impl_stable = this impl is (implicitly) stable... +passes_trait_impl_const_stability_mismatch_impl_unstable = this impl is unstable... +passes_trait_impl_const_stability_mismatch_trait_stable = ...but the trait is stable +passes_trait_impl_const_stability_mismatch_trait_unstable = ...but the trait is unstable + passes_trait_impl_const_stable = trait implementations cannot be const stable yet .note = see issue #67792 for more information diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 0aa50ad19ff96..1af1d8ff94db5 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1588,6 +1588,45 @@ pub(crate) struct TraitImplConstStable { pub span: Span, } +#[derive(Diagnostic)] +#[diag(passes_trait_impl_const_stability_mismatch)] +pub(crate) struct TraitImplConstStabilityMismatch { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub impl_stability: ImplConstStability, + #[subdiagnostic] + pub trait_stability: TraitConstStability, +} + +#[derive(Subdiagnostic)] +pub(crate) enum TraitConstStability { + #[note(passes_trait_impl_const_stability_mismatch_trait_stable)] + Stable { + #[primary_span] + span: Span, + }, + #[note(passes_trait_impl_const_stability_mismatch_trait_unstable)] + Unstable { + #[primary_span] + span: Span, + }, +} + +#[derive(Subdiagnostic)] +pub(crate) enum ImplConstStability { + #[note(passes_trait_impl_const_stability_mismatch_impl_stable)] + Stable { + #[primary_span] + span: Span, + }, + #[note(passes_trait_impl_const_stability_mismatch_impl_unstable)] + Unstable { + #[primary_span] + span: Span, + }, +} + #[derive(Diagnostic)] #[diag(passes_unknown_feature, code = E0635)] pub(crate) struct UnknownFeature { diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 34f1ca55c7875..16abbef8a0b90 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -301,7 +301,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { let mut const_stab = const_stab.map(|(stab, _span)| stab); // If this is a const fn but not annotated with stability markers, see if we can inherit regular stability. - if fn_sig.is_some_and(|s| s.header.is_const()) && const_stab.is_none() && + if fn_sig.is_some_and(|s| s.header.is_const()) && const_stab.is_none() && // We only ever inherit unstable features. let Some(inherit_regular_stab) = final_stab.filter(|s| s.is_unstable()) @@ -816,24 +816,54 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { } } - // `#![feature(const_trait_impl)]` is unstable, so any impl declared stable - // needs to have an error emitted. - if features.const_trait_impl() - && self.tcx.is_const_trait_impl(item.owner_id.to_def_id()) - && const_stab.is_some_and(|(stab, _)| stab.is_const_stable()) - { - self.tcx.dcx().emit_err(errors::TraitImplConstStable { span: item.span }); + if features.const_trait_impl() && hir::Constness::Const == *constness { + let stable_or_implied_stable = match const_stab { + None => true, + Some((stab, _)) if stab.is_const_stable() => { + // `#![feature(const_trait_impl)]` is unstable, so any impl declared stable + // needs to have an error emitted. + // Note: Remove this error once `const_trait_impl` is stabilized + self.tcx + .dcx() + .emit_err(errors::TraitImplConstStable { span: item.span }); + true + } + Some(_) => false, + }; + + if let Some(trait_id) = t.trait_def_id() + && let Some(const_stab) = self.tcx.lookup_const_stability(trait_id) + { + // the const stability of a trait impl must match the const stability on the trait. + if const_stab.is_const_stable() != stable_or_implied_stable { + let trait_span = self.tcx.def_ident_span(trait_id).unwrap(); + + let impl_stability = if stable_or_implied_stable { + errors::ImplConstStability::Stable { span: item.span } + } else { + errors::ImplConstStability::Unstable { span: item.span } + }; + let trait_stability = if const_stab.is_const_stable() { + errors::TraitConstStability::Stable { span: trait_span } + } else { + errors::TraitConstStability::Unstable { span: trait_span } + }; + + self.tcx.dcx().emit_err(errors::TraitImplConstStabilityMismatch { + span: item.span, + impl_stability, + trait_stability, + }); + } + } } } - match constness { - rustc_hir::Constness::Const => { - if let Some(def_id) = t.trait_def_id() { - // FIXME(const_trait_impl): Improve the span here. - self.tcx.check_const_stability(def_id, t.path.span, t.path.span); - } - } - rustc_hir::Constness::NotConst => {} + if let hir::Constness::Const = constness + && let Some(def_id) = t.trait_def_id() + { + // FIXME(const_trait_impl): Improve the span here. + self.tcx.check_const_stability(def_id, t.path.span, t.path.span); } for impl_item_ref in *items { diff --git a/library/core/src/ops/arith.rs b/library/core/src/ops/arith.rs index fe7ff2d9ede6a..54d79beca95ab 100644 --- a/library/core/src/ops/arith.rs +++ b/library/core/src/ops/arith.rs @@ -96,6 +96,7 @@ pub trait Add { macro_rules! add_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_ops", issue = "90080")] impl const Add for $t { type Output = $t; diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs index 11490ea2bfcb4..e74f5443ac2d8 100644 --- a/library/core/src/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -150,6 +150,7 @@ pub trait Deref { } #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_deref", issue = "88955")] impl const Deref for &T { type Target = T; @@ -163,6 +164,7 @@ impl const Deref for &T { impl !DerefMut for &T {} #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_deref", issue = "88955")] impl const Deref for &mut T { type Target = T; @@ -273,6 +275,7 @@ pub trait DerefMut: ~const Deref { } #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_deref", issue = "88955")] impl const DerefMut for &mut T { fn deref_mut(&mut self) -> &mut T { *self diff --git a/tests/ui/traits/const-traits/staged-api.rs b/tests/ui/traits/const-traits/staged-api.rs index 9a030dafd6bc4..8dd7226fc298e 100644 --- a/tests/ui/traits/const-traits/staged-api.rs +++ b/tests/ui/traits/const-traits/staged-api.rs @@ -85,4 +85,36 @@ const fn implicitly_stable_const_context() { //~^ ERROR cannot use `#[feature(const_trait_impl)]` } +// check that const stability of impls and traits must match +#[const_trait] +#[rustc_const_unstable(feature = "beef", issue = "none")] +trait U {} + +#[const_trait] +#[rustc_const_stable(since = "0.0.0", feature = "beef2")] +trait S {} + +// implied stable +impl const U for u8 {} +//~^ const stability on the impl does not match the const stability on the trait + +#[rustc_const_stable(since = "0.0.0", feature = "beef2")] +impl const U for u16 {} +//~^ const stability on the impl does not match the const stability on the trait +//~| trait implementations cannot be const stable yet + +#[rustc_const_unstable(feature = "beef", issue = "none")] +impl const U for u32 {} + +// implied stable +impl const S for u8 {} + +#[rustc_const_stable(since = "0.0.0", feature = "beef2")] +impl const S for u16 {} +//~^ trait implementations cannot be const stable yet + +#[rustc_const_unstable(feature = "beef", issue = "none")] +impl const S for u32 {} +//~^ const stability on the impl does not match the const stability on the trait + fn main() {} diff --git a/tests/ui/traits/const-traits/staged-api.stderr b/tests/ui/traits/const-traits/staged-api.stderr index a7a7a1ee7215b..cdf577287eec5 100644 --- a/tests/ui/traits/const-traits/staged-api.stderr +++ b/tests/ui/traits/const-traits/staged-api.stderr @@ -1,3 +1,70 @@ +error: const stability on the impl does not match the const stability on the trait + --> $DIR/staged-api.rs:98:1 + | +LL | impl const U for u8 {} + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: this impl is (implicitly) stable... + --> $DIR/staged-api.rs:98:1 + | +LL | impl const U for u8 {} + | ^^^^^^^^^^^^^^^^^^^^^^ +note: ...but the trait is unstable + --> $DIR/staged-api.rs:91:7 + | +LL | trait U {} + | ^ + +error: trait implementations cannot be const stable yet + --> $DIR/staged-api.rs:102:1 + | +LL | impl const U for u16 {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #67792 for more information + +error: const stability on the impl does not match the const stability on the trait + --> $DIR/staged-api.rs:102:1 + | +LL | impl const U for u16 {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this impl is (implicitly) stable... + --> $DIR/staged-api.rs:102:1 + | +LL | impl const U for u16 {} + | ^^^^^^^^^^^^^^^^^^^^^^^ +note: ...but the trait is unstable + --> $DIR/staged-api.rs:91:7 + | +LL | trait U {} + | ^ + +error: trait implementations cannot be const stable yet + --> $DIR/staged-api.rs:113:1 + | +LL | impl const S for u16 {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #67792 for more information + +error: const stability on the impl does not match the const stability on the trait + --> $DIR/staged-api.rs:117:1 + | +LL | impl const S for u32 {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this impl is unstable... + --> $DIR/staged-api.rs:117:1 + | +LL | impl const S for u32 {} + | ^^^^^^^^^^^^^^^^^^^^^^^ +note: ...but the trait is stable + --> $DIR/staged-api.rs:95:7 + | +LL | trait S {} + | ^ + error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` --> $DIR/staged-api.rs:38:5 | @@ -323,5 +390,5 @@ LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn implicitly_stable_const_context() { | -error: aborting due to 19 previous errors +error: aborting due to 24 previous errors