Fix computation of variance of generic type parameters occurring as roots of type...
[hiphop-php.git] / hphp / hack / src / typing / typing_variance.ml
blob3d0c72972b29775159671f515c604d20c2a3df8f
1 (*
2 * Copyright (c) 2015, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
8 *)
10 open Core_kernel
11 open Typing_defs
12 open Utils
14 module Env = Typing_env
15 module SN = Naming_special_names
16 module TGen = Typing_generic
17 module Cls = Decl_provider.Class
19 (*****************************************************************************)
20 (* Module checking the (co/contra)variance annotations (+/-).
22 * The algorithm works by tracking the variance of *uses* of a type parameter,
23 * and checks that the variance *declaration* on that type parameter is
24 * consistent with its uses.
26 * For every type parameter use, we keep a witness (a position in the source),
27 * that tells us where the covariance (or contravariance) was deduced.
28 * This way, when we find an error, we can point to the place that was
29 * problematic (as usual).
31 (*****************************************************************************)
33 (* Type describing the kind of position we are dealing with.
34 * Pos.t gives us the position in the source, it doesn't tell us the kind
35 * of position we are dealing with. This type keeps track of that.
37 type position_descr =
38 | Rtypedef
39 | Rmember (* Instance variable *)
40 | Rtype_parameter (* The declaration site of a type-parameter
42 | Rfun_parameter of [`Static | `Instance]
43 | Rfun_return of [`Static | `Instance]
44 | Rtype_argument of string (* The argument of a parametric class or
45 * typedef:
46 * A<T1, ..>, T1 is (Rtype_argument "A")
48 | Rconstraint_as
49 | Rconstraint_eq
50 | Rconstraint_super
51 | Rwhere_as
52 | Rwhere_super
53 | Rwhere_eq
54 | Rfun_inout_parameter of [`Static | `Instance]
56 type position_variance =
57 | Pcovariant
58 | Pcontravariant
59 | Pinvariant
61 type reason = Pos.t * position_descr * position_variance
63 (* The variance that we have inferred for a given use of a type-parameter. We keep
64 * a stack of reasons that made us infer the variance of a position.
65 * For example:
66 * T appears in foo(...): (function(...): T)
67 * T is inferred as covariant + a stack made of two elements:
68 * -) The first one points to the position of T
69 * -) The second one points to the position of (function(...): T)
70 * We can, thanks to this stack, detail why we think something is covariant
71 * in the error message.
73 type variance =
74 (* The type parameter appeared in covariant position. *)
75 | Vcovariant of reason list
77 (* The type parameter appeared in contravariant position. *)
78 | Vcontravariant of reason list
80 (* The type parameter appeared in both covariant and contravariant position.
81 * We keep a stack for each side: the left hand side is proof for covariance,
82 * while the right hand side is proof for contravariance.
84 | Vinvariant of reason list * reason list
86 (* The type parameter is not used, or is a method type parameter.
88 | Vboth
90 (*****************************************************************************)
91 (* Reason pretty-printing *)
92 (*****************************************************************************)
94 let variance_to_string = function
95 | Pcovariant -> "covariant (+)"
96 | Pcontravariant -> "contravariant (-)"
97 | Pinvariant -> "invariant"
99 let variance_to_sign = function
100 | Pcovariant -> "(+)"
101 | Pcontravariant -> "(-)"
102 | Pinvariant -> "(I)"
104 let reason_stack_to_string variance reason_stack =
105 Printf.sprintf
106 "This position is %s because it is the composition of %s\n\
107 The rest of the error messages decomposes the inference of the variance.\n\
108 Check out this link if you don't understand what this is about:\n\
109 http://en.wikipedia.org/wiki/Covariance_and_contravariance_\
110 (computer_science)\
112 variance
113 begin List.fold_right reason_stack ~f:begin fun (_, _, pvariance) acc ->
114 (variance_to_sign pvariance)^acc
115 end ~init:""
118 let reason_to_string ~sign (_, descr, variance) =
119 (if sign
120 then variance_to_sign variance ^ " "
121 else ""
123 match descr with
124 | Rtypedef ->
125 "Aliased types are covariant"
126 | Rmember ->
127 "A non private class member is always invariant"
128 | Rtype_parameter ->
129 "The type parameter was declared as " ^ variance_to_string variance
130 | Rfun_parameter `Instance ->
131 "Function parameters are contravariant"
132 | Rfun_parameter `Static ->
133 "Function parameters in non-final static functions are contravariant"
134 | Rfun_return `Instance ->
135 "Function return types are covariant"
136 | Rfun_return `Static ->
137 "Function return types in non-final static functions are covariant"
138 | Rtype_argument name ->
139 Printf.sprintf
140 "This type parameter was declared as %s (cf '%s')"
141 (variance_to_string variance)
142 name
143 | Rconstraint_super ->
144 "`super` constraints on method type parameters are covariant"
145 | Rconstraint_as ->
146 "`as` constraints on method type parameters are contravariant"
147 | Rconstraint_eq ->
148 "`=` constraints on method type parameters are invariant"
149 | Rwhere_as ->
150 "`where _ as _` constraints are covariant on the left, contravariant on the right"
151 | Rwhere_eq ->
152 "`where _ = _` constraints are invariant on the left and right"
153 | Rwhere_super ->
154 "`where _ super _` constraints are contravariant on the left, covariant on the right"
155 | Rfun_inout_parameter _ ->
156 "Inout/ref function parameters are both covariant and contravariant"
158 let detailed_message variance pos stack =
159 match stack with
160 | [] -> []
161 | [p, _, _ as r] ->
162 [p, reason_to_string ~sign:false r]
163 | _ ->
164 (pos, reason_stack_to_string variance stack) ::
165 List.map stack (fun (p, _, _ as r) -> p, reason_to_string ~sign:true r)
168 (*****************************************************************************)
169 (* Converts an annotation (+/-) to a type. *)
170 (*****************************************************************************)
172 let make_variance reason pos = function
173 | Ast_defs.Covariant ->
174 Vcovariant [pos, reason, Pcovariant]
175 | Ast_defs.Contravariant ->
176 Vcontravariant [pos, reason, Pcontravariant]
177 | Ast_defs.Invariant ->
178 Vinvariant ([pos, reason, Pinvariant], [pos, reason, Pinvariant])
180 (*****************************************************************************)
181 (* Used when we want to compose with the variance coming from another class.
182 * Let's assume: A<-T>
183 * public function foo(A<T> $x): void;
184 * A<T> is in contravariant position so we process -A<T>.
185 * because A is annotated with a minus: we deduce --T (which becomes T).
187 (*****************************************************************************)
189 let compose (pos, param_descr) from to_ =
190 (* We don't really care how we deduced the variance that we are composing
191 * with (_stack_to). That's because the decomposition could be in a different
192 * file and would be too hard to follow anyway.
193 * Let's consider the following return type: A<T>.
194 * Turns out A is declared as A<-T>.
195 * It's not a good idea for us to point to what made us deduce that the
196 * position was contravariant (the declaration side). Because the user will
197 * wonder where it comes from.
198 * It's better to point to the return type (more precisely, point to the T
199 * in the return type) and explain that this position is contravariant.
200 * Later on, the user can go and check the definition of A for herself.
202 match from, to_ with
203 | Vcovariant stack_from, Vcovariant _stack_to ->
204 let reason = pos, param_descr, Pcovariant in
205 Vcovariant (reason :: stack_from)
206 | Vcontravariant stack_from, Vcontravariant _stack_to ->
207 let reason = pos, param_descr, Pcontravariant in
208 Vcovariant (reason :: stack_from)
209 | Vcovariant stack_from, Vcontravariant _stack_to ->
210 let reason = pos, param_descr, Pcontravariant in
211 Vcontravariant (reason :: stack_from)
212 | Vcontravariant stack_from, Vcovariant _stack_to ->
213 let reason = pos, param_descr, Pcovariant in
214 Vcontravariant (reason :: stack_from)
215 | (Vinvariant _ as x), _ -> x
216 | _, Vinvariant (_co, _contra) ->
217 let reason = pos, param_descr, Pinvariant in
218 Vinvariant ([reason], [reason])
219 | Vboth, x | x, Vboth -> x
221 (*****************************************************************************)
222 (* Used for the arguments of function. *)
223 (*****************************************************************************)
225 let flip reason = function
226 | Vcovariant stack -> Vcontravariant (reason :: stack)
227 | Vcontravariant stack -> Vcovariant (reason :: stack)
228 | Vinvariant _ as x -> x
229 | Vboth -> Vboth
231 (*****************************************************************************)
232 (* Given a type parameter, returns the declared variance. *)
233 (*****************************************************************************)
235 let get_tparam_variance env name =
236 match SMap.get name env with
237 | None -> Vboth
238 | Some x -> x
240 (*****************************************************************************)
241 (* Given a type parameter, returns the variance declared. *)
242 (*****************************************************************************)
244 let make_tparam_variance t =
245 make_variance Rtype_parameter (fst t.tp_name) t.tp_variance
247 (******************************************************************************)
248 (* Checks that a 'this' type is correctly used at a given contravariant *)
249 (* position in a final class. *)
250 (******************************************************************************)
251 let check_final_this_pos_variance env_variance rpos class_ty =
252 if Cls.final class_ty then
253 List.iter (Cls.tparams class_ty)
254 begin fun t ->
255 match env_variance, t.tp_variance with
256 | Vcontravariant(_), (Ast_defs.Covariant | Ast_defs.Contravariant) ->
257 (Errors.contravariant_this
258 rpos
259 (Utils.strip_ns (Cls.name class_ty))
260 (snd t.tp_name))
261 | _ -> ()
264 (*****************************************************************************)
265 (* Returns the list of type parameter variance for a given class.
266 * Performing that operation adds a dependency on the class, because if
267 * the class changes (especially the variance), we must re-check the class.
269 * N.B.: this function works both with classes and typedefs.
271 (*****************************************************************************)
273 let get_class_variance root (pos, class_name) =
274 match class_name with
275 | name when (name = SN.Classes.cAwaitable) ->
276 [Vcovariant [pos, Rtype_argument (Utils.strip_ns name), Pcovariant]]
277 | _ ->
278 let dep = Typing_deps.Dep.Class class_name in
279 Typing_deps.add_idep (fst root) dep;
280 let tparams =
281 if Env.is_typedef class_name
282 then
283 match Decl_provider.get_typedef class_name with
284 | Some {td_tparams; _} -> td_tparams
285 | None -> []
286 else
287 match Decl_provider.get_class class_name with
288 | None -> []
289 | Some cls -> Cls.tparams cls
291 List.map tparams make_tparam_variance
293 (*****************************************************************************)
294 (* The entry point (for classes). *)
295 (*****************************************************************************)
297 (* impl is the list of `implements`, `extends`, and `use` types *)
298 let rec class_ tcopt class_name class_type impl =
299 let root = (Typing_deps.Dep.Class class_name, Some(class_type)) in
300 let tparams = Cls.tparams class_type in
301 let env = List.fold_left tparams ~init:SMap.empty ~f:(fun env tp ->
302 SMap.add (snd tp.tp_name) (make_tparam_variance tp) env) in
304 List.iter impl ~f:(type_ tcopt root Vboth env);
305 Cls.props class_type
306 |> Sequence.iter ~f:(class_member class_type tcopt root `Instance env);
307 Cls.sprops class_type
308 |> Sequence.iter ~f:(class_member class_type tcopt root `Static env);
309 Cls.methods class_type
310 |> Sequence.iter ~f:(class_method tcopt root `Instance env);
311 (* We need to apply the same restrictions to non-final static members because
312 they can be invoked through classname instances *)
313 if not (Cls.final class_type)
314 then
315 Cls.smethods class_type
316 |> Sequence.iter ~f:(class_method tcopt root `Static env)
318 (*****************************************************************************)
319 (* The entry point (for typedefs). *)
320 (*****************************************************************************)
322 and typedef tcopt type_name =
323 match Decl_provider.get_typedef type_name with
324 | Some {td_tparams; td_type; td_pos = _; td_constraint = _; td_vis = _;
325 td_decl_errors = _;} ->
326 let root = (Typing_deps.Dep.Class type_name, None) in
327 let env = List.fold_left td_tparams ~init:SMap.empty ~f:(fun env tp ->
328 SMap.add (snd tp.tp_name) (make_tparam_variance tp) env) in
330 let pos = Reason.to_pos (fst td_type) in
331 let reason_covariant = [pos, Rtypedef, Pcovariant] in
332 type_ tcopt root (Vcovariant reason_covariant) env td_type
333 | None -> ()
335 and class_member class_type tcopt root static env (_member_name, member) =
336 if static = `Static
337 then begin
338 (* Check whether the type of a static property (class variable) contains
339 * any generic type parameters. Outside of traits, this is illegal as static
340 * properties are shared across all generic instantiations.
341 * Although not strictly speaking a variance check, it fits here because
342 * it concerns the presence of generic type parameters in types.
344 if Cls.kind class_type = Ast_defs.Ctrait
345 then ()
346 else
347 let lazy (reason, _ as ty) = member.ce_type in
348 let var_type_pos = Reason.to_pos reason in
349 let class_pos = Cls.pos class_type in
350 match TGen.IsGeneric.ty ty with
351 | None -> ()
352 | Some (generic_pos, _generic_name) ->
353 Errors.static_property_type_generic_param ~class_pos ~var_type_pos ~generic_pos
355 else
356 match member.ce_visibility with
357 | Vprivate _ -> ()
358 | _ ->
359 let lazy (reason, _ as ty) = member.ce_type in
360 let pos = Reason.to_pos reason in
361 let variance = make_variance Rmember pos Ast_defs.Invariant in
362 type_ tcopt root variance env ty
364 and class_method tcopt root static env (_method_name, method_) =
365 match method_.ce_visibility with
366 | Vprivate _ -> ()
367 | _ ->
368 (* Final methods can't be overridden, so it's ok to use covariant
369 and contravariant type parameters in any position in the type *)
370 if method_.ce_final && static = `Static
371 then ()
372 else
373 match method_.ce_type with
374 | lazy (_, Tfun { ft_tparams = (tparams, _);
375 ft_params;
376 ft_arity;
377 ft_ret;
378 ft_where_constraints; _ }) ->
379 let env = List.fold_left tparams
380 ~f:begin fun env t ->
381 SMap.remove (snd t.tp_name) env
382 end ~init:env in
383 List.iter ft_params ~f:(fun_param tcopt root (Vcovariant []) static env);
384 fun_arity tcopt root (Vcovariant []) static env ft_arity;
385 List.iter tparams ~f:(fun_tparam tcopt root env);
386 List.iter ft_where_constraints ~f:(fun_where_constraint tcopt root env);
387 fun_ret tcopt root (Vcovariant []) static env ft_ret.et_type
388 | _ -> assert false
390 and fun_arity tcopt root variance static env arity =
391 match arity with
392 | Fstandard _ | Fellipsis _ -> ()
393 | Fvariadic (_, fp) -> fun_param tcopt root variance static env fp
395 and fun_param tcopt root variance static env { fp_type = { et_type = (reason, _ as ty); _ }; fp_kind; _ } =
396 let pos = Reason.to_pos reason in
397 match fp_kind with
398 | FPnormal ->
399 let reason = pos, Rfun_parameter static, Pcontravariant in
400 let variance = flip reason variance in
401 type_ tcopt root variance env ty
402 | FPref | FPinout ->
403 let variance = make_variance (Rfun_inout_parameter static) pos Ast_defs.Invariant in
404 type_ tcopt root variance env ty
406 and fun_tparam tcopt root env t =
407 List.iter t.tp_constraints ~f:(constraint_ tcopt root env)
409 and fun_where_constraint tcopt root env (ty1, ck, ty2) =
410 let pos1 = Reason.to_pos (fst ty1) in
411 let pos2 = Reason.to_pos (fst ty2) in
412 match ck with
413 | Ast_defs.Constraint_super ->
414 let var1 = Vcontravariant [pos1, Rwhere_super, Pcontravariant] in
415 let var2 = Vcovariant [pos2, Rwhere_super, Pcovariant] in
416 type_ tcopt root var1 env ty1;
417 type_ tcopt root var2 env ty2
418 | Ast_defs.Constraint_eq ->
419 let reason1 = [pos1, Rwhere_eq, Pinvariant] in
420 let reason2 = [pos2, Rwhere_eq, Pinvariant] in
421 let var = Vinvariant (reason1, reason2) in
422 type_ tcopt root var env ty1;
423 type_ tcopt root var env ty2
424 | Ast_defs.Constraint_as ->
425 let var1 = Vcovariant [pos1, Rwhere_as, Pcovariant] in
426 let var2 = Vcontravariant [pos2, Rwhere_as, Pcontravariant] in
427 type_ tcopt root var1 env ty1;
428 type_ tcopt root var2 env ty2
429 | Ast_defs.Constraint_pu_from -> failwith "TODO(T36532263): Pocket Universes"
431 and fun_ret tcopt root variance static env (reason, _ as ty) =
432 let pos = Reason.to_pos reason in
433 let reason_covariant = pos, Rfun_return static, Pcovariant in
434 let variance =
435 match variance with
436 | Vcovariant stack -> Vcovariant (reason_covariant :: stack)
437 | Vcontravariant stack -> Vcontravariant (reason_covariant :: stack)
438 | variance -> variance in
439 type_ tcopt root variance env ty
441 and type_option tcopt root variance env = function
442 | None -> ()
443 | Some ty -> type_ tcopt root variance env ty
445 and type_list tcopt root variance env tyl =
446 List.iter tyl ~f:(type_ tcopt root variance env)
448 and generic_ env variance name =
449 let declared_variance = get_tparam_variance env name in
450 match declared_variance, variance with
451 (* Happens if type parameter isn't from class *)
452 | Vboth, _ | _, Vboth -> ()
453 | Vinvariant _, _
454 | Vcovariant _, Vcovariant _ | Vcontravariant _, Vcontravariant _ -> ()
456 | Vcovariant stack1, (Vcontravariant stack2 | Vinvariant (_, stack2)) ->
457 let (pos1, _, _) = List.hd_exn stack1 in
458 let (pos2, _, _) = List.hd_exn stack2 in
459 let emsg = detailed_message "contravariant (-)" pos2 stack2 in
460 Errors.declared_covariant pos1 pos2 emsg
461 | Vcontravariant stack1, (Vcovariant stack2 | Vinvariant (stack2, _)) ->
462 let (pos1, _, _) = List.hd_exn stack1 in
463 let (pos2, _, _) = List.hd_exn stack2 in
464 let emsg = detailed_message "covariant (+)" pos2 stack2 in
465 Errors.declared_contravariant pos1 pos2 emsg
467 and type_ tcopt root variance env (reason, ty) =
468 match ty with
469 | Tany | Tmixed | Tnonnull | Terr | Tdynamic | Tnothing -> ()
470 | Tarray (ty1, ty2) ->
471 type_option tcopt root variance env ty1;
472 type_option tcopt root variance env ty2
473 | Tdarray (ty1, ty2) ->
474 type_ tcopt root variance env (reason, Tarray (Some ty1, Some ty2))
475 | Tvarray ty ->
476 type_ tcopt root variance env (reason, Tarray (Some ty, None))
477 | Tvarray_or_darray ty ->
478 let tk =
479 Typing_reason.Rvarray_or_darray_key (Reason.to_pos reason),
480 Tprim Aast.Tarraykey in
481 type_ tcopt root variance env (reason, Tarray (Some tk, Some ty))
482 | Tthis ->
483 (* Check that 'this' isn't being improperly referenced in a contravariant
484 * position.
486 Option.value_map
487 (snd root)
488 ~default:()
489 ~f:(check_final_this_pos_variance variance (Reason.to_pos reason))
490 (* With the exception of the above check, `this` constraints are bivariant
491 * (otherwise any class that used the `this` type would not be able to use
492 * covariant type params).
494 | Tgeneric name ->
495 let pos = Reason.to_pos reason in
496 (* This section makes the position more precise.
497 * Say we find a return type that is a tuple (int, int, T).
498 * The whole tuple is in covariant position, and so the position
499 * is going to include the entire tuple.
500 * That can make things pretty unreadable when the type is long.
501 * Here we replace the position with the exact position of the generic
502 * that was problematic.
504 let variance =
505 match variance with
506 | Vcovariant ((pos', x, y) :: rest) when pos <> pos' ->
507 Vcovariant ((pos, x, y) :: rest)
508 | Vcontravariant ((pos', x, y) :: rest) when pos <> pos' ->
509 Vcontravariant ((pos, x, y) :: rest)
510 | x -> x
512 generic_ env variance name
513 | Toption ty ->
514 type_ tcopt root variance env ty
515 | Tlike ty ->
516 type_ tcopt root variance env ty
517 | Tprim _ -> ()
518 | Tfun ft ->
519 List.iter ft.ft_params ~f:(fun_param tcopt root variance `Instance env);
520 fun_arity tcopt root variance `Instance env ft.ft_arity;
521 fun_ret tcopt root variance `Instance env ft.ft_ret.et_type
522 | Tapply (_, []) -> ()
523 | Tapply ((_, name as pos_name), tyl) ->
524 let variancel = get_class_variance root pos_name in
525 iter2_shortest begin fun tparam_variance (r, _ as ty) ->
526 let pos = Reason.to_pos r in
527 let reason = Rtype_argument (Utils.strip_ns name) in
528 let variance = compose (pos, reason) variance tparam_variance in
529 type_ tcopt root variance env ty
530 end variancel tyl
531 | Ttuple tyl ->
532 type_list tcopt root variance env tyl
533 (* when we add type params to type consts might need to change *)
534 | Taccess _ -> ()
535 | Tshape (_, ty_map) ->
536 Nast.ShapeMap.iter begin fun _ { sft_ty; _ } ->
537 type_ tcopt root variance env sft_ty end ty_map
539 (* `as` constraints on method type parameters must be contravariant
540 * and `super` constraints on method type parameters are covariant. To
541 * see why, suppose that we allow the wrong variance:
543 * class Foo<+T> {
544 * public function bar<Tu as T>(Tu $x) {}
547 * Let A and B be classes, with B a subtype of A. Then
549 * function f(Foo<A> $x) {
550 * $x->(new A());
553 * typechecks. However, covariance means that we could call `f()` with an
554 * instance of B. However, B::bar would expect its argument $x to be a subtype
555 * of B, but we would be passing an instance of A to it, which should be a type
556 * error. In other words, `as` constraints are upper type bounds, and since
557 * subtypes must have more relaxed constraints than their supertypes, `as`
558 * constraints must be contravariant. (Reversing this argument shows that
559 * `super` constraints must be covariant.)
561 * The preceding discussion might lead one to think that the constraints should
562 * have the same variance as the class parameter types, i.e. that `as`
563 * constraints used in the return type should be covariant. In particular,
564 * suppose
566 * class Foo<-T> {
567 * public function bar<Tu as T>(Tu $x): Tu { ... }
568 * public function baz<Tu as T>(): Tu { ... }
571 * class A {}
572 * class B extends A {
573 * public function qux() {}
576 * function f(Foo<B> $x) {
577 * $x->baz()->qux();
580 * Now `f($x)` could be a runtime error if `$x` was an instance of Foo<A>, and
581 * it seems like we should enforce covariance on Tu. However, the real problem
582 * is that constraints apply to _instantiation_, not usage. As far as Hack
583 * knows, $x->bar() could be of any type, since we have not provided any clues
584 * about how `Tu` should be instantiated. In fact `$x->bar()->whatever()`
585 * succeeds as well, because `Tu` is of type Tany -- though in an ideal world
586 * we would make it Tmixed in order to ensure soundness. Also, the signature
587 * of `baz()` doesn't entirely make sense -- why constrain Tu if it it's only
588 * getting used in one place?
590 * Thus, if one wants type safety, how `$x` _should_ be used is
592 * function f(Foo<B> $x) {
593 * $x->bar(new B()))->qux();
596 * Thus we can see that, if `$x` is used correctly (or if we enforced correct
597 * use by returning Tmixed for uninstantiated type variables), we would always
598 * know the exact type of Tu, and Tu can be validly used in both co- and
599 * contravariant positions.
601 * Remark: This is far more intuitive if you think about how Vector::concat is
602 * typed with its `Tu super T` type in both the parameter and return type
603 * positions. Uninstantiated `super` types are less likely to cause confusion,
604 * however -- you can't imagine doing very much with a returned value that is
605 * some (unspecified) supertype of a class.
607 and constraint_ tcopt root env (ck, (r, _ as ty)) =
608 let pos = Reason.to_pos r in
609 let var = match ck with
610 | Ast_defs.Constraint_as -> Vcontravariant [pos, Rconstraint_as, Pcontravariant]
611 | Ast_defs.Constraint_eq ->
612 let reasons = [pos, Rconstraint_eq, Pinvariant] in
613 Vinvariant (reasons, reasons)
614 | Ast_defs.Constraint_super -> Vcovariant [pos, Rconstraint_super, Pcovariant]
615 | Ast_defs.Constraint_pu_from -> failwith "TODO(T36532263): Pocket Universes"
617 type_ tcopt root var env ty
619 and get_typarams root env (ty: decl ty) =
620 let empty = (SMap.empty, SMap.empty) in
621 let union (pos1, neg1) (pos2, neg2) =
622 (SMap.union ~combine:(fun _ x y -> Some (x@y)) pos1 pos2,
623 SMap.union ~combine:(fun _ x y -> Some (x@y)) neg1 neg2) in
624 let flip (pos, neg) = (neg, pos) in
625 let single id pos = SMap.singleton id [pos], SMap.empty in
626 let get_typarams_union acc ty = union acc (get_typarams root env ty) in
627 match snd ty with
628 | Tgeneric id ->
629 (* If it's in the environment then it's not a generic method parameter *)
630 if Option.is_some (SMap.get id env)
631 then empty
632 else single id (fst ty)
633 | Tnonnull | Tdynamic | Tprim _ | Tnothing | Tany | Terr | Tthis | Tmixed -> empty
634 | Toption ty | Tlike ty | Taccess (ty, _) -> get_typarams root env ty
635 | Ttuple tyl -> List.fold_left tyl ~init:empty ~f:get_typarams_union
636 | Tshape (_, m) ->
637 Nast.ShapeMap.fold (fun _ {sft_ty; _} res -> get_typarams_union res sft_ty) m empty
638 | Tfun ft ->
639 let get_typarams_param acc fp =
640 let tp = get_typarams root env fp.fp_type.et_type in
641 let tp =
642 match fp.fp_kind with
643 (* Parameters behave contravariantly *)
644 | FPnormal -> flip tp
645 (* Inout/ref parameters behave both co- and contra-variantly *)
646 | FPref | FPinout -> union tp (flip tp) in
647 union acc tp in
648 let get_typarams_arity acc arity =
649 match arity with
650 | Fstandard _ | Fellipsis _ -> acc
651 | Fvariadic (_, fp) -> get_typarams_param acc fp in
652 let params = List.fold_left ft.ft_params
653 ~init:(get_typarams_arity empty ft.ft_arity) ~f:get_typarams_param in
654 let ret = get_typarams root env ft.ft_ret.et_type in
655 let get_typarams_constraint acc (ck, ty) =
656 union acc (
657 match ck with
658 | Ast_defs.Constraint_as ->
659 get_typarams root env ty
660 | Ast_defs.Constraint_eq ->
661 let tp = get_typarams root env ty in
662 union (flip tp) tp
663 | Ast_defs.Constraint_super ->
664 flip (get_typarams root env ty)
665 | Ast_defs.Constraint_pu_from -> failwith "TODO(T36532263): Pocket Universes"
666 ) in
667 let get_typarams_tparam acc tp =
668 List.fold_left tp.tp_constraints ~init:acc ~f:get_typarams_constraint in
669 let bounds =
670 List.fold_left (fst ft.ft_tparams) ~init:empty ~f:get_typarams_tparam in
671 let get_typarams_where_constraint acc (ty1, ck, ty2) =
672 union acc (match ck with
673 | Ast_defs.Constraint_super ->
674 union (flip (get_typarams root env ty1)) (get_typarams root env ty2)
675 | Ast_defs.Constraint_as ->
676 union (get_typarams root env ty1) (flip (get_typarams root env ty2))
677 | Ast_defs.Constraint_eq ->
678 let tp = union (get_typarams root env ty1) (get_typarams root env ty2) in
679 union tp (flip tp)
680 | Ast_defs.Constraint_pu_from ->
681 empty) in
682 let constrs = List.fold_left ft.ft_where_constraints ~init:empty
683 ~f:get_typarams_where_constraint in
684 union bounds (union constrs (union ret params))
685 | Tapply (pos_name, tyl) ->
686 let rec get_typarams_variance_list acc variancel tyl =
687 match variancel, tyl with
688 | variance::variancel, ty::tyl ->
689 let param = get_typarams root env ty in
690 let param =
691 match variance with
692 | Vcovariant _ -> param
693 | Vcontravariant _ -> flip param
694 | _ -> union param (flip param) in
695 get_typarams_variance_list (union acc param) variancel tyl
696 | _ -> acc in
697 let variancel = get_class_variance root pos_name in
698 get_typarams_variance_list empty variancel tyl
699 | Tarray (ty1, ty2) ->
700 let get_typarams_opt tyopt =
701 match tyopt with
702 | None -> empty
703 | Some ty -> get_typarams root env ty in
704 union (get_typarams_opt ty1) (get_typarams_opt ty2)
705 | Tdarray (ty1, ty2) ->
706 union (get_typarams root env ty1) (get_typarams root env ty2)
707 | Tvarray ty | Tvarray_or_darray ty ->
708 get_typarams root env ty