diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 7f9f5dc39a1..2c669dd6d9a 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -229,18 +229,6 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { // The local type and predicate checks are not free and only relevant for `const fn`s. if self.const_kind() == hir::ConstContext::ConstFn { - // Prevent const trait methods from being annotated as `stable`. - // FIXME: Do this as part of stability checking. - if self.is_const_stable_const_fn() { - if crate::const_eval::is_parent_const_impl_raw(tcx, def_id) { - self.ccx - .tcx - .sess - .struct_span_err(self.span, "trait methods cannot be stable const fn") - .emit(); - } - } - for (idx, local) in body.local_decls.iter_enumerated() { // Handle the return place below. if idx == RETURN_PLACE || local.internal { diff --git a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs index 768540c2d0d..0f79fe5513d 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs @@ -84,8 +84,6 @@ pub fn rustc_allow_const_fn_unstable( // functions are subject to more stringent restrictions than "const-unstable" functions: They // cannot use unstable features and can only call other "const-stable" functions. pub fn is_const_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - use attr::{ConstStability, StabilityLevel}; - // A default body marked const is not const-stable because const // trait fns currently cannot be const-stable. We shouldn't // restrict default bodies to only call const-stable functions. @@ -96,9 +94,39 @@ pub fn is_const_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { // Const-stability is only relevant for `const fn`. assert!(tcx.is_const_fn_raw(def_id)); - // A function is only const-stable if it has `#[rustc_const_stable]`. - matches!( - tcx.lookup_const_stability(def_id), - Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) - ) + // A function is only const-stable if it has `#[rustc_const_stable]` or it the trait it belongs + // to is const-stable. + match tcx.lookup_const_stability(def_id) { + Some(stab) => stab.is_const_stable(), + None if is_parent_const_stable_trait(tcx, def_id) => { + // Remove this when `#![feature(const_trait_impl)]` is stabilized, + // returning `true` unconditionally. + tcx.sess.delay_span_bug( + tcx.def_span(def_id), + "trait implementations cannot be const stable yet", + ); + true + } + None => false, // By default, items are not const stable. + } +} + +fn is_parent_const_stable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + let local_def_id = def_id.expect_local(); + let hir_id = tcx.local_def_id_to_hir_id(local_def_id); + + let Some(parent) = tcx.hir().find_parent_node(hir_id) else { return false }; + let parent_def = tcx.hir().get(parent); + + if !matches!( + parent_def, + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { constness: hir::Constness::Const, .. }), + .. + }) + ) { + return false; + } + + tcx.lookup_const_stability(parent.owner).map_or(false, |stab| stab.is_const_stable()) } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 00119267e85..7a6ab62ef67 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -970,6 +970,10 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { sess.time("layout_testing", || layout_test::test_layout(tcx)); + sess.time("stable_impl_const_trait_checking", || { + rustc_passes::stability::check_const_impl_trait(tcx) + }); + // Avoid overwhelming user with errors if borrow checking failed. // I'm not sure how helpful this is, to be honest, but it avoids a // lot of annoying errors in the ui tests (basically, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index fa519ede4ea..3b0df1d83e8 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2808,6 +2808,21 @@ impl<'tcx> TyCtxt<'tcx> { false } } + + /// Whether the trait impl is marked const. This does not consider stability or feature gates. + pub fn is_const_trait_impl_raw(self, def_id: DefId) -> bool { + let Some(local_def_id) = def_id.as_local() else { return false }; + let hir_id = self.local_def_id_to_hir_id(local_def_id); + let node = self.hir().get(hir_id); + + matches!( + node, + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { constness: hir::Constness::Const, .. }), + .. + }) + ) + } } impl<'tcx> TyCtxtAt<'tcx> { diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index b96322db26e..7b8ffeaac24 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -9,6 +9,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::{FieldDef, Generics, HirId, Item, TraitRef, Ty, TyKind, Variant}; use rustc_middle::hir::nested_filter; use rustc_middle::middle::privacy::AccessLevels; @@ -530,7 +531,8 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> { return; } - let is_const = self.tcx.is_const_fn(def_id.to_def_id()); + let is_const = self.tcx.is_const_fn(def_id.to_def_id()) + || self.tcx.is_const_trait_impl_raw(def_id.to_def_id()); let is_stable = self .tcx .lookup_stability(def_id) @@ -604,6 +606,44 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { // stable (assuming they have not inherited instability from their parent). } +struct CheckStableConstImplTrait<'tcx> { + tcx: TyCtxt<'tcx>, +} + +impl<'tcx> ItemLikeVisitor<'tcx> for CheckStableConstImplTrait<'tcx> { + fn visit_item(&mut self, item: &'tcx Item<'tcx>) { + if !matches!( + item.kind, + hir::ItemKind::Impl(hir::Impl { + of_trait: Some(_), + constness: hir::Constness::Const, + .. + }) + ) { + return; + } + + if self.tcx.lookup_const_stability(item.def_id).map_or(false, |stab| stab.is_const_stable()) + { + self.tcx + .sess + .struct_span_err(item.span, "trait implementations cannot be const stable yet") + .note("see issue #67792 for more information") + .emit(); + } + } + + fn visit_trait_item(&mut self, _trait_item: &'tcx hir::TraitItem<'tcx>) { + // Nothing to do here. + } + fn visit_impl_item(&mut self, _impl_item: &'tcx hir::ImplItem<'tcx>) { + // Nothing to do here. + } + fn visit_foreign_item(&mut self, _foreign_item: &'tcx hir::ForeignItem<'tcx>) { + // Nothing to do here. + } +} + fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index { let mut index = Index { stab_map: Default::default(), @@ -824,6 +864,17 @@ impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> { } } +pub fn check_const_impl_trait(tcx: TyCtxt<'_>) { + let features = tcx.features(); // FIXME How cheap is this call? + // Both feature gates have to be enabled for this check to have any effect. + if !features.staged_api || !features.const_trait_impl { + return; + } + + let mut visitor = CheckStableConstImplTrait { tcx }; + tcx.hir().visit_all_item_likes(&mut visitor); +} + /// Given the list of enabled features that were not language features (i.e., that /// were expected to be library features), and the list of features used from /// libraries, identify activated features that don't exist and error about them. diff --git a/src/test/ui/consts/rustc-impl-const-stability.rs b/src/test/ui/consts/rustc-impl-const-stability.rs index e3fda57548e..0c18efa0a02 100644 --- a/src/test/ui/consts/rustc-impl-const-stability.rs +++ b/src/test/ui/consts/rustc-impl-const-stability.rs @@ -1,19 +1,18 @@ -// build-pass +// check-pass #![crate_type = "lib"] #![feature(staged_api)] #![feature(const_trait_impl)] #![stable(feature = "foo", since = "1.0.0")] - #[stable(feature = "potato", since = "1.27.0")] pub struct Data { - _data: u128 + _data: u128, } #[stable(feature = "potato", since = "1.27.0")] +#[rustc_const_unstable(feature = "data_foo", issue = "none")] impl const Default for Data { - #[rustc_const_unstable(feature = "data_foo", issue = "none")] fn default() -> Data { Data { _data: 42 } } diff --git a/src/test/ui/rfc-2632-const-trait-impl/auxiliary/staged-api.rs b/src/test/ui/rfc-2632-const-trait-impl/auxiliary/staged-api.rs index 80e61ab0a9e..19e9006094b 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/auxiliary/staged-api.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/auxiliary/staged-api.rs @@ -1,5 +1,4 @@ #![feature(const_trait_impl)] - #![feature(staged_api)] #![stable(feature = "rust1", since = "1.0.0")] @@ -13,9 +12,7 @@ pub trait MyTrait { pub struct Unstable; #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "staged", issue = "none")] +#[rustc_const_unstable(feature = "unstable", issue = "none")] impl const MyTrait for Unstable { - fn func() { - - } + fn func() {} } diff --git a/src/test/ui/rfc-2632-const-trait-impl/stability.rs b/src/test/ui/rfc-2632-const-trait-impl/stability.rs index 906956f5eba..15f1db18f89 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/stability.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/stability.rs @@ -1,5 +1,3 @@ -#![feature(allow_internal_unstable)] -#![feature(const_add)] #![feature(const_trait_impl)] #![feature(staged_api)] #![stable(feature = "rust1", since = "1.0.0")] @@ -10,10 +8,10 @@ pub struct Int(i32); #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "rust1", since = "1.0.0")] impl const std::ops::Sub for Int { + //~^ ERROR trait implementations cannot be const stable yet type Output = Self; fn sub(self, rhs: Self) -> Self { - //~^ ERROR trait methods cannot be stable const fn Int(self.0 - rhs.0) } } @@ -30,16 +28,16 @@ impl const std::ops::Add for Int { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "rust1", since = "1.0.0")] -pub const fn foo() -> Int { - Int(1i32) + Int(2i32) +pub const fn const_err() { + Int(0) + Int(0); //~^ ERROR not yet stable as a const fn + Int(0) - Int(0); } -// ok #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "bar", issue = "none")] -pub const fn bar() -> Int { - Int(1i32) + Int(2i32) +pub fn non_const_success() { + Int(0) + Int(0); + Int(0) - Int(0); } fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/stability.stderr b/src/test/ui/rfc-2632-const-trait-impl/stability.stderr index 7473b801cce..fa3d85a3e6a 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/stability.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/stability.stderr @@ -1,19 +1,24 @@ -error: trait methods cannot be stable const fn - --> $DIR/stability.rs:15:5 - | -LL | / fn sub(self, rhs: Self) -> Self { -LL | | -LL | | Int(self.0 - rhs.0) -LL | | } - | |_____^ - error: `::add` is not yet stable as a const fn - --> $DIR/stability.rs:34:5 + --> $DIR/stability.rs:32:5 | -LL | Int(1i32) + Int(2i32) - | ^^^^^^^^^^^^^^^^^^^^^ +LL | Int(0) + Int(0); + | ^^^^^^^^^^^^^^^ | = help: const-stable functions can only call other const-stable functions +error: trait implementations cannot be const stable yet + --> $DIR/stability.rs:10:1 + | +LL | / impl const std::ops::Sub for Int { +LL | | +LL | | type Output = Self; +LL | | +... | +LL | | } +LL | | } + | |_^ + | + = note: see issue #67792 for more information + error: aborting due to 2 previous errors diff --git a/src/test/ui/rfc-2632-const-trait-impl/staged-api.rs b/src/test/ui/rfc-2632-const-trait-impl/staged-api.rs index 39a1b6066de..903ced42698 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/staged-api.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/staged-api.rs @@ -1,9 +1,7 @@ -// revisions: stock staged -#![cfg_attr(staged, feature(staged))] +// revisions: stable unstable +#![cfg_attr(unstable, feature(unstable))] // The feature from the ./auxiliary/staged-api.rs file. #![feature(const_trait_impl)] -#![allow(incomplete_features)] - #![feature(staged_api)] #![stable(feature = "rust1", since = "1.0.0")] @@ -16,12 +14,11 @@ use staged_api::*; pub struct Stable; #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(staged, rustc_const_stable(feature = "rust1", since = "1.0.0"))] -// ^ should trigger error with or without the attribute +#[cfg_attr(stable, rustc_const_stable(feature = "rust1", since = "1.0.0"))] impl const MyTrait for Stable { - fn func() { //~ ERROR trait methods cannot be stable const fn - - } + //[stable]~^ ERROR trait implementations cannot be const stable yet + //[unstable]~^^ ERROR implementation has missing const stability attribute + fn func() {} } fn non_const_context() { @@ -32,7 +29,7 @@ fn non_const_context() { #[unstable(feature = "none", issue = "none")] const fn const_context() { Unstable::func(); - //[stock]~^ ERROR `::func` is not yet stable as a const fn + //[stable]~^ ERROR `::func` is not yet stable as a const fn Stable::func(); } diff --git a/src/test/ui/rfc-2632-const-trait-impl/staged-api.stable.stderr b/src/test/ui/rfc-2632-const-trait-impl/staged-api.stable.stderr new file mode 100644 index 00000000000..2fde51217f5 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/staged-api.stable.stderr @@ -0,0 +1,22 @@ +error: `::func` is not yet stable as a const fn + --> $DIR/staged-api.rs:31:5 + | +LL | Unstable::func(); + | ^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unstable)]` to the crate attributes to enable + +error: trait implementations cannot be const stable yet + --> $DIR/staged-api.rs:18:1 + | +LL | / impl const MyTrait for Stable { +LL | | +LL | | +LL | | fn func() {} +LL | | } + | |_^ + | + = note: see issue #67792 for more information + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/rfc-2632-const-trait-impl/staged-api.staged.stderr b/src/test/ui/rfc-2632-const-trait-impl/staged-api.staged.stderr deleted file mode 100644 index d2ff4ce2001..00000000000 --- a/src/test/ui/rfc-2632-const-trait-impl/staged-api.staged.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: trait methods cannot be stable const fn - --> $DIR/staged-api.rs:22:5 - | -LL | / fn func() { -LL | | -LL | | } - | |_____^ - -error: aborting due to previous error - diff --git a/src/test/ui/rfc-2632-const-trait-impl/staged-api.stock.stderr b/src/test/ui/rfc-2632-const-trait-impl/staged-api.stock.stderr deleted file mode 100644 index 91c5469bd90..00000000000 --- a/src/test/ui/rfc-2632-const-trait-impl/staged-api.stock.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error: trait methods cannot be stable const fn - --> $DIR/staged-api.rs:22:5 - | -LL | / fn func() { -LL | | -LL | | } - | |_____^ - -error: `::func` is not yet stable as a const fn - --> $DIR/staged-api.rs:34:5 - | -LL | Unstable::func(); - | ^^^^^^^^^^^^^^^^ - | - = help: add `#![feature(staged)]` to the crate attributes to enable - -error: aborting due to 2 previous errors - diff --git a/src/test/ui/rfc-2632-const-trait-impl/staged-api.unstable.stderr b/src/test/ui/rfc-2632-const-trait-impl/staged-api.unstable.stderr new file mode 100644 index 00000000000..4ea1be69b3b --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/staged-api.unstable.stderr @@ -0,0 +1,12 @@ +error: implementation has missing const stability attribute + --> $DIR/staged-api.rs:18:1 + | +LL | / impl const MyTrait for Stable { +LL | | +LL | | +LL | | fn func() {} +LL | | } + | |_^ + +error: aborting due to previous error + diff --git a/src/test/ui/stability-attribute/missing-const-stability.rs b/src/test/ui/stability-attribute/missing-const-stability.rs index 57e64737d0f..d89886af314 100644 --- a/src/test/ui/stability-attribute/missing-const-stability.rs +++ b/src/test/ui/stability-attribute/missing-const-stability.rs @@ -18,9 +18,15 @@ impl Foo { pub const fn bar() {} // ok because function is unstable } -// FIXME Once #![feature(const_trait_impl)] is allowed to be stable, add a test -// for const trait impls. Right now, a "trait methods cannot be stable const fn" -// error is emitted. This occurs prior to the lint being tested here, such that -// the lint cannot currently be tested on this use case. +#[stable(feature = "stable", since = "1.0.0")] +pub trait Bar { + #[stable(feature = "stable", since = "1.0.0")] + fn fun(); +} +#[stable(feature = "stable", since = "1.0.0")] +impl const Bar for Foo { + //~^ ERROR implementation has missing const stability attribute + fn fun() {} +} fn main() {} diff --git a/src/test/ui/stability-attribute/missing-const-stability.stderr b/src/test/ui/stability-attribute/missing-const-stability.stderr index 7eba99a477a..6f2ade0d0ab 100644 --- a/src/test/ui/stability-attribute/missing-const-stability.stderr +++ b/src/test/ui/stability-attribute/missing-const-stability.stderr @@ -10,5 +10,14 @@ error: associated function has missing const stability attribute LL | pub const fn foo() {} | ^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: implementation has missing const stability attribute + --> $DIR/missing-const-stability.rs:27:1 + | +LL | / impl const Bar for Foo { +LL | | +LL | | fn fun() {} +LL | | } + | |_^ + +error: aborting due to 3 previous errors