ocaml/typing/typetexp.ml

349 lines
12 KiB
OCaml

(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, 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$ *)
(* Typechecking of type expressions for the core language *)
open Misc
open Parsetree
open Types
open Ctype
exception Already_bound
type error =
Unbound_type_variable of string
| Unbound_type_constructor of Longident.t
| Type_arity_mismatch of Longident.t * int * int
| Bound_type_variable of string
| Recursive_type
| Unbound_class of Longident.t
| Unbound_row_variable of Longident.t
| Type_mismatch of (type_expr * type_expr) list
| Alias_type_mismatch of (type_expr * type_expr) list
| Present_has_conjunction of string
| Present_has_no_type of string
| Multiple_constructor of string
exception Error of Location.t * error
(* Translation of type expressions *)
let type_variables = ref (Tbl.empty : (string, type_expr) Tbl.t)
let saved_type_variables = ref ([] : (string, type_expr) Tbl.t list)
let used_variables = ref (Tbl.empty : (string, type_expr) Tbl.t)
let bindings = ref ([] : (Location.t * type_expr * type_expr) list)
(* These two variables are used for the "delayed" policy. *)
let reset_type_variables () =
reset_global_level ();
type_variables := Tbl.empty;
saved_type_variables := []
let narrow () =
increase_global_level ();
saved_type_variables := !type_variables :: !saved_type_variables
let widen () =
restore_global_level ();
match !saved_type_variables with
tv :: rem -> type_variables := tv; saved_type_variables := rem
| [] -> assert false
let enter_type_variable strict name =
try
let v = Tbl.find name !type_variables in
if strict then raise Already_bound;
v
with Not_found ->
let v = new_global_var() in
type_variables := Tbl.add name v !type_variables;
v
let type_variable loc name =
try
Tbl.find name !type_variables
with Not_found ->
raise(Error(loc, Unbound_type_variable ("'" ^ name)))
type policy = Fixed | Extensible | Delayed
let rec transl_type env policy styp =
match styp.ptyp_desc with
Ptyp_any -> Ctype.newvar ()
| Ptyp_var name ->
begin
match policy with
Fixed ->
begin try
Tbl.find name !type_variables
with Not_found ->
raise(Error(styp.ptyp_loc, Unbound_type_variable ("'" ^ name)))
end
| Extensible ->
begin try
Tbl.find name !type_variables
with Not_found ->
let v = new_global_var () in
type_variables := Tbl.add name v !type_variables;
v
end
| Delayed ->
begin try
Tbl.find name !used_variables
with Not_found -> try
let v1 = Tbl.find name !type_variables in
let v2 = new_global_var () in
used_variables := Tbl.add name v2 !used_variables;
bindings := (styp.ptyp_loc, v1, v2)::!bindings;
v2
with Not_found ->
let v = new_global_var () in
type_variables := Tbl.add name v !type_variables;
used_variables := Tbl.add name v !used_variables;
v
end
end
| Ptyp_arrow(l, st1, st2) ->
let ty1 = transl_type env policy st1 in
let ty2 = transl_type env policy st2 in
newty (Tarrow(l, ty1, ty2, Cok))
| Ptyp_tuple stl ->
newty (Ttuple(List.map (transl_type env policy) stl))
| Ptyp_constr(lid, stl) ->
let (path, decl) =
try
Env.lookup_type lid env
with Not_found ->
raise(Error(styp.ptyp_loc, Unbound_type_constructor lid)) in
if List.length stl <> decl.type_arity then
raise(Error(styp.ptyp_loc, Type_arity_mismatch(lid, decl.type_arity,
List.length stl)));
let args = List.map (transl_type env policy) stl in
let params = List.map (fun _ -> Ctype.newvar ()) args in
let cstr = newty (Tconstr(path, params, ref Mnil)) in
begin try
Ctype.enforce_constraints env cstr
with Unify trace ->
raise (Error(styp.ptyp_loc, Type_mismatch trace))
end;
List.iter2
(fun (sty, ty) ty' ->
try unify env ty ty' with Unify trace ->
raise (Error(sty.ptyp_loc, Type_mismatch trace)))
(List.combine stl args) params;
cstr
| Ptyp_object fields ->
newobj (transl_fields env policy fields)
| Ptyp_class(lid, stl, present) ->
if policy = Fixed then
raise(Error(styp.ptyp_loc, Unbound_row_variable lid));
let (path, decl, is_variant) =
try
let (path, decl) = Env.lookup_type lid env in
match decl.type_manifest with
None -> raise Not_found
| Some ty ->
match (repr ty).desc with
Tvariant row when Btype.static_row row -> (path, decl, true)
| _ -> raise Not_found
with Not_found -> try
if present <> [] then raise Not_found;
let lid2 =
match lid with
Longident.Lident s -> Longident.Lident ("#" ^ s)
| Longident.Ldot(r, s) -> Longident.Ldot (r, "#" ^ s)
| Longident.Lapply(_, _) -> fatal_error "Typetexp.transl_type"
in
let (path, decl) = Env.lookup_type lid2 env in
(path, decl, false)
with Not_found ->
raise(Error(styp.ptyp_loc, Unbound_class lid))
in
if List.length stl <> decl.type_arity then
raise(Error(styp.ptyp_loc, Type_arity_mismatch(lid, decl.type_arity,
List.length stl)));
let args = List.map (transl_type env policy) stl in
let cstr = newty (Tconstr(path, args, ref Mnil)) in
let ty =
try Ctype.expand_head env cstr
with Unify trace ->
raise (Error(styp.ptyp_loc, Type_mismatch trace))
in
let params = Ctype.instance_list decl.type_params in
List.iter2
(fun (sty, ty') ty ->
try unify env ty' ty with Unify trace ->
raise (Error(sty.ptyp_loc, Type_mismatch trace)))
(List.combine stl args) params;
begin match ty.desc with
Tvariant row ->
let row = Btype.row_repr row in
List.iter
(fun l -> if not (List.mem_assoc l row.row_fields) then
raise(Error(styp.ptyp_loc, Present_has_no_type l)))
present;
let bound = ref row.row_bound in
let fields =
List.map
(fun (l,f) -> l,
if List.mem l present then f else
match Btype.row_field_repr f with
| Rpresent (Some ty) ->
bound := ty :: !bound;
Reither(false, [ty], false, ref None)
| Rpresent None ->
Reither (true, [], false, ref None)
| _ -> f)
row.row_fields
in
let row = { row with row_fields = fields; row_bound = !bound;
row_name = Some (path, args) } in
newty (Tvariant row)
| _ ->
ty
end
| Ptyp_alias(st, alias) ->
if Tbl.mem alias !type_variables then
raise(Error(styp.ptyp_loc, Bound_type_variable alias))
else
let ty' = new_global_var () in
type_variables := Tbl.add alias ty' !type_variables;
let ty = transl_type env policy st in
begin try unify env ty ty' with Unify trace ->
raise(Error(styp.ptyp_loc, Alias_type_mismatch trace))
end;
ty
| Ptyp_variant(fields, closed, present) ->
let bound = ref [] in
ignore (List.fold_left
(fun (ll,hl) (l,_,_) ->
if List.mem l ll then
raise(Error(styp.ptyp_loc, Multiple_constructor l));
let h = Btype.hash_variant l in
if List.mem h hl then
raise(Ctype.Tags(l, List.assoc h (List.combine hl ll)));
(l::ll, h::hl))
([],[])
fields);
let fields =
List.map
(fun (l, c, stl) ->
l, if List.mem l present then begin
if List.length stl > 1 || c && stl <> [] then
raise(Error(styp.ptyp_loc, Present_has_conjunction l));
match stl with [] -> Rpresent None
| st::_ -> Rpresent(Some(transl_type env policy st))
end else begin
let tl = List.map (transl_type env policy) stl in
bound := tl @ !bound;
Reither(c, tl, false, ref None)
end)
fields
in
List.iter
(fun l -> if not (List.mem_assoc l fields) then
raise(Error(styp.ptyp_loc, Present_has_no_type l)))
present;
let row =
{ row_fields = fields; row_more = newvar ();
row_bound = !bound; row_closed = closed; row_name = None } in
if policy = Fixed && not (Btype.static_row row) then
raise(Error(styp.ptyp_loc, Unbound_type_variable "[..]"));
newty (Tvariant row)
and transl_fields env policy =
function
[] ->
newty Tnil
| {pfield_desc = Pfield_var} as field::_ ->
if policy = Fixed then
raise(Error(field.pfield_loc, Unbound_type_variable "<..>"));
newvar ()
| {pfield_desc = Pfield(s, e)}::l ->
let ty1 = transl_type env policy e in
let ty2 = transl_fields env policy l in
newty (Tfield (s, Fpresent, ty1, ty2))
let transl_simple_type env fixed styp =
let typ = transl_type env (if fixed then Fixed else Extensible) styp in
typ
let transl_simple_type_delayed env styp =
used_variables := Tbl.empty;
bindings := [];
let typ = transl_type env Delayed styp in
let b = !bindings in
used_variables := Tbl.empty;
bindings := [];
(typ,
function () ->
List.iter
(function (loc, t1, t2) ->
try unify env t1 t2 with Unify trace ->
raise (Error(loc, Type_mismatch trace)))
b)
let transl_type_scheme env styp =
reset_type_variables();
begin_def();
let typ = transl_simple_type env false styp in
end_def();
generalize typ;
typ
(* Error report *)
open Format
open Printtyp
let report_error ppf = function
| Unbound_type_variable name ->
fprintf ppf "Unbound type parameter %s" name
| Unbound_type_constructor lid ->
fprintf ppf "Unbound type constructor %a" longident lid
| Type_arity_mismatch(lid, expected, provided) ->
fprintf ppf
"@[The type constructor %a@ expects %i argument(s),@ \
but is here applied to %i argument(s)@]"
longident lid expected provided
| Bound_type_variable name ->
fprintf ppf "Already bound type parameter %s" name
| Recursive_type ->
fprintf ppf "This type is recursive"
| Unbound_class lid ->
fprintf ppf "Unbound class %a" longident lid
| Unbound_row_variable lid ->
fprintf ppf "Unbound row variable in #%a" longident lid
| Type_mismatch trace ->
Printtyp.unification_error true trace
(function ppf ->
fprintf ppf "This type")
ppf
(function ppf ->
fprintf ppf "should be an instance of type")
| Alias_type_mismatch trace ->
Printtyp.unification_error true trace
(function ppf ->
fprintf ppf "This alias is bound to type")
ppf
(function ppf ->
fprintf ppf "but is used as an instance of type")
| Present_has_conjunction l ->
fprintf ppf "The present constructor %s has a conjunctive type" l
| Present_has_no_type l ->
fprintf ppf "The present constructor %s has no type" l
| Multiple_constructor l ->
fprintf ppf "The variant constructor %s is multiply defined" l