Skip to content

Commit cb0e670

Browse files
SamChou19815meta-codesync[bot]
authored andcommitted
[flow][oxidation] The big refactor: Pass context by reference instead of capturing it
Summary: After several weeks of persistence, the big refactor is finally ready: we now switch from a system of capturing reference-counted Rc<context> to passing context by reference. ## Motivation The current system of duping context and capturing them in lazy blocks is a major reason why we need to manually break these cyclic data structures to prevent memory leak. The cycles can form easily: context is a god object with interior mutability, and it's captured by lazy types that are themselves stored in the context. Even if we can live with the manual and error-prone cycle breaking, it's still bad: we can only break it at the end of checking a file, so a lot of stuff are kept alive unnecessarily. ## The refactor The key to unblock this is to introduce our custom Lazy type. The builtin Lazy type can be forced by just deref, and forcing it requires Context, which means that Context must be captured. Instead, we introduce a custom Lazy type that takes an argument to force, which we make it be the Context reference. Almost everything else is boring refactor ``` # using ocaml-like syntax here for brevity # from lazy ( do_some_check(cx, a, b, c) ) # to lazy ((cx) => do_some_check(cx, a, b, c)) ``` Of course, it's rust, so welcome to lifetime hell. Fortunately the AI has grind through everything. We mostly end up with a `'cx` lifetime everywhere. I think it's relatively easy to comprehend: one context created during `make_cx` higher up in the checker orchestrator, shared everywhere by reference ## Other cotable changes One notable interesting change is `dst_cx`. In both ocaml and the current rust code before this diff, it's a global (thread-local in rust, conceptually the same). It won't fly with this refactor. Therefore, this diff now stores it into the context that needs to interact with it. Conceptually, it's still the same singleton, since we are only checking one file at a time. Changelog: [internal] Reviewed By: panagosg7 Differential Revision: D97817769 fbshipit-source-id: eaa23d143788a763a9a6498381de0ce8151734fc
1 parent 807b1f4 commit cb0e670

File tree

112 files changed

+12843
-7529
lines changed

Some content is hidden

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

112 files changed

+12843
-7529
lines changed

rust_port/crates/flow_cli/src/env_builder_debug_command.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ fn print_graph(graph: Vec<(String, String)>) {
8080
println!("}}");
8181
}
8282

