Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Builtin declaration merging #7919

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -908,3 +908,5 @@ declare var console: {
warn(...data: Array<any>): void,
...
};

declare var globalThis: $GlobalThis;
1 change: 1 addition & 0 deletions src/commands/commandUtils.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,7 @@ let make_options ~flowconfig_name ~flowconfig ~lazy_mode ~root (options_flags: O
opt_no_saved_state = options_flags.no_saved_state;
opt_arch;
opt_abstract_locations;
opt_declaration_merging = FlowConfig.declaration_merging flowconfig;
opt_include_suppressions = options_flags.include_suppressions;
opt_trust_mode = Option.value options_flags.trust_mode ~default:(FlowConfig.trust_mode flowconfig);
opt_recursion_limit = FlowConfig.recursion_limit flowconfig;
Expand Down
6 changes: 6 additions & 0 deletions src/commands/config/flowConfig.ml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ module Opts = struct
type opt_error = int * error_kind

type t = {
declaration_merging: bool;
abstract_locations: bool;
all: bool;
emoji: bool;
Expand Down Expand Up @@ -134,6 +135,7 @@ module Opts = struct
|> SSet.add ".webm"

let default_options = {
declaration_merging = false;
abstract_locations = false;
all = false;
emoji = false;
Expand Down Expand Up @@ -617,6 +619,9 @@ module Opts = struct
"experimental.abstract_locations",
boolean (fun opts v -> Ok { opts with abstract_locations = v });

"experimental.declaration_merging",
boolean (fun opts v -> Ok { opts with declaration_merging = v });

"no_flowlib",
boolean (fun opts v -> Ok { opts with no_flowlib = v });

Expand Down Expand Up @@ -1014,6 +1019,7 @@ let includes config = config.includes
let libs config = config.libs

(* options *)
let declaration_merging c = c.options.Opts.declaration_merging
let abstract_locations c = c.options.Opts.abstract_locations
let all c = c.options.Opts.all
let emoji c = c.options.Opts.emoji
Expand Down
1 change: 1 addition & 0 deletions src/commands/config/flowConfig.mli
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ val includes: config -> string list
val libs: config -> string list

(* options *)
val declaration_merging: config -> bool
val abstract_locations: config -> bool
val all: config -> bool
val emoji: config -> bool
Expand Down
2 changes: 2 additions & 0 deletions src/common/options.ml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type trust_mode =
| SilentTrust

type t = {
opt_declaration_merging : bool;
opt_abstract_locations : bool;
opt_all : bool;
opt_debug : bool;
Expand Down Expand Up @@ -119,6 +120,7 @@ type t = {
opt_type_asserts: bool;
}

let declaration_merging opts = opts.opt_declaration_merging
let all opts = opts.opt_all
let arch opts = opts.opt_arch
let max_literal_length opts = opts.opt_max_literal_length
Expand Down
1 change: 1 addition & 0 deletions src/flow_dot_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ let stub_metadata ~root ~checked = { Context.
include_suppressions = false;

(* global *)
declaration_merging = false;
max_literal_length = 100;
enable_const_params = false;
enable_enums = true;
Expand Down
9 changes: 8 additions & 1 deletion src/services/inference/init_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,17 @@ let parse_lib_file ~reader options file =
let load_lib_files ~sig_cx ~options ~reader files =

let verbose = Options.verbose options in
let declaration_merging = Options.declaration_merging options in

let files = if declaration_merging then
files
else
List.rev files
in

(* iterate in reverse override order *)
let%lwt (_, result) =
List.rev files
files
|> Lwt_list.fold_left_s (
fun (exclude_syms, results) file ->

Expand Down
3 changes: 3 additions & 0 deletions src/typing/context.ml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type metadata = {
strict_local: bool;

(* global *)
declaration_merging: bool;
max_literal_length: int;
enable_const_params: bool;
enable_enums: bool;
Expand Down Expand Up @@ -156,6 +157,7 @@ let metadata_of_options options = {
strict_local = false;

(* global *)
declaration_merging = Options.declaration_merging options;
max_literal_length = Options.max_literal_length options;
enable_const_params = Options.enable_const_params options;
enable_enums = Options.enums options;
Expand Down Expand Up @@ -271,6 +273,7 @@ let trust_constructor cx = cx.trust_constructor
let cx_with_trust cx trust = { cx with trust_constructor = trust }

let metadata cx = cx.metadata
let declaration_merging cx = cx.metadata.declaration_merging
let max_literal_length cx = cx.metadata.max_literal_length
let enable_const_params cx =
cx.metadata.enable_const_params || cx.metadata.strict || cx.metadata.strict_local
Expand Down
2 changes: 2 additions & 0 deletions src/typing/context.mli
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type metadata = {
strict: bool;
strict_local: bool;
(* global *)
declaration_merging: bool;
max_literal_length: int;
enable_const_params: bool;
enable_enums: bool;
Expand Down Expand Up @@ -71,6 +72,7 @@ val find_module_sig: sig_t -> string -> Type.t
(* accessors *)
val all_unresolved: t -> ISet.t IMap.t
val metadata: t -> metadata
val declaration_merging: t -> bool
val max_literal_length: t -> int
val enable_const_params: t -> bool
val enable_enums: t -> bool
Expand Down
1 change: 1 addition & 0 deletions src/typing/coverage.ml
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ class visitor = object (self)
| FunProtoApplyT _
| FunProtoBindT _
| FunProtoCallT _
| GlobalThisT _
| InternalT _
| KeysT _
| MaybeT _
Expand Down
17 changes: 16 additions & 1 deletion src/typing/debug_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ and _json_of_t_impl json_cx t = Hh_json.(
| FunProtoApplyT _
| FunProtoBindT _
| FunProtoCallT _
| GlobalThisT _
-> []

| DefT (_, _, FunT (static, proto, funtype)) -> [
Expand Down Expand Up @@ -497,6 +498,7 @@ and _json_of_use_t_impl json_cx t = Hh_json.(
]

| SetPropT (_, _, name, _, t, _)
| OverwritePropT (_, _, name, t)
| GetPropT (_, _, name, t)
| MatchPropT (_, _, name, t)
| TestPropT (_, _, name, t) -> [
Expand Down Expand Up @@ -561,6 +563,11 @@ and _json_of_use_t_impl json_cx t = Hh_json.(
"rightType", _json_of_t json_cx r
]

| MergeTypesT (_, _, _, _, l, r) -> [
"leftType", _json_of_t json_cx l;
"rightType", _json_of_t json_cx r
]

| ComparatorT (_, _, t) -> [
"type", _json_of_t json_cx t
]
Expand Down Expand Up @@ -1796,7 +1803,8 @@ let rec dump_t_ (depth, tvars) cx t =
| FunProtoT _
| FunProtoApplyT _
| FunProtoBindT _
| FunProtoCallT _ -> p t
| FunProtoCallT _
| GlobalThisT _ -> p t
| DefT (_, trust, PolyT (_, tps, c, id)) -> p ~trust:(Some trust) ~extra:(spf "%s [%s] #%d"
(kid c)
(String.concat "; " (Core_list.map ~f:(fun tp -> tp.name) (Nel.to_list tps)))
Expand Down Expand Up @@ -2097,6 +2105,9 @@ and dump_use_t_ (depth, tvars) cx t =
(if Context.trust_tracking cx then string_of_trust_rep (lookup_trust cx) trust else "")
(kid t)
| UseT (use_op, t) -> spf "UseT (%s, %s)" (string_of_use_op use_op) (kid t)
| MergeTypesT (_, _, _, _, x, y) -> p ~extra:(spf "%s, %s"
(kid x)
(kid y)) t
| AdderT (use_op, _, _, x, y) -> p ~extra:(spf "%s, %s, %s"
(string_of_use_op use_op)
(kid x)
Expand Down Expand Up @@ -2229,6 +2240,10 @@ and dump_use_t_ (depth, tvars) cx t =
(string_of_use_op use_op)
(propref prop)
(kid ptype)) t
| OverwritePropT (use_op, _, prop, ptype) -> p ~extra:(spf "%s, (%s), %s"
(string_of_use_op use_op)
(propref prop)
(kid ptype)) t
| SetPrivatePropT (_, _, prop, _, _, ptype, _) -> p ~extra:(spf "(%s), %s"
(prop)
(kid ptype)) t
Expand Down
4 changes: 3 additions & 1 deletion src/typing/env.ml
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,9 @@ let trunc_env =

(* initialize a new environment (once per module) *)
let init_env ?(exclude_syms=SSet.empty) cx module_scope =
set_exclude_symbols exclude_syms;
if not (Context.declaration_merging cx) then
set_exclude_symbols exclude_syms;

havoc_current_activation ();
let global_scope = Scope.fresh ~var_scope_kind:Global () in
push_var_scope cx global_scope;
Expand Down
91 changes: 89 additions & 2 deletions src/typing/flow_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3732,6 +3732,9 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace =
let reason = reason_of_t l in
rec_flow cx trace (l, UseT (use_op, fix_this_class cx trace reason (r, i)))

| l, MergeTypesT (reason, flip, propref, original_t, r, u) ->
merge_builtin cx trace reason flip propref original_t l r u

(** This rule is hit when a polymorphic type appears outside a
type application expression - i.e. not followed by a type argument list
delimited by angle brackets.
Expand Down Expand Up @@ -5244,6 +5247,13 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace =
Option.iter ~f:(fun t -> rec_flow_t cx trace (AnyT.untyped reason_op, t)) prop_t;
rec_flow cx trace (t, UseT (use_op, AnyT.untyped reason_op))

| DefT (_, _, ObjT { props_tmap; _ }), OverwritePropT (_, _, Named (_, name), t) ->
let p = Field (None, t, Polarity.Neutral) in
Context.set_prop cx props_tmap name p;

| ModuleT (_, { exports_tmap; _ }, _), OverwritePropT (_, _, Named (_, name), t) ->
Context.set_export cx exports_tmap name (None, t);

| DefT (reason_obj, _, ObjT o), MatchPropT (use_op, reason_op, propref, proptype) ->
match_obj_prop cx trace ~use_op o propref reason_obj reason_op proptype

Expand Down Expand Up @@ -6522,6 +6532,14 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace =
| FunProtoCallT reason, _ ->
rec_flow cx trace (FunProtoT reason, u)

| GlobalThisT _,
SetPropT (_, _, Named (_, name), _, tin, _) ->
overwrite_builtin cx ~trace name tin;

| GlobalThisT _, _ ->
let t = builtins cx in
rec_flow cx trace (t, u)

| _, LookupT (_, _, _, propref, lookup_action) ->
let use_op = use_op_of_lookup_action lookup_action in
add_output cx ~trace (Error_message.EIncompatibleProp {
Expand Down Expand Up @@ -6932,6 +6950,7 @@ and empty_success flavor u =
either by specially propagating it or selecting cases, etc. *)
| _, UseT (_, ExactT _)
| _, AdderT _
| _, MergeTypesT _
| _, AndT _
| _, OrT _
(* Propagation cases: these cases don't use the fact that the LHS is
Expand Down Expand Up @@ -7037,6 +7056,7 @@ and empty_success flavor u =
| _, SetElemT _
| _, SetPrivatePropT _
| _, SetPropT _
| _, OverwritePropT _
| _, SpecializeT _
| _, SubstOnPredT _
| _, SuperT _
Expand Down Expand Up @@ -7132,6 +7152,7 @@ and any_propagated cx trace any u =
true

| AdderT _
| MergeTypesT _
| AndT _
| ArrRestT _
| BecomeT _
Expand Down Expand Up @@ -7192,6 +7213,7 @@ and any_propagated cx trace any u =
| SentinelPropTestT _
| SetElemT _
| SetPropT _
| OverwritePropT _
| ModuleExportsAssignT _
| SpecializeT _
| SubstOnPredT _ (* Should be impossible. We only generate these with OpenPredTs. *)
Expand Down Expand Up @@ -7308,7 +7330,8 @@ and any_propagated_use cx trace use_op any l =
| FunProtoCallT _
| FunProtoT _
| ObjProtoT _
| NullProtoT _ ->
| NullProtoT _
| GlobalThisT _ ->
true

(* Handled already in __flow *)
Expand Down Expand Up @@ -7729,6 +7752,7 @@ and check_polarity cx ?trace polarity = function
| FunProtoApplyT _
| FunProtoBindT _
| FunProtoCallT _
| GlobalThisT _
| EvalT _
| InternalT (ExtendsT _)
| InternalT (ChoiceKitT _)
Expand Down Expand Up @@ -11112,10 +11136,73 @@ and extract_non_spread cx ~trace = function
add_output cx ~trace (Error_message.(EUnsupportedSyntax (loc, SpreadArgument)));
AnyT.error reason

and merge_builtin cx trace reason flip propref original_t l r u =
let string_reason = string_of_reason reason in
ignore string_reason;
if needs_resolution r then begin
(* TODO: this doesn't work without writing to property first *)
rec_flow cx trace (u, OverwritePropT (unknown_use, reason, propref, l));
(* *)

rec_flow cx trace (r, MergeTypesT (reason, not flip, propref, original_t, l, u))
end else begin
let (l, r) = if flip then (r, l) else (l, r) in
begin match l, r with

(* polymorphic interface ~> polymorphic class *)
| DefT (_, _, PolyT (_, _, DefT (_, _, ClassT (DefT (_, _, InstanceT (_, _, _, { structural = true; own_props = o1_id; _ })))), _)),
DefT (_, _, PolyT (_, _, ThisClassT (_, DefT (_, _, InstanceT (_, _, _, { structural = false; own_props = o2_id; _ }))), _)) ->
let o1 = Context.find_props cx o1_id in
let o2 = Context.find_props cx o2_id in
ignore o1;
ignore o2;
Context.add_property_map cx o2_id (SMap.union o1 o2);

(* interface ~> class *)
| DefT (_, _, ClassT (DefT (_, _, InstanceT (_, _, _, { structural = true; own_props = o1_id; _ })))),
ThisClassT (_, DefT (_, _, InstanceT (_, _, _, { structural = false; own_props = o2_id; _ }))) ->
let o1 = Context.find_props cx o1_id in
let o2 = Context.find_props cx o2_id in
ignore o1;
ignore o2;
Context.add_property_map cx o2_id (SMap.union o1 o2);

(* module ~> module *)
| ModuleT (_, {exports_tmap = exports1_id; _}, _), ModuleT (_, {exports_tmap = exports2_id; _}, _) ->
let exports1 = Context.find_exports cx exports1_id in
let exports2 = Context.find_exports cx exports2_id in
let exports = SMap.union exports1 exports2 in
Context.add_export_map cx exports2_id exports;
ignore @@ SMap.union ~combine:(fun key (_, t1) (_, t2) ->
rec_flow cx trace (t2, MergeTypesT (reason, false, Named (reason_of_t t1, key), t2, t1, r));
None
) exports1 exports2;
()

(* any ~> any *)
| _, _ ->
()
end
end

and set_builtin cx ?trace x t =
let reason = builtin_reason (RCustom x) in
let obj = builtins cx in
let propref = Named (reason, x) in
let old_t = Tvar.mk_derivable_where cx reason (fun t ->
flow_opt cx ?trace (obj, GetPropT (unknown_use, reason, propref, t));
)
in
if Context.declaration_merging cx then
flow_opt cx ?trace (t, MergeTypesT (reason, false, propref, t, old_t, obj))
else
flow_opt cx ?trace (obj, SetPropT (unknown_use, reason, propref, Normal, t, None))

and overwrite_builtin cx ?trace x t =
let reason = builtin_reason (RCustom x) in
let propref = Named (reason, x) in
flow_opt cx ?trace (builtins cx, SetPropT (unknown_use, reason, propref, Normal, t, None))
let obj = builtins cx in
flow_opt cx ?trace (obj, OverwritePropT (unknown_use, reason, propref, t))

(* Wrapper functions around __flow that manage traces. Use these functions for
all recursive calls in the implementation of __flow. *)
Expand Down
1 change: 1 addition & 0 deletions src/typing/members.ml
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,7 @@ let rec extract_type cx this_t = match this_t with
| FunProtoBindT _
| FunProtoCallT _
| FunProtoT _
| GlobalThisT _
| KeysT (_, _)
| DefT (_, _, MixedT _)
| NullProtoT _
Expand Down
1 change: 1 addition & 0 deletions src/typing/resolvableTypeJob.ml
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ and collect_of_type ?log_unresolved cx acc = function
| FunProtoT _
| NullProtoT _
| ObjProtoT _
| GlobalThisT _
| CustomFunT (_, _)

| ExistsT _
Expand Down
Loading