Skip to content

Commit a7076f2

Browse files
SamChou19815meta-codesync[bot]
authored andcommitted
[flow][oxidation][optimization] Avoid constraints bound cloning
Summary: Right now we naively clone constraints, for no good reasons other than making rust borrow checker happy. We never bothered to clean this up, but now it's time. The fix is to use RefCell. Once we eliminates more of the non-idiomatic rust at the whole system level (like D97817769), we can think about how to optimize this aspect further. Reviewed By: marcoww6 Differential Revision: D98638103 fbshipit-source-id: 0330c017ea0a58c7e1eff6e0b246d93b81e95bc1
1 parent c2a9174 commit a7076f2

File tree

10 files changed

+99
-65
lines changed

10 files changed

+99
-65
lines changed

rust_port/crates/flow_services_coverage/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ impl CoverageVisitor {
163163
}
164164
Constraints::Unresolved(bounds) => {
165165
let keys: Vec<FlowType> =
166-
bounds.lower.keys().map(|k| k.dupe()).collect();
166+
bounds.borrow().lower.keys().map(|k| k.dupe()).collect();
167167
self.types_list(cx, OpMode::OpOr, &keys)
168168
}
169169
};

rust_port/crates/flow_typing_debug/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1450,6 +1450,7 @@ fn dump_tvar_(depth: u32, tvars: &mut BTreeSet<i32>, cx: &Context, id: i32) -> S
14501450
format!("{}, FullyResolved {}", id, payload)
14511451
}
14521452
Constraints::Unresolved(bounds) => {
1453+
let bounds = bounds.borrow();
14531454
if bounds.lower.is_empty() && bounds.upper.is_empty() {
14541455
format!("{}", id)
14551456
} else {

rust_port/crates/flow_typing_flow_common/src/flow_js_utils.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ use vec1::Vec1;
5656
// bounds of the solution, or a singleton containing the solution itself.
5757
pub fn types_of<'cx>(cx: &Context<'cx>, constraints: &Constraints<'cx, Context<'cx>>) -> Vec<Type> {
5858
match constraints {
59-
Constraints::Unresolved(bounds) => bounds.lower.keys().duped().collect(),
59+
Constraints::Unresolved(bounds) => bounds.borrow().lower.keys().duped().collect(),
6060
Constraints::Resolved(t) => vec![t.dupe()],
6161
Constraints::FullyResolved(s) => vec![cx.force_fully_resolved_tvar(s)],
6262
}
@@ -67,9 +67,12 @@ pub fn uses_of<'cx>(
6767
constraints: &Constraints<'cx, Context<'cx>>,
6868
) -> Vec<UseT<Context<'cx>>> {
6969
match constraints {
70-
Constraints::Unresolved(bounds) => {
71-
bounds.upper.keys().map(|key| key.use_t.dupe()).collect()
72-
}
70+
Constraints::Unresolved(bounds) => bounds
71+
.borrow()
72+
.upper
73+
.keys()
74+
.map(|key| key.use_t.dupe())
75+
.collect(),
7376
Constraints::Resolved(t) => vec![UseT::new(UseTInner::UseT(unknown_use(), t.dupe()))],
7477
Constraints::FullyResolved(s) => {
7578
vec![UseT::new(UseTInner::UseT(
@@ -652,7 +655,8 @@ pub mod tvar_visitors {
652655
Constraints::Resolved(t) => self.type_(cx, pole, Ok(seen), &t),
653656
Constraints::Unresolved(bounds) => {
654657
let mut result = Ok(seen);
655-
for t in bounds.lower.keys() {
658+
let lower: Vec<_> = bounds.borrow().lower.keys().cloned().collect();
659+
for t in lower.iter() {
656660
result = Ok(self.type_(cx, pole, result, t)?);
657661
}
658662
result
@@ -2542,7 +2546,9 @@ pub fn mk_distributive_tparam_subst_fn<'cx>(
25422546
lowertvars: HashMap::default(),
25432547
uppertvars: HashMap::default(),
25442548
};
2545-
let node = Node::create_root(Constraints::Unresolved(Rc::new(bounds)));
2549+
let node = Node::create_root(Constraints::Unresolved(Rc::new(
2550+
std::cell::RefCell::new(bounds),
2551+
)));
25462552
cx.add_tvar(tvar_id, node);
25472553

25482554
Type::new(TypeInner::OpenT(flow_typing_type::type_::Tvar::new(

rust_port/crates/flow_typing_flow_js/src/flow_js/constraint_helpers.rs

Lines changed: 61 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
use std::collections::BTreeMap;
99
use std::collections::HashMap;
10-
use std::rc::Rc;
1110
use std::sync::Arc;
1211

1312
use super::helpers::*;
@@ -105,14 +104,15 @@ pub(super) fn add_upper<'cx>(
105104
};
106105
cx.modify_constraints(id, |_root_id, constraints| {
107106
if let constraint::Constraints::Unresolved(bounds) = constraints {
107+
let mut bounds = bounds.borrow_mut();
108108
if bounds
109109
.upper
110110
.get(&key)
111111
.is_some_and(|existing_trace| *existing_trace == trace)
112112
{
113113
return;
114114
}
115-
Rc::make_mut(bounds).upper.insert(key, trace);
115+
bounds.upper.insert(key, trace);
116116
}
117117
});
118118
}
@@ -127,12 +127,13 @@ pub(super) fn add_lower<'cx>(
127127
) {
128128
cx.modify_constraints(id, |_root_id, constraints| {
129129
if let constraint::Constraints::Unresolved(bounds) = constraints {
130+
let mut bounds = bounds.borrow_mut();
130131
if let Some((existing_trace, existing_use_op)) = bounds.lower.get(l) {
131132
if *existing_trace == trace && existing_use_op == &use_op {
132133
return;
133134
}
134135
}
135-
Rc::make_mut(bounds).lower.insert(l.dupe(), (trace, use_op));
136+
bounds.lower.insert(l.dupe(), (trace, use_op));
136137
}
137138
});
138139
}
@@ -172,13 +173,13 @@ pub(super) fn edges_to_t<'cx>(
172173
cx: &Context<'cx>,
173174
trace: DepthTrace,
174175
opt: bool,
175-
(id1, bounds1): (i32, &constraint::Bounds<Context<'cx>>),
176+
(id1, bounds1): (i32, &constraint::BoundsRef<Context<'cx>>),
176177
t2: &UseT<Context<'cx>>,
177178
) {
178179
if !opt {
179180
add_upper(cx, id1, t2, trace);
180181
}
181-
let lowertvars = bounds1.lowertvars.clone();
182+
let lowertvars = bounds1.borrow().lowertvars.clone();
182183
iter_with_filter(cx, &lowertvars, id1, |root_id, trace_l, use_op| {
183184
let t2 = flow_use_op(cx, use_op.dupe(), t2.dupe());
184185
add_upper(
@@ -201,12 +202,12 @@ pub(super) fn edges_from_t<'cx>(
201202
new_use_op: &UseOp,
202203
opt: bool,
203204
t1: &Type,
204-
(id2, bounds2): (i32, &constraint::Bounds<Context<'cx>>),
205+
(id2, bounds2): (i32, &constraint::BoundsRef<Context<'cx>>),
205206
) {
206207
if !opt {
207208
add_lower(id2, t1, trace, new_use_op.dupe(), cx);
208209
}
209-
let uppertvars = bounds2.uppertvars.clone();
210+
let uppertvars = bounds2.borrow().uppertvars.clone();
210211
iter_with_filter(cx, &uppertvars, id2, |root_id, trace_u, use_op| {
211212
let use_op = pick_use_op(cx, new_use_op, use_op);
212213
add_lower(
@@ -225,7 +226,7 @@ pub(super) fn edges_to_ts<'cx>(
225226
trace: DepthTrace,
226227
new_use_op: &UseOp,
227228
opt: bool,
228-
(id, bounds): (i32, &constraint::Bounds<Context<'cx>>),
229+
(id, bounds): (i32, &constraint::BoundsRef<Context<'cx>>),
229230
us: &BTreeMap<constraint::UseTypeKey<Context<'cx>>, DepthTrace>,
230231
) {
231232
for (use_type_key, trace_u) in us.iter() {
@@ -247,7 +248,7 @@ pub(super) fn edges_from_ts<'cx>(
247248
new_use_op: &UseOp,
248249
opt: bool,
249250
ls: &BTreeMap<Type, (DepthTrace, UseOp)>,
250-
(id, bounds): (i32, &constraint::Bounds<Context<'cx>>),
251+
(id, bounds): (i32, &constraint::BoundsRef<Context<'cx>>),
251252
) {
252253
for (l, (trace_l, use_op)) in ls.iter() {
253254
let new_use_op = pick_use_op(cx, use_op, new_use_op);
@@ -272,7 +273,7 @@ pub(super) fn edges_and_flows_to_t<'cx>(
272273
cx: &Context<'cx>,
273274
trace: DepthTrace,
274275
opt: bool,
275-
(id1, bounds1): (i32, &constraint::Bounds<Context<'cx>>),
276+
(id1, bounds1): (i32, &constraint::BoundsRef<Context<'cx>>),
276277
t2: &UseT<Context<'cx>>,
277278
) -> Result<(), FlowJsException> {
278279
// Skip iff edge exists as part of the speculation path to the current branch
@@ -281,18 +282,25 @@ pub(super) fn edges_and_flows_to_t<'cx>(
281282
state.0.iter().any(|branch| {
282283
let speculation_id = branch.speculation_id;
283284
let case_id = branch.case.case_id;
284-
bounds1.upper.contains_key(&constraint::UseTypeKey {
285-
use_t: t2.dupe(),
286-
assoc: Some((speculation_id, case_id)),
287-
})
285+
bounds1
286+
.borrow()
287+
.upper
288+
.contains_key(&constraint::UseTypeKey {
289+
use_t: t2.dupe(),
290+
assoc: Some((speculation_id, case_id)),
291+
})
288292
})
289-
} || bounds1.upper.contains_key(&constraint::UseTypeKey {
290-
use_t: t2.dupe(),
291-
assoc: None,
292-
});
293+
} || bounds1
294+
.borrow()
295+
.upper
296+
.contains_key(&constraint::UseTypeKey {
297+
use_t: t2.dupe(),
298+
assoc: None,
299+
});
293300
if !skip {
301+
let lower = bounds1.borrow().lower.clone();
294302
edges_to_t(cx, trace, opt, (id1, bounds1), t2);
295-
flows_to_t(cx, trace, &bounds1.lower, t2)?;
303+
flows_to_t(cx, trace, &lower, t2)?;
296304
}
297305
Ok(())
298306
}
@@ -309,11 +317,12 @@ pub(super) fn edges_and_flows_from_t<'cx>(
309317
new_use_op: UseOp,
310318
opt: bool,
311319
t1: &Type,
312-
(id2, bounds2): (i32, &constraint::Bounds<Context<'cx>>),
320+
(id2, bounds2): (i32, &constraint::BoundsRef<Context<'cx>>),
313321
) -> Result<(), FlowJsException> {
314-
if !bounds2.lower.contains_key(t1) {
322+
if !bounds2.borrow().lower.contains_key(t1) {
323+
let upper = bounds2.borrow().upper.clone();
315324
edges_from_t(cx, trace, &new_use_op, opt, t1, (id2, bounds2));
316-
flows_from_t(cx, trace, &new_use_op, t1, &bounds2.upper)?;
325+
flows_from_t(cx, trace, &new_use_op, t1, &upper)?;
317326
}
318327
Ok(())
319328
}
@@ -328,6 +337,7 @@ pub(super) fn add_uppertvar<'cx>(
328337
) {
329338
cx.modify_constraints(bounds_id, |_root_id, constraints| {
330339
if let constraint::Constraints::Unresolved(bounds) = constraints {
340+
let mut bounds = bounds.borrow_mut();
331341
if bounds
332342
.uppertvars
333343
.get(&id)
@@ -337,7 +347,7 @@ pub(super) fn add_uppertvar<'cx>(
337347
{
338348
return;
339349
}
340-
Rc::make_mut(bounds).uppertvars.insert(id, (trace, use_op));
350+
bounds.uppertvars.insert(id, (trace, use_op));
341351
}
342352
});
343353
}
@@ -352,6 +362,7 @@ pub(super) fn add_lowertvar<'cx>(
352362
) {
353363
cx.modify_constraints(bounds_id, |_root_id, constraints| {
354364
if let constraint::Constraints::Unresolved(bounds) = constraints {
365+
let mut bounds = bounds.borrow_mut();
355366
if bounds
356367
.lowertvars
357368
.get(&id)
@@ -361,7 +372,7 @@ pub(super) fn add_lowertvar<'cx>(
361372
{
362373
return;
363374
}
364-
Rc::make_mut(bounds).lowertvars.insert(id, (trace, use_op));
375+
bounds.lowertvars.insert(id, (trace, use_op));
365376
}
366377
});
367378
}
@@ -378,13 +389,13 @@ pub(super) fn edges_to_tvar<'cx>(
378389
trace: DepthTrace,
379390
new_use_op: &UseOp,
380391
opt: bool,
381-
(id1, bounds1): (i32, &constraint::Bounds<Context<'cx>>),
392+
(id1, bounds1): (i32, &constraint::BoundsRef<Context<'cx>>),
382393
id2: i32,
383394
) {
384395
if !opt {
385396
add_uppertvar(cx, id1, id2, trace, new_use_op.dupe());
386397
}
387-
let lowertvars = bounds1.lowertvars.clone();
398+
let lowertvars = bounds1.borrow().lowertvars.clone();
388399
iter_with_filter(cx, &lowertvars, id1, |root_id, trace_l, use_op| {
389400
let use_op = pick_use_op(cx, use_op, new_use_op);
390401
add_uppertvar(
@@ -410,12 +421,12 @@ pub(super) fn edges_from_tvar<'cx>(
410421
new_use_op: &UseOp,
411422
opt: bool,
412423
id1: i32,
413-
(id2, bounds2): (i32, &constraint::Bounds<Context<'cx>>),
424+
(id2, bounds2): (i32, &constraint::BoundsRef<Context<'cx>>),
414425
) {
415426
if !opt {
416427
add_lowertvar(cx, id2, id1, trace, new_use_op.dupe());
417428
}
418-
let uppertvars = bounds2.uppertvars.clone();
429+
let uppertvars = bounds2.borrow().uppertvars.clone();
419430
iter_with_filter(cx, &uppertvars, id2, |root_id, trace_u, use_op| {
420431
let use_op = pick_use_op(cx, new_use_op, use_op);
421432
add_lowertvar(
@@ -437,12 +448,13 @@ pub(super) fn add_upper_edges<'cx>(
437448
trace: DepthTrace,
438449
new_use_op: UseOp,
439450
opt: bool,
440-
(id1, bounds1): (i32, &constraint::Bounds<Context<'cx>>),
441-
(id2, bounds2): (i32, &constraint::Bounds<Context<'cx>>),
451+
(id1, bounds1): (i32, &constraint::BoundsRef<Context<'cx>>),
452+
(id2, bounds2): (i32, &constraint::BoundsRef<Context<'cx>>),
442453
) {
443-
edges_to_ts(cx, trace, &new_use_op, opt, (id1, bounds1), &bounds2.upper);
454+
let upper = bounds2.borrow().upper.clone();
455+
edges_to_ts(cx, trace, &new_use_op, opt, (id1, bounds1), &upper);
444456
edges_to_tvar(cx, trace, &new_use_op, opt, (id1, bounds1), id2);
445-
let uppertvars = bounds2.uppertvars.clone();
457+
let uppertvars = bounds2.borrow().uppertvars.clone();
446458
iter_with_filter(cx, &uppertvars, id2, |tvar, trace_u, use_op| {
447459
let new_use_op = pick_use_op(cx, &new_use_op, use_op);
448460
let trace = DepthTrace::concat_trace(&[trace, *trace_u]);
@@ -459,12 +471,13 @@ pub(super) fn add_lower_edges<'cx>(
459471
trace: DepthTrace,
460472
new_use_op: UseOp,
461473
opt: bool,
462-
(id1, bounds1): (i32, &constraint::Bounds<Context<'cx>>),
463-
(id2, bounds2): (i32, &constraint::Bounds<Context<'cx>>),
474+
(id1, bounds1): (i32, &constraint::BoundsRef<Context<'cx>>),
475+
(id2, bounds2): (i32, &constraint::BoundsRef<Context<'cx>>),
464476
) {
465-
edges_from_ts(cx, trace, &new_use_op, opt, &bounds1.lower, (id2, bounds2));
477+
let lower = bounds1.borrow().lower.clone();
478+
edges_from_ts(cx, trace, &new_use_op, opt, &lower, (id2, bounds2));
466479
edges_from_tvar(cx, trace, &new_use_op, opt, id1, (id2, bounds2));
467-
let lowertvars = bounds1.lowertvars.clone();
480+
let lowertvars = bounds1.borrow().lowertvars.clone();
468481
iter_with_filter(cx, &lowertvars, id1, |tvar, trace_l, use_op| {
469482
let use_op = pick_use_op(cx, use_op, &new_use_op);
470483
let trace = DepthTrace::concat_trace(&[*trace_l, trace]);
@@ -508,16 +521,20 @@ pub(super) fn goto<'cx>(
508521
let cond1 = not_linked((id1, &bounds1), (id2, &bounds2));
509522
let cond2 = not_linked((id2, &bounds2), (id1, &bounds1));
510523
if cond1 {
511-
flows_across(cx, trace, use_op.dupe(), &bounds1.lower, &bounds2.upper)?;
524+
let (lower, upper) = {
525+
let bounds1 = bounds1.borrow();
526+
let bounds2 = bounds2.borrow();
527+
(bounds1.lower.clone(), bounds2.upper.clone())
528+
};
529+
flows_across(cx, trace, use_op.dupe(), &lower, &upper)?;
512530
}
513531
if cond2 {
514-
flows_across(
515-
cx,
516-
trace,
517-
unify_flip(use_op.dupe()),
518-
&bounds2.lower,
519-
&bounds1.upper,
520-
)?;
532+
let (lower, upper) = {
533+
let bounds1 = bounds1.borrow();
534+
let bounds2 = bounds2.borrow();
535+
(bounds2.lower.clone(), bounds1.upper.clone())
536+
};
537+
flows_across(cx, trace, unify_flip(use_op.dupe()), &lower, &upper)?;
521538
}
522539
if cond1 {
523540
add_upper_edges(

rust_port/crates/flow_typing_flow_js/src/flow_js/dispatch.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,11 @@ fn __flow_impl<'cx>(
243243
constraint::Constraints::Unresolved(bounds2),
244244
) => {
245245
if not_linked((id1, &bounds1), (id2, &bounds2)) {
246+
let (lower, upper) = {
247+
let bounds1 = bounds1.borrow();
248+
let bounds2 = bounds2.borrow();
249+
(bounds1.lower.clone(), bounds2.upper.clone())
250+
};
246251
add_upper_edges(
247252
cx,
248253
trace,
@@ -259,7 +264,7 @@ fn __flow_impl<'cx>(
259264
(id1, &bounds1),
260265
(id2, &bounds2),
261266
);
262-
flows_across(cx, trace, use_op.dupe(), &bounds1.lower, &bounds2.upper)?;
267+
flows_across(cx, trace, use_op.dupe(), &lower, &upper)?;
263268
}
264269
}
265270
(

rust_port/crates/flow_typing_flow_js/src/flow_js/helpers.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ use crate::speculation_kit;
2222
use crate::tvar_resolver;
2323

2424
pub(super) fn not_linked<CX>(
25-
(id1, _bounds1): (i32, &constraint::Bounds<CX>),
26-
(_id2, bounds2): (i32, &constraint::Bounds<CX>),
25+
(id1, _bounds1): (i32, &constraint::BoundsRef<CX>),
26+
(_id2, bounds2): (i32, &constraint::BoundsRef<CX>),
2727
) -> bool {
2828
// It suffices to check that id1 is not already in the lower bounds of
2929
// id2. Equivalently, we could check that id2 is not already in the upper
3030
// bounds of id1.
31-
!bounds2.lowertvars.contains_key(&id1)
31+
!bounds2.borrow().lowertvars.contains_key(&id1)
3232
}
3333

3434
thread_local! {

0 commit comments

Comments
 (0)