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
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3097,6 +3097,7 @@ impl CoroutineKind {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable, Debug)]
#[derive(HashStable_Generic, Walkable)]
pub enum Const {
Comptime(Span),
Yes(Span),
No,
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1603,6 +1603,7 @@ impl<'hir> LoweringContext<'_, 'hir> {

pub(super) fn lower_constness(&mut self, c: Const) -> hir::Constness {
match c {
Const::Comptime(_) => hir::Constness::Comptime,
Const::Yes(_) => hir::Constness::Const,
Const::No => hir::Constness::NotConst,
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,7 @@ impl<'a> AstValidator<'a> {
None => (),
}
match constness {
Const::Comptime(span) => report_err(span, "comptime"),
Const::Yes(span) => report_err(span, "const"),
Const::No => (),
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2109,6 +2109,7 @@ impl<'a> State<'a> {
match s {
ast::Const::No => {}
ast::Const::Yes(_) => self.word_nbsp("const"),
ast::Const::Comptime(_) => self.word_nbsp("comptime"),
}
}

Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/semantics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,14 @@ impl<S: Stage> NoArgsAttributeParser<S> for MayDangleParser {
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); //FIXME Still checked fully in `check_attr.rs`
const CREATE: fn(span: Span) -> AttributeKind = AttributeKind::MayDangle;
}

pub(crate) struct ComptimeParser;
impl<S: Stage> NoArgsAttributeParser<S> for ComptimeParser {
const PATH: &[Symbol] = &[sym::rustc_comptime];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Method(MethodKind::Inherent)),
Allow(Target::Fn),
]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::Comptime;
}
3 changes: 2 additions & 1 deletion compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ use crate::attributes::rustc_internal::{
RustcLayoutScalarValidRangeEndParser, RustcLayoutScalarValidRangeStartParser, RustcMainParser,
RustcObjectLifetimeDefaultParser, RustcSimdMonomorphizeLaneLimitParser,
};
use crate::attributes::semantics::MayDangleParser;
use crate::attributes::semantics::{ComptimeParser, MayDangleParser};
use crate::attributes::stability::{
BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser,
};
Expand Down Expand Up @@ -216,6 +216,7 @@ attribute_parsers!(
Single<WithoutArgs<AutomaticallyDerivedParser>>,
Single<WithoutArgs<CoinductiveParser>>,
Single<WithoutArgs<ColdParser>>,
Single<WithoutArgs<ComptimeParser>>,
Single<WithoutArgs<ConstContinueParser>>,
Single<WithoutArgs<ConstStabilityIndirectParser>>,
Single<WithoutArgs<CoroutineParser>>,
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_const_eval/src/const_eval/fn_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness {
hir::Node::Expr(e) if let hir::ExprKind::Closure(c) = e.kind => c.constness,
_ => {
if let Some(fn_kind) = node.fn_kind() {
if fn_kind.constness() == hir::Constness::Const {
return hir::Constness::Const;
if fn_kind.constness() != hir::Constness::NotConst {
return fn_kind.constness();
}

// If the function itself is not annotated with `const`, it may still be a `const fn`
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1390,6 +1390,11 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
rustc_force_inline, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing, EncodeCrossCrate::Yes,
"`#[rustc_force_inline]` forces a free function to be inlined"
),
rustc_attr!(
rustc_comptime, Normal, template!(Word), ErrorFollowing,
EncodeCrossCrate::Yes,
Copy link
Member

Choose a reason for hiding this comment

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

It this necessary? The other queries would store this information

"the `#[rustc_comptime]` attribute is just used to avoid adding syntax for `comptime fn`"
),

// ==========================================================================
// Internal attributes, Testing:
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir/src/attrs/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,9 @@ pub enum AttributeKind {
/// Represents `#[cold]`.
Cold(Span),

/// Represents `#[rustc_comptime]`
Comptime(Span),

/// Represents `#[rustc_confusables]`.
Confusables {
symbols: ThinVec<Symbol>,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/attrs/encode_cross_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ impl AttributeKind {
BodyStability { .. } => No,
Coinductive(..) => No,
Cold(..) => No,
Comptime(..) => Yes,
Confusables { .. } => Yes,
ConstContinue(..) => No,
ConstStability { .. } => Yes,
Expand Down
12 changes: 8 additions & 4 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2264,10 +2264,12 @@ pub enum ConstContext {
/// - Array length expressions
/// - Enum discriminants
/// - Const generics
///
/// For the most part, other contexts are treated just like a regular `const`, so they are
/// lumped into the same category.
Const { inline: bool },
Const {
/// For backwards compatibility `const` items allow
/// calls to `const fn` to get promoted.
/// We forbid that in comptime fns and inline consts.
allow_const_fn_promotion: bool,
},
}

impl ConstContext {
Expand Down Expand Up @@ -4225,13 +4227,15 @@ impl fmt::Display for Safety {

#[derive(Copy, Clone, PartialEq, Eq, Debug, Encodable, Decodable, HashStable_Generic)]
pub enum Constness {
Copy link
Member

@fmease fmease Nov 11, 2025

Choose a reason for hiding this comment

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

Personally speaking I would rename the variants like so:

pub enum Constness {
    Always, // `#[rustc_comptime]`
    Maybe, // `const`
    Never,
}

to mirror BoundConstness which is Always (for const), Maybe (for [const]) and Never. This makes it crystal clear what they represent and leaves no room for ambiguity.

If it were up to me, I would even rename Constness to ItemConstness if traits will be allowed to be compiletime, too. Otherwise, I would split it into FnConstness and TraitConstness.

Comptime,
Const,
Comment on lines +4230 to 4231
Copy link
Member

Choose a reason for hiding this comment

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

Seems worth doc comments distinguishing these two.

NotConst,
}

impl fmt::Display for Constness {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match *self {
Self::Comptime => "comptime",
Self::Const => "const",
Self::NotConst => "non-const",
})
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_pretty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2567,6 +2567,7 @@ impl<'a> State<'a> {
match s {
hir::Constness::NotConst => {}
hir::Constness::Const => self.word_nbsp("const"),
hir::Constness::Comptime => self.word_nbsp("comptime"),
}
}

Expand Down
13 changes: 12 additions & 1 deletion compiler/rustc_hir_typeck/src/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -917,7 +917,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return;
}

let host = match self.tcx.hir_body_const_context(self.body_id) {
let const_context = self.tcx.hir_body_const_context(self.body_id);

if let hir::Constness::Comptime = self.tcx.constness(callee_did) {
match const_context {
Some(hir::ConstContext::Const { .. } | hir::ConstContext::Static(_)) => {}
Some(hir::ConstContext::ConstFn) | None => {
self.dcx().span_err(span, "comptime fns can only be called at compile time");
}
}
Comment on lines +922 to +928
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't enforce cross crate if I use a comptime function in fn if my crate doesn't enable const_trait_impl.

Would it make sense to also add logic to constck? (check_const.rs)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well I'm working on turning this check on unconditionally. I can just move the comptime check before the feature gate check

}

let host = match const_context {
Some(hir::ConstContext::Const { .. } | hir::ConstContext::Static(_)) => {
ty::BoundConstness::Const
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_metadata/src/rmeta/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ fixed_size_enum! {
hir::Constness {
( NotConst )
( Const )
( Comptime )
}
}

Expand Down
10 changes: 8 additions & 2 deletions compiler/rustc_middle/src/hir/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,12 +314,18 @@ impl<'tcx> TyCtxt<'tcx> {
pub fn hir_body_const_context(self, def_id: LocalDefId) -> Option<ConstContext> {
let def_id = def_id.into();
let ccx = match self.hir_body_owner_kind(def_id) {
BodyOwnerKind::Const { inline } => ConstContext::Const { inline },
BodyOwnerKind::Const { inline } => {
ConstContext::Const { allow_const_fn_promotion: !inline }
}
BodyOwnerKind::Static(mutability) => ConstContext::Static(mutability),

BodyOwnerKind::Fn if self.is_constructor(def_id) => return None,
BodyOwnerKind::Fn | BodyOwnerKind::Closure if self.is_const_fn(def_id) => {
ConstContext::ConstFn
if self.constness(def_id) == rustc_hir::Constness::Comptime {
ConstContext::Const { allow_const_fn_promotion: false }
} else {
ConstContext::ConstFn
}
}
BodyOwnerKind::Fn if self.is_const_default_method(def_id) => ConstContext::ConstFn,
BodyOwnerKind::Fn | BodyOwnerKind::Closure | BodyOwnerKind::GlobalAsm => return None,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/ty/adt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> {

fn destructor(self, tcx: TyCtxt<'tcx>) -> Option<AdtDestructorKind> {
Some(match tcx.constness(self.destructor(tcx)?.did) {
hir::Constness::Comptime => todo!("FIXME(comptime)"),
hir::Constness::Const => AdtDestructorKind::Const,
hir::Constness::NotConst => AdtDestructorKind::NotConst,
})
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2138,7 +2138,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
) && self.constness(def_id) != hir::Constness::NotConst
}

/// Whether this item is conditionally constant for the purposes of the
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_mir_transform/src/promote_consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,10 @@ impl<'tcx> Validator<'_, 'tcx> {
// backwards compatibility reason to allow more promotion inside of them.
let promote_all_fn = matches!(
self.const_kind,
Some(hir::ConstContext::Static(_) | hir::ConstContext::Const { inline: false })
Some(
hir::ConstContext::Static(_)
| hir::ConstContext::Const { allow_const_fn_promotion: false }
)
);
if !promote_all_fn {
return Err(Unpromotable);
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_parse/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ parse_compound_assignment_expression_in_let = can't reassign to an uninitialized
.suggestion = initialize the variable
.help = if you meant to overwrite, remove the `let` binding
parse_const_comptime_fn = a function cannot be both `comptime` and `const`
.suggestion = remove the `const`
.label = `comptime` because of this
.note = `const` implies the function can be called at runtime, too
parse_const_generic_without_braces = expressions must be enclosed in braces to be used as const generic arguments
.suggestion = enclose the `const` expression in braces
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1686,6 +1686,16 @@ pub(crate) struct UnexpectedIfWithIf(
pub Span,
);

#[derive(Diagnostic)]
#[diag(parse_const_comptime_fn)]
pub(crate) struct ConstComptimeFn {
#[primary_span]
#[suggestion(applicability = "machine-applicable", code = "")]
pub span: Span,
#[label]
pub attr_span: Span,
}

#[derive(Diagnostic)]
#[diag(parse_maybe_fn_typo_with_impl)]
pub(crate) struct FnTypoWithImpl {
Expand Down
17 changes: 15 additions & 2 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2449,7 +2449,20 @@ impl<'a> Parser<'a> {
case: Case,
) -> PResult<'a, (Ident, FnSig, Generics, Option<Box<FnContract>>, Option<Box<Block>>)> {
let fn_span = self.token.span;
let header = self.parse_fn_front_matter(vis, case, FrontMatterParsingMode::Function)?; // `const ... fn`
let mut header = self.parse_fn_front_matter(vis, case, FrontMatterParsingMode::Function)?; // `const ... fn`
if let Some(attr) = attrs.iter().find(|attr| attr.has_name(sym::rustc_comptime)) {
Copy link
Member

@fmease fmease Nov 11, 2025

Choose a reason for hiding this comment

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

This doesn't account for #[cfg_attr(…, rustc_comptime)]. I would just leave the parser and ast::Const alone and do the lowering to hir::Constness (which would still have Comptime) in AST lowering. This should simplify things a lot.

Even if you anticipate it being turned into a keyword / qualifier eventually, you can't assume that and it would be super easy anyway to update the parser & AST once it's truly necessary.

match std::mem::replace(&mut header.constness, Const::Comptime(attr.span)) {
Const::Comptime(_) => unreachable!("there is no syntax for comptime fn"),
// A function can't be `const` and `comptime` at the same time
Const::Yes(span) => {
return Err(self
.dcx()
.create_err(errors::ConstComptimeFn { span, attr_span: attr.span }));
}
// Good
Const::No => {}
}
}
let ident = self.parse_ident()?; // `foo`
let mut generics = self.parse_generics()?; // `<'a, T, ...>`
let decl = match self.parse_fn_decl(&fn_parse_mode, AllowPlus::Yes, RecoverReturnSign::Yes)
Expand Down Expand Up @@ -2786,7 +2799,7 @@ impl<'a> Parser<'a> {
// that the keyword is already present and the second instance should be removed.
let wrong_kw = if self.check_keyword(exp!(Const)) {
match constness {
Const::Yes(sp) => Some(WrongKw::Duplicated(sp)),
Const::Comptime(sp) | Const::Yes(sp) => Some(WrongKw::Duplicated(sp)),
Const::No => {
recover_constness = Const::Yes(self.token.span);
match parsing_mode {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| AttributeKind::Marker(..)
| AttributeKind::SkipDuringMethodDispatch { .. }
| AttributeKind::Coinductive(..)
| AttributeKind::Comptime(..)
| AttributeKind::DenyExplicitImpl(..)
| AttributeKind::DoNotImplementViaObject(..)
| AttributeKind::SpecializationTrait(..)
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1907,6 +1907,7 @@ symbols! {
rustc_clean,
rustc_coherence_is_core,
rustc_coinductive,
rustc_comptime,
rustc_confusables,
rustc_const_stable,
rustc_const_stable_indirect,
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_trait_selection/src/traits/effects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ fn evaluate_host_effect_for_destruct_goal<'tcx>(
.map(|field| ty::TraitRef::new(tcx, destruct_def_id, [field.ty(tcx, args)]))
.collect();
match adt_def.destructor(tcx).map(|dtor| tcx.constness(dtor.did)) {
Some(hir::Constness::Comptime) => 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.
Expand Down Expand Up @@ -530,6 +531,8 @@ fn evaluate_host_effect_for_fn_goal<'tcx>(
};

match tcx.constness(def) {
// FIXME(comptime)
hir::Constness::Comptime => Err(EvaluationFailure::NoSolution),
hir::Constness::Const => Ok(tcx
.const_conditions(def)
.instantiate(tcx, args)
Expand Down
8 changes: 6 additions & 2 deletions src/librustdoc/html/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1445,14 +1445,18 @@ pub(crate) fn print_constness_with_space(
const_stab: Option<ConstStability>,
) -> &'static str {
match c {
hir::Constness::Const => match (overall_stab, const_stab) {
hir::Constness::Comptime | hir::Constness::Const => 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 "
match c {
hir::Constness::Comptime => "comptime ",
hir::Constness::Const => "const ",
_ => unreachable!(),
}
}
// const unstable (and overall stable)
(Some(_), Some(ConstStability { level: StabilityLevel::Unstable { .. }, .. })) => "",
Expand Down
2 changes: 2 additions & 0 deletions src/tools/rustfmt/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ pub(crate) fn format_coro(coroutine_kind: &ast::CoroutineKind) -> &'static str {
#[inline]
pub(crate) fn format_constness(constness: ast::Const) -> &'static str {
match constness {
ast::Const::Comptime(..) => "comptime ",
ast::Const::Yes(..) => "const ",
ast::Const::No => "",
}
Expand All @@ -94,6 +95,7 @@ pub(crate) fn format_constness(constness: ast::Const) -> &'static str {
#[inline]
pub(crate) fn format_constness_right(constness: ast::Const) -> &'static str {
match constness {
ast::Const::Comptime(..) => " comptime",
ast::Const::Yes(..) => " const",
ast::Const::No => "",
}
Expand Down
7 changes: 7 additions & 0 deletions tests/ui/comptime/const_comptime.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#![feature(rustc_attrs)]

#[rustc_comptime]
const fn foo() {}
//~^ ERROR a function cannot be both `comptime` and `const`

fn main() {}
10 changes: 10 additions & 0 deletions tests/ui/comptime/const_comptime.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
error: a function cannot be both `comptime` and `const`
--> $DIR/const_comptime.rs:4:1
|
LL | #[rustc_comptime]
| ----------------- `comptime` because of this
LL | const fn foo() {}
| ^^^^^ help: remove the `const`

error: aborting due to 1 previous error

24 changes: 24 additions & 0 deletions tests/ui/comptime/not_callable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#![feature(rustc_attrs, const_trait_impl)]

#[rustc_comptime]
fn foo() {}

fn main() {
// Ok
const { foo() };
// Not ok
foo();
//~^ ERROR: comptime fns can only be called at compile time
}

const fn bar() {
// Not ok
foo();
//~^ ERROR: comptime fns can only be called at compile time
}

#[rustc_comptime]
fn baz() {
// Ok
foo();
}
Loading
Loading