83-
fn autocomplete_hooks() -> AutocompleteHooks<ALoc> {
83+
fn autocomplete_hooks() -> AutocompleteHooks<'static, ALoc> {
8484
AutocompleteHooks {
8585
id_hook: Box::new(|_, _| false),
8686
literal_hook: Box::new(|_| false),

rust_port/crates/flow_common/src/hint.rs

Lines changed: 99 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -34,29 +34,29 @@ pub enum HintKind {
3434
// and, further, use `number` as the type of `x`.
3535

3636
#[derive(Clone)]
37-
pub struct FunCallImplicitInstantiationHints<T, TArgs, Args, PropsAndChildren> {
37+
pub struct FunCallImplicitInstantiationHints<'a, T, TArgs, Args, PropsAndChildren> {
3838
pub reason: Reason,
3939
pub return_hints: Rc<
4040
LazyCell<
41-
Vec<Hint<T, TArgs, Args, PropsAndChildren>>,
42-
Box<dyn Fn() -> Vec<Hint<T, TArgs, Args, PropsAndChildren>>>,
41+
Vec<Hint<'a, T, TArgs, Args, PropsAndChildren>>,
42+
Box<dyn Fn() -> Vec<Hint<'a, T, TArgs, Args, PropsAndChildren>> + 'a>,
4343
>,
4444
>,
45-
pub targs: Rc<LazyCell<TArgs, Box<dyn Fn() -> TArgs>>>,
46-
pub arg_list: Rc<LazyCell<Args, Box<dyn Fn() -> Args>>>,
45+
pub targs: Rc<LazyCell<TArgs, Box<dyn Fn() -> TArgs + 'a>>>,
46+
pub arg_list: Rc<LazyCell<Args, Box<dyn Fn() -> Args + 'a>>>,
4747
pub arg_index: i32,
4848
}
4949

5050
#[derive(Clone)]
51-
pub struct JsxImplicitInstantiationHints<T, TArgs, Args, PropsAndChildren> {
51+
pub struct JsxImplicitInstantiationHints<'a, T, TArgs, Args, PropsAndChildren> {
5252
pub jsx_reason: Reason,
5353
pub jsx_name: FlowSmolStr,
54-
pub jsx_targs: Rc<LazyCell<TArgs, Box<dyn Fn() -> TArgs>>>,
54+
pub jsx_targs: Rc<LazyCell<TArgs, Box<dyn Fn() -> TArgs + 'a>>>,
5555
pub jsx_props_and_children: PropsAndChildren,
5656
pub jsx_hints: Rc<
5757
LazyCell<
58-
Vec<Hint<T, TArgs, Args, PropsAndChildren>>,
59-
Box<dyn Fn() -> Vec<Hint<T, TArgs, Args, PropsAndChildren>>>,
58+
Vec<Hint<'a, T, TArgs, Args, PropsAndChildren>>,
59+
Box<dyn Fn() -> Vec<Hint<'a, T, TArgs, Args, PropsAndChildren>> + 'a>,
6060
>,
6161
>,
6262
}
@@ -76,11 +76,12 @@ pub enum PredicateKind {
7676
}
7777

7878
#[derive(Clone)]
79-
pub struct HintDecomposition<T, TArgs, Args, PropsAndChildren>(
80-
Rc<HintDecompositionInner<T, TArgs, Args, PropsAndChildren>>,
79+
pub struct HintDecomposition<'a, T, TArgs, Args, PropsAndChildren>(
80+
Rc<HintDecompositionInner<'a, T, TArgs, Args, PropsAndChildren>>,
8181
);
8282

83-
impl<T, TArgs, Args, PropsAndChildren> Dupe for HintDecomposition<T, TArgs, Args, PropsAndChildren>
83+
impl<'a, T, TArgs, Args, PropsAndChildren> Dupe
84+
for HintDecomposition<'a, T, TArgs, Args, PropsAndChildren>
8485
where
8586
T: Clone,
8687
TArgs: Clone,
@@ -89,12 +90,12 @@ where
8990
{
9091
}
9192

92-
impl<T, TArgs, Args, PropsAndChildren> HintDecomposition<T, TArgs, Args, PropsAndChildren> {
93-
pub fn new(inner: HintDecompositionInner<T, TArgs, Args, PropsAndChildren>) -> Self {
93+
impl<'a, T, TArgs, Args, PropsAndChildren> HintDecomposition<'a, T, TArgs, Args, PropsAndChildren> {
94+
pub fn new(inner: HintDecompositionInner<'a, T, TArgs, Args, PropsAndChildren>) -> Self {
9495
HintDecomposition(Rc::new(inner))
9596
}
9697

97-
pub fn inner(&self) -> &HintDecompositionInner<T, TArgs, Args, PropsAndChildren> {
98+
pub fn inner(&self) -> &HintDecompositionInner<'a, T, TArgs, Args, PropsAndChildren> {
9899
&self.0
99100
}
100101

@@ -152,22 +153,26 @@ impl<T, TArgs, Args, PropsAndChildren> HintDecomposition<T, TArgs, Args, PropsAn
152153
}
153154
}
154155

155-
pub fn map<T2, TArgs2, Args2, PropsAndChildren2>(
156+
pub fn map<'b, CX: Dupe + 'b, T2, TArgs2, Args2, PropsAndChildren2>(
156157
&self,
157-
map_base_hint: &Rc<impl Fn(T) -> T2 + 'static>,
158-
map_targs: &Rc<impl Fn(TArgs) -> TArgs2 + 'static>,
159-
map_arg_list: &Rc<impl Fn(Args) -> Args2 + 'static>,
160-
map_jsx: &Rc<impl Fn(Reason, FlowSmolStr, PropsAndChildren) -> PropsAndChildren2 + 'static>,
161-
) -> HintDecomposition<T2, TArgs2, Args2, PropsAndChildren2>
158+
cx: &CX,
159+
map_base_hint: &Rc<impl for<'c> Fn(&'c CX, T) -> T2 + 'b>,
160+
map_targs: &Rc<impl for<'c> Fn(&'c CX, TArgs) -> TArgs2 + 'b>,
161+
map_arg_list: &Rc<impl for<'c> Fn(&'c CX, Args) -> Args2 + 'b>,
162+
map_jsx: &Rc<
163+
impl for<'c> Fn(&'c CX, Reason, FlowSmolStr, PropsAndChildren) -> PropsAndChildren2 + 'b,
164+
>,
165+
) -> HintDecomposition<'b, T2, TArgs2, Args2, PropsAndChildren2>
162166
where
163-
T: Clone + 'static,
164-
TArgs: Clone + 'static,
165-
Args: Clone + 'static,
166-
PropsAndChildren: Clone + 'static,
167-
T2: Clone + 'static,
168-
TArgs2: Clone + 'static,
169-
Args2: Clone + 'static,
170-
PropsAndChildren2: Clone + 'static,
167+
'a: 'b,
168+
T: Clone + 'a,
169+
TArgs: Clone + 'a,
170+
Args: Clone + 'a,
171+
PropsAndChildren: Clone + 'a,
172+
T2: Clone + 'b,
173+
TArgs2: Clone + 'b,
174+
Args2: Clone + 'b,
175+
PropsAndChildren2: Clone + 'b,
171176
{
172177
use HintDecompositionInner::*;
173178
let inner = match self.inner() {
@@ -205,8 +210,10 @@ impl<T, TArgs, Args, PropsAndChildren> HintDecomposition<T, TArgs, Args, PropsAn
205210
arg_index,
206211
} = hints;
207212

213+
// IMPORTANT: These must remain LAZY — only map when accessed.
208214
let return_hints_mapped = Rc::new(LazyCell::new(Box::new({
209215
let return_hints = return_hints.dupe();
216+
let cx = cx.dupe();
210217
let map_base_hint = map_base_hint.dupe();
211218
let map_targs = map_targs.dupe();
212219
let map_arg_list = map_arg_list.dupe();
@@ -215,25 +222,32 @@ impl<T, TArgs, Args, PropsAndChildren> HintDecomposition<T, TArgs, Args, PropsAn
215222
LazyCell::force(&return_hints)
216223
.iter()
217224
.map(|h| {
218-
h.clone()
219-
.map(&map_base_hint, &map_targs, &map_arg_list, &map_jsx)
225+
h.clone().map(
226+
&cx,
227+
&map_base_hint,
228+
&map_targs,
229+
&map_arg_list,
230+
&map_jsx,
231+
)
220232
})
221233
.collect()
222234
}
223235
})
224-
as Box<dyn Fn() -> Vec<Hint<T2, TArgs2, Args2, PropsAndChildren2>>>));
236+
as Box<dyn Fn() -> Vec<Hint<'b, T2, TArgs2, Args2, PropsAndChildren2>> + 'b>));
225237
let targs_mapped = Rc::new(LazyCell::new(Box::new({
226238
let targs = targs.dupe();
239+
let cx = cx.dupe();
227240
let map_targs = map_targs.dupe();
228-
move || (map_targs.as_ref())(LazyCell::force(&targs).clone())
241+
move || (map_targs.as_ref())(&cx, LazyCell::force(&targs).clone())
229242
})
230-
as Box<dyn Fn() -> TArgs2>));
243+
as Box<dyn Fn() -> TArgs2 + 'b>));
231244
let arg_list_mapped = Rc::new(LazyCell::new(Box::new({
232245
let arg_list = arg_list.dupe();
246+
let cx = cx.dupe();
233247
let map_arg_list = map_arg_list.dupe();
234-
move || (map_arg_list.as_ref())(LazyCell::force(&arg_list).clone())
248+
move || (map_arg_list.as_ref())(&cx, LazyCell::force(&arg_list).clone())
235249
})
236-
as Box<dyn Fn() -> Args2>));
250+
as Box<dyn Fn() -> Args2 + 'b>));
237251

