2 * Copyright (c) 2015, Facebook, Inc.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
11 module Env
= Typing_env
13 module C
= Typing_continuations
14 module LEnvC
= Typing_per_cont_env
15 module LEnvOps
= Typing_per_cont_ops
16 module Union
= Typing_union
18 (*****************************************************************************)
19 (* Module dealing with local environments. *)
20 (*****************************************************************************)
22 let get_all_locals env
= env
.lenv
.per_cont_env
24 (*****************************************************************************)
25 (* Functions dealing with old style local environment *)
26 (*****************************************************************************)
47 (* TODO(mjt) Use a more specific reason here provided as an argument *)
49 if TypecheckerOptions.using_extended_reasons env
.genv
.tcopt
then
50 Some
(Typing_reason.witness join_pos
)
54 let (env
, ty
) = Union.union ?
reason ~approx_cancel_neg
:true env ty1 ty2
in
56 match (bound_ty1
, bound_ty2
) with
57 | (None
, None
) -> (env
, None
)
61 | (Some ty1
, Some ty2
) ->
63 Typing_intersection.intersect
64 ~r
:(Typing_defs_core.get_reason ty1
)
71 (* TODO(mjt) Determin if this need updating to if the change to reasons will
74 if phys_equal ty ty1
|| Pos.equal
Pos.none pos2
then
76 else if phys_equal ty ty2
|| Pos.equal
Pos.none pos1
then
82 if Expression_id.equal eid1 eid2
then
85 Env.make_expression_id env
90 (env
, { ty
; defined
= defined1
&& defined2
; bound_ty
; pos; eid })
93 Typing_subtype.sub_type
97 (Some
(Typing_error.Reasons_callback.unify_error_at join_pos
))
103 Typing_error_utils.add_typing_error ~env err
;
104 (* If the new type or bound violates the old one, then we want to
105 check the remainder of the code with the type of the variable
113 defined
= defined1
&& defined2
;
114 bound_ty
= Some bound_ty
;
119 let get_cont_option env cont
=
120 let local_types = get_all_locals env
in
121 LEnvC.get_cont_option cont
local_types
123 let drop_cont env cont
=
124 let local_types = get_all_locals env
in
125 let local_types = LEnvC.drop_cont cont
local_types in
126 Env.env_with_locals env
local_types
128 let drop_conts env conts
=
129 let local_types = get_all_locals env
in
130 let local_types = LEnvC.drop_conts conts
local_types in
131 Env.env_with_locals env
local_types
133 let replace_cont env cont ctxopt
=
134 let local_types = get_all_locals env
in
135 let local_types = LEnvC.replace_cont cont ctxopt
local_types in
136 Env.env_with_locals env
local_types
138 let restore_conts_from env fromlocals conts
=
139 let local_types = get_all_locals env
in
141 LEnvOps.restore_conts_from local_types ~from
:fromlocals conts
143 Env.env_with_locals env
local_types
145 let restore_and_merge_conts_from env ~join_pos fromlocals conts
=
146 let local_types = get_all_locals env
in
147 let (env
, local_types) =
148 LEnvOps.restore_and_merge_conts_from
155 Env.env_with_locals env
local_types
157 (* Merge all continuations in the provided list and update the 'next'
158 * continuation with the result. *)
159 let update_next_from_conts env ~join_pos cont_list
=
160 let local_types = get_all_locals env
in
161 let (env
, local_types) =
162 LEnvOps.update_next_from_conts env
(union ~join_pos
) local_types cont_list
164 Env.env_with_locals env
local_types
166 (* After this call, the provided continuation will be the union of itself and
167 * the next continuation *)
168 let save_and_merge_next_in_cont env ~join_pos cont
=
169 let local_types = get_all_locals env
in
170 let (env
, local_types) =
171 LEnvOps.save_and_merge_next_in_cont env
(union ~join_pos
) local_types cont
173 Env.env_with_locals env
local_types
175 let move_and_merge_next_in_cont env ~join_pos cont
=
176 let local_types = get_all_locals env
in
177 let (env
, local_types) =
178 LEnvOps.move_and_merge_next_in_cont env
(union ~join_pos
) local_types cont
180 Env.env_with_locals env
local_types
182 let union_contextopts ~join_pos
= LEnvOps.union_opts
(union ~join_pos
)
184 let union_by_cont env ~join_pos lenv1 lenv2
=
185 let locals1 = lenv1
.per_cont_env
in
186 let locals2 = lenv2
.per_cont_env
in
188 LEnvOps.union_by_cont env
(union ~join_pos
) locals1 locals2
190 Env.env_with_locals env locals
192 let join_fake lenv1 lenv2
=
193 let nextctxopt1 = LEnvC.get_cont_option C.Next lenv1
.per_cont_env
in
194 let nextctxopt2 = LEnvC.get_cont_option C.Next lenv2
.per_cont_env
in
195 match (nextctxopt1, nextctxopt2) with
196 | (Some c1
, Some c2
) ->
197 Typing_fake_members.join c1
.LEnvC.fake_members c2
.LEnvC.fake_members
198 | (None
, None
) -> Typing_fake_members.empty
199 | (Some c1
, None
) -> c1
.LEnvC.fake_members
200 | (None
, Some c2
) -> c2
.LEnvC.fake_members
202 let union_lenvs_ env ~join_pos parent_lenv lenv1 lenv2
=
203 let fake_members = join_fake lenv1 lenv2
in
204 let local_using_vars = parent_lenv
.local_using_vars in
205 let env = union_by_cont env ~join_pos lenv1 lenv2
in
206 let lenv = { env.lenv with local_using_vars } in
208 LEnvC.update_cont_entry
C.Next
lenv.per_cont_env (fun entry
->
209 LEnvC.{ entry
with fake_members })
211 let lenv = { lenv with per_cont_env } in
212 ({ env with lenv }, lenv)
214 (* Used when we want the new local environment to be the union
215 * of 2 local environments. Typical use case is an if statement.
217 * if(...) { $x = ''; } else { $x = 'foo'; }
218 * We want $x to be a string past this point.
219 * We check that the locals are defined in both branches
220 * when that is the case, their type becomes the union (least upper bound)
221 * of the types it had in each branch.
223 let union_lenvs env ~join_pos parent_lenv lenv1 lenv2
=
224 fst
@@ union_lenvs_ env ~join_pos parent_lenv lenv1 lenv2
226 let rec union_lenv_list env ~join_pos parent_lenv
= function
230 | lenv1
:: lenv2
:: lenvlist
->
231 let (env, lenv) = union_lenvs_ env ~join_pos parent_lenv lenv1 lenv2
in
232 union_lenv_list env ~join_pos parent_lenv
(lenv :: lenvlist
)
234 let stash_and_do env conts f
=
235 let parent_locals = get_all_locals env in
236 let env = drop_conts env conts
in
237 let (env, res
) = f
env in
238 let env = restore_conts_from env parent_locals conts
in
241 let env_with_empty_fakes env =
243 LEnvC.update_cont_entry
C.Next
env.lenv.per_cont_env (fun entry
->
244 LEnvC.{ entry
with fake_members = Typing_fake_members.empty
})
246 { env with lenv = { env.lenv with per_cont_env } }
249 match get_cont_option env C.Next
with