Enforce syntactical stability of const traits in HIR

This commit is contained in:
Michael Goulet 2025-01-12 20:52:30 +00:00
parent 8c39ce5b4f
commit 2743df848b
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?
| ^^