Rollup merge of #135423 - compiler-errors:enforce-const-trait-syntactical, r=oli-obk,RalfJung

Enforce syntactical stability of const traits in HIR

This PR enforces what I'm calling *syntactical* const stability of traits. In other words, it enforces the ability to name `~const`/`const` traits in trait bounds in various syntax positions in HIR (including in the trait of an impl header). This functionality is analogous to the *regular* item stability checker, which is concerned with making sure that you cannot refer to unstable items by name, and is implemented as an extension of that pass.

This is separate from enforcing the *recursive* const stability of const trait methods, which is implemented in MIR and runs on MIR bodies. That will require adding a new `NonConstOp` to the const checker and probably adjusting some logic to deduplicate redundant errors.

However, this check is separate and necessary for making sure that users don't add `~const`/`const` bounds to items when the trait is not const-stable in the first place. I chose to separate enforcing recursive const stability out of this PR to make it easier to review. I'll probably open a follow-up following this one, blocked on this PR.

r? `@RalfJung` cc `@rust-lang/project-const-traits`
This commit is contained in:
Jubilee 2025-01-14 19:56:33 -08:00 committed by GitHub
commit 11ac57af6e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 279 additions and 38 deletions

View file

@ -30,6 +30,14 @@ pub enum StabilityLevel {
Stable,
}
#[derive(Copy, Clone)]
pub enum UnstableKind {
/// Enforcing regular stability of an item
Regular,
/// Enforcing const stability of an item
Const(Span),
}
/// An entry in the `depr_map`.
#[derive(Copy, Clone, HashStable, Debug, Encodable, Decodable)]
pub struct DeprecationEntry {
@ -108,10 +116,16 @@ pub fn report_unstable(
is_soft: bool,
span: Span,
soft_handler: impl FnOnce(&'static Lint, Span, String),
kind: UnstableKind,
) {
let qual = match kind {
UnstableKind::Regular => "",
UnstableKind::Const(_) => " const",
};
let msg = match reason {
Some(r) => format!("use of unstable library feature `{feature}`: {r}"),
None => format!("use of unstable library feature `{feature}`"),
Some(r) => format!("use of unstable{qual} library feature `{feature}`: {r}"),
None => format!("use of unstable{qual} library feature `{feature}`"),
};
if is_soft {
@ -121,6 +135,9 @@ pub fn report_unstable(
if let Some((inner_types, msg, sugg, applicability)) = suggestion {
err.span_suggestion(inner_types, msg, sugg, applicability);
}
if let UnstableKind::Const(kw) = kind {
err.span_label(kw, "trait is not stable as const yet");
}
err.emit();
}
}
@ -587,6 +604,7 @@ impl<'tcx> TyCtxt<'tcx> {
is_soft,
span,
soft_handler,
UnstableKind::Regular,
),
EvalResult::Unmarked => unmarked(span, def_id),
}
@ -594,6 +612,73 @@ impl<'tcx> TyCtxt<'tcx> {
is_allowed
}
/// This function is analogous to `check_optional_stability` but with the logic in
/// `eval_stability_allow_unstable` inlined, and which operating on const stability
/// instead of regular stability.
///
/// This enforces *syntactical* const stability of const traits. In other words,
/// it enforces the ability to name `~const`/`const` traits in trait bounds in various
/// syntax positions in HIR (including in the trait of an impl header).
pub fn check_const_stability(self, def_id: DefId, span: Span, const_kw_span: Span) {
let is_staged_api = self.lookup_stability(def_id.krate.as_def_id()).is_some();
if !is_staged_api {
return;
}
// Only the cross-crate scenario matters when checking unstable APIs
let cross_crate = !def_id.is_local();
if !cross_crate {
return;
}
let stability = self.lookup_const_stability(def_id);
debug!(
"stability: \
inspecting def_id={:?} span={:?} of stability={:?}",
def_id, span, stability
);
match stability {
Some(ConstStability {
level: attr::StabilityLevel::Unstable { reason, issue, is_soft, implied_by, .. },
feature,
..
}) => {
assert!(!is_soft);
if span.allows_unstable(feature) {
debug!("body stability: skipping span={:?} since it is internal", span);
return;
}
if self.features().enabled(feature) {
return;
}
// If this item was previously part of a now-stabilized feature which is still
// enabled (i.e. the user hasn't removed the attribute for the stabilized feature
// yet) then allow use of this item.
if let Some(implied_by) = implied_by
&& self.features().enabled(implied_by)
{
return;
}
report_unstable(
self.sess,
feature,
reason.to_opt_reason(),
issue,
None,
false,
span,
|_, _, _| {},
UnstableKind::Const(const_kw_span),
);
}
Some(_) | None => {}
}
}
pub fn lookup_deprecation(self, id: DefId) -> Option<Deprecation> {
self.lookup_deprecation_entry(id).map(|depr| depr.attr)
}

View file

@ -593,9 +593,11 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> {
}
fn check_missing_const_stability(&self, def_id: LocalDefId, span: Span) {
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.def_kind(def_id.to_def_id()) == DefKind::Trait
&& self.tcx.is_const_trait(def_id.to_def_id()));
// Reachable const fn must have a stability attribute.
// Reachable const fn/trait must have a stability attribute.
if is_const
&& self.effective_visibilities.is_reachable(def_id)
&& self.tcx.lookup_const_stability(def_id).is_none()
@ -772,7 +774,13 @@ 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, .. }) => {
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());
@ -814,6 +822,16 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
}
}
match constness {
rustc_hir::Constness::Const => {
if let Some(def_id) = t.trait_def_id() {
// FIXME(const_trait_impl): Improve the span here.
self.tcx.check_const_stability(def_id, t.path.span, t.path.span);
}
}
rustc_hir::Constness::NotConst => {}
}
for impl_item_ref in *items {
let impl_item = self.tcx.associated_item(impl_item_ref.id.owner_id);
@ -829,6 +847,18 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
intravisit::walk_item(self, item);
}
fn visit_poly_trait_ref(&mut self, t: &'tcx hir::PolyTraitRef<'tcx>) {
match t.modifiers.constness {
hir::BoundConstness::Always(span) | hir::BoundConstness::Maybe(span) => {
if let Some(def_id) = t.trait_ref.trait_def_id() {
self.tcx.check_const_stability(def_id, t.trait_ref.path.span, span);
}
}
hir::BoundConstness::Never => {}
}
intravisit::walk_poly_trait_ref(self, t);
}
fn visit_path(&mut self, path: &hir::Path<'tcx>, id: hir::HirId) {
if let Some(def_id) = path.res.opt_def_id() {
let method_span = path.segments.last().map(|s| s.ident.span);

View file

@ -1031,6 +1031,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
is_soft,
span,
soft_handler,
stability::UnstableKind::Regular,
);
}
}

