diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index f0b292d3179da..4e1b46d568099 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -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, @@ -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; diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 2195581eccec3..571da622c9557 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1,4 +1,3 @@ -use std::mem; use rustc_abi::ExternAbi; use rustc_ast::visit::AssocCtxt; @@ -378,7 +377,6 @@ impl<'hir> LoweringContext<'_, 'hir> { body.as_deref(), attrs, contract.as_deref(), - header.constness, ); let itctx = ImplTraitContext::Universal; @@ -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, @@ -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, @@ -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 } @@ -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))); @@ -1440,9 +1430,8 @@ 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 { @@ -1450,10 +1439,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ( &[], 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")), }, ) @@ -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 { @@ -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| { diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 5188fb8d6aada..5e9674b7a0422 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -124,7 +124,6 @@ struct LoweringContext<'a, 'hir> { loop_scope: Option, 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, @@ -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, diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index 0b5065f12f805..57fd230ec4685 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -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, diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index d63034ed1d2bb..68357212bebe8 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -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, diff --git a/tests/ui/consts/const-closure-in-trait-impl.rs b/tests/ui/consts/const-closure-in-trait-impl.rs new file mode 100644 index 0000000000000..29722dd37c1e0 --- /dev/null +++ b/tests/ui/consts/const-closure-in-trait-impl.rs @@ -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() {} diff --git a/tests/ui/traits/const-traits/const-closure-in-non-const-trait-impl-method.rs b/tests/ui/traits/const-traits/const-closure-in-non-const-trait-impl-method.rs index b538c4b6a32fd..7980bd1793f45 100644 --- a/tests/ui/traits/const-traits/const-closure-in-non-const-trait-impl-method.rs +++ b/tests/ui/traits/const-traits/const-closure-in-non-const-trait-impl-method.rs @@ -11,6 +11,7 @@ impl 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 } } diff --git a/tests/ui/traits/const-traits/const-closure-in-non-const-trait-impl-method.stderr b/tests/ui/traits/const-traits/const-closure-in-non-const-trait-impl-method.stderr index 3ff5578748d29..15bc5b8458d80 100644 --- a/tests/ui/traits/const-traits/const-closure-in-non-const-trait-impl-method.stderr +++ b/tests/ui/traits/const-traits/const-closure-in-non-const-trait-impl-method.stderr @@ -14,6 +14,12 @@ help: ... and declare the impl to be const instead LL | impl 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`. diff --git a/tests/ui/traits/const-traits/const-closure-in-non-const-trait-method.rs b/tests/ui/traits/const-traits/const-closure-in-non-const-trait-method.rs index dfd0793180871..9bb6bf8fe704f 100644 --- a/tests/ui/traits/const-traits/const-closure-in-non-const-trait-method.rs +++ b/tests/ui/traits/const-traits/const-closure-in-non-const-trait-method.rs @@ -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 } } diff --git a/tests/ui/traits/const-traits/const-closure-in-non-const-trait-method.stderr b/tests/ui/traits/const-traits/const-closure-in-non-const-trait-method.stderr index 79462f620da37..dd728840b8252 100644 --- a/tests/ui/traits/const-traits/const-closure-in-non-const-trait-method.stderr +++ b/tests/ui/traits/const-traits/const-closure-in-non-const-trait-method.stderr @@ -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`. diff --git a/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr b/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr index f5521b90cc2d9..7befeec496d80 100644 --- a/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr +++ b/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr @@ -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() })(); - | ^^^^^ + | ^^^^^^^^ error: aborting due to 1 previous error