Skip to content

Commit 59b6f2d

Browse files
committed
Compute closure captures
1 parent 51d5862 commit 59b6f2d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+2536
-432
lines changed

crates/hir-def/src/body/lower.rs

Lines changed: 78 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ use crate::{
2828
data::adt::StructKind,
2929
db::DefDatabase,
3030
hir::{
31-
dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, ClosureKind, Expr, ExprId,
32-
Label, LabelId, Literal, MatchArm, Movability, Pat, PatId, RecordFieldPat, RecordLitField,
33-
Statement,
31+
dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Expr,
32+
ExprId, Label, LabelId, Literal, MatchArm, Movability, Pat, PatId, RecordFieldPat,
33+
RecordLitField, Statement,
3434
},
3535
item_scope::BuiltinShadowMode,
3636
lang_item::LangItem,
@@ -67,6 +67,7 @@ pub(super) fn lower(
6767
is_lowering_assignee_expr: false,
6868
is_lowering_generator: false,
6969
label_ribs: Vec::new(),
70+
current_binding_owner: None,
7071
}
7172
.collect(params, body, is_async_fn)
7273
}
@@ -92,6 +93,7 @@ struct ExprCollector<'a> {
9293

9394
// resolution
9495
label_ribs: Vec<LabelRib>,
96+
current_binding_owner: Option<ExprId>,
9597
}
9698

9799
#[derive(Clone, Debug)]
@@ -261,11 +263,16 @@ impl ExprCollector<'_> {
261263
}
262264
Some(ast::BlockModifier::Const(_)) => {
263265
self.with_label_rib(RibKind::Constant, |this| {
264-
this.collect_block_(e, |id, statements, tail| Expr::Const {
265-
id,
266-
statements,
267-
tail,
268-
})
266+
this.collect_as_a_binding_owner_bad(
267+
|this| {
268+
this.collect_block_(e, |id, statements, tail| Expr::Const {
269+
id,
270+
statements,
271+
tail,
272+
})
273+
},
274+
syntax_ptr,
275+
)
269276
})
270277
}
271278
None => self.collect_block(e),
@@ -461,6 +468,8 @@ impl ExprCollector<'_> {
461468
}
462469
}
463470
ast::Expr::ClosureExpr(e) => self.with_label_rib(RibKind::Closure, |this| {
471+
let (result_expr_id, prev_binding_owner) =
472+
this.initialize_binding_owner(syntax_ptr);
464473
let mut args = Vec::new();
465474
let mut arg_types = Vec::new();
466475
if let Some(pl) = e.param_list() {
@@ -494,17 +503,19 @@ impl ExprCollector<'_> {
494503
ClosureKind::Closure
495504
};
496505
this.is_lowering_generator = prev_is_lowering_generator;
497-
498-
this.alloc_expr(
499-
Expr::Closure {
500-
args: args.into(),
501-
arg_types: arg_types.into(),
502-
ret_type,
503-
body,
504-
closure_kind,
505-
},
506-
syntax_ptr,
507-
)
506+
let capture_by =
507+
if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref };
508+
this.is_lowering_generator = prev_is_lowering_generator;
509+
this.current_binding_owner = prev_binding_owner;
510+
this.body.exprs[result_expr_id] = Expr::Closure {
511+
args: args.into(),
512+
arg_types: arg_types.into(),
513+
ret_type,
514+
body,
515+
closure_kind,
516+
capture_by,
517+
};
518+
result_expr_id
508519
}),
509520
ast::Expr::BinExpr(e) => {
510521
let op = e.op_kind();
@@ -545,7 +556,15 @@ impl ExprCollector<'_> {
545556
ArrayExprKind::Repeat { initializer, repeat } => {
546557
let initializer = self.collect_expr_opt(initializer);
547558
let repeat = self.with_label_rib(RibKind::Constant, |this| {
548-
this.collect_expr_opt(repeat)
559+
if let Some(repeat) = repeat {
560+
let syntax_ptr = AstPtr::new(&repeat);
561+
this.collect_as_a_binding_owner_bad(
562+
|this| this.collect_expr(repeat),
563+
syntax_ptr,
564+
)
565+
} else {
566+
this.missing_expr()
567+
}
549568
});
550569
self.alloc_expr(
551570
Expr::Array(Array::Repeat { initializer, repeat }),
@@ -592,6 +611,32 @@ impl ExprCollector<'_> {
592611
})
593612
}
594613

