Skip to content

Commit

Permalink
[flow][cleanup] Cleanup Context.max_trace_depth and simplify trace
Browse files Browse the repository at this point in the history
Summary:
With some of the bigger mechanical cleanup done, it's time to simplify the structure of trace. Since now we never inspect the type part of trace, the only thing recorded in Type.trace that's relevant is the depth. This diff does the simplification.

Note that this part cannot be killed, since the recursion limit check depends on this, so we still need to pass traces around everywhere. In the future, we can expand on the trace param to make it a env param for flow_js, which can keep track of some flags like `in_implicit_instantiation` that should be moved out of Context.t.

Changelog: [internal]

Reviewed By: panagosg7

Differential Revision: D56735979

fbshipit-source-id: f9450bde33742e30a7c0881a275dcd810fbf6bab
  • Loading branch information
SamChou19815 authored and facebook-github-bot committed Apr 30, 2024
1 parent 3f4eb8b commit 0061955
Show file tree
Hide file tree
Showing 6 changed files with 29 additions and 248 deletions.
2 changes: 0 additions & 2 deletions src/typing/context.ml
Original file line number Diff line number Diff line change
Expand Up @@ -550,8 +550,6 @@ let include_suppressions cx = cx.metadata.include_suppressions

let severity_cover cx = cx.ccx.severity_cover

let max_trace_depth _ = 0

let property_maps cx = cx.ccx.sig_cx.property_maps

let call_props cx = cx.ccx.sig_cx.call_props
Expand Down
2 changes: 0 additions & 2 deletions src/typing/context.mli
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,6 @@ val include_suppressions : t -> bool

val severity_cover : t -> ExactCover.lint_severity_cover Utils_js.FilenameMap.t

val max_trace_depth : t -> int

val property_maps : t -> Type.Properties.map

val call_props : t -> Type.t IMap.t
Expand Down
53 changes: 17 additions & 36 deletions src/typing/flow_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -9033,11 +9033,10 @@ struct
As an optimization, skip [id1] when it will become either a resolved root or a
goto node (so that updating its bounds is unnecessary). *)
and edges_to_t cx trace ?(opt = false) (id1, bounds1) t2 =
let max = Context.max_trace_depth cx in
if not opt then add_upper cx t2 trace bounds1;
iter_with_filter cx bounds1.lowertvars id1 (fun (_, bounds) (trace_l, use_op) ->
let t2 = flow_use_op cx use_op t2 in
add_upper cx t2 (Trace.concat_trace ~max [trace_l; trace]) bounds
add_upper cx t2 (Trace.concat_trace [trace_l; trace]) bounds
)

(** Given [edges_from_t t1 (id2, bounds2)], for each [id] in [id2] + [bounds2.uppertvars],
Expand All @@ -9046,29 +9045,26 @@ struct
As an optimization, skip [id2] when it will become either a resolved root or a
goto node (so that updating its bounds is unnecessary). *)
and edges_from_t cx trace ~new_use_op ?(opt = false) t1 (id2, bounds2) =
let max = Context.max_trace_depth cx in
if not opt then add_lower t1 (trace, new_use_op) bounds2;
iter_with_filter cx bounds2.uppertvars id2 (fun (_, bounds) (trace_u, use_op) ->
let use_op = pick_use_op cx new_use_op use_op in
add_lower t1 (Trace.concat_trace ~max [trace; trace_u], use_op) bounds
add_lower t1 (Trace.concat_trace [trace; trace_u], use_op) bounds
)

(** for each [id'] in [id] + [bounds.lowertvars], [id'.bounds.upper += us] *)
and edges_to_ts ~new_use_op cx trace ?(opt = false) (id, bounds) us =
let max = Context.max_trace_depth cx in
us
|> UseTypeMap.iter (fun (u, _) trace_u ->
let u = flow_use_op cx new_use_op u in
edges_to_t cx (Trace.concat_trace ~max [trace; trace_u]) ~opt (id, bounds) u
edges_to_t cx (Trace.concat_trace [trace; trace_u]) ~opt (id, bounds) u
)