238252
InstantiateCallee(FunCallImplicitInstantiationHints {
239253
reason: reason.dupe(),
@@ -254,19 +268,22 @@ impl<T, TArgs, Args, PropsAndChildren> HintDecomposition<T, TArgs, Args, PropsAn
254268

255269
let jsx_targs_mapped = Rc::new(LazyCell::new(Box::new({
256270
let jsx_targs = jsx_targs.dupe();
271+
let cx = cx.dupe();
257272
let map_targs = map_targs.dupe();
258-
move || (map_targs.as_ref())(LazyCell::force(&jsx_targs).clone())
273+
move || (map_targs.as_ref())(&cx, LazyCell::force(&jsx_targs).clone())
259274
})
260-
as Box<dyn Fn() -> TArgs2>));
275+
as Box<dyn Fn() -> TArgs2 + 'b>));
261276

262277
let jsx_props_and_children_mapped = (map_jsx.as_ref())(
278+
cx,
263279
jsx_reason.dupe(),
264280
jsx_name.dupe(),
265281
jsx_props_and_children.clone(),
266282
);
267283

268284
let jsx_hints_mapped = Rc::new(LazyCell::new(Box::new({
269285
let jsx_hints = jsx_hints.dupe();
286+
let cx = cx.dupe();
270287
let map_base_hint = map_base_hint.dupe();
271288
let map_targs = map_targs.dupe();
272289
let map_arg_list = map_arg_list.dupe();
@@ -275,13 +292,18 @@ impl<T, TArgs, Args, PropsAndChildren> HintDecomposition<T, TArgs, Args, PropsAn
275292
LazyCell::force(&jsx_hints)
276293
.iter()
277294
.map(|h| {
278-
h.clone()
279-
.map(&map_base_hint, &map_targs, &map_arg_list, &map_jsx)
295+
h.clone().map(
296+
&cx,
297+
&map_base_hint,
298+
&map_targs,
299+
&map_arg_list,
300+
&map_jsx,
301+
)
280302
})
281303
.collect()
282304
}
283305
})
284-
as Box<dyn Fn() -> Vec<Hint<T2, TArgs2, Args2, PropsAndChildren2>>>));
306+
as Box<dyn Fn() -> Vec<Hint<'b, T2, TArgs2, Args2, PropsAndChildren2>> + 'b>));
285307

