Skip to content
Open
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
2 changes: 1 addition & 1 deletion compiler/rustc_ast_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ ast_passes_trait_fn_const =
*[false] {""}
}
.make_impl_const_sugg = ... and declare the impl to be const instead
.make_trait_const_sugg = ... and declare the trait to be a `#[const_trait]` instead
.make_trait_const_sugg = ... and declare the trait to be const instead
ast_passes_trait_object_single_bound = only a single explicit lifetime bound is permitted
Expand Down
23 changes: 9 additions & 14 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ enum SelfSemantic {
}

enum TraitOrTraitImpl {
Trait { span: Span, constness: Const },
Trait { vis: Span, constness: Const },
TraitImpl { constness: Const, polarity: ImplPolarity, trait_ref_span: Span },
}

Expand Down Expand Up @@ -109,10 +109,10 @@ impl<'a> AstValidator<'a> {
self.outer_trait_or_trait_impl = old;
}

fn with_in_trait(&mut self, span: Span, constness: Const, f: impl FnOnce(&mut Self)) {
fn with_in_trait(&mut self, vis: Span, constness: Const, f: impl FnOnce(&mut Self)) {
let old = mem::replace(
&mut self.outer_trait_or_trait_impl,
Some(TraitOrTraitImpl::Trait { span, constness }),
Some(TraitOrTraitImpl::Trait { vis, constness }),
);
f(self);
self.outer_trait_or_trait_impl = old;
Expand Down Expand Up @@ -265,10 +265,12 @@ impl<'a> AstValidator<'a> {
None
};

let map = self.sess.source_map();

let make_trait_const_sugg = if const_trait_impl
&& let TraitOrTraitImpl::Trait { span, constness: ast::Const::No } = parent
&& let &TraitOrTraitImpl::Trait { vis, constness: ast::Const::No } = parent
{
Some(span.shrink_to_lo())
Some(map.span_extend_while_whitespace(vis).shrink_to_hi())
Copy link
Member Author

@fmease fmease Nov 8, 2025

Choose a reason for hiding this comment

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

This pattern occurs in three more places (tho you can't really abstract over it). It yields a syntactically correct suggestion with any(*) combination of trait modifiers/qualifiers present and can deal with inherited visibility, pub , pub(…) and pub(…) (i.e., no trailing space).

(*): It does not account for specialization's default which theoretically comes before const (like visibility), however that's actually syntactically invalid but the parser does recover from it. Still, it's really not worth accounting for.

} else {
None
};
Expand All @@ -279,7 +281,7 @@ impl<'a> AstValidator<'a> {
in_impl: matches!(parent, TraitOrTraitImpl::TraitImpl { .. }),
const_context_label: parent_constness,
remove_const_sugg: (
self.sess.source_map().span_extend_while_whitespace(span),
map.span_extend_while_whitespace(span),
match parent_constness {
Some(_) => rustc_errors::Applicability::MachineApplicable,
None => rustc_errors::Applicability::MaybeIncorrect,
Expand Down Expand Up @@ -1165,13 +1167,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
..
}) => {
self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
// FIXME(const_trait_impl) remove this
let alt_const_trait_span =
attr::find_by_name(&item.attrs, sym::const_trait).map(|attr| attr.span);
let constness = match (*constness, alt_const_trait_span) {
(Const::Yes(span), _) | (Const::No, Some(span)) => Const::Yes(span),
(Const::No, None) => Const::No,
};
if *is_auto == IsAuto::Yes {
// Auto traits cannot have generics, super traits nor contain items.
self.deny_generic_params(generics, ident.span);
Expand All @@ -1188,7 +1183,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
this.visit_generics(generics);
walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits)
});
self.with_in_trait(item.span, constness, |this| {
self.with_in_trait(item.vis.span, *constness, |this| {
walk_list!(this, visit_assoc_item, items, AssocCtxt::Trait);
});
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ pub(crate) struct TraitFnConst {
pub make_impl_const_sugg: Option<Span>,
#[suggestion(
ast_passes_make_trait_const_sugg,
code = "#[const_trait]\n",
code = "const ",
applicability = "maybe-incorrect"
)]
pub make_trait_const_sugg: Option<Span>,
Expand Down
11 changes: 0 additions & 11 deletions compiler/rustc_attr_parsing/src/attributes/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,17 +101,6 @@ impl<S: Stage> NoArgsAttributeParser<S> for DoNotImplementViaObjectParser {
const CREATE: fn(Span) -> AttributeKind = AttributeKind::DoNotImplementViaObject;
}

// FIXME(const_trait_impl): remove this
// Const traits

pub(crate) struct ConstTraitParser;
impl<S: Stage> NoArgsAttributeParser<S> for ConstTraitParser {
const PATH: &[Symbol] = &[sym::const_trait];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::ConstTrait;
}

// Specialization

pub(crate) struct SpecializationTraitParser;
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ use crate::attributes::stability::{
};
use crate::attributes::test_attrs::{IgnoreParser, ShouldPanicParser};
use crate::attributes::traits::{
AllowIncoherentImplParser, CoinductiveParser, ConstTraitParser, DenyExplicitImplParser,
AllowIncoherentImplParser, CoinductiveParser, DenyExplicitImplParser,
DoNotImplementViaObjectParser, FundamentalParser, MarkerParser, ParenSugarParser,
PointeeParser, SkipDuringMethodDispatchParser, SpecializationTraitParser, TypeConstParser,
UnsafeSpecializationMarkerParser,
Expand Down Expand Up @@ -218,7 +218,6 @@ attribute_parsers!(
Single<WithoutArgs<ColdParser>>,
Single<WithoutArgs<ConstContinueParser>>,
Single<WithoutArgs<ConstStabilityIndirectParser>>,
Single<WithoutArgs<ConstTraitParser>>,
Single<WithoutArgs<CoroutineParser>>,
Single<WithoutArgs<DenyExplicitImplParser>>,
Single<WithoutArgs<DoNotImplementViaObjectParser>>,
Expand Down
18 changes: 8 additions & 10 deletions compiler/rustc_const_eval/src/check_consts/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,23 +381,21 @@ fn build_error_for_const_call<'tcx>(
`{trait_name}` is not const",
),
);
if parent.is_local() && ccx.tcx.sess.is_nightly_build() {
if let Some(parent) = parent.as_local()
&& ccx.tcx.sess.is_nightly_build()
{
if !ccx.tcx.features().const_trait_impl() {
err.help(
"add `#![feature(const_trait_impl)]` to the crate attributes to \
enable `#[const_trait]`",
enable const traits",
);
}
let indentation = ccx
.tcx
.sess
.source_map()
.indentation_before(trait_span)
.unwrap_or_default();
let span = ccx.tcx.hir_expect_item(parent).vis_span;
let span = ccx.tcx.sess.source_map().span_extend_while_whitespace(span);
err.span_suggestion_verbose(
trait_span.shrink_to_lo(),
span.shrink_to_hi(),
format!("consider making trait `{trait_name}` const"),
format!("#[const_trait]\n{indentation}"),
"const ".to_owned(),
Applicability::MaybeIncorrect,
);
} else if !ccx.tcx.sess.is_nightly_build() {
Expand Down
8 changes: 0 additions & 8 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -846,14 +846,6 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
EncodeCrossCrate::No, experimental!(register_tool),
),

// RFC 2632
// FIXME(const_trait_impl) remove this
gated!(
const_trait, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, const_trait_impl,
"`const_trait` is a temporary placeholder for marking a trait that is suitable for `const` \
`impls` and all default bodies as `const`, which may be removed or renamed in the \
future."
),
// lang-team MCP 147
gated!(
deprecated_safe, Normal, template!(List: &[r#"since = "version", note = "...""#]), ErrorFollowing,
Expand Down
3 changes: 0 additions & 3 deletions compiler/rustc_hir/src/attrs/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -489,9 +489,6 @@ pub enum AttributeKind {
/// Represents `#[rustc_const_stable_indirect]`.
ConstStabilityIndirect,

/// Represents `#[const_trait]`.
ConstTrait(Span),

/// Represents `#[coroutine]`.
Coroutine(Span),

Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_hir/src/attrs/encode_cross_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ impl AttributeKind {
ConstContinue(..) => No,
ConstStability { .. } => Yes,
ConstStabilityIndirect => No,
ConstTrait(..) => No,
Coroutine(..) => No,
Coverage(..) => No,
CrateName { .. } => No,
Expand Down
30 changes: 13 additions & 17 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -890,15 +890,6 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
};

let attrs = tcx.get_all_attrs(def_id);
// Only regular traits can be const.
// FIXME(const_trait_impl): remove this
let constness = if constness == hir::Constness::Const
|| !is_alias && find_attr!(attrs, AttributeKind::ConstTrait(_))
{
hir::Constness::Const
} else {
hir::Constness::NotConst
};

let paren_sugar = find_attr!(attrs, AttributeKind::ParenSugar(_));
if paren_sugar && !tcx.features().unboxed_closures() {
Expand Down Expand Up @@ -1366,22 +1357,27 @@ fn check_impl_constness(
}

let trait_name = tcx.item_name(trait_def_id).to_string();
let (local_trait_span, suggestion_pre) =
match (trait_def_id.is_local(), tcx.sess.is_nightly_build()) {
(true, true) => (
Some(tcx.def_span(trait_def_id).shrink_to_lo()),
let (suggestion, suggestion_pre) = match (trait_def_id.as_local(), tcx.sess.is_nightly_build())
{
(Some(trait_def_id), true) => {
let span = tcx.hir_expect_item(trait_def_id).vis_span;
let span = tcx.sess.source_map().span_extend_while_whitespace(span);

(
Some(span.shrink_to_hi()),
if tcx.features().const_trait_impl() {
""
} else {
"enable `#![feature(const_trait_impl)]` in your crate and "
},
),
(false, _) | (_, false) => (None, ""),
};
)
}
(None, _) | (_, false) => (None, ""),
};
tcx.dcx().emit_err(errors::ConstImplForNonConstTrait {
trait_ref_span: hir_trait_ref.path.span,
trait_name,
local_trait_span,
suggestion,
suggestion_pre,
marking: (),
adding: (),
Expand Down
18 changes: 4 additions & 14 deletions compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,13 +500,8 @@ pub(crate) struct ConstImplForNonConstTrait {
#[label]
pub trait_ref_span: Span,
pub trait_name: String,
#[suggestion(
applicability = "machine-applicable",
// FIXME(const_trait_impl) fix this suggestion
code = "#[const_trait] ",
style = "verbose"
)]
pub local_trait_span: Option<Span>,
#[suggestion(applicability = "machine-applicable", code = "const ", style = "verbose")]
pub suggestion: Option<Span>,
pub suggestion_pre: &'static str,
#[note]
pub marking: (),
Expand All @@ -523,14 +518,9 @@ pub(crate) struct ConstBoundForNonConstTrait {
pub modifier: &'static str,
#[note]
pub def_span: Option<Span>,
pub suggestion_pre: &'static str,
#[suggestion(
applicability = "machine-applicable",
// FIXME(const_trait_impl) fix this suggestion
code = "#[const_trait] ",
style = "verbose"
)]
#[suggestion(applicability = "machine-applicable", code = "const ", style = "verbose")]
pub suggestion: Option<Span>,
pub suggestion_pre: &'static str,
pub trait_name: String,
}

Expand Down
33 changes: 19 additions & 14 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -880,28 +880,33 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}

if let hir::BoundConstness::Always(span) | hir::BoundConstness::Maybe(span) = constness
&& !self.tcx().is_const_trait(trait_def_id)
&& !tcx.is_const_trait(trait_def_id)
{
let (def_span, suggestion, suggestion_pre) =
match (trait_def_id.is_local(), self.tcx().sess.is_nightly_build()) {
(true, true) => (
None,
Some(tcx.def_span(trait_def_id).shrink_to_lo()),
if self.tcx().features().const_trait_impl() {
""
} else {
"enable `#![feature(const_trait_impl)]` in your crate and "
},
),
(false, _) | (_, false) => (Some(tcx.def_span(trait_def_id)), None, ""),
match (trait_def_id.as_local(), tcx.sess.is_nightly_build()) {
(Some(trait_def_id), true) => {
let span = tcx.hir_expect_item(trait_def_id).vis_span;
let span = tcx.sess.source_map().span_extend_while_whitespace(span);

(
None,
Some(span.shrink_to_hi()),
if self.tcx().features().const_trait_impl() {
""
} else {
"enable `#![feature(const_trait_impl)]` in your crate and "
},
)
}
(None, _) | (_, false) => (Some(tcx.def_span(trait_def_id)), None, ""),
};
self.dcx().emit_err(crate::errors::ConstBoundForNonConstTrait {
span,
modifier: constness.as_str(),
def_span,
trait_name: self.tcx().def_path_str(trait_def_id),
suggestion_pre,
trait_name: tcx.def_path_str(trait_def_id),
suggestion,
suggestion_pre,
});
} else {
match predicate_filter {
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| AttributeKind::Marker(..)
| AttributeKind::SkipDuringMethodDispatch { .. }
| AttributeKind::Coinductive(..)
| AttributeKind::ConstTrait(..)
| AttributeKind::DenyExplicitImpl(..)
| AttributeKind::DoNotImplementViaObject(..)
| AttributeKind::SpecializationTrait(..)
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,6 @@ symbols! {
const_raw_ptr_to_usize_cast,
const_refs_to_cell,
const_refs_to_static,
const_trait,
const_trait_bound_opt_out,
const_trait_impl,
const_try,
Expand Down
3 changes: 1 addition & 2 deletions library/core/src/array/equality.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,8 @@ where
#[rustc_const_unstable(feature = "const_cmp", issue = "143800")]
impl<T: [const] Eq, const N: usize> const Eq for [T; N] {}

#[const_trait]
#[rustc_const_unstable(feature = "const_cmp", issue = "143800")]
trait SpecArrayEq<Other, const N: usize>: Sized {
const trait SpecArrayEq<Other, const N: usize>: Sized {
fn spec_eq(a: &[Self; N], b: &[Other; N]) -> bool;
fn spec_ne(a: &[Self; N], b: &[Other; N]) -> bool;
}
Expand Down
3 changes: 1 addition & 2 deletions library/core/src/cmp/bytewise.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ use crate::num::NonZero;
/// - Neither `Self` nor `Rhs` have provenance, so integer comparisons are correct.
/// - `<Self as PartialEq<Rhs>>::{eq,ne}` are equivalent to comparing the bytes.
#[rustc_specialization_trait]
#[const_trait] // FIXME(const_trait_impl): Migrate to `const unsafe trait` once #146122 is fixed.
pub(crate) unsafe trait BytewiseEq<Rhs = Self>:
pub(crate) const unsafe trait BytewiseEq<Rhs = Self>:
[const] PartialEq<Rhs> + Sized
{
}
Expand Down
9 changes: 3 additions & 6 deletions library/core/src/ops/range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -816,9 +816,8 @@ impl<T: Clone> Bound<&T> {
/// by range syntax like `..`, `a..`, `..b`, `..=c`, `d..e`, or `f..=g`.
#[stable(feature = "collections_range", since = "1.28.0")]
#[rustc_diagnostic_item = "RangeBounds"]
#[const_trait]
#[rustc_const_unstable(feature = "const_range", issue = "none")]
pub trait RangeBounds<T: ?Sized> {
pub const trait RangeBounds<T: ?Sized> {
/// Start index bound.
///
/// Returns the start value as a `Bound`.
Expand Down Expand Up @@ -954,9 +953,8 @@ pub trait RangeBounds<T: ?Sized> {
/// `IntoBounds` is implemented by Rust’s built-in range types, produced
/// by range syntax like `..`, `a..`, `..b`, `..=c`, `d..e`, or `f..=g`.
#[unstable(feature = "range_into_bounds", issue = "136903")]
#[const_trait]
#[rustc_const_unstable(feature = "const_range", issue = "none")]
pub trait IntoBounds<T>: [const] RangeBounds<T> {
pub const trait IntoBounds<T>: [const] RangeBounds<T> {
/// Convert this range into the start and end bounds.
/// Returns `(start_bound, end_bound)`.
///
Expand Down Expand Up @@ -1319,9 +1317,8 @@ pub enum OneSidedRangeBound {
/// Types that implement `OneSidedRange<T>` must return `Bound::Unbounded`
/// from one of `RangeBounds::start_bound` or `RangeBounds::end_bound`.
#[unstable(feature = "one_sided_range", issue = "69780")]
#[const_trait]
#[rustc_const_unstable(feature = "const_range", issue = "none")]
pub trait OneSidedRange<T>: RangeBounds<T> {
pub const trait OneSidedRange<T>: RangeBounds<T> {
/// An internal-only helper function for `split_off` and
/// `split_off_mut` that returns the bound of the one-sided range.
fn bound(self) -> (OneSidedRangeBound, T);
Expand Down
Loading
Loading