From 38ee80d48efa8c1be83227be36676a9ccfba55ad Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sat, 17 Jan 2026 21:40:27 -0500 Subject: [PATCH] add `#[rustc_non_const_trait_method]` --- .../src/attributes/rustc_internal.rs | 12 ++++++++++ compiler/rustc_attr_parsing/src/context.rs | 7 +++--- .../src/check_consts/check.rs | 4 ++-- .../src/const_eval/fn_queries.rs | 11 ++++++++-- compiler/rustc_feature/src/builtin_attrs.rs | 6 +++++ .../rustc_hir/src/attrs/data_structures.rs | 3 +++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_middle/src/ty/mod.rs | 9 ++++++-- compiler/rustc_passes/src/check_attr.rs | 2 +- compiler/rustc_span/src/symbol.rs | 1 + .../traits/const-traits/partial/attr-gate.rs | 7 ++++++ .../const-traits/partial/attr-gate.stderr | 13 +++++++++++ .../const-traits/partial/no-const-callers.rs | 22 +++++++++++++++++++ .../partial/no-const-callers.stderr | 19 ++++++++++++++++ 14 files changed, 107 insertions(+), 10 deletions(-) create mode 100644 tests/ui/traits/const-traits/partial/attr-gate.rs create mode 100644 tests/ui/traits/const-traits/partial/attr-gate.stderr create mode 100644 tests/ui/traits/const-traits/partial/no-const-callers.rs create mode 100644 tests/ui/traits/const-traits/partial/no-const-callers.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 6375927b01de..38f728fa9f55 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -388,3 +388,15 @@ impl CombineAttributeParser for RustcLayoutParser { result } } + +pub(crate) struct RustcNonConstTraitMethodParser; + +impl NoArgsAttributeParser for RustcNonConstTraitMethodParser { + const PATH: &'static [Symbol] = &[sym::rustc_non_const_trait_method]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::Trait { body: false })), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNonConstTraitMethod; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index a4d08de515ad..0cabc0895053 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -79,9 +79,9 @@ use crate::attributes::rustc_internal::{ RustcLayoutScalarValidRangeStartParser, RustcLegacyConstGenericsParser, RustcLintOptDenyFieldAccessParser, RustcLintOptTyParser, RustcLintQueryInstabilityParser, RustcLintUntrackedQueryInformationParser, RustcMainParser, RustcMustImplementOneOfParser, - RustcNeverReturnsNullPointerParser, RustcNoImplicitAutorefsParser, RustcNounwindParser, - RustcObjectLifetimeDefaultParser, RustcOffloadKernelParser, RustcScalableVectorParser, - RustcSimdMonomorphizeLaneLimitParser, + RustcNeverReturnsNullPointerParser, RustcNoImplicitAutorefsParser, + RustcNonConstTraitMethodParser, RustcNounwindParser, RustcObjectLifetimeDefaultParser, + RustcOffloadKernelParser, RustcScalableVectorParser, RustcSimdMonomorphizeLaneLimitParser, }; use crate::attributes::semantics::MayDangleParser; use crate::attributes::stability::{ @@ -305,6 +305,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 95dbf42d4d44..2abbf75984a4 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -778,12 +778,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // than usual. trace!("attempting to call a trait method"); - let trait_is_const = tcx.is_const_trait(trait_did); + let is_const = tcx.constness(callee) == hir::Constness::Const; // Only consider a trait to be const if the const conditions hold. // Otherwise, it's really misleading to call something "conditionally" // const when it's very obviously not conditionally const. - if trait_is_const && has_const_conditions == Some(ConstConditionsHold::Yes) { + if is_const && has_const_conditions == Some(ConstConditionsHold::Yes) { // Trait calls are always conditionally-const. self.check_op(ops::ConditionallyConstCall { callee, diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index cdf0dcff381f..46cdca53ba8c 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -1,7 +1,8 @@ +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{ Constness, ExprKind, ForeignItemKind, ImplItem, ImplItemImplKind, ImplItemKind, Item, ItemKind, - Node, TraitItem, TraitItemKind, VariantData, + Node, TraitItem, TraitItemKind, VariantData, find_attr, }; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; @@ -36,7 +37,13 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Constness { Constness::NotConst => tcx.constness(tcx.local_parent(def_id)), } } - Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(..), .. }) => tcx.trait_def(tcx.local_parent(def_id)).constness, + Node::TraitItem(ti @ TraitItem { kind: TraitItemKind::Fn(..), .. }) => { + if find_attr!(tcx.hir_attrs(ti.hir_id()), AttributeKind::RustcNonConstTraitMethod) { + Constness::NotConst + } else { + tcx.trait_def(tcx.local_parent(def_id)).constness + } + } _ => { tcx.dcx().span_bug( tcx.def_span(def_id), diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index ded8a5a4ae51..8f447e1477b3 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1329,6 +1329,12 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ "`#[rustc_has_incoherent_inherent_impls]` allows the addition of incoherent inherent impls for \ the given type by annotating all impl items with `#[rustc_allow_incoherent_impl]`" ), + rustc_attr!( + rustc_non_const_trait_method, AttributeType::Normal, template!(Word), + ErrorFollowing, EncodeCrossCrate::No, + "`#[rustc_non_const_trait_method]` should only used by the standard library to mark trait methods \ + as non-const to allow large traits to easier transition to const" + ), BuiltinAttribute { name: sym::rustc_diagnostic_item, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index bc58f1d8faa5..92dda79b0920 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1096,6 +1096,9 @@ pub enum AttributeKind { /// Represents `#[rustc_no_implicit_autorefs]` RustcNoImplicitAutorefs, + /// Represents `#[rustc_non_const_trait_method]`. + RustcNonConstTraitMethod, + /// Represents `#[rustc_nounwind]` RustcNounwind, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 4fd0f5698a8b..816ed07c1dc4 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -124,6 +124,7 @@ impl AttributeKind { RustcMustImplementOneOf { .. } => No, RustcNeverReturnsNullPointer => Yes, RustcNoImplicitAutorefs => Yes, + RustcNonConstTraitMethod => No, // should be reported via other queries like `constness` RustcNounwind => No, RustcObjcClass { .. } => No, RustcObjcSelector { .. } => No, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 3e3c9e27186a..044c99ffeb21 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2086,8 +2086,13 @@ impl<'tcx> TyCtxt<'tcx> { DefKind::Impl { of_trait: false } => { self.constness(def_id) == hir::Constness::Const } - DefKind::Impl { of_trait: true } | DefKind::Trait => { - self.is_conditionally_const(parent_def_id) + DefKind::Impl { of_trait: true } => { + self.constness(self.trait_item_of(def_id).unwrap()) == hir::Constness::Const + && self.is_conditionally_const(parent_def_id) + } + DefKind::Trait => { + self.constness(def_id) == hir::Constness::Const + && self.is_conditionally_const(parent_def_id) } _ => bug!("unexpected parent item of associated fn: {parent_def_id:?}"), } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 800965f81d3d..8cf68b280850 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -310,6 +310,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcMain | AttributeKind::RustcNeverReturnsNullPointer | AttributeKind::RustcNoImplicitAutorefs + | AttributeKind::RustcNonConstTraitMethod | AttributeKind::RustcNounwind | AttributeKind::RustcObjcClass { .. } | AttributeKind::RustcObjcSelector { .. } @@ -335,7 +336,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::Used { .. } | AttributeKind::WindowsSubsystem(..) // tidy-alphabetical-end - ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b80d624bc496..b0ef95d10ffa 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1995,6 +1995,7 @@ symbols! { rustc_no_implicit_autorefs, rustc_no_implicit_bounds, rustc_no_mir_inline, + rustc_non_const_trait_method, rustc_nonnull_optimization_guaranteed, rustc_nounwind, rustc_objc_class, diff --git a/tests/ui/traits/const-traits/partial/attr-gate.rs b/tests/ui/traits/const-traits/partial/attr-gate.rs new file mode 100644 index 000000000000..c59a03de2153 --- /dev/null +++ b/tests/ui/traits/const-traits/partial/attr-gate.rs @@ -0,0 +1,7 @@ +trait A { + #[rustc_non_const_trait_method] + //~^ ERROR: use of an internal attribute + fn a(); +} + +fn main() {} diff --git a/tests/ui/traits/const-traits/partial/attr-gate.stderr b/tests/ui/traits/const-traits/partial/attr-gate.stderr new file mode 100644 index 000000000000..bc86cbe4edf7 --- /dev/null +++ b/tests/ui/traits/const-traits/partial/attr-gate.stderr @@ -0,0 +1,13 @@ +error[E0658]: use of an internal attribute + --> $DIR/attr-gate.rs:2:5 + | +LL | #[rustc_non_const_trait_method] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable + = note: the `#[rustc_non_const_trait_method]` attribute is an internal implementation detail that will never be stable + = note: `#[rustc_non_const_trait_method]` should only used by the standard library to mark trait methods as non-const to allow large traits to easier transition to const + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/traits/const-traits/partial/no-const-callers.rs b/tests/ui/traits/const-traits/partial/no-const-callers.rs new file mode 100644 index 000000000000..efcdea36f1ca --- /dev/null +++ b/tests/ui/traits/const-traits/partial/no-const-callers.rs @@ -0,0 +1,22 @@ +#![feature(const_trait_impl, rustc_attrs)] + +const trait A { + fn a(); + #[rustc_non_const_trait_method] + fn b() { println!("hi"); } +} + +impl const A for () { + fn a() {} +} + +const fn foo() { + T::a(); + T::b(); + //~^ ERROR: cannot call non-const associated function + <()>::a(); + <()>::b(); + //~^ ERROR: cannot call non-const associated function +} + +fn main() {} diff --git a/tests/ui/traits/const-traits/partial/no-const-callers.stderr b/tests/ui/traits/const-traits/partial/no-const-callers.stderr new file mode 100644 index 000000000000..31179161c5cd --- /dev/null +++ b/tests/ui/traits/const-traits/partial/no-const-callers.stderr @@ -0,0 +1,19 @@ +error[E0015]: cannot call non-const associated function `::b` in constant functions + --> $DIR/no-const-callers.rs:15:5 + | +LL | T::b(); + | ^^^^^^ + | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + +error[E0015]: cannot call non-const associated function `<() as A>::b` in constant functions + --> $DIR/no-const-callers.rs:18:5 + | +LL | <()>::b(); + | ^^^^^^^^^ + | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0015`.