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
11 changes: 2 additions & 9 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1052,7 +1052,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
binder: &ClosureBinder,
capture_clause: CaptureBy,
closure_id: NodeId,
mut constness: Const,
constness: Const,
movability: Movability,
decl: &FnDecl,
body: &Expr,
Expand All @@ -1062,18 +1062,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
let closure_def_id = self.local_def_id(closure_id);
let (binder_clause, generic_params) = self.lower_closure_binder(binder);

if let Const::Yes(span) = constness {
if !self.is_in_const_context {
self.dcx().span_err(span, "cannot use `const` closures outside of const contexts");
constness = Const::No;
}
}

let (body_id, closure_kind) = self.with_new_scopes(fn_decl_span, move |this| {
let mut coroutine_kind = find_attr!(attrs, Coroutine(_) => hir::CoroutineKind::Coroutine(Movability::Movable));

// FIXME(contracts): Support contracts on closures?
let body_id = this.lower_fn_body(decl, None, constness, |this| {
let body_id = this.lower_fn_body(decl, None, |this| {
this.coroutine_kind = coroutine_kind;
let e = this.lower_expr_mut(body);
coroutine_kind = this.coroutine_kind;
Expand Down
23 changes: 4 additions & 19 deletions compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::mem;

use rustc_abi::ExternAbi;
use rustc_ast::visit::AssocCtxt;
Expand Down Expand Up @@ -378,7 +377,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
body.as_deref(),
attrs,
contract.as_deref(),
header.constness,
);

let itctx = ImplTraitContext::Universal;
Expand Down Expand Up @@ -1068,7 +1066,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
Some(body),
attrs,
contract.as_deref(),
sig.header.constness,
);
let (generics, sig) = self.lower_method_sig(
generics,
Expand Down Expand Up @@ -1262,7 +1259,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
body.as_deref(),
attrs,
contract.as_deref(),
sig.header.constness,
);
let (generics, sig) = self.lower_method_sig(
generics,
Expand Down Expand Up @@ -1391,13 +1387,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
f: impl FnOnce(&mut Self) -> (&'hir [hir::Param<'hir>], hir::Expr<'hir>),
) -> hir::BodyId {
let prev_coroutine_kind = self.coroutine_kind.take();
let prev_is_in_const_context = mem::take(&mut self.is_in_const_context);
let task_context = self.task_context.take();
let (parameters, result) = f(self);
let body_id = self.record_body(parameters, result);
self.task_context = task_context;
self.coroutine_kind = prev_coroutine_kind;
self.is_in_const_context = prev_is_in_const_context;
body_id
}

Expand All @@ -1416,13 +1410,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
&mut self,
decl: &FnDecl,
contract: Option<&FnContract>,
constness: Const,
body: impl FnOnce(&mut Self) -> hir::Expr<'hir>,
) -> hir::BodyId {
self.lower_body(|this| {
if let Const::Yes(_) = constness {
this.is_in_const_context = true;
}
let params =
this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x)));

Expand All @@ -1440,20 +1430,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
decl: &FnDecl,
body: &Block,
contract: Option<&FnContract>,
constness: Const,
) -> hir::BodyId {
self.lower_fn_body(decl, contract, constness, |this| this.lower_block_expr(body))
self.lower_fn_body(decl, contract, |this| this.lower_block_expr(body))
}

pub(super) fn lower_const_body(&mut self, span: Span, expr: Option<&Expr>) -> hir::BodyId {
self.lower_body(|this| {
(
&[],
match expr {
Some(expr) => {
this.is_in_const_context = true;
this.lower_expr_mut(expr)
}
Some(expr) => this.lower_expr_mut(expr),
None => this.expr_err(span, this.dcx().span_delayed_bug(span, "no block")),
},
)
Expand All @@ -1472,13 +1458,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
body: Option<&Block>,
attrs: &'hir [hir::Attribute],
contract: Option<&FnContract>,
constness: Const,
) -> hir::BodyId {
let Some(body) = body else {
// Functions without a body are an error, except if this is an intrinsic. For those we
// create a fake body so that the entire rest of the compiler doesn't have to deal with
// this as a special case.
return self.lower_fn_body(decl, contract, constness, |this| {
return self.lower_fn_body(decl, contract, |this| {
if find_attr!(attrs, RustcIntrinsic) || this.tcx.is_sdylib_interface_build() {
let span = this.lower_span(span);
let empty_block = hir::Block {
Expand All @@ -1503,7 +1488,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
};
let Some(coroutine_kind) = coroutine_kind else {
// Typical case: not a coroutine.
return self.lower_fn_body_block(decl, body, contract, constness);
return self.lower_fn_body_block(decl, body, contract);
};
// FIXME(contracts): Support contracts on async fn.
self.lower_body(|this| {
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ struct LoweringContext<'a, 'hir> {
loop_scope: Option<HirId>,
is_in_loop_condition: bool,
is_in_dyn_type: bool,
is_in_const_context: bool,

current_hir_id_owner: hir::OwnerId,
item_local_id_counter: hir::ItemLocalId,
Expand Down Expand Up @@ -193,7 +192,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
loop_scope: None,
is_in_loop_condition: false,
is_in_dyn_type: false,
is_in_const_context: false,
coroutine_kind: None,
task_context: None,
current_item: None,
Expand Down
8 changes: 7 additions & 1 deletion compiler/rustc_const_eval/src/const_eval/fn_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Constness {
// Foreign functions cannot be evaluated at compile-time.
Constness::NotConst
}
Node::Expr(e) if let ExprKind::Closure(c) = e.kind => c.constness,
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() {
tcx.dcx().span_err(tcx.def_span(def_id), "cannot use `const` closures outside of const contexts");
return Constness::NotConst;
}
c.constness
},
// FIXME(fee1-dead): extract this one out and rename this query to `fn_constness` so we don't need `is_const_fn` anymore.
Node::Item(i) if let ItemKind::Impl(impl_) = i.kind => impl_.constness,
Node::Item(Item { kind: ItemKind::Fn { sig, .. }, .. }) => sig.header.constness,
Expand Down
12 changes: 1 addition & 11 deletions compiler/rustc_middle/src/hir/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,17 +319,7 @@ impl<'tcx> TyCtxt<'tcx> {
BodyOwnerKind::Fn if self.is_constructor(def_id) => return None,
// Const closures use their parent's const context
BodyOwnerKind::Closure if self.is_const_fn(def_id) => {
return Some(
self.hir_body_const_context(self.local_parent(local_def_id)).unwrap_or_else(
|| {
assert!(
self.dcx().has_errors().is_some(),
"`const` closure with no enclosing const context",
);
ConstContext::ConstFn
},
),
);
return self.hir_body_const_context(self.local_parent(local_def_id));
}
BodyOwnerKind::Fn if self.is_const_fn(def_id) => ConstContext::ConstFn,
BodyOwnerKind::Fn | BodyOwnerKind::Closure | BodyOwnerKind::GlobalAsm => return None,
Expand Down
25 changes: 25 additions & 0 deletions tests/ui/consts/const-closure-in-trait-impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//@ check-pass

#![feature(const_closures, const_destruct, const_trait_impl)]

use std::marker::Destruct;
use std::num::NonZero;

const trait T {
fn a(&mut self, f: impl [const] Fn() + [const] Destruct);
fn b(&mut self);
}

struct S;

impl const T for S {
fn a(&mut self, f: impl [const] Fn() + [const] Destruct) {
f()
}

fn b(&mut self) {
self.a(const || {});
}
}

fn main() {}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ impl<T: Foo> Foo for &mut T {
const fn test() -> impl [const] Fn() {
//~^ ERROR functions in trait impls cannot be declared const
const move || {}
//~^ ERROR: cannot use `const` closures outside of const contexts
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ help: ... and declare the impl to be const instead
LL | impl<T: Foo> const Foo for &mut T {
| +++++

error: aborting due to 1 previous error
error: cannot use `const` closures outside of const contexts
--> $DIR/const-closure-in-non-const-trait-impl-method.rs:13:9
|
LL | const move || {}
| ^^^^^^^^^^^^^

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0379`.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ trait Tr {
const fn test() {
//~^ ERROR functions in traits cannot be declared const
(const || {})()
//~^ ERROR cannot use `const` closures outside of const contexts
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ LL | const fn test() {
| functions in traits cannot be const
| help: remove the `const`

error: aborting due to 1 previous error
error: cannot use `const` closures outside of const contexts
--> $DIR/const-closure-in-non-const-trait-method.rs:8:10
|
LL | (const || {})()
| ^^^^^^^^

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0379`.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ error: cannot use `const` closures outside of const contexts
--> $DIR/non-const-op-const-closure-non-const-outer.rs:14:6
|
LL | (const || { (()).foo() })();
| ^^^^^
| ^^^^^^^^
Copy link
Copy Markdown
Contributor Author

@oli-obk oli-obk Apr 25, 2026

Choose a reason for hiding this comment

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

The only downside of this PR is that we highlight more than the const part of the closure now

View changes since the review


error: aborting due to 1 previous error

Loading