Skip to content

Commit 5ac6ff5

Browse files
SamChou19815meta-codesync[bot]
authored andcommitted
[flow] Smart filtering for match bindings based on intermediate exhaustiveness analysis result
Summary: In this diff, I implemented smart filtering for match bindings. What does it mean exactly? An intuitive explanation is to make [this](https://flow.org/try/#1N4Igxg9gdgZglgcxALlAIwIZoKYBsD6uEEAztvhgE6UYCe+JADpdhgCYowa5kA0I2KAFcAtiRQAXSkOz9sADwxgJ+NPTbYuQ3BMnTZA+Y2yU4IwRO4A6SFBIrGVDGM7c+h46fNRLuKxJIGWh8MeT0ZfhYlCStpHzNsFBAMIQkIEQwJODAQfiEyfBE4eWw2fDgofDBMsAALfAA3KjgsXGxxZC4eAw0G-GhcWn9aY3wWZldu-g1mbGqJUoBaCRHEzrcDEgBrbAk62kXhXFxJ923d-cPRHEpTgyEoMDaqZdW7vKgoOfaSKgOKpqmDA+d4gB5fMA-P6LCCMLLQbiLOoYCqgh6-GDYRYIXYLSgkRZkCR4jpddwPfJLZjpOBkUEKTwJEJ+DAkMiUFSwkyZCC3dbdAC+-EgGiStnsAAIYMRkBKAPz2UxQBASgC8EoA5NKIBqANwAHUe0ElmAAXrLFRUVeqMntagAKADa2oAugBKCXAQ0Sn0Sx1HXASgA+EoeGngXzYLrVAD5NdgRHDaBreN7fY7xRIpcRo6q49rU1ABYbciAGiYSHBoEkGgAGKwAJgAHABOKy1kACoA) type check. This is an unresolvable problem in most of the languages with pattern matching, since there is no union type, so there is no way to assign type that represents a filtered view, but we can. To achieve this, we should make use of the exhaustiveness analysis to compute an intermediate leftover value after first n patterns, so that bindings in the n+1-th pattern can see a refined value. In the current analysis, we already compute a `ValueUnion.t` that represents a leftover value. A `ValueUnion.t` can be turned into a type easily, so boom, we get a refined type for the n+1-th match pattern. The process above can already refine the toplevel match binding, but what about nested ones, which is actually what's required to make the motivating example work. Well, for each nested one, we can run a selector on a parent `ValueUnion.t`, get back a new `ValueUnion.t` at the nested level, and then turn it into type. This will be a conservative process, so when we are unsure, we just bail out, and let the system compute a type based on destructuring as usual. The following ASCII art illustrates the process: ``` match (x) { // previous n patterns <---- based on them // we compute VU_root --------------- { // \|/ foo: // <-- VU_nested1 = select(Selector.Prop('foo', VU_ROOT) [ // | // -------------------------------------------------- // \|/ const bar // <- VU_nested2 = select(Selector.Elem(1), VU_nested1) ] } } => {...} // remaining patterns } ``` Now consider the original case of ``` const foo: ?string = 'foo'; const baz: string = match([foo]) { [null | undefined] => 'empty', [const foo] => foo, } ``` Right before the `[const foo]` pattern, the `ValueUnion.t` is roughly ``` { tuples: [Tuple { type: '[?string]', elements: [{ leafs: [] // no more null | void inexhaustible: ['string'] }]}]; } ``` You might notice that the outer tracked type is `[?string]` instead of `[string]`. This is the correct behavior, since if the pattern is just `const foo`, it's not ok to give it the type of `[string]` due to the danger of aliasing mutable values. Still, it works as expected for the nested value, since after we run the selector, we will have this child `ValueUnion.t`: ``` { leafs: [] // no more null | void inexhaustible: ['string'] } ``` , which is turned into `string`, and we are happy. Changelog: [fix] We improved the refinement system of match. Some code that previously gets a general type will now get a refined type. [example](https://flow.org/try/#1N4Igxg9gdgZglgcxALlAIwIZoKYBsD6uEEAztvhgE6UYCe+JADpdhgCYowa5kA0I2KAFcAtiRQAXSkOz9sADwxgJ+NPTbYuQ3BMnTZA+Y2yU4IwRO4A6SFBIrGVDGM7c+h46fNRLuKxJIGWh8MeT0ZfhYlCStpHzNsFBAMIQkIEQwJODAQfiEyfBE4eWw2fDgofDBMsAALfAA3KjgsXGxxZC4eAw0G-GhcWn9aY3wWZldu-g1mbGqJUoBaCRHEzrcDEgBrbAk62kXhXFxJ923d-cPRHEpTgyEoMDaqZdW7vKgoOfaSKgOKpqmDA+d4gB5fMA-P6LCCMLLQbiLOoYCqgh6-GDYRYIXYLSgkRZkCR4jpddwPfJLZjpOBkUEKTwJEJ+DAkMiUFSwkyZCC3dbdAC+-EgGiStnsAAIYMRkBKAPz2UxQBASgC8EoA5NKIBqANwAHUe0ElmAAXrLFRUVeqMntagAKADa2oAugBKCXAQ0Sn0Sx1HXASgA+EoeGngXzYLrVAD5NdgRHDaBreN7fY7xRIpcRo6q49rU1ABYbciAGiYSHBoEkGgAGKwAJgAHABOKy1kACoA) Reviewed By: gkz Differential Revision: D85388235 fbshipit-source-id: f88d5582afcc7da23f790d05f219d72d2c71be69
1 parent ea260b2 commit 5ac6ff5

12 files changed

Lines changed: 324 additions & 48 deletions

src/analysis/env_builder/name_def.ml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3269,7 +3269,10 @@ class def_finder ~autocomplete_hooks ~react_jsx env_info toplevel_scope =
32693269
~init:[]
32703270
~f:(fun i prev_pattern_locs_rev (_, { Case.pattern; body; guard; case_match_root_loc; _ })
32713271
->
3272-
let acc = MatchCaseRoot { case_match_root_loc; prev_pattern_locs_rev } in
3272+
let acc =
3273+
MatchCaseRoot
3274+
{ case_match_root_loc; root_pattern_loc = fst pattern; prev_pattern_locs_rev }
3275+
in
32733276
this#add_match_destructure_bindings
32743277
~case_match_root_loc
32753278
~has_guard:(Option.is_some guard)
@@ -3301,7 +3304,10 @@ class def_finder ~autocomplete_hooks ~react_jsx env_info toplevel_scope =
33013304
~init:[]
33023305
~f:(fun prev_pattern_locs_rev (_, { Case.pattern; body; guard; case_match_root_loc; _ })
33033306
->
3304-
let acc = MatchCaseRoot { case_match_root_loc; prev_pattern_locs_rev } in
3307+
let acc =
3308+
MatchCaseRoot
3309+
{ case_match_root_loc; root_pattern_loc = fst pattern; prev_pattern_locs_rev }
3310+
in
33053311
this#add_match_destructure_bindings
33063312
~case_match_root_loc
33073313
~has_guard:(Option.is_some guard)

src/analysis/env_builder/name_def_ordering.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -985,7 +985,7 @@ struct
985985
| Value { hints; expr; decl_kind = _; as_const = _ } ->
986986
let state = depends_of_hints state hints in
987987
depends_of_expression expr state
988-
| MatchCaseRoot { case_match_root_loc; prev_pattern_locs_rev } ->
988+
| MatchCaseRoot { case_match_root_loc; root_pattern_loc = _; prev_pattern_locs_rev } ->
989989
depends_of_node
990990
(fun visitor ->
991991
run visitor#identifier (Flow_ast_utils.match_root_ident case_match_root_loc);

src/analysis/env_builder/name_def_types.ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ type root =
102102
| Value of value
103103
| MatchCaseRoot of {
104104
case_match_root_loc: ALoc.t;
105+
root_pattern_loc: ALoc.t;
105106
prev_pattern_locs_rev: ALoc.t list;
106107
}
107108
| FunctionValue of {
@@ -241,7 +242,7 @@ module Print = struct
241242
| UnannotatedParameter r -> Reason.string_of_reason r
242243
| Annotation { annot = (loc, _); _ } -> spf "annot %s" (ALoc.debug_to_string loc)
243244
| Value { expr = (loc, _); _ } -> spf "val %s" (ALoc.debug_to_string loc)
244-
| MatchCaseRoot { case_match_root_loc; prev_pattern_locs_rev = _ } ->
245+
| MatchCaseRoot { case_match_root_loc; root_pattern_loc = _; prev_pattern_locs_rev = _ } ->
245246
spf "match root for case at %s" (ALoc.debug_to_string case_match_root_loc)
246247
| FunctionValue { function_loc; _ } -> spf "function val %s" (ALoc.debug_to_string function_loc)
247248
| ObjectValue _ -> "object"

src/typing/env_resolution.ml

Lines changed: 60 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -480,8 +480,19 @@ let rec resolve_binding cx def_scope_kind reason loc b =
480480
t
481481
| Root (Value { hints = _; expr; decl_kind; as_const }) ->
482482
expression cx ?decl:decl_kind ~as_const expr
483-
| Root (MatchCaseRoot { case_match_root_loc; prev_pattern_locs_rev = _ }) ->
484-
Type_env.var_ref cx (OrdinaryName Flow_ast_utils.match_root_name) case_match_root_loc
483+
| Root (MatchCaseRoot { case_match_root_loc; root_pattern_loc; prev_pattern_locs_rev }) ->
484+
let unfiltered_t =
485+
Type_env.var_ref cx (OrdinaryName Flow_ast_utils.match_root_name) case_match_root_loc
486+
in
487+
let node_cache = Context.node_cache cx in
488+
let patterns =
489+
Base.List.rev_map prev_pattern_locs_rev ~f:(fun l ->
490+
Base.Option.value_exn (Node_cache.get_match_pattern node_cache l)
491+
)
492+
in
493+
let value_left = Exhaustive.partial_leftover_value_union cx patterns unfiltered_t in
494+
Node_cache.set_match_pattern_value_union (Context.node_cache cx) root_pattern_loc value_left;
495+
Match_pattern_ir.ValueUnion.to_type (TypeUtil.reason_of_t unfiltered_t) value_left
485496
| Root (ObjectValue { obj_loc = loc; obj; synthesizable = ObjectSynthesizable _ }) ->
486497
let open Ast.Expression.Object in
487498
let resolve_prop ~bind_this ~prop_loc ~fn_loc fn =
@@ -832,44 +843,56 @@ let rec resolve_binding cx def_scope_kind reason loc b =
832843
let t = resolve_binding cx def_scope_kind reason loc binding in
833844
make_hooklike cx t
834845
| Select { selector; parent = (parent_loc, binding) } ->
835-
let refined_type =
836-
match selector with
837-
| Selector.Prop { prop; prop_loc; _ } ->
838-
let desc = RProperty (Some (OrdinaryName prop)) in
839-
Type_env.get_refinement cx desc prop_loc
840-
| _ -> None
846+
let node_cache = Context.node_cache cx in
847+
let filtered_pattern_type =
848+
Node_cache.get_match_pattern_value_union node_cache parent_loc
849+
|> Base.Option.bind ~f:(Match_pattern_ir.ValueUnion.select ~selector)
850+
|> Base.Option.map ~f:(fun value_left ->
851+
Node_cache.set_match_pattern_value_union node_cache loc value_left;
852+
Match_pattern_ir.ValueUnion.to_type reason value_left
853+
)
841854
in
842-
(match refined_type with
843-
| Some t ->
844-
(* When we can get a refined value on a destructured property,
845-
we must be in an assignment position and the type must have been resolved. *)
846-
t
855+
(match filtered_pattern_type with
856+
| Some t -> t
847857
| None ->
848-
let t = Type_env.checked_find_loc_env_write cx Env_api.PatternLoc parent_loc in
849-
let has_anno = binding_has_annot binding in
850-
let (selector, reason, has_default) = mk_selector_reason_has_default cx loc selector in
851-
let kind =
852-
if has_anno then
853-
DestructAnnot
854-
else
855-
DestructInfer
858+
let refined_type =
859+
match selector with
860+
| Selector.Prop { prop; prop_loc; _ } ->
861+
let desc = RProperty (Some (OrdinaryName prop)) in
862+
Type_env.get_refinement cx desc prop_loc
863+
| _ -> None
856864
in
857-
let t =
858-
Flow_js_utils.map_on_resolved_type cx reason t (fun t ->
859-
Tvar_resolver.mk_tvar_and_fully_resolve_no_wrap_where cx reason (fun tout ->
860-
Flow_js.flow cx (t, DestructuringT (reason, kind, selector, tout, Reason.mk_id ()))
861-
)
862-
)
863-
in
864-
if has_default then
865-
let (selector, reason, _) = mk_selector_reason_has_default cx loc Selector.Default in
866-
Flow_js_utils.map_on_resolved_type cx reason t (fun t ->
867-
Tvar_resolver.mk_tvar_and_fully_resolve_no_wrap_where cx reason (fun tout ->
868-
Flow_js.flow cx (t, DestructuringT (reason, kind, selector, tout, Reason.mk_id ()))
869-
)
870-
)
871-
else
872-
t)
865+
(match refined_type with
866+
| Some t ->
867+
(* When we can get a refined value on a destructured property,
868+
we must be in an assignment position and the type must have been resolved. *)
869+
t
870+
| None ->
871+
let t = Type_env.checked_find_loc_env_write cx Env_api.PatternLoc parent_loc in
872+
let has_anno = binding_has_annot binding in
873+
let (selector, reason, has_default) = mk_selector_reason_has_default cx loc selector in
874+
let kind =
875+
if has_anno then
876+
DestructAnnot
877+
else
878+
DestructInfer
879+
in
880+
let t =
881+
Flow_js_utils.map_on_resolved_type cx reason t (fun t ->
882+
Tvar_resolver.mk_tvar_and_fully_resolve_no_wrap_where cx reason (fun tout ->
883+
Flow_js.flow cx (t, DestructuringT (reason, kind, selector, tout, Reason.mk_id ()))
884+
)
885+
)
886+
in
887+
if has_default then
888+
let (selector, reason, _) = mk_selector_reason_has_default cx loc Selector.Default in
889+
Flow_js_utils.map_on_resolved_type cx reason t (fun t ->
890+
Tvar_resolver.mk_tvar_and_fully_resolve_no_wrap_where cx reason (fun tout ->
891+
Flow_js.flow cx (t, DestructuringT (reason, kind, selector, tout, Reason.mk_id ()))
892+
)
893+
)
894+
else
895+
t))
873896

874897
let resolve_inferred_function
875898
cx ~scope_kind ~statics ~needs_this_param id_loc reason function_loc function_ =

src/typing/exhaustive.ml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1537,3 +1537,11 @@ let analyze cx ~match_loc patterns arg_t =
15371537
(Error_message.EMatchNotExhaustive { loc = match_loc; examples; missing_pattern_asts = asts })
15381538
);
15391539
check_for_unused_patterns cx pattern_union used_pattern_locs
1540+
1541+
let partial_leftover_value_union cx patterns root_t =
1542+
let pattern_union = PatternUnionBuilder.of_patterns_ast cx ~raise_errors:false patterns in
1543+
let value_union = ValueUnionBuilder.of_type cx root_t in
1544+
let { value_left; used_pattern_locs = _; value_matched = _ } =
1545+
filter_values_by_patterns cx ~raise_errors:false ~value_union ~pattern_union
1546+
in
1547+
value_left

src/typing/exhaustive.mli

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,9 @@ val analyze :
1313
((ALoc.t, ALoc.t * Type.t) Flow_ast.MatchPattern.t * (* guarded *) bool) list ->
1414
Type.t ->
1515
unit
16+
17+
val partial_leftover_value_union :
18+
Context.t ->
19+
((ALoc.t, ALoc.t * Type.t) Flow_ast.MatchPattern.t * (* guarded *) bool) list ->
20+
Type.t ->
21+
Match_pattern_ir.ValueUnion.t

src/typing/match_pattern_ir.ml

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,8 @@ module rec ValueObject : sig
544544

545545
type t = Reason.t * t'
546546

547+
val to_original_type : t -> Type.t
548+
547549
val to_pattern : t -> PatternObject.t
548550
end = struct
549551
module Property = struct
@@ -571,6 +573,8 @@ end = struct
571573

572574
type t = Reason.t * t'
573575

576+
let to_original_type (_, { t; _ }) = t
577+
574578
let to_pattern (reason, { props; class_info; rest; kind; sentinel_props; _ }) =
575579
let loc = Reason.loc_of_reason reason in
576580
let (props, keys_order, rest) =
@@ -679,6 +683,10 @@ and ValueUnion : sig
679683
val is_empty : t -> bool
680684

681685
val to_pattern : t -> PatternUnion.t
686+
687+
val to_type : Reason.t -> t -> Type.t
688+
689+
val select : selector:Selector.t -> t -> t option
682690
end = struct
683691
type t = {
684692
leafs: LeafSet.t;
@@ -707,6 +715,33 @@ end = struct
707715
&& Base.List.is_empty enum_unknown_members
708716
&& Base.List.is_empty inexhaustible
709717

718+
let union
719+
{
720+
leafs = leafs1;
721+
tuples = tuples1;
722+
arrays = arrays1;
723+
objects = objects1;
724+
enum_unknown_members = enum_unknown_members1;
725+
inexhaustible = inexhaustible1;
726+
}
727+
{
728+
leafs = leafs2;
729+
tuples = tuples2;
730+
arrays = arrays2;
731+
objects = objects2;
732+
enum_unknown_members = enum_unknown_members2;
733+
inexhaustible = inexhaustible2;
734+
} =
735+
{
736+
leafs = LeafSet.union leafs1 leafs2;
737+
tuples = Base.List.rev_append (List.rev tuples1) tuples2;
738+
arrays = Base.List.rev_append (List.rev arrays1) arrays2;
739+
objects = Base.List.rev_append (List.rev objects1) objects2;
740+
enum_unknown_members =
741+
Base.List.rev_append (List.rev enum_unknown_members1) enum_unknown_members2;
742+
inexhaustible = Base.List.rev_append (List.rev inexhaustible1) inexhaustible2;
743+
}
744+
710745
let to_pattern { leafs; tuples; arrays; objects; enum_unknown_members; inexhaustible } =
711746
let (tuples_exact, tuples_inexact) =
712747
tuples
@@ -767,4 +802,73 @@ end = struct
767802
| (_, (reason, _) :: _) -> Some reason
768803
in
769804
{ PatternUnion.empty with PatternUnion.leafs; tuples_exact; tuples_inexact; objects; wildcard }
805+
806+
let to_type r { leafs; tuples; arrays; objects; enum_unknown_members; inexhaustible } =
807+
let all_possible_types =
808+
Base.List.concat
809+
[
810+
leafs |> LeafSet.elements |> Base.List.map ~f:Leaf.to_type;
811+
Base.List.map tuples ~f:ValueObject.to_original_type;
812+
Base.List.map arrays ~f:ValueObject.to_original_type;
813+
Base.List.map objects ~f:ValueObject.to_original_type;
814+
Base.List.bind enum_unknown_members ~f:(fun (_, leaf_set) ->
815+
LeafSet.elements leaf_set |> Base.List.map ~f:Leaf.to_type
816+
);
817+
inexhaustible;
818+
]
819+
in
820+
TypeUtil.union_of_ts r all_possible_types
821+
822+
let select
823+
~selector
824+
( { leafs = _; tuples; arrays; objects; enum_unknown_members = _; inexhaustible = _ } as
825+
value_union
826+
) =
827+
(* Given a list of values, select a sub-value matching the given key.
828+
* This is intend to be conservative. If we are unsure whether the key definitely exists/not exists,
829+
* we will abort and return None. Otherwise, we return `Some(candidates)` *)
830+
let conservative_find key values =
831+
let rec loop acc = function
832+
| [] -> Some (List.rev acc)
833+
| (_, { ValueObject.kind = _; t = _; props; rest; class_info = _; sentinel_props = _ })
834+
:: rest_values ->
835+
(match SMap.find_opt key props with
836+
| None ->
837+
if Base.Option.is_some rest then
838+
None
839+
else
840+
loop acc rest_values
841+
| Some None ->
842+
(* We definitely know the field doesn't exist. *)
843+
loop acc rest_values
844+
| Some (Some v) -> loop (v.ValueObject.Property.value :: acc) rest_values)
845+
in
846+
loop [] values
847+
in
848+
let conservative_find_all key =
849+
match conservative_find key tuples with
850+
| None -> None
851+
| Some tuple_candidates ->
852+
(match conservative_find key arrays with
853+
| None -> None
854+
| Some array_candidates ->
855+
(match conservative_find key objects with
856+
| None -> None
857+
| Some object_candidates ->
858+
Some (Base.List.concat [tuple_candidates; array_candidates; object_candidates])))
859+
in
860+
let vus_opt =
861+
match selector with
862+
| Selector.Elem { index; has_default = _ } ->
863+
let key = string_of_int index in
864+
conservative_find_all key
865+
| Selector.Prop { prop; prop_loc = _; has_default = _ } -> conservative_find_all prop
866+
| Selector.Computed _ -> None
867+
| Selector.ObjRest _ -> None
868+
| Selector.ArrRest _ -> None
869+
| Selector.Default -> Some [lazy value_union]
870+
in
871+
match vus_opt with
872+
| None -> None
873+
| Some vus -> Some (Base.List.fold_right vus ~init:empty ~f:(fun (lazy vu) acc -> union vu acc))
770874
end

src/typing/match_pattern_ir.mli

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,4 +183,8 @@ and ValueUnion : sig
183183
val is_empty : t -> bool
184184

185185
val to_pattern : t -> PatternUnion.t
186+
187+
val to_type : Reason.t -> t -> Type.t
188+
189+
val select : selector:Selector.t -> t -> t option
186190
end

src/typing/node_cache.ml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ type cache = {
5151
)
5252
ALocMap.t;
5353
match_patterns: ((ALoc.t, ALoc.t * Type.t) Ast.MatchPattern.t * bool) ALocMap.t;
54+
match_pattern_value_unions: Match_pattern_ir.ValueUnion.t ALocMap.t;
5455
}
5556

5657
type t = cache ref
@@ -75,6 +76,7 @@ let mk_empty () =
7576
tparams = ALocMap.empty;
7677
component_sigs = ALocMap.empty;
7778
match_patterns = ALocMap.empty;
79+
match_pattern_value_unions = ALocMap.empty;
7880
}
7981

8082
let set_annotation cache ((loc, _) as anno) =
@@ -129,6 +131,10 @@ let set_component_sig cache loc c =
129131
let set_match_pattern cache loc p =
130132
cache := { !cache with match_patterns = ALocMap.add loc p !cache.match_patterns }
131133

134+
let set_match_pattern_value_union cache loc v =
135+
cache :=
136+
{ !cache with match_pattern_value_unions = ALocMap.add loc v !cache.match_pattern_value_unions }
137+
132138
let get_annotation cache loc = ALocMap.find_opt loc !cache.annotations
133139

134140
let get_expression cache loc = ALocMap.find_opt loc !cache.expressions
@@ -162,3 +168,5 @@ let get_tparam cache loc = ALocMap.find_opt loc !cache.tparams
162168
let get_component_sig cache loc = ALocMap.find_opt loc !cache.component_sigs
163169

164170
let get_match_pattern cache loc = ALocMap.find_opt loc !cache.match_patterns
171+
172+
let get_match_pattern_value_union cache loc = ALocMap.find_opt loc !cache.match_pattern_value_unions

src/typing/node_cache.mli

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ val set_component_sig :
7979

8080
val set_match_pattern : t -> ALoc.t -> (ALoc.t, ALoc.t * Type.t) Ast.MatchPattern.t * bool -> unit
8181

82+
val set_match_pattern_value_union : t -> ALoc.t -> Match_pattern_ir.ValueUnion.t -> unit
83+
8284
val get_annotation : t -> ALoc.t -> (ALoc.t, ALoc.t * Type.t) Ast.Type.annotation option
8385

8486
val get_expression : t -> ALoc.t -> (ALoc.t, ALoc.t * Type.t) Ast.Expression.t option
@@ -149,3 +151,5 @@ val get_component_sig :
149151
option
150152

151153
val get_match_pattern : t -> ALoc.t -> ((ALoc.t, ALoc.t * Type.t) Ast.MatchPattern.t * bool) option
154+
155+
val get_match_pattern_value_union : t -> ALoc.t -> Match_pattern_ir.ValueUnion.t option

0 commit comments

Comments
 (0)