614+
fn initialize_binding_owner(
615+
&mut self,
616+
syntax_ptr: AstPtr<ast::Expr>,
617+
) -> (ExprId, Option<ExprId>) {
618+
let result_expr_id = self.alloc_expr(Expr::Missing, syntax_ptr);
619+
let prev_binding_owner = self.current_binding_owner.take();
620+
self.current_binding_owner = Some(result_expr_id);
621+
(result_expr_id, prev_binding_owner)
622+
}
623+
624+
/// FIXME: This function is bad. It will produce a dangling `Missing` expr which wastes memory. Currently
625+
/// it is used only for const blocks and repeat expressions, which are also hacky and ideally should have
626+
/// their own body. Don't add more usage for this function so that we can remove this function after
627+
/// separating those bodies.
628+
fn collect_as_a_binding_owner_bad(
629+
&mut self,
630+
job: impl FnOnce(&mut ExprCollector<'_>) -> ExprId,
631+
syntax_ptr: AstPtr<ast::Expr>,
632+
) -> ExprId {
633+
let (id, prev_owner) = self.initialize_binding_owner(syntax_ptr);
634+
let tmp = job(self);
635+
self.body.exprs[id] = mem::replace(&mut self.body.exprs[tmp], Expr::Missing);
636+
self.current_binding_owner = prev_owner;
637+
id
638+
}
639+
595640
/// Desugar `try { <stmts>; <expr> }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(<expr>) }`,
596641
/// `try { <stmts>; }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(()) }`
597642
/// and save the `<new_label>` to use it as a break target for desugaring of the `?` operator.
@@ -1112,8 +1157,13 @@ impl ExprCollector<'_> {
11121157
}
11131158
ast::Pat::ConstBlockPat(const_block_pat) => {
11141159
if let Some(block) = const_block_pat.block_expr() {
1115-
let expr_id =
1116-
self.with_label_rib(RibKind::Constant, |this| this.collect_block(block));
1160+
let expr_id = self.with_label_rib(RibKind::Constant, |this| {
1161+
let syntax_ptr = AstPtr::new(&block.clone().into());
1162+
this.collect_as_a_binding_owner_bad(
1163+
|this| this.collect_block(block),
1164+
syntax_ptr,
1165+
)
1166+
});
11171167
Pat::ConstBlock(expr_id)
11181168
} else {
11191169
Pat::Missing
@@ -1272,7 +1322,12 @@ impl ExprCollector<'_> {
12721322
}
12731323

12741324
fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
1275-
self.body.bindings.alloc(Binding { name, mode, definitions: SmallVec::new() })
1325+
self.body.bindings.alloc(Binding {
1326+
name,
1327+
mode,
1328+
definitions: SmallVec::new(),
1329+
owner: self.current_binding_owner,
1330+
})
12761331
}
12771332

