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.
This commit is contained in:
Jacob Pratt 2022-02-13 05:54:00 -05:00 committed by Oli Scherer
parent a9dd4cfa6b
commit f0620c9503
16 changed files with 196 additions and 93 deletions

View File

@ -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 {

View File

@ -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())
}

View File

@ -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,

View File

@ -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> {

View File

@ -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.

View File

@ -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 }
}

View File

@ -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() {}
}

View File

@ -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() {}

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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() {}

View File

@ -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