(** for each [id'] in [id] + [bounds.uppertvars], [id'.bounds.lower += ls] *)
and edges_from_ts cx trace ~new_use_op ?(opt = false) ls (id, bounds) =
let max = Context.max_trace_depth cx in
ls
|> TypeMap.iter (fun l (trace_l, use_op) ->
let new_use_op = pick_use_op cx use_op new_use_op in
edges_from_t cx (Trace.concat_trace ~max [trace_l; trace]) ~new_use_op ~opt l (id, bounds)
edges_from_t cx (Trace.concat_trace [trace_l; trace]) ~new_use_op ~opt l (id, bounds)
)

(** for each [id] in [id1] + [bounds1.lowertvars]:
Expand Down Expand Up @@ -9120,11 +9116,10 @@ struct
As an optimization, skip id1 when it will become either a resolved root or a
goto node (so that updating its bounds is unnecessary). *)
and edges_to_tvar cx trace ~new_use_op ?(opt = false) (id1, bounds1) id2 =
let max = Context.max_trace_depth cx in
if not opt then add_uppertvar id2 trace new_use_op bounds1;
iter_with_filter cx bounds1.lowertvars id1 (fun (_, bounds) (trace_l, use_op) ->
let use_op = pick_use_op cx use_op new_use_op in
add_uppertvar id2 (Trace.concat_trace ~max [trace_l; trace]) use_op bounds
add_uppertvar id2 (Trace.concat_trace [trace_l; trace]) use_op bounds
)

(** for each id in id2 + bounds2.uppertvars:
Expand All @@ -9135,24 +9130,22 @@ struct
As an optimization, skip id2 when it will become either a resolved root or a
goto node (so that updating its bounds is unnecessary). *)
and edges_from_tvar cx trace ~new_use_op ?(opt = false) id1 (id2, bounds2) =
let max = Context.max_trace_depth cx in
if not opt then add_lowertvar id1 trace new_use_op bounds2;
iter_with_filter cx bounds2.uppertvars id2 (fun (_, bounds) (trace_u, use_op) ->
let use_op = pick_use_op cx new_use_op use_op in
add_lowertvar id1 (Trace.concat_trace ~max [trace; trace_u]) use_op bounds
add_lowertvar id1 (Trace.concat_trace [trace; trace_u]) use_op bounds
)

(** for each id in id1 + bounds1.lowertvars:
id.bounds.upper += bounds2.upper
id.bounds.uppertvars += id2
id.bounds.uppertvars += bounds2.uppertvars *)
and add_upper_edges ~new_use_op cx trace ?(opt = false) (id1, bounds1) (id2, bounds2) =
let max = Context.max_trace_depth cx in
edges_to_ts ~new_use_op cx trace ~opt (id1, bounds1) bounds2.upper;
edges_to_tvar cx trace ~new_use_op ~opt (id1, bounds1) id2;
iter_with_filter cx bounds2.uppertvars id2 (fun (tvar, _) (trace_u, use_op) ->
let new_use_op = pick_use_op cx new_use_op use_op in
let trace = Trace.concat_trace ~max [trace; trace_u] in
let trace = Trace.concat_trace [trace; trace_u] in
edges_to_tvar cx trace ~new_use_op ~opt (id1, bounds1) tvar
)

Expand All @@ -9161,12 +9154,11 @@ struct
id.bounds.lowertvars += id1
id.bounds.lowertvars += bounds1.lowertvars *)
and add_lower_edges cx trace ~new_use_op ?(opt = false) (id1, bounds1) (id2, bounds2) =
let max = Context.max_trace_depth cx in
edges_from_ts cx trace ~new_use_op ~opt bounds1.lower (id2, bounds2);
edges_from_tvar cx trace ~new_use_op ~opt id1 (id2, bounds2);
iter_with_filter cx bounds1.lowertvars id1 (fun (tvar, _) (trace_l, use_op) ->
let use_op = pick_use_op cx use_op new_use_op in
let trace = Trace.concat_trace ~max [trace_l; trace] in
let trace = Trace.concat_trace [trace_l; trace] in
edges_from_tvar cx trace ~new_use_op:use_op ~opt tvar (id2, bounds2)
)

