From bde43795608305abfd50e467b184eec2e3f6454f Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 22 Jul 2025 14:40:01 +0000 Subject: [PATCH 1/7] Add field to distinguish const fns from always-const fns --- compiler/rustc_ast_lowering/src/item.rs | 2 +- .../src/check_consts/check.rs | 3 ++- .../rustc_const_eval/src/check_consts/ops.rs | 3 ++- .../src/const_eval/fn_queries.rs | 6 ++--- compiler/rustc_hir/src/hir.rs | 17 ++++++++---- compiler/rustc_hir_pretty/src/lib.rs | 7 ++--- compiler/rustc_metadata/src/rmeta/table.rs | 3 ++- compiler/rustc_middle/src/ty/adt.rs | 3 ++- compiler/rustc_middle/src/ty/context.rs | 5 +++- .../src/ty/context/impl_interner.rs | 2 +- compiler/rustc_middle/src/ty/mod.rs | 26 ++++++++++++------- compiler/rustc_mir_transform/src/lib.rs | 20 +++++++++----- compiler/rustc_passes/src/check_attr.rs | 2 +- compiler/rustc_passes/src/stability.rs | 4 +-- compiler/rustc_public/src/ty.rs | 4 +-- .../src/unstable/convert/internal.rs | 4 +-- .../src/unstable/convert/stable/mod.rs | 4 +-- .../src/traits/effects.rs | 18 ++++++++++--- src/librustdoc/clean/types.rs | 9 +++---- src/librustdoc/html/format.rs | 12 ++++++--- .../rustc_public/check_fn_attrs.rs | 14 +++++++--- 21 files changed, 107 insertions(+), 61 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 8ae2f3bda9f92..e5256a26ef228 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1840,7 +1840,7 @@ impl<'hir> LoweringContext<'_, 'hir> { pub(super) fn lower_constness(&mut self, c: Const) -> hir::Constness { match c { - Const::Yes(_) => hir::Constness::Const, + Const::Yes(_) => hir::Constness::Const { always: false }, Const::No => hir::Constness::NotConst, } } diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 283c9aff0394d..daf51dd967203 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -779,7 +779,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // to do different checks than usual. trace!("attempting to call a trait method"); - let is_const = tcx.constness(callee) == hir::Constness::Const; + let is_const = + matches!(tcx.constness(callee), hir::Constness::Const { always: false }); // Only consider a trait to be const if the const conditions hold. // Otherwise, it's really misleading to call something "conditionally" diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index c776fa69ba5c1..78b23627f6b5e 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -423,7 +423,8 @@ fn build_error_for_const_call<'tcx>( err.help("const traits are not yet supported on stable Rust"); } } - } else if ccx.tcx.constness(callee) != hir::Constness::Const { + } else if !matches!(ccx.tcx.constness(callee), hir::Constness::Const { always: false }) + { let name = ccx.tcx.item_name(callee); err.span_note( ccx.tcx.def_span(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 57fd230ec4685..8267cb66e9e15 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -11,13 +11,13 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Constness { let node = tcx.hir_node_by_def_id(def_id); match node { - Node::Ctor(VariantData::Tuple(..)) => Constness::Const, + Node::Ctor(VariantData::Tuple(..)) => Constness::Const { always: false }, Node::ForeignItem(item) if let ForeignItemKind::Fn(..) = item.kind => { // Foreign functions cannot be evaluated at compile-time. Constness::NotConst } Node::Expr(e) if let ExprKind::Closure(c) = e.kind => { - if let Constness::Const = c.constness && tcx.hir_body_const_context(tcx.local_parent(def_id)).is_none() { + if let Constness::Const { .. } = c.constness && tcx.hir_body_const_context(tcx.local_parent(def_id)).is_none() { tcx.dcx().span_err(tcx.def_span(def_id), "cannot use `const` closures outside of const contexts"); return Constness::NotConst; } @@ -37,7 +37,7 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Constness { .. }) => { match sig.header.constness { - Constness::Const => Constness::Const, + Constness::Const { always } => Constness::Const { always }, // inherent impl could be const Constness::NotConst => tcx.constness(tcx.local_parent(def_id)), } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 53141dcc5042f..c4155cd82631d 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -4632,17 +4632,24 @@ impl fmt::Display for Safety { } #[derive(Copy, Clone, PartialEq, Eq, Debug, Encodable, Decodable, StableHash)] -#[derive(Default)] pub enum Constness { - #[default] - Const, + Const { always: bool }, NotConst, } +/// This impl exists as an optimization so that metadata deserialization can +/// store the value directly and not have to encode it wrapped in another `Option`. +impl Default for Constness { + fn default() -> Self { + Self::Const { always: false } + } +} + impl fmt::Display for Constness { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(match *self { - Self::Const => "const", + Self::Const { always: true } => "comptime", + Self::Const { always: false } => "const", Self::NotConst => "non-const", }) } @@ -4697,7 +4704,7 @@ impl FnHeader { } pub fn is_const(&self) -> bool { - matches!(self.constness, Constness::Const) + matches!(self.constness, Constness::Const { .. }) } pub fn is_unsafe(&self) -> bool { diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 0204c4c9ab5fa..b01821ab76cbf 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -716,7 +716,7 @@ impl<'a> State<'a> { match of_trait { None => { - if let hir::Constness::Const = constness { + if let hir::Constness::Const { always: false } = constness { self.word_nbsp("const"); } impl_generics(self) @@ -733,7 +733,7 @@ impl<'a> State<'a> { impl_generics(self); - if let hir::Constness::Const = constness { + if let hir::Constness::Const { always: false } = constness { self.word_nbsp("const"); } @@ -2623,7 +2623,8 @@ impl<'a> State<'a> { fn print_constness(&mut self, s: hir::Constness) { match s { hir::Constness::NotConst => {} - hir::Constness::Const => self.word_nbsp("const"), + hir::Constness::Const { always: false } => self.word_nbsp("const"), + hir::Constness::Const { always: true } => { /* printed as an attribute */ } } } diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 26c5908563777..e3f40e5b17323 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -225,8 +225,9 @@ defaulted_enum! { defaulted_enum! { hir::Constness { - ( Const ) + ( Const { always: false } ) ( NotConst ) + ( Const { always: true } ) } } diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index f890c7aadbba5..e26dac94edd16 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -311,7 +311,8 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef> for AdtDef<'tcx> { fn destructor(self, tcx: TyCtxt<'tcx>) -> Option { Some(match tcx.constness(self.destructor(tcx)?.did) { - hir::Constness::Const => AdtDestructorKind::Const, + hir::Constness::Const { always: true } => todo!("FIXME(comptime)"), + hir::Constness::Const { always: false } => AdtDestructorKind::Const, hir::Constness::NotConst => AdtDestructorKind::NotConst, }) } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 9dcf76ead4b75..b708b669337b7 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2666,7 +2666,10 @@ impl<'tcx> TyCtxt<'tcx> { /// Whether the trait impl is marked const. This does not consider stability or feature gates. pub fn is_const_trait_impl(self, def_id: DefId) -> bool { self.def_kind(def_id) == DefKind::Impl { of_trait: true } - && self.impl_trait_header(def_id).constness == hir::Constness::Const + && matches!( + self.impl_trait_header(def_id).constness, + hir::Constness::Const { always: false } + ) } pub fn is_sdylib_interface_build(self) -> bool { diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index bc8b83e460080..f4455e6ca9a51 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -455,7 +455,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { fn closure_is_const(self, def_id: DefId) -> bool { debug_assert_matches!(self.def_kind(def_id), DefKind::Closure); - self.constness(def_id) == hir::Constness::Const + matches!(self.constness(def_id), hir::Constness::Const { always: false }) } fn alias_has_const_conditions(self, def_id: DefId) -> bool { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 8ea14b0c5a28b..ac82cf1981032 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2141,7 +2141,7 @@ impl<'tcx> TyCtxt<'tcx> { matches!( self.def_kind(def_id), DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Closure - ) && self.constness(def_id) == hir::Constness::Const + ) && matches!(self.constness(def_id), hir::Constness::Const { .. }) } /// Whether this item is conditionally constant for the purposes of the @@ -2155,12 +2155,14 @@ impl<'tcx> TyCtxt<'tcx> { match self.def_kind(def_id) { DefKind::Impl { of_trait: true } => { let header = self.impl_trait_header(def_id); - header.constness == hir::Constness::Const + matches!(header.constness, hir::Constness::Const { always: false }) && self.is_const_trait(header.trait_ref.skip_binder().def_id) } - DefKind::Impl { of_trait: false } => self.constness(def_id) == hir::Constness::Const, + DefKind::Impl { of_trait: false } => { + matches!(self.constness(def_id), hir::Constness::Const { always: false }) + } DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) => { - self.constness(def_id) == hir::Constness::Const + matches!(self.constness(def_id), hir::Constness::Const { always: false }) } DefKind::TraitAlias | DefKind::Trait => self.is_const_trait(def_id), DefKind::AssocTy => { @@ -2177,17 +2179,19 @@ impl<'tcx> TyCtxt<'tcx> { let parent_def_id = self.parent(def_id); match self.def_kind(parent_def_id) { DefKind::Impl { of_trait: false } => { - self.constness(def_id) == hir::Constness::Const + matches!(self.constness(def_id), hir::Constness::Const { always: false }) } DefKind::Impl { of_trait: true } => { let Some(trait_method_did) = self.trait_item_of(def_id) else { return false; }; - self.constness(trait_method_did) == hir::Constness::Const - && self.is_conditionally_const(parent_def_id) + matches!( + self.constness(trait_method_did), + hir::Constness::Const { always: false } + ) && self.is_conditionally_const(parent_def_id) } DefKind::Trait => { - self.constness(def_id) == hir::Constness::Const + matches!(self.constness(def_id), hir::Constness::Const { always: false }) && self.is_conditionally_const(parent_def_id) } _ => bug!("unexpected parent item of associated fn: {parent_def_id:?}"), @@ -2199,7 +2203,9 @@ impl<'tcx> TyCtxt<'tcx> { // FIXME(const_trait_impl): ATPITs could be conditionally const? hir::OpaqueTyOrigin::TyAlias { .. } => false, }, - DefKind::Closure => self.constness(def_id) == hir::Constness::Const, + DefKind::Closure => { + matches!(self.constness(def_id), hir::Constness::Const { always: false }) + } DefKind::Ctor(_, CtorKind::Const) | DefKind::Mod | DefKind::Struct @@ -2228,7 +2234,7 @@ impl<'tcx> TyCtxt<'tcx> { #[inline] pub fn is_const_trait(self, def_id: DefId) -> bool { - self.trait_def(def_id).constness == hir::Constness::Const + matches!(self.trait_def(def_id).constness, hir::Constness::Const { .. }) } pub fn impl_method_has_trait_impl_trait_tys(self, def_id: DefId) -> bool { diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 063f0f39ee06e..bbf190ce1a3e0 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -259,8 +259,12 @@ fn remap_mir_for_const_eval_select<'tcx>( let ty = tupled_args.node.ty(&body.local_decls, tcx); let fields = ty.tuple_fields(); let num_args = fields.len(); - let func = - if context == hir::Constness::Const { called_in_const } else { called_at_rt }; + let func = match context { + // Using `const_eval_select` in always-const code is useful when used in macros + // that you don't know whether they are going to be used in `const fn` or in `const` items. + hir::Constness::Const { .. } => called_in_const, + hir::Constness::NotConst => called_at_rt, + }; let (method, place): (fn(Place<'tcx>) -> Operand<'tcx>, Place<'tcx>) = match tupled_args.node { Operand::Constant(_) | Operand::RuntimeChecks(_) => { @@ -432,7 +436,7 @@ fn mir_promoted( let const_qualifs = match tcx.def_kind(def) { DefKind::Fn | DefKind::AssocFn | DefKind::Closure - if tcx.constness(def) == hir::Constness::Const => + if matches!(tcx.constness(def), hir::Constness::Const { .. }) => { tcx.mir_const_qualif(def) } @@ -496,15 +500,17 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: LocalDefId) -> Body<'_> { } let body = tcx.mir_drops_elaborated_and_const_checked(def); - let body = match tcx.hir_body_const_context(def) { + let (body, always) = match tcx.hir_body_const_context(def) { // consts and statics do not have `optimized_mir`, so we can steal the body instead of // cloning it. - Some(hir::ConstContext::Const { .. } | hir::ConstContext::Static(_)) => body.steal(), - Some(hir::ConstContext::ConstFn) => body.borrow().clone(), + Some(hir::ConstContext::Const { .. } | hir::ConstContext::Static(_)) => { + (body.steal(), true) + } + Some(hir::ConstContext::ConstFn) => (body.borrow().clone(), false), None => bug!("`mir_for_ctfe` called on non-const {def:?}"), }; - let mut body = remap_mir_for_const_eval_select(tcx, body, hir::Constness::Const); + let mut body = remap_mir_for_const_eval_select(tcx, body, hir::Constness::Const { always }); pm::run_passes(tcx, &mut body, &[&ctfe_limit::CtfeLimit], None, pm::Optimizations::Allowed); body diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 9762ac1d92b9d..1eff8b2d56bee 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -544,7 +544,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { if target == (Target::Impl { of_trait: true }) { match item.unwrap() { ItemLike::Item(it) => match it.expect_impl().constness { - Constness::Const => { + Constness::Const { .. } => { let item_span = self.tcx.hir_span(hir_id); self.tcx.emit_node_span_lint( MISPLACED_DIAGNOSTIC_ATTRIBUTES, diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 84eba2ace19ae..9b21784ee6235 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -652,7 +652,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { } if features.const_trait_impl() - && let hir::Constness::Const = constness + && let hir::Constness::Const { .. } = constness { let stable_or_implied_stable = match const_stab { None => true, @@ -696,7 +696,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { } } - if let hir::Constness::Const = constness + if let hir::Constness::Const { .. } = constness && let Some(def_id) = of_trait.trait_ref.trait_def_id() { // FIXME(const_trait_impl): Improve the span here. diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index 9b9a480ab5f82..642e616dc1adb 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -1117,13 +1117,13 @@ impl FnSig { #[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] pub enum Constness { - Const, + Const { always: bool }, NotConst, } impl Constness { pub fn is_const(self) -> bool { - matches!(self, Constness::Const) + matches!(self, Constness::Const { always: false }) } } diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index f18fe84053b32..81b32f4da10f6 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -648,8 +648,8 @@ impl RustcInternal for Constness { _tables: &mut Tables<'_, BridgeTys>, _tcx: impl InternalCx<'tcx>, ) -> Self::T<'tcx> { - match self { - Constness::Const => rustc_hir::Constness::Const, + match *self { + Constness::Const { always } => rustc_hir::Constness::Const { always }, Constness::NotConst => rustc_hir::Constness::NotConst, } } diff --git a/compiler/rustc_public/src/unstable/convert/stable/mod.rs b/compiler/rustc_public/src/unstable/convert/stable/mod.rs index ce55e898b2acb..083a30adab13f 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mod.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mod.rs @@ -24,8 +24,8 @@ impl<'tcx> Stable<'tcx> for rustc_hir::Safety { impl<'tcx> Stable<'tcx> for rustc_hir::Constness { type T = crate::ty::Constness; fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { - match self { - rustc_hir::Constness::Const => crate::ty::Constness::Const, + match *self { + rustc_hir::Constness::Const { always } => crate::ty::Constness::Const { always }, rustc_hir::Constness::NotConst => crate::ty::Constness::NotConst, } } diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index 8681a92f57ecb..b657992e2c7a0 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -463,10 +463,11 @@ fn evaluate_host_effect_for_destruct_goal<'tcx>( }) .collect(); match adt_def.destructor(tcx).map(|dtor| tcx.constness(dtor.did)) { + Some(hir::Constness::Const { always: true }) => todo!("FIXME(comptime)"), // `Drop` impl exists, but it's not const. Type cannot be `[const] Destruct`. Some(hir::Constness::NotConst) => return Err(EvaluationFailure::NoSolution), // `Drop` impl exists, and it's const. Require `Ty: [const] Drop` to hold. - Some(hir::Constness::Const) => { + Some(hir::Constness::Const { always: false }) => { let drop_def_id = tcx.require_lang_item(LangItem::Drop, obligation.cause.span); let drop_trait_ref = ty::TraitRef::new(tcx, drop_def_id, [self_ty]); const_conditions.push(drop_trait_ref); @@ -563,7 +564,9 @@ fn evaluate_host_effect_for_fn_goal<'tcx>( }; match tcx.constness(def) { - hir::Constness::Const => Ok(tcx + // FIXME(comptime) + hir::Constness::Const { always: true } => Err(EvaluationFailure::NoSolution), + hir::Constness::Const { always: false } => Ok(tcx .const_conditions(def) .instantiate(tcx, args) .into_iter() @@ -594,8 +597,15 @@ fn evaluate_host_effect_from_selection_candidate<'tcx>( Err(_) => Err(EvaluationFailure::NoSolution), Ok(Some(source)) => match source { ImplSource::UserDefined(impl_) => { - if tcx.impl_trait_header(impl_.impl_def_id).constness != hir::Constness::Const { - return Err(EvaluationFailure::NoSolution); + match tcx.impl_trait_header(impl_.impl_def_id).constness { + rustc_hir::Constness::Const { always } => { + if always { + todo!() + } + } + rustc_hir::Constness::NotConst => { + return Err(EvaluationFailure::NoSolution); + } } let mut nested = impl_.nested; diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 202727c8ef769..1d5142491a5ab 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -822,7 +822,7 @@ impl Item { { hir::Constness::NotConst } else { - hir::Constness::Const + hir::Constness::Const { always: false } } } else { hir::Constness::NotConst @@ -853,11 +853,8 @@ impl Item { safety.into() }, abi, - constness: if tcx.is_const_fn(def_id) { - hir::Constness::Const - } else { - hir::Constness::NotConst - }, + // Foreign functions can never be const or comptime + constness: hir::Constness::NotConst, asyncness: hir::IsAsync::NotAsync, } } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 8172ef1848e80..f44212f2db778 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1509,15 +1509,21 @@ pub(crate) fn print_constness_with_space( overall_stab: Option, const_stab: Option, ) -> &'static str { - match c { - hir::Constness::Const => match (overall_stab, const_stab) { + match *c { + hir::Constness::Const { always } => match (overall_stab, const_stab) { // const stable... (_, Some(ConstStability { level: StabilityLevel::Stable { .. }, .. })) // ...or when feature(staged_api) is not set... | (_, None) // ...or when const unstable, but overall unstable too | (None, Some(ConstStability { level: StabilityLevel::Unstable { .. }, .. })) => { - "const " + if always { + // FIXME(comptime) show something when stable, currently relying on the attribute + // being rendered as part of the regular attribute list. + "" + } else { + "const " + } } // const unstable (and overall stable) (Some(_), Some(ConstStability { level: StabilityLevel::Unstable { .. }, .. })) => "", diff --git a/tests/ui-fulldeps/rustc_public/check_fn_attrs.rs b/tests/ui-fulldeps/rustc_public/check_fn_attrs.rs index 552e8cbec376d..aaa3167277670 100644 --- a/tests/ui-fulldeps/rustc_public/check_fn_attrs.rs +++ b/tests/ui-fulldeps/rustc_public/check_fn_attrs.rs @@ -14,20 +14,26 @@ extern crate rustc_middle; #[macro_use] extern crate rustc_public; -use rustc_public::crate_def::CrateDef; -use rustc_public::ty::{Asyncness, Constness, FnDef}; use std::io::Write; use std::ops::ControlFlow; +use rustc_public::crate_def::CrateDef; +use rustc_public::ty::{Asyncness, Constness, FnDef}; + const CRATE_NAME: &str = "input"; fn test_stable_mir() -> ControlFlow<()> { let fns = rustc_public::local_crate().fn_defs(); - check_fn(&fns, "input::const_sync", Constness::Const, Asyncness::NotAsync); + check_fn(&fns, "input::const_sync", Constness::Const { always: false }, Asyncness::NotAsync); check_fn(&fns, "input::async_fn", Constness::NotConst, Asyncness::Async); check_fn(&fns, "input::plain", Constness::NotConst, Asyncness::NotAsync); - check_fn(&fns, "input::Widget::assoc_const", Constness::Const, Asyncness::NotAsync); + check_fn( + &fns, + "input::Widget::assoc_const", + Constness::Const { always: false }, + Asyncness::NotAsync, + ); check_fn(&fns, "input::Widget::assoc_async", Constness::NotConst, Asyncness::Async); check_fn(&fns, "input::Widget::assoc_plain", Constness::NotConst, Asyncness::NotAsync); From 322a5402f8241ff02ddfddd096aae35b66125eca Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 23 Jul 2025 10:33:12 +0000 Subject: [PATCH 2/7] Check comptime fns --- .../rustc_ast_lowering/src/diagnostics.rs | 11 ++++++++ compiler/rustc_ast_lowering/src/item.rs | 22 ++++++++++++---- .../src/attributes/semantics.rs | 12 +++++++++ compiler/rustc_attr_parsing/src/context.rs | 3 ++- compiler/rustc_feature/src/builtin_attrs.rs | 1 + .../rustc_hir/src/attrs/data_structures.rs | 3 +++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_passes/src/check_attr.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + tests/ui/README.md | 4 +++ ...-highlight-span-extra-arguments-147070.svg | 2 +- ...-test-missing-annotations-detection.stderr | 18 +++++++++++++ tests/ui/comptime/const_comptime.rs | 7 +++++ tests/ui/comptime/const_comptime.stderr | 16 ++++++++++++ tests/ui/comptime/feature-gate-test.rs | 5 ++++ tests/ui/comptime/feature-gate-test.stderr | 12 +++++++++ tests/ui/comptime/not_callable.rs | 25 ++++++++++++++++++ tests/ui/comptime/trait_comptime.rs | 26 +++++++++++++++++++ tests/ui/comptime/trait_comptime.stderr | 26 +++++++++++++++++++ .../multiline-removal-suggestion.svg | 2 +- ...between-expected-trait-and-found-trait.svg | 2 +- ...recursive-trait-fn-sig-issue-142064.stderr | 14 +++++----- .../ty-variance-issue-124423.stderr | 8 +++--- 23 files changed, 202 insertions(+), 20 deletions(-) create mode 100644 tests/ui/compiletest-self-test/ui-test-missing-annotations-detection.stderr create mode 100644 tests/ui/comptime/const_comptime.rs create mode 100644 tests/ui/comptime/const_comptime.stderr create mode 100644 tests/ui/comptime/feature-gate-test.rs create mode 100644 tests/ui/comptime/feature-gate-test.stderr create mode 100644 tests/ui/comptime/not_callable.rs create mode 100644 tests/ui/comptime/trait_comptime.rs create mode 100644 tests/ui/comptime/trait_comptime.stderr diff --git a/compiler/rustc_ast_lowering/src/diagnostics.rs b/compiler/rustc_ast_lowering/src/diagnostics.rs index 2efb91a4e355b..31f094209a946 100644 --- a/compiler/rustc_ast_lowering/src/diagnostics.rs +++ b/compiler/rustc_ast_lowering/src/diagnostics.rs @@ -122,6 +122,17 @@ pub(crate) struct AwaitOnlyInAsyncFnAndBlocks { pub item_span: Option, } +#[derive(Diagnostic)] +#[diag("a function cannot be both `comptime` and `const`")] +pub(crate) struct ConstComptimeFn { + #[primary_span] + #[suggestion("remove the `const`", applicability = "machine-applicable", code = "")] + #[note("`const` implies the function can be called at runtime, too")] + pub span: Span, + #[label("`comptime` because of this")] + pub attr_span: Span, +} + #[derive(Diagnostic)] #[diag("too many parameters for a coroutine (expected 0 or 1 parameters)", code = E0628)] pub(crate) struct CoroutineTooManyParameters { diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index e5256a26ef228..336c644961ab6 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -27,6 +27,7 @@ use super::{ AstOwner, FnDeclKind, GenericArgsMode, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode, RelaxedBoundForbiddenReason, RelaxedBoundPolicy, ResolverAstLoweringExt, }; +use crate::diagnostics::ConstComptimeFn; /// Wraps either IndexVec (during `hir_crate`), which acts like a primary /// storage for most of the MaybeOwners, or FxIndexMap during delayed AST -> HIR @@ -1773,12 +1774,23 @@ impl<'hir> LoweringContext<'_, 'hir> { safety.into() }; - hir::FnHeader { - safety, - asyncness, - constness: self.lower_constness(h.constness), - abi: self.lower_extern(h.ext), + let mut constness = self.lower_constness(h.constness); + if let Some(&attr_span) = find_attr!(attrs, RustcComptime(span) => span) { + match std::mem::replace(&mut constness, rustc_hir::Constness::Const { always: true }) { + rustc_hir::Constness::Const { always: true } => { + unreachable!("lower_constness cannot produce comptime") + } + // A function can't be `const` and `comptime` at the same time + rustc_hir::Constness::Const { always: false } => { + let Const::Yes(span) = h.constness else { unreachable!() }; + self.dcx().emit_err(ConstComptimeFn { span, attr_span }); + } + // Good + rustc_hir::Constness::NotConst => {} + } } + + hir::FnHeader { safety, asyncness, constness, abi: self.lower_extern(h.ext) } } pub(super) fn lower_abi(&mut self, abi_str: StrLit) -> ExternAbi { diff --git a/compiler/rustc_attr_parsing/src/attributes/semantics.rs b/compiler/rustc_attr_parsing/src/attributes/semantics.rs index 3e341aa034c18..07c07eb6681df 100644 --- a/compiler/rustc_attr_parsing/src/attributes/semantics.rs +++ b/compiler/rustc_attr_parsing/src/attributes/semantics.rs @@ -9,3 +9,15 @@ impl NoArgsAttributeParser for MayDangleParser { const STABILITY: AttributeStability = unstable!(dropck_eyepatch); const CREATE: fn(span: Span) -> AttributeKind = AttributeKind::MayDangle; } + +pub(crate) struct ComptimeParser; +impl NoArgsAttributeParser for ComptimeParser { + const PATH: &[Symbol] = &[sym::rustc_comptime]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Fn), + ]); + const STABILITY: AttributeStability = unstable!(rustc_attrs); + const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcComptime; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 3ef8c6665e950..50d1fefadc761 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -56,7 +56,7 @@ use crate::attributes::repr::*; use crate::attributes::rustc_allocator::*; use crate::attributes::rustc_dump::*; use crate::attributes::rustc_internal::*; -use crate::attributes::semantics::*; +use crate::attributes::semantics::{ComptimeParser, *}; use crate::attributes::stability::*; use crate::attributes::test_attrs::*; use crate::attributes::traits::*; @@ -234,6 +234,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 72919028f1ce9..eab60460903aa 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -452,6 +452,7 @@ pub static BUILTIN_ATTRIBUTES: &[Symbol] = &[ sym::rustc_no_implicit_autorefs, sym::rustc_coherence_is_core, sym::rustc_coinductive, + sym::rustc_comptime, sym::rustc_allow_incoherent_impl, sym::rustc_preserve_ub_checks, sym::rustc_deny_explicit_impl, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 4ff56a640c19e..f2b4041498c02 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1311,6 +1311,9 @@ pub enum AttributeKind { /// Represents `#[rustc_coinductive]`. RustcCoinductive, + /// Represents `#[rustc_comptime]` + RustcComptime(Span), + /// Represents `#[rustc_confusables]`. RustcConfusables { confusables: ThinVec, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 7169fa433ffd8..80eeb3400ec88 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -112,6 +112,7 @@ impl AttributeKind { RustcClean { .. } => No, RustcCoherenceIsCore => No, RustcCoinductive => No, + RustcComptime(..) => No, // Encoded directly in signature RustcConfusables { .. } => Yes, RustcConstStability { .. } => Yes, RustcConstStableIndirect => No, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 1eff8b2d56bee..5d12e762fdc0a 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -326,6 +326,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::RustcClean(..) => (), AttributeKind::RustcCoherenceIsCore => (), AttributeKind::RustcCoinductive => (), + AttributeKind::RustcComptime(_) => (), AttributeKind::RustcConfusables { .. } => (), AttributeKind::RustcConstStability { .. } => (), AttributeKind::RustcConstStableIndirect => (), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 02fe3171cd9f4..561827cd44bf5 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1725,6 +1725,7 @@ symbols! { rustc_clean, rustc_coherence_is_core, rustc_coinductive, + rustc_comptime, rustc_confusables, rustc_const_stable, rustc_const_stable_indirect, diff --git a/tests/ui/README.md b/tests/ui/README.md index 00afc98a0b55a..a1ec43ea3ba4f 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -296,6 +296,10 @@ Tests for compile flags. Meta test suite of the test harness `compiletest` itself. +## `tests/ui/comptime`: compile-time only functions and intrinsics + +Test the `#[rustc_comptime]` attribute and intrinsics that inherently can only run at compile-time. + ## `tests/ui/conditional-compilation/`: Conditional Compilation Tests for `#[cfg]` attribute or `--cfg` flags, used to compile certain files or code blocks only if certain conditions are met (such as developing on a specific architecture). diff --git a/tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.svg b/tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.svg index 549acee7cee54..af41631479cf5 100644 --- a/tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.svg +++ b/tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.svg @@ -1,4 +1,4 @@ - +