Rollup merge of #153818 - oli-obk:const-closures, r=fee1-dead

Reimplement const closures

Tracking issue: rust-lang/rust#106003

Best reviewed commit-by-commit

The old solver can't handle `for<'a> |x: &'a()| ()` closures in const contexts, but that feature is unstable itself, so we can just leave it to the next solver to handle.

We need a lot more tests, we're testing the bare minimum of success and failure paths right now.

r? @fee1-dead
This commit is contained in:
Stuart Cook
2026-03-14 17:30:24 +11:00
committed by GitHub
30 changed files with 159 additions and 150 deletions
+16 -3
View File
@@ -230,6 +230,7 @@ pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'hir> {
e.id,
expr_hir_id,
*coroutine_kind,
*constness,
fn_decl,
body,
*fn_decl_span,
@@ -1060,7 +1061,7 @@ fn lower_expr_closure(
binder: &ClosureBinder,
capture_clause: CaptureBy,
closure_id: NodeId,
constness: Const,
mut constness: Const,
movability: Movability,
decl: &FnDecl,
body: &Expr,
@@ -1070,11 +1071,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;
@@ -1157,6 +1165,7 @@ fn lower_expr_coroutine_closure(
closure_id: NodeId,
closure_hir_id: HirId,
coroutine_kind: CoroutineKind,
constness: Const,
decl: &FnDecl,
body: &Expr,
fn_decl_span: Span,
@@ -1203,6 +1212,10 @@ fn lower_expr_coroutine_closure(
let fn_decl =
self.lower_fn_decl(&decl, closure_id, fn_decl_span, FnDeclKind::Closure, None);
if let Const::Yes(span) = constness {
self.dcx().span_err(span, "const coroutines are not supported");
}
let c = self.arena.alloc(hir::Closure {
def_id: closure_def_id,
binder: binder_clause,
@@ -1216,7 +1229,7 @@ fn lower_expr_coroutine_closure(
// knows that a `FnDecl` output type like `-> &str` actually means
// "coroutine that returns &str", rather than directly returning a `&str`.
kind: hir::ClosureKind::CoroutineClosure(coroutine_desugaring),
constness: hir::Constness::NotConst,
constness: self.lower_constness(constness),
});
hir::ExprKind::Closure(c)
}
+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,
+1 -1
View File
@@ -431,7 +431,7 @@ pub fn internal(&self, feature: Symbol) -> bool {
/// Allows defining and calling c-variadic functions in const contexts.
(unstable, const_c_variadic, "1.95.0", Some(151787)),
/// Allows `const || {}` closures in const contexts.
(incomplete, const_closures, "1.68.0", Some(106003)),
(unstable, const_closures, "1.68.0", Some(106003)),
/// Allows using `[const] Destruct` bounds and calling drop impls in const contexts.
(unstable, const_destruct, "1.85.0", Some(133214)),
/// Allows `for _ in _` loops in const contexts.
@@ -1069,6 +1069,9 @@ pub(super) fn const_conditions<'tcx>(
},
// N.B. Tuple ctors are unconditionally constant.
Node::Ctor(hir::VariantData::Tuple { .. }) => return Default::default(),
Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(_), .. }) => {
(hir::Generics::empty(), None, tcx.is_conditionally_const(tcx.local_parent(def_id)))
}
_ => bug!("const_conditions called on wrong item: {def_id:?}"),
};
+6 -4
View File
@@ -310,16 +310,18 @@ pub fn hir_body_owner_kind(self, def_id: impl Into<DefId>) -> BodyOwnerKind {
/// This should only be used for determining the context of a body, a return
/// value of `Some` does not always suggest that the owner of the body is `const`,
/// just that it has to be checked as if it were.
pub fn hir_body_const_context(self, def_id: LocalDefId) -> Option<ConstContext> {
let def_id = def_id.into();
pub fn hir_body_const_context(self, local_def_id: LocalDefId) -> Option<ConstContext> {
let def_id = local_def_id.into();
let ccx = match self.hir_body_owner_kind(def_id) {
BodyOwnerKind::Const { inline } => ConstContext::Const { 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
// 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));
}
BodyOwnerKind::Fn if self.is_const_fn(def_id) => ConstContext::ConstFn,
BodyOwnerKind::Fn | BodyOwnerKind::Closure | BodyOwnerKind::GlobalAsm => return None,
};
@@ -402,6 +402,11 @@ fn fn_is_const(self, def_id: DefId) -> bool {
self.is_conditionally_const(def_id)
}
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
}
fn alias_has_const_conditions(self, def_id: DefId) -> bool {
debug_assert_matches!(self.def_kind(def_id), DefKind::AssocTy | DefKind::OpaqueTy);
self.is_conditionally_const(def_id)
+1 -5
View File
@@ -2118,11 +2118,7 @@ pub fn is_conditionally_const(self, def_id: impl Into<DefId>) -> bool {
// FIXME(const_trait_impl): ATPITs could be conditionally const?
hir::OpaqueTyOrigin::TyAlias { .. } => false,
},
DefKind::Closure => {
// Closures and RPITs will eventually have const conditions
// for `[const]` bounds.
false
}
DefKind::Closure => self.constness(def_id) == hir::Constness::Const,
DefKind::Ctor(_, CtorKind::Const)
| DefKind::Mod
| DefKind::Struct
@@ -661,10 +661,11 @@ fn coroutine_closure_to_ambiguous_coroutine<I: Interner>(
///
/// Doing so on all calls to `extract_tupled_inputs_and_output_from_callable`
/// would be wasteful.
#[instrument(level = "trace", skip(cx), ret)]
pub(in crate::solve) fn extract_fn_def_from_const_callable<I: Interner>(
cx: I,
self_ty: I::Ty,
) -> Result<(ty::Binder<I, (I::Ty, I::Ty)>, I::FunctionId, I::GenericArgs), NoSolution> {
) -> Result<(ty::Binder<I, (I::Ty, I::Ty)>, I::DefId, I::GenericArgs), NoSolution> {
match self_ty.kind() {
ty::FnDef(def_id, args) => {
let sig = cx.fn_sig(def_id);
@@ -675,7 +676,7 @@ pub(in crate::solve) fn extract_fn_def_from_const_callable<I: Interner>(
Ok((
sig.instantiate(cx, args)
.map_bound(|sig| (Ty::new_tup(cx, sig.inputs().as_slice()), sig.output())),
def_id,
def_id.into(),
args,
))
} else {
@@ -686,9 +687,19 @@ pub(in crate::solve) fn extract_fn_def_from_const_callable<I: Interner>(
ty::FnPtr(..) => {
return Err(NoSolution);
}
// `Closure`s are not const for now.
ty::Closure(..) => {
return Err(NoSolution);
ty::Closure(def, args) => {
if cx.closure_is_const(def) {
let closure_args = args.as_closure();
Ok((
closure_args
.sig()
.map_bound(|sig| (sig.inputs().get(0).unwrap(), sig.output())),
def.into(),
args,
))
} else {
return Err(NoSolution);
}
}
// `CoroutineClosure`s are not const for now.
ty::CoroutineClosure(..) => {
@@ -272,6 +272,7 @@ fn consider_builtin_fn_ptr_trait_candidate(
todo!("Fn* are not yet const")
}
#[instrument(level = "trace", skip_all, ret)]
fn consider_builtin_fn_trait_candidates(
ecx: &mut EvalCtxt<'_, D>,
goal: Goal<I, Self>,
@@ -289,7 +290,7 @@ fn consider_builtin_fn_trait_candidates(
let output_is_sized_pred =
ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]);
let requirements = cx
.const_conditions(def_id.into())
.const_conditions(def_id)
.iter_instantiated(cx, args)
.map(|trait_ref| {
(
@@ -519,10 +519,21 @@ fn evaluate_host_effect_for_fn_goal<'tcx>(
// We may support function pointers at some point in the future
ty::FnPtr(..) => return Err(EvaluationFailure::NoSolution),
// Closures could implement `[const] Fn`,
// Coroutines could implement `[const] Fn`,
// but they don't really need to right now.
ty::Closure(..) | ty::CoroutineClosure(_, _) => {
return Err(EvaluationFailure::NoSolution);
ty::CoroutineClosure(_, _) => return Err(EvaluationFailure::NoSolution),
ty::Closure(def, args) => {
// For now we limit ourselves to closures without binders. The next solver can handle them.
let sig =
args.as_closure().sig().no_bound_vars().ok_or(EvaluationFailure::NoSolution)?;
(
def,
tcx.mk_args_from_iter(
[ty::GenericArg::from(*sig.inputs().get(0).unwrap()), sig.output().into()]
.into_iter(),
),
)
}
// Everything else needs explicit impls or cannot have an impl
+1
View File
@@ -308,6 +308,7 @@ fn impl_super_outlives(
fn impl_is_const(self, def_id: Self::ImplId) -> bool;
fn fn_is_const(self, def_id: Self::FunctionId) -> bool;
fn closure_is_const(self, def_id: Self::ClosureId) -> bool;
fn alias_has_const_conditions(self, def_id: Self::DefId) -> bool;
fn const_conditions(
self,
@@ -1,4 +1,4 @@
error[E0277]: the trait bound `(): [const] Bar` is not satisfied
error[E0277]: the trait bound `(): const Bar` is not satisfied
--> $DIR/call-const-closure.rs:16:18
|
LL | (const || ().foo())();
@@ -0,0 +1,14 @@
error[E0277]: the trait bound `(): const Bar` is not satisfied
--> $DIR/call-const-closure.rs:16:18
|
LL | (const || ().foo())();
| ^^^
|
help: make the `impl` of trait `Bar` `const`
|
LL | impl const Bar for () {
| +++++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.
@@ -1,8 +1,8 @@
//@ compile-flags: -Znext-solver
//@[next] compile-flags: -Znext-solver
//@ revisions: next old
//@ edition:2021
#![feature(const_trait_impl, const_closures)]
#![allow(incomplete_features)]
const trait Bar {
fn foo(&self);
@@ -14,8 +14,7 @@ fn foo(&self) {}
const FOO: () = {
(const || ().foo())();
//~^ ERROR the trait bound `(): [const] Bar` is not satisfied
// FIXME(const_trait_impl): The constness environment for const closures is wrong.
//~^ ERROR the trait bound `(): const Bar` is not satisfied
};
fn main() {}
+3 -4
View File
@@ -1,11 +1,10 @@
// FIXME(const_trait_impl) check-pass
//@ compile-flags: -Znext-solver
//@ check-pass
//@[next] compile-flags: -Znext-solver
//@revisions: next old
#![feature(const_closures, const_trait_impl)]
#![allow(incomplete_features)]
const _: () = {
assert!((const || true)());
//~^ ERROR }: [const] Fn()` is not satisfied
};
fn main() {}
-9
View File
@@ -1,9 +0,0 @@
error[E0277]: the trait bound `{closure@$DIR/call.rs:7:14: 7:22}: [const] Fn()` is not satisfied
--> $DIR/call.rs:7:13
|
LL | assert!((const || true)());
| ^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.
@@ -1,4 +1,3 @@
#![allow(incomplete_features)]
#![feature(const_closures, const_trait_impl)]
const fn create_array<const N: usize>(mut f: impl FnMut(usize) -> u32 + Copy) -> [u32; N] {
@@ -16,9 +15,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,5 +1,5 @@
error[E0277]: the trait bound `impl FnMut(usize) -> u32 + Copy: [const] FnMut(usize)` is not satisfied
--> $DIR/const-closure-issue-125866-error.rs:8:22
--> $DIR/const-closure-issue-125866-error.rs:7:22
|
LL | array[i] = f(i);
| - ^
@@ -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,8 +1,6 @@
//@ known-bug: #110395
// FIXME check-pass
//@check-pass
#![feature(const_trait_impl, const_closures)]
#![allow(incomplete_features)]
const fn test() -> impl [const] Fn() {
const move || {}
@@ -1,9 +0,0 @@
error[E0277]: the trait bound `{closure@$DIR/const-closure-parse-not-item.rs:8:5: 8:18}: [const] Fn()` is not satisfied
--> $DIR/const-closure-parse-not-item.rs:7:20
|
LL | const fn test() -> impl [const] Fn() {
| ^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.
@@ -1,16 +0,0 @@
//@ known-bug: #110395
//@ compile-flags: -Znext-solver
#![feature(const_closures, const_trait_impl)]
#![allow(incomplete_features)]
trait Foo {
fn foo(&self);
}
impl Foo for () {
fn foo(&self) {}
}
fn main() {
(const || (()).foo())();
}
@@ -1,9 +0,0 @@
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
|
LL | (const || (()).foo())();
| ^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.
+4 -3
View File
@@ -1,13 +1,14 @@
// 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: cannot call conditionally-const closure in constants
//~| ERROR: `Fn` is not yet stable as a const trait
}
macro_rules! e {
($e:expr) => {}
($e:expr) => {};
}
e!((const || {}));
+27 -11
View File
@@ -1,15 +1,15 @@
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
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: const closures are experimental
--> $DIR/gate.rs:13:5
--> $DIR/gate.rs:14:5
|
LL | e!((const || {}));
| ^^^^^
@@ -18,13 +18,29 @@ 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[E0658]: cannot call conditionally-const closure in constants
--> $DIR/gate.rs:4:13
|
LL | (const || {})();
| ^^^^^^^^^^^^^^^
LL | const { (const || {})() };
| ^^^^^^^^^^^^^^^
|
= note: closures need an RFC before allowed to be called in constants
= note: calls in constants are limited to constant functions, tuple structs and tuple variants
= note: see issue #143874 <https://github.com/rust-lang/rust/issues/143874> for more information
= help: add `#![feature(const_trait_impl)]` 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: aborting due to 3 previous errors
error: `Fn` is not yet stable as a const trait
--> $DIR/gate.rs:4:13
|
LL | const { (const || {})() };
| ^^^^^^^^^^^^^^^
|
help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
|
LL + #![feature(const_trait_impl)]
|
Some errors have detailed explanations: E0277, E0658.
For more information about an error, try `rustc --explain E0277`.
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0658`.
@@ -1,14 +1,13 @@
#![feature(const_trait_impl)]
#![feature(const_trait_impl, const_closures, const_cmp)]
const fn test() -> impl [const] Fn() {
//~^ ERROR: }: [const] Fn()` is not satisfied
const move || { //~ ERROR const closures are experimental
const move || {
let sl: &[u8] = b"foo";
match sl {
[first, remainder @ ..] => {
assert_eq!(first, &b'f');
// FIXME(const_closures) ^ ERROR cannot call non-const function
//~^ ERROR cannot call non-const function
}
[] => panic!(),
}
@@ -1,20 +1,13 @@
error[E0658]: const closures are experimental
--> $DIR/ice-112822-expected-type-for-param.rs:5:5
error[E0015]: cannot call non-const function `core::panicking::assert_failed::<&u8, &u8>` in constant functions
--> $DIR/ice-112822-expected-type-for-param.rs:9:17
|
LL | const move || {
| ^^^^^
LL | assert_eq!(first, &b'f');
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= 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
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
note: function `assert_failed` is not const
--> $SRC_DIR/core/src/panicking.rs:LL:COL
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
error[E0277]: the trait bound `{closure@$DIR/ice-112822-expected-type-for-param.rs:5:5: 5:18}: [const] Fn()` is not satisfied
--> $DIR/ice-112822-expected-type-for-param.rs:3:20
|
LL | const fn test() -> impl [const] Fn() {
| ^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0277, E0658.
For more information about an error, try `rustc --explain E0277`.
For more information about this error, try `rustc --explain E0015`.
@@ -1,5 +1,4 @@
#![feature(const_closures, const_trait_impl)]
#![allow(incomplete_features)]
trait Foo {
fn foo(&self);
@@ -13,5 +12,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:14:6
|
LL | (const || { (()).foo() })();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.