Expand Down Expand Up @@ -10562,10 +10554,8 @@ struct
let use_op = unknown_use in
let trace =
match trace with
| None -> Trace.unit_trace tvar (UseT (use_op, t'))
| Some trace ->
let max = Context.max_trace_depth cx in
Trace.rec_trace ~max tvar (UseT (use_op, t')) trace
| None -> Trace.unit_trace
| Some trace -> Trace.rec_trace trace
in
let (_, id) = open_tvar tvar in
resolve_id cx trace ~use_op ~fully_resolved id t'
Expand Down Expand Up @@ -10674,18 +10664,14 @@ struct
propagates bounds across type variables, where nothing interesting is going
on other than concatenating subtraces to make longer traces to describe
transitive data flows *)
and join_flow cx ts (t1, t2) =
let max = Context.max_trace_depth cx in
__flow cx (t1, t2) (Trace.concat_trace ~max ts)
and join_flow cx ts (t1, t2) = __flow cx (t1, t2) (Trace.concat_trace ts)

(* Call __flow while embedding traces. Typically this is used in code that
simplifies a constraint to generate subconstraints: the current trace is
"pushed" when recursing into the subconstraints, so that when we finally hit
an error and walk back, we can know why the particular constraints that
caused the immediate error were generated. *)
and rec_flow cx trace (t1, t2) =
let max = Context.max_trace_depth cx in
__flow cx (t1, t2) (Trace.rec_trace ~max t1 t2 trace)
and rec_flow cx trace (t1, t2) = __flow cx (t1, t2) (Trace.rec_trace trace)

and rec_flow_t cx trace ~use_op (t1, t2) = rec_flow cx trace (t1, UseT (use_op, t2))

Expand All @@ -10697,10 +10683,8 @@ struct
and flow_opt cx ?trace (t1, t2) =
let trace =
match trace with
| None -> Trace.unit_trace t1 t2
| Some trace ->
let max = Context.max_trace_depth cx in
Trace.rec_trace ~max t1 t2 trace
| None -> Trace.unit_trace
| Some trace -> Trace.rec_trace trace
in
__flow cx (t1, t2) trace

Expand Down Expand Up @@ -10733,16 +10717,13 @@ struct
(* Wrapper functions around __unify that manage traces. Use these functions for
all recursive calls in the implementation of __unify. *)
and rec_unify cx trace ~use_op ?(unify_any = false) t1 t2 =
let max = Context.max_trace_depth cx in
__unify cx ~use_op ~unify_any t1 t2 (Trace.rec_trace ~max t1 (UseT (use_op, t2)) trace)
__unify cx ~use_op ~unify_any t1 t2 (Trace.rec_trace trace)

and unify_opt cx ?trace ~use_op ?(unify_any = false) t1 t2 =
let trace =
match trace with
| None -> Trace.unit_trace t1 (UseT (unknown_use, t2))
| Some trace ->
let max = Context.max_trace_depth cx in
Trace.rec_trace ~max t1 (UseT (unknown_use, t2)) trace
| None -> Trace.unit_trace
| Some trace -> Trace.rec_trace trace
in
__unify cx ~use_op ~unify_any t1 t2 trace

Expand Down
161 changes: 8 additions & 153 deletions src/typing/trace.ml
Original file line number Diff line number Diff line change
Expand Up @@ -5,161 +5,16 @@
* LICENSE file in the root directory of this source tree.
*)

open Utils_js
open Reason
open Type
open TypeUtil
let trace_depth n = n

let compare = Stdlib.compare
let dummy_trace = 0

let trace_depth = snd
let unit_trace = 1

let dummy_trace = ([], 0)

(* Single-step trace with no parent. This corresponds to a
top-level invocation of the flow function, e.g. due to
a constraint generated in Type_inference_js *)
let unit_trace lower upper = ([Step { lower; upper; parent = [] }], 1)

(* Single-step trace with a parent. This corresponds to a
recursive invocation of the flow function.
Optimization: only embed when modes.trace > 0,
because otherwise we're not going to see any traces anyway.
*)
let rec_trace ~max lower upper parent =
let rec_trace parent =
let parent_depth = trace_depth parent in
let parent =
if max > 0 then
fst parent
else
[]
in
([Step { lower; upper; parent }], parent_depth + 1)

(* Join a list of traces. If the maximum depth of traces is configured to 0, then
agressively throw away traces and only compute the updated depth. *)
let concat_trace ~max ts =
let d = List.fold_left (fun acc (_, d) -> Base.Int.max acc d) 0 ts in
let steps =
if max > 0 then
Base.List.concat_map ~f:fst ts
else
[]
in
(steps, d)

(* used to index trace nodes *)
module TraceMap : WrappedMap.S with type key = trace_step list = WrappedMap.Make (struct
type key = trace_step list

type t = key

let compare = compare
end)

(* index the nodes in a trace down to a given level.
returns two maps, trace -> index and index -> trace
*)
let index_trace =
let rec f (level, tmap, imap) trace =
if level <= 0 || TraceMap.mem trace tmap then
(level, tmap, imap)
else
let (tmap, imap) =
let i = TraceMap.cardinal tmap in
(TraceMap.(add trace i tmap), IMap.(add i trace imap))
in
List.fold_left
(fun acc (Step { parent; _ }) ->
match parent with
| [] -> acc
| _ -> f acc parent)
(level - 1, tmap, imap)
trace
in
fun level trace ->
let (_, tmap, imap) = f (level, TraceMap.empty, IMap.empty) (fst trace) in
(tmap, imap)

(* scan a trace tree, return maximum position length
of reasons at or above the given depth limit, and
min of that limit and actual max depth *)
let max_depth_of_trace limit trace =
let rec f depth (Step { parent; _ }) =
if depth > limit then
depth
else
match parent with
| [] -> depth
| trace -> List.fold_left f (depth + 1) trace
in
List.fold_left f 1 (fst trace)

(* reformat a reason's description with
- the given prefix and suffix: if either is nonempty,
"desc" becomes "prefix[desc]suffix"
*)
let pretty_r r prefix suffix =
update_desc_new_reason
(fun desc ->
let desc_str = string_of_desc desc in
let custom =
if prefix = "" && suffix = "" then
desc_str
else
spf "%s[%s]%s" prefix desc_str suffix
in
RCustom custom)
r

(* prettyprint a trace. what we print:
- a list of paths, numbered 1..n, root first.
parent_depth + 1

- for each path, its list of steps.
usually a step is 2 main lines, one each for lower and upper.
but we elide the former if its a tvar that was also the prior
step's upper.
if the step was derived from another path, we append a note
to that effect.
*)
let reasons_of_trace ?(level = 0) trace =
let max_depth = max_depth_of_trace level trace in
let level = min level max_depth in
let (tmap, imap) = index_trace level trace in
let is_pipelined_tvar ~steps ~i lower =
i > 0
&&
let upper =
match List.nth steps (i - 1) with
| Step { upper; _ } -> upper
in
match upper with
| UseT (_, upper) -> lower = upper
| _ -> false
in
let print_step steps i (Step { lower; upper; parent }) =
(* omit lower if it's a pipelined tvar *)
( if is_pipelined_tvar ~steps ~i lower then
[]
else
[pretty_r (reason_of_t lower) (spf "%s " (string_of_ctor lower)) ""]
)
@ [
pretty_r
(reason_of_use_t upper)
(spf "~> %s " (string_of_use_ctor upper))
( if parent = [] then
""
else
match TraceMap.find_opt parent tmap with
| Some i -> spf " (from path %d)" (i + 1)
| None -> " (from [not shown])"
);
]
in
let print_path i steps =
let desc = RCustom (spf "* path %d:" (i + 1)) in
locationless_reason desc :: Base.List.concat (List.mapi (print_step steps) steps)
in
Base.List.concat (List.rev (IMap.fold (fun i flow acc -> print_path i flow :: acc) imap []))
let concat_trace ts =
let d = List.fold_left (fun acc d -> Base.Int.max acc d) 0 ts in
d
10 changes: 3 additions & 7 deletions src/typing/trace.mli
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,12 @@
* LICENSE file in the root directory of this source tree.
*)

val compare : Type.trace -> Type.trace -> int

val trace_depth : Type.trace -> int

val unit_trace : Type.t -> Type.use_t -> Type.trace
val unit_trace : Type.trace

val rec_trace : max:int -> Type.t -> Type.use_t -> Type.trace -> Type.trace
val rec_trace : Type.trace -> Type.trace

val concat_trace : max:int -> Type.trace list -> Type.trace
val concat_trace : Type.trace list -> Type.trace

val dummy_trace : Type.trace

val reasons_of_trace : ?level:int -> Type.trace -> Reason.reason list

0 comments on commit 0061955

Please sign in to comment.