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.
12 module Reason
= Typing_reason
13 module SN
= Naming_special_names
18 | Vprotected
of string [@@deriving show
]
20 (* All the possible types, reason is a trace of why a type
21 was inferred in a certain way.
23 Types exists in two phases. Phase one is 'decl', meaning it is a type that
24 was declared in user code. Phase two is 'locl', meaning it is a type that is
25 inferred via local inference.
27 (* create private types to represent the different type phases *)
28 type decl
= private DeclPhase
29 type locl
= private LoclPhase
31 let show_phase_ty _
= "<phase_ty>"
32 let pp_phase_ty _ _
= Printf.printf
"%s\n" "<phase_ty>"
34 type 'phase ty
= ( Reason.t
* 'phase ty_
)
36 (* A shape may specify whether or not fields are required. For example, consider
39 type ShapeWithOptionalField = shape(?'a' => ?int);
41 With this definition, the field 'a' may be unprovided in a shape. In this
42 case, the field 'a' would have sf_optional set to true.
44 and 'phase shape_field_type
= {
50 (*========== Following Types Exist Only in the Declared Phase ==========*)
51 (* The late static bound type of a class *)
54 (* Either an object type or a type alias, ty list are the arguments *)
55 | Tapply
: Nast.sid
* decl ty list
-> decl ty_
57 (* The type of a generic parameter. The constraints on a generic parameter
58 * are accessed through the lenv.tpenv component of the environment, which
59 * is set up when checking the body of a function or method. See uses of
60 * Typing_phase.localize_generic_parameters_with_bounds.
62 | Tgeneric
: string -> decl ty_
64 (* Name of class, name of type const, remaining names of type consts *)
65 | Taccess
: taccess_type
-> decl ty_
67 (* The type of the various forms of "array":
68 * Tarray (None, None) => "array"
69 * Tarray (Some ty, None) => "array<ty>"
70 * Tarray (Some ty1, Some ty2) => "array<ty1, ty2>"
71 * Tarray (None, Some ty) => [invalid]
73 | Tarray
: decl ty
option * decl ty
option -> decl ty_
75 (* Tdarray (ty1, ty2) => "darray<ty1, ty2>" *)
76 | Tdarray
: decl ty
* decl ty
-> decl ty_
78 (* Tvarray (ty) => "varray<ty>" *)
79 | Tvarray
: decl ty
-> decl ty_
81 (* Tvarray_or_darray (ty) => "varray_or_darray<ty>" *)
82 | Tvarray_or_darray
: decl ty
-> decl ty_
84 (*========== Following Types Exist in Both Phases ==========*)
85 (* "Any" is the type of a variable with a missing annotation, and "mixed" is
86 * the type of a variable annotated as "mixed". THESE TWO ARE VERY DIFFERENT!
87 * Any unifies with anything, i.e., it is both a supertype and subtype of any
88 * other type. You can do literally anything to it; it's the "trust me" type.
89 * Mixed, on the other hand, is only a supertype of everything. You need to do
90 * a case analysis to figure out what it is (i.e., its elimination form).
92 * Here's an example to demonstrate:
94 * function f($x): int {
98 * In that example, $x has type Tany. This unifies with anything, so adding
99 * one to it is allowed, and returning that as int is allowed.
101 * In contrast, if $x were annotated as mixed, adding one to that would be
102 * a type error -- mixed is not a subtype of int, and you must be a subtype
103 * of int to take part in addition. (The converse is true though -- int is a
104 * subtype of mixed.) A case analysis would need to be done on $x, via
110 (* A dynamic type is a special type which, similar to mixed, is considered the
111 * supertype of all types (except mixed), can be used in any operation like
112 * TAny, but, unlike TAny, does not subtype or unify with regular types:
113 * it captures dynamicism within function scope.
114 * See tests in typecheck/dynamic/ for more examples.
119 (* Nullable, called "option" in the ML parlance. *)
120 | Toption
: 'phase ty
-> 'phase ty_
122 (* All the primitive types: int, string, void, etc. *)
123 | Tprim
: Nast.tprim
-> 'phase ty_
125 (* A wrapper around fun_type, which contains the full type information for a
126 * function, method, lambda, etc. Note that lambdas have an additional layer
127 * of indirection before you get to Tfun -- see Tanon below. *)
128 | Tfun
: 'phase fun_type
-> 'phase ty_
131 (* Tuple, with ordered list of the types of the elements of the tuple. *)
132 | Ttuple
: 'phase ty list
-> 'phase ty_
134 (* Whether all fields of this shape are known, types of each of the
138 : shape_fields_known
* ('phase shape_field_type
Nast.ShapeMap.t
)
141 (*========== Below Are Types That Cannot Be Declared In User Code ==========*)
143 (* A type variable (not to be confused with a type parameter). This is the
144 * core of how type inference works. If you aren't familiar with it, a
145 * suitable explanation couldn't possibly fit here; terms to google for
146 * include "Hindley-Milner type inference", "unification", and "algorithm W".
148 | Tvar
: Ident.t
-> locl ty_
150 (* The type of an opaque type (e.g. a "newtype" outside of the file where it
151 * was defined). They are "opaque", which means that they only unify with
152 * themselves. However, it is possible to have a constraint that allows us to
153 * relax this. For example:
155 * newtype my_type as int = ...
157 * Outside of the file where the type was defined, this translates to:
159 * Tabstract (AKnewtype (pos, "my_type", []), Some (Tprim Tint))
161 * Which means that my_type is abstract, but is subtype of int as well.
163 * We also create abstract types for generic parameters of a function, i.e.
165 * function foo<T>(T $x): void {
169 * The type 'T' will be represented as an abstract type when type checking
172 * Finally abstract types are also derived from the 'this' type and
173 * accessing type constants on it, resulting in a dependent type.
175 | Tabstract
: abstract_kind
* locl ty
option -> locl ty_
177 (* An anonymous function, including the fun arity, and the identifier to
178 * type the body of the function. (The actual closure is stored in
179 * Typing_env.env.genv.anons) *)
180 | Tanon
: locl fun_arity
* Ident.t
-> locl ty_
182 (* This is a kinda-union-type we use in order to defer picking which common
183 * ancestor for a type we should use until we hit a type annotation.
187 * class C implements I {}
188 * class D extends C {}
198 * What is the type of $x? We need to pick some common ancestor, but which
199 * one? Both C and I would be acceptable, which do we mean? This is where
200 * Tunresolved comes in -- after the if/else, the type of $x is
201 * Unresolved[C, D] -- it could be *either one*, and we defer the check until
202 * we hit an annotation. In particular, when we hit the "return", we make sure
203 * that it is compatible with both C and D, and then we know we've found the
204 * right supertype. Since we don't do global inference, we'll always either
205 * hit an annotation to check, or hit a place an annotation is missing in
206 * which case we can just throw away the type.
208 * Note that this is *not* really a union type -- most notably, it's allowed
209 * to grow as inference goes on, which union types don't. For example:
211 * function f(): Vector<num> {
218 * (Eliding some Tvar for clarity) On the first line, $v is
219 * Vector<Unresolved[]>. On the second, Vector<Unresolved[int]>. On the third,
220 * Vector<Unresolved[int,float]> -- it grows! Then when we finally return $v,
221 * we see that int and float are both compatible with num, and we have found
222 * our suitable supertype.
224 * One final implication of this growing is that if an unresolved is used in
225 * a contravariant position, we must collapse it down to whatever is annotated
226 * right then, in order to be sound.
228 | Tunresolved
: locl ty list
-> locl ty_
230 (* Tobject is an object type compatible with all objects. This type is also
231 * compatible with some string operations (since a class might implement
232 * __toString), but not with string type hints. In a similar way, Tobject
233 * is compatible with some array operations (since a class might implement
234 * ArrayAccess), but not with array type hints.
236 * Tobject is currently used to type code like:
237 * ../test/typecheck/return_unknown_class.php
241 (* An instance of a class or interface, ty list are the arguments *)
242 | Tclass
: Nast.sid
* locl ty list
-> locl ty_
244 (* Localized version of Tarray *)
245 | Tarraykind
: array_kind
-> locl ty_
248 (* Those three types directly correspond to their decl level counterparts:
249 * array, array<_> and array<_, _> *)
251 (* An array declared as a varray. *)
252 | AKvarray
of locl ty
254 (* An array declared as a darray. *)
255 | AKdarray
of locl ty
* locl ty
256 (* An array annotated as a varray_or_darray. *)
257 | AKvarray_or_darray
of locl ty
258 | AKmap
of locl ty
* locl ty
259 (* This is a type created when we see array() literal *)
261 (* Array "used like a shape" - initialized and indexed with keys that are
262 * only string/class constants *)
263 | AKshape
of (locl ty
* locl ty
) Nast.ShapeMap.t
264 (* Array "used like a tuple" - initialized without keys and indexed with
265 * integers that are within initialized range *)
266 | AKtuple
of (locl ty
) IMap.t
268 (* An abstract type derived from either a newtype, a type parameter, or some
272 (* newtype foo<T1, T2> ... *)
273 | AKnewtype
of string * locl ty list
276 (* <T super C> ; None if 'as' constrained *)
277 | AKgeneric
of string
278 (* see dependent_type *)
279 | AKdependent
of dependent_type
281 (* A dependent type consists of a base kind which indicates what the type is
282 * dependent on. It is either dependent on:
284 * - The class context (what 'static' is resolved to in a class)
288 * Dependent types also have a path component (derived from accessing a type
289 * constant). Thus the dependent type (`expr 0, ['A', 'B', 'C']) roughly means
290 * "The type resulting from accessing the type constant A then the type constant
291 * B and then the type constant C on the expression reference by 0"
294 (* Type that is the subtype of the late bound type within a class. *)
296 (* The late bound type within a class. It is the type of 'new static()' and
297 * '$this'. This is different from the 'this' type. The 'this' type isn't
298 * quite strong enough in some cases. It means you are a subtype of the late
299 * bound class, but there are instances where you need the exact type.
300 * We may not need both since the only way to make something of type 'this'
301 * that is not 'static' is with 'instanceof static'.
304 (* A class name, new type, or generic, i.e.
306 * abstract class C { abstract const type T }
308 * The type C::T is (`cls '\C', ['T'])
311 (* A reference to some expression. For example:
315 * The expression $x would have a reference Ident.t
316 * The expression $x->foo() would have a different one
321 and taccess_type
= decl ty
* Nast.sid list
323 (* Local shape constructed using "shape" keyword has all the fields
326 * $s = shape('x' => 4, 'y' => 4);
328 * It has fields 'x' and 'y' and definitely no other fields. On the other
329 * hand, shape types that come from typehints may (due to structural
330 * subtyping of shapes) have some other, unlisted fields:
332 * type s = shape('x' => int);
337 * f(shape('x' => 4, 'y' => 5));
339 * The call to f is valid because of structural subtyping - shapes are
340 * permitted to "forget" fields. But the 'y' field still exists at runtime,
341 * and we cannot say inside the body of $f that we know that 'x' is the only
342 * field. This is relevant when deciding if it's safe to omit optional fields
343 * - if shape fields are not fully known, even optional fields have to be
344 * explicitly set/unset.
346 * We also track in additional map of FieldsPartiallyKnown names of fields
347 * that are known to not exist (because they were explicitly unset).
349 and shape_fields_known
=
351 | FieldsPartiallyKnown
of Pos.t
Nast.ShapeMap.t
353 (* represents reactivity of function
354 - None corresponds to non-reactive function
355 - Some reactivity - to reactive function with specified reactivity flavor
357 Nonreactive <: Local -t <: Shallow -t <: Reactive -t
359 MaybeReactive represents conditional reactivity of function that depends on
360 reactivity of function arguments
362 function f(<<__MaybeRx>> $g) { ... }
363 call to function f will be treated as reactive only if $g is reactive
367 | Local
of decl ty
option
368 | Shallow
of decl ty
option
369 | Reactive
of decl ty
option
370 | MaybeReactive
of reactivity
372 (* The type of a function AND a method.
373 * A function has a min and max arity because of optional arguments *)
374 and 'phase fun_type
= {
376 ft_deprecated
: string option ;
378 ft_is_coroutine
: bool ;
379 ft_arity
: 'phase fun_arity
;
380 ft_tparams
: 'phase tparam list
;
381 ft_where_constraints
: 'phase where_constraint list
;
382 ft_params
: 'phase fun_params
;
384 ft_ret_by_ref
: bool ;
385 ft_reactive
: reactivity
;
386 ft_return_disposable
: bool ;
388 ft_returns_mutable
: bool ;
389 ft_decl_errors
: Errors.t
option ;
390 ft_returns_void_to_rx
: bool ;
393 (* Arity information for a fun_type; indicating the minimum number of
394 * args expected by the function and the maximum number of args for
395 * standard, non-variadic functions or the type of variadic argument taken *)
396 and 'phase fun_arity
=
397 | Fstandard
of int * int (* min ; max *)
398 (* PHP5.6-style ...$args finishes the func declaration *)
399 | Fvariadic
of int * 'phase fun_param
(* min ; variadic param type *)
400 (* HH-style ... anonymous variadic arg; body presumably uses func_get_args *)
401 | Fellipsis
of int (* min *)
408 and param_rx_condition
=
410 | Param_rx_if_impl
of decl ty
412 and 'phase fun_param
= {
414 fp_name
: string option;
416 fp_kind
: param_mode
;
417 fp_accept_disposable
: bool;
419 fp_rx_condition
: param_rx_condition
option;
422 and 'phase fun_params
= 'phase fun_param list
426 ce_is_xhp_attr
: bool;
428 (* true if this elt arose from require-extends or other mechanisms
429 of hack "synthesizing" methods that were not written by the
430 programmer. The eventual purpose of this is to make sure that
431 elts that *are* written by the programmer take precedence over
433 ce_synthesized
: bool;
434 ce_visibility
: visibility
;
436 ce_type
: decl ty
Lazy.t
;
437 (* identifies the class from which this elt originates *)
442 cc_synthesized
: bool;
446 cc_expr
: Nast.expr
option;
447 (* identifies the class from which this const originates *)
451 (* The position is that of the hint in the `use` / `implements` AST node
452 * that causes a class to have this requirement applied to it. E.g.
457 * require extends Foo; <- position of the decl ty
460 * class Baz extends Foo implements Bar { <- position of the `implements`
463 and requirement
= Pos.t
* decl ty
467 (* Whether the typechecker knows of all (non-interface) ancestors
468 * and thus known all accessible members of this class *)
469 tc_members_fully_known
: bool;
473 (* When a class is abstract (or in a trait) the initialization of
474 * a protected member can be delayed *)
475 tc_deferred_init_members
: SSet.t
;
476 tc_kind
: Ast.class_kind
;
478 tc_is_disposable
: bool;
481 tc_tparams
: decl tparam list
;
482 tc_consts
: class_const
SMap.t
;
483 tc_typeconsts
: typeconst_type
SMap.t
;
484 tc_props
: class_elt
SMap.t
;
485 tc_sprops
: class_elt
SMap.t
;
486 tc_methods
: class_elt
SMap.t
;
487 tc_smethods
: class_elt
SMap.t
;
488 tc_construct
: class_elt
option * bool;
489 (* This includes all the classes, interfaces and traits this class is
491 tc_ancestors
: decl ty
SMap.t
;
492 tc_req_ancestors
: requirement list
;
493 tc_req_ancestors_extends
: SSet.t
; (* the extends of req_ancestors *)
495 tc_enum_type
: enum_type
option;
496 tc_decl_errors
: Errors.t
option;
499 and typeconst_type
= {
501 ttc_constraint
: decl ty
option;
502 ttc_type
: decl ty
option;
508 te_constraint
: decl ty
option;
513 td_vis
: Nast.typedef_visibility
;
514 td_tparams
: decl tparam list
;
515 td_constraint
: decl ty
option;
517 td_decl_errors
: Errors.t
option;
521 Ast.variance
* Ast.id
* (Ast.constraint_kind
* 'phase ty
) list
523 and 'phase where_constraint
=
524 'phase ty
* Ast.constraint_kind
* 'phase ty
530 (* Tracks information about how a type was expanded *)
532 (* A list of the type defs and type access we have expanded thus far. Used
533 * to prevent entering into a cycle when expanding these types
535 type_expansions
: (Pos.t
* string) list
;
536 substs
: locl ty
SMap.t
;
538 (* The class that the type is extracted from. Used for creating expression
539 * dependent types for type constants.
541 from_class
: Nast.class_id_
option;
544 type ety
= expand_env
* locl ty
546 let has_expanded {type_expansions
; _
} x
=
547 List.exists type_expansions
begin function
548 | (_
, x'
) when x
= x'
-> true
552 (* The identifier for this *)
553 let this = Local_id.make
"$this"
555 let arity_min ft_arity
: int = match ft_arity
with
556 | Fstandard
(min
, _
) | Fvariadic
(min
, _
) | Fellipsis min
-> min
558 let get_param_mode ~is_ref callconv
=
559 (* If a param has both & and inout, this should have errored in parsing. *)
561 | Some
Ast.Pinout
-> FPinout
562 | None
when is_ref
-> FPref
565 module AbstractKind
= struct
566 let to_string = function
567 | AKnewtype
(name
, _
) -> name
568 | AKgeneric name
-> name
569 | AKenum name
-> "enum "^
(Utils.strip_ns name
)
570 | AKdependent
(dt
, ids
) ->
573 | `
this -> SN.Typehints.this
574 | `static
-> "<"^
SN.Classes.cStatic^
">"
577 let display_id = Reason.get_expr_display_id i
in
578 "<expr#"^string_of_int
display_id^
">" in
579 String.concat
"::" (dt::ids
)
581 let is_generic_dep_ty s
= String_utils.is_substring
"::" s
584 module ShapeFieldMap
= struct
585 include Nast.ShapeMap
587 let map_and_rekey shape_map key_f value_f
=
588 let f_over_shape_field_type ({ sft_ty
; _
} as shape_field_type
) =
589 { shape_field_type
with sft_ty
= value_f sft_ty
} in
590 Nast.ShapeMap.map_and_rekey
593 f_over_shape_field_type
595 let map_env f env shape_map
=
596 let f_over_shape_field_type env
({ sft_ty
; _
} as shape_field_type
) =
597 let env, sft_ty
= f
env sft_ty
in
598 env, { shape_field_type
with sft_ty
} in
599 Nast.ShapeMap.map_env f_over_shape_field_type env shape_map
601 let map f shape_map
= map_and_rekey shape_map
(fun x
-> x
) f
603 let iter f shape_map
=
604 let f_over_shape_field_type shape_map_key
{ sft_ty
; _
} =
605 f shape_map_key sft_ty
in
606 Nast.ShapeMap.iter f_over_shape_field_type shape_map
608 let iter_values f
= iter (fun _
-> f
)
611 module ShapeFieldList
= struct
614 let map_env env xs ~f
=
615 let f_over_shape_field_type env ({ sft_ty
; _
} as shape_field_type
) =
616 let env, sft_ty
= f
env sft_ty
in
617 env, { shape_field_type
with sft_ty
} in
618 Hh_core.List.map_env env xs ~f
:f_over_shape_field_type
621 (*****************************************************************************)
623 (*****************************************************************************)
625 (* Set to true when we are trying to infer the missing type hints. *)
626 let is_suggest_mode = ref false
628 (* Ordinal value for type constructor, for localized types *)
629 let ty_con_ordinal ty
=
644 | Tunresolved _
-> 13
649 let array_kind_con_ordinal ak
=
655 | AKvarray_or_darray _
-> 4
661 let abstract_kind_con_ordinal ak
=
668 (* Compare two types syntactically, ignoring reason information and other
669 * small differences that do not affect type inference behaviour. This
670 * comparison function can be used to construct tree-based sets of types,
671 * or to compare two types for "exact" equality.
672 * Note that this function does *not* expand type variables, or type
674 * But if ty_compare ty1 ty2 = 0, then the types must not be distinguishable
675 * by any typing rules.
677 let rec ty_compare ty1 ty2
=
678 let ty_1, ty_2
= (snd ty1
, snd ty2
) in
679 match ty_1, ty_2
with
680 | Tprim ty1
, Tprim ty2
->
682 | Toption ty
, Toption ty2
->
684 | Tfun fty
, Tfun fty2
->
685 tfun_compare fty fty2
686 | Tunresolved tyl1
, Tunresolved tyl2
687 | Ttuple tyl1
, Ttuple tyl2
->
688 tyl_compare tyl1 tyl2
689 | Tabstract
(ak1
, opt_cstr1
), Tabstract
(ak2
, opt_cstr2
) ->
690 begin match abstract_kind_compare ak1 ak2
with
691 | 0 -> opt_ty_compare opt_cstr1 opt_cstr2
694 (* An instance of a class or interface, ty list are the arguments *)
695 | Tclass
(id
, tyl
), Tclass
(id2
, tyl2
) ->
696 begin match String.compare
(snd id
) (snd id2
) with
697 | 0 -> tyl_compare tyl tyl2
700 | Tarraykind ak1
, Tarraykind ak2
->
701 array_kind_compare ak1 ak2
702 | Tshape
(known1
, fields1
), Tshape
(known2
, fields2
) ->
703 begin match shape_fields_known_compare known1 known2
with
705 List.compare ~cmp
:(fun (k1
,v1
) (k2
,v2
) ->
706 match compare k1 k2
with
707 | 0 -> shape_field_type_compare v1 v2
709 (Nast.ShapeMap.elements fields1
) (Nast.ShapeMap.elements fields2
)
712 | Tvar v1
, Tvar v2
->
714 | Tanon
(_
, id1
), Tanon
(_
, id2
) ->
717 ty_con_ordinal ty1
- ty_con_ordinal ty2
719 and shape_fields_known_compare sfk1 sfk2
=
720 match sfk1
, sfk2
with
721 | FieldsFullyKnown
, FieldsFullyKnown
-> 0
722 | FieldsFullyKnown
, FieldsPartiallyKnown _
-> -1
723 | FieldsPartiallyKnown _
, FieldsFullyKnown
-> 1
724 | FieldsPartiallyKnown f1
, FieldsPartiallyKnown f2
->
725 compare
(Nast.ShapeMap.keys f1
) (Nast.ShapeMap.keys f2
)
727 and shape_field_type_compare sft1 sft2
=
728 match ty_compare sft1
.sft_ty sft2
.sft_ty
with
729 | 0 -> compare sft1
.sft_optional sft2
.sft_optional
732 and tfun_compare fty1 fty2
=
733 begin match ty_compare fty1
.ft_ret fty2
.ft_ret
with
735 begin match ft_params_compare fty1
.ft_params fty2
.ft_params
with
738 (fty1
.ft_is_coroutine
, fty1
.ft_arity
, fty1
.ft_ret_by_ref
, fty1
.ft_reactive
,
739 fty1
.ft_return_disposable
, fty1
.ft_mutable
, fty1
.ft_returns_mutable
)
740 (fty2
.ft_is_coroutine
, fty2
.ft_arity
, fty2
.ft_ret_by_ref
, fty2
.ft_reactive
,
741 fty2
.ft_return_disposable
, fty2
.ft_mutable
, fty2
.ft_returns_mutable
)
747 and ft_params_compare params1 params2
=
748 List.compare ~cmp
:ft_param_compare params1 params2
750 and ft_param_compare param1 param2
=
751 match ty_compare param1
.fp_type param2
.fp_type
with
754 (param1
.fp_kind
, param1
.fp_accept_disposable
, param1
.fp_mutable
)
755 (param2
.fp_kind
, param2
.fp_accept_disposable
, param2
.fp_mutable
)
758 and tyl_compare tyl1 tyl2
=
759 List.compare ~cmp
:ty_compare tyl1 tyl2
761 and opt_ty_compare opt_ty1 opt_ty2
=
762 match opt_ty1
, opt_ty2
with
766 | Some ty1
, Some ty2
-> ty_compare ty1 ty2
768 and array_kind_compare ak1 ak2
=
770 | AKmap
(ty1
, ty2
), AKmap
(ty3
, ty4
)
771 | AKdarray
(ty1
, ty2
), AKdarray
(ty3
, ty4
) ->
772 tyl_compare
[ty1
; ty2
] [ty3
; ty4
]
773 | AKvarray ty1
, AKvarray ty2
774 | AKvarray_or_darray ty1
, AKvarray_or_darray ty2
775 | AKvec ty1
, AKvec ty2
->
778 array_kind_con_ordinal ak1
- array_kind_con_ordinal ak2
780 and abstract_kind_compare t1 t2
=
782 | AKnewtype
(id
, tyl
), AKnewtype
(id2
, tyl2
) ->
783 begin match String.compare id id2
with
784 | 0 -> tyl_compare tyl tyl2
787 | AKgeneric id1
, AKgeneric id2
788 | AKenum id1
, AKenum id2
->
789 String.compare id1 id2
790 | AKdependent d1
, AKdependent d2
->
793 abstract_kind_con_ordinal t1
- abstract_kind_con_ordinal t2
795 let ty_equal ty1 ty2
= ty_compare ty1 ty2
= 0