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.
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.
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
46 * A<T1, ..>, T1 is (Rtype_argument "A")
54 | Rfun_inout_parameter
of [`Static
| `Instance
]
56 type position_variance
=
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.
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.
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.
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
=
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_\
113 begin List.fold_right reason_stack ~f
:begin fun (_
, _
, pvariance
) acc
->
114 (variance_to_sign pvariance
)^acc
118 let reason_to_string ~sign
(_
, descr
, variance
) =
120 then variance_to_sign variance ^
" "
125 "Aliased types are covariant"
127 "A non private class member is always invariant"
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
->
140 "This type parameter was declared as %s (cf '%s')"
141 (variance_to_string variance
)
143 | Rconstraint_super
->
144 "`super` constraints on method type parameters are covariant"
146 "`as` constraints on method type parameters are contravariant"
148 "`=` constraints on method type parameters are invariant"
150 "`where _ as _` constraints are covariant on the left, contravariant on the right"
152 "`where _ = _` constraints are invariant on the left and right"
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
=
162 [p
, reason_to_string ~sign
:false r
]
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.
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
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
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
)
255 match env_variance
, t
.tp_variance
with
256 | Vcontravariant
(_
), (Ast_defs.Covariant
| Ast_defs.Contravariant
) ->
257 (Errors.contravariant_this
259 (Utils.strip_ns
(Cls.name class_ty
))
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
]]
278 let dep = Typing_deps.Dep.Class class_name
in
279 Typing_deps.add_idep
(fst root
) dep;
281 if Env.is_typedef class_name
283 match Decl_provider.get_typedef class_name
with
284 | Some
{td_tparams
; _
} -> td_tparams
287 match Decl_provider.get_class class_name
with
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);
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
)
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
335 and class_member class_type tcopt
root static
env (_member_name
, member
) =
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
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
352 | Some
(generic_pos
, _generic_name
) ->
353 Errors.static_property_type_generic_param ~
class_pos ~
var_type_pos ~generic_pos
356 match member
.ce_visibility
with
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
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
373 match method_
.ce_type
with
374 | lazy (_
, Tfun
{ ft_tparams
= (tparams, _
);
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
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
390 and fun_arity tcopt
root variance static
env arity
=
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
399 let reason = pos, Rfun_parameter static
, Pcontravariant
in
400 let variance = flip reason variance in
401 type_ tcopt
root variance env ty
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
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
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
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
-> ()
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
) =
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
))
476 type_ tcopt
root variance env (reason, Tarray
(Some ty
, None
))
477 | Tvarray_or_darray ty
->
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
))
483 (* Check that 'this' isn't being improperly referenced in a contravariant
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).
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.
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
)
512 generic_
env variance name
514 type_ tcopt
root variance env ty
516 type_ tcopt
root variance env ty
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
532 type_list tcopt
root variance env tyl
533 (* when we add type params to type consts might need to change *)
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:
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) {
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,
567 * public function bar<Tu as T>(Tu $x): Tu { ... }
568 * public function baz<Tu as T>(): Tu { ... }
572 * class B extends A {
573 * public function qux() {}
576 * function f(Foo<B> $x) {
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
629 (* If it's in the environment then it's not a generic method parameter *)
630 if Option.is_some
(SMap.get id
env)
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
637 Nast.ShapeMap.fold
(fun _
{sft_ty
; _
} res
-> get_typarams_union res sft_ty
) m
empty
639 let get_typarams_param acc fp
=
640 let tp = get_typarams
root env fp
.fp_type
.et_type
in
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
648 let get_typarams_arity acc arity
=
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
) =
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
663 | Ast_defs.Constraint_super
->
664 flip (get_typarams
root env ty
)
665 | Ast_defs.Constraint_pu_from
-> failwith
"TODO(T36532263): Pocket Universes"
667 let get_typarams_tparam acc
tp =
668 List.fold_left
tp.tp_constraints ~init
:acc ~f
:get_typarams_constraint in
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
680 | Ast_defs.Constraint_pu_from
->
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
692 | Vcovariant _
-> param
693 | Vcontravariant _
-> flip param
694 | _
-> union param (flip param) in
695 get_typarams_variance_list (union acc
param) variancel tyl
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
=
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