2 * Copyright (c) 2017, 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 open Typing_mutability_env
13 module Env
= Typing_env
15 module LMap
= Local_id.Map
17 module type Env_S
= sig
20 val env_reactivity
: env
-> reactivity
22 val get_fun
: env
-> Decl_provider.fun_key
-> Decl_provider.fun_decl
option
24 val expand_type
: env
-> locl_ty
-> env
* locl_ty
27 module Shared
(Env
: Env_S
) = struct
28 (* true if function has <<__ReturnMutable>> annotation, otherwise false *)
29 let is_fun_call_returning_mutable (env
: Env.env
) (e
: Tast.expr
) : bool =
30 let fun_ty_returns_mutable fun_ty
=
31 match get_node fun_ty
with
32 | Tfun fty
-> get_ft_returns_mutable fty
35 let fun_returns_mutable id
=
36 match Env.get_fun env
(snd id
) with
37 | Some
{ fe_type
= fun_ty
; _
} -> fun_ty_returns_mutable fun_ty
42 | T.Call
((_
, T.Id id
), _
, _
, _
)
43 | T.Call
((_
, T.Fun_id id
), _
, _
, _
) ->
44 fun_returns_mutable id
45 | T.Call
(((_
, fun_ty
), T.Obj_get _
), _
, _
, _
)
46 | T.Call
(((_
, fun_ty
), T.Class_const _
), _
, _
, _
)
47 | T.Call
(((_
, fun_ty
), T.Lvar _
), _
, _
, _
) ->
48 let (_
, efun_ty
) = Env.expand_type env fun_ty
in
49 fun_ty_returns_mutable efun_ty
53 include Shared
(struct
54 type env
= Typing_env_types.env
56 let env_reactivity = Typing_env_types.env_reactivity
58 let get_fun = Typing_env.get_fun
60 let expand_type = Typing_env.expand_type
63 let handle_value_in_return
64 ~function_returns_mutable
65 ~function_returns_void_for_rx
66 (env
: Typing_env_types.env
)
68 (e
: Tast.expr
) : Typing_env_types.env
=
69 let error_mutable e mut_opt
=
72 | Immutable
-> "(non-mutable)"
73 | MaybeMutable
-> "(maybe-mutable)"
74 | Borrowed
-> "(borrowed)"
75 | Mutable
-> assert false
77 Errors.invalid_mutable_return_result
(Tast.get_position e
) fun_pos
kind
79 let error_borrowed_as_immutable e
=
80 (* attempt to return borrowed value as immutable *)
81 Errors.cannot_return_borrowed_value_as_immutable
87 (* ignore nulls - it is ok to return then from functions
88 that return nullable types and for non-nullable return types it will
89 be an error anyways *)
91 (* allow bare new expressions
92 - implicit Rx\mutable in __MutableReturn functions *)
96 | T.Call
((_
, T.Id
(_
, id
)), _
, _
, _
) when String.equal id
SN.Rx.mutable_
->
97 (* ok to return result of Rx\mutable - implicit Rx\move *)
100 (* ok for pipe if rhs returns mutable *)
102 | T.Binop
(Ast_defs.QuestionQuestion
, l
, r
) ->
103 let env = aux env l
in
106 let mut_env = Env.get_env_mutability
env in
108 match LMap.find_opt id
mut_env with
109 | Some
(p
, Mutable
) ->
110 (* it is ok to return mutably owned values *)
111 let env = Env.unset_local
env id
in
112 Env.env_with_mut
env (LMap.add id
(p
, Mutable
) mut_env)
113 | Some
(_
, Borrowed
) when not function_returns_mutable
->
114 (* attempt to return borrowed value as immutable
115 unless function is marked with __ReturnsVoidToRx in which case caller
116 will not be able to alias the value *)
117 if not function_returns_void_for_rx
then error_borrowed_as_immutable e
;
119 | Some
(_
, mut
) when function_returns_mutable
->
125 when (not function_returns_mutable
)
126 && Option.is_some
(Env.function_is_mutable
env) ->
127 (* mutable this is treated as borrowed and this cannot be returned as immutable
128 unless function is marked with __ReturnsVoidToRx in which case caller
129 will not be able to alias the value *)
130 if not function_returns_void_for_rx
then error_borrowed_as_immutable e
;
133 (* for __MutableReturn functions allow delegating calls
134 to __MutableReturn functions *)
135 ( if function_returns_mutable
&& not
(is_fun_call_returning_mutable env e
)
138 "not a valid return value for `__MutableReturn` functions."
140 Errors.invalid_mutable_return_result
(Tast.get_position e
) fun_pos
kind
146 let freeze_or_move_local
148 (env : Typing_env_types.env)
149 (tel
: Tast.expr list
)
150 (invalid_target
: Pos.t
-> Pos.t
-> string -> unit)
151 (invalid_use
: Pos.t
-> unit) : Typing_env_types.env =
153 | [(_
, T.Any
)] -> env
154 | [(_
, T.Lvar
(id_pos
, id
))] ->
155 let mut_env = Env.get_env_mutability
env in
157 match LMap.find_opt id
mut_env with
158 | Some
(p
, Mutable
) ->
159 let env = Env.unset_local
env id
in
160 Env.env_with_mut
env (LMap.add id
(p
, Mutable
) mut_env)
162 invalid_target p id_pos
(to_string x
);
165 invalid_target p id_pos
"immutable";
168 | [((id_pos
, _
), T.This
)] ->
169 invalid_target p id_pos
"the `this` type, which is mutably borrowed";
172 (* Error, freeze/move takes a single local as an argument *)
176 let freeze_local (p
: Pos.t
) (env : Typing_env_types.env) (tel
: Tast.expr list
)
177 : Typing_env_types.env =
182 Errors.invalid_freeze_target
183 Errors.invalid_freeze_use
185 let move_local (p
: Pos.t
) (env : Typing_env_types.env) (tel
: Tast.expr list
) :
186 Typing_env_types.env =
191 Errors.invalid_move_target
192 Errors.invalid_move_use
194 let rec is_move_or_mutable_call ?
(allow_move
= true) te
=
196 | T.Call
((_
, T.Id
(_
, n
)), _
, _
, _
) ->
197 String.equal n
SN.Rx.mutable_
|| (allow_move
&& String.equal n
SN.Rx.move
)
198 | T.Pipe
(_
, _
, (_
, r
)) -> is_move_or_mutable_call ~allow_move
:false r
201 (* Checks for assignment errors as a pass on the TAST *)
202 let handle_assignment_mutability
203 (env : Typing_env_types.env) (te1
: Tast.expr
) (te2
: Tast.expr_
option) :
204 Typing_env_types.env =
205 (* If e2 is a mutable expression, then e1 is added to the mutability env *)
206 let mut_env = Env.get_env_mutability
env in
208 match (snd te1
, te2
) with
211 equal_param_mutability
212 (Env.function_is_mutable
env)
213 (Some Param_borrowed_mutable
) ->
214 (* aliasing $this - bad for __Mutable and __MaybeMutable functions *)
215 ( Env.error_if_reactive_context
env @@ fun () ->
216 Errors.reassign_mutable_this
218 ~is_maybe_mutable
:false
219 (Tast.get_position te1
) );
223 equal_param_mutability
224 (Env.function_is_mutable
env)
225 (Some Param_maybe_mutable
) ->
226 (* aliasing $this - bad for __Mutable and __MaybeMutable functions *)
227 ( Env.error_if_reactive_context
env @@ fun () ->
228 Errors.reassign_mutable_this
230 ~is_maybe_mutable
:true
231 (Tast.get_position te1
) );
233 (* var = mutable(v)/move(v) - add the var to the env since it points to a owned mutable value *)
234 | (T.Lvar
(p
, id
), Some e
) when is_move_or_mutable_call e
->
236 match LMap.find_opt id
mut_env with
237 | Some
((_
, (Immutable
| Borrowed
| MaybeMutable
)) as mut
) ->
238 (* error when assigning owned mutable to another mutability flavor *)
239 Errors.invalid_mutability_flavor p
(to_string mut
) "mutable"
242 LMap.add id
(Tast.get_position te1
, Mutable
) mut_env
243 (* Reassigning mutables is not allowed; error *)
244 | (_
, Some
(T.Lvar
(p
, id2
)))
245 when Option.value_map
246 (LMap.find_opt id2
mut_env)
248 ~f
:(fun (_
, m
) -> not
(equal_mut_type m Immutable
)) ->
249 ( Env.error_if_reactive_context
env @@ fun () ->
250 match LMap.find id2
mut_env with
251 | (_
, MaybeMutable
) ->
252 Errors.reassign_maybe_mutable_var ~in_collection
:false p
253 | _
-> Errors.reassign_mutable_var ~in_collection
:false p
);
255 (* If the Lvar gets reassigned and shadowed to something that
256 isn't a mutable, it is now a regular immutable variable.
258 | (T.Lvar
(p
, id
), _
) ->
260 match LMap.find_opt id
mut_env with
261 (* ok if local is immutable*)
262 | Some
(_
, Immutable
) -> mut_env
263 (* error assigning immutable value to local known to be mutable *)
265 Errors.invalid_mutability_flavor p
(to_string mut
) "immutable";
268 (* ok - add new locals *)
269 LMap.add id
(Tast.get_position te1
, Immutable
) mut_env
273 Env.env_with_mut
env mut_env