From f5beb474b70cf82971638764fd588e678904941e Mon Sep 17 00:00:00 2001 From: "Jake Bailey (Hacklang)" Date: Sat, 3 Oct 2020 16:10:12 -0700 Subject: [PATCH] Move Nast -> decl functions into Decl_nast and Decl_folded_class modules Summary: The Decl module provides functions which accept a Nast, convert it into a decl, and write that decl into shared memory. This is a problem for the migration to the direct decl parser, which produces a decl directly from source text. If we want to abstract over whether the direct decl parser is enabled, we will need to change these APIs so that they all accept a filename rather than an AST. This diff does so--APIs accepting a filename are left in the Decl module, and APIs accepting a Nast (and their implementations) are moved to a new module, Decl_nast. The implementation of class-folding (i.e., producing a Decl_defs.decl_class_type) is move into another module, Decl_folded_class. Reviewed By: shiqicao Differential Revision: D24033149 fbshipit-source-id: 0c397daf509bb4d3a52503153ebd36214620b1fe --- hphp/hack/src/decl/decl.ml | 1471 +++----------------- hphp/hack/src/decl/decl.mli | 22 +- .../src/decl/{decl.ml => decl_folded_class.ml} | 376 +---- hphp/hack/src/decl/decl_folded_class.mli | 19 + hphp/hack/src/decl/decl_nast.ml | 227 +++ hphp/hack/src/decl/decl_nast.mli | 41 + hphp/hack/src/decl/dune.inc | 51 +- hphp/hack/src/hh_single_type_check.ml | 7 +- hphp/hack/src/typing/dune | 3 + hphp/hack/src/typing/typing.ml | 4 +- 10 files changed, 530 insertions(+), 1691 deletions(-) rewrite hphp/hack/src/decl/decl.ml (91%) copy hphp/hack/src/decl/{decl.ml => decl_folded_class.ml} (71%) create mode 100644 hphp/hack/src/decl/decl_folded_class.mli create mode 100644 hphp/hack/src/decl/decl_nast.ml create mode 100644 hphp/hack/src/decl/decl_nast.mli diff --git a/hphp/hack/src/decl/decl.ml b/hphp/hack/src/decl/decl.ml dissimilarity index 91% index 7a380e06756..c0b1852bf97 100644 --- a/hphp/hack/src/decl/decl.ml +++ b/hphp/hack/src/decl/decl.ml @@ -1,1300 +1,171 @@ -(* - * Copyright (c) 2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the "hack" directory of this source tree. - * - *) - -(*****************************************************************************) -(* Module used to declare the types. - * For each class we want to build a complete type, that is the type of - * the methods defined in the class plus everything that was inherited. - *) -(*****************************************************************************) -open Hh_prelude -open Decl_defs -open Decl_fun_utils -open Aast -open Shallow_decl_defs -open Typing_defs -open Typing_deps -module Reason = Typing_reason -module Inst = Decl_instantiate -module Attrs = Typing_defs.Attributes -module SN = Naming_special_names - -let tracked_names : FileInfo.names option ref = ref None - -let start_tracking () : unit = tracked_names := Some FileInfo.empty_names - -let record_fun (s : string) : unit = - match !tracked_names with - | None -> () - | Some names -> - tracked_names := - Some FileInfo.{ names with n_funs = SSet.add s names.n_funs } - -let record_class (s : string) : unit = - match !tracked_names with - | None -> () - | Some names -> - tracked_names := - Some FileInfo.{ names with n_classes = SSet.add s names.n_classes } - -let record_record_def (s : string) : unit = - match !tracked_names with - | None -> () - | Some names -> - tracked_names := - Some - FileInfo.{ names with n_record_defs = SSet.add s names.n_record_defs } - -let record_typedef (s : string) : unit = - match !tracked_names with - | None -> () - | Some names -> - tracked_names := - Some FileInfo.{ names with n_types = SSet.add s names.n_types } - -let record_const (s : string) : unit = - match !tracked_names with - | None -> () - | Some names -> - tracked_names := - Some FileInfo.{ names with n_consts = SSet.add s names.n_consts } - -let stop_tracking () : FileInfo.names = - let res = - match !tracked_names with - | None -> - Hh_logger.log - "Warning: called Decl.stop_tracking without corresponding start_tracking"; - FileInfo.empty_names - | Some names -> names - in - tracked_names := None; - res - -(*****************************************************************************) -(* Checking that the kind of a class is compatible with its parent - * For example, a class cannot extend an interface, an interface cannot - * extend a trait etc ... - *) -(*****************************************************************************) - -let check_extend_kind - (parent_pos : Pos.t) - (parent_kind : Ast_defs.class_kind) - (parent_name : string) - (child_pos : Pos.t) - (child_kind : Ast_defs.class_kind) - (child_name : string) : unit = - match (parent_kind, child_kind) with - (* What is allowed *) - | ( (Ast_defs.Cabstract | Ast_defs.Cnormal), - (Ast_defs.Cabstract | Ast_defs.Cnormal) ) - | (Ast_defs.Cabstract, Ast_defs.Cenum) - (* enums extend BuiltinEnum under the hood *) - | (Ast_defs.Ctrait, Ast_defs.Ctrait) - | (Ast_defs.Cinterface, Ast_defs.Cinterface) -> - () - | _ -> - (* What is disallowed *) - Errors.wrong_extend_kind - ~parent_pos - ~parent_kind - ~parent_name - ~child_pos - ~child_kind - ~child_name - -(*****************************************************************************) -(* Functions used retrieve everything implemented in parent classes - * The return values: - * env: the new environment - * parents: the name of all the parents and grand parents of the class this - * includes traits. - * is_complete: true if all the parents live in Hack - *) -(*****************************************************************************) - -let disallow_trait_reuse (env : Decl_env.env) : bool = - TypecheckerOptions.disallow_trait_reuse (Decl_env.tcopt env) - -let report_reused_trait - (parent_type : Decl_defs.decl_class_type) - (shallow_class : Shallow_decl_defs.shallow_class) : string -> unit = - Errors.trait_reuse - parent_type.dc_pos - parent_type.dc_name - shallow_class.sc_name - -(** - * Verifies that a class never reuses the same trait throughout its hierarchy. - * - * Since Hack only has single inheritance and we already put up a warning for - * cyclic class hierarchies, if there is any overlap between our extends and - * our parents' extends, that overlap must be a trait. - * - * This does not hold for interfaces because they have multiple inheritance, - * but interfaces cannot use traits in the first place. - * - * XHP attribute dependencies don't actually pull the trait into the class, - * so we need to track them totally separately. - *) -let check_no_duplicate_traits - (parent_type : Decl_defs.decl_class_type) - (shallow_class : Shallow_decl_defs.shallow_class) - (c_extends : SSet.t) - (full_extends : SSet.t) : unit = - let class_size = SSet.cardinal c_extends in - let parents_size = SSet.cardinal parent_type.dc_extends in - let full_size = SSet.cardinal full_extends in - if class_size + parents_size > full_size then - let duplicates = SSet.inter c_extends parent_type.dc_extends in - SSet.iter (report_reused_trait parent_type shallow_class) duplicates - -(** - * Adds the traits/classes which are part of a class' hierarchy. - * - * Traits are tracked separately but merged into the parents list when - * typechecking so that the class can access the trait members which are - * declared as private/protected. - *) -let add_grand_parents_or_traits - (no_trait_reuse : bool) - (parent_pos : Pos.t) - (shallow_class : Shallow_decl_defs.shallow_class) - (acc : SSet.t * bool * [> `Extends_pass | `Xhp_pass ]) - (parent_type : Decl_defs.decl_class_type) : SSet.t * bool * 'a = - let (extends, is_complete, pass) = acc in - let class_pos = fst shallow_class.sc_name in - let class_kind = shallow_class.sc_kind in - let class_name = snd shallow_class.sc_name in - if phys_equal pass `Extends_pass then - check_extend_kind - parent_pos - parent_type.dc_kind - parent_type.dc_name - class_pos - class_kind - class_name; - - (* If we are crawling the xhp attribute deps, we need to merge their xhp deps - * as well *) - let parent_deps = - if phys_equal pass `Xhp_pass then - SSet.union parent_type.dc_extends parent_type.dc_xhp_attr_deps - else - parent_type.dc_extends - in - let extends' = SSet.union extends parent_deps in - (* Verify that merging the parent's extends did not introduce trait reuse *) - if no_trait_reuse then - check_no_duplicate_traits parent_type shallow_class extends extends'; - (extends', parent_type.dc_members_fully_known && is_complete, pass) - -let get_class_parent_or_trait - (env : Decl_env.env) - (shallow_class : Shallow_decl_defs.shallow_class) - ((parents, is_complete, pass) : - SSet.t * bool * [> `Extends_pass | `Xhp_pass ]) - (ty : Typing_defs.decl_phase Typing_defs.ty) : SSet.t * bool * 'a = - (* See comment on check_no_duplicate_traits for reasoning here *) - let no_trait_reuse = - disallow_trait_reuse env - && (not (phys_equal pass `Xhp_pass)) - && not Ast_defs.(equal_class_kind shallow_class.sc_kind Cinterface) - in - let (_, (parent_pos, parent), _) = Decl_utils.unwrap_class_type ty in - (* If we already had this exact trait, we need to flag trait reuse *) - let reused_trait = no_trait_reuse && SSet.mem parent parents in - let parents = SSet.add parent parents in - let parent_type = Decl_env.get_class_dep env parent in - match parent_type with - | None -> - (* The class lives in PHP *) - (parents, false, pass) - | Some parent_type -> - (* The parent class lives in Hack, so we can report reused traits *) - if reused_trait then report_reused_trait parent_type shallow_class parent; - let acc = (parents, is_complete, pass) in - add_grand_parents_or_traits - no_trait_reuse - parent_pos - shallow_class - acc - parent_type - -let get_class_parents_and_traits - (env : Decl_env.env) (shallow_class : Shallow_decl_defs.shallow_class) : - SSet.t * SSet.t * bool = - let parents = SSet.empty in - let is_complete = true in - (* extends parents *) - let acc = (parents, is_complete, `Extends_pass) in - let (parents, is_complete, _) = - List.fold_left - shallow_class.sc_extends - ~f:(get_class_parent_or_trait env shallow_class) - ~init:acc - in - (* traits *) - let acc = (parents, is_complete, `Traits_pass) in - let (parents, is_complete, _) = - List.fold_left - shallow_class.sc_uses - ~f:(get_class_parent_or_trait env shallow_class) - ~init:acc - in - (* XHP classes whose attributes were imported via "attribute :foo;" syntax *) - let acc = (SSet.empty, is_complete, `Xhp_pass) in - let (xhp_parents, is_complete, _) = - List.fold_left - shallow_class.sc_xhp_attr_uses - ~f:(get_class_parent_or_trait env shallow_class) - ~init:acc - in - (parents, xhp_parents, is_complete) - -(*****************************************************************************) -(* Section declaring the type of a function *) -(*****************************************************************************) - -let rec ifun_decl - ~(write_shmem : bool) (ctx : Provider_context.t) (f : Nast.fun_) : - Typing_defs.fun_elt = - let f = Errors.ignore_ (fun () -> Naming.fun_ ctx f) in - let fe = fun_decl ctx f in - if write_shmem then Decl_heap.Funs.add (snd f.f_name) fe; - fe - -and fun_decl (ctx : Provider_context.t) (f : Nast.fun_) : Typing_defs.fun_elt = - let dep = Dep.Fun (snd f.f_name) in - let env = { Decl_env.mode = f.f_mode; droot = Some dep; ctx } in - let fe = fun_decl_in_env env ~is_lambda:false f in - record_fun (snd f.f_name); - fe - -and fun_decl_in_env (env : Decl_env.env) ~(is_lambda : bool) (f : Nast.fun_) : - Typing_defs.fun_elt = - check_params f.f_params; - let reactivity = fun_reactivity env f.f_user_attributes in - let returns_mutable = fun_returns_mutable f.f_user_attributes in - let returns_void_to_rx = fun_returns_void_to_rx f.f_user_attributes in - let return_disposable = has_return_disposable_attribute f.f_user_attributes in - let params = make_params env ~is_lambda f.f_params in - let capability = - hint_to_type - ~is_lambda:false - ~default: - (Typing_make_type.default_capability (Reason.Rhint (fst f.f_name))) - env - (Reason.Rwitness (fst f.f_name)) - (hint_of_type_hint f.f_cap) - in - let ret_ty = - ret_from_fun_kind - ~is_lambda - env - (fst f.f_name) - f.f_fun_kind - (hint_of_type_hint f.f_ret) - in - let arity = - match f.f_variadic with - | FVvariadicArg param -> - assert param.param_is_variadic; - Fvariadic (make_param_ty env ~is_lambda param) - | FVellipsis p -> Fvariadic (make_ellipsis_param_ty p) - | FVnonVariadic -> Fstandard - in - let tparams = List.map f.f_tparams (type_param env) in - let where_constraints = - List.map f.f_where_constraints (where_constraint env) - in - let fe_deprecated = - Naming_attributes_deprecated.deprecated - ~kind:"function" - f.f_name - f.f_user_attributes - in - let fe_php_std_lib = - Naming_attributes.mem SN.UserAttributes.uaPHPStdLib f.f_user_attributes - in - let fe_type = - mk - ( Reason.Rwitness (fst f.f_name), - Tfun - { - ft_arity = arity; - ft_tparams = tparams; - ft_where_constraints = where_constraints; - ft_params = params; - ft_implicit_params = { capability }; - ft_ret = { et_type = ret_ty; et_enforced = false }; - ft_reactive = reactivity; - ft_flags = - make_ft_flags - f.f_fun_kind - (* Functions can't be mutable because they don't have "this" *) - None - ~returns_mutable - ~return_disposable - ~returns_void_to_rx; - } ) - in - { fe_pos = fst f.f_name; fe_type; fe_deprecated; fe_php_std_lib } - -(*****************************************************************************) -(* Section declaring the type of a class *) -(*****************************************************************************) - -type class_env = { - ctx: Provider_context.t; - stack: SSet.t; -} - -let check_if_cyclic (class_env : class_env) ((pos, cid) : Pos.t * string) : bool - = - let stack = class_env.stack in - let is_cyclic = SSet.mem cid stack in - if is_cyclic then Errors.cyclic_class_def stack pos; - is_cyclic - -let shallow_decl_enabled (ctx : Provider_context.t) : bool = - TypecheckerOptions.shallow_class_decl (Provider_context.get_tcopt ctx) - -let pu_enum_fold - origin - (acc : Typing_defs.pu_enum_type SMap.t) - (spu : Shallow_decl_defs.shallow_pu_enum) : Typing_defs.pu_enum_type SMap.t - = - let spu_name = snd spu.spu_name in - let tpu = - match SMap.find_opt spu_name acc with - | None -> Decl_to_typing.shallow_pu_enum_to_pu_enum_type origin spu - | Some tpu -> - let origin = { pu_class = origin; pu_enum = spu_name } in - { - tpu_name = spu.spu_name; - tpu_is_final = spu.spu_is_final; - tpu_case_types = - List.fold_left - spu.spu_case_types - ~init:tpu.tpu_case_types - ~f:(fun acc tp -> - let sid = snd tp.tp_name in - SMap.add sid (origin, tp) acc); - tpu_case_values = - List.fold_left - spu.spu_case_values - ~init:tpu.tpu_case_values - ~f:(fun acc (name, dty) -> - SMap.add (snd name) (origin, name, dty) acc); - tpu_members = - List.fold_left spu.spu_members ~init:tpu.tpu_members ~f:(fun acc sm -> - let tpum_types = - match SMap.find_opt (snd sm.spum_atom) acc with - | None -> SMap.empty - | Some tm -> tm.tpum_types - in - let tpum_exprs = - match SMap.find_opt (snd sm.spum_atom) acc with - | None -> SMap.empty - | Some tm -> tm.tpum_exprs - in - let tpum_types = - List.fold_left - sm.spum_types - ~init:tpum_types - ~f:(fun acc (sid, declty) -> - let k = snd sid in - SMap.add k (origin, sid, declty) acc) - in - let tpum_exprs = - List.fold_left sm.spum_exprs ~init:tpum_exprs ~f:(fun acc k -> - SMap.add (snd k) (origin, k) acc) - in - SMap.add - (snd sm.spum_atom) - { - tpum_atom = sm.spum_atom; - tpum_origin = origin; - tpum_types; - tpum_exprs; - } - acc); - } - in - SMap.add (snd spu.spu_name) tpu acc - -let rec class_decl_if_missing - ~(sh : SharedMem.uses) (class_env : class_env) (c : Nast.class_) : - Decl_defs.decl_class_type option = - let ((_, cid) as c_name) = c.c_name in - if check_if_cyclic class_env c_name then - None - else if shallow_decl_enabled class_env.ctx then - (* This function is often called for its side effect of ensuring that the - class is declared. When shallow-decl is enabled, we still want this - side effect (for use cases like on-the-fly declaring entire files in - Decl_redecl_service for incremental typechecking), but since we are not - producing a folded class declaration, there is nothing we can return. - This is a code smell--we should use a function with a different - signature when we only want this side effect. *) - let (_ : shallow_class) = - Shallow_classes_provider.decl class_env.ctx ~use_cache:true c - in - None - else - match Decl_heap.Classes.get cid with - | Some _ as class_ -> class_ - | None -> - (* Class elements are in memory if and only if the class itself is there. - * Exiting before class declaration is ready would break this invariant *) - WorkerCancel.with_no_cancellations @@ fun () -> - let class_ = class_naming_and_decl ~sh class_env cid c in - Some class_ - -and class_naming_and_decl - ~(sh : SharedMem.uses) - (class_env : class_env) - (cid : string) - (c : Nast.class_) : Decl_defs.decl_class_type = - let class_env = { class_env with stack = SSet.add cid class_env.stack } in - let shallow_class = - Shallow_classes_provider.decl class_env.ctx ~use_cache:false c - in - let (errors, tc) = - Errors.do_ (fun () -> - class_parents_decl ~sh class_env shallow_class; - class_decl ~sh class_env.ctx shallow_class) - in - let name = snd shallow_class.sc_name in - record_class name; - let class_ = { tc with dc_decl_errors = Some errors } in - Decl_heap.Classes.add name class_; - class_ - -and class_parents_decl - ~(sh : SharedMem.uses) - (class_env : class_env) - (c : Shallow_decl_defs.shallow_class) : unit = - let class_type class_ = - let (_ : class_type option) = class_type_decl ~sh class_env class_ in - () - in - List.iter c.sc_extends class_type; - List.iter c.sc_implements class_type; - List.iter c.sc_uses class_type; - List.iter c.sc_xhp_attr_uses class_type; - List.iter c.sc_req_extends class_type; - List.iter c.sc_req_implements class_type; - let enum_includes = - Aast.enum_includes_map ~f:(fun et -> et.te_includes) c.sc_enum_type - in - List.iter enum_includes class_type; - () - -and is_disposable_type (env : Decl_env.env) (hint : Typing_defs.decl_ty) : bool - = - match get_node hint with - | Tapply ((_, c), _) -> - begin - match Decl_env.get_class_dep env c with - | None -> false - | Some c -> c.dc_is_disposable - end - | _ -> false - -and class_type_decl - ~(sh : SharedMem.uses) (class_env : class_env) (hint : Typing_defs.decl_ty) - : Typing_defs.class_type option = - match get_node hint with - | Tapply ((_, cid), _) -> - begin - match Naming_provider.get_class_path class_env.ctx cid with - | Some fn when not (Decl_heap.Classes.mem cid) -> - (* We are supposed to redeclare the class *) - let class_opt = Ast_provider.find_class_in_file class_env.ctx fn cid in - Errors.run_in_context fn Errors.Decl (fun () -> - Option.Monad_infix.( - class_opt - >>= class_decl_if_missing ~sh class_env - >>| Decl_class.to_class_type)) - | _ -> None - end - | _ -> - (* This class lives in PHP land *) - None - -and class_is_abstract (c : Shallow_decl_defs.shallow_class) : bool = - match c.sc_kind with - | Ast_defs.Cabstract - | Ast_defs.Cinterface - | Ast_defs.Ctrait - | Ast_defs.Cenum -> - true - | _ -> false - -(* When all type constants have been inherited and declared, this step synthesizes - * the defaults of abstract type constants into concrete type constants. *) -and synthesize_defaults - (k : string) - (tc : Typing_defs.typeconst_type) - ((typeconsts, consts) : - Typing_defs.typeconst_type SMap.t * Typing_defs.class_const SMap.t) : - Typing_defs.typeconst_type SMap.t * Typing_defs.class_const SMap.t = - match tc.ttc_abstract with - | TCAbstract (Some default) -> - let concrete = - { - tc with - ttc_abstract = TCConcrete; - ttc_constraint = None; - ttc_type = Some default; - } - in - let typeconsts = SMap.add k concrete typeconsts in - (* OCaml 4.06 has an update method that makes this operation much more ergonomic *) - let constant = SMap.find_opt k consts in - let consts = - Option.value_map constant ~default:consts ~f:(fun c -> - SMap.add k { c with cc_abstract = false } consts) - in - (typeconsts, consts) - | _ -> (typeconsts, consts) - -and class_decl - ~(sh : SharedMem.uses) - (ctx : Provider_context.t) - (c : Shallow_decl_defs.shallow_class) : Decl_defs.decl_class_type = - let is_abstract = class_is_abstract c in - let const = Attrs.mem SN.UserAttributes.uaConst c.sc_user_attributes in - let (_p, cls_name) = c.sc_name in - let class_dep = Dep.Class cls_name in - let env = { Decl_env.mode = c.sc_mode; droot = Some class_dep; ctx } in - let inherited = Decl_inherit.make env c in - let props = inherited.Decl_inherit.ih_props in - let props = - List.fold_left ~f:(prop_decl ~write_shmem:true c) ~init:props c.sc_props - in - let m = inherited.Decl_inherit.ih_methods in - let (m, condition_types) = - List.fold_left - ~f:(method_decl_acc ~write_shmem:true ~is_static:false c) - ~init:(m, SSet.empty) - c.sc_methods - in - let consts = inherited.Decl_inherit.ih_consts in - let consts = - List.fold_left ~f:(class_const_fold c) ~init:consts c.sc_consts - in - let consts = SMap.add SN.Members.mClass (class_class_decl c.sc_name) consts in - let typeconsts = inherited.Decl_inherit.ih_typeconsts in - let (typeconsts, consts) = - List.fold_left - c.sc_typeconsts - ~f:(typeconst_fold c) - ~init:(typeconsts, consts) - in - let (typeconsts, consts) = - if Ast_defs.(equal_class_kind c.sc_kind Cnormal) then - SMap.fold synthesize_defaults typeconsts (typeconsts, consts) - else - (typeconsts, consts) - in - let pu_enums = inherited.Decl_inherit.ih_pu_enums in - let pu_enums = - List.fold_left c.sc_pu_enums ~f:(pu_enum_fold cls_name) ~init:pu_enums - in - let sclass_var = static_prop_decl ~write_shmem:true c in - let sprops = inherited.Decl_inherit.ih_sprops in - let sprops = List.fold_left c.sc_sprops ~f:sclass_var ~init:sprops in - let sm = inherited.Decl_inherit.ih_smethods in - let (sm, condition_types) = - List.fold_left - c.sc_static_methods - ~f:(method_decl_acc ~write_shmem:true ~is_static:true c) - ~init:(sm, condition_types) - in - let parent_cstr = inherited.Decl_inherit.ih_cstr in - let cstr = constructor_decl ~sh parent_cstr c in - let has_concrete_cstr = - match fst cstr with - | None -> false - | Some elt when get_elt_abstract elt -> false - | _ -> true - in - let impl = c.sc_extends @ c.sc_implements @ c.sc_uses in - let impl = - match - List.find c.sc_methods ~f:(fun sm -> - String.equal (snd sm.sm_name) SN.Members.__toString) - with - | Some { sm_name = (pos, _); _ } - when String.( <> ) cls_name SN.Classes.cStringish -> - (* HHVM implicitly adds Stringish interface for every class/iface/trait - * with a __toString method; "string" also implements this interface *) - (* Declare Stringish and parents if not already declared *) - let class_env = { ctx; stack = SSet.empty } in - let ty = - mk (Reason.Rhint pos, Tapply ((pos, SN.Classes.cStringish), [])) - in - let (_ : Typing_defs.class_type option) = - class_type_decl ~sh class_env ty - in - ty :: impl - | _ -> impl - in - let impl = List.map impl (get_implements env) in - let impl = List.fold_right impl ~f:(SMap.fold SMap.add) ~init:SMap.empty in - let (extends, xhp_attr_deps, ext_strict) = - get_class_parents_and_traits env c - in - let (req_ancestors, req_ancestors_extends) = - Decl_requirements.get_class_requirements env c - in - (* Interfaces IDisposable and IAsyncDisposable are *disposable types*, as - * are any classes that implement either of these interfaces, directly or - * indirectly. Also treat any trait that *requires* extension or - * implementation of a disposable class as disposable itself. - *) - let is_disposable_class_name cls_name = - String.equal cls_name SN.Classes.cIDisposable - || String.equal cls_name SN.Classes.cIAsyncDisposable - in - let is_disposable = - is_disposable_class_name cls_name - || SMap.exists (fun n _ -> is_disposable_class_name n) impl - || List.exists - (c.sc_req_extends @ c.sc_req_implements) - (is_disposable_type env) - in - (* If this class is disposable then we require that any extended class or - * trait that is used, is also disposable, in order that escape analysis - * has been applied on the $this parameter. - *) - let ext_strict = - List.fold_left c.sc_uses ~f:(trait_exists env) ~init:ext_strict - in - let enum = c.sc_enum_type in - let enum_inner_ty = SMap.find_opt SN.FB.tInner typeconsts in - let consts = - Decl_enum.rewrite_class - c.sc_name - enum - Option.(enum_inner_ty >>= fun t -> t.ttc_type) - (fun x -> SMap.find_opt x impl) - consts - in - let has_own_cstr = has_concrete_cstr && Option.is_some c.sc_constructor in - let deferred_members = - if shallow_decl_enabled ctx then - SSet.empty - else - snd (Decl_init_check.class_ ~has_own_cstr env c) - in - let sealed_whitelist = get_sealed_whitelist c in - let tc = - { - dc_final = c.sc_final; - dc_const = const; - dc_abstract = is_abstract; - dc_need_init = has_concrete_cstr; - dc_deferred_init_members = deferred_members; - dc_members_fully_known = ext_strict; - dc_kind = c.sc_kind; - dc_is_xhp = c.sc_is_xhp; - dc_has_xhp_keyword = c.sc_has_xhp_keyword; - dc_is_disposable = is_disposable; - dc_name = snd c.sc_name; - dc_pos = fst c.sc_name; - dc_tparams = c.sc_tparams; - dc_where_constraints = c.sc_where_constraints; - dc_substs = inherited.Decl_inherit.ih_substs; - dc_consts = consts; - dc_typeconsts = typeconsts; - dc_pu_enums = pu_enums; - dc_props = props; - dc_sprops = sprops; - dc_methods = m; - dc_smethods = sm; - dc_construct = cstr; - dc_ancestors = impl; - dc_extends = extends; - dc_sealed_whitelist = sealed_whitelist; - dc_xhp_attr_deps = xhp_attr_deps; - dc_req_ancestors = req_ancestors; - dc_req_ancestors_extends = req_ancestors_extends; - dc_enum_type = enum; - dc_decl_errors = None; - dc_condition_types = condition_types; - } - in - SMap.iter - begin - fun x _ -> - Typing_deps.add_idep class_dep (Dep.Class x) - end - impl; - tc - -and get_sealed_whitelist (c : Shallow_decl_defs.shallow_class) : SSet.t option = - match Attributes.find SN.UserAttributes.uaSealed c.sc_user_attributes with - | None -> None - | Some { ua_classname_params; _ } -> Some (SSet.of_list ua_classname_params) - -and get_implements (env : Decl_env.env) (ht : Typing_defs.decl_ty) : - Typing_defs.decl_ty SMap.t = - let (_r, (_p, c), paraml) = Decl_utils.unwrap_class_type ht in - let class_ = Decl_env.get_class_dep env c in - match class_ with - | None -> - (* The class lives in PHP land *) - SMap.singleton c ht - | Some class_ -> - let subst = Inst.make_subst class_.dc_tparams paraml in - let sub_implements = - SMap.map (fun ty -> Inst.instantiate subst ty) class_.dc_ancestors - in - SMap.add c ht sub_implements - -and trait_exists (env : Decl_env.env) (acc : bool) (trait : Typing_defs.decl_ty) - : bool = - match get_node trait with - | Tapply ((_, trait), _) -> - let class_ = Decl_env.get_class_dep env trait in - (match class_ with - | None -> false - | Some _class -> acc) - | _ -> false - -and constructor_decl - ~(sh : SharedMem.uses) - ((pcstr, pconsist) : Decl_defs.element option * Typing_defs.consistent_kind) - (class_ : Shallow_decl_defs.shallow_class) : - Decl_defs.element option * Typing_defs.consistent_kind = - let SharedMem.Uses = sh in - (* constructors in children of class_ must be consistent? *) - let cconsist = - if class_.sc_final then - FinalClass - else if - Attrs.mem - SN.UserAttributes.uaConsistentConstruct - class_.sc_user_attributes - then - ConsistentConstruct - else - Inconsistent - in - let cstr = - match class_.sc_constructor with - | None -> pcstr - | Some method_ -> build_constructor ~write_shmem:true class_ method_ - in - (cstr, Decl_utils.coalesce_consistent pconsist cconsist) - -and build_constructor - ~(write_shmem : bool) - (class_ : Shallow_decl_defs.shallow_class) - (method_ : Shallow_decl_defs.shallow_method) : Decl_defs.element option = - let (_, class_name) = class_.sc_name in - let vis = visibility class_name method_.sm_visibility in - let pos = fst method_.sm_name in - let cstr = - { - elt_flags = - make_ce_flags - ~xhp_attr:None - ~final:method_.sm_final - ~abstract:method_.sm_abstract - ~lateinit:false - ~const:false - ~lsb:false - ~memoizelsb:false - ~synthesized:false - ~override:false - ~dynamicallycallable:false; - elt_visibility = vis; - elt_origin = class_name; - elt_reactivity = None; - elt_deprecated = method_.sm_deprecated; - } - in - let fe = - { - fe_pos = pos; - fe_deprecated = method_.sm_deprecated; - fe_type = method_.sm_type; - fe_php_std_lib = false; - } - in - if write_shmem then Decl_heap.Constructors.add class_name fe; - Some cstr - -and class_const_fold - (c : Shallow_decl_defs.shallow_class) - (acc : Typing_defs.class_const SMap.t) - (scc : Shallow_decl_defs.shallow_class_const) : - Typing_defs.class_const SMap.t = - let c_name = snd c.sc_name in - let cc = - { - cc_synthesized = false; - cc_abstract = scc.scc_abstract; - cc_pos = fst scc.scc_name; - cc_type = scc.scc_type; - cc_origin = c_name; - } - in - let acc = SMap.add (snd scc.scc_name) cc acc in - acc - -(* Every class, interface, and trait implicitly defines a ::class to - * allow accessing its fully qualified name as a string *) -and class_class_decl (class_id : Ast_defs.id) : Typing_defs.class_const = - let (pos, name) = class_id in - let reason = Reason.Rclass_class (pos, name) in - let classname_ty = - mk (reason, Tapply ((pos, SN.Classes.cClassname), [mk (reason, Tthis)])) - in - { - cc_abstract = false; - cc_pos = pos; - cc_synthesized = true; - cc_type = classname_ty; - cc_origin = name; - } - -and prop_decl - ~(write_shmem : bool) - (c : Shallow_decl_defs.shallow_class) - (acc : Decl_defs.element SMap.t) - (sp : Shallow_decl_defs.shallow_prop) : Decl_defs.element SMap.t = - let (sp_pos, sp_name) = sp.sp_name in - let ty = - match sp.sp_type with - | None -> mk (Reason.Rwitness sp_pos, Typing_defs.make_tany ()) - | Some ty' -> ty' - in - let vis = visibility (snd c.sc_name) sp.sp_visibility in - let elt = - { - elt_flags = - make_ce_flags - ~xhp_attr:sp.sp_xhp_attr - ~final:true - ~lsb:false - ~synthesized:false - ~override:false - ~memoizelsb:false - ~const:sp.sp_const - ~lateinit:sp.sp_lateinit - ~abstract:sp.sp_abstract - ~dynamicallycallable:false; - elt_visibility = vis; - elt_origin = snd c.sc_name; - elt_reactivity = None; - elt_deprecated = None; - } - in - if write_shmem then Decl_heap.Props.add (elt.elt_origin, sp_name) ty; - let acc = SMap.add sp_name elt acc in - acc - -and static_prop_decl - ~(write_shmem : bool) - (c : Shallow_decl_defs.shallow_class) - (acc : Decl_defs.element SMap.t) - (sp : Shallow_decl_defs.shallow_prop) : Decl_defs.element SMap.t = - let (sp_pos, sp_name) = sp.sp_name in - let ty = - match sp.sp_type with - | None -> mk (Reason.Rwitness sp_pos, Typing_defs.make_tany ()) - | Some ty' -> ty' - in - let vis = visibility (snd c.sc_name) sp.sp_visibility in - let elt = - { - elt_flags = - make_ce_flags - ~xhp_attr:sp.sp_xhp_attr - ~final:true - ~const:sp.sp_const - ~lateinit:sp.sp_lateinit - ~lsb:sp.sp_lsb - ~override:false - ~memoizelsb:false - ~abstract:sp.sp_abstract - ~synthesized:false - ~dynamicallycallable:false; - elt_visibility = vis; - elt_origin = snd c.sc_name; - elt_reactivity = None; - elt_deprecated = None; - } - in - if write_shmem then Decl_heap.StaticProps.add (elt.elt_origin, sp_name) ty; - let acc = SMap.add sp_name elt acc in - acc - -and visibility (cid : string) (visibility : Aast_defs.visibility) : - Typing_defs.visibility = - match visibility with - | Public -> Vpublic - | Protected -> Vprotected cid - | Private -> Vprivate cid - -(* each concrete type constant T = implicitly defines a -class constant with the same name which is TypeStructure *) -and typeconst_structure - (c : Shallow_decl_defs.shallow_class) - (stc : Shallow_decl_defs.shallow_typeconst) : Typing_defs.class_const = - let pos = fst stc.stc_name in - let r = Reason.Rwitness pos in - let tsid = (pos, SN.FB.cTypeStructure) in - let ts_ty = - mk (r, Tapply (tsid, [mk (r, Taccess (mk (r, Tthis), [stc.stc_name]))])) - in - let abstract = - match stc.stc_abstract with - | TCAbstract _ -> true - | _ -> false - in - { - cc_abstract = abstract; - cc_pos = pos; - cc_synthesized = true; - cc_type = ts_ty; - cc_origin = snd c.sc_name; - } - -and typeconst_fold - (c : Shallow_decl_defs.shallow_class) - (acc : Typing_defs.typeconst_type SMap.t * Typing_defs.class_const SMap.t) - (stc : Shallow_decl_defs.shallow_typeconst) : - Typing_defs.typeconst_type SMap.t * Typing_defs.class_const SMap.t = - let (typeconsts, consts) = acc in - match c.sc_kind with - | Ast_defs.Ctrait - | Ast_defs.Cenum -> - acc - | Ast_defs.Cinterface - | Ast_defs.Cabstract - | Ast_defs.Cnormal -> - let name = snd stc.stc_name in - let c_name = snd c.sc_name in - let ts = typeconst_structure c stc in - let consts = SMap.add name ts consts in - let ptc_opt = SMap.find_opt name typeconsts in - let enforceable = - (* Without the positions, this is a simple OR, but this way allows us to - * report the position of the <<__Enforceable>> attribute to the user *) - if snd stc.stc_enforceable then - stc.stc_enforceable - else - match ptc_opt with - | Some ptc -> ptc.ttc_enforceable - | None -> (Pos.none, false) - in - let reifiable = - if Option.is_some stc.stc_reifiable then - stc.stc_reifiable - else - Option.bind ptc_opt (fun ptc -> ptc.ttc_reifiable) - in - let tc = - { - ttc_abstract = stc.stc_abstract; - ttc_name = stc.stc_name; - ttc_constraint = stc.stc_constraint; - ttc_type = stc.stc_type; - ttc_origin = c_name; - ttc_enforceable = enforceable; - ttc_reifiable = reifiable; - } - in - let typeconsts = SMap.add (snd stc.stc_name) tc typeconsts in - (typeconsts, consts) - -and method_decl_acc - ~(write_shmem : bool) - ~(is_static : bool) - (c : Shallow_decl_defs.shallow_class) - ((acc, condition_types) : Decl_defs.element SMap.t * SSet.t) - (m : Shallow_decl_defs.shallow_method) : Decl_defs.element SMap.t * SSet.t = - (* If method doesn't override anything but has the <<__Override>> attribute, then - * set the override flag in ce_flags and let typing emit an appropriate error *) - let check_override = m.sm_override && not (SMap.mem (snd m.sm_name) acc) in - let (pos, id) = m.sm_name in - let get_reactivity t = - match get_node t with - | Tfun { ft_reactive; _ } -> ft_reactive - | _ -> Local None - in - let condition_types = - match get_reactivity m.sm_type with - | Pure (Some ty) - | Reactive (Some ty) - | Shallow (Some ty) - | Local (Some ty) -> - begin - match get_node ty with - | Tapply ((_, cls), []) -> SSet.add cls condition_types - | _ -> condition_types - end - | _ -> condition_types - in - let vis = - match (SMap.find_opt id acc, m.sm_visibility) with - | (Some { elt_visibility = Vprotected _ as parent_vis; _ }, Protected) -> - parent_vis - | _ -> visibility (snd c.sc_name) m.sm_visibility - in - let elt = - { - elt_flags = - make_ce_flags - ~xhp_attr:None - ~final:m.sm_final - ~abstract:m.sm_abstract - ~override:check_override - ~synthesized:false - ~lsb:false - ~memoizelsb:false - ~const:false - ~lateinit:false - ~dynamicallycallable:m.sm_dynamicallycallable; - elt_visibility = vis; - elt_origin = snd c.sc_name; - elt_reactivity = m.sm_reactivity; - elt_deprecated = m.sm_deprecated; - } - in - let fe = - { - fe_pos = pos; - fe_deprecated = None; - fe_type = m.sm_type; - fe_php_std_lib = false; - } - in - if write_shmem then - if is_static then - Decl_heap.StaticMethods.add (elt.elt_origin, id) fe - else - Decl_heap.Methods.add (elt.elt_origin, id) fe; - let acc = SMap.add id elt acc in - (acc, condition_types) - -(*****************************************************************************) -(* Dealing with records *) -(*****************************************************************************) - -let record_def_decl (rd : Nast.record_def) : Typing_defs.record_def_type = - let extends = - match rd.rd_extends with - (* The only valid type hint for record parents is a record - name. Records do not support generics. *) - | Some (_, Happly (id, [])) -> Some id - | _ -> None - in - let fields = - List.map rd.rd_fields ~f:(fun (id, _, default) -> - match default with - | Some _ -> (id, Typing_defs.HasDefaultValue) - | None -> (id, ValueRequired)) - in - { - rdt_name = rd.rd_name; - rdt_extends = extends; - rdt_fields = fields; - rdt_abstract = rd.rd_abstract; - rdt_pos = rd.rd_span; - } - -let type_record_def_naming_and_decl - ~(write_shmem : bool) (ctx : Provider_context.t) (rd : Nast.record_def) : - Typing_defs.record_def_type = - let rd = Errors.ignore_ (fun () -> Naming.record_def ctx rd) in - let tdecl = record_def_decl rd in - record_record_def (snd rd.rd_name); - if write_shmem then Decl_heap.RecordDefs.add (snd rd.rd_name) tdecl; - tdecl - -let record_def_decl_if_missing - ~(sh : SharedMem.uses) (ctx : Provider_context.t) (rd : Nast.record_def) : - unit = - let SharedMem.Uses = sh in - let (_, rdid) = rd.rd_name in - if not (Decl_heap.RecordDefs.mem rdid) then - let (_ : Typing_defs.record_def_type) = - type_record_def_naming_and_decl ~write_shmem:true ctx rd - in - () - -(*****************************************************************************) -(* Dealing with typedefs *) -(*****************************************************************************) - -let rec type_typedef_decl_if_missing - ~(sh : SharedMem.uses) (ctx : Provider_context.t) (typedef : Nast.typedef) : - unit = - let SharedMem.Uses = sh in - let (_, name) = typedef.t_name in - if not (Decl_heap.Typedefs.mem name) then - let (_ : typedef_type) = - type_typedef_naming_and_decl ~write_shmem:true ctx typedef - in - () - -and typedef_decl (ctx : Provider_context.t) (tdef : Nast.typedef) : - Typing_defs.typedef_type = - let { - t_annotation = (); - t_name = (td_pos, tid); - t_tparams = params; - t_constraint = tcstr; - t_kind = concrete_type; - t_user_attributes = _; - t_namespace = _; - t_mode = mode; - t_vis = td_vis; - t_span = _; - t_emit_id = _; - } = - tdef - in - let dep = Typing_deps.Dep.Class tid in - let env = { Decl_env.mode; droot = Some dep; ctx } in - let td_tparams = List.map params (type_param env) in - let td_type = Decl_hint.hint env concrete_type in - let td_constraint = Option.map tcstr (Decl_hint.hint env) in - { td_vis; td_tparams; td_constraint; td_type; td_pos } - -and type_typedef_naming_and_decl - ~(write_shmem : bool) (ctx : Provider_context.t) (tdef : Nast.typedef) : - Typing_defs.typedef_type = - let tdef = Errors.ignore_ (fun () -> Naming.typedef ctx tdef) in - let tdecl = typedef_decl ctx tdef in - record_typedef (snd tdef.t_name); - if write_shmem then Decl_heap.Typedefs.add (snd tdef.t_name) tdecl; - tdecl - -(*****************************************************************************) -(* Global constants *) -(*****************************************************************************) - -let const_decl (ctx : Provider_context.t) (cst : Nast.gconst) : - Typing_defs.decl_ty = - let (cst_pos, _cst_name) = cst.cst_name in - let dep = Dep.GConst (snd cst.cst_name) in - let env = { Decl_env.mode = cst.cst_mode; droot = Some dep; ctx } in - match cst.cst_type with - | Some h -> Decl_hint.hint env h - | None -> - (match Decl_utils.infer_const cst.cst_value with - | Some tprim -> mk (Reason.Rwitness (fst cst.cst_value), Tprim tprim) - (* A NAST check will take care of rejecting constants that have neither - * an initializer nor a literal initializer *) - | None -> mk (Reason.Rwitness cst_pos, Typing_defs.make_tany ())) - -let iconst_decl - ~(write_shmem : bool) (ctx : Provider_context.t) (cst : Nast.gconst) : - Typing_defs.decl_ty = - let cst = Errors.ignore_ (fun () -> Naming.global_const ctx cst) in - let hint_ty = const_decl ctx cst in - record_const (snd cst.cst_name); - if write_shmem then Decl_heap.GConsts.add (snd cst.cst_name) hint_ty; - hint_ty - -(*****************************************************************************) -let rec name_and_declare_types_program - ~(sh : SharedMem.uses) (ctx : Provider_context.t) (prog : Nast.program) : - unit = - List.iter prog (fun def -> - match def with - | Namespace (_, prog) -> name_and_declare_types_program ~sh ctx prog - | NamespaceUse _ -> () - | SetNamespaceEnv _ -> () - | FileAttributes _ -> () - | Fun f -> - let (_ : fun_elt) = ifun_decl ~write_shmem:true ctx f in - () - | Class c -> - let class_env = { ctx; stack = SSet.empty } in - let (_ : decl_class_type option) = - class_decl_if_missing ~sh class_env c - in - () - | RecordDef rd -> record_def_decl_if_missing ~sh ctx rd - | Typedef typedef -> type_typedef_decl_if_missing ~sh ctx typedef - | Stmt _ -> () - | Constant cst -> - let (_ : decl_ty) = iconst_decl ~write_shmem:true ctx cst in - ()) - -let make_env - ~(sh : SharedMem.uses) (ctx : Provider_context.t) (fn : Relative_path.t) : - unit = - let ast = Ast_provider.get_ast ctx fn in - name_and_declare_types_program ~sh ctx ast; - () - -let err_not_found (file : Relative_path.t) (name : string) : 'a = - let err_str = - Printf.sprintf "%s not found in %s" name (Relative_path.to_absolute file) - in - raise (Decl_not_found err_str) - -let declare_class_in_file - ~(sh : SharedMem.uses) - (ctx : Provider_context.t) - (file : Relative_path.t) - (name : string) : Decl_defs.decl_class_type option = - match Ast_provider.find_class_in_file ctx file name with - | Some cls -> - let class_env = { ctx; stack = SSet.empty } in - class_decl_if_missing ~sh class_env cls - | None -> err_not_found file name - -let declare_fun_in_file - ~(write_shmem : bool) - (ctx : Provider_context.t) - (file : Relative_path.t) - (name : string) : Typing_defs.fun_elt = - match Ast_provider.find_fun_in_file ctx file name with - | Some f -> ifun_decl ~write_shmem ctx f - | None -> err_not_found file name - -let declare_record_def_in_file - ~(write_shmem : bool) - (ctx : Provider_context.t) - (file : Relative_path.t) - (name : string) : Typing_defs.record_def_type = - match Ast_provider.find_record_def_in_file ctx file name with - | Some rd -> type_record_def_naming_and_decl ~write_shmem ctx rd - | None -> err_not_found file name - -let declare_typedef_in_file - ~(write_shmem : bool) - (ctx : Provider_context.t) - (file : Relative_path.t) - (name : string) : Typing_defs.typedef_type = - match Ast_provider.find_typedef_in_file ctx file name with - | Some t -> type_typedef_naming_and_decl ~write_shmem ctx t - | None -> err_not_found file name - -let declare_const_in_file - ~(write_shmem : bool) - (ctx : Provider_context.t) - (file : Relative_path.t) - (name : string) : Typing_defs.decl_ty = - match Ast_provider.find_gconst_in_file ctx file name with - | Some cst -> iconst_decl ~write_shmem ctx cst - | None -> err_not_found file name +(* + * Copyright (c) 2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the "hack" directory of this source tree. + * + *) + +open Hh_prelude + +let tracked_names : FileInfo.names option ref = ref None + +let start_tracking () : unit = tracked_names := Some FileInfo.empty_names + +let record_fun (s : string) : unit = + match !tracked_names with + | None -> () + | Some names -> + tracked_names := + Some FileInfo.{ names with n_funs = SSet.add s names.n_funs } + +let record_class (s : string) : unit = + match !tracked_names with + | None -> () + | Some names -> + tracked_names := + Some FileInfo.{ names with n_classes = SSet.add s names.n_classes } + +let record_record_def (s : string) : unit = + match !tracked_names with + | None -> () + | Some names -> + tracked_names := + Some + FileInfo.{ names with n_record_defs = SSet.add s names.n_record_defs } + +let record_typedef (s : string) : unit = + match !tracked_names with + | None -> () + | Some names -> + tracked_names := + Some FileInfo.{ names with n_types = SSet.add s names.n_types } + +let record_const (s : string) : unit = + match !tracked_names with + | None -> () + | Some names -> + tracked_names := + Some FileInfo.{ names with n_consts = SSet.add s names.n_consts } + +let stop_tracking () : FileInfo.names = + let res = + match !tracked_names with + | None -> + Hh_logger.log + "Warning: called Decl.stop_tracking without corresponding start_tracking"; + FileInfo.empty_names + | Some names -> names + in + tracked_names := None; + res + +(*****************************************************************************) +let rec name_and_declare_types_program + ~(sh : SharedMem.uses) (ctx : Provider_context.t) (prog : Nast.program) : + unit = + let open Aast in + List.iter prog (fun def -> + match def with + | Namespace (_, prog) -> name_and_declare_types_program ~sh ctx prog + | NamespaceUse _ -> () + | SetNamespaceEnv _ -> () + | FileAttributes _ -> () + | Fun f -> + let (_ : string * Typing_defs.fun_elt) = + Decl_nast.fun_naming_and_decl ~write_shmem:true ctx f + in + () + | Class c -> + let class_env = Decl_folded_class.{ ctx; stack = SSet.empty } in + let (_ : (string * Decl_defs.decl_class_type) option) = + Decl_folded_class.class_decl_if_missing ~sh class_env c + in + () + | RecordDef rd -> Decl_nast.record_def_decl_if_missing ~sh ctx rd + | Typedef typedef -> Decl_nast.typedef_decl_if_missing ~sh ctx typedef + | Stmt _ -> () + | Constant cst -> + let (_ : string * Typing_defs.decl_ty) = + Decl_nast.const_naming_and_decl ~write_shmem:true ctx cst + in + ()) + +let make_env + ~(sh : SharedMem.uses) (ctx : Provider_context.t) (fn : Relative_path.t) : + unit = + let ast = Ast_provider.get_ast ctx fn in + name_and_declare_types_program ~sh ctx ast; + () + +let err_not_found (file : Relative_path.t) (name : string) : 'a = + let err_str = + Printf.sprintf "%s not found in %s" name (Relative_path.to_absolute file) + in + raise (Decl_defs.Decl_not_found err_str) + +let declare_class_in_file + ~(sh : SharedMem.uses) + (ctx : Provider_context.t) + (file : Relative_path.t) + (name : string) : Decl_defs.decl_class_type option = + match Ast_provider.find_class_in_file ctx file name with + | Some cls -> + let class_env = Decl_folded_class.{ ctx; stack = SSet.empty } in + (match Decl_folded_class.class_decl_if_missing ~sh class_env cls with + | None -> None + | Some (name, decl) -> + record_class name; + Some decl) + | None -> err_not_found file name + +let declare_fun_in_file + ~(write_shmem : bool) + (ctx : Provider_context.t) + (file : Relative_path.t) + (name : string) : Typing_defs.fun_elt = + match Ast_provider.find_fun_in_file ctx file name with + | Some f -> + let (name, decl) = Decl_nast.fun_naming_and_decl ~write_shmem ctx f in + record_fun name; + decl + | None -> err_not_found file name + +let declare_record_def_in_file + ~(write_shmem : bool) + (ctx : Provider_context.t) + (file : Relative_path.t) + (name : string) : Typing_defs.record_def_type = + match Ast_provider.find_record_def_in_file ctx file name with + | Some rd -> + let (name, decl) = + Decl_nast.record_def_naming_and_decl ~write_shmem ctx rd + in + record_record_def name; + decl + | None -> err_not_found file name + +let declare_typedef_in_file + ~(write_shmem : bool) + (ctx : Provider_context.t) + (file : Relative_path.t) + (name : string) : Typing_defs.typedef_type = + match Ast_provider.find_typedef_in_file ctx file name with + | Some t -> + let (name, decl) = Decl_nast.typedef_naming_and_decl ~write_shmem ctx t in + record_typedef name; + decl + | None -> err_not_found file name + +let declare_const_in_file + ~(write_shmem : bool) + (ctx : Provider_context.t) + (file : Relative_path.t) + (name : string) : Typing_defs.decl_ty = + match Ast_provider.find_gconst_in_file ctx file name with + | Some cst -> + let (name, decl) = Decl_nast.const_naming_and_decl ~write_shmem ctx cst in + record_const name; + decl + | None -> err_not_found file name diff --git a/hphp/hack/src/decl/decl.mli b/hphp/hack/src/decl/decl.mli index cb03e6a337c..45f12f0e9b7 100644 --- a/hphp/hack/src/decl/decl.mli +++ b/hphp/hack/src/decl/decl.mli @@ -8,24 +8,18 @@ *) (* - * This function works by side effects. It is adding in the - * Naming_table the nast produced from the ast passed as a parameter - * (the SharedMem must thus have been initialized via SharedMem.init - * prior to calling this function). It also assumes the Parser_heap - * has been previously populated. It also adds dependencies - * via Typing_deps.add_idep. It finally adds all the typing information - * about classes, functions, typedefs, respectively in the globals - * in Typing_env.Class, Typing_env.Fun, and Typing_env.Typedef. + * This function works by side effects. It is adding in the Naming_table the + * nast produced from the filename passed as a parameter (the SharedMem must + * thus have been initialized via SharedMem.init prior to calling this + * function). Its performance benefits if the Parser_heap has been previously + * populated. It also adds dependencies via Typing_deps.add_idep. It finally + * adds all the typing information about classes, functions, typedefs, + * respectively in the globals in Typing_env.Class, Typing_env.Fun, and + * Typing_env.Typedef. *) -val name_and_declare_types_program : - sh:SharedMem.uses -> Provider_context.t -> Nast.program -> unit - val make_env : sh:SharedMem.uses -> Provider_context.t -> Relative_path.t -> unit -val fun_decl_in_env : - Decl_env.env -> is_lambda:bool -> Nast.fun_ -> Typing_defs.fun_elt - val declare_const_in_file : write_shmem:bool -> Provider_context.t -> diff --git a/hphp/hack/src/decl/decl.ml b/hphp/hack/src/decl/decl_folded_class.ml similarity index 71% copy from hphp/hack/src/decl/decl.ml copy to hphp/hack/src/decl/decl_folded_class.ml index 7a380e06756..01af5498278 100644 --- a/hphp/hack/src/decl/decl.ml +++ b/hphp/hack/src/decl/decl_folded_class.ml @@ -8,14 +8,13 @@ *) (*****************************************************************************) -(* Module used to declare the types. +(* Module used to declare class types. * For each class we want to build a complete type, that is the type of * the methods defined in the class plus everything that was inherited. *) (*****************************************************************************) open Hh_prelude open Decl_defs -open Decl_fun_utils open Aast open Shallow_decl_defs open Typing_defs @@ -25,58 +24,6 @@ module Inst = Decl_instantiate module Attrs = Typing_defs.Attributes module SN = Naming_special_names -let tracked_names : FileInfo.names option ref = ref None - -let start_tracking () : unit = tracked_names := Some FileInfo.empty_names - -let record_fun (s : string) : unit = - match !tracked_names with - | None -> () - | Some names -> - tracked_names := - Some FileInfo.{ names with n_funs = SSet.add s names.n_funs } - -let record_class (s : string) : unit = - match !tracked_names with - | None -> () - | Some names -> - tracked_names := - Some FileInfo.{ names with n_classes = SSet.add s names.n_classes } - -let record_record_def (s : string) : unit = - match !tracked_names with - | None -> () - | Some names -> - tracked_names := - Some - FileInfo.{ names with n_record_defs = SSet.add s names.n_record_defs } - -let record_typedef (s : string) : unit = - match !tracked_names with - | None -> () - | Some names -> - tracked_names := - Some FileInfo.{ names with n_types = SSet.add s names.n_types } - -let record_const (s : string) : unit = - match !tracked_names with - | None -> () - | Some names -> - tracked_names := - Some FileInfo.{ names with n_consts = SSet.add s names.n_consts } - -let stop_tracking () : FileInfo.names = - let res = - match !tracked_names with - | None -> - Hh_logger.log - "Warning: called Decl.stop_tracking without corresponding start_tracking"; - FileInfo.empty_names - | Some names -> names - in - tracked_names := None; - res - (*****************************************************************************) (* Checking that the kind of a class is compatible with its parent * For example, a class cannot extend an interface, an interface cannot @@ -259,99 +206,6 @@ let get_class_parents_and_traits in (parents, xhp_parents, is_complete) -(*****************************************************************************) -(* Section declaring the type of a function *) -(*****************************************************************************) - -let rec ifun_decl - ~(write_shmem : bool) (ctx : Provider_context.t) (f : Nast.fun_) : - Typing_defs.fun_elt = - let f = Errors.ignore_ (fun () -> Naming.fun_ ctx f) in - let fe = fun_decl ctx f in - if write_shmem then Decl_heap.Funs.add (snd f.f_name) fe; - fe - -and fun_decl (ctx : Provider_context.t) (f : Nast.fun_) : Typing_defs.fun_elt = - let dep = Dep.Fun (snd f.f_name) in - let env = { Decl_env.mode = f.f_mode; droot = Some dep; ctx } in - let fe = fun_decl_in_env env ~is_lambda:false f in - record_fun (snd f.f_name); - fe - -and fun_decl_in_env (env : Decl_env.env) ~(is_lambda : bool) (f : Nast.fun_) : - Typing_defs.fun_elt = - check_params f.f_params; - let reactivity = fun_reactivity env f.f_user_attributes in - let returns_mutable = fun_returns_mutable f.f_user_attributes in - let returns_void_to_rx = fun_returns_void_to_rx f.f_user_attributes in - let return_disposable = has_return_disposable_attribute f.f_user_attributes in - let params = make_params env ~is_lambda f.f_params in - let capability = - hint_to_type - ~is_lambda:false - ~default: - (Typing_make_type.default_capability (Reason.Rhint (fst f.f_name))) - env - (Reason.Rwitness (fst f.f_name)) - (hint_of_type_hint f.f_cap) - in - let ret_ty = - ret_from_fun_kind - ~is_lambda - env - (fst f.f_name) - f.f_fun_kind - (hint_of_type_hint f.f_ret) - in - let arity = - match f.f_variadic with - | FVvariadicArg param -> - assert param.param_is_variadic; - Fvariadic (make_param_ty env ~is_lambda param) - | FVellipsis p -> Fvariadic (make_ellipsis_param_ty p) - | FVnonVariadic -> Fstandard - in - let tparams = List.map f.f_tparams (type_param env) in - let where_constraints = - List.map f.f_where_constraints (where_constraint env) - in - let fe_deprecated = - Naming_attributes_deprecated.deprecated - ~kind:"function" - f.f_name - f.f_user_attributes - in - let fe_php_std_lib = - Naming_attributes.mem SN.UserAttributes.uaPHPStdLib f.f_user_attributes - in - let fe_type = - mk - ( Reason.Rwitness (fst f.f_name), - Tfun - { - ft_arity = arity; - ft_tparams = tparams; - ft_where_constraints = where_constraints; - ft_params = params; - ft_implicit_params = { capability }; - ft_ret = { et_type = ret_ty; et_enforced = false }; - ft_reactive = reactivity; - ft_flags = - make_ft_flags - f.f_fun_kind - (* Functions can't be mutable because they don't have "this" *) - None - ~returns_mutable - ~return_disposable - ~returns_void_to_rx; - } ) - in - { fe_pos = fst f.f_name; fe_type; fe_deprecated; fe_php_std_lib } - -(*****************************************************************************) -(* Section declaring the type of a class *) -(*****************************************************************************) - type class_env = { ctx: Provider_context.t; stack: SSet.t; @@ -433,7 +287,7 @@ let pu_enum_fold let rec class_decl_if_missing ~(sh : SharedMem.uses) (class_env : class_env) (c : Nast.class_) : - Decl_defs.decl_class_type option = + (string * Decl_defs.decl_class_type) option = let ((_, cid) as c_name) = c.c_name in if check_if_cyclic class_env c_name then None @@ -451,7 +305,7 @@ let rec class_decl_if_missing None else match Decl_heap.Classes.get cid with - | Some _ as class_ -> class_ + | Some class_ -> Some (cid, class_) | None -> (* Class elements are in memory if and only if the class itself is there. * Exiting before class declaration is ready would break this invariant *) @@ -463,7 +317,7 @@ and class_naming_and_decl ~(sh : SharedMem.uses) (class_env : class_env) (cid : string) - (c : Nast.class_) : Decl_defs.decl_class_type = + (c : Nast.class_) : string * Decl_defs.decl_class_type = let class_env = { class_env with stack = SSet.add cid class_env.stack } in let shallow_class = Shallow_classes_provider.decl class_env.ctx ~use_cache:false c @@ -474,17 +328,18 @@ and class_naming_and_decl class_decl ~sh class_env.ctx shallow_class) in let name = snd shallow_class.sc_name in - record_class name; let class_ = { tc with dc_decl_errors = Some errors } in Decl_heap.Classes.add name class_; - class_ + (name, class_) and class_parents_decl ~(sh : SharedMem.uses) (class_env : class_env) (c : Shallow_decl_defs.shallow_class) : unit = let class_type class_ = - let (_ : class_type option) = class_type_decl ~sh class_env class_ in + let (_ : Decl_defs.decl_class_type option) = + class_type_decl ~sh class_env class_ + in () in List.iter c.sc_extends class_type; @@ -512,7 +367,7 @@ and is_disposable_type (env : Decl_env.env) (hint : Typing_defs.decl_ty) : bool and class_type_decl ~(sh : SharedMem.uses) (class_env : class_env) (hint : Typing_defs.decl_ty) - : Typing_defs.class_type option = + : Decl_defs.decl_class_type option = match get_node hint with | Tapply ((_, cid), _) -> begin @@ -522,9 +377,7 @@ and class_type_decl let class_opt = Ast_provider.find_class_in_file class_env.ctx fn cid in Errors.run_in_context fn Errors.Decl (fun () -> Option.Monad_infix.( - class_opt - >>= class_decl_if_missing ~sh class_env - >>| Decl_class.to_class_type)) + class_opt >>= class_decl_if_missing ~sh class_env >>| snd)) | _ -> None end | _ -> @@ -644,7 +497,7 @@ and class_decl let ty = mk (Reason.Rhint pos, Tapply ((pos, SN.Classes.cStringish), [])) in - let (_ : Typing_defs.class_type option) = + let (_ : Decl_defs.decl_class_type option) = class_type_decl ~sh class_env ty in ty :: impl @@ -1091,210 +944,3 @@ and method_decl_acc Decl_heap.Methods.add (elt.elt_origin, id) fe; let acc = SMap.add id elt acc in (acc, condition_types) - -(*****************************************************************************) -(* Dealing with records *) -(*****************************************************************************) - -let record_def_decl (rd : Nast.record_def) : Typing_defs.record_def_type = - let extends = - match rd.rd_extends with - (* The only valid type hint for record parents is a record - name. Records do not support generics. *) - | Some (_, Happly (id, [])) -> Some id - | _ -> None - in - let fields = - List.map rd.rd_fields ~f:(fun (id, _, default) -> - match default with - | Some _ -> (id, Typing_defs.HasDefaultValue) - | None -> (id, ValueRequired)) - in - { - rdt_name = rd.rd_name; - rdt_extends = extends; - rdt_fields = fields; - rdt_abstract = rd.rd_abstract; - rdt_pos = rd.rd_span; - } - -let type_record_def_naming_and_decl - ~(write_shmem : bool) (ctx : Provider_context.t) (rd : Nast.record_def) : - Typing_defs.record_def_type = - let rd = Errors.ignore_ (fun () -> Naming.record_def ctx rd) in - let tdecl = record_def_decl rd in - record_record_def (snd rd.rd_name); - if write_shmem then Decl_heap.RecordDefs.add (snd rd.rd_name) tdecl; - tdecl - -let record_def_decl_if_missing - ~(sh : SharedMem.uses) (ctx : Provider_context.t) (rd : Nast.record_def) : - unit = - let SharedMem.Uses = sh in - let (_, rdid) = rd.rd_name in - if not (Decl_heap.RecordDefs.mem rdid) then - let (_ : Typing_defs.record_def_type) = - type_record_def_naming_and_decl ~write_shmem:true ctx rd - in - () - -(*****************************************************************************) -(* Dealing with typedefs *) -(*****************************************************************************) - -let rec type_typedef_decl_if_missing - ~(sh : SharedMem.uses) (ctx : Provider_context.t) (typedef : Nast.typedef) : - unit = - let SharedMem.Uses = sh in - let (_, name) = typedef.t_name in - if not (Decl_heap.Typedefs.mem name) then - let (_ : typedef_type) = - type_typedef_naming_and_decl ~write_shmem:true ctx typedef - in - () - -and typedef_decl (ctx : Provider_context.t) (tdef : Nast.typedef) : - Typing_defs.typedef_type = - let { - t_annotation = (); - t_name = (td_pos, tid); - t_tparams = params; - t_constraint = tcstr; - t_kind = concrete_type; - t_user_attributes = _; - t_namespace = _; - t_mode = mode; - t_vis = td_vis; - t_span = _; - t_emit_id = _; - } = - tdef - in - let dep = Typing_deps.Dep.Class tid in - let env = { Decl_env.mode; droot = Some dep; ctx } in - let td_tparams = List.map params (type_param env) in - let td_type = Decl_hint.hint env concrete_type in - let td_constraint = Option.map tcstr (Decl_hint.hint env) in - { td_vis; td_tparams; td_constraint; td_type; td_pos } - -and type_typedef_naming_and_decl - ~(write_shmem : bool) (ctx : Provider_context.t) (tdef : Nast.typedef) : - Typing_defs.typedef_type = - let tdef = Errors.ignore_ (fun () -> Naming.typedef ctx tdef) in - let tdecl = typedef_decl ctx tdef in - record_typedef (snd tdef.t_name); - if write_shmem then Decl_heap.Typedefs.add (snd tdef.t_name) tdecl; - tdecl - -(*****************************************************************************) -(* Global constants *) -(*****************************************************************************) - -let const_decl (ctx : Provider_context.t) (cst : Nast.gconst) : - Typing_defs.decl_ty = - let (cst_pos, _cst_name) = cst.cst_name in - let dep = Dep.GConst (snd cst.cst_name) in - let env = { Decl_env.mode = cst.cst_mode; droot = Some dep; ctx } in - match cst.cst_type with - | Some h -> Decl_hint.hint env h - | None -> - (match Decl_utils.infer_const cst.cst_value with - | Some tprim -> mk (Reason.Rwitness (fst cst.cst_value), Tprim tprim) - (* A NAST check will take care of rejecting constants that have neither - * an initializer nor a literal initializer *) - | None -> mk (Reason.Rwitness cst_pos, Typing_defs.make_tany ())) - -let iconst_decl - ~(write_shmem : bool) (ctx : Provider_context.t) (cst : Nast.gconst) : - Typing_defs.decl_ty = - let cst = Errors.ignore_ (fun () -> Naming.global_const ctx cst) in - let hint_ty = const_decl ctx cst in - record_const (snd cst.cst_name); - if write_shmem then Decl_heap.GConsts.add (snd cst.cst_name) hint_ty; - hint_ty - -(*****************************************************************************) -let rec name_and_declare_types_program - ~(sh : SharedMem.uses) (ctx : Provider_context.t) (prog : Nast.program) : - unit = - List.iter prog (fun def -> - match def with - | Namespace (_, prog) -> name_and_declare_types_program ~sh ctx prog - | NamespaceUse _ -> () - | SetNamespaceEnv _ -> () - | FileAttributes _ -> () - | Fun f -> - let (_ : fun_elt) = ifun_decl ~write_shmem:true ctx f in - () - | Class c -> - let class_env = { ctx; stack = SSet.empty } in - let (_ : decl_class_type option) = - class_decl_if_missing ~sh class_env c - in - () - | RecordDef rd -> record_def_decl_if_missing ~sh ctx rd - | Typedef typedef -> type_typedef_decl_if_missing ~sh ctx typedef - | Stmt _ -> () - | Constant cst -> - let (_ : decl_ty) = iconst_decl ~write_shmem:true ctx cst in - ()) - -let make_env - ~(sh : SharedMem.uses) (ctx : Provider_context.t) (fn : Relative_path.t) : - unit = - let ast = Ast_provider.get_ast ctx fn in - name_and_declare_types_program ~sh ctx ast; - () - -let err_not_found (file : Relative_path.t) (name : string) : 'a = - let err_str = - Printf.sprintf "%s not found in %s" name (Relative_path.to_absolute file) - in - raise (Decl_not_found err_str) - -let declare_class_in_file - ~(sh : SharedMem.uses) - (ctx : Provider_context.t) - (file : Relative_path.t) - (name : string) : Decl_defs.decl_class_type option = - match Ast_provider.find_class_in_file ctx file name with - | Some cls -> - let class_env = { ctx; stack = SSet.empty } in - class_decl_if_missing ~sh class_env cls - | None -> err_not_found file name - -let declare_fun_in_file - ~(write_shmem : bool) - (ctx : Provider_context.t) - (file : Relative_path.t) - (name : string) : Typing_defs.fun_elt = - match Ast_provider.find_fun_in_file ctx file name with - | Some f -> ifun_decl ~write_shmem ctx f - | None -> err_not_found file name - -let declare_record_def_in_file - ~(write_shmem : bool) - (ctx : Provider_context.t) - (file : Relative_path.t) - (name : string) : Typing_defs.record_def_type = - match Ast_provider.find_record_def_in_file ctx file name with - | Some rd -> type_record_def_naming_and_decl ~write_shmem ctx rd - | None -> err_not_found file name - -let declare_typedef_in_file - ~(write_shmem : bool) - (ctx : Provider_context.t) - (file : Relative_path.t) - (name : string) : Typing_defs.typedef_type = - match Ast_provider.find_typedef_in_file ctx file name with - | Some t -> type_typedef_naming_and_decl ~write_shmem ctx t - | None -> err_not_found file name - -let declare_const_in_file - ~(write_shmem : bool) - (ctx : Provider_context.t) - (file : Relative_path.t) - (name : string) : Typing_defs.decl_ty = - match Ast_provider.find_gconst_in_file ctx file name with - | Some cst -> iconst_decl ~write_shmem ctx cst - | None -> err_not_found file name diff --git a/hphp/hack/src/decl/decl_folded_class.mli b/hphp/hack/src/decl/decl_folded_class.mli new file mode 100644 index 00000000000..61c712f922e --- /dev/null +++ b/hphp/hack/src/decl/decl_folded_class.mli @@ -0,0 +1,19 @@ +(* + * Copyright (c) 2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the "hack" directory of this source tree. + * + *) + +type class_env = { + ctx: Provider_context.t; + stack: SSet.t; +} + +val class_decl_if_missing : + sh:SharedMem.uses -> + class_env -> + Nast.class_ -> + (string * Decl_defs.decl_class_type) option diff --git a/hphp/hack/src/decl/decl_nast.ml b/hphp/hack/src/decl/decl_nast.ml new file mode 100644 index 00000000000..39507c7a2c4 --- /dev/null +++ b/hphp/hack/src/decl/decl_nast.ml @@ -0,0 +1,227 @@ +(* + * Copyright (c) 2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the "hack" directory of this source tree. + * + *) + +(*****************************************************************************) +(* Module used to convert an AST into a type signature. Produces signatures for + * functions, global constants, typedefs, and records. Classes are handled in + * Decl_folded_class (folded decl) or Typing_classes_heap (shallow decl). *) +(*****************************************************************************) + +open Hh_prelude +open Decl_fun_utils +open Aast +open Typing_defs +open Typing_deps +module Reason = Typing_reason +module SN = Naming_special_names + +(*****************************************************************************) +(* Section declaring the type of a function *) +(*****************************************************************************) + +let rec fun_naming_and_decl + ~(write_shmem : bool) (ctx : Provider_context.t) (f : Nast.fun_) : + string * Typing_defs.fun_elt = + let f = Errors.ignore_ (fun () -> Naming.fun_ ctx f) in + let fe = fun_decl ctx f in + if write_shmem then Decl_heap.Funs.add (snd f.f_name) fe; + (snd f.f_name, fe) + +and fun_decl (ctx : Provider_context.t) (f : Nast.fun_) : Typing_defs.fun_elt = + let dep = Dep.Fun (snd f.f_name) in + let env = { Decl_env.mode = f.f_mode; droot = Some dep; ctx } in + fun_decl_in_env env ~is_lambda:false f + +and fun_decl_in_env (env : Decl_env.env) ~(is_lambda : bool) (f : Nast.fun_) : + Typing_defs.fun_elt = + check_params f.f_params; + let reactivity = fun_reactivity env f.f_user_attributes in + let returns_mutable = fun_returns_mutable f.f_user_attributes in + let returns_void_to_rx = fun_returns_void_to_rx f.f_user_attributes in + let return_disposable = has_return_disposable_attribute f.f_user_attributes in + let params = make_params env ~is_lambda f.f_params in + let capability = + hint_to_type + ~is_lambda:false + ~default: + (Typing_make_type.default_capability (Reason.Rhint (fst f.f_name))) + env + (Reason.Rwitness (fst f.f_name)) + (hint_of_type_hint f.f_cap) + in + let ret_ty = + ret_from_fun_kind + ~is_lambda + env + (fst f.f_name) + f.f_fun_kind + (hint_of_type_hint f.f_ret) + in + let arity = + match f.f_variadic with + | FVvariadicArg param -> + assert param.param_is_variadic; + Fvariadic (make_param_ty env ~is_lambda param) + | FVellipsis p -> Fvariadic (make_ellipsis_param_ty p) + | FVnonVariadic -> Fstandard + in + let tparams = List.map f.f_tparams (type_param env) in + let where_constraints = + List.map f.f_where_constraints (where_constraint env) + in + let fe_deprecated = + Naming_attributes_deprecated.deprecated + ~kind:"function" + f.f_name + f.f_user_attributes + in + let fe_php_std_lib = + Naming_attributes.mem SN.UserAttributes.uaPHPStdLib f.f_user_attributes + in + let fe_type = + mk + ( Reason.Rwitness (fst f.f_name), + Tfun + { + ft_arity = arity; + ft_tparams = tparams; + ft_where_constraints = where_constraints; + ft_params = params; + ft_implicit_params = { capability }; + ft_ret = { et_type = ret_ty; et_enforced = false }; + ft_reactive = reactivity; + ft_flags = + make_ft_flags + f.f_fun_kind + (* Functions can't be mutable because they don't have "this" *) + None + ~returns_mutable + ~return_disposable + ~returns_void_to_rx; + } ) + in + { fe_pos = fst f.f_name; fe_type; fe_deprecated; fe_php_std_lib } + +(*****************************************************************************) +(* Dealing with records *) +(*****************************************************************************) + +let record_def_decl (rd : Nast.record_def) : Typing_defs.record_def_type = + let extends = + match rd.rd_extends with + (* The only valid type hint for record parents is a record + name. Records do not support generics. *) + | Some (_, Happly (id, [])) -> Some id + | _ -> None + in + let fields = + List.map rd.rd_fields ~f:(fun (id, _, default) -> + match default with + | Some _ -> (id, Typing_defs.HasDefaultValue) + | None -> (id, ValueRequired)) + in + { + rdt_name = rd.rd_name; + rdt_extends = extends; + rdt_fields = fields; + rdt_abstract = rd.rd_abstract; + rdt_pos = rd.rd_span; + } + +let record_def_naming_and_decl + ~(write_shmem : bool) (ctx : Provider_context.t) (rd : Nast.record_def) : + string * Typing_defs.record_def_type = + let rd = Errors.ignore_ (fun () -> Naming.record_def ctx rd) in + let tdecl = record_def_decl rd in + if write_shmem then Decl_heap.RecordDefs.add (snd rd.rd_name) tdecl; + (snd rd.rd_name, tdecl) + +let record_def_decl_if_missing + ~(sh : SharedMem.uses) (ctx : Provider_context.t) (rd : Nast.record_def) : + unit = + let SharedMem.Uses = sh in + let (_, rdid) = rd.rd_name in + if not (Decl_heap.RecordDefs.mem rdid) then + let (_ : string * Typing_defs.record_def_type) = + record_def_naming_and_decl ~write_shmem:true ctx rd + in + () + +(*****************************************************************************) +(* Dealing with typedefs *) +(*****************************************************************************) + +let rec typedef_decl_if_missing + ~(sh : SharedMem.uses) (ctx : Provider_context.t) (typedef : Nast.typedef) : + unit = + let SharedMem.Uses = sh in + let (_, name) = typedef.t_name in + if not (Decl_heap.Typedefs.mem name) then + let (_ : string * typedef_type) = + typedef_naming_and_decl ~write_shmem:true ctx typedef + in + () + +and typedef_decl (ctx : Provider_context.t) (tdef : Nast.typedef) : + Typing_defs.typedef_type = + let { + t_annotation = (); + t_name = (td_pos, tid); + t_tparams = params; + t_constraint = tcstr; + t_kind = concrete_type; + t_user_attributes = _; + t_namespace = _; + t_mode = mode; + t_vis = td_vis; + t_span = _; + t_emit_id = _; + } = + tdef + in + let dep = Typing_deps.Dep.Class tid in + let env = { Decl_env.mode; droot = Some dep; ctx } in + let td_tparams = List.map params (type_param env) in + let td_type = Decl_hint.hint env concrete_type in + let td_constraint = Option.map tcstr (Decl_hint.hint env) in + { td_vis; td_tparams; td_constraint; td_type; td_pos } + +and typedef_naming_and_decl + ~(write_shmem : bool) (ctx : Provider_context.t) (tdef : Nast.typedef) : + string * Typing_defs.typedef_type = + let tdef = Errors.ignore_ (fun () -> Naming.typedef ctx tdef) in + let tdecl = typedef_decl ctx tdef in + if write_shmem then Decl_heap.Typedefs.add (snd tdef.t_name) tdecl; + (snd tdef.t_name, tdecl) + +(*****************************************************************************) +(* Global constants *) +(*****************************************************************************) + +let const_decl (ctx : Provider_context.t) (cst : Nast.gconst) : + Typing_defs.decl_ty = + let (cst_pos, _cst_name) = cst.cst_name in + let dep = Dep.GConst (snd cst.cst_name) in + let env = { Decl_env.mode = cst.cst_mode; droot = Some dep; ctx } in + match cst.cst_type with + | Some h -> Decl_hint.hint env h + | None -> + (match Decl_utils.infer_const cst.cst_value with + | Some tprim -> mk (Reason.Rwitness (fst cst.cst_value), Tprim tprim) + (* A NAST check will take care of rejecting constants that have neither + * an initializer nor a literal initializer *) + | None -> mk (Reason.Rwitness cst_pos, Typing_defs.make_tany ())) + +let const_naming_and_decl + ~(write_shmem : bool) (ctx : Provider_context.t) (cst : Nast.gconst) : + string * Typing_defs.decl_ty = + let cst = Errors.ignore_ (fun () -> Naming.global_const ctx cst) in + let hint_ty = const_decl ctx cst in + if write_shmem then Decl_heap.GConsts.add (snd cst.cst_name) hint_ty; + (snd cst.cst_name, hint_ty) diff --git a/hphp/hack/src/decl/decl_nast.mli b/hphp/hack/src/decl/decl_nast.mli new file mode 100644 index 00000000000..40841c6d0f7 --- /dev/null +++ b/hphp/hack/src/decl/decl_nast.mli @@ -0,0 +1,41 @@ +(* + * Copyright (c) 2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the "hack" directory of this source tree. + * + *) + +val fun_decl_in_env : + Decl_env.env -> is_lambda:bool -> Nast.fun_ -> Typing_defs.fun_elt + +val fun_naming_and_decl : + write_shmem:bool -> + Provider_context.t -> + Nast.fun_ -> + string * Typing_defs.fun_elt + +val record_def_naming_and_decl : + write_shmem:bool -> + Provider_context.t -> + Nast.record_def -> + string * Typing_defs.record_def_type + +val record_def_decl_if_missing : + sh:SharedMem.uses -> Provider_context.t -> Nast.record_def -> unit + +val typedef_naming_and_decl : + write_shmem:bool -> + Provider_context.t -> + Nast.typedef -> + string * Typing_defs.typedef_type + +val typedef_decl_if_missing : + sh:SharedMem.uses -> Provider_context.t -> Nast.typedef -> unit + +val const_naming_and_decl : + write_shmem:bool -> + Provider_context.t -> + Nast.gconst -> + string * Typing_defs.decl_ty diff --git a/hphp/hack/src/decl/dune.inc b/hphp/hack/src/decl/dune.inc index 2f072a73b76..632e7b876d4 100644 --- a/hphp/hack/src/decl/dune.inc +++ b/hphp/hack/src/decl/dune.inc @@ -18,22 +18,19 @@ (library (name decl) (modules - decl - decl_inherit - decl_requirements) + decl) (libraries affectedDeps classDiff ast_provider decl_class decl_class_elements - decl_enum decl_env decl_fun_utils + decl_folded_class decl_heap - decl_init_check + decl_nast decl_pos_utils - decl_to_typing full_fidelity naming naming_heap @@ -45,6 +42,48 @@ (preprocess (pps ppx_deriving.std))) (library + (name decl_nast) + (modules + decl_nast) + (libraries + decl_env + decl_fun_utils + decl_heap + decl_init_check + decl_pos_utils + decl_utils + full_fidelity + naming + naming_attributes + naming_attributes_deprecated + typing_defs) + (preprocess (pps ppx_deriving.std))) + +(library + (name decl_folded_class) + (modules + decl_folded_class + decl_inherit + decl_requirements) + (libraries + ast_provider + decl_class + decl_class_elements + decl_enum + decl_env + decl_fun_utils + decl_heap + decl_init_check + decl_pos_utils + decl_to_typing + naming + procs_procs + shallow_classes_provider + shallow_decl_defs + typing_defs) + (preprocess (pps ppx_deriving.std))) + +(library (name direct_decl_parser) (modules direct_decl_parser) (libraries diff --git a/hphp/hack/src/hh_single_type_check.ml b/hphp/hack/src/hh_single_type_check.ml index 269afade8c8..fbfb3d317bb 100644 --- a/hphp/hack/src/hh_single_type_check.ml +++ b/hphp/hack/src/hh_single_type_check.ml @@ -957,12 +957,9 @@ let parse_and_name ctx files_contents = let parse_name_and_decl ctx files_contents = Errors.do_ (fun () -> let (parsed_files, files_info) = parse_and_name ctx files_contents in - Relative_path.Map.iter parsed_files (fun fn parsed_file -> + Relative_path.Map.iter parsed_files (fun fn _ -> Errors.run_in_context fn Errors.Decl (fun () -> - Decl.name_and_declare_types_program - ~sh:SharedMem.Uses - ctx - parsed_file.Parser_return.ast)); + Decl.make_env ~sh:SharedMem.Uses ctx fn)); files_info) diff --git a/hphp/hack/src/typing/dune b/hphp/hack/src/typing/dune index 75466c41029..9809207f6ba 100644 --- a/hphp/hack/src/typing/dune +++ b/hphp/hack/src/typing/dune @@ -165,6 +165,9 @@ typing_toplevel typing_try) (libraries + decl_enum + decl_init_check + decl_nast logging naming_special_names pcre diff --git a/hphp/hack/src/typing/typing.ml b/hphp/hack/src/typing/typing.ml index 3b736070fde..c6579b56707 100644 --- a/hphp/hack/src/typing/typing.ml +++ b/hphp/hack/src/typing/typing.ml @@ -2562,7 +2562,9 @@ and expr_ Typing_check_decls.fun_ env f; (* This is the function type as declared on the lambda itself. * If type hints are absent then use Tany instead. *) - let declared_fe = Decl.fun_decl_in_env env.decl_env ~is_lambda:true f in + let declared_fe = + Decl_nast.fun_decl_in_env env.decl_env ~is_lambda:true f + in let { fe_type; fe_pos; _ } = declared_fe in let (declared_pos, declared_ft) = match get_node fe_type with -- 2.11.4.GIT