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.
10 (*****************************************************************************)
11 (* On the fly type-declaration are called when the user modified a file
12 * we are not at initilalization time anymore. Therefore, we have a bit more
13 * work to do. We need calculate what must be re-checked.
15 (*****************************************************************************)
17 open Reordered_argument_collections
20 type get_classes_in_file
= Relative_path.t
-> SSet.t
22 type redo_type_decl_result
= {
27 old_decl_missing_count
: int;
30 let lvl = Hh_logger.Level.Debug
32 let shallow_decl_enabled (ctx
: Provider_context.t
) =
33 TypecheckerOptions.shallow_class_decl
(Provider_context.get_tcopt ctx
)
35 let force_shallow_decl_fanout_enabled (ctx
: Provider_context.t
) =
36 TypecheckerOptions.force_shallow_decl_fanout
(Provider_context.get_tcopt ctx
)
38 (*****************************************************************************)
39 (* The neutral element of declaration (cf procs/multiWorker.mli) *)
40 (*****************************************************************************)
41 let on_the_fly_neutral = Errors.empty
43 let compute_deps_neutral () =
44 let empty = DepSet.make
() in
45 ((empty, empty, empty), 0)
47 (*****************************************************************************)
48 (* This is the place where we are going to put everything necessary for
49 * the redeclaration. We could "pass" the values directly to the workers,
50 * but it gives too much work to the master and slows things down,
51 * so what we do instead is pass the data through shared memory via
53 * I tried replicating the data to speed things up but it had no effect.
55 (*****************************************************************************)
57 module OnTheFlyStore
= GlobalStorage.Make
(struct
58 type t
= Naming_table.fast
61 (*****************************************************************************)
62 (* Re-declaring the types in a file *)
63 (*****************************************************************************)
65 let on_the_fly_decl_file ctx errors fn
=
66 let (decl_errors
, ()) =
67 Errors.do_with_context fn
Errors.Decl
(fun () ->
68 Decl.make_env ~sh
:SharedMem.Uses ctx fn
)
70 Errors.merge decl_errors errors
72 (*****************************************************************************)
73 (* Given a set of classes, compare the old and the new type and deduce
74 * what must be rechecked accordingly.
76 (*****************************************************************************)
78 let compute_classes_deps ctx old_classes new_classes acc classes
=
79 let (changed
, to_redecl
, to_recheck
) = acc
in
80 let ((rc
, rdd
, rdc
), old_classes_missing
) =
81 Decl_compare.get_classes_deps ~ctx old_classes new_classes classes
83 let changed = DepSet.union rc
changed in
84 let to_redecl = DepSet.union rdd
to_redecl in
85 let to_recheck = DepSet.union rdc
to_recheck in
86 ((changed, to_redecl, to_recheck), old_classes_missing
)
88 (*****************************************************************************)
89 (* Given a set of functions, compare the old and the new type and deduce
90 * what must be rechecked accordingly.
92 (*****************************************************************************)
94 let compute_funs_deps ctx old_funs
(changed, to_redecl, to_recheck) funs
=
95 let ((rc
, rdd
, rdc
), old_funs_missing
) =
96 Decl_compare.get_funs_deps ~ctx old_funs funs
98 let changed = DepSet.union rc
changed in
99 let to_redecl = DepSet.union rdd
to_redecl in
100 let to_recheck = DepSet.union rdc
to_recheck in
101 ((changed, to_redecl, to_recheck), old_funs_missing
)
103 (*****************************************************************************)
104 (* Given a set of typedefs, compare the old and the new type and deduce
105 * what must be rechecked accordingly.
107 (*****************************************************************************)
109 let compute_types_deps ctx old_types
(changed, to_redecl, to_recheck) types
=
110 let ((rc
, rdc
), old_types_missing
) =
111 Decl_compare.get_types_deps ~ctx old_types types
113 let changed = DepSet.union rc
changed in
114 let to_recheck = DepSet.union rdc
to_recheck in
115 ((changed, to_redecl, to_recheck), old_types_missing
)
117 (*****************************************************************************)
118 (* Given a set of global constants, compare the old and the new type and
119 * deduce what must be rechecked accordingly.
121 (*****************************************************************************)
123 let compute_gconsts_deps
124 ctx old_gconsts
(changed, to_redecl, to_recheck) gconsts
=
125 let ((rc
, rdd
, rdc
), old_gconsts_missing
) =
126 Decl_compare.get_gconsts_deps ~ctx old_gconsts gconsts
128 let changed = DepSet.union rc
changed in
129 let to_redecl = DepSet.union rdd
to_redecl in
130 let to_recheck = DepSet.union rdc
to_recheck in
131 ((changed, to_redecl, to_recheck), old_gconsts_missing
)
133 (*****************************************************************************)
134 (* Redeclares a list of files
135 * And then computes the files that must be redeclared/rechecked by looking
136 * at what changed in the signatures of the classes/functions.
138 (*****************************************************************************)
140 let redeclare_files ctx filel
=
142 List.fold_left filel ~f
:(on_the_fly_decl_file ctx
) ~init
:Errors.empty
144 (List.length filel
, errors)
146 let on_the_fly_decl_files filel
=
147 SharedMem.invalidate_local_caches
();
149 (* Redeclaring the files *)
150 redeclare_files filel
152 let compute_deps ctx fast
(filel
: Relative_path.t list
) =
153 let infol = List.map filel ~f
:(fun fn
-> Relative_path.Map.find fast fn
) in
155 List.fold_left
infol ~f
:FileInfo.merge_names ~init
:FileInfo.empty_names
157 let { FileInfo.n_classes
; n_funs
; n_types
; n_consts
} = names in
158 let empty = DepSet.make
() in
159 let acc = (empty, empty, empty) in
160 (* Fetching everything at once is faster *)
161 let old_funs = Decl_heap.Funs.get_old_batch n_funs
in
162 let (acc, old_funs_missing
) = compute_funs_deps ctx
old_funs acc n_funs
in
163 let old_types = Decl_heap.Typedefs.get_old_batch n_types
in
164 let (acc, old_types_missing
) = compute_types_deps ctx
old_types acc n_types
in
165 let old_consts = Decl_heap.GConsts.get_old_batch n_consts
in
166 let (acc, old_gconsts_missing
) =
167 compute_gconsts_deps ctx
old_consts acc n_consts
170 let (acc, old_classes_missing
) =
171 if shallow_decl_enabled ctx
|| force_shallow_decl_fanout_enabled ctx
then
174 let old_classes = Decl_heap.Classes.get_old_batch n_classes
in
175 let new_classes = Decl_heap.Classes.get_batch n_classes
in
176 compute_classes_deps ctx
old_classes new_classes acc n_classes
179 let old_decl_missing_count =
182 + old_gconsts_missing
183 + old_classes_missing
185 let (changed, to_redecl, to_recheck) = acc in
186 ((changed, to_redecl, to_recheck), old_decl_missing_count)
188 (*****************************************************************************)
189 (* Load the environment and then redeclare *)
190 (*****************************************************************************)
192 let load_and_on_the_fly_decl_files ctx _ filel
=
193 try on_the_fly_decl_files ctx filel
with
195 Printf.printf
"Error: %s\n" (Exn.to_string e
);
196 Out_channel.flush stdout
;
199 let load_and_compute_deps ctx _acc
(filel
: Relative_path.t list
) :
200 (DepSet.t
* DepSet.t
* DepSet.t
* int) * int =
202 let fast = OnTheFlyStore.load
() in
203 let ((changed, to_redecl, to_recheck), old_decl_missing_count) =
204 compute_deps ctx
fast filel
206 ((changed, to_redecl, to_recheck, List.length filel
), old_decl_missing_count)
209 Printf.printf
"Error: %s\n" (Exn.to_string e
);
210 Out_channel.flush stdout
;
213 (*****************************************************************************)
214 (* Merges the results coming back from the different workers *)
215 (*****************************************************************************)
218 files_initial_count files_declared_count
(count
, errorl1
) errorl2
=
219 files_declared_count
:= !files_declared_count
+ count
;
220 ServerProgress.send_percentage_progress
221 ~operation
:"declaring"
222 ~done_count
:!files_declared_count
223 ~total_count
:files_initial_count
227 Errors.merge errorl1 errorl2
229 let merge_compute_deps
232 ( (changed1
, to_redecl1
, to_recheck1
, computed_count
),
233 old_decl_missing_count1
)
234 ((changed2
, to_redecl2
, to_recheck2
), old_decl_missing_count2
) =
235 files_computed_count
:= !files_computed_count
+ computed_count
;
237 let (changed, to_redecl, to_recheck) =
238 ( DepSet.union changed1 changed2
,
239 DepSet.union to_redecl1 to_redecl2
,
240 DepSet.union to_recheck1 to_recheck2
)
243 ServerProgress.send_percentage_progress
244 ~operation
:"computing dependencies of"
245 ~done_count
:!files_computed_count
246 ~total_count
:files_initial_count
250 ( (changed, to_redecl, to_recheck),
251 old_decl_missing_count1
+ old_decl_missing_count2
)
253 (*****************************************************************************)
254 (* The parallel worker *)
255 (*****************************************************************************)
256 let parallel_on_the_fly_decl
257 (ctx
: Provider_context.t
)
258 (workers
: MultiWorker.worker list
option)
260 (fast : FileInfo.names Relative_path.Map.t
)
261 (fnl
: Relative_path.t list
) :
262 (Errors.t
* DepSet.t
* DepSet.t
* DepSet.t
) * int =
264 OnTheFlyStore.store
fast;
265 let files_initial_count = List.length fnl
in
266 let files_declared_count = ref 0 in
267 let t = Unix.gettimeofday
() in
268 Hh_logger.log ~
lvl "Declaring on-the-fly %d files" files_initial_count;
269 ServerProgress.send_percentage_progress
270 ~operation
:"declaring"
271 ~done_count
:!files_declared_count
272 ~total_count
:files_initial_count
278 ~job
:(load_and_on_the_fly_decl_files ctx
)
279 ~neutral
:on_the_fly_neutral
280 ~merge
:(merge_on_the_fly files_initial_count files_declared_count)
281 ~next
:(MultiWorker.next ~max_size
:bucket_size workers fnl
)
283 let t = Hh_logger.log_duration ~
lvl "Finished declaring on-the-fly" t in
284 Hh_logger.log ~
lvl "Computing dependencies of %d files" files_initial_count;
285 let files_computed_count = ref 0 in
286 ServerProgress.send_percentage_progress
287 ~operation
:"computing dependencies of"
288 ~done_count
:!files_computed_count
289 ~total_count
:files_initial_count
292 let ((changed, to_redecl, to_recheck), old_decl_missing_count) =
295 ~job
:(load_and_compute_deps ctx
)
296 ~neutral
:(compute_deps_neutral ())
297 ~merge
:(merge_compute_deps files_initial_count files_computed_count)
298 ~next
:(MultiWorker.next ~max_size
:bucket_size workers fnl
)
301 Hh_logger.log_duration ~
lvl "Finished computing dependencies" t
303 OnTheFlyStore.clear
();
304 ((errors, changed, to_redecl, to_recheck), old_decl_missing_count)
307 if SharedMem.SMTelemetry.is_heap_overflow
() then
308 Exit.exit
Exit_status.Redecl_heap_overflow
312 (*****************************************************************************)
313 (* Code invalidating the heap *)
314 (*****************************************************************************)
316 (ctx
: Provider_context.t)
317 { FileInfo.n_funs
; n_classes
; n_types
; n_consts
}
318 (elems
: Decl_class_elements.t SMap.t)
319 ~
(collect_garbage
: bool) : unit =
320 Decl_heap.Funs.oldify_batch n_funs
;
321 Decl_class_elements.oldify_all elems
;
322 Decl_heap.Classes.oldify_batch n_classes
;
323 Shallow_classes_provider.oldify_batch ctx n_classes
;
324 Decl_heap.Typedefs.oldify_batch n_types
;
325 Decl_heap.GConsts.oldify_batch n_consts
;
326 if collect_garbage
then SharedMem.GC.collect `gentle
;
330 (ctx
: Provider_context.t)
331 { FileInfo.n_funs
; n_classes
; n_types
; n_consts
}
332 (elems
: Decl_class_elements.t SMap.t) : unit =
333 Decl_heap.Funs.remove_old_batch n_funs
;
334 Decl_class_elements.remove_old_all elems
;
335 Decl_heap.Classes.remove_old_batch n_classes
;
336 Shallow_classes_provider.remove_old_batch ctx n_classes
;
337 Decl_heap.Typedefs.remove_old_batch n_types
;
338 Decl_heap.GConsts.remove_old_batch n_consts
;
339 SharedMem.GC.collect `gentle
;
343 (ctx
: Provider_context.t)
344 { FileInfo.n_funs
; n_classes
; n_types
; n_consts
}
345 (elems
: Decl_class_elements.t SMap.t)
346 ~
(collect_garbage
: bool) : unit =
347 Decl_heap.Funs.remove_batch n_funs
;
348 Decl_class_elements.remove_all elems
;
349 Decl_heap.Classes.remove_batch n_classes
;
350 Shallow_classes_provider.remove_batch ctx n_classes
;
351 Linearization_provider.remove_batch ctx n_classes
;
352 Decl_heap.Typedefs.remove_batch n_types
;
353 Decl_heap.GConsts.remove_batch n_consts
;
354 if collect_garbage
then SharedMem.GC.collect `gentle
;
357 let is_dependent_class_of_any ctx classes
(c
: string) : bool =
358 if SSet.mem classes c
then
360 else if shallow_decl_enabled ctx
then
363 match Decl_heap.Classes.get c
with
365 (* it might be a dependent class, but we are only doing this
366 * check for the purpose of invalidating things from the heap
367 * - if it's already not there, then we don't care. *)
369 let intersection_nonempty s1 s2
= SSet.exists s1 ~f
:(SSet.mem s2
) in
370 SMap.exists c
.Decl_defs.dc_ancestors ~f
:(fun c _
-> SSet.mem classes c
)
371 || intersection_nonempty c
.Decl_defs.dc_extends classes
372 || intersection_nonempty c
.Decl_defs.dc_xhp_attr_deps classes
373 || intersection_nonempty c
.Decl_defs.dc_req_ancestors_extends classes
375 let get_maybe_dependent_classes
376 (get_classes
: Relative_path.t -> SSet.t)
378 (files
: Relative_path.Set.t) : string list
=
379 Relative_path.Set.fold files ~init
:classes ~f
:(fun x
acc ->
380 SSet.union
acc @@ get_classes x
)
383 let get_dependent_classes_files (ctx
: Provider_context.t) (classes
: SSet.t) :
384 Relative_path.Set.t =
385 let mode = Provider_context.get_deps_mode ctx
in
386 let visited = VisitedSet.make
() in
389 ~init
:Typing_deps.(DepSet.make
())
391 let source_class = Dep.make
(Dep.Type c
) in
392 Typing_deps.get_extend_deps ~
mode ~
visited ~
source_class ~
acc)
393 |> Naming_provider.get_files ctx
395 let filter_dependent_classes
396 (ctx
: Provider_context.t)
398 (maybe_dependent_classes
: string list
) : string list
=
399 List.filter maybe_dependent_classes ~f
:(is_dependent_class_of_any ctx classes
)
401 module ClassSetStore
= GlobalStorage.Make
(struct
405 let load_and_filter_dependent_classes
406 (ctx
: Provider_context.t) (maybe_dependent_classes
: string list
) :
408 let classes = ClassSetStore.load
() in
409 ( filter_dependent_classes ctx
classes maybe_dependent_classes
,
410 List.length maybe_dependent_classes
)
412 let merge_dependent_classes
413 classes_initial_count
414 classes_filtered_count
415 (dependent_classes
, filtered
)
417 classes_filtered_count
:= !classes_filtered_count
+ filtered
;
418 ServerProgress.send_percentage_progress
419 ~operation
:"filtering"
420 ~done_count
:!classes_filtered_count
421 ~total_count
:classes_initial_count
424 dependent_classes
@ acc
426 let filter_dependent_classes_parallel
427 (ctx
: Provider_context.t)
428 (workers
: MultiWorker.worker list
option)
431 (maybe_dependent_classes
: string list
) : string list
=
432 if List.length maybe_dependent_classes
< 10 then
433 filter_dependent_classes ctx
classes maybe_dependent_classes
435 ClassSetStore.store
classes;
436 let classes_initial_count = List.length maybe_dependent_classes
in
437 let classes_filtered_count = ref 0 in
438 let t = Unix.gettimeofday
() in
439 Hh_logger.log ~
lvl "Filtering %d dependent classes" classes_initial_count;
440 ServerProgress.send_percentage_progress
441 ~operation
:"filtering"
442 ~done_count
:!classes_filtered_count
443 ~total_count
:classes_initial_count
449 ~job
:(fun _ c
-> load_and_filter_dependent_classes ctx c
)
451 (merge_dependent_classes classes_initial_count classes_filtered_count)
455 ~max_size
:bucket_size
457 maybe_dependent_classes
)
460 Hh_logger.log_duration ~
lvl "Finished filtering dependent classes" t
462 ClassSetStore.clear
();
466 let get_dependent_classes
467 (ctx
: Provider_context.t)
468 (workers
: MultiWorker.worker list
option)
470 (get_classes
: Relative_path.t -> SSet.t)
471 (classes : SSet.t) : SSet.t =
472 get_dependent_classes_files ctx
classes
473 |> get_maybe_dependent_classes get_classes
classes
474 |> filter_dependent_classes_parallel ctx workers ~bucket_size
classes
478 classes_initial_count classes_processed_count
(elements
, count
) acc =
479 classes_processed_count
:= !classes_processed_count
+ count
;
481 let acc = SMap.union elements
acc in
482 ServerProgress.send_percentage_progress
483 ~operation
:"getting members of"
484 ~done_count
:!classes_processed_count
485 ~total_count
:classes_initial_count
487 ~extra
:(Some
(Printf.sprintf
"%d elements" (SMap.cardinal
acc)));
491 * Get the [Decl_class_elements.t]s corresponding to the classes contained in
494 (ctx
: Provider_context.t)
495 (workers
: MultiWorker.worker list
option)
498 (defs
: FileInfo.names) : Decl_class_elements.t SMap.t =
499 if shallow_decl_enabled ctx
then
502 let classes = SSet.elements defs
.FileInfo.n_classes
in
503 (* Getting the members of a class requires fetching the class from the heap.
504 * Doing this for too many classes will cause a large amount of allocations
505 * to be performed on the master process triggering the GC and slowing down
506 * redeclaration. Using the workers prevents this from occurring
508 let classes_initial_count = List.length
classes in
509 let t = Unix.gettimeofday
() in
510 Hh_logger.log ~
lvl "Getting elements of %d classes" classes_initial_count;
512 if classes_initial_count < 10 then
513 Decl_class_elements.get_for_classes ~old
classes
515 let classes_processed_count = ref 0 in
516 ServerProgress.send_percentage_progress
517 ~operation
:"getting members of"
518 ~done_count
:!classes_processed_count
519 ~total_count
:classes_initial_count
525 (Decl_class_elements.get_for_classes ~old c
, List.length c
))
526 ~merge
:(merge_elements classes_initial_count classes_processed_count)
528 ~next
:(MultiWorker.next ~max_size
:bucket_size workers
classes)
532 Hh_logger.log_duration ~
lvl "Finished getting elements" t
536 let invalidate_folded_classes_for_shallow_fanout
537 ctx workers ~bucket_size ~get_classes_in_file changed_classes
=
540 |> Typing_deps.add_extend_deps
(Provider_context.get_deps_mode ctx
)
541 |> Shallow_class_fanout.class_names_from_deps ~ctx ~get_classes_in_file
543 let get_elems n_classes
=
544 get_elems ctx workers ~bucket_size
FileInfo.{ empty_names
with n_classes
}
546 Decl_class_elements.remove_old_all
(get_elems invalidated ~old
:true);
547 Decl_class_elements.remove_all
(get_elems invalidated ~old
:false);
548 Decl_heap.Classes.remove_old_batch
invalidated;
549 Decl_heap.Classes.remove_batch
invalidated;
550 SharedMem.GC.collect `gentle
;
553 (*****************************************************************************)
554 (* The main entry point *)
555 (*****************************************************************************)
558 (ctx
: Provider_context.t)
559 (workers
: MultiWorker.worker list
option)
561 (get_classes
: Relative_path.t -> SSet.t)
562 ~
(previously_oldified_defs
: FileInfo.names)
563 ~
(defs
: FileInfo.names Relative_path.Map.t) : redo_type_decl_result
=
565 Relative_path.Map.fold defs ~init
:FileInfo.empty_names ~f
:(fun _
->
566 FileInfo.merge_names
)
568 (* Some of the defintions are already in the old heap, left there by a
569 * previous lazy check *)
570 let (oldified_defs
, current_defs
) =
571 Decl_utils.split_defs
all_defs previously_oldified_defs
573 (* Oldify the remaining defs along with their elements *)
574 let get_elems = get_elems ctx workers ~bucket_size
in
575 let current_elems = get_elems current_defs ~old
:false in
576 oldify_defs ctx current_defs
current_elems ~collect_garbage
:true;
578 (* Fetch the already oldified elements too so we can remove them later *)
579 let oldified_elems = get_elems oldified_defs ~old
:true in
580 let all_elems = SMap.union
current_elems oldified_elems in
581 let fnl = Relative_path.Map.keys defs
in
583 (* If there aren't enough files, let's do this ourselves ... it's faster! *)
584 let ((errors, changed, to_redecl, to_recheck), old_decl_missing_count) =
585 if List.length
fnl < 10 then
586 let ((_declared
: int), errors) = on_the_fly_decl_files ctx
fnl in
587 let ((changed, to_redecl, to_recheck), old_decl_missing_count) =
588 compute_deps ctx defs
fnl
590 ((errors, changed, to_redecl, to_recheck), old_decl_missing_count)
592 parallel_on_the_fly_decl ctx workers bucket_size defs
fnl
594 let (changed, to_recheck) =
595 if shallow_decl_enabled ctx
then (
596 let AffectedDeps.{ changed = changed'
; mro_invalidated
; needs_recheck
} =
597 Shallow_decl_compare.compute_class_fanout
600 ~fetch_old_decls
:(Remote_old_decl_client.fetch_old_decls ~ctx
)
603 let changed = DepSet.union
changed changed'
in
604 let to_recheck = DepSet.union
to_recheck needs_recheck
in
605 let mro_invalidated =
607 |> Naming_provider.get_files ctx
608 |> Relative_path.Set.fold ~init
:SSet.empty ~f
:(fun path
acc ->
609 SSet.union
acc (get_classes path
))
611 Linearization_provider.remove_batch ctx
mro_invalidated;
612 (changed, to_recheck)
613 ) else if force_shallow_decl_fanout_enabled ctx
then (
615 { changed = changed'
; mro_invalidated = _
; needs_recheck
} =
616 Shallow_decl_compare.compute_class_fanout
619 ~fetch_old_decls
:(Remote_old_decl_client.fetch_old_decls ~ctx
)
623 invalidate_folded_classes_for_shallow_fanout
627 ~get_classes_in_file
:get_classes
630 let changed = DepSet.union
changed changed'
in
631 let to_recheck = DepSet.union
to_recheck needs_recheck
in
633 (changed, to_recheck)
635 (changed, to_recheck)
637 remove_old_defs ctx
all_defs all_elems;
639 Hh_logger.log
"Finished recomputing type declarations:";
640 Hh_logger.log
" changed: %d" (DepSet.cardinal
changed);
641 Hh_logger.log
" to_redecl: %d" (DepSet.cardinal
to_redecl);
642 Hh_logger.log
" to_recheck: %d" (DepSet.cardinal
to_recheck);
644 { errors; changed; to_redecl; to_recheck; old_decl_missing_count }
647 (ctx
: Provider_context.t)
648 ?
(collect_garbage
= true)
649 (workers
: MultiWorker.worker list
option)
650 (get_classes
: Relative_path.t -> SSet.t)
652 ~
(previously_oldified_defs
: FileInfo.names)
653 ~
(defs
: FileInfo.names) : unit =
654 (* Some defs are already oldified, waiting for their recheck *)
655 let (oldified_defs
, current_defs
) =
656 Decl_utils.split_defs defs previously_oldified_defs
658 let get_elems = get_elems ctx workers ~bucket_size
in
659 (* Oldify things that are not oldified yet *)
660 let current_elems = get_elems current_defs ~old
:false in
661 oldify_defs ctx current_defs
current_elems ~collect_garbage
;
663 (* For the rest, just invalidate their current versions *)
664 let oldified_elems = get_elems oldified_defs ~old
:false in
665 remove_defs ctx oldified_defs
oldified_elems ~collect_garbage
;
667 (* Oldifying/removing classes also affects their elements
668 * (see Decl_class_elements), which might be shared with other classes. We
669 * need to remove all of them too to avoid dangling references *)
670 let all_classes = defs
.FileInfo.n_classes
in
671 let dependent_classes =
672 get_dependent_classes ctx workers get_classes ~bucket_size
all_classes
674 let dependent_classes =
676 { empty_names
with n_classes
= SSet.diff
dependent_classes all_classes }
678 remove_defs ctx
dependent_classes SMap.empty ~collect_garbage
681 (ctx
: Provider_context.t)
683 (workers
: MultiWorker.worker list
option)
684 (names : FileInfo.names) : unit =
685 let elems = get_elems ctx workers ~bucket_size
names ~old
:true in
686 remove_old_defs ctx
names elems