Files
rust/src/boot/me/semant.ml
T

1993 lines
60 KiB
OCaml

open Common;;
type slots_table = (Ast.slot_key,node_id) Hashtbl.t
type items_table = (Ast.ident,node_id) Hashtbl.t
type block_slots_table = (node_id,slots_table) Hashtbl.t
type block_items_table = (node_id,items_table) Hashtbl.t
;;
type code = {
code_fixup: fixup;
code_quads: Il.quads;
code_vregs_and_spill: (int * fixup) option;
}
;;
type glue =
GLUE_activate
| GLUE_yield
| GLUE_exit_main_task
| GLUE_exit_task
| GLUE_mark of Ast.ty
| GLUE_drop of Ast.ty
| GLUE_free of Ast.ty
| GLUE_copy of Ast.ty (* One-level copy. *)
| GLUE_clone of Ast.ty (* Deep copy. *)
| GLUE_compare of Ast.ty
| GLUE_hash of Ast.ty
| GLUE_write of Ast.ty
| GLUE_read of Ast.ty
| GLUE_unwind
| GLUE_get_next_pc
| GLUE_mark_frame of node_id (* node is the frame *)
| GLUE_drop_frame of node_id (* node is the frame *)
| GLUE_reloc_frame of node_id (* node is the frame *)
| GLUE_fn_binding of node_id (* node is the 'bind' stmt *)
| GLUE_obj_drop of node_id (* node is the obj *)
| GLUE_loop_body of node_id (* node is the 'for each' body block *)
| GLUE_forward of (Ast.ident * Ast.ty_obj * Ast.ty_obj)
;;
type data =
DATA_str of string
| DATA_name of Ast.name
| DATA_tydesc of Ast.ty
| DATA_frame_glue_fns of node_id
| DATA_obj_vtbl of node_id
| DATA_forwarding_vtbl of (Ast.ty_obj * Ast.ty_obj)
| DATA_crate
;;
type defn =
DEFN_slot of Ast.slot
| DEFN_item of Ast.mod_item_decl
| DEFN_ty_param of Ast.ty_param
| DEFN_obj_fn of (node_id * Ast.fn)
| DEFN_obj_drop of node_id
| DEFN_loop_body of node_id
;;
type glue_code = (glue, code) Hashtbl.t;;
type item_code = (node_id, code) Hashtbl.t;;
type file_code = (node_id, item_code) Hashtbl.t;;
type data_frags = (data, (fixup * Asm.frag)) Hashtbl.t;;
let string_of_name (n:Ast.name) : string =
Fmt.fmt_to_str Ast.fmt_name n
;;
(* The only need for a carg is to uniquely identify a constraint-arg
* in a scope-independent fashion. So we just look up the node that's
* used as the base of any such arg and glue it on the front of the
* symbolic name.
*)
type constr_key_arg = Constr_arg_node of (node_id * Ast.carg_path)
| Constr_arg_lit of Ast.lit
type constr_key =
Constr_pred of (node_id * constr_key_arg array)
| Constr_init of node_id
type ctxt =
{ ctxt_sess: Session.sess;
ctxt_frame_args: (node_id,node_id list) Hashtbl.t;
ctxt_frame_blocks: (node_id,node_id list) Hashtbl.t;
ctxt_block_slots: block_slots_table;
ctxt_block_items: block_items_table;
ctxt_slot_is_arg: (node_id,unit) Hashtbl.t;
ctxt_slot_keys: (node_id,Ast.slot_key) Hashtbl.t;
ctxt_all_item_names: (node_id,Ast.name) Hashtbl.t;
ctxt_all_item_types: (node_id,Ast.ty) Hashtbl.t;
ctxt_all_lval_types: (node_id,Ast.ty) Hashtbl.t;
ctxt_all_cast_types: (node_id,Ast.ty) Hashtbl.t;
ctxt_all_type_items: (node_id,Ast.ty) Hashtbl.t;
ctxt_all_stmts: (node_id,Ast.stmt) Hashtbl.t;
ctxt_item_files: (node_id,filename) Hashtbl.t;
ctxt_all_lvals: (node_id,Ast.lval) Hashtbl.t;
(* definition id --> definition *)
ctxt_all_defns: (node_id,defn) Hashtbl.t;
(* reference id --> definition id *)
ctxt_lval_to_referent: (node_id,node_id) Hashtbl.t;
ctxt_pattag_to_item: (node_id,node_id) Hashtbl.t;
ctxt_required_items: (node_id, (required_lib * nabi_conv)) Hashtbl.t;
ctxt_required_syms: (node_id, string) Hashtbl.t;
(* Layout-y stuff. *)
ctxt_slot_aliased: (node_id,unit) Hashtbl.t;
ctxt_slot_is_obj_state: (node_id,unit) Hashtbl.t;
ctxt_slot_vregs: (node_id,((int option) ref)) Hashtbl.t;
ctxt_slot_offsets: (node_id,size) Hashtbl.t;
ctxt_frame_sizes: (node_id,size) Hashtbl.t;
ctxt_call_sizes: (node_id,size) Hashtbl.t;
ctxt_block_is_loop_body: (node_id,unit) Hashtbl.t;
ctxt_stmt_loop_depths: (node_id,int) Hashtbl.t;
ctxt_slot_loop_depths: (node_id,int) Hashtbl.t;
(* Typestate-y stuff. *)
ctxt_constrs: (constr_id,constr_key) Hashtbl.t;
ctxt_constr_ids: (constr_key,constr_id) Hashtbl.t;
ctxt_preconditions: (node_id,Bits.t) Hashtbl.t;
ctxt_postconditions: (node_id,Bits.t) Hashtbl.t;
ctxt_prestates: (node_id,Bits.t) Hashtbl.t;
ctxt_poststates: (node_id,Bits.t) Hashtbl.t;
ctxt_call_lval_params: (node_id,Ast.ty array) Hashtbl.t;
ctxt_copy_stmt_is_init: (node_id,unit) Hashtbl.t;
ctxt_post_stmt_slot_drops: (node_id,node_id list) Hashtbl.t;
(* Translation-y stuff. *)
ctxt_fn_fixups: (node_id,fixup) Hashtbl.t;
ctxt_block_fixups: (node_id,fixup) Hashtbl.t;
ctxt_file_fixups: (node_id,fixup) Hashtbl.t;
ctxt_spill_fixups: (node_id,fixup) Hashtbl.t;
ctxt_abi: Abi.abi;
ctxt_activate_fixup: fixup;
ctxt_yield_fixup: fixup;
ctxt_unwind_fixup: fixup;
ctxt_exit_task_fixup: fixup;
ctxt_debug_aranges_fixup: fixup;
ctxt_debug_pubnames_fixup: fixup;
ctxt_debug_info_fixup: fixup;
ctxt_debug_abbrev_fixup: fixup;
ctxt_debug_line_fixup: fixup;
ctxt_debug_frame_fixup: fixup;
ctxt_image_base_fixup: fixup;
ctxt_crate_fixup: fixup;
ctxt_file_code: file_code;
ctxt_all_item_code: item_code;
ctxt_glue_code: glue_code;
ctxt_data: data_frags;
ctxt_native_required:
(required_lib,((string,fixup) Hashtbl.t)) Hashtbl.t;
ctxt_native_provided:
(segment,((string, fixup) Hashtbl.t)) Hashtbl.t;
ctxt_required_rust_sym_num: (node_id, int) Hashtbl.t;
ctxt_required_c_sym_num: ((required_lib * string), int) Hashtbl.t;
ctxt_required_lib_num: (required_lib, int) Hashtbl.t;
ctxt_main_fn_fixup: fixup option;
ctxt_main_name: string option;
}
;;
let new_ctxt sess abi crate =
{ ctxt_sess = sess;
ctxt_frame_args = Hashtbl.create 0;
ctxt_frame_blocks = Hashtbl.create 0;
ctxt_block_slots = Hashtbl.create 0;
ctxt_block_items = Hashtbl.create 0;
ctxt_slot_is_arg = Hashtbl.create 0;
ctxt_slot_keys = Hashtbl.create 0;
ctxt_all_item_names = Hashtbl.create 0;
ctxt_all_item_types = Hashtbl.create 0;
ctxt_all_lval_types = Hashtbl.create 0;
ctxt_all_cast_types = Hashtbl.create 0;
ctxt_all_type_items = Hashtbl.create 0;
ctxt_all_stmts = Hashtbl.create 0;
ctxt_item_files = crate.Ast.crate_files;
ctxt_all_lvals = Hashtbl.create 0;
ctxt_all_defns = Hashtbl.create 0;
ctxt_lval_to_referent = Hashtbl.create 0;
ctxt_pattag_to_item = Hashtbl.create 0;
ctxt_required_items = crate.Ast.crate_required;
ctxt_required_syms = crate.Ast.crate_required_syms;
ctxt_constrs = Hashtbl.create 0;
ctxt_constr_ids = Hashtbl.create 0;
ctxt_preconditions = Hashtbl.create 0;
ctxt_postconditions = Hashtbl.create 0;
ctxt_prestates = Hashtbl.create 0;
ctxt_poststates = Hashtbl.create 0;
ctxt_copy_stmt_is_init = Hashtbl.create 0;
ctxt_post_stmt_slot_drops = Hashtbl.create 0;
ctxt_call_lval_params = Hashtbl.create 0;
ctxt_slot_aliased = Hashtbl.create 0;
ctxt_slot_is_obj_state = Hashtbl.create 0;
ctxt_slot_vregs = Hashtbl.create 0;
ctxt_slot_offsets = Hashtbl.create 0;
ctxt_frame_sizes = Hashtbl.create 0;
ctxt_call_sizes = Hashtbl.create 0;
ctxt_block_is_loop_body = Hashtbl.create 0;
ctxt_slot_loop_depths = Hashtbl.create 0;
ctxt_stmt_loop_depths = Hashtbl.create 0;
ctxt_fn_fixups = Hashtbl.create 0;
ctxt_block_fixups = Hashtbl.create 0;
ctxt_file_fixups = Hashtbl.create 0;
ctxt_spill_fixups = Hashtbl.create 0;
ctxt_abi = abi;
ctxt_activate_fixup = new_fixup "activate glue";
ctxt_yield_fixup = new_fixup "yield glue";
ctxt_unwind_fixup = new_fixup "unwind glue";
ctxt_exit_task_fixup = new_fixup "exit-task glue";
ctxt_debug_aranges_fixup = new_fixup "debug_aranges section";
ctxt_debug_pubnames_fixup = new_fixup "debug_pubnames section";
ctxt_debug_info_fixup = new_fixup "debug_info section";
ctxt_debug_abbrev_fixup = new_fixup "debug_abbrev section";
ctxt_debug_line_fixup = new_fixup "debug_line section";
ctxt_debug_frame_fixup = new_fixup "debug_frame section";
ctxt_image_base_fixup = new_fixup "loaded image base";
ctxt_crate_fixup = new_fixup "root crate structure";
ctxt_file_code = Hashtbl.create 0;
ctxt_all_item_code = Hashtbl.create 0;
ctxt_glue_code = Hashtbl.create 0;
ctxt_data = Hashtbl.create 0;
ctxt_native_required = Hashtbl.create 0;
ctxt_native_provided = Hashtbl.create 0;
ctxt_required_rust_sym_num = Hashtbl.create 0;
ctxt_required_c_sym_num = Hashtbl.create 0;
ctxt_required_lib_num = Hashtbl.create 0;
ctxt_main_fn_fixup =
(match crate.Ast.crate_main with
None -> None
| Some n -> Some (new_fixup (string_of_name n)));
ctxt_main_name =
(match crate.Ast.crate_main with
None -> None
| Some n -> Some (string_of_name n));
}
;;
let report_err cx ido str =
let sess = cx.ctxt_sess in
let spano = match ido with
None -> None
| Some id -> (Session.get_span sess id)
in
match spano with
None ->
Session.fail sess "Error: %s\n%!" str
| Some span ->
Session.fail sess "%s:E:Error: %s\n%!"
(Session.string_of_span span) str
;;
let bugi (cx:ctxt) (i:node_id) =
let k s =
report_err cx (Some i) s;
failwith s
in Printf.ksprintf k
;;
(* Convenience accessors. *)
(* resolve an lval reference id to the id of its definition *)
let lval_to_referent (cx:ctxt) (id:node_id) : node_id =
if Hashtbl.mem cx.ctxt_lval_to_referent id
then Hashtbl.find cx.ctxt_lval_to_referent id
else bug () "unresolved lval"
;;
(* resolve an lval reference id to its definition *)
let resolve_lval_id (cx:ctxt) (id:node_id) : defn =
Hashtbl.find cx.ctxt_all_defns (lval_to_referent cx id)
;;
let referent_is_slot (cx:ctxt) (id:node_id) : bool =
match Hashtbl.find cx.ctxt_all_defns id with
DEFN_slot _ -> true
| _ -> false
;;
let referent_is_item (cx:ctxt) (id:node_id) : bool =
match Hashtbl.find cx.ctxt_all_defns id with
DEFN_item _ -> true
| _ -> false
;;
(* coerce an lval definition id to a slot *)
let referent_to_slot (cx:ctxt) (id:node_id) : Ast.slot =
match Hashtbl.find cx.ctxt_all_defns id with
DEFN_slot slot -> slot
| _ -> bugi cx id "unknown slot"
;;
(* coerce an lval reference id to its definition slot *)
let lval_to_slot (cx:ctxt) (id:node_id) : Ast.slot =
match resolve_lval_id cx id with
DEFN_slot slot -> slot
| _ -> bugi cx id "unknown slot"
;;
let get_stmt_depth (cx:ctxt) (id:node_id) : int =
Hashtbl.find cx.ctxt_stmt_loop_depths id
;;
let get_slot_depth (cx:ctxt) (id:node_id) : int =
Hashtbl.find cx.ctxt_slot_loop_depths id
;;
let get_fn_fixup (cx:ctxt) (id:node_id) : fixup =
if Hashtbl.mem cx.ctxt_fn_fixups id
then Hashtbl.find cx.ctxt_fn_fixups id
else bugi cx id "fn without fixup"
;;
let get_framesz (cx:ctxt) (id:node_id) : size =
if Hashtbl.mem cx.ctxt_frame_sizes id
then Hashtbl.find cx.ctxt_frame_sizes id
else bugi cx id "missing framesz"
;;
let get_callsz (cx:ctxt) (id:node_id) : size =
if Hashtbl.mem cx.ctxt_call_sizes id
then Hashtbl.find cx.ctxt_call_sizes id
else bugi cx id "missing callsz"
;;
let rec n_item_ty_params (cx:ctxt) (id:node_id) : int =
match Hashtbl.find cx.ctxt_all_defns id with
DEFN_item i -> Array.length i.Ast.decl_params
| DEFN_obj_fn (oid,_) -> n_item_ty_params cx oid
| DEFN_obj_drop oid -> n_item_ty_params cx oid
| DEFN_loop_body fid -> n_item_ty_params cx fid
| _ -> bugi cx id "n_item_ty_params on non-item"
;;
let item_is_obj_fn (cx:ctxt) (id:node_id) : bool =
match Hashtbl.find cx.ctxt_all_defns id with
DEFN_obj_fn _
| DEFN_obj_drop _ -> true
| _ -> false
;;
let get_spill (cx:ctxt) (id:node_id) : fixup =
if Hashtbl.mem cx.ctxt_spill_fixups id
then Hashtbl.find cx.ctxt_spill_fixups id
else bugi cx id "missing spill fixup"
;;
let require_native (cx:ctxt) (lib:required_lib) (name:string) : fixup =
let lib_tab = (htab_search_or_add cx.ctxt_native_required lib
(fun _ -> Hashtbl.create 0))
in
htab_search_or_add lib_tab name
(fun _ -> new_fixup ("require: " ^ name))
;;
let provide_native (cx:ctxt) (seg:segment) (name:string) : fixup =
let seg_tab = (htab_search_or_add cx.ctxt_native_provided seg
(fun _ -> Hashtbl.create 0))
in
htab_search_or_add seg_tab name
(fun _ -> new_fixup ("provide: " ^ name))
;;
let provide_existing_native
(cx:ctxt)
(seg:segment)
(name:string)
(fix:fixup)
: unit =
let seg_tab = (htab_search_or_add cx.ctxt_native_provided seg
(fun _ -> Hashtbl.create 0))
in
htab_put seg_tab name fix
;;
let slot_ty (s:Ast.slot) : Ast.ty =
match s.Ast.slot_ty with
Some t -> t
| None -> bug () "untyped slot"
;;
let fn_output_ty (fn_ty:Ast.ty) : Ast.ty =
match fn_ty with
Ast.TY_fn ({ Ast.sig_output_slot = slot }, _) ->
begin
match slot.Ast.slot_ty with
Some ty -> ty
| None -> bug () "function has untyped output slot"
end
| _ -> bug () "fn_output_ty on non-TY_fn"
;;
let tag_or_iso_ty_tup_by_name (ty:Ast.ty) (name:Ast.name) : Ast.ty_tup =
match ty with
Ast.TY_tag tags ->
Hashtbl.find tags name
| Ast.TY_iso { Ast.iso_index = i; Ast.iso_group = gp } ->
Hashtbl.find gp.(i) name
| _ ->
bug () "tag_or_iso_ty_tup_by_name called with non-tag or -iso type"
;;
let defn_is_slot (d:defn) : bool =
match d with
DEFN_slot _ -> true
| _ -> false
;;
let defn_is_item (d:defn) : bool =
match d with
DEFN_item _ -> true
| _ -> false
;;
let slot_is_obj_state (cx:ctxt) (sid:node_id) : bool =
Hashtbl.mem cx.ctxt_slot_is_obj_state sid
;;
(* determines whether d defines a statically-known value *)
let defn_is_static (d:defn) : bool =
not (defn_is_slot d)
;;
let defn_is_callable (d:defn) : bool =
match d with
DEFN_slot { Ast.slot_ty = Some Ast.TY_fn _ }
| DEFN_item { Ast.decl_item = (Ast.MOD_ITEM_fn _ ) } -> true
| _ -> false
;;
(* Constraint manipulation. *)
let rec apply_names_to_carg_path
(names:(Ast.name_base option) array)
(cp:Ast.carg_path)
: Ast.carg_path =
match cp with
Ast.CARG_ext (Ast.CARG_base Ast.BASE_formal,
Ast.COMP_idx i) ->
begin
match names.(i) with
Some nb ->
Ast.CARG_base (Ast.BASE_named nb)
| None -> bug () "Indexing off non-named carg"
end
| Ast.CARG_ext (cp', e) ->
Ast.CARG_ext (apply_names_to_carg_path names cp', e)
| _ -> cp
;;
let apply_names_to_carg
(names:(Ast.name_base option) array)
(carg:Ast.carg)
: Ast.carg =
match carg with
Ast.CARG_path cp ->
Ast.CARG_path (apply_names_to_carg_path names cp)
| Ast.CARG_lit _ -> carg
;;
let apply_names_to_constr
(names:(Ast.name_base option) array)
(constr:Ast.constr)
: Ast.constr =
{ constr with
Ast.constr_args =
Array.map (apply_names_to_carg names) constr.Ast.constr_args }
;;
let atoms_to_names (atoms:Ast.atom array)
: (Ast.name_base option) array =
Array.map
begin
fun atom ->
match atom with
Ast.ATOM_lval (Ast.LVAL_base nbi) -> Some nbi.node
| _ -> None
end
atoms
;;
let rec lval_base_id (lv:Ast.lval) : node_id =
match lv with
Ast.LVAL_base nbi -> nbi.id
| Ast.LVAL_ext (lv, _) -> lval_base_id lv
;;
let rec lval_base_slot (cx:ctxt) (lv:Ast.lval) : node_id option =
match lv with
Ast.LVAL_base nbi ->
let referent = lval_to_referent cx nbi.id in
if referent_is_slot cx referent
then Some referent
else None
| Ast.LVAL_ext (lv, _) -> lval_base_slot cx lv
;;
let rec lval_slots (cx:ctxt) (lv:Ast.lval) : node_id array =
match lv with
Ast.LVAL_base nbi ->
let referent = lval_to_referent cx nbi.id in
if referent_is_slot cx referent
then [| referent |]
else [| |]
| Ast.LVAL_ext (lv, Ast.COMP_named _) -> lval_slots cx lv
| Ast.LVAL_ext (lv, Ast.COMP_atom a) ->
Array.append (lval_slots cx lv) (atom_slots cx a)
and atom_slots (cx:ctxt) (a:Ast.atom) : node_id array =
match a with
Ast.ATOM_literal _ -> [| |]
| Ast.ATOM_lval lv -> lval_slots cx lv
;;
let lval_option_slots (cx:ctxt) (lv:Ast.lval option) : node_id array =
match lv with
None -> [| |]
| Some lv -> lval_slots cx lv
;;
let resolve_lval (cx:ctxt) (lv:Ast.lval) : defn =
resolve_lval_id cx (lval_base_id lv)
;;
let atoms_slots (cx:ctxt) (az:Ast.atom array) : node_id array =
Array.concat (List.map (atom_slots cx) (Array.to_list az))
;;
let tup_inputs_slots (cx:ctxt) (az:Ast.tup_input array) : node_id array =
Array.concat (List.map
(fun (_,_,a) -> atom_slots cx a)
(Array.to_list az))
;;
let rec_inputs_slots (cx:ctxt)
(inputs:Ast.rec_input array) : node_id array =
Array.concat (List.map
(fun (_, _, _, atom) -> atom_slots cx atom)
(Array.to_list inputs))
;;
let expr_slots (cx:ctxt) (e:Ast.expr) : node_id array =
match e with
Ast.EXPR_binary (_, a, b) ->
Array.append (atom_slots cx a) (atom_slots cx b)
| Ast.EXPR_unary (_, u) -> atom_slots cx u
| Ast.EXPR_atom a -> atom_slots cx a
;;
(* Type extraction. *)
let interior_slot_full mut ty : Ast.slot =
{ Ast.slot_mode = Ast.MODE_interior;
Ast.slot_mutable = mut;
Ast.slot_ty = Some ty }
;;
let exterior_slot_full mut ty : Ast.slot =
{ Ast.slot_mode = Ast.MODE_exterior;
Ast.slot_mutable = mut;
Ast.slot_ty = Some ty }
;;
let interior_slot ty : Ast.slot = interior_slot_full false ty
;;
let exterior_slot ty : Ast.slot = exterior_slot_full false ty
;;
(* General folds of Ast.ty. *)
type ('ty, 'slot, 'slots, 'tag) ty_fold =
{
(* Functions that correspond to interior nodes in Ast.ty. *)
ty_fold_slot : (Ast.mode * bool * 'ty) -> 'slot;
ty_fold_slots : ('slot array) -> 'slots;
ty_fold_tags : (Ast.name, 'slots) Hashtbl.t -> 'tag;
(* Functions that correspond to the Ast.ty constructors. *)
ty_fold_any: unit -> 'ty;
ty_fold_nil : unit -> 'ty;
ty_fold_bool : unit -> 'ty;
ty_fold_mach : ty_mach -> 'ty;
ty_fold_int : unit -> 'ty;
ty_fold_uint : unit -> 'ty;
ty_fold_char : unit -> 'ty;
ty_fold_str : unit -> 'ty;
ty_fold_tup : 'slots -> 'ty;
ty_fold_vec : 'slot -> 'ty;
ty_fold_rec : (Ast.ident * 'slot) array -> 'ty;
ty_fold_tag : 'tag -> 'ty;
ty_fold_iso : (int * 'tag array) -> 'ty;
ty_fold_idx : int -> 'ty;
ty_fold_fn : (('slots * Ast.constrs * 'slot) * Ast.ty_fn_aux) -> 'ty;
ty_fold_obj : (Ast.effect
* (Ast.ident, (('slots * Ast.constrs * 'slot) *
Ast.ty_fn_aux)) Hashtbl.t) -> 'ty;
ty_fold_chan : 'ty -> 'ty;
ty_fold_port : 'ty -> 'ty;
ty_fold_task : unit -> 'ty;
ty_fold_native : opaque_id -> 'ty;
ty_fold_param : (int * Ast.effect) -> 'ty;
ty_fold_named : Ast.name -> 'ty;
ty_fold_type : unit -> 'ty;
ty_fold_constrained : ('ty * Ast.constrs) -> 'ty }
;;
let rec fold_ty (f:('ty, 'slot, 'slots, 'tag) ty_fold) (ty:Ast.ty) : 'ty =
let fold_slot (s:Ast.slot) : 'slot =
f.ty_fold_slot (s.Ast.slot_mode,
s.Ast.slot_mutable,
fold_ty f (slot_ty s))
in
let fold_slots (slots:Ast.slot array) : 'slots =
f.ty_fold_slots (Array.map fold_slot slots)
in
let fold_tags (ttag:Ast.ty_tag) : 'tag =
f.ty_fold_tags (htab_map ttag (fun k v -> (k, fold_slots v)))
in
let fold_sig tsig =
(fold_slots tsig.Ast.sig_input_slots,
tsig.Ast.sig_input_constrs,
fold_slot tsig.Ast.sig_output_slot)
in
let fold_obj fns =
htab_map fns (fun i (tsig, taux) -> (i, (fold_sig tsig, taux)))
in
match ty with
Ast.TY_any -> f.ty_fold_any ()
| Ast.TY_nil -> f.ty_fold_nil ()
| Ast.TY_bool -> f.ty_fold_bool ()
| Ast.TY_mach m -> f.ty_fold_mach m
| Ast.TY_int -> f.ty_fold_int ()
| Ast.TY_uint -> f.ty_fold_uint ()
| Ast.TY_char -> f.ty_fold_char ()
| Ast.TY_str -> f.ty_fold_str ()
| Ast.TY_tup t -> f.ty_fold_tup (fold_slots t)
| Ast.TY_vec s -> f.ty_fold_vec (fold_slot s)
| Ast.TY_rec r -> f.ty_fold_rec (Array.map (fun (k,v) -> (k,fold_slot v)) r)
| Ast.TY_tag tt -> f.ty_fold_tag (fold_tags tt)
| Ast.TY_iso ti -> f.ty_fold_iso (ti.Ast.iso_index,
(Array.map fold_tags ti.Ast.iso_group))
| Ast.TY_idx i -> f.ty_fold_idx i
| Ast.TY_fn (tsig,taux) -> f.ty_fold_fn (fold_sig tsig, taux)
| Ast.TY_chan t -> f.ty_fold_chan (fold_ty f t)
| Ast.TY_port t -> f.ty_fold_port (fold_ty f t)
| Ast.TY_obj (eff,t) -> f.ty_fold_obj (eff, (fold_obj t))
| Ast.TY_task -> f.ty_fold_task ()
| Ast.TY_native x -> f.ty_fold_native x
| Ast.TY_param x -> f.ty_fold_param x
| Ast.TY_named n -> f.ty_fold_named n
| Ast.TY_type -> f.ty_fold_type ()
| Ast.TY_constrained (t, constrs) ->
f.ty_fold_constrained (fold_ty f t, constrs)
;;
type 'a simple_ty_fold = ('a, 'a, 'a, 'a) ty_fold
;;
let ty_fold_default (default:'a) : 'a simple_ty_fold =
{ ty_fold_slot = (fun _ -> default);
ty_fold_slots = (fun _ -> default);
ty_fold_tags = (fun _ -> default);
ty_fold_any = (fun _ -> default);
ty_fold_nil = (fun _ -> default);
ty_fold_bool = (fun _ -> default);
ty_fold_mach = (fun _ -> default);
ty_fold_int = (fun _ -> default);
ty_fold_uint = (fun _ -> default);
ty_fold_char = (fun _ -> default);
ty_fold_str = (fun _ -> default);
ty_fold_tup = (fun _ -> default);
ty_fold_vec = (fun _ -> default);
ty_fold_rec = (fun _ -> default);
ty_fold_tag = (fun _ -> default);
ty_fold_iso = (fun _ -> default);
ty_fold_idx = (fun _ -> default);
ty_fold_fn = (fun _ -> default);
ty_fold_obj = (fun _ -> default);
ty_fold_chan = (fun _ -> default);
ty_fold_port = (fun _ -> default);
ty_fold_task = (fun _ -> default);
ty_fold_native = (fun _ -> default);
ty_fold_param = (fun _ -> default);
ty_fold_named = (fun _ -> default);
ty_fold_type = (fun _ -> default);
ty_fold_constrained = (fun _ -> default) }
;;
let ty_fold_rebuild (id:Ast.ty -> Ast.ty)
: (Ast.ty, Ast.slot, Ast.slot array, Ast.ty_tag) ty_fold =
let rebuild_fn ((islots, constrs, oslot), aux) =
({ Ast.sig_input_slots = islots;
Ast.sig_input_constrs = constrs;
Ast.sig_output_slot = oslot }, aux)
in
{ ty_fold_slot = (fun (mode, mut, t) ->
{ Ast.slot_mode = mode;
Ast.slot_mutable = mut;
Ast.slot_ty = Some t });
ty_fold_slots = (fun slots -> slots);
ty_fold_tags = (fun htab -> htab);
ty_fold_any = (fun _ -> id Ast.TY_any);
ty_fold_nil = (fun _ -> id Ast.TY_nil);
ty_fold_bool = (fun _ -> id Ast.TY_bool);
ty_fold_mach = (fun m -> id (Ast.TY_mach m));
ty_fold_int = (fun _ -> id Ast.TY_int);
ty_fold_uint = (fun _ -> id Ast.TY_uint);
ty_fold_char = (fun _ -> id Ast.TY_char);
ty_fold_str = (fun _ -> id Ast.TY_str);
ty_fold_tup = (fun slots -> id (Ast.TY_tup slots));
ty_fold_vec = (fun slot -> id (Ast.TY_vec slot));
ty_fold_rec = (fun entries -> id (Ast.TY_rec entries));
ty_fold_tag = (fun tag -> id (Ast.TY_tag tag));
ty_fold_iso = (fun (i, tags) -> id (Ast.TY_iso { Ast.iso_index = i;
Ast.iso_group = tags }));
ty_fold_idx = (fun i -> id (Ast.TY_idx i));
ty_fold_fn = (fun t -> id (Ast.TY_fn (rebuild_fn t)));
ty_fold_obj = (fun (eff,fns) ->
id (Ast.TY_obj
(eff, (htab_map fns
(fun id fn -> (id, rebuild_fn fn))))));
ty_fold_chan = (fun t -> id (Ast.TY_chan t));
ty_fold_port = (fun t -> id (Ast.TY_port t));
ty_fold_task = (fun _ -> id Ast.TY_task);
ty_fold_native = (fun oid -> id (Ast.TY_native oid));
ty_fold_param = (fun (i, mut) -> id (Ast.TY_param (i, mut)));
ty_fold_named = (fun n -> id (Ast.TY_named n));
ty_fold_type = (fun _ -> id (Ast.TY_type));
ty_fold_constrained = (fun (t, constrs) ->
id (Ast.TY_constrained (t, constrs))) }
;;
let rebuild_ty_under_params
(ty:Ast.ty)
(params:Ast.ty_param array)
(args:Ast.ty array)
(resolve_names:bool)
: Ast.ty =
if (Array.length params) <> (Array.length args)
then err None "mismatched type-params"
else
let nmap = Hashtbl.create (Array.length args) in
let pmap = Hashtbl.create (Array.length args) in
let _ =
Array.iteri
begin
fun i (ident, param) ->
htab_put pmap (Ast.TY_param param) args.(i);
if resolve_names
then
htab_put nmap ident args.(i)
end
params
in
let substituted = ref false in
let rec rebuild_ty t =
let base = ty_fold_rebuild (fun t -> t) in
let ty_fold_param (i, mut) =
let param = Ast.TY_param (i, mut) in
match htab_search pmap param with
None -> param
| Some arg -> (substituted := true; arg)
in
let ty_fold_named n =
let rec rebuild_name n =
match n with
Ast.NAME_base nb ->
Ast.NAME_base (rebuild_name_base nb)
| Ast.NAME_ext (n, nc) ->
Ast.NAME_ext (rebuild_name n,
rebuild_name_component nc)
and rebuild_name_base nb =
match nb with
Ast.BASE_ident i ->
Ast.BASE_ident i
| Ast.BASE_temp t ->
Ast.BASE_temp t
| Ast.BASE_app (i, tys) ->
Ast.BASE_app (i, rebuild_tys tys)
and rebuild_name_component nc =
match nc with
Ast.COMP_ident i ->
Ast.COMP_ident i
| Ast.COMP_app (i, tys) ->
Ast.COMP_app (i, rebuild_tys tys)
| Ast.COMP_idx i ->
Ast.COMP_idx i
and rebuild_tys tys =
Array.map (fun t -> rebuild_ty t) tys
in
let n = rebuild_name n in
match n with
Ast.NAME_base (Ast.BASE_ident id)
when resolve_names ->
begin
match htab_search nmap id with
None -> Ast.TY_named n
| Some arg -> (substituted := true; arg)
end
| _ -> Ast.TY_named n
in
let fold =
{ base with
ty_fold_param = ty_fold_param;
ty_fold_named = ty_fold_named;
}
in
let t' = fold_ty fold t in
(*
* FIXME: "substituted" and "ty'" here are only required
* because the current type-equality-comparison code in Type
* uses <> and will judge some cases, such as rebuilt tags, as
* unequal simply due to the different hashtable order in the
* fold.
*)
if !substituted
then t'
else t
in
rebuild_ty ty
;;
let associative_binary_op_ty_fold
(default:'a)
(fn:'a -> 'a -> 'a)
: 'a simple_ty_fold =
let base = ty_fold_default default in
let reduce ls =
match ls with
[] -> default
| x::xs -> List.fold_left fn x xs
in
let reduce_fn ((islots, _, oslot), _) =
fn islots oslot
in
{ base with
ty_fold_slots = (fun slots -> reduce (Array.to_list slots));
ty_fold_slot = (fun (_, _, a) -> a);
ty_fold_tags = (fun tab -> reduce (htab_vals tab));
ty_fold_tup = (fun a -> a);
ty_fold_vec = (fun a -> a);
ty_fold_rec = (fun sz ->
reduce (Array.to_list
(Array.map (fun (_, s) -> s) sz)));
ty_fold_tag = (fun a -> a);
ty_fold_iso = (fun (_,iso) -> reduce (Array.to_list iso));
ty_fold_fn = reduce_fn;
ty_fold_obj = (fun (_,fns) ->
reduce (List.map reduce_fn (htab_vals fns)));
ty_fold_chan = (fun a -> a);
ty_fold_port = (fun a -> a);
ty_fold_constrained = (fun (a, _) -> a) }
let ty_fold_bool_and (default:bool) : bool simple_ty_fold =
associative_binary_op_ty_fold default (fun a b -> a & b)
;;
let ty_fold_bool_or (default:bool) : bool simple_ty_fold =
associative_binary_op_ty_fold default (fun a b -> a || b)
;;
let ty_fold_int_max (default:int) : int simple_ty_fold =
associative_binary_op_ty_fold default (fun a b -> max a b)
;;
let ty_fold_list_concat _ : ('a list) simple_ty_fold =
associative_binary_op_ty_fold [] (fun a b -> a @ b)
;;
let type_is_structured (t:Ast.ty) : bool =
let fold = ty_fold_bool_or false in
let fold = { fold with
ty_fold_tup = (fun _ -> true);
ty_fold_vec = (fun _ -> true);
ty_fold_rec = (fun _ -> true);
ty_fold_tag = (fun _ -> true);
ty_fold_iso = (fun _ -> true);
ty_fold_idx = (fun _ -> true);
ty_fold_fn = (fun _ -> true);
ty_fold_obj = (fun _ -> true) }
in
fold_ty fold t
;;
(* Effect analysis. *)
let effect_le x y =
match (x,y) with
(Ast.UNSAFE, _) -> true
| (Ast.STATE, Ast.PURE) -> true
| (Ast.STATE, Ast.IO) -> true
| (Ast.STATE, Ast.STATE) -> true
| (Ast.IO, Ast.PURE) -> true
| (Ast.IO, Ast.IO) -> true
| (Ast.PURE, Ast.PURE) -> true
| _ -> false
;;
let lower_effect_of x y =
if effect_le x y then x else y
;;
let type_effect (t:Ast.ty) : Ast.effect =
let fold_slot ((*mode*)_, mut, eff) =
if mut
then lower_effect_of Ast.STATE eff
else eff
in
let fold = associative_binary_op_ty_fold Ast.PURE lower_effect_of in
let fold = { fold with ty_fold_slot = fold_slot } in
fold_ty fold t
;;
let type_has_state (t:Ast.ty) : bool =
effect_le (type_effect t) Ast.STATE
;;
(* Various type analyses. *)
let is_prim_type (t:Ast.ty) : bool =
match t with
Ast.TY_int
| Ast.TY_uint
| Ast.TY_char
| Ast.TY_mach _
| Ast.TY_bool -> true
| _ -> false
;;
let type_contains_chan (t:Ast.ty) : bool =
let fold_chan _ = true in
let fold = ty_fold_bool_or false in
let fold = { fold with ty_fold_chan = fold_chan } in
fold_ty fold t
;;
let type_is_unsigned_2s_complement t =
match t with
Ast.TY_mach TY_u8
| Ast.TY_mach TY_u16
| Ast.TY_mach TY_u32
| Ast.TY_mach TY_u64
| Ast.TY_char
| Ast.TY_uint
| Ast.TY_bool -> true
| _ -> false
;;
let type_is_signed_2s_complement t =
match t with
Ast.TY_mach TY_i8
| Ast.TY_mach TY_i16
| Ast.TY_mach TY_i32
| Ast.TY_mach TY_i64
| Ast.TY_int -> true
| _ -> false
;;
let type_is_2s_complement t =
(type_is_unsigned_2s_complement t)
|| (type_is_signed_2s_complement t)
;;
let n_used_type_params t =
let fold_param (i,_) = i+1 in
let fold = ty_fold_int_max 0 in
let fold = { fold with ty_fold_param = fold_param } in
fold_ty fold t
;;
let check_concrete params thing =
if Array.length params = 0
then thing
else bug () "unhandled parametric binding"
;;
let project_type_to_slot
(base_ty:Ast.ty)
(comp:Ast.lval_component)
: Ast.slot =
match (base_ty, comp) with
(Ast.TY_rec elts, Ast.COMP_named (Ast.COMP_ident id)) ->
begin
match atab_search elts id with
Some slot -> slot
| None -> err None "unknown record-member '%s'" id
end
| (Ast.TY_tup elts, Ast.COMP_named (Ast.COMP_idx i)) ->
if 0 <= i && i < (Array.length elts)
then elts.(i)
else err None "out-of-range tuple index %d" i
| (Ast.TY_vec slot, Ast.COMP_atom _) ->
slot
| (Ast.TY_str, Ast.COMP_atom _) ->
interior_slot (Ast.TY_mach TY_u8)
| (Ast.TY_obj (_, fns), Ast.COMP_named (Ast.COMP_ident id)) ->
interior_slot (Ast.TY_fn (Hashtbl.find fns id))
| (_,_) ->
bug ()
"unhandled form of lval-ext in Semant."
"project_slot: %a indexed by %a"
Ast.sprintf_ty base_ty Ast.sprintf_lval_component comp
;;
(* NB: this will fail if lval is not a slot. *)
let rec lval_slot (cx:ctxt) (lval:Ast.lval) : Ast.slot =
match lval with
Ast.LVAL_base nb -> lval_to_slot cx nb.id
| Ast.LVAL_ext (base, comp) ->
let base_ty = slot_ty (lval_slot cx base) in
project_type_to_slot base_ty comp
;;
let exports_permit (view:Ast.mod_view) (ident:Ast.ident) : bool =
(Hashtbl.mem view.Ast.view_exports Ast.EXPORT_all_decls) ||
(Hashtbl.mem view.Ast.view_exports (Ast.EXPORT_ident ident))
;;
(* NB: this will fail if lval is not an item. *)
let rec lval_item (cx:ctxt) (lval:Ast.lval) : Ast.mod_item =
match lval with
Ast.LVAL_base nb ->
begin
let referent = lval_to_referent cx nb.id in
match htab_search cx.ctxt_all_defns referent with
Some (DEFN_item item) -> {node=item; id=referent}
| _ -> err (Some (lval_base_id lval))
"lval does not name an item"
end
| Ast.LVAL_ext (base, comp) ->
let base_item = lval_item cx base in
match base_item.node.Ast.decl_item with
Ast.MOD_ITEM_mod (view, items) ->
begin
let i, args =
match comp with
Ast.COMP_named (Ast.COMP_ident i) -> (i, [||])
| Ast.COMP_named (Ast.COMP_app (i, args)) -> (i, args)
| _ ->
bug ()
"unhandled lval-component '%a' in Semant.lval_item"
Ast.sprintf_lval_component comp
in
match htab_search items i with
| Some sub when exports_permit view i ->
assert
((Array.length sub.node.Ast.decl_params) =
(Array.length args));
check_concrete base_item.node.Ast.decl_params sub
| _ -> err (Some (lval_base_id lval))
"unknown module item '%s'" i
end
| _ -> err (Some (lval_base_id lval))
"lval base %a does not name a module" Ast.sprintf_lval base
;;
let lval_is_slot (cx:ctxt) (lval:Ast.lval) : bool =
match resolve_lval cx lval with
DEFN_slot _ -> true
| _ -> false
;;
let lval_is_item (cx:ctxt) (lval:Ast.lval) : bool =
match resolve_lval cx lval with
DEFN_item _ -> true
| _ -> false
;;
let lval_is_direct_fn (cx:ctxt) (lval:Ast.lval) : bool =
let defn = resolve_lval cx lval in
(defn_is_static defn) && (defn_is_callable defn)
;;
let lval_is_direct_mod (cx:ctxt) (lval:Ast.lval) : bool =
let defn = resolve_lval cx lval in
if not (defn_is_static defn)
then false
else
match defn with
DEFN_item { Ast.decl_item = Ast.MOD_ITEM_mod _ } -> true
| _ -> false
;;
let lval_is_static (cx:ctxt) (lval:Ast.lval) : bool =
defn_is_static (resolve_lval cx lval)
;;
let lval_is_callable (cx:ctxt) (lval:Ast.lval) : bool =
defn_is_callable (resolve_lval cx lval)
;;
let lval_is_obj_vtbl (cx:ctxt) (lval:Ast.lval) : bool =
if lval_is_slot cx lval
then
match lval with
Ast.LVAL_ext (base, _) ->
begin
match slot_ty (lval_slot cx base) with
Ast.TY_obj _ -> true
| _ -> false
end
| _ -> false
else false
;;
let rec lval_ty (cx:ctxt) (lval:Ast.lval) : Ast.ty =
let base_id = lval_base_id lval in
Hashtbl.find cx.ctxt_all_lval_types base_id
;;
let rec atom_type (cx:ctxt) (at:Ast.atom) : Ast.ty =
match at with
Ast.ATOM_literal {node=(Ast.LIT_int _); id=_} -> Ast.TY_int
| Ast.ATOM_literal {node=(Ast.LIT_uint _); id=_} -> Ast.TY_uint
| Ast.ATOM_literal {node=(Ast.LIT_bool _); id=_} -> Ast.TY_bool
| Ast.ATOM_literal {node=(Ast.LIT_char _); id=_} -> Ast.TY_char
| Ast.ATOM_literal {node=(Ast.LIT_nil); id=_} -> Ast.TY_nil
| Ast.ATOM_literal {node=(Ast.LIT_mach (m,_,_)); id=_} -> Ast.TY_mach m
| Ast.ATOM_lval lv -> lval_ty cx lv
;;
let expr_type (cx:ctxt) (e:Ast.expr) : Ast.ty =
match e with
Ast.EXPR_binary (op, a, _) ->
begin
match op with
Ast.BINOP_eq | Ast.BINOP_ne | Ast.BINOP_lt | Ast.BINOP_le
| Ast.BINOP_ge | Ast.BINOP_gt -> Ast.TY_bool
| _ -> atom_type cx a
end
| Ast.EXPR_unary (Ast.UNOP_not, _) -> Ast.TY_bool
| Ast.EXPR_unary (_, a) -> atom_type cx a
| Ast.EXPR_atom a -> atom_type cx a
;;
(* Mappings between mod items and their respective types. *)
let arg_slots (slots:Ast.header_slots) : Ast.slot array =
Array.map (fun (sid,_) -> sid.node) slots
;;
let tup_slots (slots:Ast.header_tup) : Ast.slot array =
Array.map (fun sid -> sid.node) slots
;;
let ty_fn_of_fn (fn:Ast.fn) : Ast.ty_fn =
({ Ast.sig_input_slots = arg_slots fn.Ast.fn_input_slots;
Ast.sig_input_constrs = fn.Ast.fn_input_constrs;
Ast.sig_output_slot = fn.Ast.fn_output_slot.node },
fn.Ast.fn_aux )
;;
let ty_obj_of_obj (obj:Ast.obj) : Ast.ty_obj =
(obj.Ast.obj_effect,
htab_map obj.Ast.obj_fns (fun i f -> (i, ty_fn_of_fn f.node)))
;;
let ty_of_mod_item ((*inside*)_:bool) (item:Ast.mod_item) : Ast.ty =
match item.node.Ast.decl_item with
Ast.MOD_ITEM_type _ -> Ast.TY_type
| Ast.MOD_ITEM_fn f -> (Ast.TY_fn (ty_fn_of_fn f))
| Ast.MOD_ITEM_mod _ -> bug () "Semant.ty_of_mod_item on mod"
| Ast.MOD_ITEM_obj ob ->
let taux = { Ast.fn_effect = Ast.PURE;
Ast.fn_is_iter = false }
in
let tobj = Ast.TY_obj (ty_obj_of_obj ob) in
let tsig = { Ast.sig_input_slots = arg_slots ob.Ast.obj_state;
Ast.sig_input_constrs = ob.Ast.obj_constrs;
Ast.sig_output_slot = interior_slot tobj }
in
(Ast.TY_fn (tsig, taux))
| Ast.MOD_ITEM_tag (htup, ttag, _) ->
let taux = { Ast.fn_effect = Ast.PURE;
Ast.fn_is_iter = false }
in
let tsig = { Ast.sig_input_slots = tup_slots htup;
Ast.sig_input_constrs = [| |];
Ast.sig_output_slot = interior_slot (Ast.TY_tag ttag) }
in
(Ast.TY_fn (tsig, taux))
;;
(* Scopes and the visitor that builds them. *)
type scope =
SCOPE_block of node_id
| SCOPE_mod_item of Ast.mod_item
| SCOPE_obj_fn of (Ast.fn identified)
| SCOPE_crate of Ast.crate
;;
let id_of_scope (sco:scope) : node_id =
match sco with
SCOPE_block id -> id
| SCOPE_mod_item i -> i.id
| SCOPE_obj_fn f -> f.id
| SCOPE_crate c -> c.id
;;
let scope_stack_managing_visitor
(scopes:(scope list) ref)
(inner:Walk.visitor)
: Walk.visitor =
let push s =
scopes := s :: (!scopes)
in
let pop _ =
scopes := List.tl (!scopes)
in
let visit_block_pre b =
push (SCOPE_block b.id);
inner.Walk.visit_block_pre b
in
let visit_block_post b =
inner.Walk.visit_block_post b;
pop();
in
let visit_mod_item_pre n p i =
push (SCOPE_mod_item i);
inner.Walk.visit_mod_item_pre n p i
in
let visit_mod_item_post n p i =
inner.Walk.visit_mod_item_post n p i;
pop();
in
let visit_obj_fn_pre obj ident fn =
push (SCOPE_obj_fn fn);
inner.Walk.visit_obj_fn_pre obj ident fn
in
let visit_obj_fn_post obj ident fn =
inner.Walk.visit_obj_fn_post obj ident fn;
pop();
in
let visit_crate_pre c =
push (SCOPE_crate c);
inner.Walk.visit_crate_pre c
in
let visit_crate_post c =
inner.Walk.visit_crate_post c;
pop()
in
{ inner with
Walk.visit_block_pre = visit_block_pre;
Walk.visit_block_post = visit_block_post;
Walk.visit_mod_item_pre = visit_mod_item_pre;
Walk.visit_mod_item_post = visit_mod_item_post;
Walk.visit_obj_fn_pre = visit_obj_fn_pre;
Walk.visit_obj_fn_post = visit_obj_fn_post;
Walk.visit_crate_pre = visit_crate_pre;
Walk.visit_crate_post = visit_crate_post; }
;;
(* Generic lookup, used for slots, items, types, etc. *)
type resolved = ((scope list * node_id) option) ;;
let get_item (cx:ctxt) (node:node_id) : Ast.mod_item_decl =
match htab_search cx.ctxt_all_defns node with
Some (DEFN_item item) -> item
| Some _ -> err (Some node) "defn is not an item"
| None -> bug () "missing defn"
;;
let get_slot (cx:ctxt) (node:node_id) : Ast.slot =
match htab_search cx.ctxt_all_defns node with
Some (DEFN_slot slot) -> slot
| Some _ -> err (Some node) "defn is not a slot"
| None -> bug () "missing defn"
;;
let get_mod_item
(cx:ctxt)
(node:node_id)
: (Ast.mod_view * Ast.mod_items) =
match get_item cx node with
{ Ast.decl_item = Ast.MOD_ITEM_mod md } -> md
| _ -> err (Some node) "defn is not a mod"
;;
let get_name_comp_ident
(comp:Ast.name_component)
: Ast.ident =
match comp with
Ast.COMP_ident i -> i
| Ast.COMP_app (i, _) -> i
| Ast.COMP_idx i -> string_of_int i
;;
let get_name_base_ident
(comp:Ast.name_base)
: Ast.ident =
match comp with
Ast.BASE_ident i -> i
| Ast.BASE_app (i, _) -> i
| Ast.BASE_temp _ ->
bug () "get_name_base_ident on BASE_temp"
;;
let rec project_ident_from_items
(cx:ctxt)
(scopes:scope list)
((view:Ast.mod_view),(items:Ast.mod_items))
(ident:Ast.ident)
(inside:bool)
: resolved =
if not (inside || (exports_permit view ident))
then None
else
match htab_search items ident with
Some i -> Some (scopes, i.id)
| None ->
match htab_search view.Ast.view_imports ident with
None -> None
| Some name -> lookup_by_name cx scopes name
and project_name_comp_from_resolved
(cx:ctxt)
(mod_res:resolved)
(ext:Ast.name_component)
: resolved =
match mod_res with
None -> None
| Some (scopes, id) ->
let scope = (SCOPE_mod_item {id=id; node=get_item cx id}) in
let scopes = scope :: scopes in
let ident = get_name_comp_ident ext in
let md = get_mod_item cx id in
project_ident_from_items cx scopes md ident false
and lookup_by_name
(cx:ctxt)
(scopes:scope list)
(name:Ast.name)
: resolved =
assert (Ast.sane_name name);
match name with
Ast.NAME_base nb ->
let ident = get_name_base_ident nb in
lookup_by_ident cx scopes ident
| Ast.NAME_ext (name, ext) ->
let base_res = lookup_by_name cx scopes name in
project_name_comp_from_resolved cx base_res ext
and lookup_by_ident
(cx:ctxt)
(scopes:scope list)
(ident:Ast.ident)
: resolved =
let check_slots scopes islots =
arr_search islots
(fun _ (sloti,ident') ->
if ident = ident'
then Some (scopes, sloti.id)
else None)
in
let check_params scopes params =
arr_search params
(fun _ {node=(i,_); id=id} ->
if i = ident then Some (scopes, id) else None)
in
let passed_capture_scope = ref false in
let would_capture r =
match r with
None -> None
| Some _ ->
if !passed_capture_scope
then err None "attempted dynamic environment-capture"
else r
in
let check_scope scopes scope =
match scope with
SCOPE_block block_id ->
let block_slots = Hashtbl.find cx.ctxt_block_slots block_id in
let block_items = Hashtbl.find cx.ctxt_block_items block_id in
begin
match htab_search block_slots (Ast.KEY_ident ident) with
Some id -> would_capture (Some (scopes, id))
| None ->
match htab_search block_items ident with
Some id -> Some (scopes, id)
| None -> None
end
| SCOPE_crate crate ->
project_ident_from_items
cx scopes crate.node.Ast.crate_items ident true
| SCOPE_obj_fn fn ->
would_capture (check_slots scopes fn.node.Ast.fn_input_slots)
| SCOPE_mod_item item ->
begin
let item_match =
match item.node.Ast.decl_item with
Ast.MOD_ITEM_fn f ->
check_slots scopes f.Ast.fn_input_slots
| Ast.MOD_ITEM_obj obj ->
begin
match htab_search obj.Ast.obj_fns ident with
Some fn -> Some (scopes, fn.id)
| None -> check_slots scopes obj.Ast.obj_state
end
| Ast.MOD_ITEM_mod md ->
project_ident_from_items cx scopes md ident true
| _ -> None
in
match item_match with
Some _ -> item_match
| None ->
would_capture
(check_params scopes item.node.Ast.decl_params)
end
in
let rec search scopes =
match scopes with
[] -> None
| scope::rest ->
match check_scope scopes scope with
None ->
begin
let is_ty_item i =
match i.node.Ast.decl_item with
Ast.MOD_ITEM_type _ -> true
| _ -> false
in
match scope with
SCOPE_block _
| SCOPE_obj_fn _ ->
search rest
| SCOPE_mod_item item when is_ty_item item ->
search rest
| _ ->
passed_capture_scope := true;
search rest
end
| x -> x
in
search scopes
;;
let lookup_by_temp
(cx:ctxt)
(scopes:scope list)
(temp:temp_id)
: ((scope list * node_id) option) =
let passed_item_scope = ref false in
let check_scope scope =
if !passed_item_scope
then None
else
match scope with
SCOPE_block block_id ->
let block_slots = Hashtbl.find cx.ctxt_block_slots block_id in
htab_search block_slots (Ast.KEY_temp temp)
| _ ->
passed_item_scope := true;
None
in
list_search_ctxt scopes check_scope
;;
let lookup
(cx:ctxt)
(scopes:scope list)
(key:Ast.slot_key)
: ((scope list * node_id) option) =
match key with
Ast.KEY_temp temp -> lookup_by_temp cx scopes temp
| Ast.KEY_ident ident -> lookup_by_ident cx scopes ident
;;
let run_passes
(cx:ctxt)
(name:string)
(path:Ast.name_component Stack.t)
(passes:Walk.visitor array)
(log:string->unit)
(crate:Ast.crate)
: unit =
let do_pass i pass =
let logger s = log (Printf.sprintf "pass %d: %s" i s) in
Walk.walk_crate
(Walk.path_managing_visitor path
(Walk.mod_item_logging_visitor logger path pass))
crate
in
let sess = cx.ctxt_sess in
if sess.Session.sess_failed
then ()
else
try
Session.time_inner name sess
(fun _ -> Array.iteri do_pass passes)
with
Semant_err (ido, str) -> report_err cx ido str
;;
(* Rust type -> IL type conversion. *)
let word_sty (abi:Abi.abi) : Il.scalar_ty =
Il.ValTy abi.Abi.abi_word_bits
;;
let word_rty (abi:Abi.abi) : Il.referent_ty =
Il.ScalarTy (word_sty abi)
;;
let tydesc_rty (abi:Abi.abi) : Il.referent_ty =
(*
* NB: must match corresponding tydesc structure
* in trans and offsets in ABI exactly.
*)
Il.StructTy
[|
word_rty abi; (* Abi.tydesc_field_first_param *)
word_rty abi; (* Abi.tydesc_field_size *)
word_rty abi; (* Abi.tydesc_field_align *)
Il.ScalarTy (Il.AddrTy Il.CodeTy); (* Abi.tydesc_field_copy_glue *)
Il.ScalarTy (Il.AddrTy Il.CodeTy); (* Abi.tydesc_field_drop_glue *)
Il.ScalarTy (Il.AddrTy Il.CodeTy); (* Abi.tydesc_field_free_glue *)
Il.ScalarTy (Il.AddrTy Il.CodeTy); (* Abi.tydesc_field_mark_glue *)
Il.ScalarTy (Il.AddrTy Il.CodeTy); (* Abi.tydesc_field_obj_drop_glue *)
|]
;;
let obj_closure_rty (abi:Abi.abi) : Il.referent_ty =
Il.StructTy [| word_rty abi;
Il.ScalarTy (Il.AddrTy (tydesc_rty abi));
word_rty abi (* A lie: it's opaque, but this permits
* GEP'ing to it. *)
|]
;;
let rec referent_type (abi:Abi.abi) (t:Ast.ty) : Il.referent_ty =
let s t = Il.ScalarTy t in
let v b = Il.ValTy b in
let p t = Il.AddrTy t in
let sv b = s (v b) in
let sp t = s (p t) in
let word = word_rty abi in
let ptr = sp Il.OpaqueTy in
let rc_ptr = sp (Il.StructTy [| word; Il.OpaqueTy |]) in
let codeptr = sp Il.CodeTy in
let tup ttup = Il.StructTy (Array.map (slot_referent_type abi) ttup) in
let tag ttag =
let union =
Il.UnionTy
(Array.map
(fun key -> tup (Hashtbl.find ttag key))
(sorted_htab_keys ttag))
in
let discriminant = word in
Il.StructTy [| discriminant; union |]
in
match t with
Ast.TY_any -> Il.StructTy [| word; ptr |]
| Ast.TY_nil -> Il.NilTy
| Ast.TY_int
| Ast.TY_uint -> word
| Ast.TY_bool -> sv Il.Bits8
| Ast.TY_mach (TY_u8)
| Ast.TY_mach (TY_i8) -> sv Il.Bits8
| Ast.TY_mach (TY_u16)
| Ast.TY_mach (TY_i16) -> sv Il.Bits16
| Ast.TY_mach (TY_u32)
| Ast.TY_mach (TY_i32)
| Ast.TY_mach (TY_f32)
| Ast.TY_char -> sv Il.Bits32
| Ast.TY_mach (TY_u64)
| Ast.TY_mach (TY_i64)
| Ast.TY_mach (TY_f64) -> sv Il.Bits64
| Ast.TY_str -> sp (Il.StructTy [| word; word; word; ptr |])
| Ast.TY_vec _ -> sp (Il.StructTy [| word; word; word; ptr |])
| Ast.TY_tup tt -> tup tt
| Ast.TY_rec tr -> tup (Array.map snd tr)
| Ast.TY_fn _ ->
let fn_closure_ptr = sp (Il.StructTy [| word; Il.OpaqueTy |]) in
Il.StructTy [| codeptr; fn_closure_ptr |]
| Ast.TY_obj _ ->
let obj_closure_ptr = sp (obj_closure_rty abi) in
Il.StructTy [| ptr; obj_closure_ptr |]
| Ast.TY_tag ttag -> tag ttag
| Ast.TY_iso tiso -> tag tiso.Ast.iso_group.(tiso.Ast.iso_index)
| Ast.TY_idx _ -> word (* A lie, but permits GEP'ing to it. *)
| Ast.TY_chan _
| Ast.TY_port _
| Ast.TY_task -> rc_ptr
| Ast.TY_type -> sp (tydesc_rty abi)
| Ast.TY_native _ -> ptr
| Ast.TY_param (i, _) -> Il.ParamTy i
| Ast.TY_named _ -> bug () "named type in referent_type"
| Ast.TY_constrained (t, _) -> referent_type abi t
and slot_referent_type (abi:Abi.abi) (sl:Ast.slot) : Il.referent_ty =
let s t = Il.ScalarTy t in
let v b = Il.ValTy b in
let p t = Il.AddrTy t in
let sv b = s (v b) in
let sp t = s (p t) in
let word = sv abi.Abi.abi_word_bits in
let rty = referent_type abi (slot_ty sl) in
match sl.Ast.slot_mode with
Ast.MODE_exterior _ -> sp (Il.StructTy [| word; rty |])
| Ast.MODE_interior _ -> rty
| Ast.MODE_alias _ -> sp rty
;;
let task_rty (abi:Abi.abi) : Il.referent_ty =
Il.StructTy
begin
Array.init
Abi.n_visible_task_fields
(fun _ -> word_rty abi)
end
;;
let call_args_referent_type_full
(abi:Abi.abi)
(out_slot:Ast.slot)
(n_ty_params:int)
(in_slots:Ast.slot array)
(iterator_arg_rtys:Il.referent_ty array)
(indirect_arg_rtys:Il.referent_ty array)
: Il.referent_ty =
let out_slot_rty = slot_referent_type abi out_slot in
let out_ptr_rty = Il.ScalarTy (Il.AddrTy out_slot_rty) in
let task_ptr_rty = Il.ScalarTy (Il.AddrTy (task_rty abi)) in
let ty_param_rtys =
let td = Il.ScalarTy (Il.AddrTy (tydesc_rty abi)) in
Il.StructTy (Array.init n_ty_params (fun _ -> td))
in
let arg_rtys = Il.StructTy (Array.map (slot_referent_type abi) in_slots) in
(*
* NB: must match corresponding calltup structure in trans and
* member indices in ABI exactly.
*)
Il.StructTy
[|
out_ptr_rty; (* Abi.calltup_elt_out_ptr *)
task_ptr_rty; (* Abi.calltup_elt_task_ptr *)
ty_param_rtys; (* Abi.calltup_elt_ty_params *)
arg_rtys; (* Abi.calltup_elt_args *)
Il.StructTy iterator_arg_rtys; (* Abi.calltup_elt_iterator_args *)
Il.StructTy indirect_arg_rtys (* Abi.calltup_elt_indirect_args *)
|]
;;
let call_args_referent_type
(cx:ctxt)
(n_ty_params:int)
(callee_ty:Ast.ty)
(closure:Il.referent_ty option)
: Il.referent_ty =
let indirect_arg_rtys =
match closure with
None -> [| |]
| Some c ->
[|
(* Abi.indirect_args_elt_closure *)
Il.ScalarTy (Il.AddrTy c)
|]
in
let iterator_arg_rtys _ =
[|
(* Abi.iterator_args_elt_loop_size *)
Il.ScalarTy (Il.ValTy cx.ctxt_abi.Abi.abi_word_bits);
(* Abi.iterator_args_elt_loop_info_ptr *)
Il.ScalarTy (Il.AddrTy Il.OpaqueTy)
|]
in
match callee_ty with
Ast.TY_fn (tsig, taux) ->
call_args_referent_type_full
cx.ctxt_abi
tsig.Ast.sig_output_slot
n_ty_params
tsig.Ast.sig_input_slots
(if taux.Ast.fn_is_iter then (iterator_arg_rtys()) else [||])
indirect_arg_rtys
| _ -> bug cx "Semant.call_args_referent_type on non-callable type"
;;
let indirect_call_args_referent_type
(cx:ctxt)
(n_ty_params:int)
(callee_ty:Ast.ty)
(closure:Il.referent_ty)
: Il.referent_ty =
call_args_referent_type cx n_ty_params callee_ty (Some closure)
;;
let direct_call_args_referent_type
(cx:ctxt)
(callee_node:node_id)
: Il.referent_ty =
let ity = Hashtbl.find cx.ctxt_all_item_types callee_node in
let n_ty_params =
if item_is_obj_fn cx callee_node
then 0
else n_item_ty_params cx callee_node
in
call_args_referent_type cx n_ty_params ity None
;;
let ty_sz (abi:Abi.abi) (t:Ast.ty) : int64 =
force_sz (Il.referent_ty_size abi.Abi.abi_word_bits (referent_type abi t))
;;
let ty_align (abi:Abi.abi) (t:Ast.ty) : int64 =
force_sz (Il.referent_ty_align abi.Abi.abi_word_bits (referent_type abi t))
;;
let slot_sz (abi:Abi.abi) (s:Ast.slot) : int64 =
force_sz (Il.referent_ty_size abi.Abi.abi_word_bits
(slot_referent_type abi s))
;;
let word_slot (abi:Abi.abi) : Ast.slot =
interior_slot (Ast.TY_mach abi.Abi.abi_word_ty)
;;
let alias_slot (ty:Ast.ty) : Ast.slot =
{ Ast.slot_mode = Ast.MODE_alias;
Ast.slot_mutable = false;
Ast.slot_ty = Some ty }
;;
let mutable_alias_slot (ty:Ast.ty) : Ast.slot =
{ Ast.slot_mode = Ast.MODE_alias;
Ast.slot_mutable = true;
Ast.slot_ty = Some ty }
;;
let mk_ty_fn_or_iter
(out_slot:Ast.slot)
(arg_slots:Ast.slot array)
(is_iter:bool)
: Ast.ty =
(* In some cases we don't care what aux or constrs are. *)
let taux = { Ast.fn_effect = Ast.PURE;
Ast.fn_is_iter = is_iter; }
in
let tsig = { Ast.sig_input_slots = arg_slots;
Ast.sig_input_constrs = [| |];
Ast.sig_output_slot = out_slot; }
in
Ast.TY_fn (tsig, taux)
;;
let mk_ty_fn
(out_slot:Ast.slot)
(arg_slots:Ast.slot array)
: Ast.ty =
mk_ty_fn_or_iter out_slot arg_slots false
;;
let mk_simple_ty_fn
(arg_slots:Ast.slot array)
: Ast.ty =
(* In some cases we don't care what the output slot is. *)
let out_slot = interior_slot Ast.TY_nil in
mk_ty_fn out_slot arg_slots
;;
let mk_simple_ty_iter
(arg_slots:Ast.slot array)
: Ast.ty =
(* In some cases we don't care what the output slot is. *)
let out_slot = interior_slot Ast.TY_nil in
mk_ty_fn_or_iter out_slot arg_slots true
;;
(* name mangling support. *)
let item_name (cx:ctxt) (id:node_id) : Ast.name =
Hashtbl.find cx.ctxt_all_item_names id
;;
let item_str (cx:ctxt) (id:node_id) : string =
string_of_name (item_name cx id)
;;
let ty_str (ty:Ast.ty) : string =
let base = associative_binary_op_ty_fold "" (fun a b -> a ^ b) in
let fold_slot (mode,mut,ty) =
(if mut then "m" else "")
^ (match mode with
Ast.MODE_exterior -> "e"
| Ast.MODE_alias -> "a"
| Ast.MODE_interior -> "")
^ ty
in
let num n = (string_of_int n) ^ "$" in
let len a = num (Array.length a) in
let join az = Array.fold_left (fun a b -> a ^ b) "" az in
let fold_slots slots =
"t"
^ (len slots)
^ (join slots)
in
let fold_rec entries =
"r"
^ (len entries)
^ (Array.fold_left
(fun str (ident, s) -> str ^ "$" ^ ident ^ "$" ^ s)
"" entries)
in
let fold_tags tags =
"g"
^ (num (Hashtbl.length tags))
^ (Array.fold_left
(fun str key -> str ^ (string_of_name key) ^ (Hashtbl.find tags key))
"" (sorted_htab_keys tags))
in
let fold_iso (n, tags) =
"G"
^ (num n)
^ (len tags)
^ (join tags)
in
let fold_mach m =
match m with
TY_u8 -> "U0"
| TY_u16 -> "U1"
| TY_u32 -> "U2"
| TY_u64 -> "U3"
| TY_i8 -> "I0"
| TY_i16 -> "I1"
| TY_i32 -> "I2"
| TY_i64 -> "I3"
| TY_f32 -> "F2"
| TY_f64 -> "F3"
in
let fold =
{ base with
(* Structural types. *)
ty_fold_slot = fold_slot;
ty_fold_slots = fold_slots;
ty_fold_tags = fold_tags;
ty_fold_rec = fold_rec;
ty_fold_nil = (fun _ -> "n");
ty_fold_bool = (fun _ -> "b");
ty_fold_mach = fold_mach;
ty_fold_int = (fun _ -> "i");
ty_fold_uint = (fun _ -> "u");
ty_fold_char = (fun _ -> "c");
ty_fold_obj = (fun _ -> "o");
ty_fold_str = (fun _ -> "s");
ty_fold_vec = (fun s -> "v" ^ s);
ty_fold_iso = fold_iso;
ty_fold_idx = (fun i -> "x" ^ (string_of_int i));
(* FIXME: encode constrs, aux as well. *)
ty_fold_fn = (fun ((ins,_,out),_) -> "f" ^ ins ^ out);
(* Built-in special types. *)
ty_fold_any = (fun _ -> "A");
ty_fold_chan = (fun t -> "H" ^ t);
ty_fold_port = (fun t -> "R" ^ t);
ty_fold_task = (fun _ -> "T");
ty_fold_native = (fun _ -> "N");
ty_fold_param = (fun _ -> "P");
ty_fold_type = (fun _ -> "Y");
(* FIXME: encode obj types. *)
(* FIXME: encode opaque and param numbers. *)
ty_fold_named = (fun _ -> bug () "string-encoding named type");
(* FIXME: encode constrs as well. *)
ty_fold_constrained = (fun (t,_)-> t) }
in
fold_ty fold ty
;;
let glue_str (cx:ctxt) (g:glue) : string =
match g with
GLUE_activate -> "glue$activate"
| GLUE_yield -> "glue$yield"
| GLUE_exit_main_task -> "glue$exit_main_task"
| GLUE_exit_task -> "glue$exit_task"
| GLUE_mark ty -> "glue$mark$" ^ (ty_str ty)
| GLUE_drop ty -> "glue$drop$" ^ (ty_str ty)
| GLUE_free ty -> "glue$free$" ^ (ty_str ty)
| GLUE_copy ty -> "glue$copy$" ^ (ty_str ty)
| GLUE_clone ty -> "glue$clone$" ^ (ty_str ty)
| GLUE_compare ty -> "glue$compare$" ^ (ty_str ty)
| GLUE_hash ty -> "glue$hash$" ^ (ty_str ty)
| GLUE_write ty -> "glue$write$" ^ (ty_str ty)
| GLUE_read ty -> "glue$read$" ^ (ty_str ty)
| GLUE_unwind -> "glue$unwind"
| GLUE_get_next_pc -> "glue$get_next_pc"
| GLUE_mark_frame i -> "glue$mark_frame$" ^ (item_str cx i)
| GLUE_drop_frame i -> "glue$drop_frame$" ^ (item_str cx i)
| GLUE_reloc_frame i -> "glue$reloc_frame$" ^ (item_str cx i)
(*
* FIXME: the node_id here isn't an item, it's a statement;
* lookup bind target and encode bound arg tuple type.
*)
| GLUE_fn_binding i
-> "glue$fn_binding$" ^ (string_of_int (int_of_node i))
| GLUE_obj_drop oid
-> (item_str cx oid) ^ ".drop"
| GLUE_loop_body i
-> "glue$loop_body$" ^ (string_of_int (int_of_node i))
| GLUE_forward (id, oty1, oty2)
-> "glue$forward$"
^ id
^ "$" ^ (ty_str (Ast.TY_obj oty1))
^ "$" ^ (ty_str (Ast.TY_obj oty2))
;;
(*
* Local Variables:
* fill-column: 78;
* indent-tabs-mode: nil
* buffer-file-coding-system: utf-8-unix
* compile-command: "make -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
* End:
*)