Global fallback for constants
[hiphop-php.git] / hphp / hack / src / typing / typing_compare.ml
blob841969b0cde7e987f6c6189b0b04ba2472bd9131
1 (**
2 * Copyright (c) 2014, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the "hack" directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
9 *)
12 (*****************************************************************************)
13 (* Module used when we want to figure out what has changed.
14 * 1) The user modifies a file
15 * 2) We compute the type of the file (cf typing_redecl_service.ml)
16 * 3) We compare the old type and the new type of the class (or function)
17 * to see if anything has changed. This is where the code of this module
18 * comes into play. This code compares the old and the new class type
19 * and computes the set of dependencies if something needs to be
20 * either redeclared or checked again.
22 (*****************************************************************************)
23 open Utils
24 open Typing_defs
25 open Typing_deps
27 module Env = Typing_env
29 (*****************************************************************************)
30 (* Module comparing types "modulo" positions.
31 * It returns a substitution (from positions to position) and a true when
32 * both types are equivalent.
33 * Let's imagine we have ty1 ty2:
34 * 1) They are equivalent, and all the positions are the same
35 * CompareTypes.ty ty1 ty2 = [], true
36 * 2) They are equivalent, but a position differs:
37 * CompareTypes.ty ty1 ty2 = subst, true
38 * where (apply_subst subst ty1) = ty2
39 * 3) They are different:
40 * CompareTypes.ty ty1 ty2 = _, false
42 (*****************************************************************************)
43 module CompareTypes = struct
45 type result = (Pos.t * Pos.t) list * bool
47 let default = [], false
49 let string_id (subst, same) (p1, s1) (p2, s2) =
50 if s1 <> s2
51 then default
52 else (p1, p2) :: subst, same
54 let pos (subst, same as acc) p1 p2 =
55 if p1 = p2
56 then acc
57 else (p1, p2) :: subst, same
59 let reason acc r1 r2 =
60 let p1 = Typing_reason.to_pos r1 in
61 let p2 = Typing_reason.to_pos r2 in
62 pos acc p1 p2
64 let smap f acc smap1 smap2 =
65 let acc =
66 SMap.fold begin fun x ce1 acc ->
67 try
68 let ce2 = SMap.find_unsafe x smap2 in
69 f acc ce1 ce2
70 with Not_found -> default
71 end smap1 acc
73 (* Checking if all the members in smap2 are defined in smap1 *)
74 try
75 SMap.iter (fun x _ -> ignore (SMap.find_unsafe x smap1)) smap2;
76 acc
77 with Not_found -> default
79 let rec ty acc (r1, x) (r2, y) =
80 let acc = reason acc r1 r2 in
81 let acc = ty_ acc x y in
82 acc
84 and ty_ (subst, same as acc) ty1 ty2 =
85 match ty1, ty2 with
86 | Tobject, Tobject
87 | Tany, Tany
88 | Tmixed, Tmixed -> acc
89 | Tarray (b1, ty1, ty2), Tarray (b2, ty3, ty4) ->
90 let same = same && b1 = b2 in
91 let acc = ty_opt (subst, same) ty1 ty3 in
92 let acc = ty_opt acc ty2 ty4 in
93 acc
94 | Tgeneric (s1, x), Tgeneric (s2, y) ->
95 let same = same && s1 = s2 in
96 let acc = ty_opt (subst, same) x y in
97 acc
98 | Toption ty1, Toption ty2 ->
99 ty acc ty1 ty2
100 | Tprim x, Tprim y ->
101 subst, same && x = y
102 | Tvar x, Tvar y ->
103 subst, same && x = y
104 | Tfun f1, Tfun f2 ->
105 fun_type acc f1 f2
106 | Tabstract (sid1, tyl1, cstr1), Tabstract (sid2, tyl2, cstr2) ->
107 let acc = ty_opt acc cstr1 cstr2 in
108 let acc = string_id acc sid1 sid2 in
109 let acc = tyl acc tyl1 tyl2 in
111 | Tapply (sid1, tyl1), Tapply (sid2, tyl2) ->
112 let acc = string_id acc sid1 sid2 in
113 let acc = tyl acc tyl1 tyl2 in
115 | Tunresolved tyl1, Tunresolved tyl2
116 | Ttuple tyl1, Ttuple tyl2 ->
117 tyl acc tyl1 tyl2
118 | Tanon (x1, y1, z1), Tanon (x2, y2, z2) ->
119 subst, same && x1 = x2 && y1 = y2 && z1 = z2
120 | Tshape fdm1, Tshape fdm2 ->
121 SMap.fold begin fun name v1 acc ->
122 match SMap.get name fdm2 with
123 | None -> default
124 | Some v2 ->
125 ty acc v1 v2
126 end fdm1 acc
127 | (Tanon _|Tany|Tmixed|Tarray (_, _, _)| Tshape _ |
128 Tgeneric (_, _)|Toption _|Tprim _|Tvar _| Tabstract _ |
129 Tfun _|Tapply (_, _)|Ttuple _|Tunresolved _|Tobject), _ -> default
131 and tyl acc tyl1 tyl2 =
132 if List.length tyl1 <> List.length tyl2
133 then default
134 else List.fold_left2 ty acc tyl1 tyl2
136 and ty_opt acc ty1 ty2 =
137 match ty1, ty2 with
138 | None, None -> acc
139 | Some ty1, Some ty2 -> ty acc ty1 ty2
140 | _ -> default
142 and fun_type acc ft1 ft2 =
143 let acc = pos acc ft1.ft_pos ft2.ft_pos in
144 let acc = tparam_list acc ft1.ft_tparams ft2.ft_tparams in
145 let acc = fun_params acc ft1.ft_params ft2.ft_params in
146 let subst, same = ty acc ft1.ft_ret ft2.ft_ret in
147 subst,
148 same &&
149 ft1.ft_unsafe = ft2.ft_unsafe &&
150 ft1.ft_abstract = ft2.ft_abstract &&
151 ft1.ft_arity_min = ft2.ft_arity_min &&
152 ft1.ft_arity_max = ft2.ft_arity_max
154 and fun_params acc params1 params2 =
155 if List.length params1 <> List.length params2
156 then default
157 else List.fold_left2 fun_param acc params1 params2
159 and fun_param acc (name1, ty1) (name2, ty2) =
160 if name1 <> name2
161 then default
162 else ty acc ty1 ty2
164 and tparam_list acc tpl1 tpl2 =
165 if List.length tpl1 <> List.length tpl2
166 then default
167 else List.fold_left2 tparam acc tpl1 tpl2
169 and tparam acc (sid1, x1) (sid2, x2) =
170 let acc = string_id acc sid1 sid2 in
171 let acc = ty_opt acc x1 x2 in
174 and class_elt (subst, same) celt1 celt2 =
175 let same = same && celt1.ce_visibility = celt2.ce_visibility in
176 let same = same && celt1.ce_final = celt2.ce_final in
177 let same = same && celt1.ce_override = celt2.ce_override in
178 ty (subst, same) celt1.ce_type celt2.ce_type
180 and members acc m1 m2 = smap class_elt acc m1 m2
182 and constructor acc c1 c2 =
183 match c1, c2 with
184 | Some x1, Some x2 -> class_elt acc x1 x2
185 | _ -> acc
187 and implements acc imp1 imp2 = smap ty acc imp1 imp2
189 and class_ (subst, same) c1 c2 =
190 let same =
191 c1.tc_final = c2.tc_final &&
192 c1.tc_need_init = c2.tc_need_init &&
193 c1.tc_members_fully_known = c2.tc_members_fully_known &&
194 c1.tc_abstract = c2.tc_abstract &&
195 c1.tc_kind = c2.tc_kind &&
196 c1.tc_name = c2.tc_name &&
197 SSet.compare c1.tc_members_init c2.tc_members_init = 0 &&
198 SSet.compare c1.tc_extends c2.tc_extends = 0 &&
199 SSet.compare c1.tc_req_ancestors_extends c2.tc_req_ancestors_extends = 0 &&
200 SSet.compare c1.tc_req_ancestors c2.tc_req_ancestors = 0
202 let acc = subst, same in
203 let acc = tparam_list acc c1.tc_tparams c2.tc_tparams in
204 let acc = members acc c1.tc_consts c2.tc_consts in
205 let acc = members acc c1.tc_cvars c2.tc_cvars in
206 let acc = members acc c1.tc_scvars c2.tc_scvars in
207 let acc = members acc c1.tc_methods c2.tc_methods in
208 let acc = members acc c1.tc_smethods c2.tc_smethods in
209 let acc = constructor acc c1.tc_construct c2.tc_construct in
210 let acc = implements acc c1.tc_ancestors c2.tc_ancestors in
211 let acc = implements acc c1.tc_ancestors_checked_when_concrete c2.tc_ancestors_checked_when_concrete in
216 (*****************************************************************************)
217 (* Functor traversing a type, but applies a user defined function for
218 * positions.
220 (*****************************************************************************)
221 module TraversePos(ImplementPos: sig val pos: Pos.t -> Pos.t end) = struct
222 open Typing_reason
224 let pos = ImplementPos.pos
226 let rec reason = function
227 | Rnone -> Rnone
228 | Rwitness p -> Rwitness (pos p)
229 | Ridx p -> Ridx (pos p)
230 | Ridx_vector p -> Ridx_vector (pos p)
231 | Rappend p -> Rappend (pos p)
232 | Rfield p -> Rfield (pos p)
233 | Rforeach p -> Rforeach (pos p)
234 | Raccess p -> Raccess (pos p)
235 | Rcall p -> Rcall (pos p)
236 | Rarith p -> Rarith (pos p)
237 | Rarith_ret p -> Rarith_ret (pos p)
238 | Rstring2 p -> Rstring2 (pos p)
239 | Rcomp p -> Rcomp (pos p)
240 | Rconcat p -> Rconcat (pos p)
241 | Rconcat_ret p -> Rconcat_ret (pos p)
242 | Rlogic p -> Rlogic (pos p)
243 | Rlogic_ret p -> Rlogic_ret (pos p)
244 | Rbitwise p -> Rbitwise (pos p)
245 | Rbitwise_ret p -> Rbitwise_ret (pos p)
246 | Rstmt p -> Rstmt (pos p)
247 | Rno_return p -> Rno_return (pos p)
248 | Rno_return_async p -> Rno_return_async (pos p)
249 | Rhint p -> Rhint (pos p)
250 | Rnull_check p -> Rnull_check (pos p)
251 | Rnot_in_cstr p -> Rnot_in_cstr (pos p)
252 | Rthrow p -> Rthrow (pos p)
253 | Rattr p -> Rattr (pos p)
254 | Rxhp p -> Rxhp (pos p)
255 | Rret_div p -> Rret_div (pos p)
256 | Rlost_info (s, r1, p2) -> Rlost_info (s, reason r1, pos p2)
257 | Rcoerced (p1, p2, x) -> Rcoerced (pos p1, pos p2, x)
258 | Rformat (p1, s, r) -> Rformat (pos p1, s, reason r)
259 | Rclass_class (p, s) -> Rclass_class (pos p, s)
260 | Runknown_class p -> Runknown_class (pos p)
261 | Rdynamic_yield (p1, p2, s1, s2) -> Rdynamic_yield(pos p1, pos p2, s1, s2)
262 | Rmap_append p -> Rmap_append (pos p)
264 let string_id (p, x) = pos p, x
266 let rec ty (p, x) =
267 reason p, ty_ x
269 and ty_ = function
270 | Tanon _
271 | Tvar _ -> raise (Error [Pos.none, "internal error"])
272 | Tany
273 | Tmixed as x -> x
274 | Tarray (b, ty1, ty2) -> Tarray (b, ty_opt ty1, ty_opt ty2)
275 | Tprim _ as x -> x
276 | Tgeneric (s, t) -> Tgeneric (s, ty_opt t)
277 | Ttuple tyl -> Ttuple (List.map (ty) tyl)
278 | Tunresolved tyl -> Tunresolved (List.map (ty) tyl)
279 | Toption x -> Toption (ty x)
280 | Tfun ft -> Tfun (fun_type ft)
281 | Tapply (sid, xl) -> Tapply (string_id sid, List.map (ty) xl)
282 | Tabstract (sid, xl, x) ->
283 Tabstract (string_id sid, List.map (ty) xl, ty_opt x)
284 | Tobject as x -> x
285 | Tshape fdm -> Tshape (SMap.map ty fdm)
287 and ty_opt = function
288 | None -> None
289 | Some x -> Some (ty x)
291 and fun_type ft =
292 { ft with
293 ft_tparams = List.map (type_param) ft.ft_tparams ;
294 ft_params = List.map fun_param ft.ft_params ;
295 ft_ret = ty ft.ft_ret ;
296 ft_pos = pos ft.ft_pos ;
299 and fun_param (x, y) = x, ty y
301 and class_elt ce =
302 { ce_final = ce.ce_final ;
303 ce_override = ce.ce_override ;
304 ce_visibility = ce.ce_visibility ;
305 ce_type = ty ce.ce_type ;
306 ce_origin = ce.ce_origin;
309 and type_param (sid, y) =
310 string_id sid, ty_opt y
312 and class_type tc =
313 { tc_final = tc.tc_final ;
314 tc_need_init = tc.tc_need_init ;
315 tc_members_init = tc.tc_members_init ;
316 tc_abstract = tc.tc_abstract ;
317 tc_members_fully_known = tc.tc_members_fully_known ;
318 tc_kind = tc.tc_kind ;
319 tc_name = tc.tc_name ;
320 tc_extends = tc.tc_extends ;
321 tc_req_ancestors = tc.tc_req_ancestors ;
322 tc_req_ancestors_extends = tc.tc_req_ancestors_extends ;
323 tc_tparams = List.map type_param tc.tc_tparams ;
324 tc_consts = SMap.map class_elt tc.tc_consts ;
325 tc_cvars = SMap.map class_elt tc.tc_cvars ;
326 tc_scvars = SMap.map class_elt tc.tc_scvars ;
327 tc_methods = SMap.map class_elt tc.tc_methods ;
328 tc_smethods = SMap.map class_elt tc.tc_smethods ;
329 tc_construct = constructor tc.tc_construct ;
330 tc_ancestors = SMap.map ty tc.tc_ancestors ;
331 tc_ancestors_checked_when_concrete = SMap.map ty tc.tc_ancestors_checked_when_concrete ;
332 tc_user_attributes = tc.tc_user_attributes ;
333 tc_prefetch_classes = tc.tc_prefetch_classes ;
334 tc_prefetch_funs = tc.tc_prefetch_funs ;
335 tc_mtime = 0.0 ;
338 and constructor = function
339 | None -> None
340 | Some c -> Some (class_elt c)
342 and typedef = function
343 | Typing_env.Typedef.Error as x -> x
344 | Typing_env.Typedef.Ok (is_abstract, tparams, tcstr, h) ->
345 let tparams = List.map type_param tparams in
346 let tcstr = ty_opt tcstr in
347 let tdef = (is_abstract, tparams, tcstr, ty h) in
348 Typing_env.Typedef.Ok tdef
351 (*****************************************************************************)
352 (* Applies a position substitution to a class type *)
353 (*****************************************************************************)
354 module SubstPos = struct
355 let class_type subst c =
356 let module IPos = struct
357 let pos x =
358 try Hashtbl.find subst x with Not_found -> x
359 end in
360 let module Apply = TraversePos(IPos) in
361 Apply.class_type c
364 (*****************************************************************************)
365 (* Returns a signature with all the positions replaced with Pos.none *)
366 (*****************************************************************************)
367 module NormalizeSig = struct
368 include TraversePos(struct let pos _ = Pos.none end)
371 (*****************************************************************************)
372 (* Given two classes give back the set of functions or classes that need
373 * to be rechecked
375 (*****************************************************************************)
376 module ClassDiff = struct
378 type t = {
379 consts : SSet.t ;
380 cvars : SSet.t ;
381 scvars : SSet.t ;
382 methods : SSet.t ;
383 smethods : SSet.t ;
384 cstr : bool ;
387 let smap_left s1 s2 =
388 SMap.fold begin fun x ty1 diff ->
389 let ty2 = SMap.get x s2 in
390 match ty2 with
391 | Some ty2 ->
392 if ty1 = ty2 then diff else
393 SSet.add x diff
394 | None ->
395 SSet.add x diff
396 end s1 SSet.empty
398 let smap s1 s2 =
399 SSet.union (smap_left s1 s2) (smap_left s2 s1)
401 let add_inverted_dep build_obj x acc =
402 ISet.union (Typing_deps.get_ideps (build_obj x)) acc
404 let add_inverted_deps acc build_obj xset =
405 SSet.fold (add_inverted_dep build_obj) xset acc
407 let compare cid class1 class2 =
408 let acc = ISet.empty in
409 let is_unchanged = true in
411 (* compare class constants *)
412 let consts_diff = smap class1.tc_consts class2.tc_consts in
413 let is_unchanged = is_unchanged && SSet.is_empty consts_diff in
414 let acc = add_inverted_deps acc (fun x -> Dep.Const (cid, x)) consts_diff in
416 (* compare class members *)
417 let cvars_diff = smap class1.tc_cvars class2.tc_cvars in
418 let is_unchanged = is_unchanged && SSet.is_empty cvars_diff in
419 let acc = add_inverted_deps acc (fun x -> Dep.CVar (cid, x)) cvars_diff in
421 (* compare class static members *)
422 let scvars_diff = smap class1.tc_scvars class2.tc_scvars in
423 let is_unchanged = is_unchanged && SSet.is_empty scvars_diff in
424 let acc = add_inverted_deps acc (fun x -> Dep.SCVar (cid, x)) scvars_diff in
426 (* compare class methods *)
427 let methods_diff = smap class1.tc_methods class2.tc_methods in
428 let is_unchanged = is_unchanged && SSet.is_empty methods_diff in
429 let acc = add_inverted_deps acc (fun x -> Dep.Method (cid, x)) methods_diff in
431 (* compare class static methods *)
432 let smethods_diff = smap class1.tc_smethods class2.tc_smethods in
433 let is_unchanged = is_unchanged && SSet.is_empty smethods_diff in
434 let acc = add_inverted_deps acc (fun x -> Dep.SMethod (cid, x)) smethods_diff in
436 (* compare class constructors *)
437 let cstr_diff = class1.tc_construct <> class2.tc_construct in
438 let is_unchanged = is_unchanged && not cstr_diff in
439 let cstr_ideps = Typing_deps.get_ideps (Dep.Cstr cid) in
440 let acc = if cstr_diff then ISet.union acc cstr_ideps else acc in
442 acc, is_unchanged
446 (*****************************************************************************)
447 (* Determines if there is a "big" difference between two classes
448 * What it really means: most of the time, a change in a class doesn't affect
449 * the users of the class, recomputing the sub-classes is enough.
450 * However, there are some cases, where we really need to re-check all the
451 * use cases of a class. For example: if a class doesn't implement an
452 * interface anymore, all the subtyping is changed, so we have to recheck
453 * all the places where the class was used.
455 (*****************************************************************************)
456 let class_big_diff class1 class2 =
457 let class1 = NormalizeSig.class_type class1 in
458 let class2 = NormalizeSig.class_type class2 in
459 class1.tc_need_init <> class2.tc_need_init ||
460 SSet.compare class1.tc_members_init class2.tc_members_init <> 0 ||
461 class1.tc_members_fully_known <> class2.tc_members_fully_known ||
462 class1.tc_kind <> class2.tc_kind ||
463 class1.tc_tparams <> class2.tc_tparams ||
464 SMap.compare class1.tc_ancestors class2.tc_ancestors <> 0 ||
465 SMap.compare class1.tc_ancestors_checked_when_concrete class2.tc_ancestors_checked_when_concrete <> 0 ||
466 SSet.compare class1.tc_req_ancestors class2.tc_req_ancestors <> 0 ||
467 SSet.compare class1.tc_req_ancestors_extends class2.tc_req_ancestors_extends <> 0 ||
468 SSet.compare class1.tc_extends class2.tc_extends <> 0
470 (*****************************************************************************)
471 (* Given a class name adds all the subclasses, we need a "trace" to follow
472 * what we have already added.
474 (*****************************************************************************)
475 let rec get_extend_deps trace cid_hash to_redecl =
476 if ISet.mem cid_hash !trace
477 then to_redecl
478 else begin
479 trace := ISet.add cid_hash !trace;
480 let cid_hash = Typing_deps.Dep.extends_of_class cid_hash in
481 let ideps = Typing_deps.get_ideps_from_hash cid_hash in
482 ISet.fold begin fun obj acc ->
483 if Typing_deps.Dep.is_class obj
484 then
485 let to_redecl = ISet.add obj acc in
486 get_extend_deps trace obj to_redecl
487 else to_redecl
488 end ideps to_redecl
491 (*****************************************************************************)
492 (* GET EVERYTHING, don't think, don't try to be subtle, don't try to be
493 * smart what so ever, just get EVERYTHING that ever used the class "cid"
494 * (cid = class identifier).
495 * Hence the name "get_bazooka".
497 (*****************************************************************************)
498 and get_all_dependencies trace cid (to_redecl, to_recheck) =
499 let bazooka = Typing_deps.get_bazooka (Dep.Class cid) in
500 let to_redecl = ISet.union bazooka to_redecl in
501 let to_recheck = ISet.union bazooka to_recheck in
502 let cid_hash = Typing_deps.Dep.make (Dep.Class cid) in
503 let to_redecl = get_extend_deps trace cid_hash to_redecl in
504 to_redecl, to_recheck
506 (*****************************************************************************)
507 (* Determine which functions/classes have to be rechecked after comparing
508 * the old and the new type signature of "fid" (function identifier).
510 (*****************************************************************************)
511 let get_fun_deps old_funs fid (to_redecl, to_recheck) =
512 match SMap.find_unsafe fid old_funs, Env.Funs.get fid with
513 | None, None ->
514 to_redecl, to_recheck
515 | None, _ | _, None ->
516 let where_fun_is_used = Typing_deps.get_bazooka (Dep.Fun fid) in
517 let to_recheck = ISet.union where_fun_is_used to_recheck in
518 let fun_name = Typing_deps.get_bazooka (Dep.FunName fid) in
519 ISet.union fun_name to_redecl, ISet.union fun_name to_recheck
520 | Some fty1, Some fty2 ->
521 let fty1 = NormalizeSig.fun_type fty1 in
522 let fty2 = NormalizeSig.fun_type fty2 in
523 let is_same_signature = fty1 = fty2 in
524 if is_same_signature
525 then to_redecl, to_recheck
526 else
527 (* No need to add Dep.FunName stuff here -- we found a function with the
528 * right name already otherwise we'd be in the None case above. *)
529 let where_fun_is_used = Typing_deps.get_bazooka (Dep.Fun fid) in
530 to_redecl, ISet.union where_fun_is_used to_recheck
532 let get_funs_deps old_funs funs =
533 SSet.fold (get_fun_deps old_funs) funs (ISet.empty, ISet.empty)
535 (*****************************************************************************)
536 (* Determine which functions/classes have to be rechecked after comparing
537 * the old and the new typedef
539 (*****************************************************************************)
540 let get_type_deps old_types tid to_recheck =
541 match SMap.find_unsafe tid old_types, Env.Typedefs.get tid with
542 | None, None ->
543 to_recheck
544 | None, _ | _, None ->
545 let bazooka = Typing_deps.get_bazooka (Dep.Class tid) in
546 ISet.union bazooka to_recheck
547 | Some tdef1, Some tdef2 ->
548 let tdef1 = NormalizeSig.typedef tdef1 in
549 let tdef2 = NormalizeSig.typedef tdef2 in
550 let is_same_signature = tdef1 = tdef2 in
551 if is_same_signature
552 then to_recheck
553 else
554 let where_type_is_used = Typing_deps.get_ideps (Dep.Class tid) in
555 let to_recheck = ISet.union where_type_is_used to_recheck in
556 to_recheck
558 let get_types_deps old_types types =
559 SSet.fold (get_type_deps old_types) types ISet.empty
561 (*****************************************************************************)
562 (* Determine which top level definitions have to be rechecked if the constant
563 * changed.
565 (*****************************************************************************)
566 let get_gconst_deps old_gconsts cst_id (to_redecl, to_recheck) =
567 match SMap.find_unsafe cst_id old_gconsts, Env.GConsts.get cst_id with
568 | None, None ->
569 to_redecl, to_recheck
570 | None, _ | _, None ->
571 let where_const_is_used = Typing_deps.get_bazooka (Dep.GConst cst_id) in
572 let to_recheck = ISet.union where_const_is_used to_recheck in
573 let const_name = Typing_deps.get_bazooka (Dep.GConstName cst_id) in
574 ISet.union const_name to_redecl, ISet.union const_name to_recheck
575 | Some cst1, Some cst2 ->
576 let is_same_signature = cst1 = cst2 in
577 if is_same_signature
578 then to_redecl, to_recheck
579 else
580 let where_type_is_used = Typing_deps.get_ideps (Dep.GConst cst_id) in
581 let to_recheck = ISet.union where_type_is_used to_recheck in
582 to_redecl, to_recheck
584 let get_gconsts_deps old_gconsts gconsts =
585 SSet.fold (get_gconst_deps old_gconsts) gconsts (ISet.empty, ISet.empty)
587 (*****************************************************************************)
588 (* Determine which functions/classes have to be rechecked after comparing
589 * the old and the new type signature of "cid" (class identifier).
591 (*****************************************************************************)
592 let get_class_deps old_classes new_classes trace cid (to_redecl, to_recheck) =
593 match SMap.find_unsafe cid old_classes, SMap.find_unsafe cid new_classes with
594 | None, None -> to_redecl, to_recheck
595 | None, _ | _, None ->
596 get_all_dependencies trace cid (to_redecl, to_recheck)
597 | Some class1, Some class2 when class_big_diff class1 class2 ->
598 get_all_dependencies trace cid (to_redecl, to_recheck)
599 | Some class1, Some class2 ->
600 let nclass1 = NormalizeSig.class_type class1 in
601 let nclass2 = NormalizeSig.class_type class2 in
602 let deps, is_unchanged = ClassDiff.compare cid nclass1 nclass2 in
603 let cid_hash = Typing_deps.Dep.make (Dep.Class cid) in
604 if is_unchanged
605 then
606 let _, is_unchanged = ClassDiff.compare cid class1 class2 in
607 if is_unchanged
608 then to_redecl, to_recheck
609 else
610 (* If we reach this case it means that class1 and class2
611 * have the same signatures, but that some of their
612 * positions differ. We therefore must redeclare the sub-classes
613 * but not recheck them.
615 let to_redecl = get_extend_deps trace cid_hash to_redecl in
616 to_redecl, to_recheck
617 else
618 let to_redecl = get_extend_deps trace cid_hash to_redecl in
619 let to_recheck = ISet.union to_redecl to_recheck in
620 ISet.union deps to_redecl, ISet.union deps to_recheck
622 let get_classes_deps old_classes new_classes trace classes =
623 SSet.fold
624 (get_class_deps old_classes new_classes trace)
625 classes
626 (ISet.empty, ISet.empty)
628 (*****************************************************************************)
629 (* When the type of a class didn't change, returns a substitution from
630 * the positions in the old type to the positions in the new ones.
631 * The idea is applying this substitution is enough to compute the new type
632 * of all the subclasses. No need to fully redeclare all of them.
634 (*****************************************************************************)
635 let get_classes_psubst old_classes new_classes classes =
636 let subst = Hashtbl.create 23 in
637 let is_empty = ref true in
638 SSet.iter begin fun cid ->
639 match SMap.find_unsafe cid old_classes, SMap.find_unsafe cid new_classes with
640 | Some class1, Some class2 ->
641 let subst_list, same = CompareTypes.class_ ([], true) class1 class2 in
642 if same then begin
643 List.iter begin fun (pos1, pos2) ->
644 is_empty := false;
645 Hashtbl.add subst pos1 pos2
646 end subst_list
648 | _ -> ()
649 end classes;
650 subst, !is_empty