Skip to content

Commit

Permalink
Rollup merge of #135423 - compiler-errors:enforce-const-trait-syntact…
Browse files Browse the repository at this point in the history
…ical, 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`
  • Loading branch information
workingjubilee authored Jan 15, 2025
2 parents 7c85da9 + 2743df8 commit 11ac57a
Show file tree
Hide file tree
Showing 21 changed files with 278 additions and 37 deletions.
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) {
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

0 comments on commit 11ac57a

Please sign in to comment.