mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-14 04:56:49 +00:00
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:
parent
a9dd4cfa6b
commit
f0620c9503
@ -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 {
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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> {
|
||||
|
@ -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.
|
||||
|
@ -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 }
|
||||
}
|
||||
|
@ -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() {}
|
||||
}
|
||||
|
@ -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() {}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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() {}
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user