12781333
fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {

crates/hir-def/src/body/pretty.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ use std::fmt::{self, Write};
55
use syntax::ast::HasName;
66

77
use crate::{
8-
hir::{Array, BindingAnnotation, BindingId, ClosureKind, Literal, Movability, Statement},
8+
hir::{
9+
Array, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Literal, Movability, Statement,
10+
},
911
pretty::{print_generic_args, print_path, print_type_ref},
1012
type_ref::TypeRef,
1113
};
@@ -360,7 +362,7 @@ impl<'a> Printer<'a> {
360362
self.print_expr(*index);
361363
w!(self, "]");
362364
}
363-
Expr::Closure { args, arg_types, ret_type, body, closure_kind } => {
365+
Expr::Closure { args, arg_types, ret_type, body, closure_kind, capture_by } => {
364366
match closure_kind {
365367
ClosureKind::Generator(Movability::Static) => {
366368
w!(self, "static ");
@@ -370,6 +372,12 @@ impl<'a> Printer<'a> {
370372
}
371373
_ => (),
372374
}
375+
match capture_by {
376+
CaptureBy::Value => {
377+
w!(self, "move ");
378+
}
379+
CaptureBy::Ref => (),
380+
}
373381
w!(self, "|");
374382
for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() {
375383
if i != 0 {

crates/hir-def/src/hir.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ pub enum Expr {
275275
ret_type: Option<Interned<TypeRef>>,
276276
body: ExprId,
277277
closure_kind: ClosureKind,
278+
capture_by: CaptureBy,
278279
},
279280
Tuple {
280281
exprs: Box<[ExprId]>,
@@ -292,6 +293,14 @@ pub enum ClosureKind {
292293
Async,
293294
}
294295

296+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
297+
pub enum CaptureBy {
298+
/// `move |x| y + x`.
299+
Value,
300+
/// `move` keyword was not specified.
301+
Ref,
302+
}
303+
295304
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
296305
pub enum Movability {
297306
Static,
@@ -484,6 +493,22 @@ pub struct Binding {
484493
pub name: Name,
485494
pub mode: BindingAnnotation,
486495
pub definitions: SmallVec<[PatId; 1]>,
496+
/// Id of the closure/generator that owns this binding. If it is owned by the
497+
/// top level expression, this field would be `None`.
498+
pub owner: Option<ExprId>,
499+
}
500+
501+
impl Binding {
502+
pub fn is_upvar(&self, relative_to: ExprId) -> bool {
503+
match self.owner {
504+
Some(x) => {
505+
// We assign expression ids in a way that outer closures will recieve
506+
// a lower id
507+
x.into_raw() < relative_to.into_raw()
508+
}
509+
None => true,
510+
}
511+
}
487512
}
488513

489514
#[derive(Debug, Clone, Eq, PartialEq)]

crates/hir-ty/src/chalk_ext.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ use hir_def::{
1212
use crate::{
1313
db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
1414
from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders,
15-
CallableDefId, CallableSig, DynTy, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy,
16-
QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause,
15+
CallableDefId, CallableSig, ClosureId, DynTy, FnPointer, ImplTraitId, Interner, Lifetime,
16+
ProjectionTy, QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags,
17+
WhereClause,
1718
};
1819

1920
pub trait TyExt {
@@ -28,6 +29,7 @@ pub trait TyExt {
2829
fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>;
2930
fn as_builtin(&self) -> Option<BuiltinType>;
3031
fn as_tuple(&self) -> Option<&Substitution>;
32+
fn as_closure(&self) -> Option<ClosureId>;
3133
fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>;
3234
fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>;
3335
fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)>;
@@ -128,6 +130,13 @@ impl TyExt for Ty {
128130
}
129131
}
130132

133+
fn as_closure(&self) -> Option<ClosureId> {
134+
match self.kind(Interner) {
135+
TyKind::Closure(id, _) => Some(*id),
136+
_ => None,
137+
}
138+
}
139+
131140
fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId> {
132141
match self.callable_def(db) {
133142
Some(CallableDefId::FunctionId(func)) => Some(func),

crates/hir-ty/src/consteval/tests.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,81 @@ fn try_block() {
11051105
);
11061106
}
11071107

1108+
#[test]
1109+
fn closures() {
1110+
check_number(
1111+
r#"
1112+
//- minicore: fn, copy
1113+
const GOAL: i32 = {
1114+
let y = 5;
1115+
let c = |x| x + y;
1116+
c(2)
1117+
};
1118+
"#,
1119+
7,
1120+
);
1121+
check_number(
1122+
r#"
1123+
//- minicore: fn, copy
1124+
const GOAL: i32 = {
1125+
let y = 5;
1126+
let c = |(a, b): &(i32, i32)| *a + *b + y;
1127+
c(&(2, 3))
1128+
};
1129+
"#,
1130+
10,
1131+
);
1132+
check_number(
1133+
r#"
1134+
//- minicore: fn, copy
1135+
const GOAL: i32 = {
1136+
let mut y = 5;
1137+
let c = |x| {
1138+
y = y + x;
1139+
};
1140+
c(2);
1141+
c(3);
1142+
y
1143+
};
1144+
"#,
1145+
10,
1146+
);
1147+
check_number(
1148+
r#"
1149+
//- minicore: fn, copy
1150+
struct X(i32);
1151+
impl X {
1152+
fn mult(&mut self, n: i32) {
1153+
self.0 = self.0 * n
1154+
}
1155+
}
1156+
const GOAL: i32 = {
1157+
let x = X(1);
1158+
let c = || {
1159+
x.mult(2);
1160+
|| {
1161+
x.mult(3);
1162+
|| {
1163+
|| {
1164+
x.mult(4);
1165+
|| {
1166+
x.mult(x.0);
1167+
|| {
1168+
x.0
1169+
}
1170+
}
1171+
}
1172+
}
1173+
}
1174+
};
1175+
let r = c()()()()()();
1176+
r + x.0
1177+
};
1178+
"#,
1179+
24 * 24 * 2,
1180+
);
1181+
}
1182+
11081183
#[test]
11091184
fn or_pattern() {
11101185
check_number(

crates/hir-ty/src/db.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ use crate::{
1919
consteval::ConstEvalError,
2020
method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
2121
mir::{BorrowckResult, MirBody, MirLowerError},
22-
Binders, CallableDefId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner,
23-
PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty, TyDefId,
24-
ValueTyDefId,
22+
Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult,
23+
Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty,
24+
TyDefId, ValueTyDefId,
2525
};
2626
use hir_expand::name::Name;
2727

@@ -38,8 +38,11 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
3838
#[salsa::cycle(crate::mir::mir_body_recover)]
3939
fn mir_body(&self, def: DefWithBodyId) -> Result<Arc<MirBody>, MirLowerError>;
4040

41+
#[salsa::invoke(crate::mir::mir_body_for_closure_query)]
42+
fn mir_body_for_closure(&self, def: ClosureId) -> Result<Arc<MirBody>, MirLowerError>;
43+
4144
#[salsa::invoke(crate::mir::borrowck_query)]
42-
fn borrowck(&self, def: DefWithBodyId) -> Result<Arc<BorrowckResult>, MirLowerError>;
45+
fn borrowck(&self, def: DefWithBodyId) -> Result<Arc<[BorrowckResult]>, MirLowerError>;
4346

4447
#[salsa::invoke(crate::lower::ty_query)]
4548
#[salsa::cycle(crate::lower::ty_recover)]

0 commit comments

Comments
 (0)