286308
InstantiateComponent(JsxImplicitInstantiationHints {
287309
jsx_reason: jsx_reason.dupe(),
@@ -297,7 +319,7 @@ impl<T, TArgs, Args, PropsAndChildren> HintDecomposition<T, TArgs, Args, PropsAn
297319
}
298320
}
299321

300-
pub enum HintDecompositionInner<T, TArgs, Args, PropsAndChildren> {
322+
pub enum HintDecompositionInner<'a, T, TArgs, Args, PropsAndChildren> {
301323
/// Hint on `{ f: e }` becomes hint on `e`
302324
DecompObjProp(FlowSmolStr),
303325
/// Hint on `{ [k]: e }` becomes hint on `e`
@@ -343,19 +365,22 @@ pub enum HintDecompositionInner<T, TArgs, Args, PropsAndChildren> {
343365
SimplifyCallee(Reason),
344366
/// Type of f in f(...) is instantiated with arguments and return hint.
345367
/// Returns f if the type of f is not polymorphic.
346-
InstantiateCallee(FunCallImplicitInstantiationHints<T, TArgs, Args, PropsAndChildren>),
368+
InstantiateCallee(FunCallImplicitInstantiationHints<'a, T, TArgs, Args, PropsAndChildren>),
347369
/// Type of Comp in <Comp ... /> is instantiated with props and children.
348370
/// Returns Comp if the type of Comp is not polymorphic.
349-
InstantiateComponent(JsxImplicitInstantiationHints<T, TArgs, Args, PropsAndChildren>),
371+
InstantiateComponent(JsxImplicitInstantiationHints<'a, T, TArgs, Args, PropsAndChildren>),
350372
/// T of Promise<T> becomes hint on return in async scope
351373
DecompPromise,
352374
}
353375

354376
#[derive(Clone)]
355-
pub enum Hint<T, TArgs, Args, PropsAndChildren> {
377+
pub enum Hint<'a, T, TArgs, Args, PropsAndChildren> {
356378
HintT(T, HintKind),
357379
HintDecomp(
358-
Vec1<(usize, HintDecomposition<T, TArgs, Args, PropsAndChildren>)>,
380+
Vec1<(
381+
usize,
382+
HintDecomposition<'a, T, TArgs, Args, PropsAndChildren>,
383+
)>,
359384
T,
360385
HintKind,
361386
),
@@ -364,9 +389,9 @@ pub enum Hint<T, TArgs, Args, PropsAndChildren> {
364389
HintPlaceholder,
365390
}
366391

367-
impl<T, TArgs, Args, PropsAndChildren> Hint<T, TArgs, Args, PropsAndChildren> {
392+
impl<'a, T, TArgs, Args, PropsAndChildren> Hint<'a, T, TArgs, Args, PropsAndChildren> {
368393
pub fn decompose(
369-
decomp: HintDecomposition<T, TArgs, Args, PropsAndChildren>,
394+
decomp: HintDecomposition<'a, T, TArgs, Args, PropsAndChildren>,
370395
hints: Vec<Self>,
371396
) -> Vec<Self>
372397
where
@@ -413,33 +438,42 @@ impl<T, TArgs, Args, PropsAndChildren> Hint<T, TArgs, Args, PropsAndChildren> {
413438
}
414439
}
415440

416-
pub fn map<T2, TArgs2, Args2, PropsAndChildren2>(
441+
pub fn map<'b, CX: Dupe + 'b, T2, TArgs2, Args2, PropsAndChildren2>(
417442
self,
418-
map_base_hint: &Rc<impl Fn(T) -> T2 + 'static>,
419-
map_targs: &Rc<impl Fn(TArgs) -> TArgs2 + 'static>,
420-
map_arg_list: &Rc<impl Fn(Args) -> Args2 + 'static>,
421-
map_jsx: &Rc<impl Fn(Reason, FlowSmolStr, PropsAndChildren) -> PropsAndChildren2 + 'static>,
422-
) -> Hint<T2, TArgs2, Args2, PropsAndChildren2>
443+
cx: &CX,
444+
map_base_hint: &Rc<impl for<'c> Fn(&'c CX, T) -> T2 + 'b>,
445+
map_targs: &Rc<impl for<'c> Fn(&'c CX, TArgs) -> TArgs2 + 'b>,
446+
map_arg_list: &Rc<impl for<'c> Fn(&'c CX, Args) -> Args2 + 'b>,
447+
map_jsx: &Rc<
448+
impl for<'c> Fn(&'c CX, Reason, FlowSmolStr, PropsAndChildren) -> PropsAndChildren2 + 'b,
449+
>,
450+
) -> Hint<'b, T2, TArgs2, Args2, PropsAndChildren2>
423451
where
424-
T: Clone + 'static,
425-
TArgs: Clone + 'static,
426-
Args: Clone + 'static,
427-
PropsAndChildren: Clone + 'static,
428-
T2: Clone + 'static,
429-
TArgs2: Clone + 'static,
430-
Args2: Clone + 'static,
431-
PropsAndChildren2: Clone + 'static,
452+
'a: 'b,
453+
T: Clone + 'a,
454+
TArgs: Clone + 'a,
455+
Args: Clone + 'a,
456+
PropsAndChildren: Clone + 'a,
457+
T2: Clone + 'b,
458+
TArgs2: Clone + 'b,
459+
Args2: Clone + 'b,
460+
PropsAndChildren2: Clone + 'b,
432461
{
433462
match self {
434-
Hint::HintT(t, kind) => Hint::HintT((map_base_hint.as_ref())(t), kind),
463+
Hint::HintT(t, kind) => Hint::HintT((map_base_hint.as_ref())(cx, t), kind),
435464
Hint::HintDecomp(ops, t, kind) => {
436465
let mapped_ops = ops
437466
.into_iter()
438-
.map(|(i, op)| (i, op.map(map_base_hint, map_targs, map_arg_list, map_jsx)))
467+
.map(|(i, op)| {
468+
(
469+
i,
470+
op.map(cx, map_base_hint, map_targs, map_arg_list, map_jsx),
471+
)
472+
})
439473
.collect::<Vec<_>>();
440474
Hint::HintDecomp(
441475
Vec1::try_from_vec(mapped_ops).unwrap(),
442-
(map_base_hint.as_ref())(t),
476+
(map_base_hint.as_ref())(cx, t),
443477
kind,
444478
)
445479
}

rust_port/crates/flow_env_builder/src/env_api.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,10 @@ impl DefLocType {
9898
}
9999
}
100100

101-
pub struct AutocompleteHooks<L> {
102-
pub id_hook: Box<dyn Fn(&str, &L) -> bool>,
103-
pub literal_hook: Box<dyn Fn(&L) -> bool>,
104-
pub obj_prop_decl_hook: Box<dyn Fn(&str, &L) -> bool>,
101+
pub struct AutocompleteHooks<'a, L> {
102+
pub id_hook: Box<dyn Fn(&str, &L) -> bool + 'a>,
103+
pub literal_hook: Box<dyn Fn(&L) -> bool + 'a>,
104+
pub obj_prop_decl_hook: Box<dyn Fn(&str, &L) -> bool + 'a>,
105105
}
106106

107107
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]

0 commit comments

Comments
 (0)