ocaml/typing/printtyp.ml

897 lines
28 KiB
OCaml

(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy and Jerome Vouillon, projet Cristal, INRIA Rocquencourt*)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* en Automatique. All rights reserved. This file is distributed *)
(* under the terms of the Q Public License version 1.0. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Printing functions *)
open Misc
open Ctype
open Format
open Longident
open Path
open Asttypes
open Types
open Btype
open Outcometree
(* Redefine it here since goal differs *)
let rec opened_object ty =
match (repr ty).desc with
Tobject (t, _) -> opened_object t
| Tfield(_, _, _, t) -> opened_object t
| Tvar -> true
| Tunivar -> true
| _ -> false
(* Print a long identifier *)
let rec longident ppf = function
| Lident s -> fprintf ppf "%s" s
| Ldot(p, s) -> fprintf ppf "%a.%s" longident p s
| Lapply(p1, p2) -> fprintf ppf "%a(%a)" longident p1 longident p2
(* Print an identifier *)
let ident ppf id = fprintf ppf "%s" (Ident.name id)
(* Print a path *)
let ident_pervasive = Ident.create_persistent "Pervasives"
let rec tree_of_path = function
| Pident id ->
Oide_ident (Ident.name id)
| Pdot(Pident id, s, pos) when Ident.same id ident_pervasive ->
Oide_ident s
| Pdot(p, s, pos) ->
Oide_dot (tree_of_path p, s)
| Papply(p1, p2) ->
Oide_apply (tree_of_path p1, tree_of_path p2)
let rec path ppf = function
| Pident id ->
ident ppf id
| Pdot(Pident id, s, pos) when Ident.same id ident_pervasive ->
fprintf ppf "%s" s
| Pdot(p, s, pos) ->
fprintf ppf "%a.%s" path p s
| Papply(p1, p2) ->
fprintf ppf "%a(%a)" path p1 path p2
(* Print a type expression *)
let names = ref ([] : (type_expr * string) list)
let name_counter = ref 0
let reset_names () = names := []; name_counter := 0
let new_name () =
let name =
if !name_counter < 26
then String.make 1 (Char.chr(97 + !name_counter))
else String.make 1 (Char.chr(97 + !name_counter mod 26)) ^
string_of_int(!name_counter / 26) in
incr name_counter;
name
let name_of_type t =
try List.assq t !names with Not_found ->
let name = new_name () in
names := (t, name) :: !names;
name
let check_name_of_type t = ignore(name_of_type t)
let non_gen_mark sch ty =
if sch && ty.desc = Tvar && ty.level <> generic_level then "_" else ""
let print_name_of_type sch ppf t =
fprintf ppf "'%s%s" (non_gen_mark sch t) (name_of_type t)
let visited_objects = ref ([] : type_expr list)
let aliased = ref ([] : type_expr list)
let delayed = ref ([] : type_expr list)
let add_delayed t =
if not (List.mem_assq t !names) then delayed := t :: !delayed
let is_aliased ty = List.memq (proxy ty) !aliased
let add_alias ty =
let px = proxy ty in
if not (is_aliased px) then aliased := px :: !aliased
let namable_row row =
row.row_name <> None &&
List.for_all
(fun (_, f) ->
match row_field_repr f with
| Reither(c, l, _, _) ->
row.row_closed && if c then l = [] else List.length l = 1
| _ -> true)
row.row_fields
let rec mark_loops_rec visited ty =
let ty = repr ty in
let px = proxy ty in
if List.memq px visited then add_alias px else
let visited = px :: visited in
match ty.desc with
| Tvar -> ()
| Tarrow(_, ty1, ty2, _) ->
mark_loops_rec visited ty1; mark_loops_rec visited ty2
| Ttuple tyl -> List.iter (mark_loops_rec visited) tyl
| Tconstr(_, tyl, _) ->
List.iter (mark_loops_rec visited) tyl
| Tvariant row ->
if List.memq px !visited_objects then add_alias px else
begin
let row = row_repr row in
if not (static_row row) then
visited_objects := px :: !visited_objects;
match row.row_name with
| Some(p, tyl) when namable_row row ->
List.iter (mark_loops_rec visited) tyl
| _ ->
iter_row (mark_loops_rec visited) {row with row_bound = []}
end
| Tobject (fi, nm) ->
if List.memq px !visited_objects then add_alias px else
begin
if opened_object ty then
visited_objects := px :: !visited_objects;
begin match !nm with
| None ->
let fields, _ = flatten_fields fi in
List.iter
(fun (_, kind, ty) ->
if field_kind_repr kind = Fpresent then
mark_loops_rec visited ty)
fields
| Some (_, l) ->
List.iter (mark_loops_rec visited) (List.tl l)
end
end
| Tfield(_, kind, ty1, ty2) when field_kind_repr kind = Fpresent ->
mark_loops_rec visited ty1; mark_loops_rec visited ty2
| Tfield(_, _, _, ty2) ->
mark_loops_rec visited ty2
| Tnil -> ()
| Tsubst ty -> mark_loops_rec visited ty
| Tlink _ -> fatal_error "Printtyp.mark_loops_rec (2)"
| Tpoly (ty, tyl) ->
List.iter (fun t -> add_alias t) tyl;
mark_loops_rec visited ty
| Tunivar -> ()
let mark_loops ty =
normalize_type Env.empty ty;
mark_loops_rec [] ty;;
let reset_loop_marks () =
visited_objects := []; aliased := []; delayed := []
let reset () =
reset_names (); reset_loop_marks ()
let reset_and_mark_loops ty =
reset (); mark_loops ty
let reset_and_mark_loops_list tyl =
reset (); List.iter mark_loops tyl
(* Disabled in classic mode when printing an unification error *)
let print_labels = ref true
let print_label ppf l =
if !print_labels && l <> "" || is_optional l then fprintf ppf "%s:" l
let rec tree_of_typexp sch ty =
let ty = repr ty in
let px = proxy ty in
if List.mem_assq px !names && not (List.memq px !delayed) then
let mark = is_non_gen sch ty in
Otyp_var (mark, name_of_type px) else
let pr_typ () =
match ty.desc with
| Tvar ->
Otyp_var (is_non_gen sch ty, name_of_type ty)
| Tarrow(l, ty1, ty2, _) ->
let pr_arrow l ty1 ty2 =
let lab =
if !print_labels && l <> "" || is_optional l then l else ""
in
let t1 =
if is_optional l then
match (repr ty1).desc with
| Tconstr(path, [ty], _)
when Path.same path Predef.path_option ->
tree_of_typexp sch ty
| _ -> Otyp_stuff "<hidden>"
else tree_of_typexp sch ty1 in
Otyp_arrow (lab, t1, tree_of_typexp sch ty2) in
pr_arrow l ty1 ty2
| Ttuple tyl ->
Otyp_tuple (tree_of_typlist sch tyl)
| Tconstr(p, tyl, abbrev) ->
Otyp_constr (tree_of_path p, tree_of_typlist sch tyl)
| Tvariant row ->
let row = row_repr row in
let fields =
if row.row_closed then
List.filter (fun (_, f) -> row_field_repr f <> Rabsent)
row.row_fields
else row.row_fields in
let present =
List.filter
(fun (_, f) ->
match row_field_repr f with
| Rpresent _ -> true
| _ -> false)
fields in
let all_present = List.length present = List.length fields in
begin match row.row_name with
| Some(p, tyl) when namable_row row ->
let id = tree_of_path p in
let args = tree_of_typlist sch tyl in
if row.row_closed && all_present then
Otyp_constr (id, args)
else
let non_gen = is_non_gen sch px in
let tags =
if all_present then None else Some (List.map fst present) in
Otyp_variant (non_gen, Ovar_name(tree_of_path p, args),
row.row_closed, tags)
| _ ->
let non_gen =
not (row.row_closed && all_present) && is_non_gen sch px in
let fields = List.map (tree_of_row_field sch) fields in
let tags =
if all_present then None else Some (List.map fst present) in
Otyp_variant (non_gen, Ovar_fields fields, row.row_closed, tags)
end
| Tobject (fi, nm) ->
tree_of_typobject sch fi nm
| Tsubst ty ->
tree_of_typexp sch ty
| Tlink _ | Tnil | Tfield _ ->
fatal_error "Printtyp.tree_of_typexp"
| Tpoly (ty, []) ->
tree_of_typexp sch ty
| Tpoly (ty, tyl) ->
let tyl = List.map repr tyl in
(* let tyl = List.filter is_aliased tyl in *)
if tyl = [] then tree_of_typexp sch ty else begin
List.iter add_delayed tyl;
let tl = List.map name_of_type tyl in
Otyp_poly (tl, tree_of_typexp sch ty)
end
| Tunivar ->
Otyp_var (false, name_of_type ty)
in
if List.memq px !delayed then delayed := List.filter ((!=) px) !delayed;
if is_aliased px && ty.desc <> Tvar && ty.desc <> Tunivar then begin
check_name_of_type px;
Otyp_alias (pr_typ (), name_of_type px) end
else pr_typ ()
and tree_of_row_field sch (l, f) =
match row_field_repr f with
| Rpresent None | Reither(true, [], _, _) -> (l, false, [])
| Rpresent(Some ty) -> (l, false, [tree_of_typexp sch ty])
| Reither(c, tyl, _, _) ->
if c (* contradiction: un constructeur constant qui a un argument *)
then (l, true, tree_of_typlist sch tyl)
else (l, false, tree_of_typlist sch tyl)
| Rabsent -> (l, false, [] (* une erreur, en fait *))
and tree_of_typlist sch = function
| [] -> []
| ty :: tyl ->
let tr = tree_of_typexp sch ty in
tr :: tree_of_typlist sch tyl
and tree_of_typobject sch fi nm =
begin match !nm with
| None ->
let pr_fields fi =
let (fields, rest) = flatten_fields fi in
let present_fields =
List.fold_right
(fun (n, k, t) l ->
match field_kind_repr k with
| Fpresent -> (n, t) :: l
| _ -> l)
fields [] in
let sorted_fields =
Sort.list (fun (n, _) (n', _) -> n <= n') present_fields in
tree_of_typfields sch rest sorted_fields in
let (fields, rest) = pr_fields fi in
Otyp_object (fields, rest)
| Some (p, ty :: tyl) ->
let non_gen = is_non_gen sch (repr ty) in
let args = tree_of_typlist sch tyl in
Otyp_class (non_gen, tree_of_path p, args)
| _ ->
fatal_error "Printtyp.tree_of_typobject"
end
and is_non_gen sch ty =
sch && ty.desc = Tvar && ty.level <> generic_level
and tree_of_typfields sch rest = function
| [] ->
let rest =
match rest.desc with
| Tvar | Tunivar -> Some (is_non_gen sch rest)
| Tnil -> None
| _ -> fatal_error "typfields (1)"
in
([], rest)
| (s, t) :: l ->
let field = (s, tree_of_typexp sch t) in
let (fields, rest) = tree_of_typfields sch rest l in
(field :: fields, rest)
let typexp sch prio ppf ty =
!Oprint.out_type ppf (tree_of_typexp sch ty)
let type_expr ppf ty = typexp false 0 ppf ty
and type_sch ppf ty = typexp true 0 ppf ty
and type_scheme ppf ty = reset_and_mark_loops ty; typexp true 0 ppf ty
(* Maxence *)
let type_scheme_max ?(b_reset_names=true) ppf ty =
if b_reset_names then reset_names () ;
typexp true 0 ppf ty
(* Fin Maxence *)
let tree_of_type_scheme ty = reset_and_mark_loops ty; tree_of_typexp true ty
(* Print one type declaration *)
let tree_of_constraints params =
List.fold_right
(fun ty list ->
let ty' = unalias ty in
if proxy ty != proxy ty' then
let tr = tree_of_typexp true ty in
(tr, tree_of_typexp true ty') :: list
else list)
params []
let filter_params tyl =
let params =
List.fold_left
(fun tyl ty ->
let ty = repr ty in
if List.memq ty tyl then Btype.newgenty (Tsubst ty) :: tyl
else ty :: tyl)
[] tyl
in List.rev params
let string_of_mutable = function
| Immutable -> ""
| Mutable -> "mutable "
let rec tree_of_type_decl id decl =
reset();
let params = filter_params decl.type_params in
List.iter add_alias params;
List.iter mark_loops params;
List.iter check_name_of_type (List.map proxy params);
let ty_manifest =
match decl.type_manifest with
| None -> None
| Some ty ->
let ty =
(* Special hack to hide variant name *)
match repr ty with {desc=Tvariant row} ->
let row = row_repr row in
begin match row.row_name with
Some (Pident id', _) when Ident.same id id' ->
newgenty (Tvariant {row with row_name = None})
| _ -> ty
end
| _ -> ty
in
mark_loops ty;
Some ty
in
let rec mark = function
| Type_abstract -> ()
| Type_variant [] -> ()
| Type_variant cstrs ->
List.iter (fun (_, args) -> List.iter mark_loops args) cstrs
| Type_record(l, rep) ->
List.iter (fun (_, _, ty) -> mark_loops ty) l
| Type_private tkind -> mark tkind in
mark decl.type_kind;
let type_param =
function
| Otyp_var (_, id) -> id
| _ -> "?"
in
let type_defined decl =
if decl.type_kind = Type_abstract && ty_manifest = None
&& List.exists (fun x -> x <> (true,true,true)) decl.type_variance then
let vari = List.map (fun (co,cn,ct) -> (co,cn)) decl.type_variance in
(Ident.name id,
List.combine
(List.map (fun ty -> type_param (tree_of_typexp false ty)) params)
vari)
else
let ty =
tree_of_typexp false
(Btype.newgenty (Tconstr(Pident id, params, ref Mnil)))
in
match ty with
| Otyp_constr (Oide_ident id, tyl) ->
(id, List.map (fun ty -> (type_param ty, (true, true))) tyl)
| _ -> ("?", [])
in
let tree_of_manifest ty1 =
match ty_manifest with
| None -> ty1
| Some ty -> Otyp_manifest (tree_of_typexp false ty, ty1)
in
let (name, args) = type_defined decl in
let constraints = tree_of_constraints params in
let rec tree_of_tkind = function
| Type_abstract ->
begin match ty_manifest with
| None -> Otyp_abstract
| Some ty -> tree_of_typexp false ty
end
| Type_variant cstrs ->
tree_of_manifest (Otyp_sum (List.map tree_of_constructor cstrs))
| Type_record(lbls, rep) ->
tree_of_manifest (Otyp_record (List.map tree_of_label lbls))
| Type_private tkind -> Otyp_private (tree_of_tkind tkind) in
let ty = tree_of_tkind decl.type_kind
in
(name, args, ty, constraints)
and tree_of_constructor (name, args) =
(name, tree_of_typlist false args)
and tree_of_label (name, mut, arg) =
(name, mut = Mutable, tree_of_typexp false arg)
let tree_of_type_declaration id decl =
Osig_type [tree_of_type_decl id decl]
let type_declaration id ppf decl =
!Oprint.out_sig_item ppf (tree_of_type_declaration id decl)
(* Print an exception declaration *)
let tree_of_exception_declaration id decl =
let tyl = tree_of_typlist false decl in
Osig_exception (Ident.name id, tyl)
let exception_declaration id ppf decl =
!Oprint.out_sig_item ppf (tree_of_exception_declaration id decl)
(* Print a value declaration *)
let tree_of_value_description id decl =
let id = Ident.name id in
let ty = tree_of_type_scheme decl.val_type in
let prims =
match decl.val_kind with
| Val_prim p -> Primitive.description_list p
| _ -> []
in
Osig_value (id, ty, prims)
let value_description id ppf decl =
!Oprint.out_sig_item ppf (tree_of_value_description id decl)
(* Print a class type *)
let class_var sch ppf l (m, t) =
fprintf ppf
"@ @[<2>val %s%s :@ %a@]" (string_of_mutable m) l (typexp sch 0) t
let metho sch concrete ppf (lab, kind, ty) =
if lab <> "*dummy method*" then begin
let priv =
match field_kind_repr kind with
| Fvar _ (* {contents = None} *) -> "private "
| _ (* Fpresent *) -> "" in
let virt =
if Concr.mem lab concrete then "" else "virtual " in
fprintf ppf "@ @[<2>method %s%s%s :@ %a@]" priv virt lab (typexp sch 0) ty
end
let method_type ty =
let ty = repr ty in
match ty.desc with
Tpoly(ty, _) -> ty
| _ -> ty
let tree_of_metho sch concrete csil (lab, kind, ty) =
if lab <> "*dummy method*" then begin
let priv =
match field_kind_repr kind with
| Fvar _ (* {contents = None} *) -> true
| _ (* Fpresent *) -> false in
let virt = not (Concr.mem lab concrete) in
let ty = method_type ty in
Ocsg_method (lab, priv, virt, tree_of_typexp sch ty) :: csil
end
else csil
let rec prepare_class_type params = function
| Tcty_constr (p, tyl, cty) ->
let sty = Ctype.self_type cty in
if List.memq (proxy sty) !visited_objects
|| List.exists (fun ty -> (repr ty).desc <> Tvar) params
|| List.exists (deep_occur sty) tyl
then prepare_class_type params cty
else List.iter mark_loops tyl
| Tcty_signature sign ->
let sty = repr sign.cty_self in
(* Self may have a name *)
let px = proxy sty in
if List.memq px !visited_objects then add_alias sty
else visited_objects := px :: !visited_objects;
let (fields, _) =
Ctype.flatten_fields (Ctype.object_fields sign.cty_self)
in
List.iter (fun (_, _, ty) -> mark_loops (method_type ty)) fields;
Vars.iter (fun _ (_, ty) -> mark_loops ty) sign.cty_vars
| Tcty_fun (_, ty, cty) ->
mark_loops ty;
prepare_class_type params cty
let rec tree_of_class_type sch params =
function
| Tcty_constr (p', tyl, cty) ->
let sty = Ctype.self_type cty in
if List.memq (proxy sty) !visited_objects
|| List.exists (fun ty -> (repr ty).desc <> Tvar) params
then
tree_of_class_type sch params cty
else
Octy_constr (tree_of_path p', tree_of_typlist true tyl)
| Tcty_signature sign ->
let sty = repr sign.cty_self in
let self_ty =
if is_aliased sty then
Some (Otyp_var (false, name_of_type (proxy sty)))
else None
in
let (fields, _) =
Ctype.flatten_fields (Ctype.object_fields sign.cty_self)
in
let csil = [] in
let csil =
List.fold_left
(fun csil (ty1, ty2) -> Ocsg_constraint (ty1, ty2) :: csil)
csil (tree_of_constraints params)
in
let all_vars =
Vars.fold (fun l (m, t) all -> (l, m, t) :: all) sign.cty_vars [] in
let csil =
List.fold_left
(fun csil (l, m, t) ->
Ocsg_value (l, m = Mutable, tree_of_typexp sch t) :: csil)
csil all_vars
in
let csil =
List.fold_left (tree_of_metho sch sign.cty_concr) csil fields
in
Octy_signature (self_ty, List.rev csil)
| Tcty_fun (l, ty, cty) ->
let lab = if !print_labels && l <> "" || is_optional l then l else "" in
let ty =
if is_optional l then
match (repr ty).desc with
| Tconstr(path, [ty], _) when Path.same path Predef.path_option -> ty
| _ -> newconstr (Path.Pident(Ident.create "<hidden>")) []
else ty in
let tr = tree_of_typexp sch ty in
Octy_fun (lab, tr, tree_of_class_type sch params cty)
let class_type ppf cty =
reset ();
prepare_class_type [] cty;
!Oprint.out_class_type ppf (tree_of_class_type false [] cty)
let tree_of_class_params = function
| [] -> []
| params ->
let tyl = tree_of_typlist true params in
List.map (function Otyp_var (_, s) -> s | _ -> "?") tyl
let tree_of_class_declaration id cl =
let params = filter_params cl.cty_params in
reset ();
List.iter add_alias params;
prepare_class_type params cl.cty_type;
let sty = self_type cl.cty_type in
List.iter mark_loops params;
List.iter check_name_of_type (List.map proxy params);
if is_aliased sty then check_name_of_type (proxy sty);
let vir_flag = cl.cty_new = None in
Osig_class
(vir_flag, Ident.name id, tree_of_class_params params,
tree_of_class_type true params cl.cty_type)
let class_declaration id ppf cl =
!Oprint.out_sig_item ppf (tree_of_class_declaration id cl)
let tree_of_cltype_declaration id cl =
let params = List.map repr cl.clty_params in
reset ();
List.iter add_alias params;
prepare_class_type params cl.clty_type;
let sty = self_type cl.clty_type in
List.iter mark_loops params;
List.iter check_name_of_type (List.map proxy params);
if is_aliased sty then check_name_of_type (proxy sty);
let sign = Ctype.signature_of_class_type cl.clty_type in
let virt =
let (fields, _) =
Ctype.flatten_fields (Ctype.object_fields sign.cty_self) in
List.exists
(fun (lab, _, ty) ->
not (lab = "*dummy method*" || Concr.mem lab sign.cty_concr))
fields in
Osig_class_type
(virt, Ident.name id, tree_of_class_params params,
tree_of_class_type true params cl.clty_type)
let cltype_declaration id ppf cl =
!Oprint.out_sig_item ppf (tree_of_cltype_declaration id cl)
(* Print a module type *)
let rec tree_of_modtype = function
| Tmty_ident p ->
Omty_ident (tree_of_path p)
| Tmty_signature sg ->
Omty_signature (tree_of_signature sg)
| Tmty_functor(param, ty_arg, ty_res) ->
Omty_functor
(Ident.name param, tree_of_modtype ty_arg, tree_of_modtype ty_res)
and tree_of_signature = function
| [] -> []
| item :: rem ->
match item with
| Tsig_value(id, decl) ->
tree_of_value_description id decl :: tree_of_signature rem
| Tsig_type(id, decl) ->
let (type_decl_list, rem) =
let rec more_type_declarations = function
| Tsig_type(id, decl) :: rem ->
let (type_decl_list, rem) = more_type_declarations rem in
(id, decl) :: type_decl_list, rem
| rem -> [], rem in
more_type_declarations rem
in
let type_decl_list =
List.map (fun (id, decl) -> tree_of_type_decl id decl)
((id, decl) :: type_decl_list)
in
Osig_type type_decl_list
::
tree_of_signature rem
| Tsig_exception(id, decl) ->
Osig_exception (Ident.name id, tree_of_typlist false decl) ::
tree_of_signature rem
| Tsig_module(id, mty) ->
Osig_module (Ident.name id, tree_of_modtype mty) ::
tree_of_signature rem
| Tsig_modtype(id, decl) ->
tree_of_modtype_declaration id decl :: tree_of_signature rem
| Tsig_class(id, decl) ->
let rem =
match rem with
| ctydecl :: tydecl1 :: tydecl2 :: rem -> rem
| _ -> []
in
tree_of_class_declaration id decl :: tree_of_signature rem
| Tsig_cltype(id, decl) ->
let rem =
match rem with
| tydecl1 :: tydecl2 :: rem -> rem
| _ -> []
in
tree_of_cltype_declaration id decl :: tree_of_signature rem
and tree_of_modtype_declaration id decl =
let mty =
match decl with
| Tmodtype_abstract -> Omty_abstract
| Tmodtype_manifest mty -> tree_of_modtype mty
in
Osig_modtype (Ident.name id, mty)
let tree_of_module id mty = Osig_module (Ident.name id, tree_of_modtype mty)
let modtype ppf mty = !Oprint.out_module_type ppf (tree_of_modtype mty)
let modtype_declaration id ppf decl =
!Oprint.out_sig_item ppf (tree_of_modtype_declaration id decl)
(* Print a signature body (used by -i when compiling a .ml) *)
let print_signature ppf tree =
fprintf ppf "@[<v>%a@]" !Oprint.out_signature tree
let signature ppf sg =
fprintf ppf "%a" print_signature (tree_of_signature sg)
(* Print an unification error *)
let type_expansion t ppf t' =
if t == t' then type_expr ppf t else
let t' = if proxy t = proxy t' then unalias t' else t' in
fprintf ppf "@[<2>%a@ =@ %a@]" type_expr t type_expr t'
let rec trace fst txt ppf = function
| (t1, t1') :: (t2, t2') :: rem ->
if not fst then fprintf ppf "@,";
fprintf ppf "@[Type@;<1 2>%a@ %s@;<1 2>%a@] %a"
(type_expansion t1) t1' txt (type_expansion t2) t2'
(trace false txt) rem
| _ -> ()
let rec mismatch = function
| [(_, t); (_, t')] -> (t, t')
| _ :: _ :: rem -> mismatch rem
| _ -> assert false
let rec filter_trace = function
| (t1, t1') :: (t2, t2') :: rem ->
let rem' = filter_trace rem in
if t1 == t1' && t2 == t2'
then rem'
else (t1, t1') :: (t2, t2') :: rem'
| _ -> []
(* Hide variant name and var, to force printing the expanded type *)
let hide_variant_name t =
match repr t with
| {desc = Tvariant row} as t when (row_repr row).row_name <> None ->
newty2 t.level
(Tvariant {(row_repr row) with row_name = None;
row_more = newty2 (row_more row).level Tvar})
| _ -> t
let prepare_expansion (t, t') =
let t' = hide_variant_name t' in
mark_loops t; if t != t' then mark_loops t';
(t, t')
let print_tags ppf fields =
match fields with [] -> ()
| (t, _) :: fields ->
fprintf ppf "`%s" t;
List.iter (fun (t, _) -> fprintf ppf ",@ `%s" t) fields
let explanation unif t3 t4 ppf =
match t3.desc, t4.desc with
| Tfield _, Tvar | Tvar, Tfield _ ->
fprintf ppf "@,Self type cannot escape its class"
| Tconstr (p, _, _), Tvar
when unif && t4.level < Path.binding_time p ->
fprintf ppf
"@,@[The type constructor@;<1 2>%a@ would escape its scope@]"
path p
| Tvar, Tconstr (p, _, _)
when unif && t3.level < Path.binding_time p ->
fprintf ppf
"@,@[The type constructor@;<1 2>%a@ would escape its scope@]"
path p
| Tvar, Tunivar | Tunivar, Tvar ->
fprintf ppf "@,The universal variable %a would escape its scope"
type_expr (if t3.desc = Tunivar then t3 else t4)
| Tfield ("*dummy method*", _, _, _), _
| _, Tfield ("*dummy method*", _, _, _) ->
fprintf ppf
"@,Self type cannot be unified with a closed object type"
| Tfield (l, _, _, _), _ ->
fprintf ppf
"@,@[Only the first object type has a method %s@]" l
| _, Tfield (l, _, _, _) ->
fprintf ppf
"@,@[Only the second object type has a method %s@]" l
| Tvariant row1, Tvariant row2 ->
let row1 = row_repr row1 and row2 = row_repr row2 in
begin match
row1.row_fields, row1.row_closed, row2.row_fields, row1.row_closed with
| [], true, [], true ->
fprintf ppf "@,These two variant types have no intersection"
| [], true, fields, _ ->
fprintf ppf
"@,@[The first variant type does not allow tag(s)@ @[<hov>%a@]@]"
print_tags fields
| fields, _, [], true ->
fprintf ppf
"@,@[The second variant type does not allow tag(s)@ @[<hov>%a@]@]"
print_tags fields
| _ -> ()
end
| _ -> ()
let unification_error unif tr txt1 ppf txt2 =
reset ();
let tr = List.map (fun (t, t') -> (t, hide_variant_name t')) tr in
let (t3, t4) = mismatch tr in
match tr with
| [] | _ :: [] -> assert false
| t1 :: t2 :: tr ->
try
let t1, t1' = prepare_expansion t1
and t2, t2' = prepare_expansion t2 in
print_labels := not !Clflags.classic;
let tr = filter_trace tr in
let tr = List.map prepare_expansion tr in
fprintf ppf
"@[<v>\
@[%t@;<1 2>%a@ \
%t@;<1 2>%a\
@]%a%t\
@]"
txt1 (type_expansion t1) t1'
txt2 (type_expansion t2) t2'
(trace false "is not compatible with type") tr
(explanation unif t3 t4);
print_labels := true
with exn ->
print_labels := true;
raise exn
let report_unification_error ppf tr txt1 txt2 =
unification_error true tr txt1 ppf txt2;;
let trace fst txt ppf tr =
print_labels := not !Clflags.classic;
try match tr with
t1 :: t2 :: tr' ->
if fst then trace fst txt ppf (t1 :: t2 :: filter_trace tr')
else trace fst txt ppf (filter_trace tr);
print_labels := true
| _ -> ()
with exn ->
print_labels := true;
raise exn
let report_subtyping_error ppf tr1 txt1 tr2 =
reset ();
let tr1 = List.map prepare_expansion tr1
and tr2 = List.map prepare_expansion tr2 in
trace true txt1 ppf tr1;
if tr2 = [] then () else
let t3, t4 = mismatch tr2 in
trace false "is not compatible with type" ppf tr2;
explanation true t3 t4 ppf