1097 lines
36 KiB
OCaml
1097 lines
36 KiB
OCaml
(***********************************************************************)
|
|
(* *)
|
|
(* Objective Caml *)
|
|
(* *)
|
|
(* Jerome Vouillon, projet Cristal, INRIA Rocquencourt *)
|
|
(* *)
|
|
(* Copyright 1996 Institut National de Recherche en Informatique et *)
|
|
(* Automatique. Distributed only by permission. *)
|
|
(* *)
|
|
(***********************************************************************)
|
|
|
|
(* $Id$ *)
|
|
|
|
open Misc
|
|
open Parsetree
|
|
open Asttypes
|
|
open Types
|
|
open Typedtree
|
|
open Typecore
|
|
open Typetexp
|
|
|
|
type error =
|
|
Duplicate_method of string
|
|
| Duplicate_variable of string
|
|
| Duplicate_super_variable of string
|
|
| Repeated_parameter
|
|
| Virtual_class of string * string
|
|
| Closed_class of string
|
|
| Closed_ancestor of string * Path.t * string
|
|
| Non_closed of Ident.t * type_expr list * type_expr *
|
|
Ctype.closed_schema_result
|
|
| Mutable_var of string
|
|
| Undefined_var of string
|
|
| Variable_type_mismatch of string * (type_expr * type_expr) list
|
|
| Method_type_mismatch of string * (type_expr * type_expr) list
|
|
| Unconsistent_constraint
|
|
| Unbound_class of Longident.t
|
|
| Argument_type_mismatch of (type_expr * type_expr) list
|
|
| Abbrev_type_clash of type_expr * type_expr * type_expr
|
|
| Bad_parameters of Ident.t * type_expr * type_expr
|
|
| Illdefined_class of string
|
|
| Argument_arity_mismatch of Path.t * int * int
|
|
| Parameter_arity_mismatch of Path.t * int * int
|
|
| Parameter_mismatch of (type_expr * type_expr) list
|
|
|
|
exception Error of Location.t * error
|
|
|
|
let check_mutable loc lab mut mut' =
|
|
match mut, mut' with
|
|
(Immutable, Mutable) ->
|
|
raise(Error(loc, Mutable_var lab))
|
|
| _ ->
|
|
()
|
|
|
|
let rec add_methods env self concr concr_lst t =
|
|
match (Ctype.repr t).desc with
|
|
Tfield (lab, _, t') ->
|
|
Ctype.filter_method env lab self;
|
|
if Concr.mem lab concr_lst then
|
|
(Ctype.filter_method env lab concr; ());
|
|
add_methods env self concr concr_lst t'
|
|
| _ ->
|
|
()
|
|
|
|
let equalize_methods env self obj =
|
|
match (Ctype.expand_root env obj).desc with
|
|
Tobject (ty, _) ->
|
|
let rec equalize_methods_rec t =
|
|
match (Ctype.repr t).desc with
|
|
Tfield (lab, _, t') ->
|
|
Ctype.filter_method env lab self;
|
|
equalize_methods_rec t'
|
|
| _ ->
|
|
()
|
|
in
|
|
equalize_methods_rec ty
|
|
| _ ->
|
|
fatal_error "Typeclass.equalize_methods"
|
|
|
|
let make_stub env cl =
|
|
Ctype.begin_def ();
|
|
|
|
(* Create self (class type) *)
|
|
let self = Ctype.newobj (Ctype.newvar ()) in
|
|
let concr = Ctype.newobj (Ctype.newvar ()) in
|
|
|
|
(* Find concrete methods and marks methods *)
|
|
let concr_meths =
|
|
List.fold_left
|
|
(function meths ->
|
|
function
|
|
Pcf_inher (nm, _, _, _, loc) ->
|
|
let (_, anc) =
|
|
try
|
|
Env.lookup_class nm env
|
|
with Not_found ->
|
|
raise(Error(loc, Unbound_class nm))
|
|
in
|
|
begin match (Ctype.expand_root env anc.cty_self).desc with
|
|
Tobject (ty, _) ->
|
|
add_methods env self concr anc.cty_concr ty;
|
|
Concr.union anc.cty_concr meths
|
|
| _ -> fatal_error "Typeclass.make_stub"
|
|
end
|
|
| Pcf_val _ ->
|
|
meths
|
|
| Pcf_virt (lab, _, _) ->
|
|
Ctype.filter_method env lab self;
|
|
meths
|
|
| Pcf_meth (lab, _, _) ->
|
|
Ctype.filter_method env lab self;
|
|
Ctype.filter_method env lab concr;
|
|
Concr.add lab meths)
|
|
Concr.empty cl.pcl_field
|
|
in
|
|
Ctype.close_object concr;
|
|
|
|
Ctype.end_def ();
|
|
Ctype.generalize self;
|
|
Ctype.generalize concr;
|
|
|
|
(* Temporary object type *)
|
|
let temp_obj_params =
|
|
List.map (fun _ -> Ctype.newvar ()) (fst cl.pcl_param)
|
|
in
|
|
let temp_obj = Ctype.instance self in
|
|
let obj_temp_abbrev =
|
|
{ type_params = temp_obj_params;
|
|
type_arity = List.length temp_obj_params;
|
|
type_kind = Type_abstract;
|
|
type_manifest = Some temp_obj }
|
|
in let (obj_id, temp_env) = Env.enter_type cl.pcl_name obj_temp_abbrev env
|
|
in let abbrev =
|
|
Ctype.newty (Tconstr (Path.Pident obj_id, temp_obj_params, ref []))
|
|
in
|
|
|
|
(* Temporary class type *)
|
|
let (temp_cl_params, temp_cl) =
|
|
if cl.pcl_closed = Closed then
|
|
(temp_obj_params,
|
|
Ctype.newty (Tconstr(Path.Pident obj_id, temp_obj_params, ref [])))
|
|
else begin
|
|
let params = List.map (fun _ -> Ctype.newvar ()) (fst cl.pcl_param) in
|
|
let ty = Ctype.instance self in
|
|
Ctype.set_object_name ty params obj_id;
|
|
(params, ty)
|
|
end
|
|
in
|
|
let cl_temp_abbrev =
|
|
{ type_params = temp_cl_params;
|
|
type_arity = List.length temp_cl_params;
|
|
type_kind = Type_abstract;
|
|
type_manifest = Some temp_cl }
|
|
in let (cl_id, temp_env) =
|
|
Env.enter_type ("#" ^ cl.pcl_name) cl_temp_abbrev temp_env
|
|
in let cl_abbrev =
|
|
Ctype.newty (Tconstr (Path.Pident cl_id, temp_cl_params, ref []))
|
|
in
|
|
|
|
(* Temporary type for new *)
|
|
let new_args = List.map (fun _ -> Ctype.newvar ()) cl.pcl_args in
|
|
let new_ty =
|
|
if cl.pcl_kind = Concrete then
|
|
Some (List.fold_right
|
|
(fun arg ty -> Ctype.newty (Tarrow(arg, ty)))
|
|
new_args abbrev)
|
|
else
|
|
None
|
|
in let cl_temp_sig =
|
|
{ cty_params = [];
|
|
cty_args = []; cty_vars = Vars.empty;
|
|
cty_self = self; cty_concr = concr_meths;
|
|
cty_new = new_ty }
|
|
in let (id, temp_env) = Env.enter_class cl.pcl_name cl_temp_sig temp_env in
|
|
|
|
((cl, id, cl_id, obj_id, self, concr, concr_meths, new_args, new_ty,
|
|
temp_cl, temp_cl_params, cl_abbrev, temp_obj, temp_obj_params, abbrev),
|
|
temp_env)
|
|
|
|
let rec type_meth env loc self ty =
|
|
match (Ctype.repr ty).desc with
|
|
Tfield (lab, ty, ty') ->
|
|
let ty0 = Ctype.filter_method env lab self in
|
|
begin try
|
|
Ctype.unify env ty ty0
|
|
with Ctype.Unify trace ->
|
|
raise(Error(loc, Method_type_mismatch (lab, trace)))
|
|
end;
|
|
type_meth env loc self ty'
|
|
| _ ->
|
|
()
|
|
|
|
let missing_method env ty ty' =
|
|
let rec missing_method_rec met=
|
|
match (Ctype.repr met).desc with
|
|
Tfield(lab, _, met') ->
|
|
begin try
|
|
Ctype.filter_method env lab ty;
|
|
missing_method_rec met'
|
|
with Ctype.Unify _ ->
|
|
lab
|
|
end
|
|
| _ ->
|
|
fatal_error "Typeclass.missing_method (1)"
|
|
in
|
|
match (Ctype.expand_root env ty').desc with
|
|
Tobject (met, _) ->
|
|
missing_method_rec met
|
|
| _ ->
|
|
fatal_error "Typeclass.missing_method (2)"
|
|
|
|
let rec closed_scheme t =
|
|
match (Ctype.repr t).desc with
|
|
Tfield (lab, _, t') ->
|
|
Ctype.newty (Tfield (lab, Ctype.newvar (), closed_scheme t'))
|
|
| Tnil ->
|
|
Ctype.newty Tnil
|
|
| _ ->
|
|
fatal_error "Typeclass.closed_scheme"
|
|
|
|
let vals_remove lab vals =
|
|
Vars.fold (fun l k v -> if lab = l then v else Vars.add l k v)
|
|
vals Vars.empty
|
|
|
|
let insert_value env lab priv mut ty loc vals =
|
|
begin try
|
|
let (mut', ty') = Vars.find lab vals in
|
|
check_mutable loc lab mut mut';
|
|
try Ctype.unify env ty ty' with Ctype.Unify trace ->
|
|
raise(Error(loc, Variable_type_mismatch(lab, trace)))
|
|
with Not_found -> () end;
|
|
if priv = Private then
|
|
vals_remove lab vals
|
|
else
|
|
Vars.add lab (mut, ty) vals
|
|
|
|
let change_value_status lab priv mut loc vals =
|
|
try
|
|
let (mut', ty') = Vars.find lab vals in
|
|
check_mutable loc lab mut mut';
|
|
if priv = Private then
|
|
(vals_remove lab vals, ty')
|
|
else
|
|
(Vars.add lab (mut, ty') vals, ty')
|
|
with Not_found ->
|
|
raise(Error(loc, Undefined_var lab))
|
|
|
|
let type_class_field env var_env self cl (met_env, fields, vars_sig) =
|
|
function
|
|
Pcf_inher (cl_name, params, args, super, loc) ->
|
|
(* Find class type *)
|
|
let (path, cl_type) =
|
|
try
|
|
Env.lookup_class cl_name env
|
|
with Not_found ->
|
|
raise(Error(loc, Unbound_class cl_name))
|
|
in
|
|
let (params', args', vars', self') = Ctype.instance_class cl_type in
|
|
|
|
(* Unify parameters *)
|
|
if List.length params <> List.length params' then
|
|
raise(Error(loc, Parameter_arity_mismatch
|
|
(path, List.length params', List.length params)));
|
|
List.iter2
|
|
(fun sty ty ->
|
|
let ty' = Typetexp.transl_simple_type var_env false sty in
|
|
try Ctype.unify var_env ty' ty with Ctype.Unify trace ->
|
|
raise(Error(sty.ptyp_loc, Parameter_mismatch trace)))
|
|
params params';
|
|
|
|
(* Type arguments *)
|
|
if List.length args <> List.length args' then
|
|
raise(Error(loc, Argument_arity_mismatch
|
|
(path, List.length args', List.length args)));
|
|
let args = List.map2 (type_expect var_env) args args' in
|
|
|
|
(* Variables *)
|
|
let (vars, vars_sig, met_env) =
|
|
Vars.fold
|
|
(fun lab (mut, ty) (l, v_sig, env) ->
|
|
let (id, env) =
|
|
Env.enter_value lab
|
|
{val_type = ty; val_kind = Val_ivar mut} env
|
|
in
|
|
((lab, id)::l,
|
|
insert_value var_env lab Public mut ty loc v_sig,
|
|
env))
|
|
vars' ([], vars_sig, met_env)
|
|
in
|
|
|
|
(* Self type *)
|
|
let ty' = Ctype.expand_root var_env self' in
|
|
begin match ty'.desc with
|
|
Tobject (fi, _) ->
|
|
if ty' != Ctype.expand_root var_env self then begin
|
|
if not (Ctype.opened_object self') then
|
|
begin try
|
|
Ctype.unify var_env self
|
|
(Ctype.newobj (closed_scheme fi))
|
|
with Ctype.Unify _ ->
|
|
let lab = missing_method var_env self' self in
|
|
raise(Error(loc, Closed_ancestor
|
|
(cl.pcl_name, path, lab)))
|
|
end;
|
|
ty'.desc <- Tlink self;
|
|
type_meth var_env loc self fi
|
|
end
|
|
| _ ->
|
|
fatal_error "Typeclass.transl_class"
|
|
end;
|
|
|
|
(* Super methods *)
|
|
let (met, met_env) =
|
|
match super with
|
|
None ->
|
|
([], met_env)
|
|
| Some name ->
|
|
let ty = Ctype.newobj (Ctype.newvar ()) in
|
|
let used_methods =
|
|
Concr.fold
|
|
(fun lab rem ->
|
|
Ctype.unify met_env (Ctype.filter_method met_env lab ty)
|
|
(Ctype.filter_method met_env lab self);
|
|
(lab, Ident.create lab)::rem)
|
|
cl_type.cty_concr
|
|
[]
|
|
in
|
|
Ctype.close_object ty;
|
|
let (id, met_env) =
|
|
Env.enter_value name
|
|
{val_type = ty; val_kind = Val_anc used_methods} met_env
|
|
in
|
|
(used_methods, met_env)
|
|
in
|
|
(met_env, Cf_inher (path, args, vars, met)::fields, vars_sig)
|
|
|
|
| Pcf_val (lab, priv, mut, sinit, loc) ->
|
|
begin match sinit with
|
|
Some sexp ->
|
|
let exp = type_exp var_env sexp in
|
|
let (id, met_env) =
|
|
Env.enter_value lab
|
|
{val_type = exp.exp_type; val_kind = Val_ivar mut} met_env
|
|
in
|
|
(met_env, Cf_val (lab, id, priv, Some exp)::fields,
|
|
insert_value var_env lab priv mut exp.exp_type loc vars_sig)
|
|
| None ->
|
|
let (vars_sig, ty) =
|
|
change_value_status lab priv mut loc vars_sig
|
|
in
|
|
let (id, met_env) =
|
|
Env.enter_value lab
|
|
{val_type = ty; val_kind = Val_ivar mut} met_env
|
|
in
|
|
(met_env, Cf_val (lab, id, priv, None)::fields, vars_sig)
|
|
end
|
|
|
|
| Pcf_virt (lab, ty, loc) ->
|
|
let ty = transl_simple_type met_env false ty in
|
|
let ty' = Ctype.filter_method met_env lab self in
|
|
begin try Ctype.unify met_env ty ty' with Ctype.Unify trace ->
|
|
raise(Error(loc, Method_type_mismatch (lab, trace)))
|
|
end;
|
|
(met_env, fields, vars_sig)
|
|
|
|
| Pcf_meth (lab, expr, loc) ->
|
|
let (texp, ty) = type_method met_env self cl.pcl_self expr in
|
|
let ty' = Ctype.filter_method met_env lab self in
|
|
begin try Ctype.unify met_env ty ty' with Ctype.Unify trace ->
|
|
raise(Error(loc, Method_type_mismatch (lab, trace)))
|
|
end;
|
|
(met_env, Cf_meth (lab, texp)::fields, vars_sig)
|
|
|
|
let transl_class temp_env env
|
|
(cl, id, cl_id, obj_id, self, concr, concr_meths, new_args, new_ty,
|
|
temp_cl, temp_cl_params, cl_abbrev, temp_obj, temp_obj_params, abbrev)
|
|
=
|
|
reset_type_variables ();
|
|
Ctype.begin_def ();
|
|
|
|
(* Self type *)
|
|
let self = Ctype.instance self in
|
|
|
|
(* Introduce parameters *)
|
|
let params =
|
|
try
|
|
List.map (enter_type_variable true) (fst cl.pcl_param)
|
|
with Already_bound ->
|
|
raise(Error(snd cl.pcl_param, Repeated_parameter))
|
|
in
|
|
|
|
(* Bind self type variable *)
|
|
begin match cl.pcl_self_ty with
|
|
Some v -> Ctype.unify temp_env self (enter_type_variable false v)
|
|
| None -> ()
|
|
end;
|
|
|
|
(* Add constraints *)
|
|
List.iter
|
|
(function (v, ty, loc) ->
|
|
try
|
|
Ctype.unify temp_env
|
|
(type_variable loc v) (transl_simple_type temp_env false ty)
|
|
with Ctype.Unify _ ->
|
|
raise(Error(loc, Unconsistent_constraint)))
|
|
cl.pcl_cstr;
|
|
|
|
(* Type arguments and fields *)
|
|
let (args, var_env) = type_pattern_list temp_env cl.pcl_args in
|
|
let arg_sig = List.map (fun exp -> exp.pat_type) args in
|
|
let (_, fields, vars_sig) =
|
|
List.fold_left (type_class_field env var_env self cl)
|
|
(temp_env, [], Vars.empty)
|
|
cl.pcl_field
|
|
in
|
|
|
|
(* Closeness of self (1) *)
|
|
if cl.pcl_closed = Closed then
|
|
Ctype.close_object self;
|
|
|
|
(* Generalize class *)
|
|
Ctype.end_def ();
|
|
List.iter Ctype.generalize params;
|
|
List.iter Ctype.generalize arg_sig;
|
|
Vars.iter (fun l (m, t) -> Ctype.generalize t) vars_sig;
|
|
Ctype.generalize self;
|
|
|
|
(* Temporary class abbreviation *)
|
|
let (cl_params, cl_ty) = Ctype.instance_parameterized_type params self in
|
|
begin try Ctype.unify temp_env temp_cl cl_ty with Ctype.Unify _ ->
|
|
Ctype.remove_object_name temp_cl;
|
|
raise(Error(cl.pcl_loc, Abbrev_type_clash (cl_abbrev, cl_ty, temp_cl)))
|
|
end;
|
|
begin try
|
|
List.iter2 (Ctype.unify temp_env) temp_cl_params cl_params
|
|
with Ctype.Unify _ ->
|
|
raise(Error(cl.pcl_loc,
|
|
Bad_parameters (cl_id, cl_abbrev,
|
|
Ctype.newty (Tconstr (Path.Pident cl_id, cl_params,
|
|
ref [])))))
|
|
end;
|
|
|
|
(* Object abbreviation and arguments for new *)
|
|
let (obj_params, arg_sig', obj_ty) =
|
|
Ctype.instance_parameterized_type_2 params arg_sig self
|
|
in
|
|
begin try Ctype.unify temp_env abbrev obj_ty with Ctype.Unify _ ->
|
|
raise(Error(cl.pcl_loc, Abbrev_type_clash (abbrev, obj_ty, temp_obj)))
|
|
end;
|
|
begin try
|
|
List.iter2 (Ctype.unify temp_env) temp_obj_params obj_params
|
|
with Ctype.Unify _ ->
|
|
raise(Error(cl.pcl_loc,
|
|
Bad_parameters (obj_id, abbrev,
|
|
Ctype.newty (Tconstr (Path.Pident obj_id, obj_params,
|
|
ref [])))))
|
|
end;
|
|
Ctype.close_object temp_obj;
|
|
List.iter2
|
|
(fun ty (exp, ty') ->
|
|
begin try
|
|
Ctype.unify temp_env ty' ty
|
|
with Ctype.Unify trace ->
|
|
raise(Error(exp.pat_loc, Argument_type_mismatch trace))
|
|
end)
|
|
new_args (List.combine args arg_sig');
|
|
|
|
(* Fill interface / implementation *)
|
|
let cl_imp =
|
|
{ cl_args = args;
|
|
cl_field = List.rev fields;
|
|
cl_loc = cl.pcl_loc }
|
|
in let cl_sig =
|
|
{ cty_params = params;
|
|
cty_args = arg_sig;
|
|
cty_vars = vars_sig;
|
|
cty_self = self;
|
|
cty_concr = concr_meths;
|
|
cty_new = new_ty }
|
|
in let new_env = Env.add_class id cl_sig env in
|
|
((cl, id, cl_id, obj_id, cl_sig, cl_imp,
|
|
concr, abbrev, new_args, temp_obj, temp_obj_params),
|
|
new_env)
|
|
|
|
let build_new_type temp_env env
|
|
(cl, id, cl_id, obj_id, cl_sig, cl_imp,
|
|
concr, abbrev, new_args, temp_obj, temp_obj_params)
|
|
=
|
|
(* Modify constrainsts to ensure the object abbreviation is well-formed *)
|
|
let (params, args, vars, self) = Ctype.instance_class cl_sig in
|
|
List.iter2 (Ctype.unify temp_env) params temp_obj_params;
|
|
(* Never fails *)
|
|
|
|
(* Closeness of self (2) *)
|
|
if (cl.pcl_closed <> Closed) & not (Ctype.opened_object self) then
|
|
raise(Error(cl.pcl_loc, Closed_class cl.pcl_name));
|
|
|
|
(* Check whether the class can be concrete *)
|
|
if cl.pcl_kind = Concrete then begin
|
|
let concr = Ctype.instance concr in
|
|
try
|
|
Ctype.unify temp_env concr temp_obj
|
|
with Ctype.Unify _ ->
|
|
let lab = missing_method temp_env concr temp_obj in
|
|
raise(Error(cl.pcl_loc,
|
|
Virtual_class (cl.pcl_name, lab)))
|
|
end;
|
|
|
|
equalize_methods temp_env self temp_obj;
|
|
|
|
(* self should not be an abbreviation (printtyp) *)
|
|
let exp_self = Ctype.expand_root temp_env self in
|
|
|
|
(* Final class type *)
|
|
let cl_sig =
|
|
{ cty_params = params;
|
|
cty_args = args;
|
|
cty_vars = vars;
|
|
cty_self = exp_self;
|
|
cty_concr = cl_sig.cty_concr;
|
|
cty_new = cl_sig.cty_new } (* new is still monomorphic *)
|
|
in let new_env = Env.add_class id cl_sig env in
|
|
((cl, id, cl_id, obj_id, cl_sig, cl_imp), new_env)
|
|
|
|
let generalize_class (_, _, _, _, cl_sig, _) =
|
|
List.iter Ctype.generalize cl_sig.cty_params;
|
|
List.iter Ctype.generalize cl_sig.cty_args;
|
|
Vars.iter (fun l (m, t) -> Ctype.generalize t) cl_sig.cty_vars;
|
|
Ctype.generalize cl_sig.cty_self;
|
|
match cl_sig.cty_new with Some ty -> Ctype.generalize ty | None -> ()
|
|
|
|
let make_abbrev env
|
|
(cl, id, cl_id, obj_id, cl_sig, cl_imp)
|
|
=
|
|
(* Class type abbreviation *)
|
|
Ctype.begin_def ();
|
|
let (params, self) =
|
|
Ctype.instance_parameterized_type cl_sig.cty_params cl_sig.cty_self
|
|
in let (cl_ty, cstr) = Ctype.prune self params
|
|
in let cl_ty_params = List.map fst cstr in
|
|
Ctype.end_def ();
|
|
Ctype.generalize cl_ty;
|
|
List.iter Ctype.generalize cl_ty_params;
|
|
let cl_abbrev =
|
|
{ type_params = cl_ty_params;
|
|
type_arity = List.length cl_ty_params;
|
|
type_kind = Type_abstract;
|
|
type_manifest = Some
|
|
(if cl.pcl_closed = Closed then
|
|
Ctype.newgenty (Tconstr(Path.Pident obj_id, cl_ty_params, ref []))
|
|
else begin
|
|
Ctype.set_object_name cl_ty cl_ty_params obj_id;
|
|
cl_ty
|
|
end) }
|
|
in let new_env = Env.add_type cl_id cl_abbrev env in
|
|
|
|
(* Object type abbreviation *)
|
|
Ctype.begin_def ();
|
|
let (obj_ty_params, obj_ty) =
|
|
Ctype.instance_parameterized_type cl_ty_params cl_ty
|
|
in
|
|
Ctype.close_object obj_ty;
|
|
Ctype.end_def ();
|
|
List.iter Ctype.generalize obj_ty_params;
|
|
begin match Ctype.closed_schema_verbose obj_ty with
|
|
None -> ()
|
|
| Some v ->
|
|
raise(Error(cl.pcl_loc,
|
|
Non_closed(obj_id, obj_ty_params, obj_ty, v)))
|
|
end;
|
|
Ctype.generalize obj_ty;
|
|
let obj_abbrev =
|
|
{ type_params = obj_ty_params;
|
|
type_arity = List.length obj_ty_params;
|
|
type_kind = Type_abstract;
|
|
type_manifest = Some (Ctype.unroll_abbrev obj_id obj_ty_params obj_ty) }
|
|
in let new_env = Env.add_type obj_id obj_abbrev new_env in
|
|
|
|
((id, cl_sig, cl_id, cl_abbrev, obj_id, obj_abbrev, cl_imp), new_env)
|
|
|
|
let rec iter f env =
|
|
function
|
|
[] ->
|
|
([], env)
|
|
| cl :: cl_rem ->
|
|
let (cl', env') = f env cl in
|
|
let (cl_rem', env'') = iter f env' cl_rem in
|
|
(cl'::cl_rem', env'')
|
|
|
|
let transl_classes env cl =
|
|
Ctype.begin_def ();
|
|
let (info, temp_env) = iter make_stub env cl in
|
|
let (info, _) = iter (transl_class temp_env) env info in
|
|
let (info, new_env) = iter (build_new_type temp_env) env info in
|
|
Ctype.end_def ();
|
|
List.iter generalize_class info;
|
|
let (info, new_env) = iter make_abbrev new_env info in
|
|
(info, new_env)
|
|
|
|
|
|
(*----------------------------------------------------------------------*)
|
|
|
|
(* Represent object types as abstract types *)
|
|
let enter_class env cl =
|
|
let abstr_type =
|
|
{ type_params = [];
|
|
type_arity = List.length (fst cl.pcty_param);
|
|
type_kind = Type_abstract;
|
|
type_manifest = None }
|
|
in
|
|
let (obj_id, ext_env) = Env.enter_type cl.pcty_name abstr_type env in
|
|
((cl, obj_id), ext_env)
|
|
|
|
let insert_value env lab priv mut ty loc val_sig val_redef =
|
|
let val_sig' =
|
|
if priv = Private then
|
|
vals_remove lab val_sig
|
|
else
|
|
Vars.add lab (mut, ty) val_sig
|
|
in
|
|
try
|
|
let (mut', ty') = Vars.find lab val_sig in
|
|
check_mutable loc lab mut mut';
|
|
(val_sig', (lab, ty', ty, loc)::val_redef)
|
|
with Not_found ->
|
|
(val_sig', val_redef)
|
|
|
|
let change_value_status lab priv mut loc val_sig =
|
|
try
|
|
let (mut', ty') = Vars.find lab val_sig in
|
|
check_mutable loc lab mut mut';
|
|
if priv = Private then
|
|
vals_remove lab val_sig
|
|
else
|
|
Vars.add lab (mut, ty') val_sig
|
|
with Not_found ->
|
|
raise(Error(loc, Undefined_var lab))
|
|
|
|
let insert_meth env self lab ty loc meth_redef =
|
|
let ty0 = Ctype.filter_method env lab self in
|
|
if not
|
|
(List.exists (function (lab', _, _, _) -> lab = lab') meth_redef)
|
|
then
|
|
Ctype.unify env ty ty0;
|
|
(lab, ty0, ty, loc)::meth_redef
|
|
|
|
let rec type_meth env loc self methods ty =
|
|
match (Ctype.repr ty).desc with
|
|
Tfield (lab, ty, ty') ->
|
|
insert_meth env self lab ty loc (type_meth env loc self methods ty')
|
|
| _ ->
|
|
methods
|
|
|
|
let type_class_type_field env temp_env cl self
|
|
(val_sig, val_redef, meth_redef) =
|
|
function
|
|
Pctf_inher (name, params, loc) ->
|
|
let (path, cl_sig) =
|
|
try Env.lookup_class name env with Not_found ->
|
|
raise (Error (loc, Unbound_class name))
|
|
in
|
|
let (cstr, args, vals, super) = Ctype.prune_class_type cl_sig in
|
|
if (List.length params <> List.length cstr) then
|
|
raise(Error(loc, Parameter_arity_mismatch
|
|
(path, List.length cstr, List.length params)));
|
|
List.iter2
|
|
(fun t1 (t2, _) ->
|
|
Ctype.unify temp_env
|
|
(Typetexp.transl_simple_type temp_env false t1) t2)
|
|
params cstr;
|
|
let super = Ctype.expand_root temp_env super in
|
|
let (val_sig, val_redef) =
|
|
Vars.fold
|
|
(fun lab (mut, ty) (v_sig, v_redef) ->
|
|
insert_value temp_env lab Public mut ty loc v_sig v_redef)
|
|
vals
|
|
(val_sig, val_redef)
|
|
in
|
|
let meth_redef =
|
|
match super.desc with
|
|
Tobject (fi, _) ->
|
|
if not (Ctype.opened_object super) then
|
|
begin try
|
|
Ctype.unify temp_env self (Ctype.newobj (closed_scheme fi))
|
|
with Ctype.Unify _ ->
|
|
let lab = missing_method temp_env super self in
|
|
raise(Error(loc,
|
|
Closed_ancestor (cl.pcty_name, path, lab)))
|
|
end;
|
|
super.desc <- Tlink self;
|
|
type_meth temp_env loc self meth_redef fi
|
|
| _ ->
|
|
fatal_error "Typeclass.type_class_type_field"
|
|
in
|
|
(val_sig, val_redef, meth_redef)
|
|
|
|
| Pctf_val (lab, priv, mut, sty, loc) ->
|
|
begin match sty with
|
|
Some sty ->
|
|
let ty = transl_simple_type temp_env false sty in
|
|
let (val_sig, val_redef) =
|
|
insert_value temp_env lab priv mut ty loc val_sig val_redef
|
|
in
|
|
(val_sig, val_redef, meth_redef)
|
|
| None ->
|
|
(change_value_status lab priv mut loc val_sig, val_redef, meth_redef)
|
|
end
|
|
|
|
| Pctf_virt (lab, sty, loc) ->
|
|
let ty = transl_simple_type temp_env false sty in
|
|
(val_sig, val_redef, insert_meth temp_env self lab ty loc meth_redef)
|
|
|
|
| Pctf_meth (lab, sty, loc) ->
|
|
let ty = transl_simple_type temp_env false sty in
|
|
(val_sig, val_redef, insert_meth temp_env self lab ty loc meth_redef)
|
|
|
|
(* Build class and object types *)
|
|
let build_abbrevs temp_env env (cl, obj_id) =
|
|
reset_type_variables ();
|
|
Ctype.begin_def ();
|
|
|
|
(* Create self (class type) *)
|
|
let self = Ctype.newobj (Ctype.newvar ()) in
|
|
let concr = Ctype.newobj (Ctype.newvar ()) in
|
|
|
|
(* Find concrete methods and marks methods *)
|
|
let concr_meths =
|
|
List.fold_left
|
|
(function meths ->
|
|
function
|
|
Pctf_inher (nm, _, loc) ->
|
|
let (_, anc) =
|
|
try
|
|
Env.lookup_class nm env
|
|
with Not_found ->
|
|
raise(Error(loc, Unbound_class nm))
|
|
in
|
|
begin match (Ctype.expand_root env anc.cty_self).desc with
|
|
Tobject (ty, _) ->
|
|
add_methods env self concr anc.cty_concr ty;
|
|
Concr.union anc.cty_concr meths
|
|
| _ -> fatal_error "Typeclass.make_stub"
|
|
end
|
|
| Pctf_val _ ->
|
|
meths
|
|
| Pctf_virt (lab, _, _) ->
|
|
Ctype.filter_method env lab self;
|
|
meths
|
|
| Pctf_meth (lab, _, _) ->
|
|
Ctype.filter_method env lab self;
|
|
Ctype.filter_method env lab concr;
|
|
Concr.add lab meths)
|
|
Concr.empty cl.pcty_field
|
|
in
|
|
Ctype.close_object concr;
|
|
|
|
(* Introduce parameters *)
|
|
let params =
|
|
try
|
|
List.map (enter_type_variable true) (fst cl.pcty_param)
|
|
with Already_bound ->
|
|
raise(Error(snd cl.pcty_param, Repeated_parameter))
|
|
in
|
|
|
|
(* Bind self type variable *)
|
|
begin match cl.pcty_self with
|
|
Some v -> Ctype.unify temp_env self (enter_type_variable false v)
|
|
| None -> ()
|
|
end;
|
|
|
|
(* Translate argument types *)
|
|
let args = List.map (transl_simple_type temp_env false) cl.pcty_args in
|
|
|
|
(* Translate fields *)
|
|
let (val_sig, val_redef, meth_redef) =
|
|
List.fold_left (type_class_type_field env temp_env cl self)
|
|
(Vars.empty, [], [])
|
|
cl.pcty_field
|
|
in
|
|
|
|
(* Closeness of self *)
|
|
if cl.pcty_closed = Closed then
|
|
Ctype.close_object self;
|
|
if (cl.pcty_closed <> Closed) & not (Ctype.opened_object self) then
|
|
raise(Error(cl.pcty_loc, Closed_class cl.pcty_name));
|
|
|
|
(* Generalize class *)
|
|
Ctype.end_def ();
|
|
List.iter Ctype.generalize params;
|
|
List.iter Ctype.generalize args;
|
|
Vars.iter (fun _ (_, ty) -> Ctype.generalize ty) val_sig;
|
|
Ctype.generalize self;
|
|
Ctype.generalize concr;
|
|
List.iter
|
|
(function (_, ty, ty', _) -> Ctype.generalize ty; Ctype.generalize ty')
|
|
val_redef;
|
|
List.iter
|
|
(function (_, ty, ty', _) -> Ctype.generalize ty; Ctype.generalize ty')
|
|
meth_redef;
|
|
|
|
(* Object type abbreviation *)
|
|
Ctype.begin_def ();
|
|
let (obj_params, obj_ty) = Ctype.instance_parameterized_type params self in
|
|
Ctype.close_object obj_ty;
|
|
Ctype.end_def ();
|
|
List.iter Ctype.generalize obj_params;
|
|
begin match Ctype.closed_schema_verbose obj_ty with
|
|
None -> ()
|
|
| Some v ->
|
|
raise(Error(cl.pcty_loc,
|
|
Non_closed(obj_id, obj_params, obj_ty, v)))
|
|
end;
|
|
Ctype.generalize obj_ty;
|
|
let obj_abbrev =
|
|
{ type_params = obj_params;
|
|
type_arity = List.length obj_params;
|
|
type_kind = Type_abstract;
|
|
type_manifest = Some obj_ty }
|
|
in let ext_env = Env.add_type obj_id obj_abbrev env in
|
|
|
|
(* Class type abbreviation *)
|
|
let cl_abbrev =
|
|
{ type_params = List.map Ctype.repr params;
|
|
type_arity = List.length params;
|
|
type_kind = Type_abstract;
|
|
type_manifest = Some
|
|
(if cl.pcty_closed = Closed then
|
|
Ctype.newgenty (Tconstr(Path.Pident obj_id, params, ref []))
|
|
else begin
|
|
Ctype.set_object_name self params obj_id;
|
|
self
|
|
end) }
|
|
in
|
|
let (cl_id, ext_env) =
|
|
Env.enter_type ("#" ^ cl.pcty_name) cl_abbrev ext_env
|
|
in
|
|
|
|
(* Set class type name *)
|
|
|
|
(* Temporary class signature *)
|
|
let temp_cl_sig =
|
|
{ cty_params = params;
|
|
cty_args = args;
|
|
cty_vars = val_sig;
|
|
cty_self = self;
|
|
cty_concr = concr_meths;
|
|
cty_new = None }
|
|
in
|
|
|
|
((cl, cl_id, cl_abbrev, obj_id, obj_abbrev, obj_params, obj_ty, temp_cl_sig,
|
|
val_redef, meth_redef, concr),
|
|
ext_env)
|
|
|
|
let check_abbrev env
|
|
(cl, cl_id, cl_abbrev, obj_id, obj_abbrev, obj_params, obj_ty, temp_cl_sig,
|
|
val_redef, meth_redef, concr) =
|
|
begin try Ctype.correct_abbrev env obj_id obj_params obj_ty with
|
|
Ctype.Nonlinear_abbrev ->
|
|
raise(Error(cl.pcty_loc, Illdefined_class cl.pcty_name))
|
|
end;
|
|
env
|
|
|
|
let check_field_redef env error (lab, ty, ty', loc) =
|
|
if not (Ctype.equal env [] ty [] ty') then
|
|
raise(Error(loc, error lab ty ty'))
|
|
|
|
let build_class_type env
|
|
(cl, cl_id, cl_abbrev, obj_id, obj_abbrev, obj_params, obj_ty, temp_cl_sig,
|
|
val_redef, meth_redef, concr) =
|
|
|
|
(* Check variable and method redefining *)
|
|
List.iter
|
|
(check_field_redef env
|
|
(fun l t t' -> Variable_type_mismatch(l, [(t', t'); (t, t)])))
|
|
val_redef;
|
|
List.iter
|
|
(check_field_redef env
|
|
(fun l t t' -> Method_type_mismatch(l, [(t', t'); (t, t)])))
|
|
meth_redef;
|
|
|
|
(* Class type skeleton *)
|
|
reset_type_variables ();
|
|
Ctype.begin_def ();
|
|
let (params, args, vals, self) = Ctype.instance_class temp_cl_sig in
|
|
|
|
(* Re-introduce parameters and bind self type variable *)
|
|
List.iter2
|
|
(fun v ty -> Ctype.unify env (enter_type_variable true v) ty)
|
|
(fst cl.pcty_param) params;
|
|
begin match cl.pcty_self with
|
|
Some v -> Ctype.unify env (enter_type_variable false v) self
|
|
| None -> ()
|
|
end;
|
|
|
|
(* Add constraints *)
|
|
List.iter
|
|
(function (v, ty, loc) ->
|
|
try
|
|
Ctype.unify env
|
|
(type_variable loc v) (transl_simple_type env false ty)
|
|
with Ctype.Unify _ ->
|
|
raise(Error(loc, Unconsistent_constraint)))
|
|
cl.pcty_cstr;
|
|
|
|
Ctype.end_def ();
|
|
List.iter Ctype.generalize params;
|
|
List.iter Ctype.generalize args;
|
|
Vars.iter (fun l (m, t) -> Ctype.generalize t) vals;
|
|
Ctype.generalize self;
|
|
|
|
(* Class type *)
|
|
let cl_sig =
|
|
{ cty_params = params;
|
|
cty_args = args;
|
|
cty_vars = vals;
|
|
cty_self = self;
|
|
cty_concr = temp_cl_sig.cty_concr;
|
|
cty_new = None }
|
|
in let (id, new_env) = Env.enter_class cl.pcty_name cl_sig env in
|
|
|
|
(* Build type of [new cl] *)
|
|
cl_sig.cty_new <-
|
|
if cl.pcty_kind = Concrete then begin
|
|
let concr = Ctype.instance concr in
|
|
let temp_obj = Ctype.instance obj_ty in
|
|
begin try
|
|
Ctype.unify env concr temp_obj
|
|
with Ctype.Unify _ ->
|
|
let lab = missing_method env concr temp_obj in
|
|
raise(Error(cl.pcty_loc,
|
|
Virtual_class (cl.pcty_name, lab)))
|
|
end;
|
|
Ctype.begin_def ();
|
|
let (params, args, _, self) = Ctype.instance_class cl_sig in
|
|
let abbrev =
|
|
Ctype.newty (Tconstr (Path.Pident obj_id, params, ref []))
|
|
in
|
|
Ctype.unify env self abbrev;
|
|
let ty_new =
|
|
List.fold_right (fun arg ty -> Ctype.newty (Tarrow(arg, ty)))
|
|
args abbrev
|
|
in
|
|
Ctype.end_def ();
|
|
Ctype.generalize ty_new;
|
|
Some ty_new
|
|
end else
|
|
None;
|
|
|
|
((id, cl_sig, cl_id, cl_abbrev, obj_id, obj_abbrev), new_env)
|
|
|
|
let rec transl_class_types env class_type_list =
|
|
(* Type classes are first abstract *)
|
|
let (info, temp_env) = iter enter_class env class_type_list in
|
|
Ctype.init_def(Ident.current_time());
|
|
let (info, new_env) = iter (build_abbrevs temp_env) env info in
|
|
List.iter (check_abbrev new_env) info;
|
|
let (info, final_env) = iter build_class_type new_env info in
|
|
(info, final_env)
|
|
|
|
(* Error report *)
|
|
|
|
open Format
|
|
|
|
let report_error = function
|
|
Duplicate_method s ->
|
|
print_string "Two methods are named"; print_space ();
|
|
print_string s
|
|
| Duplicate_variable s ->
|
|
print_string "Two instance variables are named"; print_space ();
|
|
print_string s
|
|
| Duplicate_super_variable s ->
|
|
print_string "Two ancestors are named"; print_space ();
|
|
print_string s
|
|
| Repeated_parameter ->
|
|
print_string "A type parameter occurs several times"
|
|
| Virtual_class (cl, met) ->
|
|
print_string "The class"; print_space ();
|
|
print_string cl; print_space ();
|
|
print_string "should be virtual: its methods"; print_space ();
|
|
print_string met; print_space ();
|
|
print_string "is undefined"
|
|
| Closed_class cl ->
|
|
print_string "The class"; print_space ();
|
|
print_string cl; print_space ();
|
|
print_string "is closed, but not marked closed"
|
|
| Closed_ancestor (cl, anc, met) ->
|
|
print_string "The class"; print_space ();
|
|
print_string cl; print_space ();
|
|
print_string "inherits from the closed class"; print_space ();
|
|
Printtyp.path anc; print_space ();
|
|
print_string "which has no method"; print_space ();
|
|
print_string met
|
|
| Non_closed (id, args, typ, var) ->
|
|
open_box 0;
|
|
Printtyp.reset ();
|
|
Printtyp.mark_loops typ;
|
|
begin match var with
|
|
Ctype.Var v ->
|
|
print_string "The type variable"; print_space ();
|
|
Printtyp.type_expr v; print_space ();
|
|
print_string "is not bound in implicit type definition"
|
|
| _ ->
|
|
print_string "Unbound row variable in implicit type definition"
|
|
end;
|
|
print_break 1 2;
|
|
open_box 0;
|
|
Printtyp.type_expr (Ctype.newty (Tconstr(Path.Pident id, args, ref [])));
|
|
print_space (); print_string "="; print_space ();
|
|
Printtyp.type_expr typ;
|
|
close_box ();
|
|
close_box ();
|
|
print_space ();
|
|
print_string "It should be captured by a class type parameter"
|
|
| Mutable_var v ->
|
|
print_string "The variable"; print_space ();
|
|
print_string v; print_space ();
|
|
print_string "was mutable and is redefined as immutable"
|
|
| Undefined_var v ->
|
|
print_string "The variable"; print_space ();
|
|
print_string v; print_space ();
|
|
print_string "is undefined"
|
|
| Variable_type_mismatch (v, trace) ->
|
|
Printtyp.unification_error trace
|
|
(function () ->
|
|
print_string "The variable ";
|
|
print_string v; print_space ();
|
|
print_string "has type")
|
|
(function () ->
|
|
print_string "but is expected to have type")
|
|
| Method_type_mismatch (m, trace) ->
|
|
Printtyp.unification_error trace
|
|
(function () ->
|
|
print_string "The method ";
|
|
print_string m; print_space ();
|
|
print_string "has type")
|
|
(function () ->
|
|
print_string "but is expected to have type")
|
|
| Unconsistent_constraint ->
|
|
print_string "The class constraints are not consistent"
|
|
| Unbound_class cl ->
|
|
print_string "Unbound class"; print_space ();
|
|
Printtyp.longident cl
|
|
| Argument_type_mismatch trace ->
|
|
Printtyp.unification_error trace
|
|
(function () ->
|
|
print_string "This argument has type")
|
|
(function () ->
|
|
print_string "but is expected to have type")
|
|
| Abbrev_type_clash (abbrev, actual, expected) ->
|
|
open_box 0;
|
|
Printtyp.reset ();
|
|
Printtyp.mark_loops abbrev; Printtyp.mark_loops actual;
|
|
Printtyp.mark_loops expected;
|
|
print_string "The abbreviation"; print_space ();
|
|
Printtyp.type_expr abbrev; print_space ();
|
|
print_string "expands to type"; print_space ();
|
|
Printtyp.type_expr actual; print_space ();
|
|
print_string "but is used with type"; print_space ();
|
|
Printtyp.type_expr expected;
|
|
close_box ()
|
|
| Bad_parameters (id, params, cstrs) ->
|
|
open_box 0;
|
|
Printtyp.reset ();
|
|
Printtyp.mark_loops params; Printtyp.mark_loops cstrs;
|
|
print_string "The abbreviation"; print_space ();
|
|
Printtyp.ident id; print_space ();
|
|
print_string "is used with parameters"; print_space ();
|
|
Printtyp.type_expr params; print_space ();
|
|
print_string "wich are incompatible with constraints"; print_space ();
|
|
Printtyp.type_expr cstrs; print_space ();
|
|
close_box ()
|
|
| Illdefined_class s ->
|
|
print_string "The class "; print_string s;
|
|
print_string " is ill-defined"
|
|
| Parameter_mismatch trace ->
|
|
Printtyp.unification_error trace
|
|
(function () ->
|
|
print_string "The type parameter")
|
|
(function () ->
|
|
print_string "does not meet its constraint: it should be")
|
|
| Argument_arity_mismatch(p, expected, provided) ->
|
|
open_box 0;
|
|
print_string "The class "; Printtyp.path p;
|
|
print_space(); print_string "expects "; print_int expected;
|
|
print_string " argument(s),"; print_space();
|
|
print_string "but is here applied to "; print_int provided;
|
|
print_string " argument(s)";
|
|
close_box()
|
|
| Parameter_arity_mismatch(p, expected, provided) ->
|
|
open_box 0;
|
|
print_string "The class "; Printtyp.path p;
|
|
print_space(); print_string "expects "; print_int expected;
|
|
print_string " type parameter(s),"; print_space();
|
|
print_string "but is here applied to "; print_int provided;
|
|
print_string " type parameter(s)";
|
|
close_box()
|