Reject const closures outside const contexts

This commit is contained in:
Oli Scherer
2026-03-12 08:44:28 +00:00
parent accbfccd59
commit ae4ddd2806
10 changed files with 50 additions and 52 deletions
+9 -2
View File
@@ -1062,7 +1062,7 @@ fn lower_expr_closure(
binder: &ClosureBinder,
capture_clause: CaptureBy,
closure_id: NodeId,
constness: Const,
mut constness: Const,
movability: Movability,
decl: &FnDecl,
body: &Expr,
@@ -1072,11 +1072,18 @@ fn lower_expr_closure(
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, |this| {
let body_id = this.lower_fn_body(decl, None, constness, |this| {
this.coroutine_kind = coroutine_kind;
let e = this.lower_expr_mut(body);
coroutine_kind = this.coroutine_kind;
+20 -4
View File
@@ -1,3 +1,5 @@
use std::mem;
use rustc_abi::ExternAbi;
use rustc_ast::visit::AssocCtxt;
use rustc_ast::*;
@@ -345,6 +347,7 @@ fn lower_item_kind(
body.as_deref(),
attrs,
contract.as_deref(),
header.constness,
);
let itctx = ImplTraitContext::Universal;
@@ -1024,6 +1027,7 @@ fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> {
Some(body),
attrs,
contract.as_deref(),
sig.header.constness,
);
let (generics, sig) = self.lower_method_sig(
generics,
@@ -1217,6 +1221,7 @@ fn lower_impl_item(
body.as_deref(),
attrs,
contract.as_deref(),
sig.header.constness,
);
let (generics, sig) = self.lower_method_sig(
generics,
@@ -1346,11 +1351,13 @@ pub(super) fn lower_body(
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
}
@@ -1369,9 +1376,13 @@ pub(super) fn lower_fn_body(
&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)));
@@ -1389,8 +1400,9 @@ fn lower_fn_body_block(
decl: &FnDecl,
body: &Block,
contract: Option<&FnContract>,
constness: Const,
) -> hir::BodyId {
self.lower_fn_body(decl, contract, |this| this.lower_block_expr(body))
self.lower_fn_body(decl, contract, constness, |this| this.lower_block_expr(body))
}
pub(super) fn lower_const_body(&mut self, span: Span, expr: Option<&Expr>) -> hir::BodyId {
@@ -1398,7 +1410,10 @@ pub(super) fn lower_const_body(&mut self, span: Span, expr: Option<&Expr>) -> hi
(
&[],
match expr {
Some(expr) => this.lower_expr_mut(expr),
Some(expr) => {
this.is_in_const_context = true;
this.lower_expr_mut(expr)
}
None => this.expr_err(span, this.dcx().span_delayed_bug(span, "no block")),
},
)
@@ -1417,12 +1432,13 @@ fn lower_maybe_coroutine_body(
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, |this| {
return self.lower_fn_body(decl, contract, constness, |this| {
if find_attr!(attrs, RustcIntrinsic) || this.tcx.is_sdylib_interface_build() {
let span = this.lower_span(span);
let empty_block = hir::Block {
@@ -1447,7 +1463,7 @@ fn lower_maybe_coroutine_body(
};
let Some(coroutine_kind) = coroutine_kind else {
// Typical case: not a coroutine.
return self.lower_fn_body_block(decl, body, contract);
return self.lower_fn_body_block(decl, body, contract, constness);
};
// FIXME(contracts): Support contracts on async fn.
self.lower_body(|this| {
+2
View File
@@ -129,6 +129,7 @@ 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,
@@ -190,6 +191,7 @@ fn new(
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,
@@ -16,9 +16,9 @@
}
fn main() {
let x = create_array(const |i| 2 * i as u32);
let x = const { create_array(const |i| 2 * i as u32) };
assert_eq!(x, [0, 2, 4, 6, 8]);
let y = create_array(const |i| 2 * i as u32 + 1);
let y = const { create_array(const |i| 2 * i as u32 + 1) };
assert_eq!(y, [1, 3, 5, 7, 9]);
}
@@ -1,25 +0,0 @@
//@ check-pass
#![allow(incomplete_features)]
#![feature(const_closures, const_trait_impl)]
const fn create_array<const N: usize>(mut f: impl [const] FnMut(usize) -> u32 + Copy) -> [u32; N] {
let mut array = [0; N];
let mut i = 0;
loop {
array[i] = f(i);
i += 1;
if i == N {
break;
}
}
array
}
fn main() {
let x = create_array(const |i| 2 * i as u32);
assert_eq!(x, [0, 2, 4, 6, 8]);
let y = create_array(const |i| 2 * i as u32 + 1);
assert_eq!(y, [1, 3, 5, 7, 9]);
}
@@ -1,9 +1,8 @@
error[E0277]: the trait bound `{closure@$DIR/const_closure-const_trait_impl-ice-113381.rs:15:6: 15:14}: [const] Fn()` is not satisfied
--> $DIR/const_closure-const_trait_impl-ice-113381.rs:15:5
error: cannot use `const` closures outside of const contexts
--> $DIR/const_closure-const_trait_impl-ice-113381.rs:15:6
|
LL | (const || (()).foo())();
| ^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.
+3 -3
View File
@@ -1,13 +1,13 @@
// gate-test-const_closures
fn main() {
(const || {})();
const { (const || {})() };
//~^ ERROR: const closures are experimental
//~| ERROR: the trait bound `{closure@$DIR/gate.rs:4:6: 4:14}: [const] Fn()` is not satisfied
//~| ERROR: the trait bound `{closure@$DIR/gate.rs:4:14: 4:22}: [const] Fn()` is not satisfied
}
macro_rules! e {
($e:expr) => {}
($e:expr) => {};
}
e!((const || {}));
+7 -7
View File
@@ -1,8 +1,8 @@
error[E0658]: const closures are experimental
--> $DIR/gate.rs:4:6
--> $DIR/gate.rs:4:14
|
LL | (const || {})();
| ^^^^^
LL | const { (const || {})() };
| ^^^^^
|
= note: see issue #106003 <https://github.com/rust-lang/rust/issues/106003> for more information
= help: add `#![feature(const_closures)]` to the crate attributes to enable
@@ -18,11 +18,11 @@ LL | e!((const || {}));
= help: add `#![feature(const_closures)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0277]: the trait bound `{closure@$DIR/gate.rs:4:6: 4:14}: [const] Fn()` is not satisfied
--> $DIR/gate.rs:4:5
error[E0277]: the trait bound `{closure@$DIR/gate.rs:4:14: 4:22}: [const] Fn()` is not satisfied
--> $DIR/gate.rs:4:13
|
LL | (const || {})();
| ^^^^^^^^^^^^^^^
LL | const { (const || {})() };
| ^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors
@@ -13,5 +13,5 @@ fn main() {
// #150052 deduplicate diagnostics for const trait supertraits
// so we only get one error here
(const || { (()).foo() })();
//~^ ERROR: }: [const] Fn()` is not satisfied
//~^ ERROR: cannot use `const` closures outside of const contexts
}
@@ -1,9 +1,8 @@
error[E0277]: the trait bound `{closure@$DIR/non-const-op-const-closure-non-const-outer.rs:15:6: 15:14}: [const] Fn()` is not satisfied
--> $DIR/non-const-op-const-closure-non-const-outer.rs:15:5
error: cannot use `const` closures outside of const contexts
--> $DIR/non-const-op-const-closure-non-const-outer.rs:15:6
|
LL | (const || { (()).foo() })();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.