mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Rollup merge of #155772 - oli-obk:const-closures-everywhere, r=fee1-dead
Check closure's constness validity in the constness query fixes rust-lang/rust#155584 instead of checking during ast lowering, where it's not easily possible to obtain all the right information in time. While lowering an assoc item we don't know if the parent was a const trait or a const impl. Tracing this information is quite annoying, and complicates a lot of code, which checking here after the fact is trivial.
This commit is contained in:
@@ -1052,7 +1052,7 @@ fn lower_expr_closure(
|
||||
binder: &ClosureBinder,
|
||||
capture_clause: CaptureBy,
|
||||
closure_id: NodeId,
|
||||
mut constness: Const,
|
||||
constness: Const,
|
||||
movability: Movability,
|
||||
decl: &FnDecl,
|
||||
body: &Expr,
|
||||
@@ -1062,18 +1062,11 @@ 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, 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;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::mem;
|
||||
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_ast::visit::AssocCtxt;
|
||||
use rustc_ast::*;
|
||||
@@ -378,7 +376,6 @@ fn lower_item_kind(
|
||||
body.as_deref(),
|
||||
attrs,
|
||||
contract.as_deref(),
|
||||
header.constness,
|
||||
);
|
||||
|
||||
let itctx = ImplTraitContext::Universal;
|
||||
@@ -1068,7 +1065,6 @@ 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,
|
||||
@@ -1262,7 +1258,6 @@ fn lower_impl_item(
|
||||
body.as_deref(),
|
||||
attrs,
|
||||
contract.as_deref(),
|
||||
sig.header.constness,
|
||||
);
|
||||
let (generics, sig) = self.lower_method_sig(
|
||||
generics,
|
||||
@@ -1391,13 +1386,11 @@ 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
|
||||
}
|
||||
|
||||
@@ -1416,13 +1409,9 @@ 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)));
|
||||
|
||||
@@ -1440,9 +1429,8 @@ fn lower_fn_body_block(
|
||||
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 +1438,7 @@ pub(super) fn lower_const_body(&mut self, span: Span, expr: Option<&Expr>) -> hi
|
||||
(
|
||||
&[],
|
||||
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 +1457,12 @@ 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, 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 +1487,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, constness);
|
||||
return self.lower_fn_body_block(decl, body, contract);
|
||||
};
|
||||
// FIXME(contracts): Support contracts on async fn.
|
||||
self.lower_body(|this| {
|
||||
|
||||
@@ -124,7 +124,6 @@ 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,
|
||||
@@ -193,7 +192,6 @@ fn new(tcx: TyCtxt<'hir>, resolver: &'a ResolverAstLowering<'hir>) -> Self {
|
||||
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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -319,17 +319,7 @@ 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 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,
|
||||
|
||||
@@ -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() {}
|
||||
@@ -11,6 +11,7 @@ impl<T: Foo> 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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,12 @@ 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
|
||||
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`.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user