about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJacob Pratt <jacob@jhpratt.dev>2022-02-13 05:54:00 -0500
committerOli Scherer <git-spam-no-reply9815368754983@oli-obk.de>2022-05-19 12:21:45 +0000
commitf0620c95038c1e586f5165d5d3be7cf009aaf387 (patch)
tree0df4aedeb4c4593fca47616f7d209abea39224bf
parenta9dd4cfa6b5398b945d3ad39e01bedb5d861ed60 (diff)
downloadrust-f0620c95038c1e586f5165d5d3be7cf009aaf387.tar.gz
rust-f0620c95038c1e586f5165d5d3be7cf009aaf387.zip
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.
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs12
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/mod.rs42
-rw-r--r--compiler/rustc_interface/src/passes.rs4
-rw-r--r--compiler/rustc_middle/src/ty/context.rs15
-rw-r--r--compiler/rustc_passes/src/stability.rs53
-rw-r--r--src/test/ui/consts/rustc-impl-const-stability.rs7
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/auxiliary/staged-api.rs7
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/stability.rs16
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/stability.stderr29
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/staged-api.rs17
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/staged-api.stable.stderr22
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/staged-api.staged.stderr10
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/staged-api.stock.stderr18
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/staged-api.unstable.stderr12
-rw-r--r--src/test/ui/stability-attribute/missing-const-stability.rs14
-rw-r--r--src/test/ui/stability-attribute/missing-const-stability.stderr11
16 files changed, 196 insertions, 93 deletions
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 <https://github.com/rust-lang/rust/issues/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: `<Int as Add>::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 <https://github.com/rust-lang/rust/issues/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 `<staged_api::Unstable as staged_api::MyTrait>::func` is not yet stable as a const fn
+    //[stable]~^ ERROR `<staged_api::Unstable as staged_api::MyTrait>::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: `<staged_api::Unstable as staged_api::MyTrait>::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 <https://github.com/rust-lang/rust/issues/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: `<staged_api::Unstable as staged_api::MyTrait>::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