Skip to content

Commit 8e1c34f

Browse files
committed
Check closure's constness validity in the constness query
instead of during ast lowering, where it's not easily possible to obtain all the right information in time
1 parent 2b2f28d commit 8e1c34f

12 files changed

Lines changed: 33 additions & 71 deletions

compiler/rustc_ast_lowering/src/expr.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,7 +1052,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
10521052
binder: &ClosureBinder,
10531053
capture_clause: CaptureBy,
10541054
closure_id: NodeId,
1055-
mut constness: Const,
1055+
constness: Const,
10561056
movability: Movability,
10571057
decl: &FnDecl,
10581058
body: &Expr,
@@ -1062,18 +1062,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
10621062
let closure_def_id = self.local_def_id(closure_id);
10631063
let (binder_clause, generic_params) = self.lower_closure_binder(binder);
10641064

1065-
if let Const::Yes(span) = constness {
1066-
if !self.is_in_const_context {
1067-
self.dcx().span_err(span, "cannot use `const` closures outside of const contexts");
1068-
constness = Const::No;
1069-
}
1070-
}
1071-
10721065
let (body_id, closure_kind) = self.with_new_scopes(fn_decl_span, move |this| {
10731066
let mut coroutine_kind = find_attr!(attrs, Coroutine(_) => hir::CoroutineKind::Coroutine(Movability::Movable));
10741067

10751068
// FIXME(contracts): Support contracts on closures?
1076-
let body_id = this.lower_fn_body(decl, None, constness, |this| {
1069+
let body_id = this.lower_fn_body(decl, None, |this| {
10771070
this.coroutine_kind = coroutine_kind;
10781071
let e = this.lower_expr_mut(body);
10791072
coroutine_kind = this.coroutine_kind;

compiler/rustc_ast_lowering/src/item.rs

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use std::mem;
2-
31
use rustc_abi::ExternAbi;
42
use rustc_ast::visit::AssocCtxt;
53
use rustc_ast::*;
@@ -378,7 +376,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
378376
body.as_deref(),
379377
attrs,
380378
contract.as_deref(),
381-
header.constness,
382379
);
383380

384381
let itctx = ImplTraitContext::Universal;
@@ -1068,7 +1065,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
10681065
Some(body),
10691066
attrs,
10701067
contract.as_deref(),
1071-
sig.header.constness,
10721068
);
10731069
let (generics, sig) = self.lower_method_sig(
10741070
generics,
@@ -1262,7 +1258,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
12621258
body.as_deref(),
12631259
attrs,
12641260
contract.as_deref(),
1265-
sig.header.constness,
12661261
);
12671262
let (generics, sig) = self.lower_method_sig(
12681263
generics,
@@ -1391,13 +1386,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
13911386
f: impl FnOnce(&mut Self) -> (&'hir [hir::Param<'hir>], hir::Expr<'hir>),
13921387
) -> hir::BodyId {
13931388
let prev_coroutine_kind = self.coroutine_kind.take();
1394-
let prev_is_in_const_context = mem::take(&mut self.is_in_const_context);
13951389
let task_context = self.task_context.take();
13961390
let (parameters, result) = f(self);
13971391
let body_id = self.record_body(parameters, result);
13981392
self.task_context = task_context;
13991393
self.coroutine_kind = prev_coroutine_kind;
1400-
self.is_in_const_context = prev_is_in_const_context;
14011394
body_id
14021395
}
14031396

@@ -1416,13 +1409,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
14161409
&mut self,
14171410
decl: &FnDecl,
14181411
contract: Option<&FnContract>,
1419-
constness: Const,
14201412
body: impl FnOnce(&mut Self) -> hir::Expr<'hir>,
14211413
) -> hir::BodyId {
14221414
self.lower_body(|this| {
1423-
if let Const::Yes(_) = constness {
1424-
this.is_in_const_context = true;
1425-
}
14261415
let params =
14271416
this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x)));
14281417

@@ -1440,20 +1429,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
14401429
decl: &FnDecl,
14411430
body: &Block,
14421431
contract: Option<&FnContract>,
1443-
constness: Const,
14441432
) -> hir::BodyId {
1445-
self.lower_fn_body(decl, contract, constness, |this| this.lower_block_expr(body))
1433+
self.lower_fn_body(decl, contract, |this| this.lower_block_expr(body))
14461434
}
14471435

14481436
pub(super) fn lower_const_body(&mut self, span: Span, expr: Option<&Expr>) -> hir::BodyId {
14491437
self.lower_body(|this| {
14501438
(
14511439
&[],
14521440
match expr {
1453-
Some(expr) => {
1454-
this.is_in_const_context = true;
1455-
this.lower_expr_mut(expr)
1456-
}
1441+
Some(expr) => this.lower_expr_mut(expr),
14571442
None => this.expr_err(span, this.dcx().span_delayed_bug(span, "no block")),
14581443
},
14591444
)
@@ -1472,13 +1457,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
14721457
body: Option<&Block>,
14731458
attrs: &'hir [hir::Attribute],
14741459
contract: Option<&FnContract>,
1475-
constness: Const,
14761460
) -> hir::BodyId {
14771461
let Some(body) = body else {
14781462
// Functions without a body are an error, except if this is an intrinsic. For those we
14791463
// create a fake body so that the entire rest of the compiler doesn't have to deal with
14801464
// this as a special case.
1481-
return self.lower_fn_body(decl, contract, constness, |this| {
1465+
return self.lower_fn_body(decl, contract, |this| {
14821466
if find_attr!(attrs, RustcIntrinsic) || this.tcx.is_sdylib_interface_build() {
14831467
let span = this.lower_span(span);
14841468
let empty_block = hir::Block {
@@ -1503,7 +1487,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
15031487
};
15041488
let Some(coroutine_kind) = coroutine_kind else {
15051489
// Typical case: not a coroutine.
1506-
return self.lower_fn_body_block(decl, body, contract, constness);
1490+
return self.lower_fn_body_block(decl, body, contract);
15071491
};
15081492
// FIXME(contracts): Support contracts on async fn.
15091493
self.lower_body(|this| {

compiler/rustc_ast_lowering/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@ struct LoweringContext<'a, 'hir> {
124124
loop_scope: Option<HirId>,
125125
is_in_loop_condition: bool,
126126
is_in_dyn_type: bool,
127-
is_in_const_context: bool,
128127

129128
current_hir_id_owner: hir::OwnerId,
130129
item_local_id_counter: hir::ItemLocalId,
@@ -193,7 +192,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
193192
loop_scope: None,
194193
is_in_loop_condition: false,
195194
is_in_dyn_type: false,
196-
is_in_const_context: false,
197195
coroutine_kind: None,
198196
task_context: None,
199197
current_item: None,

compiler/rustc_const_eval/src/const_eval/fn_queries.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Constness {
1616
// Foreign functions cannot be evaluated at compile-time.
1717
Constness::NotConst
1818
}
19-
Node::Expr(e) if let ExprKind::Closure(c) = e.kind => c.constness,
19+
Node::Expr(e) if let ExprKind::Closure(c) = e.kind => {
20+
if let Constness::Const = c.constness && tcx.hir_body_const_context(tcx.local_parent(def_id)).is_none() {
21+
tcx.dcx().span_err(tcx.def_span(def_id), "cannot use `const` closures outside of const contexts");
22+
return Constness::NotConst;
23+
}
24+
c.constness
25+
},
2026
// FIXME(fee1-dead): extract this one out and rename this query to `fn_constness` so we don't need `is_const_fn` anymore.
2127
Node::Item(i) if let ItemKind::Impl(impl_) = i.kind => impl_.constness,
2228
Node::Item(Item { kind: ItemKind::Fn { sig, .. }, .. }) => sig.header.constness,

compiler/rustc_middle/src/hir/map.rs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -319,17 +319,7 @@ impl<'tcx> TyCtxt<'tcx> {
319319
BodyOwnerKind::Fn if self.is_constructor(def_id) => return None,
320320
// Const closures use their parent's const context
321321
BodyOwnerKind::Closure if self.is_const_fn(def_id) => {
322-
return Some(
323-
self.hir_body_const_context(self.local_parent(local_def_id)).unwrap_or_else(
324-
|| {
325-
assert!(
326-
self.dcx().has_errors().is_some(),
327-
"`const` closure with no enclosing const context",
328-
);
329-
ConstContext::ConstFn
330-
},
331-
),
332-
);
322+
return self.hir_body_const_context(self.local_parent(local_def_id));
333323
}
334324
BodyOwnerKind::Fn if self.is_const_fn(def_id) => ConstContext::ConstFn,
335325
BodyOwnerKind::Fn | BodyOwnerKind::Closure | BodyOwnerKind::GlobalAsm => return None,

tests/ui/consts/const-closure-in-trait-impl.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//@ check-pass
2+
13
#![feature(const_closures, const_destruct, const_trait_impl)]
24

35
use std::marker::Destruct;
@@ -17,8 +19,6 @@ impl const T for S {
1719

1820
fn b(&mut self) {
1921
self.a(const || {});
20-
//~^ ERROR: cannot use `const` closures outside of const contexts
21-
//~| ERROR: [const] Fn()` is not satisfied
2222
}
2323
}
2424

tests/ui/consts/const-closure-in-trait-impl.stderr

Lines changed: 0 additions & 23 deletions
This file was deleted.

tests/ui/traits/const-traits/const-closure-in-non-const-trait-impl-method.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ impl<T: Foo> Foo for &mut T {
1111
const fn test() -> impl [const] Fn() {
1212
//~^ ERROR functions in trait impls cannot be declared const
1313
const move || {}
14+
//~^ ERROR: cannot use `const` closures outside of const contexts
1415
}
1516
}
1617

tests/ui/traits/const-traits/const-closure-in-non-const-trait-impl-method.stderr

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ help: ... and declare the impl to be const instead
1414
LL | impl<T: Foo> const Foo for &mut T {
1515
| +++++
1616

17-
error: aborting due to 1 previous error
17+
error: cannot use `const` closures outside of const contexts
18+
--> $DIR/const-closure-in-non-const-trait-impl-method.rs:13:9
19+
|
20+
LL | const move || {}
21+
| ^^^^^^^^^^^^^
22+
23+
error: aborting due to 2 previous errors
1824

1925
For more information about this error, try `rustc --explain E0379`.

tests/ui/traits/const-traits/const-closure-in-non-const-trait-method.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ trait Tr {
66
const fn test() {
77
//~^ ERROR functions in traits cannot be declared const
88
(const || {})()
9+
//~^ ERROR cannot use `const` closures outside of const contexts
910
}
1011
}
1112

0 commit comments

Comments
 (0)