Skip to content

Commit ae91b21

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 d173b30 commit ae91b21

12 files changed

Lines changed: 33 additions & 70 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 & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use std::mem;
21

32
use rustc_abi::ExternAbi;
43
use rustc_ast::visit::AssocCtxt;
@@ -378,7 +377,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
378377
body.as_deref(),
379378
attrs,
380379
contract.as_deref(),
381-
header.constness,
382380
);
383381

384382
let itctx = ImplTraitContext::Universal;
@@ -1068,7 +1066,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
10681066
Some(body),
10691067
attrs,
10701068
contract.as_deref(),
1071-
sig.header.constness,
10721069
);
10731070
let (generics, sig) = self.lower_method_sig(
10741071
generics,
@@ -1262,7 +1259,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
12621259
body.as_deref(),
12631260
attrs,
12641261
contract.as_deref(),
1265-
sig.header.constness,
12661262
);
12671263
let (generics, sig) = self.lower_method_sig(
12681264
generics,
@@ -1391,13 +1387,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
13911387
f: impl FnOnce(&mut Self) -> (&'hir [hir::Param<'hir>], hir::Expr<'hir>),
13921388
) -> hir::BodyId {
13931389
let prev_coroutine_kind = self.coroutine_kind.take();
1394-
let prev_is_in_const_context = mem::take(&mut self.is_in_const_context);
13951390
let task_context = self.task_context.take();
13961391
let (parameters, result) = f(self);
13971392
let body_id = self.record_body(parameters, result);
13981393
self.task_context = task_context;
13991394
self.coroutine_kind = prev_coroutine_kind;
1400-
self.is_in_const_context = prev_is_in_const_context;
14011395
body_id
14021396
}
14031397

@@ -1416,13 +1410,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
14161410
&mut self,
14171411
decl: &FnDecl,
14181412
contract: Option<&FnContract>,
1419-
constness: Const,
14201413
body: impl FnOnce(&mut Self) -> hir::Expr<'hir>,
14211414
) -> hir::BodyId {
14221415
self.lower_body(|this| {
1423-
if let Const::Yes(_) = constness {
1424-
this.is_in_const_context = true;
1425-
}
14261416
let params =
14271417
this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x)));
14281418

@@ -1440,20 +1430,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
14401430
decl: &FnDecl,
14411431
body: &Block,
14421432
contract: Option<&FnContract>,
1443-
constness: Const,
14441433
) -> hir::BodyId {
1445-
self.lower_fn_body(decl, contract, constness, |this| this.lower_block_expr(body))
1434+
self.lower_fn_body(decl, contract, |this| this.lower_block_expr(body))
14461435
}
14471436

14481437
pub(super) fn lower_const_body(&mut self, span: Span, expr: Option<&Expr>) -> hir::BodyId {
14491438
self.lower_body(|this| {
14501439
(
14511440
&[],
14521441
match expr {
1453-
Some(expr) => {
1454-
this.is_in_const_context = true;
1455-
this.lower_expr_mut(expr)
1456-
}
1442+
Some(expr) => this.lower_expr_mut(expr),
14571443
None => this.expr_err(span, this.dcx().span_delayed_bug(span, "no block")),
14581444
},
14591445
)
@@ -1472,13 +1458,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
14721458
body: Option<&Block>,
14731459
attrs: &'hir [hir::Attribute],
14741460
contract: Option<&FnContract>,
1475-
constness: Const,
14761461
) -> hir::BodyId {
14771462
let Some(body) = body else {
14781463
// Functions without a body are an error, except if this is an intrinsic. For those we
14791464
// create a fake body so that the entire rest of the compiler doesn't have to deal with
14801465
// this as a special case.
1481-
return self.lower_fn_body(decl, contract, constness, |this| {
1466+
return self.lower_fn_body(decl, contract, |this| {
14821467
if find_attr!(attrs, RustcIntrinsic) || this.tcx.is_sdylib_interface_build() {
14831468
let span = this.lower_span(span);
14841469
let empty_block = hir::Block {
@@ -1503,7 +1488,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
15031488
};
15041489
let Some(coroutine_kind) = coroutine_kind else {
15051490
// Typical case: not a coroutine.
1506-
return self.lower_fn_body_block(decl, body, contract, constness);
1491+
return self.lower_fn_body_block(decl, body, contract);
15071492
};
15081493
// FIXME(contracts): Support contracts on async fn.
15091494
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)