From 0b94fa4e7a8e9ebf4f3cd71caa8a7f5e8de111c2 Mon Sep 17 00:00:00 2001 From: Jacob Pratt Date: Fri, 4 Feb 2022 19:03:34 -0500 Subject: [PATCH 1/4] Fall back to being const-unstable when undeclared --- .../src/transform/check_consts/mod.rs | 25 +++++-------------- 1 file changed, 6 insertions(+), 19 deletions(-) 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 b026bb2bad65..77c8f5e4fb3e 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs @@ -86,7 +86,7 @@ pub fn rustc_allow_const_fn_unstable(tcx: TyCtxt<'_>, def_id: DefId, feature_gat // 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, Stability, StabilityLevel}; + 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 @@ -98,22 +98,9 @@ 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)); - // Functions with `#[rustc_const_unstable]` are const-unstable. - match tcx.lookup_const_stability(def_id) { - Some(ConstStability { level: StabilityLevel::Unstable { .. }, .. }) => return false, - Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => return true, - None => {} - } - - // Functions with `#[unstable]` are const-unstable. - // - // FIXME(ecstaticmorse): We should keep const-stability attributes wholly separate from normal stability - // attributes. `#[unstable]` should be irrelevant. - if let Some(Stability { level: StabilityLevel::Unstable { .. }, .. }) = - tcx.lookup_stability(def_id) - { - return false; - } - - true + // A function is only const-stable if it has `#[rustc_const_stable]`. + matches!( + tcx.lookup_const_stability(def_id), + Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) + ) } From 9a52401d8ede6d4ec767d5630bc68d1c5c7fb4be Mon Sep 17 00:00:00 2001 From: Jacob Pratt Date: Tue, 8 Feb 2022 04:52:11 -0500 Subject: [PATCH 2/4] Add and use stability helper methods This avoids an ambiguity (when reading) where `.level.is_stable()` is not immediately clear whether it is general stability or const stability. --- compiler/rustc_attr/src/builtin.rs | 20 +++++++++++++++++++ .../src/const_eval/fn_queries.rs | 2 +- .../src/transform/check_consts/check.rs | 2 +- compiler/rustc_middle/src/ty/context.rs | 2 +- compiler/rustc_passes/src/stability.rs | 8 +++----- src/librustdoc/clean/inline.rs | 4 ++-- src/librustdoc/clean/types.rs | 2 +- src/librustdoc/html/render/print_item.rs | 5 +---- 8 files changed, 30 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 613320087d2d..8e93766a38fa 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -101,6 +101,16 @@ pub struct Stability { pub feature: Symbol, } +impl Stability { + pub fn is_unstable(&self) -> bool { + self.level.is_unstable() + } + + pub fn is_stable(&self) -> bool { + self.level.is_stable() + } +} + /// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes. #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(HashStable_Generic)] @@ -111,6 +121,16 @@ pub struct ConstStability { pub promotable: bool, } +impl ConstStability { + pub fn is_const_unstable(&self) -> bool { + self.level.is_unstable() + } + + pub fn is_const_stable(&self) -> bool { + self.level.is_stable() + } +} + /// The available stability levels. #[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] #[derive(HashStable_Generic)] diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index 05fbbf45d7c2..7a0af60f5d92 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -9,7 +9,7 @@ use rustc_target::spec::abi::Abi; pub fn is_unstable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> Option { if tcx.is_const_fn_raw(def_id) { let const_stab = tcx.lookup_const_stability(def_id)?; - if const_stab.level.is_unstable() { Some(const_stab.feature) } else { None } + if const_stab.is_const_unstable() { Some(const_stab.feature) } else { None } } else { None } 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 7dc279cc840f..9e199845f6f4 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -938,7 +938,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // have no `rustc_const_stable` attributes to be const-unstable as well. This // should be fixed later. let callee_is_unstable_unmarked = tcx.lookup_const_stability(callee).is_none() - && tcx.lookup_stability(callee).map_or(false, |s| s.level.is_unstable()); + && tcx.lookup_stability(callee).map_or(false, |s| s.is_unstable()); if callee_is_unstable_unmarked { trace!("callee_is_unstable_unmarked"); // We do not use `const` modifiers for intrinsic "functions", as intrinsics are diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 494535f3d597..4e97cc66b460 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2785,7 +2785,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn is_const_fn(self, def_id: DefId) -> bool { if self.is_const_fn_raw(def_id) { match self.lookup_const_stability(def_id) { - Some(stability) if stability.level.is_unstable() => { + Some(stability) if stability.is_const_unstable() => { // has a `rustc_const_unstable` attribute, check whether the user enabled the // corresponding feature gate. self.features() diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index f36a1f61aacc..85fc4e1a5c0c 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -147,7 +147,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { // Propagate unstability. This can happen even for non-staged-api crates in case // -Zforce-unstable-if-unmarked is set. if let Some(stab) = self.parent_stab { - if inherit_deprecation.yes() && stab.level.is_unstable() { + if inherit_deprecation.yes() && stab.is_unstable() { self.index.stab_map.insert(def_id, stab); } } @@ -190,7 +190,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if const_stab.is_none() { debug!("annotate: const_stab not found, parent = {:?}", self.parent_const_stab); if let Some(parent) = self.parent_const_stab { - if parent.level.is_unstable() { + if parent.is_const_unstable() { self.index.const_stab_map.insert(def_id, parent); } } @@ -272,9 +272,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if stab.is_none() { debug!("annotate: stab not found, parent = {:?}", self.parent_stab); if let Some(stab) = self.parent_stab { - if inherit_deprecation.yes() && stab.level.is_unstable() - || inherit_from_parent.yes() - { + if inherit_deprecation.yes() && stab.is_unstable() || inherit_from_parent.yes() { self.index.stab_map.insert(def_id, stab); } } diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index ec538f11831b..69ebc7c66b14 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -351,7 +351,7 @@ crate fn build_impl( } if let Some(stab) = tcx.lookup_stability(did) { - if stab.level.is_unstable() && stab.feature == sym::rustc_private { + if stab.is_unstable() && stab.feature == sym::rustc_private { return; } } @@ -380,7 +380,7 @@ crate fn build_impl( } if let Some(stab) = tcx.lookup_stability(did) { - if stab.level.is_unstable() && stab.feature == sym::rustc_private { + if stab.is_unstable() && stab.feature == sym::rustc_private { return; } } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index c0e7cd0b1f58..9e86f406b0be 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -597,7 +597,7 @@ impl Item { self.stability(tcx).as_ref().and_then(|s| { let mut classes = Vec::with_capacity(2); - if s.level.is_unstable() { + if s.is_unstable() { classes.push("unstable"); } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index e6c7745c6e10..0c4363a600b1 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -430,10 +430,7 @@ fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) -> // The "rustc_private" crates are permanently unstable so it makes no sense // to render "unstable" everywhere. - if item - .stability(tcx) - .as_ref() - .map(|s| s.level.is_unstable() && s.feature != sym::rustc_private) + if item.stability(tcx).as_ref().map(|s| s.is_unstable() && s.feature != sym::rustc_private) == Some(true) { tags += &tag_html("unstable", "", "Experimental"); From 69b71de1e2e5d75c996db996c49cfef7c18c59c6 Mon Sep 17 00:00:00 2001 From: Jacob Pratt Date: Sun, 13 Feb 2022 05:54:00 -0500 Subject: [PATCH 3/4] Proper const stability check, default to unstable Rather than deferring to const eval for checking if a trait is const, we now check up-front. This allows the error to be emitted earlier, notably at the same time as other stability checks. Also included in this commit is a change of the default const stability level to UNstable. Previously, an item that was `const` but did not explicitly state it was unstable was implicitly stable. --- .../src/transform/check_consts/check.rs | 12 ----- .../src/transform/check_consts/mod.rs | 42 ++++++++++++--- compiler/rustc_interface/src/passes.rs | 4 ++ compiler/rustc_middle/src/ty/context.rs | 15 ++++++ compiler/rustc_passes/src/stability.rs | 53 ++++++++++++++++++- .../ui/consts/rustc-impl-const-stability.rs | 7 ++- .../auxiliary/staged-api.rs | 7 +-- .../ui/rfc-2632-const-trait-impl/stability.rs | 16 +++--- .../stability.stderr | 29 +++++----- .../rfc-2632-const-trait-impl/staged-api.rs | 17 +++--- .../staged-api.stable.stderr | 22 ++++++++ .../staged-api.staged.stderr | 10 ---- .../staged-api.stock.stderr | 18 ------- .../staged-api.unstable.stderr | 12 +++++ .../missing-const-stability.rs | 14 +++-- .../missing-const-stability.stderr | 11 +++- 16 files changed, 196 insertions(+), 93 deletions(-) create mode 100644 src/test/ui/rfc-2632-const-trait-impl/staged-api.stable.stderr delete mode 100644 src/test/ui/rfc-2632-const-trait-impl/staged-api.staged.stderr delete mode 100644 src/test/ui/rfc-2632-const-trait-impl/staged-api.stock.stderr create mode 100644 src/test/ui/rfc-2632-const-trait-impl/staged-api.unstable.stderr 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 9e199845f6f4..210a0fd1e223 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -218,18 +218,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 77c8f5e4fb3e..c5c1f9b04272 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs @@ -86,8 +86,6 @@ pub fn rustc_allow_const_fn_unstable(tcx: TyCtxt<'_>, def_id: DefId, feature_gat // 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. @@ -98,9 +96,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 1aceb4e95e61..c6d79d50ac7f 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -982,6 +982,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 4e97cc66b460..103a41da17ca 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2802,6 +2802,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 85fc4e1a5c0c..15cf8da60023 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::{DefId, LocalDefId, CRATE_DEF_ID, CRATE_DEF_INDEX}; 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 e3fda57548ea..0c18efa0a023 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 80e61ab0a9e8..19e9006094be 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 906956f5eba4..15f1db18f89d 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 7473b801cce6..fa3d85a3e6ad 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 39a1b6066dea..903ced42698a 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 000000000000..2fde51217f5d --- /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 d2ff4ce20013..000000000000 --- 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 91c5469bd90a..000000000000 --- 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 000000000000..4ea1be69b3bb --- /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 57e64737d0fa..d89886af314a 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 7eba99a477ab..6f2ade0d0abf 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 From 6a45f4d71ecabd7471a6a117b0f51fb99addefd3 Mon Sep 17 00:00:00 2001 From: Jacob Pratt Date: Tue, 22 Feb 2022 14:54:47 -0500 Subject: [PATCH 4/4] Move check to existing pass This alters the diagnostics a bit, as the trait method is still stable. The only thing this check does is ensure that compilation fails if a trait implementation is declared const-stable. --- compiler/rustc_interface/src/passes.rs | 4 - compiler/rustc_passes/src/stability.rs | 82 ++++++------------- .../ui/rfc-2632-const-trait-impl/stability.rs | 43 ---------- .../stability.stderr | 24 ------ .../rfc-2632-const-trait-impl/staged-api.rs | 24 ++++-- .../staged-api.stable.stderr | 17 +--- .../staged-api.unstable.stderr | 26 +++--- 7 files changed, 62 insertions(+), 158 deletions(-) delete mode 100644 src/test/ui/rfc-2632-const-trait-impl/stability.rs delete mode 100644 src/test/ui/rfc-2632-const-trait-impl/stability.stderr diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index c6d79d50ac7f..1aceb4e95e61 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -982,10 +982,6 @@ 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_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 15cf8da60023..a725bce81d16 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -9,7 +9,6 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, CRATE_DEF_INDEX}; 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; @@ -606,44 +605,6 @@ 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(), @@ -748,16 +709,23 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { // For implementations of traits, check the stability of each item // individually as it's possible to have a stable trait with unstable // items. - hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref t), self_ty, items, .. }) => { - if self.tcx.features().staged_api { + hir::ItemKind::Impl(hir::Impl { + of_trait: Some(ref t), + self_ty, + items, + constness, + .. + }) => { + let features = self.tcx.features(); + if features.staged_api { + let attrs = self.tcx.hir().attrs(item.hir_id()); + let (stab, const_stab) = attr::find_stability(&self.tcx.sess, attrs, item.span); + // If this impl block has an #[unstable] attribute, give an // error if all involved types and traits are stable, because // it will have no effect. // See: https://github.com/rust-lang/rust/issues/55436 - let attrs = self.tcx.hir().attrs(item.hir_id()); - if let (Some((Stability { level: attr::Unstable { .. }, .. }, span)), _) = - attr::find_stability(&self.tcx.sess, attrs, item.span) - { + if let Some((Stability { level: attr::Unstable { .. }, .. }, span)) = stab { let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true }; c.visit_ty(self_ty); c.visit_trait_ref(t); @@ -773,6 +741,19 @@ 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 + && constness == hir::Constness::Const + && const_stab.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(); + } } for impl_item_ref in items { @@ -864,17 +845,6 @@ 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/rfc-2632-const-trait-impl/stability.rs b/src/test/ui/rfc-2632-const-trait-impl/stability.rs deleted file mode 100644 index 15f1db18f89d..000000000000 --- a/src/test/ui/rfc-2632-const-trait-impl/stability.rs +++ /dev/null @@ -1,43 +0,0 @@ -#![feature(const_trait_impl)] -#![feature(staged_api)] -#![stable(feature = "rust1", since = "1.0.0")] - -#[stable(feature = "rust1", since = "1.0.0")] -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 { - Int(self.0 - rhs.0) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_add", issue = "none")] -impl const std::ops::Add for Int { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - Int(self.0 + rhs.0) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_stable(feature = "rust1", since = "1.0.0")] -pub const fn const_err() { - Int(0) + Int(0); - //~^ ERROR not yet stable as a const fn - Int(0) - Int(0); -} - -#[stable(feature = "rust1", since = "1.0.0")] -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 deleted file mode 100644 index fa3d85a3e6ad..000000000000 --- a/src/test/ui/rfc-2632-const-trait-impl/stability.stderr +++ /dev/null @@ -1,24 +0,0 @@ -error: `::add` is not yet stable as a const fn - --> $DIR/stability.rs:32:5 - | -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 903ced42698a..f8a55a0a4a02 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 @@ -11,26 +11,36 @@ extern crate staged_api; use staged_api::*; #[stable(feature = "rust1", since = "1.0.0")] -pub struct Stable; +pub struct Foo; #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(stable, rustc_const_stable(feature = "rust1", since = "1.0.0"))] -impl const MyTrait for Stable { +#[cfg_attr(unstable, rustc_const_unstable(feature = "foo", issue = "none"))] +impl const MyTrait for Foo { //[stable]~^ ERROR trait implementations cannot be const stable yet - //[unstable]~^^ ERROR implementation has missing const stability attribute fn func() {} } +// Const stability has no impact on usage in non-const contexts. fn non_const_context() { Unstable::func(); - Stable::func(); + Foo::func(); } #[unstable(feature = "none", issue = "none")] const fn const_context() { Unstable::func(); - //[stable]~^ ERROR `::func` is not yet stable as a const fn - Stable::func(); + // ^ This is okay regardless of whether the `unstable` feature is enabled, as this function is + // not const-stable. + Foo::func(); + //[unstable]~^ not yet stable as a const fn +} + +#[stable(feature = "rust1", since = "1.0.0")] +const fn stable_const_context() { + Unstable::func(); + //[unstable]~^ ERROR not yet stable as a const fn + Foo::func(); + //[unstable]~^ ERROR not yet stable as a const fn } fn main() {} 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 index 2fde51217f5d..25407a0b43c1 100644 --- 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 @@ -1,22 +1,11 @@ -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 +error: implementation has missing const stability attribute --> $DIR/staged-api.rs:18:1 | -LL | / impl const MyTrait for Stable { -LL | | +LL | / impl const MyTrait for Foo { LL | | LL | | fn func() {} LL | | } | |_^ - | - = note: see issue #67792 for more information -error: aborting due to 2 previous errors +error: aborting due to previous error 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 index 4ea1be69b3bb..30fe97bb884b 100644 --- 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 @@ -1,12 +1,18 @@ -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: `::func` is not yet stable as a const fn + --> $DIR/staged-api.rs:34:5 + | +LL | Foo::func(); + | ^^^^^^^^^^^ + | + = help: add `#![feature(foo)]` to the crate attributes to enable + +error: `::func` is not yet stable as a const fn + --> $DIR/staged-api.rs:42:5 + | +LL | Foo::func(); + | ^^^^^^^^^^^ + | + = help: add `#![feature(foo)]` to the crate attributes to enable -error: aborting due to previous error +error: aborting due to 2 previous errors