declare_folded_class NO LONGER _in_file
[hiphop-php.git] / hphp / hack / src / providers / decl_provider.ml
blob4fe3d1c16dbc06cd1e19a9e1244a527180d4471e
1 (*
2 * Copyright (c) Facebook, Inc. and its affiliates.
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the "hack" directory of this source tree.
7 *)
9 open Hh_prelude
10 module Class = Typing_classes_heap.Api
12 type fun_key = string
14 type type_key = string
16 type gconst_key = string
18 type module_key = string
20 type fun_decl = Typing_defs.fun_elt
22 type class_decl = Typing_classes_heap.Api.t
24 type typedef_decl = Typing_defs.typedef_type
26 type gconst_decl = Typing_defs.const_decl
28 type module_decl = Typing_defs.module_def_type
30 let ( let* ) = Caml.Option.bind
32 let err_not_found = Typedef_provider.err_not_found
34 let find_in_direct_decl_parse = Typedef_provider.find_in_direct_decl_parse
36 (** This cache caches the result of full class computations
37 (the class merged with all its inherited members.) *)
38 module Cache =
39 SharedMem.FreqCache
40 (StringKey)
41 (struct
42 type t = Typing_classes_heap.class_t
44 let description = "Decl_Typing_ClassType"
45 end)
46 (struct
47 let capacity = 1000
48 end)
50 let declare_folded_class (ctx : Provider_context.t) (name : type_key) :
51 Decl_defs.decl_class_type * Decl_store.class_members option =
52 match Provider_context.get_backend ctx with
53 | Provider_backend.Analysis -> failwith "invalid"
54 | _ ->
55 (match
56 Errors.run_in_decl_mode (fun () ->
57 Decl_folded_class.class_decl_if_missing ~sh:SharedMem.Uses ctx name)
58 with
59 | None -> err_not_found None name
60 | Some decl_and_members -> decl_and_members)
62 let lookup_or_populate_class_cache class_name populate =
63 match Cache.get class_name with
64 | Some _ as result -> result
65 | None ->
66 begin
67 match populate class_name with
68 | None -> None
69 | Some v as result ->
70 Cache.add class_name v;
71 result
72 end
74 let get_class
75 ?(tracing_info : Decl_counters.tracing_info option)
76 (ctx : Provider_context.t)
77 (class_name : type_key) : class_decl option =
78 Decl_counters.count_decl ?tracing_info Decl_counters.Class class_name
79 @@ fun counter ->
80 (* There's a confusing matrix of possibilities:
81 SHALLOW - in this case, the Typing_classes_heap.class_t we get back is
82 just a small shim that does memoization; further accessors on it
83 like "get_method" will lazily call Linearization_provider and Shallow_classes_provider
84 to get more information
85 EAGER - in this case, the Typing_classes_heap.class_t we get back is
86 an "folded" object which keeps an intire index of all members, although
87 those members are fetched lazily via Lazy.t.
89 and
91 LOCAL BACKEND - the class_t is cached in the local backend.
92 SHAREDMEM BACKEND - the class_t is cached in the worker-local 'Cache' heap.
93 Note that in the case of eager, the class_t is really just a fairly simple
94 derivation of the decl_class_type that lives in shmem.
95 DECL BACKEND - the class_t is cached in the worker-local 'Cache' heap *)
96 match Provider_context.get_backend ctx with
97 | Provider_backend.Analysis ->
98 begin
99 match
100 lookup_or_populate_class_cache class_name (fun class_name ->
101 Decl_store.((get ()).get_class class_name)
102 |> Option.map ~f:Typing_classes_heap.make_eager_class_decl)
103 with
104 | None -> None
105 | Some v -> Some (counter, v, Some ctx)
107 | Provider_backend.Pessimised_shared_memory _ ->
108 (* No pessimisation needs to be done here directly. All pessimisation is
109 * done on the shallow classes within [Shallow_classes_provider] that the
110 * [Typing_classes_heap.Api.t] returned here is constructed from
111 * Crucially, we do not use the [Cache] here, which would contain
112 * outdated member types once we update its members during
113 * pessimisation. *)
114 begin
115 match Typing_classes_heap.get ctx class_name declare_folded_class with
116 | None -> None
117 | Some v -> Some (counter, v, Some ctx)
119 | Provider_backend.Shared_memory
120 | Provider_backend.Decl_service _ ->
121 begin
122 match
123 lookup_or_populate_class_cache class_name (fun class_name ->
124 Typing_classes_heap.get ctx class_name declare_folded_class)
125 with
126 | None -> None
127 | Some v -> Some (counter, v, Some ctx)
129 | Provider_backend.Local_memory { Provider_backend.decl_cache; _ } ->
130 let open Option.Monad_infix in
131 Typing_classes_heap.get_class_with_cache
133 class_name
134 decl_cache
135 declare_folded_class
136 >>| fun cls -> (counter, cls, Some ctx)
137 | Provider_backend.Rust_provider_backend backend ->
138 begin
139 match
140 lookup_or_populate_class_cache class_name (fun class_name ->
141 Rust_provider_backend.Decl.get_folded_class backend class_name
142 |> Option.map ~f:Typing_classes_heap.make_eager_class_decl)
143 with
144 | None -> None
145 | Some v -> Some (counter, v, Some ctx)
148 let maybe_pessimise_fun_decl ctx fun_decl =
149 if TypecheckerOptions.everything_sdt (Provider_context.get_tcopt ctx) then
150 Typing_defs.
152 fun_decl with
153 fe_type =
154 Decl_enforceability.(
155 pessimise_fun_type
156 ~fun_kind:Function
158 fun_decl.fe_pos
159 fun_decl.fe_type);
161 else
162 fun_decl
164 let get_fun
165 ?(tracing_info : Decl_counters.tracing_info option)
166 (ctx : Provider_context.t)
167 (fun_name : fun_key) : fun_decl option =
168 Option.map ~f:(maybe_pessimise_fun_decl ctx)
169 @@ Decl_counters.count_decl Decl_counters.Fun ?tracing_info fun_name
170 @@ fun _counter ->
171 match Provider_context.get_backend ctx with
172 | Provider_backend.Analysis -> Decl_store.((get ()).get_fun fun_name)
173 | Provider_backend.Pessimised_shared_memory info ->
174 (match Decl_store.((get ()).get_fun fun_name) with
175 | Some c -> Some c
176 | None ->
177 (match Naming_provider.get_fun_path ctx fun_name with
178 | Some filename ->
179 let* original_ft =
180 find_in_direct_decl_parse
181 ~cache_results:false
183 filename
184 fun_name
185 Shallow_decl_defs.to_fun_decl_opt
187 let ft =
188 info.Provider_backend.pessimise_fun
189 filename
190 ~name:fun_name
191 original_ft
193 if info.Provider_backend.store_pessimised_result then
194 Decl_store.((get ()).add_fun) fun_name ft;
195 Some ft
196 | None -> None))
197 | Provider_backend.Shared_memory ->
198 (match Decl_store.((get ()).get_fun fun_name) with
199 | Some c -> Some c
200 | None ->
201 (match Naming_provider.get_fun_path ctx fun_name with
202 | Some filename ->
203 find_in_direct_decl_parse
204 ~cache_results:true
206 filename
207 fun_name
208 Shallow_decl_defs.to_fun_decl_opt
209 | None -> None))
210 | Provider_backend.Local_memory { Provider_backend.decl_cache; _ } ->
211 Provider_backend.Decl_cache.find_or_add
212 decl_cache
213 ~key:(Provider_backend.Decl_cache_entry.Fun_decl fun_name)
214 ~default:(fun () ->
215 match Naming_provider.get_fun_path ctx fun_name with
216 | Some filename ->
217 find_in_direct_decl_parse
218 ~cache_results:true
220 filename
221 fun_name
222 Shallow_decl_defs.to_fun_decl_opt
223 | None -> None)
224 | Provider_backend.Decl_service { decl; _ } ->
225 Decl_service_client.rpc_get_fun decl fun_name
226 | Provider_backend.Rust_provider_backend backend ->
227 Rust_provider_backend.Decl.get_fun backend fun_name
229 let maybe_pessimise_typedef_decl ctx typedef_decl =
230 if TypecheckerOptions.everything_sdt (Provider_context.get_tcopt ctx) then
231 (* TODO: deal with super constraint *)
232 match typedef_decl.Typing_defs.td_as_constraint with
233 | Some _ -> typedef_decl
234 | None ->
235 let open Typing_defs in
236 let pos = typedef_decl.td_pos in
238 typedef_decl with
239 td_as_constraint =
240 Some
241 (Decl_enforceability.supportdyn_mixed
243 (Reason.Rwitness_from_decl pos));
245 else
246 typedef_decl
248 let get_typedef
249 ?(tracing_info : Decl_counters.tracing_info option)
250 (ctx : Provider_context.t)
251 (typedef_name : type_key) : typedef_decl option =
252 Option.map ~f:(maybe_pessimise_typedef_decl ctx)
253 @@ Decl_counters.count_decl Decl_counters.Typedef ?tracing_info typedef_name
254 @@ fun _counter ->
255 match Provider_context.get_backend ctx with
256 | Provider_backend.Analysis -> Decl_store.((get ()).get_typedef typedef_name)
257 | Provider_backend.Shared_memory ->
258 Typedef_provider.get_typedef ctx typedef_name
259 | Provider_backend.Pessimised_shared_memory info ->
260 (match Decl_store.((get ()).get_typedef typedef_name) with
261 | Some c -> Some c
262 | None ->
263 (match Naming_provider.get_typedef_path ctx typedef_name with
264 | Some filename ->
265 let* original_typedef =
266 find_in_direct_decl_parse
267 ~cache_results:false
269 filename
270 typedef_name
271 Shallow_decl_defs.to_typedef_decl_opt
273 let typedef =
274 info.Provider_backend.pessimise_typedef
275 filename
276 ~name:typedef_name
277 original_typedef
279 if info.Provider_backend.store_pessimised_result then
280 Decl_store.((get ()).add_typedef) typedef_name typedef;
281 Some typedef
282 | None -> None))
283 | Provider_backend.Local_memory { Provider_backend.decl_cache; _ } ->
284 Provider_backend.Decl_cache.find_or_add
285 decl_cache
286 ~key:(Provider_backend.Decl_cache_entry.Typedef_decl typedef_name)
287 ~default:(fun () ->
288 match Naming_provider.get_typedef_path ctx typedef_name with
289 | Some filename ->
290 find_in_direct_decl_parse
291 ~cache_results:true
293 filename
294 typedef_name
295 Shallow_decl_defs.to_typedef_decl_opt
296 | None -> None)
297 | Provider_backend.Decl_service { decl; _ } ->
298 Decl_service_client.rpc_get_typedef decl typedef_name
299 | Provider_backend.Rust_provider_backend backend ->
300 Rust_provider_backend.Decl.get_typedef backend typedef_name
302 let get_gconst
303 ?(tracing_info : Decl_counters.tracing_info option)
304 (ctx : Provider_context.t)
305 (gconst_name : gconst_key) : gconst_decl option =
306 Decl_counters.count_decl Decl_counters.GConst ?tracing_info gconst_name
307 @@ fun _counter ->
308 match Provider_context.get_backend ctx with
309 | Provider_backend.Analysis -> Decl_store.((get ()).get_gconst gconst_name)
310 | Provider_backend.Pessimised_shared_memory info ->
311 (match Decl_store.((get ()).get_gconst gconst_name) with
312 | Some c -> Some c
313 | None ->
314 (match Naming_provider.get_const_path ctx gconst_name with
315 | Some filename ->
316 let* original_gconst =
317 find_in_direct_decl_parse
318 ~cache_results:false
320 filename
321 gconst_name
322 Shallow_decl_defs.to_const_decl_opt
324 let gconst =
325 info.Provider_backend.pessimise_gconst
326 filename
327 ~name:gconst_name
328 original_gconst
330 (if info.Provider_backend.store_pessimised_result then
331 Decl_store.((get ()).add_gconst gconst_name gconst));
332 Some gconst
333 | None -> None))
334 | Provider_backend.Shared_memory ->
335 (match Decl_store.((get ()).get_gconst gconst_name) with
336 | Some c -> Some c
337 | None ->
338 (match Naming_provider.get_const_path ctx gconst_name with
339 | Some filename ->
340 find_in_direct_decl_parse
341 ~cache_results:true
343 filename
344 gconst_name
345 Shallow_decl_defs.to_const_decl_opt
346 | None -> None))
347 | Provider_backend.Local_memory { Provider_backend.decl_cache; _ } ->
348 Provider_backend.Decl_cache.find_or_add
349 decl_cache
350 ~key:(Provider_backend.Decl_cache_entry.Gconst_decl gconst_name)
351 ~default:(fun () ->
352 match Naming_provider.get_const_path ctx gconst_name with
353 | Some filename ->
354 find_in_direct_decl_parse
355 ~cache_results:true
357 filename
358 gconst_name
359 Shallow_decl_defs.to_const_decl_opt
360 | None -> None)
361 | Provider_backend.Decl_service { decl; _ } ->
362 Decl_service_client.rpc_get_gconst decl gconst_name
363 | Provider_backend.Rust_provider_backend backend ->
364 Rust_provider_backend.Decl.get_gconst backend gconst_name
366 let prepare_for_typecheck
367 (ctx : Provider_context.t) (path : Relative_path.t) (content : string) :
368 unit =
369 match Provider_context.get_backend ctx with
370 | Provider_backend.Analysis
371 | Provider_backend.Rust_provider_backend _
372 | Provider_backend.Pessimised_shared_memory _
373 | Provider_backend.Shared_memory
374 | Provider_backend.Local_memory _ ->
376 (* When using the decl service, before typechecking the file, populate our
377 decl caches with the symbols declared within that file. If we leave this to
378 the decl service, then in longer files, the decls declared later in the
379 file may be evicted by the time we attempt to typecheck them, forcing the
380 decl service to re-parse the file. This can lead to many re-parses in
381 extreme cases. *)
382 | Provider_backend.Decl_service { decl; _ } ->
383 Decl_service_client.parse_and_cache_decls_in decl path content
385 let get_module
386 ?(tracing_info : Decl_counters.tracing_info option)
387 (ctx : Provider_context.t)
388 (module_name : module_key) : module_decl option =
389 Decl_counters.count_decl Decl_counters.Module_decl ?tracing_info module_name
390 @@ fun _counter ->
391 let fetch_from_backing_store () =
392 Naming_provider.get_module_path ctx module_name
393 |> Option.bind ~f:(fun filename ->
394 find_in_direct_decl_parse
395 ~cache_results:true
397 filename
398 module_name
399 Shallow_decl_defs.to_module_decl_opt)
401 match Provider_context.get_backend ctx with
402 | Provider_backend.Analysis -> Decl_store.((get ()).get_module module_name)
403 | Provider_backend.Pessimised_shared_memory _
404 | Provider_backend.Shared_memory ->
405 Option.first_some
406 Decl_store.((get ()).get_module module_name)
407 (fetch_from_backing_store ())
408 | Provider_backend.Local_memory { Provider_backend.decl_cache; _ } ->
409 Provider_backend.Decl_cache.find_or_add
410 decl_cache
411 ~key:(Provider_backend.Decl_cache_entry.Module_decl module_name)
412 ~default:fetch_from_backing_store
413 | Provider_backend.Decl_service { decl; _ } ->
414 Decl_service_client.rpc_get_module decl module_name
415 | Provider_backend.Rust_provider_backend backend ->
416 Rust_provider_backend.Decl.get_module backend module_name
418 let get_overridden_method ctx ~class_name ~method_name ~is_static :
419 Typing_defs.class_elt option =
420 let open Option.Monad_infix in
421 get_class ctx class_name >>= fun cls ->
422 Class.overridden_method cls ~method_name ~is_static ~get_class
424 let local_changes_push_sharedmem_stack () =
425 Decl_store.((get ()).push_local_changes ())
427 let local_changes_pop_sharedmem_stack () =
428 Decl_store.((get ()).pop_local_changes ())
430 let declare_folded_class_in_file_FOR_TESTS_ONLY ctx cid =
431 fst (declare_folded_class ctx cid)