Rollup merge of #154957 - lapla-cogito:issue_153891, r=oli-obk

Fix ICE when const closure appears inside a non-const trait method

Fixes rust-lang/rust#153891

`hir_body_const_context()` unconditionally delegated to the parent's const context for const closures, returning `None` when the parent had no const context. This caused `mir_const_qualif()` to hit a `span_bug!`, since `mir_promoted()` had already decided to call it based on the closure's own syntactic constness. Fall back to `ConstContext::ConstFn` when the parent's const context is `None`, so that the const closure body is still properly const-checked rather than triggering an ICE.

Examining [another attempt](https://github.com/rust-lang/rust/pull/153900/changes) at this issue (which has already been closed), I thought that its approach represents a workaround fix to avoid inconsistencies in the caller of `mir_promoted()`, whereas I think the correct behavior is for `hir_body_const_context()` itself to return the proper value.
This commit is contained in:
Guillaume Gomez
2026-04-23 14:42:47 +02:00
committed by GitHub
5 changed files with 71 additions and 1 deletions
+11 -1
View File
@@ -319,7 +319,17 @@ pub fn hir_body_const_context(self, local_def_id: LocalDefId) -> Option<ConstCon
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 self.hir_body_const_context(self.local_parent(local_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
},
),
);
}
BodyOwnerKind::Fn if self.is_const_fn(def_id) => ConstContext::ConstFn,
BodyOwnerKind::Fn | BodyOwnerKind::Closure | BodyOwnerKind::GlobalAsm => return None,
@@ -0,0 +1,17 @@
#![feature(const_trait_impl)]
#![feature(const_closures)]
// Regression test for https://github.com/rust-lang/rust/issues/153891
const trait Foo {
fn test() -> impl [const] Fn();
}
impl<T: Foo> Foo for &mut T {
const fn test() -> impl [const] Fn() {
//~^ ERROR functions in trait impls cannot be declared const
const move || {}
}
}
fn main() {}
@@ -0,0 +1,19 @@
error[E0379]: functions in trait impls cannot be declared const
--> $DIR/const-closure-in-non-const-trait-impl-method.rs:11:5
|
LL | const fn test() -> impl [const] Fn() {
| ^^^^^ functions in trait impls cannot be const
|
help: remove the `const` ...
|
LL - const fn test() -> impl [const] Fn() {
LL + fn test() -> impl [const] Fn() {
|
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
For more information about this error, try `rustc --explain E0379`.
@@ -0,0 +1,12 @@
#![feature(const_closures)]
// Regression test for https://github.com/rust-lang/rust/issues/153891
trait Tr {
const fn test() {
//~^ ERROR functions in traits cannot be declared const
(const || {})()
}
}
fn main() {}
@@ -0,0 +1,12 @@
error[E0379]: functions in traits cannot be declared const
--> $DIR/const-closure-in-non-const-trait-method.rs:6:5
|
LL | const fn test() {
| ^^^^^-
| |
| functions in traits cannot be const
| help: remove the `const`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0379`.