View file

@ -8,6 +8,7 @@
#![allow(missing_docs)]
#[const_trait]
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
pub trait CarryingMulAdd: Copy + 'static {
type Unsigned: Copy + 'static;
fn carrying_mul_add(

View file

@ -952,6 +952,7 @@ marker_impls! {
/// This should be used for `~const` bounds,
/// as non-const bounds will always hold for every type.
#[unstable(feature = "const_destruct", issue = "133214")]
#[rustc_const_unstable(feature = "const_destruct", issue = "133214")]
#[lang = "destruct"]
#[rustc_on_unimplemented(message = "can't drop `{Self}`", append_const_msg)]
#[rustc_deny_explicit_impl]

View file

@ -65,6 +65,7 @@
/// ```
#[lang = "add"]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_ops", issue = "90080")]
#[rustc_on_unimplemented(
on(all(_Self = "{integer}", Rhs = "{float}"), message = "cannot add a float to an integer",),
on(all(_Self = "{float}", Rhs = "{integer}"), message = "cannot add an integer to a float",),

View file

@ -134,6 +134,7 @@
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_diagnostic_item = "Deref"]
#[const_trait]
#[rustc_const_unstable(feature = "const_deref", issue = "88955")]
pub trait Deref {
/// The resulting type after dereferencing.
#[stable(feature = "rust1", since = "1.0.0")]
@ -263,6 +264,7 @@ impl<T: ?Sized> const Deref for &mut T {
#[doc(alias = "*")]
#[stable(feature = "rust1", since = "1.0.0")]
#[const_trait]
#[rustc_const_unstable(feature = "const_deref", issue = "88955")]
pub trait DerefMut: ~const Deref {
/// Mutably dereferences the value.
#[stable(feature = "rust1", since = "1.0.0")]

View file

@ -204,6 +204,7 @@
#[lang = "drop"]
#[stable(feature = "rust1", since = "1.0.0")]
#[const_trait]
#[rustc_const_unstable(feature = "const_destruct", issue = "133214")]
pub trait Drop {
/// Executes the destructor for this type.
///

View file

@ -1,4 +1,4 @@
#![feature(const_trait_impl)]
#![feature(const_trait_impl, const_destruct)]
struct A();

View file

@ -1,6 +1,4 @@
//@ known-bug: #103507
#![feature(const_trait_impl)]
#![feature(const_trait_impl, const_destruct)]
struct Panic;
impl const Drop for Panic { fn drop(&mut self) { panic!(); } }
@ -8,15 +6,15 @@ impl const Drop for Panic { fn drop(&mut self) { panic!(); } }
pub const fn id<T>(x: T) -> T { x }
pub const C: () = {
let _: &'static _ = &id(&Panic);
//FIXME ~^ ERROR: temporary value dropped while borrowed
//FIXME ~| ERROR: temporary value dropped while borrowed
//~^ ERROR: temporary value dropped while borrowed
//~| ERROR: temporary value dropped while borrowed
};
fn main() {
let _: &'static _ = &id(&Panic);
//FIXME ~^ ERROR: temporary value dropped while borrowed
//FIXME ~| ERROR: temporary value dropped while borrowed
//~^ ERROR: temporary value dropped while borrowed
//~| ERROR: temporary value dropped while borrowed
let _: &'static _ = &&(Panic, 0).1;
//FIXME~^ ERROR: temporary value dropped while borrowed
//FIXME~| ERROR: temporary value dropped while borrowed
//~^ ERROR: temporary value dropped while borrowed
//~| ERROR: temporary value dropped while borrowed
}

View file

@ -1,17 +1,25 @@
error[E0493]: destructor of `Panic` cannot be evaluated at compile-time
--> $DIR/promoted_const_call.rs:10:30
error[E0716]: temporary value dropped while borrowed
--> $DIR/promoted_const_call.rs:8:26
|
LL | let _: &'static _ = &id(&Panic);
| ^^^^^ - value is dropped here
| |
| the destructor for this type cannot be evaluated in constants
|
= note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
= help: add `#![feature(const_destruct)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
| ---------- ^^^^^^^^^^ creates a temporary value which is freed while still in use
| |
| type annotation requires that borrow lasts for `'static`
...
LL | };
| - temporary value is freed at the end of this statement
error[E0716]: temporary value dropped while borrowed
--> $DIR/promoted_const_call.rs:16:26
--> $DIR/promoted_const_call.rs:8:30
|
LL | let _: &'static _ = &id(&Panic);
| ---------- ^^^^^ - temporary value is freed at the end of this statement
| | |
| | creates a temporary value which is freed while still in use
| type annotation requires that borrow lasts for `'static`
error[E0716]: temporary value dropped while borrowed
--> $DIR/promoted_const_call.rs:14:26
|
LL | let _: &'static _ = &id(&Panic);
| ---------- ^^^^^^^^^^ creates a temporary value which is freed while still in use
@ -22,7 +30,7 @@ LL | }
| - temporary value is freed at the end of this statement
error[E0716]: temporary value dropped while borrowed
--> $DIR/promoted_const_call.rs:16:30
--> $DIR/promoted_const_call.rs:14:30
|
LL | let _: &'static _ = &id(&Panic);
| ---------- ^^^^^ - temporary value is freed at the end of this statement
@ -31,7 +39,7 @@ LL | let _: &'static _ = &id(&Panic);
| type annotation requires that borrow lasts for `'static`
error[E0716]: temporary value dropped while borrowed
--> $DIR/promoted_const_call.rs:19:26
--> $DIR/promoted_const_call.rs:17:26
|
LL | let _: &'static _ = &&(Panic, 0).1;
| ---------- ^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
@ -42,7 +50,7 @@ LL | }
| - temporary value is freed at the end of this statement
error[E0716]: temporary value dropped while borrowed
--> $DIR/promoted_const_call.rs:19:27
--> $DIR/promoted_const_call.rs:17:27
|
LL | let _: &'static _ = &&(Panic, 0).1;
| ---------- ^^^^^^^^^^ creates a temporary value which is freed while still in use
@ -52,7 +60,6 @@ LL | let _: &'static _ = &&(Panic, 0).1;
LL | }
| - temporary value is freed at the end of this statement
error: aborting due to 5 previous errors
error: aborting due to 6 previous errors
Some errors have detailed explanations: E0493, E0716.
For more information about an error, try `rustc --explain E0493`.
For more information about this error, try `rustc --explain E0716`.

View file

@ -22,6 +22,7 @@ impl Foo {
#[stable(feature = "stable", since = "1.0.0")]
#[const_trait]
pub trait Bar {
//~^ ERROR trait has missing const stability attribute
#[stable(feature = "stable", since = "1.0.0")]
fn fun();
}

View file

@ -4,8 +4,18 @@ error: function has missing const stability attribute
LL | pub const fn foo() {}
| ^^^^^^^^^^^^^^^^^^^^^
error: trait has missing const stability attribute
--> $DIR/missing-const-stability.rs:24:1
|
LL | / pub trait Bar {
LL | |
LL | | #[stable(feature = "stable", since = "1.0.0")]
LL | | fn fun();
LL | | }
| |_^
error: function has missing const stability attribute
--> $DIR/missing-const-stability.rs:36:1
--> $DIR/missing-const-stability.rs:37:1
|
LL | pub const unsafe fn size_of_val<T>(x: *const T) -> usize { 42 }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -16,5 +26,5 @@ error: associated function has missing const stability attribute
LL | pub const fn foo() {}
| ^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors
error: aborting due to 4 previous errors

View file

@ -4,6 +4,7 @@
#![stable(feature = "rust1", since = "1.0.0")]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "unstable", issue = "none")]
#[const_trait]
pub trait MyTrait {
#[stable(feature = "rust1", since = "1.0.0")]

View file

@ -1,6 +1,6 @@
//@ known-bug: #110395
#![feature(const_trait_impl)]
#![feature(const_trait_impl, const_ops)]
struct Int(i32);

View file

@ -1,6 +1,6 @@
//@ known-bug: #110395
#![feature(const_trait_impl)]
#![feature(const_trait_impl, const_ops)]
pub struct Int(i32);

View file

@ -1,6 +1,6 @@
//@ check-pass
#![feature(const_trait_impl)]
#![feature(const_trait_impl, const_ops)]
use std::marker::PhantomData;

View file

@ -0,0 +1,34 @@
//@ aux-build:staged-api.rs
// Ensure that we enforce const stability of traits in `~const`/`const` bounds.
#![feature(const_trait_impl)]
use std::ops::Deref;
extern crate staged_api;
use staged_api::MyTrait;
#[const_trait]
trait Foo: ~const MyTrait {
//~^ ERROR use of unstable const library feature `unstable`
type Item: ~const MyTrait;
//~^ ERROR use of unstable const library feature `unstable`
}
const fn where_clause<T>() where T: ~const MyTrait {}
//~^ ERROR use of unstable const library feature `unstable`
const fn nested<T>() where T: Deref<Target: ~const MyTrait> {}
//~^ ERROR use of unstable const library feature `unstable`
const fn rpit() -> impl ~const MyTrait { Local }
//~^ ERROR use of unstable const library feature `unstable`
struct Local;
impl const MyTrait for Local {
//~^ ERROR use of unstable const library feature `unstable`
fn func() {}
}
fn main() {}

View file

@ -0,0 +1,67 @@
error[E0658]: use of unstable const library feature `unstable`
--> $DIR/syntactical-unstable.rs:13:19
|
LL | trait Foo: ~const MyTrait {
| ------ ^^^^^^^
| |
| trait is not stable as const yet
|
= help: add `#![feature(unstable)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: use of unstable const library feature `unstable`
--> $DIR/syntactical-unstable.rs:19:44
|
LL | const fn where_clause<T>() where T: ~const MyTrait {}
| ------ ^^^^^^^
| |
| trait is not stable as const yet
|
= help: add `#![feature(unstable)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: use of unstable const library feature `unstable`
--> $DIR/syntactical-unstable.rs:22:52
|
LL | const fn nested<T>() where T: Deref<Target: ~const MyTrait> {}
| ------ ^^^^^^^
| |
| trait is not stable as const yet
|
= help: add `#![feature(unstable)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: use of unstable const library feature `unstable`
--> $DIR/syntactical-unstable.rs:25:32
|
LL | const fn rpit() -> impl ~const MyTrait { Local }
| ------ ^^^^^^^
| |
| trait is not stable as const yet
|
= help: add `#![feature(unstable)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: use of unstable const library feature `unstable`
--> $DIR/syntactical-unstable.rs:29:12
|
LL | impl const MyTrait for Local {
| ^^^^^^^ trait is not stable as const yet
|
= help: add `#![feature(unstable)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: use of unstable const library feature `unstable`
--> $DIR/syntactical-unstable.rs:15:23
|
LL | type Item: ~const MyTrait;
| ------ ^^^^^^^
| |
| trait is not stable as const yet
|
= help: add `#![feature(unstable)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0658`.

View file

@ -38,6 +38,7 @@ impl const FromResidual for T {
}
#[stable(feature = "foo", since = "1.0")]
#[rustc_const_unstable(feature = "const_tr", issue = "none")]
#[const_trait]
pub trait Tr {
#[stable(feature = "foo", since = "1.0")]

View file

@ -17,7 +17,7 @@ LL | impl const FromResidual for T {
= note: adding a non-const method body in the future would be a breaking change
error[E0015]: `?` is not allowed on `T` in constant functions
--> $DIR/trait-default-body-stability.rs:45:9
--> $DIR/trait-default-body-stability.rs:46:9
|
LL | T?
| ^^
@ -25,7 +25,7 @@ LL | T?
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
error[E0015]: `?` is not allowed on `T` in constant functions
--> $DIR/trait-default-body-stability.rs:45:9
--> $DIR/trait-default-body-stability.rs:46:9
|
LL | T?
| ^^