Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforce syntactical stability of const traits in HIR #135423

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 87 additions & 2 deletions compiler/rustc_middle/src/middle/stability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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();
}
}
Expand Down Expand Up @@ -587,13 +604,81 @@ impl<'tcx> TyCtxt<'tcx> {
is_soft,
span,
soft_handler,
UnstableKind::Regular,
),
EvalResult::Unmarked => unmarked(span, def_id),
}

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) {
Copy link
Member Author

@compiler-errors compiler-errors Jan 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

And I don't think we have soft unstability for consts, so I asserted against that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems like a comment worth putting into the code.

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)
}
Expand Down
36 changes: 33 additions & 3 deletions compiler/rustc_passes/src/stability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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);

Expand All @@ -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);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_resolve/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1031,6 +1031,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
is_soft,
span,
soft_handler,
stability::UnstableKind::Regular,
);
}
}
Expand Down
1 change: 1 addition & 0 deletions library/core/src/intrinsics/fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
1 change: 1 addition & 0 deletions library/core/src/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
1 change: 1 addition & 0 deletions library/core/src/ops/arith.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",),
Expand Down
2 changes: 2 additions & 0 deletions library/core/src/ops/deref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -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")]
Expand Down
1 change: 1 addition & 0 deletions library/core/src/ops/drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/consts/promoted-const-drop.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![feature(const_trait_impl)]
#![feature(const_trait_impl, const_destruct)]

struct A();

Expand Down
16 changes: 7 additions & 9 deletions tests/ui/consts/promoted_const_call.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
//@ 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!(); } }

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
}
37 changes: 22 additions & 15 deletions tests/ui/consts/promoted_const_call.stderr
Original file line number Diff line number Diff line change
@@ -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
| ---------- ^^^^^^^^^^ 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:8:30
|
= 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
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:16:26
--> $DIR/promoted_const_call.rs:14:26
|
LL | let _: &'static _ = &id(&Panic);
| ---------- ^^^^^^^^^^ creates a temporary value which is freed while still in use
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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`.
1 change: 1 addition & 0 deletions tests/ui/stability-attribute/missing-const-stability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
14 changes: 12 additions & 2 deletions tests/ui/stability-attribute/missing-const-stability.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -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

1 change: 1 addition & 0 deletions tests/ui/traits/const-traits/auxiliary/staged-api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
Loading
Loading