summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.ocamlformat1
-rw-r--r--src/Assumed.ml300
-rw-r--r--src/Collections.ml378
-rw-r--r--src/ConstStrings.ml7
-rw-r--r--src/Contexts.ml472
-rw-r--r--src/Cps.ml193
-rw-r--r--src/Crates.ml90
-rw-r--r--src/Errors.ml2
-rw-r--r--src/Expressions.ml118
-rw-r--r--src/ExpressionsUtils.ml10
-rw-r--r--src/ExtractToFStar.ml1638
-rw-r--r--src/FunsAnalysis.ml143
-rw-r--r--src/Identifiers.ml139
-rw-r--r--src/Interpreter.ml396
-rw-r--r--src/InterpreterBorrows.ml1580
-rw-r--r--src/InterpreterBorrowsCore.ml1181
-rw-r--r--src/InterpreterExpansion.ml733
-rw-r--r--src/InterpreterExpressions.ml720
-rw-r--r--src/InterpreterPaths.ml801
-rw-r--r--src/InterpreterProjectors.ml543
-rw-r--r--src/InterpreterStatements.ml1370
-rw-r--r--src/InterpreterUtils.ml245
-rw-r--r--src/Invariants.ml794
-rw-r--r--src/LlbcAst.ml205
-rw-r--r--src/LlbcAstUtils.ml73
-rw-r--r--src/LlbcOfJson.ml915
-rw-r--r--src/Logging.ml179
-rw-r--r--src/Meta.ml44
-rw-r--r--src/Names.ml80
-rw-r--r--src/OfJsonBasic.ml75
-rw-r--r--src/PrePasses.ml54
-rw-r--r--src/Print.ml1283
-rw-r--r--src/PrintPure.ml594
-rw-r--r--src/Pure.ml581
-rw-r--r--src/PureMicroPasses.ml1375
-rw-r--r--src/PureToExtract.ml723
-rw-r--r--src/PureTypeCheck.ml178
-rw-r--r--src/PureUtils.ml450
-rw-r--r--src/Scalars.ml59
-rw-r--r--src/StringUtils.ml106
-rw-r--r--src/Substitute.ml357
-rw-r--r--src/SymbolicAst.ml98
-rw-r--r--src/SymbolicToPure.ml1824
-rw-r--r--src/SynthesizeSymbolic.ml156
-rw-r--r--src/Translate.ml871
-rw-r--r--src/TranslateCore.ml65
-rw-r--r--src/Types.ml208
-rw-r--r--src/TypesAnalysis.ml328
-rw-r--r--src/TypesUtils.ml190
-rw-r--r--src/Utils.ml6
-rw-r--r--src/Values.ml844
-rw-r--r--src/ValuesUtils.ml121
-rw-r--r--src/driver.ml208
-rw-r--r--src/dune48
54 files changed, 0 insertions, 24152 deletions
diff --git a/src/.ocamlformat b/src/.ocamlformat
deleted file mode 100644
index b0ae150e..00000000
--- a/src/.ocamlformat
+++ /dev/null
@@ -1 +0,0 @@
-doc-comments=before \ No newline at end of file
diff --git a/src/Assumed.ml b/src/Assumed.ml
deleted file mode 100644
index cb089c08..00000000
--- a/src/Assumed.ml
+++ /dev/null
@@ -1,300 +0,0 @@
-(** This module contains various utilities for the assumed functions.
-
- Note that [Box::free] is peculiar: we don't really handle it as a function,
- because it is legal to free a box whose boxed value is [⊥] (it often
- happens that we move a value out of a box before freeing this box).
- Semantically speaking, we thus handle [Box::free] as a value drop and
- not as a function call, and thus never need its signature.
-
- TODO: implementing the concrete evaluation functions for the assumed
- functions is really annoying (see
- [InterpreterStatements.eval_non_local_function_call_concrete]).
- I think it should be possible, in most situations, to write bodies which
- model the behaviour of those unsafe functions. For instance, [Box::deref_mut]
- should simply be:
- {[
- fn deref_mut<'a, T>(x : &'a mut Box<T>) -> &'a mut T {
- &mut ( *x ) // box dereferencement is a primitive operation
- }
- ]}
-
- For vectors, we could "cheat" by using the index as a field index (vectors
- would be encoded as ADTs with a variable number of fields). Of course, it
- would require a bit of engineering, but it would probably be quite lightweight
- in the end.
- {[
- Vec::get_mut<'a,T>(v : &'a mut Vec<T>, i : usize) -> &'a mut T {
- &mut ( ( *x ).i )
- }
- ]}
- *)
-
-open Names
-open TypesUtils
-module T = Types
-module A = LlbcAst
-
-module Sig = struct
- (** A few utilities *)
-
- let rvar_id_0 = T.RegionVarId.of_int 0
- let rvar_0 : T.RegionVarId.id T.region = T.Var rvar_id_0
- let rg_id_0 = T.RegionGroupId.of_int 0
- let tvar_id_0 = T.TypeVarId.of_int 0
- let tvar_0 : T.sty = T.TypeVar tvar_id_0
-
- (** Region 'a of id 0 *)
- let region_param_0 : T.region_var = { T.index = rvar_id_0; name = Some "'a" }
-
- (** Region group: [{ parent={}; regions:{'a of id 0} }] *)
- let region_group_0 : T.region_var_group =
- { T.id = rg_id_0; regions = [ rvar_id_0 ]; parents = [] }
-
- (** Type parameter [T] of id 0 *)
- let type_param_0 : T.type_var = { T.index = tvar_id_0; name = "T" }
-
- let mk_ref_ty (r : T.RegionVarId.id T.region) (ty : T.sty) (is_mut : bool) :
- T.sty =
- let ref_kind = if is_mut then T.Mut else T.Shared in
- mk_ref_ty r ty ref_kind
-
- (** [fn<T>(&'a mut T, T) -> T] *)
- let mem_replace_sig : A.fun_sig =
- (* The signature fields *)
- let region_params = [ region_param_0 ] (* <'a> *) in
- let regions_hierarchy = [ region_group_0 ] (* [{<'a>}] *) in
- let type_params = [ type_param_0 ] (* <T> *) in
- let inputs =
- [ mk_ref_ty rvar_0 tvar_0 true (* &'a mut T *); tvar_0 (* T *) ]
- in
- let output = tvar_0 (* T *) in
- {
- region_params;
- num_early_bound_regions = 0;
- regions_hierarchy;
- type_params;
- inputs;
- output;
- }
-
- (** [fn<T>(T) -> Box<T>] *)
- let box_new_sig : A.fun_sig =
- {
- region_params = [];
- num_early_bound_regions = 0;
- regions_hierarchy = [];
- type_params = [ type_param_0 ] (* <T> *);
- inputs = [ tvar_0 (* T *) ];
- output = mk_box_ty tvar_0 (* Box<T> *);
- }
-
- (** [fn<T>(Box<T>) -> ()] *)
- let box_free_sig : A.fun_sig =
- {
- region_params = [];
- num_early_bound_regions = 0;
- regions_hierarchy = [];
- type_params = [ type_param_0 ] (* <T> *);
- inputs = [ mk_box_ty tvar_0 (* Box<T> *) ];
- output = mk_unit_ty (* () *);
- }
-
- (** Helper for [Box::deref_shared] and [Box::deref_mut].
- Returns:
- [fn<'a, T>(&'a (mut) Box<T>) -> &'a (mut) T]
- *)
- let box_deref_gen_sig (is_mut : bool) : A.fun_sig =
- (* The signature fields *)
- let region_params = [ region_param_0 ] in
- let regions_hierarchy = [ region_group_0 ] (* <'a> *) in
- {
- region_params;
- num_early_bound_regions = 0;
- regions_hierarchy;
- type_params = [ type_param_0 ] (* <T> *);
- inputs =
- [ mk_ref_ty rvar_0 (mk_box_ty tvar_0) is_mut (* &'a (mut) Box<T> *) ];
- output = mk_ref_ty rvar_0 tvar_0 is_mut (* &'a (mut) T *);
- }
-
- (** [fn<'a, T>(&'a Box<T>) -> &'a T] *)
- let box_deref_shared_sig = box_deref_gen_sig false
-
- (** [fn<'a, T>(&'a mut Box<T>) -> &'a mut T] *)
- let box_deref_mut_sig = box_deref_gen_sig true
-
- (** [fn<T>() -> Vec<T>] *)
- let vec_new_sig : A.fun_sig =
- let region_params = [] in
- let regions_hierarchy = [] in
- let type_params = [ type_param_0 ] (* <T> *) in
- let inputs = [] in
- let output = mk_vec_ty tvar_0 (* Vec<T> *) in
- {
- region_params;
- num_early_bound_regions = 0;
- regions_hierarchy;
- type_params;
- inputs;
- output;
- }
-
- (** [fn<T>(&'a mut Vec<T>, T)] *)
- let vec_push_sig : A.fun_sig =
- (* The signature fields *)
- let region_params = [ region_param_0 ] in
- let regions_hierarchy = [ region_group_0 ] (* <'a> *) in
- let type_params = [ type_param_0 ] (* <T> *) in
- let inputs =
- [
- mk_ref_ty rvar_0 (mk_vec_ty tvar_0) true (* &'a mut Vec<T> *);
- tvar_0 (* T *);
- ]
- in
- let output = mk_unit_ty (* () *) in
- {
- region_params;
- num_early_bound_regions = 0;
- regions_hierarchy;
- type_params;
- inputs;
- output;
- }
-
- (** [fn<T>(&'a mut Vec<T>, usize, T)] *)
- let vec_insert_sig : A.fun_sig =
- (* The signature fields *)
- let region_params = [ region_param_0 ] in
- let regions_hierarchy = [ region_group_0 ] (* <'a> *) in
- let type_params = [ type_param_0 ] (* <T> *) in
- let inputs =
- [
- mk_ref_ty rvar_0 (mk_vec_ty tvar_0) true (* &'a mut Vec<T> *);
- mk_usize_ty (* usize *);
- tvar_0 (* T *);
- ]
- in
- let output = mk_unit_ty (* () *) in
- {
- region_params;
- num_early_bound_regions = 0;
- regions_hierarchy;
- type_params;
- inputs;
- output;
- }
-
- (** [fn<T>(&'a Vec<T>) -> usize] *)
- let vec_len_sig : A.fun_sig =
- (* The signature fields *)
- let region_params = [ region_param_0 ] in
- let regions_hierarchy = [ region_group_0 ] (* <'a> *) in
- let type_params = [ type_param_0 ] (* <T> *) in
- let inputs =
- [ mk_ref_ty rvar_0 (mk_vec_ty tvar_0) false (* &'a Vec<T> *) ]
- in
- let output = mk_usize_ty (* usize *) in
- {
- region_params;
- num_early_bound_regions = 0;
- regions_hierarchy;
- type_params;
- inputs;
- output;
- }
-
- (** Helper:
- [fn<T>(&'a (mut) Vec<T>, usize) -> &'a (mut) T]
- *)
- let vec_index_gen_sig (is_mut : bool) : A.fun_sig =
- (* The signature fields *)
- let region_params = [ region_param_0 ] in
- let regions_hierarchy = [ region_group_0 ] (* <'a> *) in
- let type_params = [ type_param_0 ] (* <T> *) in
- let inputs =
- [
- mk_ref_ty rvar_0 (mk_vec_ty tvar_0) is_mut (* &'a (mut) Vec<T> *);
- mk_usize_ty (* usize *);
- ]
- in
- let output = mk_ref_ty rvar_0 tvar_0 is_mut (* &'a (mut) T *) in
- {
- region_params;
- num_early_bound_regions = 0;
- regions_hierarchy;
- type_params;
- inputs;
- output;
- }
-
- (** [fn<T>(&'a Vec<T>, usize) -> &'a T] *)
- let vec_index_shared_sig : A.fun_sig = vec_index_gen_sig false
-
- (** [fn<T>(&'a mut Vec<T>, usize) -> &'a mut T] *)
- let vec_index_mut_sig : A.fun_sig = vec_index_gen_sig true
-end
-
-type assumed_info = A.assumed_fun_id * A.fun_sig * bool * name
-
-(** The list of assumed functions and all their information:
- - their signature
- - a boolean indicating whether the function can fail or not
- - their name
-
- Rk.: following what is written above, we don't include [Box::free].
-
- Remark about the vector functions: for [Vec::len] to be correct and return
- a [usize], we have to make sure that vectors are bounded by the max usize.
- Followingly, [Vec::push] is monadic.
- *)
-let assumed_infos : assumed_info list =
- let deref_pre = [ "core"; "ops"; "deref" ] in
- let vec_pre = [ "alloc"; "vec"; "Vec" ] in
- let index_pre = [ "core"; "ops"; "index" ] in
- [
- (A.Replace, Sig.mem_replace_sig, false, to_name [ "core"; "mem"; "replace" ]);
- (BoxNew, Sig.box_new_sig, false, to_name [ "alloc"; "boxed"; "Box"; "new" ]);
- ( BoxFree,
- Sig.box_free_sig,
- false,
- to_name [ "alloc"; "boxed"; "Box"; "free" ] );
- ( BoxDeref,
- Sig.box_deref_shared_sig,
- false,
- to_name (deref_pre @ [ "Deref"; "deref" ]) );
- ( BoxDerefMut,
- Sig.box_deref_mut_sig,
- false,
- to_name (deref_pre @ [ "DerefMut"; "deref_mut" ]) );
- (VecNew, Sig.vec_new_sig, false, to_name (vec_pre @ [ "new" ]));
- (VecPush, Sig.vec_push_sig, true, to_name (vec_pre @ [ "push" ]));
- (VecInsert, Sig.vec_insert_sig, true, to_name (vec_pre @ [ "insert" ]));
- (VecLen, Sig.vec_len_sig, false, to_name (vec_pre @ [ "len" ]));
- ( VecIndex,
- Sig.vec_index_shared_sig,
- true,
- to_name (index_pre @ [ "Index"; "index" ]) );
- ( VecIndexMut,
- Sig.vec_index_mut_sig,
- true,
- to_name (index_pre @ [ "IndexMut"; "index_mut" ]) );
- ]
-
-let get_assumed_info (id : A.assumed_fun_id) : assumed_info =
- match List.find_opt (fun (id', _, _, _) -> id = id') assumed_infos with
- | Some info -> info
- | None ->
- raise
- (Failure ("get_assumed_info: not found: " ^ A.show_assumed_fun_id id))
-
-let get_assumed_sig (id : A.assumed_fun_id) : A.fun_sig =
- let _, sg, _, _ = get_assumed_info id in
- sg
-
-let get_assumed_name (id : A.assumed_fun_id) : fun_name =
- let _, _, _, name = get_assumed_info id in
- name
-
-let assumed_can_fail (id : A.assumed_fun_id) : bool =
- let _, _, b, _ = get_assumed_info id in
- b
diff --git a/src/Collections.ml b/src/Collections.ml
deleted file mode 100644
index 0933b3e4..00000000
--- a/src/Collections.ml
+++ /dev/null
@@ -1,378 +0,0 @@
-(** The following file redefines several modules like Map or Set. *)
-
-module F = Format
-
-module List = struct
- include List
-
- (** Split a list at a given index.
-
- [split_at ls i] splits [ls] into two lists where the first list has
- length [i].
-
- Raise [Failure] if the list is too short.
- *)
- let rec split_at (ls : 'a list) (i : int) =
- if i < 0 then raise (Invalid_argument "split_at take positive integers")
- else if i = 0 then ([], ls)
- else
- match ls with
- | [] ->
- raise
- (Failure "The int given to split_at should be <= the list's length")
- | x :: ls' ->
- let ls1, ls2 = split_at ls' (i - 1) in
- (x :: ls1, ls2)
-
- (** Pop the last element of a list
-
- Raise [Failure] if the list is empty.
- *)
- let rec pop_last (ls : 'a list) : 'a list * 'a =
- match ls with
- | [] -> raise (Failure "The list is empty")
- | [ x ] -> ([], x)
- | x :: ls ->
- let ls, last = pop_last ls in
- (x :: ls, last)
-
- (** Return the n first elements of the list *)
- let prefix (n : int) (ls : 'a list) : 'a list = fst (split_at ls n)
-
- (** Iter and link the iterations.
-
- Iterate over a list, but call a function between every two elements
- (but not before the first element, and not after the last).
- *)
- let iter_link (link : unit -> unit) (f : 'a -> unit) (ls : 'a list) : unit =
- let rec iter ls =
- match ls with
- | [] -> ()
- | [ x ] -> f x
- | x :: y :: ls ->
- f x;
- link ();
- iter (y :: ls)
- in
- iter ls
-
- (** Fold and link the iterations.
-
- Similar to {!iter_link} but for fold left operations.
- *)
- let fold_left_link (link : unit -> unit) (f : 'a -> 'b -> 'a) (init : 'a)
- (ls : 'b list) : 'a =
- let rec fold (acc : 'a) (ls : 'b list) : 'a =
- match ls with
- | [] -> acc
- | [ x ] -> f acc x
- | x :: y :: ls ->
- let acc = f acc x in
- link ();
- fold acc (y :: ls)
- in
- fold init ls
-
- let to_cons_nil (ls : 'a list) : 'a =
- match ls with
- | [ x ] -> x
- | _ -> raise (Failure "The list should have length exactly one")
-
- let pop (ls : 'a list) : 'a * 'a list =
- match ls with
- | x :: ls' -> (x, ls')
- | _ -> raise (Failure "The list should have length > 0")
-end
-
-module type OrderedType = sig
- include Map.OrderedType
-
- val to_string : t -> string
- val pp_t : Format.formatter -> t -> unit
- val show_t : t -> string
-end
-
-(** Ordered string *)
-module OrderedString : OrderedType with type t = string = struct
- include String
-
- let to_string s = s
- let pp_t fmt s = Format.pp_print_string fmt s
- let show_t s = s
-end
-
-module type Map = sig
- include Map.S
-
- val add_list : (key * 'a) list -> 'a t -> 'a t
- val of_list : (key * 'a) list -> 'a t
-
- (** "Simple" pretty printing function.
-
- Is useful when we need to customize a bit [show_t], but without using
- something as burdensome as [pp_t].
-
- [to_string (Some indent) m] prints [m] by breaking line after every binding
- and inserting [indent].
- *)
- val to_string : string option -> ('a -> string) -> 'a t -> string
-
- val pp : (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a t -> unit
- val show : ('a -> string) -> 'a t -> string
-end
-
-module MakeMap (Ord : OrderedType) : Map with type key = Ord.t = struct
- module Map = Map.Make (Ord)
- include Map
-
- let add_list bl m = List.fold_left (fun s (key, e) -> add key e s) m bl
- let of_list bl = add_list bl empty
-
- let to_string indent_opt a_to_string m =
- let indent, break =
- match indent_opt with Some indent -> (indent, "\n") | None -> ("", " ")
- in
- let sep = "," ^ break in
- let ls =
- Map.fold
- (fun key v ls ->
- (indent ^ Ord.to_string key ^ " -> " ^ a_to_string v) :: ls)
- m []
- in
- match ls with
- | [] -> "{}"
- | _ -> "{" ^ break ^ String.concat sep (List.rev ls) ^ break ^ "}"
-
- let pp (pp_a : Format.formatter -> 'a -> unit) (fmt : Format.formatter)
- (m : 'a t) : unit =
- let pp_string = F.pp_print_string fmt in
- let pp_space () = F.pp_print_space fmt () in
- pp_string "{";
- F.pp_open_box fmt 2;
- Map.iter
- (fun key x ->
- Ord.pp_t fmt key;
- pp_space ();
- pp_string "->";
- pp_space ();
- pp_a fmt x;
- pp_string ",";
- F.pp_print_break fmt 1 0)
- m;
- F.pp_close_box fmt ();
- F.pp_print_break fmt 0 0;
- pp_string "}"
-
- let show show_a m = to_string None show_a m
-end
-
-module type Set = sig
- include Set.S
-
- val add_list : elt list -> t -> t
- val of_list : elt list -> t
-
- (** "Simple" pretty printing function.
-
- Is useful when we need to customize a bit [show_t], but without using
- something as burdensome as [pp_t].
-
- [to_string (Some indent) s] prints [s] by breaking line after every element
- and inserting [indent].
- *)
- val to_string : string option -> t -> string
-
- val pp : Format.formatter -> t -> unit
- val show : t -> string
- val pairwise_distinct : elt list -> bool
-end
-
-module MakeSet (Ord : OrderedType) : Set with type elt = Ord.t = struct
- module Set = Set.Make (Ord)
- include Set
-
- let add_list bl s = List.fold_left (fun s e -> add e s) s bl
- let of_list bl = add_list bl empty
-
- let to_string indent_opt m =
- let indent, break =
- match indent_opt with Some indent -> (indent, "\n") | None -> ("", " ")
- in
- let sep = "," ^ break in
- let ls = Set.fold (fun v ls -> (indent ^ Ord.to_string v) :: ls) m [] in
- match ls with
- | [] -> "{}"
- | _ -> "{" ^ break ^ String.concat sep (List.rev ls) ^ break ^ "}"
-
- let pp (fmt : Format.formatter) (m : t) : unit =
- let pp_string = F.pp_print_string fmt in
- pp_string "{";
- F.pp_open_box fmt 2;
- Set.iter
- (fun x ->
- Ord.pp_t fmt x;
- pp_string ",";
- F.pp_print_break fmt 1 0)
- m;
- F.pp_close_box fmt ();
- F.pp_print_break fmt 0 0;
- pp_string "}"
-
- let show s = to_string None s
-
- let pairwise_distinct ls =
- let s = ref empty in
- let rec check ls =
- match ls with
- | [] -> true
- | x :: ls' ->
- if mem x !s then false
- else (
- s := add x !s;
- check ls')
- in
- check ls
-end
-
-(** A map where the bindings are injective (i.e., if two keys are distinct,
- their bindings are distinct).
-
- This is useful for instance when generating mappings from our internal
- identifiers to names (i.e., strings) when generating code, in order to
- make sure that we don't have potentially dangerous collisions.
- *)
-module type InjMap = sig
- type key
- type elem
- type t
-
- val empty : t
- val is_empty : t -> bool
- val mem : key -> t -> bool
- val add : key -> elem -> t -> t
- val singleton : key -> elem -> t
- val remove : key -> t -> t
- val compare : (elem -> elem -> int) -> t -> t -> int
- val equal : (elem -> elem -> bool) -> t -> t -> bool
- val iter : (key -> elem -> unit) -> t -> unit
- val fold : (key -> elem -> 'b -> 'b) -> t -> 'b -> 'b
- val for_all : (key -> elem -> bool) -> t -> bool
- val exists : (key -> elem -> bool) -> t -> bool
- val filter : (key -> elem -> bool) -> t -> t
- val partition : (key -> elem -> bool) -> t -> t * t
- val cardinal : t -> int
- val bindings : t -> (key * elem) list
- val min_binding : t -> key * elem
- val min_binding_opt : t -> (key * elem) option
- val max_binding : t -> key * elem
- val max_binding_opt : t -> (key * elem) option
- val choose : t -> key * elem
- val choose_opt : t -> (key * elem) option
- val split : key -> t -> t * elem option * t
- val find : key -> t -> elem
- val find_opt : key -> t -> elem option
- val find_first : (key -> bool) -> t -> key * elem
- val find_first_opt : (key -> bool) -> t -> (key * elem) option
- val find_last : (key -> bool) -> t -> key * elem
- val find_last_opt : (key -> bool) -> t -> (key * elem) option
- val to_seq : t -> (key * elem) Seq.t
- val to_seq_from : key -> t -> (key * elem) Seq.t
- val add_seq : (key * elem) Seq.t -> t -> t
- val of_seq : (key * elem) Seq.t -> t
- val add_list : (key * elem) list -> t -> t
- val of_list : (key * elem) list -> t
-end
-
-(** See {!InjMap} *)
-module MakeInjMap (Key : OrderedType) (Elem : OrderedType) :
- InjMap with type key = Key.t with type elem = Elem.t = struct
- module Map = MakeMap (Key)
- module Set = MakeSet (Elem)
-
- type key = Key.t
- type elem = Elem.t
- type t = { map : elem Map.t; elems : Set.t }
-
- let empty = { map = Map.empty; elems = Set.empty }
- let is_empty m = Map.is_empty m.map
- let mem k m = Map.mem k m.map
-
- let add k e m =
- assert (not (Set.mem e m.elems));
- { map = Map.add k e m.map; elems = Set.add e m.elems }
-
- let singleton k e = { map = Map.singleton k e; elems = Set.singleton e }
-
- let remove k m =
- match Map.find_opt k m.map with
- | None -> m
- | Some x -> { map = Map.remove k m.map; elems = Set.remove x m.elems }
-
- let compare f m1 m2 = Map.compare f m1.map m2.map
- let equal f m1 m2 = Map.equal f m1.map m2.map
- let iter f m = Map.iter f m.map
- let fold f m x = Map.fold f m.map x
- let for_all f m = Map.for_all f m.map
- let exists f m = Map.exists f m.map
-
- (** Small helper *)
- let bindings_to_elems_set (bls : (key * elem) list) : Set.t =
- let elems = List.map snd bls in
- let elems = List.fold_left (fun s e -> Set.add e s) Set.empty elems in
- elems
-
- (** Small helper *)
- let map_to_elems_set (map : elem Map.t) : Set.t =
- bindings_to_elems_set (Map.bindings map)
-
- (** Small helper *)
- let map_to_t (map : elem Map.t) : t =
- let elems = map_to_elems_set map in
- { map; elems }
-
- let filter f m =
- let map = Map.filter f m.map in
- let elems = map_to_elems_set map in
- { map; elems }
-
- let partition f m =
- let map1, map2 = Map.partition f m.map in
- (map_to_t map1, map_to_t map2)
-
- let cardinal m = Map.cardinal m.map
- let bindings m = Map.bindings m.map
- let min_binding m = Map.min_binding m.map
- let min_binding_opt m = Map.min_binding_opt m.map
- let max_binding m = Map.max_binding m.map
- let max_binding_opt m = Map.max_binding_opt m.map
- let choose m = Map.choose m.map
- let choose_opt m = Map.choose_opt m.map
-
- let split k m =
- let l, data, r = Map.split k m.map in
- let l = map_to_t l in
- let r = map_to_t r in
- (l, data, r)
-
- let find k m = Map.find k m.map
- let find_opt k m = Map.find_opt k m.map
- let find_first k m = Map.find_first k m.map
- let find_first_opt k m = Map.find_first_opt k m.map
- let find_last k m = Map.find_last k m.map
- let find_last_opt k m = Map.find_last_opt k m.map
- let to_seq m = Map.to_seq m.map
- let to_seq_from k m = Map.to_seq_from k m.map
-
- let rec add_seq s m =
- (* Note that it is important to check that we don't add bindings mapping
- * to the same element *)
- match s () with
- | Seq.Nil -> m
- | Seq.Cons ((k, e), s) ->
- let m = add k e m in
- add_seq s m
-
- let of_seq s = add_seq s empty
- let add_list ls m = List.fold_left (fun m (key, elem) -> add key elem m) m ls
- let of_list ls = add_list ls empty
-end
diff --git a/src/ConstStrings.ml b/src/ConstStrings.ml
deleted file mode 100644
index ae169a2e..00000000
--- a/src/ConstStrings.ml
+++ /dev/null
@@ -1,7 +0,0 @@
-(** Some utilities *)
-
-(** Basename for state variables (introduced when using state-error monads) *)
-let state_basename = "st"
-
-(** ADT constructor prefix (used when pretty-printing) *)
-let constructor_prefix = "Mk"
diff --git a/src/Contexts.ml b/src/Contexts.ml
deleted file mode 100644
index 510976f4..00000000
--- a/src/Contexts.ml
+++ /dev/null
@@ -1,472 +0,0 @@
-open Types
-open Values
-open LlbcAst
-module V = Values
-open ValuesUtils
-
-(** Some global counters.
-
- Note that those counters were initially stored in {!eval_ctx} values,
- but it proved better to make them global and stateful:
- - when branching (and thus executing on several paths with different
- contexts) it is better to really have unique ids everywhere (and
- not have fresh ids shared by several contexts even though introduced
- after the branching) because at some point we might need to merge the
- different contexts
- - also, it is a lot more convenient to not store those counters in contexts
- objects
-
- =============
- **WARNING**:
- =============
- Pay attention when playing with closures, as you may not always generate
- fresh identifiers without noticing it, especially when using type abbreviations.
- For instance, consider the following:
- {[
- type fun_type = unit -> ...
- fn f x : fun_type =
- let id = fresh_id () in
- ...
-
- let g = f x in // <-- the fresh identifier gets generated here
- let x1 = g () in // <-- no fresh generation here
- let x2 = g () in
- ...
- ]}
-
- This is why, in such cases, we often introduce all the inputs, even
- when they are not used (which happens!).
- {[
- fn f x : fun_type =
- fun .. ->
- let id = fresh_id () in
- ...
- ]}
-
- Note that in practice, we never reuse closures, except when evaluating
- a branching in the execution (which is fine, because the branches evaluate
- independentely of each other). Still, it is always a good idea to be
- defensive.
-
- However, the same problem arises with logging.
-
- Also, a more defensive way would be to not use global references, and
- store the counters in the evaluation context. This is actually what was
- originally done, before we updated the code to use global counters because
- it proved more convenient (and even before updating the code of the
- interpreter to use CPS).
- *)
-
-let symbolic_value_id_counter, fresh_symbolic_value_id =
- SymbolicValueId.fresh_stateful_generator ()
-
-let borrow_id_counter, fresh_borrow_id = BorrowId.fresh_stateful_generator ()
-let region_id_counter, fresh_region_id = RegionId.fresh_stateful_generator ()
-
-let abstraction_id_counter, fresh_abstraction_id =
- AbstractionId.fresh_stateful_generator ()
-
-let fun_call_id_counter, fresh_fun_call_id =
- FunCallId.fresh_stateful_generator ()
-
-(** We shouldn't need to reset the global counters, but it might be good to
- do it from time to time, for instance every time we start evaluating/
- synthesizing a function.
-
- The reasons are manifold:
- - it might prevent the counters from overflowing (although this seems
- extremely unlikely - as a side node: we have overflow checks to make
- sure the synthesis doesn't get impacted by potential overflows)
- - most importantly, it allows to always manipulate low values, which
- is always a lot more readable when debugging
- *)
-let reset_global_counters () =
- symbolic_value_id_counter := SymbolicValueId.generator_zero;
- borrow_id_counter := BorrowId.generator_zero;
- region_id_counter := RegionId.generator_zero;
- abstraction_id_counter := AbstractionId.generator_zero;
- fun_call_id_counter := FunCallId.generator_zero
-
-(** A binder used in an environment, to map a variable to a value *)
-type binder = {
- index : VarId.id; (** Unique variable identifier *)
- name : string option; (** Possible name *)
-}
-[@@deriving show]
-
-(** Environment value: mapping from variable to value, abstraction (only
- used in symbolic mode) or stack frame delimiter.
-
- TODO: rename Var (-> Binding?)
- *)
-type env_elem =
- | Var of (binder option[@opaque]) * typed_value
- (** Variable binding - the binder is None if the variable is a dummy variable
- (we use dummy variables to store temporaries while doing bookkeeping such
- as ending borrows for instance). *)
- | Abs of abs
- | Frame
-[@@deriving
- show,
- visitors
- {
- name = "iter_env_elem";
- variety = "iter";
- ancestors = [ "iter_abs" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- concrete = true;
- },
- visitors
- {
- name = "map_env_elem";
- variety = "map";
- ancestors = [ "map_abs" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- concrete = true;
- }]
-
-type env = env_elem list
-[@@deriving
- show,
- visitors
- {
- name = "iter_env";
- variety = "iter";
- ancestors = [ "iter_env_elem" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- concrete = true;
- },
- visitors
- {
- name = "map_env";
- variety = "map";
- ancestors = [ "map_env_elem" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- concrete = true;
- }]
-
-type interpreter_mode = ConcreteMode | SymbolicMode [@@deriving show]
-
-type config = {
- mode : interpreter_mode;
- (** Concrete mode (interpreter) or symbolic mode (for synthesis) **)
- check_invariants : bool;
- (** Check that invariants are maintained whenever we execute a statement *)
- greedy_expand_symbolics_with_borrows : bool;
- (** Expand all symbolic values containing borrows upon introduction - allows
- to use restrict ourselves to a simpler model for the projectors over
- symbolic values.
- The interpreter fails if doing this requires to do a branching (because
- we need to expand an enumeration with strictly more than one variant)
- or if we need to expand a recursive type (because this leads to looping).
- *)
- allow_bottom_below_borrow : bool;
- (** Experimental.
-
- We sometimes want to temporarily break the invariant that there is no
- bottom value below a borrow. If this value is true, we don't check
- the invariant, and the rule becomes: we can't end a borrow *if* it contains
- a bottom value. The consequence is that it becomes ok to temporarily
- have bottom below a borrow, if we put something else inside before ending
- the borrow.
-
- For instance, when evaluating an assignment, we move the value which
- will be overwritten then do some administrative tasks with the borrows,
- then move the rvalue to its destination. We currently want to be able
- to check the invariants every time we end a borrow/an abstraction,
- meaning at intermediate steps of the assignment where the invariants
- might actually be broken.
- *)
- return_unit_end_abs_with_no_loans : bool;
- (** If a function doesn't return any borrows, we can immediately call
- its backward functions. If this option is on, whenever we call a
- function *and* this function returns unit, we immediately end all the
- abstractions which are introduced and don't contain loans. This can be
- useful to make the code cleaner (the backward function is introduced
- where the function call happened) and make sure all forward functions
- with no return value are followed by a backward function.
- *)
-}
-[@@deriving show]
-
-(** See {!config} *)
-type partial_config = {
- check_invariants : bool;
- greedy_expand_symbolics_with_borrows : bool;
- allow_bottom_below_borrow : bool;
- return_unit_end_abs_with_no_loans : bool;
-}
-
-let config_of_partial (mode : interpreter_mode) (config : partial_config) :
- config =
- {
- mode;
- check_invariants = config.check_invariants;
- greedy_expand_symbolics_with_borrows =
- config.greedy_expand_symbolics_with_borrows;
- allow_bottom_below_borrow = config.allow_bottom_below_borrow;
- return_unit_end_abs_with_no_loans = config.return_unit_end_abs_with_no_loans;
- }
-
-type type_context = {
- type_decls_groups : Crates.type_declaration_group TypeDeclId.Map.t;
- type_decls : type_decl TypeDeclId.Map.t;
- type_infos : TypesAnalysis.type_infos;
-}
-[@@deriving show]
-
-type fun_context = { fun_decls : fun_decl FunDeclId.Map.t } [@@deriving show]
-
-type global_context = { global_decls : global_decl GlobalDeclId.Map.t }
-[@@deriving show]
-
-(** Evaluation context *)
-type eval_ctx = {
- type_context : type_context;
- fun_context : fun_context;
- global_context : global_context;
- type_vars : type_var list;
- env : env;
- ended_regions : RegionId.Set.t;
-}
-[@@deriving show]
-
-let lookup_type_var (ctx : eval_ctx) (vid : TypeVarId.id) : type_var =
- TypeVarId.nth ctx.type_vars vid
-
-let opt_binder_has_vid (bv : binder option) (vid : VarId.id) : bool =
- match bv with Some bv -> bv.index = vid | None -> false
-
-let ctx_lookup_binder (ctx : eval_ctx) (vid : VarId.id) : binder =
- (* TOOD: we might want to stop at the end of the frame *)
- let rec lookup env =
- match env with
- | [] ->
- raise (Invalid_argument ("Variable not found: " ^ VarId.to_string vid))
- | Var (var, _) :: env' ->
- if opt_binder_has_vid var vid then Option.get var else lookup env'
- | (Abs _ | Frame) :: env' -> lookup env'
- in
- lookup ctx.env
-
-(** TODO: make this more efficient with maps *)
-let ctx_lookup_type_decl (ctx : eval_ctx) (tid : TypeDeclId.id) : type_decl =
- TypeDeclId.Map.find tid ctx.type_context.type_decls
-
-(** TODO: make this more efficient with maps *)
-let ctx_lookup_fun_decl (ctx : eval_ctx) (fid : FunDeclId.id) : fun_decl =
- FunDeclId.Map.find fid ctx.fun_context.fun_decls
-
-(** TODO: make this more efficient with maps *)
-let ctx_lookup_global_decl (ctx : eval_ctx) (gid : GlobalDeclId.id) :
- global_decl =
- GlobalDeclId.Map.find gid ctx.global_context.global_decls
-
-(** Retrieve a variable's value in an environment *)
-let env_lookup_var_value (env : env) (vid : VarId.id) : typed_value =
- (* We take care to stop at the end of current frame: different variables
- in different frames can have the same id!
- *)
- let rec lookup env =
- match env with
- | [] -> failwith "Unexpected"
- | Var (var, v) :: env' ->
- if opt_binder_has_vid var vid then v else lookup env'
- | Abs _ :: env' -> lookup env'
- | Frame :: _ -> failwith "End of frame"
- in
- lookup env
-
-(** Retrieve a variable's value in an evaluation context *)
-let ctx_lookup_var_value (ctx : eval_ctx) (vid : VarId.id) : typed_value =
- env_lookup_var_value ctx.env vid
-
-(** Update a variable's value in an environment
-
- This is a helper function: it can break invariants and doesn't perform
- any check.
-*)
-let env_update_var_value (env : env) (vid : VarId.id) (nv : typed_value) : env =
- (* We take care to stop at the end of current frame: different variables
- in different frames can have the same id!
- *)
- let rec update env =
- match env with
- | [] -> failwith "Unexpected"
- | Var (var, v) :: env' ->
- if opt_binder_has_vid var vid then Var (var, nv) :: env'
- else Var (var, v) :: update env'
- | Abs abs :: env' -> Abs abs :: update env'
- | Frame :: _ -> failwith "End of frame"
- in
- update env
-
-let var_to_binder (var : var) : binder = { index = var.index; name = var.name }
-
-(** Update a variable's value in an evaluation context.
-
- This is a helper function: it can break invariants and doesn't perform
- any check.
-*)
-let ctx_update_var_value (ctx : eval_ctx) (vid : VarId.id) (nv : typed_value) :
- eval_ctx =
- { ctx with env = env_update_var_value ctx.env vid nv }
-
-(** Push a variable in the context's environment.
-
- Checks that the pushed variable and its value have the same type (this
- is important).
-*)
-let ctx_push_var (ctx : eval_ctx) (var : var) (v : typed_value) : eval_ctx =
- assert (var.var_ty = v.ty);
- let bv = var_to_binder var in
- { ctx with env = Var (Some bv, v) :: ctx.env }
-
-(** Push a list of variables.
-
- Checks that the pushed variables and their values have the same type (this
- is important).
-*)
-let ctx_push_vars (ctx : eval_ctx) (vars : (var * typed_value) list) : eval_ctx
- =
- assert (
- List.for_all
- (fun (var, (value : typed_value)) -> var.var_ty = value.ty)
- vars);
- let vars =
- List.map (fun (var, value) -> Var (Some (var_to_binder var), value)) vars
- in
- let vars = List.rev vars in
- { ctx with env = List.append vars ctx.env }
-
-(** Push a dummy variable in the context's environment. *)
-let ctx_push_dummy_var (ctx : eval_ctx) (v : typed_value) : eval_ctx =
- { ctx with env = Var (None, v) :: ctx.env }
-
-(** Pop the first dummy variable from a context's environment. *)
-let ctx_pop_dummy_var (ctx : eval_ctx) : eval_ctx * typed_value =
- let rec pop_var (env : env) : env * typed_value =
- match env with
- | [] -> failwith "Could not find a dummy variable"
- | Var (None, v) :: env -> (env, v)
- | ee :: env ->
- let env, v = pop_var env in
- (ee :: env, v)
- in
- let env, v = pop_var ctx.env in
- ({ ctx with env }, v)
-
-(** Read the first dummy variable in a context's environment. *)
-let ctx_read_first_dummy_var (ctx : eval_ctx) : typed_value =
- let rec read_var (env : env) : typed_value =
- match env with
- | [] -> failwith "Could not find a dummy variable"
- | Var (None, v) :: _env -> v
- | _ :: env -> read_var env
- in
- read_var ctx.env
-
-(** Push an uninitialized variable (which thus maps to {!Values.Bottom}) *)
-let ctx_push_uninitialized_var (ctx : eval_ctx) (var : var) : eval_ctx =
- ctx_push_var ctx var (mk_bottom var.var_ty)
-
-(** Push a list of uninitialized variables (which thus map to {!Values.Bottom}) *)
-let ctx_push_uninitialized_vars (ctx : eval_ctx) (vars : var list) : eval_ctx =
- let vars = List.map (fun v -> (v, mk_bottom v.var_ty)) vars in
- ctx_push_vars ctx vars
-
-let env_lookup_abs (env : env) (abs_id : V.AbstractionId.id) : V.abs =
- let rec lookup env =
- match env with
- | [] -> failwith "Unexpected"
- | Var (_, _) :: env' -> lookup env'
- | Abs abs :: env' -> if abs.abs_id = abs_id then abs else lookup env'
- | Frame :: env' -> lookup env'
- in
- lookup env
-
-let ctx_lookup_abs (ctx : eval_ctx) (abs_id : V.AbstractionId.id) : V.abs =
- env_lookup_abs ctx.env abs_id
-
-let ctx_type_decl_is_rec (ctx : eval_ctx) (id : TypeDeclId.id) : bool =
- let decl_group = TypeDeclId.Map.find id ctx.type_context.type_decls_groups in
- match decl_group with Crates.Rec _ -> true | Crates.NonRec _ -> false
-
-(** Visitor to iterate over the values in the *current* frame *)
-class ['self] iter_frame =
- object (self : 'self)
- inherit [_] V.iter_abs
-
- method visit_Var : 'acc -> binder option -> typed_value -> unit =
- fun acc _vid v -> self#visit_typed_value acc v
-
- method visit_Abs : 'acc -> abs -> unit =
- fun acc abs -> self#visit_abs acc abs
-
- method visit_env_elem : 'acc -> env_elem -> unit =
- fun acc em ->
- match em with
- | Var (vid, v) -> self#visit_Var acc vid v
- | Abs abs -> self#visit_Abs acc abs
- | Frame -> failwith "Unreachable"
-
- method visit_env : 'acc -> env -> unit =
- fun acc env ->
- match env with
- | [] -> ()
- | Frame :: _ -> (* We stop here *) ()
- | em :: env ->
- self#visit_env_elem acc em;
- self#visit_env acc env
- end
-
-(** Visitor to map over the values in the *current* frame *)
-class ['self] map_frame_concrete =
- object (self : 'self)
- inherit [_] V.map_abs
-
- method visit_Var : 'acc -> binder option -> typed_value -> env_elem =
- fun acc vid v ->
- let v = self#visit_typed_value acc v in
- Var (vid, v)
-
- method visit_Abs : 'acc -> abs -> env_elem =
- fun acc abs -> Abs (self#visit_abs acc abs)
-
- method visit_env_elem : 'acc -> env_elem -> env_elem =
- fun acc em ->
- match em with
- | Var (vid, v) -> self#visit_Var acc vid v
- | Abs abs -> self#visit_Abs acc abs
- | Frame -> failwith "Unreachable"
-
- method visit_env : 'acc -> env -> env =
- fun acc env ->
- match env with
- | [] -> []
- | Frame :: env -> (* We stop here *) Frame :: env
- | em :: env ->
- let em = self#visit_env_elem acc em in
- let env = self#visit_env acc env in
- em :: env
- end
-
-(** Visitor to iterate over the values in a context *)
-class ['self] iter_eval_ctx =
- object (_self : 'self)
- inherit [_] iter_env as super
-
- method visit_eval_ctx : 'acc -> eval_ctx -> unit =
- fun acc ctx -> super#visit_env acc ctx.env
- end
-
-(** Visitor to map the values in a context *)
-class ['self] map_eval_ctx =
- object (_self : 'self)
- inherit [_] map_env as super
-
- method visit_eval_ctx : 'acc -> eval_ctx -> eval_ctx =
- fun acc ctx ->
- let env = super#visit_env acc ctx.env in
- { ctx with env }
- end
diff --git a/src/Cps.ml b/src/Cps.ml
deleted file mode 100644
index c2c0363b..00000000
--- a/src/Cps.ml
+++ /dev/null
@@ -1,193 +0,0 @@
-(** This module defines various utilities to write the interpretation functions
- in continuation passing style. *)
-
-module T = Types
-module V = Values
-module C = Contexts
-module SA = SymbolicAst
-
-(** TODO: change the name *)
-type eval_error = EPanic
-
-(** Result of evaluating a statement *)
-type statement_eval_res =
- | Unit
- | Break of int
- | Continue of int
- | Return
- | Panic
-
-(** Synthesized expresssion - dummy for now *)
-type sexpr = SOne | SList of sexpr list
-
-type eval_result = SA.expression option
-
-(** Continuation function *)
-type m_fun = C.eval_ctx -> eval_result
-
-(** Continuation taking another continuation as parameter *)
-type cm_fun = m_fun -> m_fun
-
-(** Continuation taking a typed value as parameter - TODO: use more *)
-type typed_value_m_fun = V.typed_value -> m_fun
-
-(** Continuation taking another continuation as parameter and a typed
- value as parameter.
- *)
-type typed_value_cm_fun = V.typed_value -> cm_fun
-
-(** Type of a continuation used when evaluating a statement *)
-type st_m_fun = statement_eval_res -> m_fun
-
-(** Type of a continuation used when evaluating a statement *)
-type st_cm_fun = st_m_fun -> m_fun
-
-(** Convert a unit function to a cm function *)
-let unit_to_cm_fun (f : C.eval_ctx -> unit) : cm_fun =
- fun cf ctx ->
- f ctx;
- cf ctx
-
-(** *)
-let update_to_cm_fun (f : C.eval_ctx -> C.eval_ctx) : cm_fun =
- fun cf ctx ->
- let ctx = f ctx in
- cf ctx
-
-(** Composition of functions taking continuations as parameters.
- We tried to make this as general as possible. *)
-let comp (f : 'c -> 'd -> 'e) (g : ('a -> 'b) -> 'c) : ('a -> 'b) -> 'd -> 'e =
- fun cf ctx -> f (g cf) ctx
-
-let comp_unit (f : cm_fun) (g : C.eval_ctx -> unit) : cm_fun =
- comp f (unit_to_cm_fun g)
-
-let comp_update (f : cm_fun) (g : C.eval_ctx -> C.eval_ctx) : cm_fun =
- comp f (update_to_cm_fun g)
-
-(** This is just a test, to check that {!comp} is general enough to handle a case
- where a function must compute a value and give it to the continuation.
- It happens for functions like {!InterpreterExpressions.eval_operand}.
-
- Keeping this here also makes it a good reference, when one wants to figure
- out the signatures he should use for such a composition.
- *)
-let comp_ret_val (f : (V.typed_value -> m_fun) -> m_fun)
- (g : m_fun -> V.typed_value -> m_fun) : cm_fun =
- comp f g
-
-let apply (f : cm_fun) (g : m_fun) : m_fun = fun ctx -> f g ctx
-let id_cm_fun : cm_fun = fun cf ctx -> cf ctx
-
-(** If we have a list of [inputs] of type ['a list] and a function [f] which
- evaluates one element of type ['a] to compute a result of type ['b] before
- giving it to a continuation, the following function performs a fold operation:
- it evaluates all the inputs one by one by accumulating the results in a list,
- and gives the list to a continuation.
-
- Note that we make sure that the results are listed in the order in
- which they were computed (the first element of the list is the result
- of applying [f] to the first element of the inputs).
-
- See the unit test below for an illustration.
- *)
-let fold_left_apply_continuation (f : 'a -> ('c -> 'd) -> 'c -> 'd)
- (inputs : 'a list) (cf : 'c -> 'd) : 'c -> 'd =
- let rec eval_list (inputs : 'a list) (cf : 'c -> 'd) : 'c -> 'd =
- fun ctx ->
- match inputs with
- | [] -> cf ctx
- | x :: inputs -> comp (f x) (fun cf -> eval_list inputs cf) cf ctx
- in
- eval_list inputs cf
-
-(** Unit test/example for {!fold_left_apply_continuation} *)
-let _ =
- fold_left_apply_continuation
- (fun x cf (ctx : int) -> cf (ctx + x))
- [ 1; 20; 300; 4000 ]
- (fun (ctx : int) -> assert (ctx = 4321))
- 0
-
-(** If we have a list of [inputs] of type ['a list] and a function [f] which
- evaluates one element of type ['a] to compute a result of type ['b] before
- giving it to a continuation, the following function performs a fold operation:
- it evaluates all the inputs one by one by accumulating the results in a list,
- and gives the list to a continuation.
-
- Note that we make sure that the results are listed in the order in
- which they were computed (the first element of the list is the result
- of applying [f] to the first element of the inputs).
-
- See the unit test below for an illustration.
- *)
-let fold_left_list_apply_continuation (f : 'a -> ('b -> 'c -> 'd) -> 'c -> 'd)
- (inputs : 'a list) (cf : 'b list -> 'c -> 'd) : 'c -> 'd =
- let rec eval_list (inputs : 'a list) (cf : 'b list -> 'c -> 'd)
- (outputs : 'b list) : 'c -> 'd =
- fun ctx ->
- match inputs with
- | [] -> cf (List.rev outputs) ctx
- | x :: inputs ->
- comp (f x) (fun cf v -> eval_list inputs cf (v :: outputs)) cf ctx
- in
- eval_list inputs cf []
-
-(** Unit test/example for {!fold_left_list_apply_continuation} *)
-let _ =
- fold_left_list_apply_continuation
- (fun x cf (ctx : unit) -> cf (10 + x) ctx)
- [ 0; 1; 2; 3; 4 ]
- (fun values _ctx -> assert (values = [ 10; 11; 12; 13; 14 ]))
- ()
-
-(** Composition of functions taking continuations as parameters.
-
- We sometimes have the following situation, where we want to compose three
- functions [send], [transmit] and [receive] such that:
- - those three functions take continuations as parameters
- - [send] generates a value and gives it to its continuation
- - [receive] expects a value (so we can compose [send] and [receive] like
- so: [comp send receive])
- - [transmit] doesn't expect any value and needs to be called between [send]
- and [receive]
-
- In this situation, we need to take the value given by [send] and "transmit"
- it to [receive].
-
- This is what this function does (see the unit test below for an illustration).
- *)
-let comp_transmit (f : ('v -> 'm) -> 'n) (g : 'm -> 'm) : ('v -> 'm) -> 'n =
- fun cf -> f (fun v -> g (cf v))
-
-(** Example of use of {!comp_transmit} *)
-let () =
- let return3 (cf : int -> unit -> unit) (ctx : unit) = cf 3 ctx in
- let do_nothing (cf : unit -> unit) (ctx : unit) = cf ctx in
- let consume3 (x : int) (ctx : unit) : unit =
- assert (x = 3);
- ctx
- in
- let cc = comp_transmit return3 do_nothing in
- let cc = cc consume3 in
- cc ()
-
-(** Sometimes, we want to compose a function with a continuation which checks
- its computed value and its updated context, before transmitting them
- *)
-let comp_check_value (f : ('v -> 'ctx -> 'a) -> 'ctx -> 'b)
- (g : 'v -> 'ctx -> unit) : ('v -> 'ctx -> 'a) -> 'ctx -> 'b =
- fun cf ->
- f (fun v ctx ->
- g v ctx;
- cf v ctx)
-
-(** This case is similar to {!comp_check_value}, but even simpler (we only check
- the context)
- *)
-let comp_check_ctx (f : ('ctx -> 'a) -> 'ctx -> 'b) (g : 'ctx -> unit) :
- ('ctx -> 'a) -> 'ctx -> 'b =
- fun cf ->
- f (fun ctx ->
- g ctx;
- cf ctx)
diff --git a/src/Crates.ml b/src/Crates.ml
deleted file mode 100644
index 844afb94..00000000
--- a/src/Crates.ml
+++ /dev/null
@@ -1,90 +0,0 @@
-open Types
-open LlbcAst
-
-type 'id g_declaration_group = NonRec of 'id | Rec of 'id list
-[@@deriving show]
-
-type type_declaration_group = TypeDeclId.id g_declaration_group
-[@@deriving show]
-
-type fun_declaration_group = FunDeclId.id g_declaration_group [@@deriving show]
-
-(** Module declaration. Globals cannot be mutually recursive. *)
-type declaration_group =
- | Type of type_declaration_group
- | Fun of fun_declaration_group
- | Global of GlobalDeclId.id
-[@@deriving show]
-
-type llbc_crate = {
- name : string;
- declarations : declaration_group list;
- types : type_decl list;
- functions : fun_decl list;
- globals : global_decl list;
-}
-(** LLBC crate *)
-
-let compute_defs_maps (c : llbc_crate) :
- type_decl TypeDeclId.Map.t
- * fun_decl FunDeclId.Map.t
- * global_decl GlobalDeclId.Map.t =
- let types_map =
- List.fold_left
- (fun m (def : type_decl) -> TypeDeclId.Map.add def.def_id def m)
- TypeDeclId.Map.empty c.types
- in
- let funs_map =
- List.fold_left
- (fun m (def : fun_decl) -> FunDeclId.Map.add def.def_id def m)
- FunDeclId.Map.empty c.functions
- in
- let globals_map =
- List.fold_left
- (fun m (def : global_decl) -> GlobalDeclId.Map.add def.def_id def m)
- GlobalDeclId.Map.empty c.globals
- in
- (types_map, funs_map, globals_map)
-
-(** Split a module's declarations between types, functions and globals *)
-let split_declarations (decls : declaration_group list) :
- type_declaration_group list
- * fun_declaration_group list
- * GlobalDeclId.id list =
- let rec split decls =
- match decls with
- | [] -> ([], [], [])
- | d :: decls' -> (
- let types, funs, globals = split decls' in
- match d with
- | Type decl -> (decl :: types, funs, globals)
- | Fun decl -> (types, decl :: funs, globals)
- | Global decl -> (types, funs, decl :: globals))
- in
- split decls
-
-(** Split a module's declarations into three maps from type/fun/global ids to
- declaration groups.
- *)
-let split_declarations_to_group_maps (decls : declaration_group list) :
- type_declaration_group TypeDeclId.Map.t
- * fun_declaration_group FunDeclId.Map.t
- * GlobalDeclId.Set.t =
- let module G (M : Map.S) = struct
- let add_group (map : M.key g_declaration_group M.t)
- (group : M.key g_declaration_group) : M.key g_declaration_group M.t =
- match group with
- | NonRec id -> M.add id group map
- | Rec ids -> List.fold_left (fun map id -> M.add id group map) map ids
-
- let create_map (groups : M.key g_declaration_group list) :
- M.key g_declaration_group M.t =
- List.fold_left add_group M.empty groups
- end in
- let types, funs, globals = split_declarations decls in
- let module TG = G (TypeDeclId.Map) in
- let types = TG.create_map types in
- let module FG = G (FunDeclId.Map) in
- let funs = FG.create_map funs in
- let globals = GlobalDeclId.Set.of_list globals in
- (types, funs, globals)
diff --git a/src/Errors.ml b/src/Errors.ml
deleted file mode 100644
index 31a53cf4..00000000
--- a/src/Errors.ml
+++ /dev/null
@@ -1,2 +0,0 @@
-exception IntegerOverflow of unit
-exception Unimplemented
diff --git a/src/Expressions.ml b/src/Expressions.ml
deleted file mode 100644
index e2eaf1e7..00000000
--- a/src/Expressions.ml
+++ /dev/null
@@ -1,118 +0,0 @@
-open Types
-open Values
-
-type field_proj_kind =
- | ProjAdt of TypeDeclId.id * VariantId.id option
- | ProjOption of VariantId.id
- (** Option is an assumed type, coming from the standard library *)
- | ProjTuple of int
-[@@deriving show]
-(* arity of the tuple *)
-
-type projection_elem =
- | Deref
- | DerefBox
- | Field of field_proj_kind * FieldId.id
-[@@deriving show]
-
-type projection = projection_elem list [@@deriving show]
-type place = { var_id : VarId.id; projection : projection } [@@deriving show]
-type borrow_kind = Shared | Mut | TwoPhaseMut [@@deriving show]
-
-type unop =
- | Not
- | Neg
- | Cast of integer_type * integer_type
- (** Cast an integer from a source type to a target type *)
-[@@deriving show, ord]
-
-(** A binary operation
-
- Note that we merge checked binops and unchecked binops: we perform a
- micro-pass on the MIR AST to remove the assertions introduced by rustc,
- and later extract the binops which can fail (addition, substraction, etc.)
- or have preconditions (division, remainder...) to monadic functions.
- *)
-type binop =
- | BitXor
- | BitAnd
- | BitOr
- | Eq
- | Lt
- | Le
- | Ne
- | Ge
- | Gt
- | Div
- | Rem
- | Add
- | Sub
- | Mul
- | Shl
- | Shr
-[@@deriving show, ord]
-
-let all_binops =
- [
- BitXor;
- BitAnd;
- BitOr;
- Eq;
- Lt;
- Le;
- Ne;
- Ge;
- Gt;
- Div;
- Rem;
- Add;
- Sub;
- Mul;
- Shl;
- Shr;
- ]
-
-type operand =
- | Copy of place
- | Move of place
- | Constant of ety * constant_value
-[@@deriving show]
-
-(** An aggregated ADT.
-
- Note that ADTs are desaggregated at some point in MIR. For instance, if
- we have in Rust:
- {[
- let ls = Cons(hd, tl);
- ]}
-
- In MIR we have (yes, the discriminant update happens *at the end* for some
- reason):
- {[
- (ls as Cons).0 = move hd;
- (ls as Cons).1 = move tl;
- discriminant(ls) = 0; // assuming [Cons] is the variant of index 0
- ]}
-
- Note that in our semantics, we handle both cases (in case of desaggregated
- initialization, [ls] is initialized to [⊥], then this [⊥] is expanded to
- [Cons (⊥, ⊥)] upon the first assignment, at which point we can initialize
- the field 0, etc.).
- *)
-type aggregate_kind =
- | AggregatedTuple
- | AggregatedOption of VariantId.id * ety
- (* TODO: AggregatedOption should be merged with AggregatedAdt *)
- | AggregatedAdt of
- TypeDeclId.id * VariantId.id option * erased_region list * ety list
-[@@deriving show]
-
-(* TODO: move the aggregate kind to operands *)
-type rvalue =
- | Use of operand
- | Ref of place * borrow_kind
- | UnaryOp of unop * operand
- | BinaryOp of binop * operand * operand
- | Discriminant of place
- | Aggregate of aggregate_kind * operand list
-[@@deriving show]
diff --git a/src/ExpressionsUtils.ml b/src/ExpressionsUtils.ml
deleted file mode 100644
index c3ccfb15..00000000
--- a/src/ExpressionsUtils.ml
+++ /dev/null
@@ -1,10 +0,0 @@
-module E = Expressions
-
-let unop_can_fail (unop : E.unop) : bool =
- match unop with Neg | Cast _ -> true | Not -> false
-
-let binop_can_fail (binop : E.binop) : bool =
- match binop with
- | BitXor | BitAnd | BitOr | Eq | Lt | Le | Ne | Ge | Gt -> false
- | Div | Rem | Add | Sub | Mul -> true
- | Shl | Shr -> raise Errors.Unimplemented
diff --git a/src/ExtractToFStar.ml b/src/ExtractToFStar.ml
deleted file mode 100644
index 5d212941..00000000
--- a/src/ExtractToFStar.ml
+++ /dev/null
@@ -1,1638 +0,0 @@
-(** Extract to F* *)
-
-open Errors
-open Pure
-open PureUtils
-open TranslateCore
-open PureToExtract
-open StringUtils
-module F = Format
-
-(** A qualifier for a type definition.
-
- Controls whether we should use [type ...] or [and ...] (for mutually
- recursive datatypes).
- *)
-type type_decl_qualif =
- | Type (** [type t = ...] *)
- | And (** [type t0 = ... and t1 = ...] *)
- | AssumeType (** [assume type t] *)
- | TypeVal (** In an fsti: [val t : Type0] *)
-
-(** A qualifier for function definitions.
-
- Controls whether we should use [let ...], [let rec ...] or [and ...],
- or only generate a declaration with [val] or [assume val]
- *)
-type fun_decl_qualif = Let | LetRec | And | Val | AssumeVal
-
-let fun_decl_qualif_keyword (qualif : fun_decl_qualif) : string =
- match qualif with
- | Let -> "let"
- | LetRec -> "let rec"
- | And -> "and"
- | Val -> "val"
- | AssumeVal -> "assume val"
-
-(** Small helper to compute the name of an int type *)
-let fstar_int_name (int_ty : integer_type) =
- match int_ty with
- | Isize -> "isize"
- | I8 -> "i8"
- | I16 -> "i16"
- | I32 -> "i32"
- | I64 -> "i64"
- | I128 -> "i128"
- | Usize -> "usize"
- | U8 -> "u8"
- | U16 -> "u16"
- | U32 -> "u32"
- | U64 -> "u64"
- | U128 -> "u128"
-
-(** Small helper to compute the name of a unary operation *)
-let fstar_unop_name (unop : unop) : string =
- match unop with
- | Not -> "not"
- | Neg int_ty -> fstar_int_name int_ty ^ "_neg"
- | Cast _ -> raise (Failure "Unsupported")
-
-(** Small helper to compute the name of a binary operation (note that many
- binary operations like "less than" are extracted to primitive operations,
- like [<].
- *)
-let fstar_named_binop_name (binop : E.binop) (int_ty : integer_type) : string =
- let binop =
- match binop with
- | Div -> "div"
- | Rem -> "rem"
- | Add -> "add"
- | Sub -> "sub"
- | Mul -> "mul"
- | _ -> raise (Failure "Unreachable")
- in
- fstar_int_name int_ty ^ "_" ^ binop
-
-(** A list of keywords/identifiers used in F* and with which we want to check
- collision. *)
-let fstar_keywords =
- let named_unops =
- fstar_unop_name Not
- :: List.map (fun it -> fstar_unop_name (Neg it)) T.all_signed_int_types
- in
- let named_binops = [ E.Div; Rem; Add; Sub; Mul ] in
- let named_binops =
- List.concat
- (List.map
- (fun bn ->
- List.map (fun it -> fstar_named_binop_name bn it) T.all_int_types)
- named_binops)
- in
- let misc =
- [
- "let";
- "rec";
- "in";
- "fn";
- "val";
- "int";
- "nat";
- "list";
- "FStar";
- "FStar.Mul";
- "type";
- "match";
- "with";
- "assert";
- "assert_norm";
- "Type0";
- "unit";
- "not";
- "scalar_cast";
- ]
- in
- List.concat [ named_unops; named_binops; misc ]
-
-let fstar_assumed_adts : (assumed_ty * string) list =
- [ (State, "state"); (Result, "result"); (Option, "option"); (Vec, "vec") ]
-
-let fstar_assumed_structs : (assumed_ty * string) list = []
-
-let fstar_assumed_variants : (assumed_ty * VariantId.id * string) list =
- [
- (Result, result_return_id, "Return");
- (Result, result_fail_id, "Fail");
- (Option, option_some_id, "Some");
- (Option, option_none_id, "None");
- ]
-
-let fstar_assumed_functions :
- (A.assumed_fun_id * T.RegionGroupId.id option * string) list =
- let rg0 = Some T.RegionGroupId.zero in
- [
- (Replace, None, "mem_replace_fwd");
- (Replace, rg0, "mem_replace_back");
- (VecNew, None, "vec_new");
- (VecPush, None, "vec_push_fwd") (* Shouldn't be used *);
- (VecPush, rg0, "vec_push_back");
- (VecInsert, None, "vec_insert_fwd") (* Shouldn't be used *);
- (VecInsert, rg0, "vec_insert_back");
- (VecLen, None, "vec_len");
- (VecIndex, None, "vec_index_fwd");
- (VecIndex, rg0, "vec_index_back") (* shouldn't be used *);
- (VecIndexMut, None, "vec_index_mut_fwd");
- (VecIndexMut, rg0, "vec_index_mut_back");
- ]
-
-let fstar_names_map_init =
- {
- keywords = fstar_keywords;
- assumed_adts = fstar_assumed_adts;
- assumed_structs = fstar_assumed_structs;
- assumed_variants = fstar_assumed_variants;
- assumed_functions = fstar_assumed_functions;
- }
-
-let fstar_extract_unop (extract_expr : bool -> texpression -> unit)
- (fmt : F.formatter) (inside : bool) (unop : unop) (arg : texpression) : unit
- =
- match unop with
- | Not | Neg _ ->
- let unop = fstar_unop_name unop in
- if inside then F.pp_print_string fmt "(";
- F.pp_print_string fmt unop;
- F.pp_print_space fmt ();
- extract_expr true arg;
- if inside then F.pp_print_string fmt ")"
- | Cast (src, tgt) ->
- (* The source type is an implicit parameter *)
- if inside then F.pp_print_string fmt "(";
- F.pp_print_string fmt "scalar_cast";
- F.pp_print_space fmt ();
- F.pp_print_string fmt
- (StringUtils.capitalize_first_letter
- (PrintPure.integer_type_to_string src));
- F.pp_print_space fmt ();
- F.pp_print_string fmt
- (StringUtils.capitalize_first_letter
- (PrintPure.integer_type_to_string tgt));
- F.pp_print_space fmt ();
- extract_expr true arg;
- if inside then F.pp_print_string fmt ")"
-
-let fstar_extract_binop (extract_expr : bool -> texpression -> unit)
- (fmt : F.formatter) (inside : bool) (binop : E.binop)
- (int_ty : integer_type) (arg0 : texpression) (arg1 : texpression) : unit =
- if inside then F.pp_print_string fmt "(";
- (* Some binary operations have a special treatment *)
- (match binop with
- | Eq | Lt | Le | Ne | Ge | Gt ->
- let binop =
- match binop with
- | Eq -> "="
- | Lt -> "<"
- | Le -> "<="
- | Ne -> "<>"
- | Ge -> ">="
- | Gt -> ">"
- | _ -> raise (Failure "Unreachable")
- in
- extract_expr false arg0;
- F.pp_print_space fmt ();
- F.pp_print_string fmt binop;
- F.pp_print_space fmt ();
- extract_expr false arg1
- | Div | Rem | Add | Sub | Mul ->
- let binop = fstar_named_binop_name binop int_ty in
- F.pp_print_string fmt binop;
- F.pp_print_space fmt ();
- extract_expr false arg0;
- F.pp_print_space fmt ();
- extract_expr false arg1
- | BitXor | BitAnd | BitOr | Shl | Shr -> raise Unimplemented);
- if inside then F.pp_print_string fmt ")"
-
-(**
- [ctx]: we use the context to lookup type definitions, to retrieve type names.
- This is used to compute variable names, when they have no basenames: in this
- case we use the first letter of the type name.
-
- [variant_concatenate_type_name]: if true, add the type name as a prefix
- to the variant names.
- Ex.:
- In Rust:
- {[
- enum List = {
- Cons(u32, Box<List>),x
- Nil,
- }
- ]}
-
- F*, if option activated:
- {[
- type list =
- | ListCons : u32 -> list -> list
- | ListNil : list
- ]}
-
- F*, if option not activated:
- {[
- type list =
- | Cons : u32 -> list -> list
- | Nil : list
- ]}
-
- Rk.: this should be true by default, because in Rust all the variant names
- are actively uniquely identifier by the type name [List::Cons(...)], while
- in other languages it is not necessarily the case, and thus clashes can mess
- up type checking. Note that some languages actually forbids the name clashes
- (it is the case of F* ).
- *)
-let mk_formatter (ctx : trans_ctx) (crate_name : string)
- (variant_concatenate_type_name : bool) : formatter =
- let int_name = fstar_int_name in
-
- (* Prepare a name.
- * The first id elem is always the crate: if it is the local crate,
- * we remove it.
- * We also remove all the disambiguators, then convert everything to strings.
- * **Rmk:** because we remove the disambiguators, there may be name collisions
- * (which is ok, because we check for name collisions and fail if there is any).
- *)
- let get_name (name : name) : string list =
- (* Rmk.: initially we only filtered the disambiguators equal to 0 *)
- let name = Names.filter_disambiguators name in
- match name with
- | Ident crate :: name ->
- let name = if crate = crate_name then name else Ident crate :: name in
- let name =
- List.map
- (function
- | Names.Ident s -> s
- | Disambiguator d -> Names.Disambiguator.to_string d)
- name
- in
- name
- | _ ->
- raise (Failure ("Unexpected name shape: " ^ Print.name_to_string name))
- in
- let get_type_name = get_name in
- let type_name_to_camel_case name =
- let name = get_type_name name in
- let name = List.map to_camel_case name in
- String.concat "" name
- in
- let type_name_to_snake_case name =
- let name = get_type_name name in
- let name = List.map to_snake_case name in
- String.concat "_" name
- in
- let type_name name = type_name_to_snake_case name ^ "_t" in
- let field_name (def_name : name) (field_id : FieldId.id)
- (field_name : string option) : string =
- let def_name = type_name_to_snake_case def_name ^ "_" in
- match field_name with
- | Some field_name -> def_name ^ field_name
- | None -> def_name ^ FieldId.to_string field_id
- in
- let variant_name (def_name : name) (variant : string) : string =
- let variant = to_camel_case variant in
- if variant_concatenate_type_name then
- type_name_to_camel_case def_name ^ variant
- else variant
- in
- let struct_constructor (basename : name) : string =
- let tname = type_name basename in
- "Mk" ^ tname
- in
- let get_fun_name = get_name in
- let fun_name_to_snake_case (fname : fun_name) : string =
- let fname = get_fun_name fname in
- (* Converting to snake case should be a no-op, but it doesn't cost much *)
- let fname = List.map to_snake_case fname in
- (* Concatenate the elements *)
- String.concat "_" fname
- in
- let global_name (name : global_name) : string =
- (* Converting to snake case also lowercases the letters (in Rust, global
- * names are written in capital letters). *)
- let parts = List.map to_snake_case (get_name name) in
- String.concat "_" parts
- in
- let fun_name (_fid : A.fun_id) (fname : fun_name) (num_rgs : int)
- (rg : region_group_info option) (filter_info : bool * int) : string =
- let fname = fun_name_to_snake_case fname in
- (* Compute the suffix *)
- let suffix = default_fun_suffix num_rgs rg filter_info in
- (* Concatenate *)
- fname ^ suffix
- in
-
- let decreases_clause_name (_fid : A.FunDeclId.id) (fname : fun_name) : string
- =
- let fname = fun_name_to_snake_case fname in
- (* Compute the suffix *)
- let suffix = "_decreases" in
- (* Concatenate *)
- fname ^ suffix
- in
-
- let var_basename (_varset : StringSet.t) (basename : string option) (ty : ty)
- : string =
- (* If there is a basename, we use it *)
- match basename with
- | Some basename ->
- (* This should be a no-op *)
- to_snake_case basename
- | None -> (
- (* No basename: we use the first letter of the type *)
- match ty with
- | Adt (type_id, tys) -> (
- match type_id with
- | Tuple ->
- (* The "pair" case is frequent enough to have its special treatment *)
- if List.length tys = 2 then "p" else "t"
- | Assumed Result -> "r"
- | Assumed Option -> "opt"
- | Assumed Vec -> "v"
- | Assumed State -> "st"
- | AdtId adt_id ->
- let def =
- TypeDeclId.Map.find adt_id ctx.type_context.type_decls
- in
- (* We do the following:
- * - compute the type name, and retrieve the last ident
- * - convert this to snake case
- * - take the first letter of every "letter group"
- * Ex.: ["hashmap"; "HashMap"] ~~> "HashMap" -> "hash_map" -> "hm"
- *)
- (* Thename shouldn't be empty, and its last element should
- * be an ident *)
- let cl = List.nth def.name (List.length def.name - 1) in
- let cl = to_snake_case (Names.as_ident cl) in
- let cl = String.split_on_char '_' cl in
- let cl = List.filter (fun s -> String.length s > 0) cl in
- assert (List.length cl > 0);
- let cl = List.map (fun s -> s.[0]) cl in
- StringUtils.string_of_chars cl)
- | TypeVar _ -> "x" (* lacking imagination here... *)
- | Bool -> "b"
- | Char -> "c"
- | Integer _ -> "i"
- | Str -> "s"
- | Arrow _ -> "f"
- | Array _ | Slice _ -> raise Unimplemented)
- in
- let type_var_basename (_varset : StringSet.t) (basename : string) : string =
- (* This is *not* a no-op: type variables in Rust often start with
- * a capital letter *)
- to_snake_case basename
- in
- let append_index (basename : string) (i : int) : string =
- basename ^ string_of_int i
- in
-
- let extract_constant_value (fmt : F.formatter) (_inside : bool)
- (cv : constant_value) : unit =
- match cv with
- | Scalar sv -> F.pp_print_string fmt (Z.to_string sv.V.value)
- | Bool b ->
- let b = if b then "true" else "false" in
- F.pp_print_string fmt b
- | Char c -> F.pp_print_string fmt ("'" ^ String.make 1 c ^ "'")
- | String s ->
- (* We need to replace all the line breaks *)
- let s =
- StringUtils.map
- (fun c -> if c = '\n' then "\n" else String.make 1 c)
- s
- in
- F.pp_print_string fmt ("\"" ^ s ^ "\"")
- in
- {
- bool_name = "bool";
- char_name = "char";
- int_name;
- str_name = "string";
- field_name;
- variant_name;
- struct_constructor;
- type_name;
- global_name;
- fun_name;
- decreases_clause_name;
- var_basename;
- type_var_basename;
- append_index;
- extract_constant_value;
- extract_unop = fstar_extract_unop;
- extract_binop = fstar_extract_binop;
- }
-
-(** [inside] constrols whether we should add parentheses or not around type
- application (if [true] we add parentheses).
- *)
-let rec extract_ty (ctx : extraction_ctx) (fmt : F.formatter) (inside : bool)
- (ty : ty) : unit =
- match ty with
- | Adt (type_id, tys) -> (
- match type_id with
- | Tuple ->
- (* This is a bit annoying, but in F* [()] is not the unit type:
- * we have to write [unit]... *)
- if tys = [] then F.pp_print_string fmt "unit"
- else (
- F.pp_print_string fmt "(";
- Collections.List.iter_link
- (fun () ->
- F.pp_print_space fmt ();
- F.pp_print_string fmt "&";
- F.pp_print_space fmt ())
- (extract_ty ctx fmt true) tys;
- F.pp_print_string fmt ")")
- | AdtId _ | Assumed _ ->
- let print_paren = inside && tys <> [] in
- if print_paren then F.pp_print_string fmt "(";
- F.pp_print_string fmt (ctx_get_type type_id ctx);
- if tys <> [] then F.pp_print_space fmt ();
- Collections.List.iter_link (F.pp_print_space fmt)
- (extract_ty ctx fmt true) tys;
- if print_paren then F.pp_print_string fmt ")")
- | TypeVar vid -> F.pp_print_string fmt (ctx_get_type_var vid ctx)
- | Bool -> F.pp_print_string fmt ctx.fmt.bool_name
- | Char -> F.pp_print_string fmt ctx.fmt.char_name
- | Integer int_ty -> F.pp_print_string fmt (ctx.fmt.int_name int_ty)
- | Str -> F.pp_print_string fmt ctx.fmt.str_name
- | Arrow (arg_ty, ret_ty) ->
- if inside then F.pp_print_string fmt "(";
- extract_ty ctx fmt false arg_ty;
- F.pp_print_space fmt ();
- F.pp_print_string fmt "->";
- F.pp_print_space fmt ();
- extract_ty ctx fmt false ret_ty;
- if inside then F.pp_print_string fmt ")"
- | Array _ | Slice _ -> raise Unimplemented
-
-(** Compute the names for all the top-level identifiers used in a type
- definition (type name, variant names, field names, etc. but not type
- parameters).
-
- We need to do this preemptively, beforce extracting any definition,
- because of recursive definitions.
- *)
-let extract_type_decl_register_names (ctx : extraction_ctx) (def : type_decl) :
- extraction_ctx =
- (* Compute and register the type def name *)
- let ctx = ctx_add_type_decl def ctx in
- (* Compute and register:
- * - the variant names, if this is an enumeration
- * - the field names, if this is a structure
- *)
- let ctx =
- match def.kind with
- | Struct fields ->
- (* Add the fields *)
- let ctx =
- fst
- (ctx_add_fields def (FieldId.mapi (fun id f -> (id, f)) fields) ctx)
- in
- (* Add the constructor name *)
- fst (ctx_add_struct def ctx)
- | Enum variants ->
- fst
- (ctx_add_variants def
- (VariantId.mapi (fun id v -> (id, v)) variants)
- ctx)
- | Opaque ->
- (* Nothing to do *)
- ctx
- in
- (* Return *)
- ctx
-
-let extract_type_decl_struct_body (ctx : extraction_ctx) (fmt : F.formatter)
- (def : type_decl) (fields : field list) : unit =
- (* We want to generate a definition which looks like this:
- {[
- type t = { x : int; y : bool; }
- ]}
-
- If there isn't enough space on one line:
- {[
- type t =
- {
- x : int; y : bool;
- }
- ]}
-
- And if there is even less space:
- {[
- type t =
- {
- x : int;
- y : bool;
- }
- ]}
-
- Also, in case there are no fields, we need to define the type as [unit]
- ([type t = {}] doesn't work in F* ).
- *)
- (* Note that we already printed: [type t =] *)
- if fields = [] then (
- F.pp_print_space fmt ();
- F.pp_print_string fmt "unit")
- else (
- F.pp_print_space fmt ();
- F.pp_print_string fmt "{";
- F.pp_print_break fmt 1 ctx.indent_incr;
- (* The body itself *)
- F.pp_open_hvbox fmt 0;
- (* Print the fields *)
- let print_field (field_id : FieldId.id) (f : field) : unit =
- let field_name = ctx_get_field (AdtId def.def_id) field_id ctx in
- F.pp_open_box fmt ctx.indent_incr;
- F.pp_print_string fmt field_name;
- F.pp_print_space fmt ();
- F.pp_print_string fmt ":";
- F.pp_print_space fmt ();
- extract_ty ctx fmt false f.field_ty;
- F.pp_print_string fmt ";";
- F.pp_close_box fmt ()
- in
- let fields = FieldId.mapi (fun fid f -> (fid, f)) fields in
- Collections.List.iter_link (F.pp_print_space fmt)
- (fun (fid, f) -> print_field fid f)
- fields;
- (* Close *)
- F.pp_close_box fmt ();
- F.pp_print_space fmt ();
- F.pp_print_string fmt "}")
-
-let extract_type_decl_enum_body (ctx : extraction_ctx) (fmt : F.formatter)
- (def : type_decl) (def_name : string) (type_params : string list)
- (variants : variant list) : unit =
- (* We want to generate a definition which looks like this:
- {[
- type list a = | Cons : a -> list a -> list a | Nil : list a
- ]}
-
- If there isn't enough space on one line:
- {[
- type s =
- | Cons : a -> list a -> list a
- | Nil : list a
- ]}
-
- And if we need to write the type of a variant on several lines:
- {[
- type s =
- | Cons :
- a ->
- list a ->
- list a
- | Nil : list a
- ]}
-
- Finally, it is possible to give names to the variant fields in Rust.
- In this situation, we generate a definition like this:
- {[
- type s =
- | Cons : hd:a -> tl:list a -> list a
- | Nil : list a
- ]}
-
- Note that we already printed: [type s =]
- *)
- (* Print the variants *)
- let print_variant (variant_id : VariantId.id) (variant : variant) : unit =
- let variant_name = ctx_get_variant (AdtId def.def_id) variant_id ctx in
- F.pp_print_space fmt ();
- F.pp_open_hvbox fmt ctx.indent_incr;
- (* variant box *)
- (* [| Cons :]
- * Note that we really don't want any break above so we print everything
- * at once. *)
- F.pp_print_string fmt ("| " ^ variant_name ^ " :");
- F.pp_print_space fmt ();
- let print_field (fid : FieldId.id) (f : field) (ctx : extraction_ctx) :
- extraction_ctx =
- (* Open the field box *)
- F.pp_open_box fmt ctx.indent_incr;
- (* Print the field names
- * [ x :]
- * Note that when printing fields, we register the field names as
- * *variables*: they don't need to be unique at the top level. *)
- let ctx =
- match f.field_name with
- | None -> ctx
- | Some field_name ->
- let var_id = VarId.of_int (FieldId.to_int fid) in
- let field_name =
- ctx.fmt.var_basename ctx.names_map.names_set (Some field_name)
- f.field_ty
- in
- let ctx, field_name = ctx_add_var field_name var_id ctx in
- F.pp_print_string fmt (field_name ^ " :");
- F.pp_print_space fmt ();
- ctx
- in
- (* Print the field type *)
- extract_ty ctx fmt false f.field_ty;
- (* Print the arrow [->]*)
- F.pp_print_space fmt ();
- F.pp_print_string fmt "->";
- (* Close the field box *)
- F.pp_close_box fmt ();
- F.pp_print_space fmt ();
- (* Return *)
- ctx
- in
- (* Print the fields *)
- let fields = FieldId.mapi (fun fid f -> (fid, f)) variant.fields in
- let _ =
- List.fold_left (fun ctx (fid, f) -> print_field fid f ctx) ctx fields
- in
- (* Print the final type *)
- F.pp_open_hovbox fmt 0;
- F.pp_print_string fmt def_name;
- List.iter
- (fun type_param ->
- F.pp_print_space fmt ();
- F.pp_print_string fmt type_param)
- type_params;
- F.pp_close_box fmt ();
- (* Close the variant box *)
- F.pp_close_box fmt ()
- in
- (* Print the variants *)
- let variants = VariantId.mapi (fun vid v -> (vid, v)) variants in
- List.iter (fun (vid, v) -> print_variant vid v) variants
-
-(** Extract a type declaration.
-
- Note that all the names used for extraction should already have been
- registered.
- *)
-let extract_type_decl (ctx : extraction_ctx) (fmt : F.formatter)
- (qualif : type_decl_qualif) (def : type_decl) : unit =
- (* Retrieve the definition name *)
- let def_name = ctx_get_local_type def.def_id ctx in
- (* Add the type params - note that we need those bindings only for the
- * body translation (they are not top-level) *)
- let ctx_body, type_params = ctx_add_type_params def.type_params ctx in
- (* Add a break before *)
- F.pp_print_break fmt 0 0;
- (* Print a comment to link the extracted type to its original rust definition *)
- F.pp_print_string fmt ("(** [" ^ Print.name_to_string def.name ^ "] *)");
- F.pp_print_space fmt ();
- (* Open a box for the definition, so that whenever possible it gets printed on
- * one line *)
- F.pp_open_hvbox fmt 0;
- (* Open a box for "type TYPE_NAME (TYPE_PARAMS) =" *)
- F.pp_open_hovbox fmt ctx.indent_incr;
- (* > "type TYPE_NAME" *)
- let extract_body, qualif =
- match qualif with
- | Type -> (true, "type")
- | And -> (true, "and")
- | AssumeType -> (false, "assume type")
- | TypeVal -> (false, "val")
- in
- F.pp_print_string fmt (qualif ^ " " ^ def_name);
- (* Print the type parameters *)
- if def.type_params <> [] then (
- F.pp_print_space fmt ();
- F.pp_print_string fmt "(";
- List.iter
- (fun (p : type_var) ->
- let pname = ctx_get_type_var p.index ctx_body in
- F.pp_print_string fmt pname;
- F.pp_print_space fmt ())
- def.type_params;
- F.pp_print_string fmt ":";
- F.pp_print_space fmt ();
- F.pp_print_string fmt "Type0)");
- (* Print the "=" if we extract the body*)
- if extract_body then (
- F.pp_print_space fmt ();
- F.pp_print_string fmt "=")
- else (
- (* Otherwise print ": Type0" *)
- F.pp_print_space fmt ();
- F.pp_print_string fmt ":";
- F.pp_print_space fmt ();
- F.pp_print_string fmt "Type0");
- (* Close the box for "type TYPE_NAME (TYPE_PARAMS) =" *)
- F.pp_close_box fmt ();
- (if extract_body then
- match def.kind with
- | Struct fields -> extract_type_decl_struct_body ctx_body fmt def fields
- | Enum variants ->
- extract_type_decl_enum_body ctx_body fmt def def_name type_params
- variants
- | Opaque -> raise (Failure "Unreachable"));
- (* Close the box for the definition *)
- F.pp_close_box fmt ();
- (* Add breaks to insert new lines between definitions *)
- F.pp_print_break fmt 0 0
-
-(** Extract the state type declaration. *)
-let extract_state_type (fmt : F.formatter) (ctx : extraction_ctx)
- (qualif : type_decl_qualif) : unit =
- (* Add a break before *)
- F.pp_print_break fmt 0 0;
- (* Print a comment *)
- F.pp_print_string fmt "(** The state type used in the state-error monad *)";
- F.pp_print_space fmt ();
- (* Open a box for the definition, so that whenever possible it gets printed on
- * one line *)
- F.pp_open_hvbox fmt 0;
- (* Retrieve the name *)
- let state_name = ctx_get_assumed_type State ctx in
- (* The qualif should be [AssumeType] or [TypeVal] *)
- (match qualif with
- | Type | And -> raise (Failure "Unexpected")
- | AssumeType ->
- F.pp_print_string fmt "assume";
- F.pp_print_space fmt ();
- F.pp_print_string fmt "type";
- F.pp_print_space fmt ();
- F.pp_print_string fmt state_name;
- F.pp_print_space fmt ();
- F.pp_print_string fmt ":";
- F.pp_print_space fmt ();
- F.pp_print_string fmt "Type0"
- | TypeVal ->
- F.pp_print_string fmt "val";
- F.pp_print_space fmt ();
- F.pp_print_string fmt state_name;
- F.pp_print_space fmt ();
- F.pp_print_string fmt ":";
- F.pp_print_space fmt ();
- F.pp_print_string fmt "Type0");
- (* Close the box for the definition *)
- F.pp_close_box fmt ();
- (* Add breaks to insert new lines between definitions *)
- F.pp_print_break fmt 0 0
-
-(** Compute the names for all the pure functions generated from a rust function
- (forward function and backward functions).
- *)
-let extract_fun_decl_register_names (ctx : extraction_ctx) (keep_fwd : bool)
- (has_decreases_clause : bool) (def : pure_fun_translation) : extraction_ctx
- =
- let fwd, back_ls = def in
- (* Register the decrease clause, if necessary *)
- let ctx =
- if has_decreases_clause then ctx_add_decrases_clause fwd ctx else ctx
- in
- (* Register the forward function name *)
- let ctx = ctx_add_fun_decl (keep_fwd, def) fwd ctx in
- (* Register the backward functions' names *)
- let ctx =
- List.fold_left
- (fun ctx back -> ctx_add_fun_decl (keep_fwd, def) back ctx)
- ctx back_ls
- in
- (* Return *)
- ctx
-
-(** Simply add the global name to the context. *)
-let extract_global_decl_register_names (ctx : extraction_ctx)
- (def : A.global_decl) : extraction_ctx =
- ctx_add_global_decl_and_body def ctx
-
-(** The following function factorizes the extraction of ADT values.
-
- Note that patterns can introduce new variables: we thus return an extraction
- context updated with new bindings.
-
- TODO: we don't need something very generic anymore
- *)
-let extract_adt_g_value
- (extract_value : extraction_ctx -> bool -> 'v -> extraction_ctx)
- (fmt : F.formatter) (ctx : extraction_ctx) (inside : bool)
- (variant_id : VariantId.id option) (field_values : 'v list) (ty : ty) :
- extraction_ctx =
- match ty with
- | Adt (Tuple, _) ->
- (* Tuple *)
- F.pp_print_string fmt "(";
- let ctx =
- Collections.List.fold_left_link
- (fun () ->
- F.pp_print_string fmt ",";
- F.pp_print_space fmt ())
- (fun ctx v -> extract_value ctx false v)
- ctx field_values
- in
- F.pp_print_string fmt ")";
- ctx
- | Adt (adt_id, _) ->
- (* "Regular" ADT *)
- (* We print something of the form: [Cons field0 ... fieldn].
- * We could update the code to print something of the form:
- * [{ field0=...; ...; fieldn=...; }] in case of structures.
- *)
- let cons =
- match variant_id with
- | Some vid -> ctx_get_variant adt_id vid ctx
- | None -> ctx_get_struct adt_id ctx
- in
- if inside && field_values <> [] then F.pp_print_string fmt "(";
- F.pp_print_string fmt cons;
- let ctx =
- Collections.List.fold_left
- (fun ctx v ->
- F.pp_print_space fmt ();
- extract_value ctx true v)
- ctx field_values
- in
- if inside && field_values <> [] then F.pp_print_string fmt ")";
- ctx
- | _ -> raise (Failure "Inconsistent typed value")
-
-(* Extract globals in the same way as variables *)
-let extract_global (ctx : extraction_ctx) (fmt : F.formatter)
- (id : A.GlobalDeclId.id) : unit =
- F.pp_print_string fmt (ctx_get_global id ctx)
-
-(** [inside]: see [extract_ty].
-
- As a pattern can introduce new variables, we return an extraction context
- updated with new bindings.
- *)
-let rec extract_typed_pattern (ctx : extraction_ctx) (fmt : F.formatter)
- (inside : bool) (v : typed_pattern) : extraction_ctx =
- match v.value with
- | PatConcrete cv ->
- ctx.fmt.extract_constant_value fmt inside cv;
- ctx
- | PatVar (v, _) ->
- let vname =
- ctx.fmt.var_basename ctx.names_map.names_set v.basename v.ty
- in
- let ctx, vname = ctx_add_var vname v.id ctx in
- F.pp_print_string fmt vname;
- ctx
- | PatDummy ->
- F.pp_print_string fmt "_";
- ctx
- | PatAdt av ->
- let extract_value ctx inside v = extract_typed_pattern ctx fmt inside v in
- extract_adt_g_value extract_value fmt ctx inside av.variant_id
- av.field_values v.ty
-
-(** [inside]: controls the introduction of parentheses. See [extract_ty]
-
- TODO: replace the formatting boolean [inside] with something more general?
- Also, it seems we don't really use it...
- Cases to consider:
- - right-expression in a let: [let x = re in _] (never parentheses?)
- - next expression in a let: [let x = _ in next_e] (never parentheses?)
- - application argument: [f (exp)]
- - match/if scrutinee: [if exp then _ else _]/[match exp | _ -> _]
- *)
-let rec extract_texpression (ctx : extraction_ctx) (fmt : F.formatter)
- (inside : bool) (e : texpression) : unit =
- match e.e with
- | Var var_id ->
- let var_name = ctx_get_var var_id ctx in
- F.pp_print_string fmt var_name
- | Const cv -> ctx.fmt.extract_constant_value fmt inside cv
- | App _ ->
- let app, args = destruct_apps e in
- extract_App ctx fmt inside app args
- | Abs _ ->
- let xl, e = destruct_abs_list e in
- extract_Abs ctx fmt inside xl e
- | Qualif _ ->
- (* We use the app case *)
- extract_App ctx fmt inside e []
- | Let (monadic, lv, re, next_e) ->
- extract_Let ctx fmt inside monadic lv re next_e
- | Switch (scrut, body) -> extract_Switch ctx fmt inside scrut body
- | Meta (_, e) -> extract_texpression ctx fmt inside e
-
-(* Extract an application *or* a top-level qualif (function extraction has
- * to handle top-level qualifiers, so it seemed more natural to merge the
- * two cases) *)
-and extract_App (ctx : extraction_ctx) (fmt : F.formatter) (inside : bool)
- (app : texpression) (args : texpression list) : unit =
- (* We don't do the same thing if the app is a top-level identifier (function,
- * ADT constructor...) or a "regular" expression *)
- match app.e with
- | Qualif qualif -> (
- (* Top-level qualifier *)
- match qualif.id with
- | Func fun_id ->
- extract_function_call ctx fmt inside fun_id qualif.type_args args
- | Global global_id -> extract_global ctx fmt global_id
- | AdtCons adt_cons_id ->
- extract_adt_cons ctx fmt inside adt_cons_id qualif.type_args args
- | Proj proj ->
- extract_field_projector ctx fmt inside app proj qualif.type_args args)
- | _ ->
- (* "Regular" expression *)
- (* Open parentheses *)
- if inside then F.pp_print_string fmt "(";
- (* Open a box for the application *)
- F.pp_open_hovbox fmt ctx.indent_incr;
- (* Print the app expression *)
- let app_inside = (inside && args = []) || args <> [] in
- extract_texpression ctx fmt app_inside app;
- (* Print the arguments *)
- List.iter
- (fun ve ->
- F.pp_print_space fmt ();
- extract_texpression ctx fmt true ve)
- args;
- (* Close the box for the application *)
- F.pp_close_box fmt ();
- (* Close parentheses *)
- if inside then F.pp_print_string fmt ")"
-
-(** Subcase of the app case: function call *)
-and extract_function_call (ctx : extraction_ctx) (fmt : F.formatter)
- (inside : bool) (fid : fun_id) (type_args : ty list)
- (args : texpression list) : unit =
- match (fid, args) with
- | Unop unop, [ arg ] ->
- (* A unop can have *at most* one argument (the result can't be a function!).
- * Note that the way we generate the translation, we shouldn't get the
- * case where we have no argument (all functions are fully instantiated,
- * and no AST transformation introduces partial calls). *)
- ctx.fmt.extract_unop (extract_texpression ctx fmt) fmt inside unop arg
- | Binop (binop, int_ty), [ arg0; arg1 ] ->
- (* Number of arguments: similar to unop *)
- ctx.fmt.extract_binop
- (extract_texpression ctx fmt)
- fmt inside binop int_ty arg0 arg1
- | Regular (fun_id, rg_id), _ ->
- if inside then F.pp_print_string fmt "(";
- (* Open a box for the function call *)
- F.pp_open_hovbox fmt ctx.indent_incr;
- (* Print the function name *)
- let fun_name = ctx_get_function fun_id rg_id ctx in
- F.pp_print_string fmt fun_name;
- (* Print the type parameters *)
- List.iter
- (fun ty ->
- F.pp_print_space fmt ();
- extract_ty ctx fmt true ty)
- type_args;
- (* Print the arguments *)
- List.iter
- (fun ve ->
- F.pp_print_space fmt ();
- extract_texpression ctx fmt true ve)
- args;
- (* Close the box for the function call *)
- F.pp_close_box fmt ();
- (* Return *)
- if inside then F.pp_print_string fmt ")"
- | _ ->
- raise
- (Failure
- ("Unreachable:\n" ^ "Function: " ^ show_fun_id fid
- ^ ",\nNumber of arguments: "
- ^ string_of_int (List.length args)
- ^ ",\nArguments: "
- ^ String.concat " " (List.map show_texpression args)))
-
-(** Subcase of the app case: ADT constructor *)
-and extract_adt_cons (ctx : extraction_ctx) (fmt : F.formatter) (inside : bool)
- (adt_cons : adt_cons_id) (type_args : ty list) (args : texpression list) :
- unit =
- match adt_cons.adt_id with
- | Tuple ->
- (* Tuple *)
- (* For now, we only support fully applied tuple constructors *)
- assert (List.length type_args = List.length args);
- F.pp_print_string fmt "(";
- Collections.List.iter_link
- (fun () ->
- F.pp_print_string fmt ",";
- F.pp_print_space fmt ())
- (fun v -> extract_texpression ctx fmt false v)
- args;
- F.pp_print_string fmt ")"
- | _ ->
- (* "Regular" ADT *)
- (* We print something of the form: [Cons field0 ... fieldn].
- * We could update the code to print something of the form:
- * [{ field0=...; ...; fieldn=...; }] in case of fully
- * applied structure constructors.
- *)
- let cons =
- match adt_cons.variant_id with
- | Some vid -> ctx_get_variant adt_cons.adt_id vid ctx
- | None -> ctx_get_struct adt_cons.adt_id ctx
- in
- let use_parentheses = inside && args <> [] in
- if use_parentheses then F.pp_print_string fmt "(";
- F.pp_print_string fmt cons;
- Collections.List.iter
- (fun v ->
- F.pp_print_space fmt ();
- extract_texpression ctx fmt true v)
- args;
- if use_parentheses then F.pp_print_string fmt ")"
-
-(** Subcase of the app case: ADT field projector. *)
-and extract_field_projector (ctx : extraction_ctx) (fmt : F.formatter)
- (inside : bool) (original_app : texpression) (proj : projection)
- (_proj_type_params : ty list) (args : texpression list) : unit =
- (* We isolate the first argument (if there is), in order to pretty print the
- * projection ([x.field] instead of [MkAdt?.field x] *)
- match args with
- | [ arg ] ->
- (* Exactly one argument: pretty-print *)
- let field_name = ctx_get_field proj.adt_id proj.field_id ctx in
- (* Open a box *)
- F.pp_open_hovbox fmt ctx.indent_incr;
- (* Extract the expression *)
- extract_texpression ctx fmt true arg;
- (* We allow to break where the "." appears *)
- F.pp_print_break fmt 0 0;
- F.pp_print_string fmt ".";
- F.pp_print_string fmt field_name;
- (* Close the box *)
- F.pp_close_box fmt ()
- | arg :: args ->
- (* Call extract_App again, but in such a way that the first argument is
- * isolated *)
- extract_App ctx fmt inside (mk_app original_app arg) args
- | [] ->
- (* No argument: shouldn't happen *)
- raise (Failure "Unreachable")
-
-and extract_Abs (ctx : extraction_ctx) (fmt : F.formatter) (inside : bool)
- (xl : typed_pattern list) (e : texpression) : unit =
- (* Open a box for the abs expression *)
- F.pp_open_hovbox fmt ctx.indent_incr;
- (* Open parentheses *)
- if inside then F.pp_print_string fmt "(";
- (* Print the lambda - note that there should always be at least one variable *)
- assert (xl <> []);
- F.pp_print_string fmt "fun";
- let ctx =
- List.fold_left
- (fun ctx x ->
- F.pp_print_space fmt ();
- extract_typed_pattern ctx fmt true x)
- ctx xl
- in
- F.pp_print_space fmt ();
- F.pp_print_string fmt "->";
- F.pp_print_space fmt ();
- (* Print the body *)
- extract_texpression ctx fmt false e;
- (* Close parentheses *)
- if inside then F.pp_print_string fmt ")";
- (* Close the box for the abs expression *)
- F.pp_close_box fmt ()
-
-and extract_Let (ctx : extraction_ctx) (fmt : F.formatter) (inside : bool)
- (monadic : bool) (lv : typed_pattern) (re : texpression)
- (next_e : texpression) : unit =
- (* Open a box for the whole expression *)
- F.pp_open_hvbox fmt 0;
- (* Open parentheses *)
- if inside then F.pp_print_string fmt "(";
- (* Open a box for the let-binding *)
- F.pp_open_hovbox fmt ctx.indent_incr;
- let ctx =
- if monadic then (
- (* Note that in F*, the left value of a monadic let-binding can only be
- * a variable *)
- let ctx = extract_typed_pattern ctx fmt true lv in
- F.pp_print_space fmt ();
- F.pp_print_string fmt "<--";
- F.pp_print_space fmt ();
- extract_texpression ctx fmt false re;
- F.pp_print_string fmt ";";
- ctx)
- else (
- F.pp_print_string fmt "let";
- F.pp_print_space fmt ();
- let ctx = extract_typed_pattern ctx fmt true lv in
- F.pp_print_space fmt ();
- F.pp_print_string fmt "=";
- F.pp_print_space fmt ();
- extract_texpression ctx fmt false re;
- F.pp_print_space fmt ();
- F.pp_print_string fmt "in";
- ctx)
- in
- (* Close the box for the let-binding *)
- F.pp_close_box fmt ();
- (* Print the next expression *)
- F.pp_print_space fmt ();
- extract_texpression ctx fmt false next_e;
- (* Close parentheses *)
- if inside then F.pp_print_string fmt ")";
- (* Close the box for the whole expression *)
- F.pp_close_box fmt ()
-
-and extract_Switch (ctx : extraction_ctx) (fmt : F.formatter) (inside : bool)
- (scrut : texpression) (body : switch_body) : unit =
- (* Open a box for the whole expression *)
- F.pp_open_hvbox fmt 0;
- (* Open parentheses *)
- if inside then F.pp_print_string fmt "(";
- (* Extract the switch *)
- (match body with
- | If (e_then, e_else) ->
- (* Open a box for the [if] *)
- F.pp_open_hovbox fmt ctx.indent_incr;
- F.pp_print_string fmt "if";
- F.pp_print_space fmt ();
- let scrut_inside = PureUtils.let_group_requires_parentheses scrut in
- extract_texpression ctx fmt scrut_inside scrut;
- (* Close the box for the [if] *)
- F.pp_close_box fmt ();
- (* Extract the branches *)
- let extract_branch (is_then : bool) (e_branch : texpression) : unit =
- F.pp_print_space fmt ();
- (* Open a box for the then/else+branch *)
- F.pp_open_hovbox fmt ctx.indent_incr;
- let then_or_else = if is_then then "then" else "else" in
- F.pp_print_string fmt then_or_else;
- F.pp_print_space fmt ();
- (* Open a box for the branch *)
- F.pp_open_hovbox fmt ctx.indent_incr;
- (* Print the [begin] if necessary *)
- let parenth = PureUtils.let_group_requires_parentheses e_branch in
- if parenth then (
- F.pp_print_string fmt "begin";
- F.pp_print_space fmt ());
- (* Print the branch expression *)
- extract_texpression ctx fmt false e_branch;
- (* Close the [begin ... end ] *)
- if parenth then (
- F.pp_print_space fmt ();
- F.pp_print_string fmt "end");
- (* Close the box for the branch *)
- F.pp_close_box fmt ();
- (* Close the box for the then/else+branch *)
- F.pp_close_box fmt ()
- in
-
- extract_branch true e_then;
- extract_branch false e_else
- | Match branches ->
- (* Open a box for the [match ... with] *)
- F.pp_open_hovbox fmt ctx.indent_incr;
- (* Print the [match ... with] *)
- F.pp_print_string fmt "begin match";
- F.pp_print_space fmt ();
- let scrut_inside = PureUtils.let_group_requires_parentheses scrut in
- extract_texpression ctx fmt scrut_inside scrut;
- F.pp_print_space fmt ();
- F.pp_print_string fmt "with";
- (* Close the box for the [match ... with] *)
- F.pp_close_box fmt ();
-
- (* Extract the branches *)
- let extract_branch (br : match_branch) : unit =
- F.pp_print_space fmt ();
- (* Open a box for the pattern+branch *)
- F.pp_open_hovbox fmt ctx.indent_incr;
- F.pp_print_string fmt "|";
- (* Print the pattern *)
- F.pp_print_space fmt ();
- let ctx = extract_typed_pattern ctx fmt false br.pat in
- F.pp_print_space fmt ();
- F.pp_print_string fmt "->";
- F.pp_print_space fmt ();
- (* Open a box for the branch *)
- F.pp_open_hovbox fmt ctx.indent_incr;
- (* Print the branch itself *)
- extract_texpression ctx fmt false br.branch;
- (* Close the box for the branch *)
- F.pp_close_box fmt ();
- (* Close the box for the pattern+branch *)
- F.pp_close_box fmt ()
- in
-
- List.iter extract_branch branches;
-
- (* End the match *)
- F.pp_print_space fmt ();
- F.pp_print_string fmt "end");
- (* Close parentheses *)
- if inside then F.pp_print_string fmt ")";
- (* Close the box for the whole expression *)
- F.pp_close_box fmt ()
-
-(** A small utility to print the parameters of a function signature.
-
- We return two contexts:
- - the context augmented with bindings for the type parameters
- - the previous context augmented with bindings for the input values
- *)
-let extract_fun_parameters (ctx : extraction_ctx) (fmt : F.formatter)
- (def : fun_decl) : extraction_ctx * extraction_ctx =
- (* Add the type parameters - note that we need those bindings only for the
- * body translation (they are not top-level) *)
- let ctx, _ = ctx_add_type_params def.signature.type_params ctx in
- (* Print the parameters - rk.: we should have filtered the functions
- * with no input parameters *)
- (* The type parameters *)
- if def.signature.type_params <> [] then (
- (* Open a box for the type parameters *)
- F.pp_open_hovbox fmt 0;
- F.pp_print_string fmt "(";
- List.iter
- (fun (p : type_var) ->
- let pname = ctx_get_type_var p.index ctx in
- F.pp_print_string fmt pname;
- F.pp_print_space fmt ())
- def.signature.type_params;
- F.pp_print_string fmt ":";
- F.pp_print_space fmt ();
- F.pp_print_string fmt "Type0)";
- (* Close the box for the type parameters *)
- F.pp_close_box fmt ();
- F.pp_print_space fmt ());
- (* The input parameters - note that doing this adds bindings to the context *)
- let ctx_body =
- match def.body with
- | None -> ctx
- | Some body ->
- List.fold_left
- (fun ctx (lv : typed_pattern) ->
- (* Open a box for the input parameter *)
- F.pp_open_hovbox fmt 0;
- F.pp_print_string fmt "(";
- let ctx = extract_typed_pattern ctx fmt false lv in
- F.pp_print_space fmt ();
- F.pp_print_string fmt ":";
- F.pp_print_space fmt ();
- extract_ty ctx fmt false lv.ty;
- F.pp_print_string fmt ")";
- (* Close the box for the input parameters *)
- F.pp_close_box fmt ();
- F.pp_print_space fmt ();
- ctx)
- ctx body.inputs_lvs
- in
- (ctx, ctx_body)
-
-(** A small utility to print the types of the input parameters in the form:
- [u32 -> list u32 -> ...]
- (we don't print the return type of the function)
-
- This is used for opaque function declarations, in particular.
- *)
-let extract_fun_input_parameters_types (ctx : extraction_ctx)
- (fmt : F.formatter) (def : fun_decl) : unit =
- let extract_param (ty : ty) : unit =
- let inside = false in
- extract_ty ctx fmt inside ty;
- F.pp_print_space fmt ();
- F.pp_print_string fmt "->";
- F.pp_print_space fmt ()
- in
- List.iter extract_param def.signature.inputs
-
-(** Extract a decrease clause function template body.
-
- In order to help the user, we can generate a template for the functions
- required by the decreases clauses. We simply generate definitions of
- the following form in a separate file:
- {[
- let f_decrease (t : Type0) (x : t) : nat = admit()
- ]}
-
- Where the translated functions for [f] look like this:
- {[
- let f_fwd (t : Type0) (x : t) : Tot ... (decreases (f_decrease t x)) = ...
- ]}
- *)
-let extract_template_decreases_clause (ctx : extraction_ctx) (fmt : F.formatter)
- (def : fun_decl) : unit =
- (* Retrieve the function name *)
- let def_name = ctx_get_decreases_clause def.def_id ctx in
- (* Add a break before *)
- F.pp_print_break fmt 0 0;
- (* Print a comment to link the extracted type to its original rust definition *)
- F.pp_print_string fmt
- ("(** [" ^ Print.fun_name_to_string def.basename ^ "]: decreases clause *)");
- F.pp_print_space fmt ();
- (* Open a box for the definition, so that whenever possible it gets printed on
- * one line *)
- F.pp_open_hvbox fmt 0;
- (* Add the [unfold] keyword *)
- F.pp_print_string fmt "unfold";
- F.pp_print_space fmt ();
- (* Open a box for "let FUN_NAME (PARAMS) : EFFECT = admit()" *)
- F.pp_open_hvbox fmt ctx.indent_incr;
- (* Open a box for "let FUN_NAME (PARAMS) : EFFECT =" *)
- F.pp_open_hovbox fmt ctx.indent_incr;
- (* > "let FUN_NAME" *)
- F.pp_print_string fmt ("let " ^ def_name);
- F.pp_print_space fmt ();
- (* Extract the parameters *)
- let _, _ = extract_fun_parameters ctx fmt def in
- F.pp_print_string fmt ":";
- (* Print the signature *)
- F.pp_print_space fmt ();
- F.pp_print_string fmt "nat";
- (* Print the "=" *)
- F.pp_print_space fmt ();
- F.pp_print_string fmt "=";
- (* Close the box for "let FUN_NAME (PARAMS) : EFFECT =" *)
- F.pp_close_box fmt ();
- F.pp_print_space fmt ();
- (* Print the "admit ()" *)
- F.pp_print_string fmt "admit ()";
- (* Close the box for "let FUN_NAME (PARAMS) : EFFECT = admit()" *)
- F.pp_close_box fmt ();
- (* Close the box for the whole definition *)
- F.pp_close_box fmt ();
- (* Add breaks to insert new lines between definitions *)
- F.pp_print_break fmt 0 0
-
-(** Extract a function declaration.
-
- Note that all the names used for extraction should already have been
- registered.
-
- We take the definition of the forward translation as parameter (which is
- equal to the definition to extract, if we extract a forward function) because
- it is useful for the decrease clause.
- *)
-let extract_fun_decl (ctx : extraction_ctx) (fmt : F.formatter)
- (qualif : fun_decl_qualif) (has_decreases_clause : bool) (def : fun_decl) :
- unit =
- assert (not def.is_global_decl_body);
- (* Retrieve the function name *)
- let def_name = ctx_get_local_function def.def_id def.back_id ctx in
- (* (* Add the type parameters - note that we need those bindings only for the
- * body translation (they are not top-level) *)
- let ctx, _ = ctx_add_type_params def.signature.type_params ctx in *)
- (* Add a break before *)
- F.pp_print_break fmt 0 0;
- (* Print a comment to link the extracted type to its original rust definition *)
- F.pp_print_string fmt
- ("(** [" ^ Print.fun_name_to_string def.basename ^ "] *)");
- F.pp_print_space fmt ();
- (* Open a box for the definition, so that whenever possible it gets printed on
- * one line *)
- F.pp_open_hvbox fmt ctx.indent_incr;
- (* Open a box for "let FUN_NAME (PARAMS) : EFFECT =" *)
- F.pp_open_hovbox fmt ctx.indent_incr;
- (* > "let FUN_NAME" *)
- let is_opaque = Option.is_none def.body in
- let qualif = fun_decl_qualif_keyword qualif in
- F.pp_print_string fmt (qualif ^ " " ^ def_name);
- F.pp_print_space fmt ();
- (* Open a box for "(PARAMS) : EFFECT =" *)
- F.pp_open_hvbox fmt 0;
- (* Open a box for "(PARAMS)" *)
- F.pp_open_hovbox fmt 0;
- let ctx, ctx_body = extract_fun_parameters ctx fmt def in
- (* Close the box for "(PARAMS)" *)
- F.pp_close_box fmt ();
- (* Print the return type - note that we have to be careful when
- * printing the input values for the decrease clause, because
- * it introduces bindings in the context... We thus "forget"
- * the bindings we introduced above.
- * TODO: figure out a cleaner way *)
- let _ =
- F.pp_print_string fmt ":";
- F.pp_print_space fmt ();
- (* Open a box for the EFFECT *)
- F.pp_open_hvbox fmt 0;
- (* Open a box for the return type *)
- F.pp_open_hovbox fmt ctx.indent_incr;
- (* Print the return type *)
- (* For opaque definitions, as we don't have named parameters under the hand,
- * we don't print parameters in the form [(x : a) (y : b) ...] above,
- * but wait until here to print the types: [a -> b -> ...]. *)
- if is_opaque then extract_fun_input_parameters_types ctx fmt def;
- (* [Tot] *)
- if has_decreases_clause then (
- F.pp_print_string fmt "Tot";
- F.pp_print_space fmt ());
- extract_ty ctx fmt has_decreases_clause def.signature.output;
- (* Close the box for the return type *)
- F.pp_close_box fmt ();
- (* Print the decrease clause - rk.: a function with a decreases clause
- * is necessarily a transparent function *)
- if has_decreases_clause then (
- F.pp_print_space fmt ();
- (* Open a box for the decrease clause *)
- F.pp_open_hovbox fmt 0;
- (* *)
- F.pp_print_string fmt "(decreases";
- F.pp_print_space fmt ();
- F.pp_print_string fmt "(";
- (* The name of the decrease clause *)
- let decr_name = ctx_get_decreases_clause def.def_id ctx in
- F.pp_print_string fmt decr_name;
- (* Print the type parameters *)
- List.iter
- (fun (p : type_var) ->
- let pname = ctx_get_type_var p.index ctx in
- F.pp_print_space fmt ();
- F.pp_print_string fmt pname)
- def.signature.type_params;
- (* Print the input values: we have to be careful here to print
- * only the input values which are in common with the *forward*
- * function (the additional input values "given back" to the
- * backward functions have no influence on termination: we thus
- * share the decrease clauses between the forward and the backward
- * functions).
- *)
- let inputs_lvs =
- let all_inputs = (Option.get def.body).inputs_lvs in
- (* We have to count:
- * - the forward inputs
- * - the state
- *)
- let num_fwd_inputs = def.signature.info.num_fwd_inputs in
- let num_fwd_inputs =
- if def.signature.info.effect_info.input_state then 1 + num_fwd_inputs
- else num_fwd_inputs
- in
- Collections.List.prefix num_fwd_inputs all_inputs
- in
- let _ =
- List.fold_left
- (fun ctx (lv : typed_pattern) ->
- F.pp_print_space fmt ();
- let ctx = extract_typed_pattern ctx fmt false lv in
- ctx)
- ctx inputs_lvs
- in
- F.pp_print_string fmt "))";
- (* Close the box for the decrease clause *)
- F.pp_close_box fmt ());
- (* Close the box for the EFFECT *)
- F.pp_close_box fmt ()
- in
- (* Print the "=" *)
- if not is_opaque then (
- F.pp_print_space fmt ();
- F.pp_print_string fmt "=");
- (* Close the box for "(PARAMS) : EFFECT =" *)
- F.pp_close_box fmt ();
- (* Close the box for "let FUN_NAME (PARAMS) : EFFECT =" *)
- F.pp_close_box fmt ();
- if not is_opaque then (
- F.pp_print_space fmt ();
- (* Open a box for the body *)
- F.pp_open_hvbox fmt 0;
- (* Extract the body *)
- let _ = extract_texpression ctx_body fmt false (Option.get def.body).body in
- (* Close the box for the body *)
- F.pp_close_box fmt ());
- (* Close the box for the definition *)
- F.pp_close_box fmt ();
- (* Add breaks to insert new lines between definitions *)
- F.pp_print_break fmt 0 0
-
-(** Extract a global declaration body of the shape "QUALIF NAME : TYPE = BODY" with a custom body extractor *)
-let extract_global_decl_body (ctx : extraction_ctx) (fmt : F.formatter)
- (qualif : fun_decl_qualif) (name : string) (ty : ty)
- (extract_body : (F.formatter -> unit) Option.t) : unit =
- let is_opaque = Option.is_none extract_body in
-
- (* Open the definition box (depth=0) *)
- F.pp_open_hvbox fmt ctx.indent_incr;
-
- (* Open "QUALIF NAME : TYPE =" box (depth=1) *)
- F.pp_open_hovbox fmt ctx.indent_incr;
- (* Print "QUALIF NAME " *)
- F.pp_print_string fmt (fun_decl_qualif_keyword qualif ^ " " ^ name);
- F.pp_print_space fmt ();
-
- (* Open ": TYPE =" box (depth=2) *)
- F.pp_open_hvbox fmt 0;
- (* Print ": " *)
- F.pp_print_string fmt ":";
- F.pp_print_space fmt ();
-
- (* Open "TYPE" box (depth=3) *)
- F.pp_open_hovbox fmt ctx.indent_incr;
- (* Print "TYPE" *)
- extract_ty ctx fmt false ty;
- (* Close "TYPE" box (depth=3) *)
- F.pp_close_box fmt ();
-
- if not is_opaque then (
- (* Print " =" *)
- F.pp_print_space fmt ();
- F.pp_print_string fmt "=");
- (* Close ": TYPE =" box (depth=2) *)
- F.pp_close_box fmt ();
- (* Close "QUALIF NAME : TYPE =" box (depth=1) *)
- F.pp_close_box fmt ();
-
- if not is_opaque then (
- F.pp_print_space fmt ();
- (* Open "BODY" box (depth=1) *)
- F.pp_open_hvbox fmt 0;
- (* Print "BODY" *)
- (Option.get extract_body) fmt;
- (* Close "BODY" box (depth=1) *)
- F.pp_close_box fmt ());
- (* Close the definition box (depth=0) *)
- F.pp_close_box fmt ()
-
-(** Extract a global declaration.
- We generate the body which computes the global value separately from the value declaration itself.
-
- For example in Rust,
- [static X: u32 = 3;]
-
- will be translated to:
- [let x_body : result u32 = Return 3]
- [let x_c : u32 = eval_global x_body]
- *)
-let extract_global_decl (ctx : extraction_ctx) (fmt : F.formatter)
- (global : A.global_decl) (body : fun_decl) (interface : bool) : unit =
- assert body.is_global_decl_body;
- assert (Option.is_none body.back_id);
- assert (List.length body.signature.inputs = 0);
- assert (List.length body.signature.doutputs = 1);
- assert (List.length body.signature.type_params = 0);
-
- (* Add a break then the name of the corresponding LLBC declaration *)
- F.pp_print_break fmt 0 0;
- F.pp_print_string fmt
- ("(** [" ^ Print.global_name_to_string global.name ^ "] *)");
- F.pp_print_space fmt ();
-
- let decl_name = ctx_get_global global.def_id ctx in
- let body_name = ctx_get_function (Regular global.body_id) None ctx in
-
- let decl_ty, body_ty =
- let ty = body.signature.output in
- if body.signature.info.effect_info.can_fail then (unwrap_result_ty ty, ty)
- else (ty, mk_result_ty ty)
- in
- match body.body with
- | None ->
- let qualif = if interface then Val else AssumeVal in
- extract_global_decl_body ctx fmt qualif decl_name decl_ty None
- | Some body ->
- extract_global_decl_body ctx fmt Let body_name body_ty
- (Some (fun fmt -> extract_texpression ctx fmt false body.body));
- F.pp_print_break fmt 0 0;
- extract_global_decl_body ctx fmt Let decl_name decl_ty
- (Some (fun fmt -> F.pp_print_string fmt ("eval_global " ^ body_name)));
- F.pp_print_break fmt 0 0
-
-(** Extract a unit test, if the function is a unit function (takes no
- parameters, returns unit).
-
- A unit test simply checks that the function normalizes to [Return ()]:
- {[
- let _ = assert_norm (FUNCTION () = Return ())
- ]}
- *)
-let extract_unit_test_if_unit_fun (ctx : extraction_ctx) (fmt : F.formatter)
- (def : fun_decl) : unit =
- (* We only insert unit tests for forward functions *)
- assert (def.back_id = None);
- (* Check if this is a unit function *)
- let sg = def.signature in
- if
- sg.type_params = []
- && (sg.inputs = [ mk_unit_ty ] || sg.inputs = [])
- && sg.output = mk_result_ty mk_unit_ty
- then (
- (* Add a break before *)
- F.pp_print_break fmt 0 0;
- (* Print a comment *)
- F.pp_print_string fmt
- ("(** Unit test for [" ^ Print.fun_name_to_string def.basename ^ "] *)");
- F.pp_print_space fmt ();
- (* Open a box for the test *)
- F.pp_open_hovbox fmt ctx.indent_incr;
- (* Print the test *)
- F.pp_print_string fmt "let _ =";
- F.pp_print_space fmt ();
- F.pp_print_string fmt "assert_norm";
- F.pp_print_space fmt ();
- F.pp_print_string fmt "(";
- let fun_name = ctx_get_local_function def.def_id def.back_id ctx in
- F.pp_print_string fmt fun_name;
- if sg.inputs <> [] then (
- F.pp_print_space fmt ();
- F.pp_print_string fmt "()");
- F.pp_print_space fmt ();
- F.pp_print_string fmt "=";
- F.pp_print_space fmt ();
- let success = ctx_get_variant (Assumed Result) result_return_id ctx in
- F.pp_print_string fmt (success ^ " ())");
- (* Close the box for the test *)
- F.pp_close_box fmt ();
- (* Add a break after *)
- F.pp_print_break fmt 0 0)
- else (* Do nothing *)
- ()
diff --git a/src/FunsAnalysis.ml b/src/FunsAnalysis.ml
deleted file mode 100644
index 248ad8b3..00000000
--- a/src/FunsAnalysis.ml
+++ /dev/null
@@ -1,143 +0,0 @@
-(** Compute various information, including:
- - can a function fail (by having `Fail` in its body, or transitively
- calling a function which can fail - this is false for globals)
- - can a function diverge (by being recursive, containing a loop or
- transitively calling a function which can diverge)
- - does a function perform stateful operations (i.e., do we need a state
- to translate it)
- *)
-
-open LlbcAst
-open Crates
-module EU = ExpressionsUtils
-
-type fun_info = {
- can_fail : bool;
- (* Not used yet: all the extracted functions use an error monad *)
- stateful : bool;
- divergent : bool; (* Not used yet *)
-}
-[@@deriving show]
-(** Various information about a function.
-
- Note that not all this information is used yet to adjust the extraction yet.
- *)
-
-type modules_funs_info = fun_info FunDeclId.Map.t
-(** Various information about a module's functions *)
-
-let analyze_module (m : llbc_crate) (funs_map : fun_decl FunDeclId.Map.t)
- (globals_map : global_decl GlobalDeclId.Map.t) (use_state : bool) :
- modules_funs_info =
- let infos = ref FunDeclId.Map.empty in
-
- let register_info (id : FunDeclId.id) (info : fun_info) : unit =
- assert (not (FunDeclId.Map.mem id !infos));
- infos := FunDeclId.Map.add id info !infos
- in
-
- (* Analyze a group of mutually recursive functions.
- * As the functions can call each other, we compute the same information
- * for all of them (if one of the functions can fail, then all of them
- * can fail, etc.).
- *
- * We simply check if the functions contains panic statements, loop statements,
- * recursive calls, etc. We use the previously computed information in case
- * of function calls.
- *)
- let analyze_fun_decls (fun_ids : FunDeclId.Set.t) (d : fun_decl list) :
- fun_info =
- let can_fail = ref false in
- let stateful = ref false in
- let divergent = ref false in
-
- let visit_fun (f : fun_decl) : unit =
- let obj =
- object (self)
- inherit [_] iter_statement as super
- method may_fail b = can_fail := !can_fail || b
-
- method! visit_Assert env a =
- self#may_fail true;
- super#visit_Assert env a
-
- method! visit_rvalue _env rv =
- match rv with
- | Use _ | Ref _ | Discriminant _ | Aggregate _ -> ()
- | UnaryOp (uop, _) -> can_fail := EU.unop_can_fail uop || !can_fail
- | BinaryOp (bop, _, _) ->
- can_fail := EU.binop_can_fail bop || !can_fail
-
- method! visit_Call env call =
- (match call.func with
- | Regular id ->
- if FunDeclId.Set.mem id fun_ids then divergent := true
- else
- let info = FunDeclId.Map.find id !infos in
- self#may_fail info.can_fail;
- stateful := !stateful || info.stateful;
- divergent := !divergent || info.divergent
- | Assumed id ->
- (* None of the assumed functions is stateful for now *)
- can_fail := !can_fail || Assumed.assumed_can_fail id);
- super#visit_Call env call
-
- method! visit_Panic env =
- self#may_fail true;
- super#visit_Panic env
-
- method! visit_Loop env loop =
- divergent := true;
- super#visit_Loop env loop
- end
- in
- (* Sanity check: global bodies don't contain stateful calls *)
- assert ((not f.is_global_decl_body) || not !stateful);
- match f.body with
- | None ->
- (* Opaque function: we consider they fail by default *)
- obj#may_fail true;
- stateful := (not f.is_global_decl_body) && use_state
- | Some body -> obj#visit_statement () body.body
- in
- List.iter visit_fun d;
- (* We need to know if the declaration group contains a global - note that
- * groups containing globals contain exactly one declaration *)
- let is_global_decl_body = List.exists (fun f -> f.is_global_decl_body) d in
- assert ((not is_global_decl_body) || List.length d == 1);
- (* We ignore on purpose functions that cannot fail and consider they *can*
- * fail: the result of the analysis is not used yet to adjust the translation
- * so that the functions which syntactically can't fail don't use an error monad.
- * However, we do keep the result of the analysis for global bodies.
- * *)
- can_fail := (not is_global_decl_body) || !can_fail;
- { can_fail = !can_fail; stateful = !stateful; divergent = !divergent }
- in
-
- let analyze_fun_decl_group (d : fun_declaration_group) : unit =
- (* Retrieve the function declarations *)
- let funs = match d with NonRec id -> [ id ] | Rec ids -> ids in
- let funs = List.map (fun id -> FunDeclId.Map.find id funs_map) funs in
- let fun_ids = List.map (fun (d : fun_decl) -> d.def_id) funs in
- let fun_ids = FunDeclId.Set.of_list fun_ids in
- let info = analyze_fun_decls fun_ids funs in
- List.iter (fun (f : fun_decl) -> register_info f.def_id info) funs
- in
-
- let rec analyze_decl_groups (decls : declaration_group list) : unit =
- match decls with
- | [] -> ()
- | Type _ :: decls' -> analyze_decl_groups decls'
- | Fun decl :: decls' ->
- analyze_fun_decl_group decl;
- analyze_decl_groups decls'
- | Global id :: decls' ->
- (* Analyze a global by analyzing its body function *)
- let global = GlobalDeclId.Map.find id globals_map in
- analyze_fun_decl_group (NonRec global.body_id);
- analyze_decl_groups decls'
- in
-
- analyze_decl_groups m.declarations;
-
- !infos
diff --git a/src/Identifiers.ml b/src/Identifiers.ml
deleted file mode 100644
index b022b18d..00000000
--- a/src/Identifiers.ml
+++ /dev/null
@@ -1,139 +0,0 @@
-module C = Collections
-
-(** Signature for a module describing an identifier.
-
- We often need identifiers (for definitions, variables, etc.) and in
- order to make sure we don't mix them, we use a generative functor
- (see {!IdGen}).
-*)
-module type Id = sig
- type id
-
- (** Id generator - simply a counter *)
- type generator
-
- val zero : id
- val generator_zero : generator
- val generator_from_incr_id : id -> generator
- val fresh_stateful_generator : unit -> generator ref * (unit -> id)
- val mk_stateful_generator : generator -> generator ref * (unit -> id)
- val incr : id -> id
-
- (* TODO: this should be stateful! - but we may want to be able to duplicate
- contexts...
- Maybe we could have a [fresh] and a [global_fresh]
- TODO: change the order of the returned types
- *)
- val fresh : generator -> id * generator
- val to_string : id -> string
- val pp_id : Format.formatter -> id -> unit
- val show_id : id -> string
- val id_of_json : Yojson.Basic.t -> (id, string) result
- val compare_id : id -> id -> int
- val max : id -> id -> id
- val min : id -> id -> id
- val pp_generator : Format.formatter -> generator -> unit
- val show_generator : generator -> string
- val to_int : id -> int
- val of_int : int -> id
- val nth : 'a list -> id -> 'a
- (* TODO: change the signature (invert the index and the list *)
-
- val nth_opt : 'a list -> id -> 'a option
-
- (** Update the nth element of the list.
-
- Raises [Invalid_argument] if the identifier is out of range.
- *)
- val update_nth : 'a list -> id -> 'a -> 'a list
-
- val mapi : (id -> 'a -> 'b) -> 'a list -> 'b list
-
- (** Same as {!mapi}, but where the indices start with 1.
-
- TODO: generalize to [map_from_i]
- *)
- val mapi_from1 : (id -> 'a -> 'b) -> 'a list -> 'b list
-
- val iteri : (id -> 'a -> unit) -> 'a list -> unit
-
- module Ord : C.OrderedType with type t = id
- module Set : C.Set with type elt = id
- module Map : C.Map with type key = id
-end
-
-(** Generative functor for identifiers.
-
- See {!Id}.
-*)
-module IdGen () : Id = struct
- (* TODO: use Z.t *)
- type id = int [@@deriving show]
- type generator = id [@@deriving show]
-
- let zero = 0
- let generator_zero = 0
-
- let incr x =
- (* Identifiers should never overflow (because max_int is a really big
- * value - but we really want to make sure we detect overflows if
- * they happen *)
- if x = max_int then raise (Errors.IntegerOverflow ()) else x + 1
-
- let generator_from_incr_id id = incr id
-
- let mk_stateful_generator g =
- let g = ref g in
- let fresh () =
- let id = !g in
- g := incr id;
- id
- in
- (g, fresh)
-
- let fresh_stateful_generator () = mk_stateful_generator 0
- let fresh gen = (gen, incr gen)
- let to_string = string_of_int
- let to_int x = x
- let of_int x = x
-
- let id_of_json js =
- (* TODO: check boundaries ? *)
- match js with
- | `Int i -> Ok i
- | _ -> Error ("id_of_json: failed on " ^ Yojson.Basic.show js)
-
- let compare_id = compare
- let max id0 id1 = if id0 > id1 then id0 else id1
- let min id0 id1 = if id0 < id1 then id0 else id1
- let nth v id = List.nth v id
- let nth_opt v id = List.nth_opt v id
-
- let rec update_nth vec id v =
- match (vec, id) with
- | [], _ -> raise (Invalid_argument "Out of range")
- | _ :: vec', 0 -> v :: vec'
- | x :: vec', _ -> x :: update_nth vec' (id - 1) v
-
- let mapi = List.mapi
-
- let mapi_from1 f ls =
- let rec aux i ls =
- match ls with [] -> [] | x :: ls' -> f i x :: aux (i + 1) ls'
- in
- aux 1 ls
-
- let iteri = List.iteri
-
- module Ord = struct
- type t = id
-
- let compare = compare
- let to_string = to_string
- let pp_t = pp_id
- let show_t = show_id
- end
-
- module Set = C.MakeSet (Ord)
- module Map = C.MakeMap (Ord)
-end
diff --git a/src/Interpreter.ml b/src/Interpreter.ml
deleted file mode 100644
index 7f51c5b9..00000000
--- a/src/Interpreter.ml
+++ /dev/null
@@ -1,396 +0,0 @@
-open Cps
-open InterpreterUtils
-open InterpreterProjectors
-open InterpreterBorrows
-open InterpreterStatements
-open LlbcAstUtils
-module L = Logging
-module T = Types
-module A = LlbcAst
-module SA = SymbolicAst
-
-(** The local logger *)
-let log = L.interpreter_log
-
-let compute_type_fun_global_contexts (m : Crates.llbc_crate) :
- C.type_context * C.fun_context * C.global_context =
- let type_decls_list, _, _ = Crates.split_declarations m.declarations in
- let type_decls, fun_decls, global_decls = Crates.compute_defs_maps m in
- let type_decls_groups, _funs_defs_groups, _globals_defs_groups =
- Crates.split_declarations_to_group_maps m.declarations
- in
- let type_infos =
- TypesAnalysis.analyze_type_declarations type_decls type_decls_list
- in
- let type_context = { C.type_decls_groups; type_decls; type_infos } in
- let fun_context = { C.fun_decls } in
- let global_context = { C.global_decls } in
- (type_context, fun_context, global_context)
-
-let initialize_eval_context (type_context : C.type_context)
- (fun_context : C.fun_context) (global_context : C.global_context)
- (type_vars : T.type_var list) : C.eval_ctx =
- C.reset_global_counters ();
- {
- C.type_context;
- C.fun_context;
- C.global_context;
- C.type_vars;
- C.env = [ C.Frame ];
- C.ended_regions = T.RegionId.Set.empty;
- }
-
-(** Initialize an evaluation context to execute a function.
-
- Introduces local variables initialized in the following manner:
- - input arguments are initialized as symbolic values
- - the remaining locals are initialized as [⊥]
- Abstractions are introduced for the regions present in the function
- signature.
-
- We return:
- - the initialized evaluation context
- - the list of symbolic values introduced for the input values
- - the instantiated function signature
- *)
-let initialize_symbolic_context_for_fun (type_context : C.type_context)
- (fun_context : C.fun_context) (global_context : C.global_context)
- (fdef : A.fun_decl) : C.eval_ctx * V.symbolic_value list * A.inst_fun_sig =
- (* The abstractions are not initialized the same way as for function
- * calls: they contain *loan* projectors, because they "provide" us
- * with the input values (which behave as if they had been returned
- * by some function calls...).
- * Also, note that we properly set the set of parents of every abstraction:
- * this should not be necessary, as those abstractions should never be
- * *automatically* ended (because ending some borrows requires to end
- * one of them), but rather selectively ended when generating code
- * for each of the backward functions. We do it only because we can
- * do it, and because it gives a bit of sanity.
- * *)
- let sg = fdef.signature in
- (* Create the context *)
- let ctx =
- initialize_eval_context type_context fun_context global_context
- sg.type_params
- in
- (* Instantiate the signature *)
- let type_params = List.map (fun tv -> T.TypeVar tv.T.index) sg.type_params in
- let inst_sg = instantiate_fun_sig type_params sg in
- (* Create fresh symbolic values for the inputs *)
- let input_svs =
- List.map (fun ty -> mk_fresh_symbolic_value V.SynthInput ty) inst_sg.inputs
- in
- (* Initialize the abstractions as empty (i.e., with no avalues) abstractions *)
- let call_id = C.fresh_fun_call_id () in
- assert (call_id = V.FunCallId.zero);
- let compute_abs_avalues (abs : V.abs) (ctx : C.eval_ctx) :
- C.eval_ctx * V.typed_avalue list =
- (* Project over the values - we use *loan* projectors, as explained above *)
- let avalues =
- List.map (mk_aproj_loans_value_from_symbolic_value abs.regions) input_svs
- in
- (ctx, avalues)
- in
- let region_can_end _ = true in
- let ctx =
- create_push_abstractions_from_abs_region_groups call_id V.SynthInput
- inst_sg.A.regions_hierarchy region_can_end compute_abs_avalues ctx
- in
- (* Split the variables between return var, inputs and remaining locals *)
- let body = Option.get fdef.body in
- let ret_var = List.hd body.locals in
- let input_vars, local_vars =
- Collections.List.split_at (List.tl body.locals) body.arg_count
- in
- (* Push the return variable (initialized with ⊥) *)
- let ctx = C.ctx_push_uninitialized_var ctx ret_var in
- (* Push the input variables (initialized with symbolic values) *)
- let input_values = List.map mk_typed_value_from_symbolic_value input_svs in
- let ctx = C.ctx_push_vars ctx (List.combine input_vars input_values) in
- (* Push the remaining local variables (initialized with ⊥) *)
- let ctx = C.ctx_push_uninitialized_vars ctx local_vars in
- (* Return *)
- (ctx, input_svs, inst_sg)
-
-(** Small helper.
-
- This is a continuation function called by the symbolic interpreter upon
- reaching the [return] instruction when synthesizing a *backward* function:
- this continuation takes care of doing the proper manipulations to finish
- the synthesis (mostly by ending abstractions).
-*)
-let evaluate_function_symbolic_synthesize_backward_from_return
- (config : C.config) (fdef : A.fun_decl) (inst_sg : A.inst_fun_sig)
- (back_id : T.RegionGroupId.id) (ctx : C.eval_ctx) : SA.expression option =
- (* We need to instantiate the function signature - to retrieve
- * the return type. Note that it is important to re-generate
- * an instantiation of the signature, so that we use fresh
- * region ids for the return abstractions. *)
- let sg = fdef.signature in
- let type_params = List.map (fun tv -> T.TypeVar tv.T.index) sg.type_params in
- let ret_inst_sg = instantiate_fun_sig type_params sg in
- let ret_rty = ret_inst_sg.output in
- (* Move the return value out of the return variable *)
- let cf_pop_frame = ctx_pop_frame config in
-
- (* We need to find the parents regions/abstractions of the region we
- * will end - this will allow us to, first, mark the other return
- * regions as non-endable, and, second, end those parent regions in
- * proper order. *)
- let parent_rgs = list_parent_region_groups sg back_id in
- let parent_input_abs_ids =
- T.RegionGroupId.mapi
- (fun rg_id rg ->
- if T.RegionGroupId.Set.mem rg_id parent_rgs then Some rg.T.id else None)
- inst_sg.regions_hierarchy
- in
- let parent_input_abs_ids =
- List.filter_map (fun x -> x) parent_input_abs_ids
- in
-
- (* Insert the return value in the return abstractions (by applying
- * borrow projections) *)
- let cf_consume_ret ret_value ctx =
- let ret_call_id = C.fresh_fun_call_id () in
- let compute_abs_avalues (abs : V.abs) (ctx : C.eval_ctx) :
- C.eval_ctx * V.typed_avalue list =
- let ctx, avalue =
- apply_proj_borrows_on_input_value config ctx abs.regions
- abs.ancestors_regions ret_value ret_rty
- in
- (ctx, [ avalue ])
- in
-
- (* Initialize and insert the abstractions in the context.
- *
- * We take care of disallowing ending the return regions which we
- * shouldn't end (see the documentation of the [can_end] field of [abs]
- * for more information. *)
- let parent_and_current_rgs = T.RegionGroupId.Set.add back_id parent_rgs in
- let region_can_end rid =
- T.RegionGroupId.Set.mem rid parent_and_current_rgs
- in
- assert (region_can_end back_id);
- let ctx =
- create_push_abstractions_from_abs_region_groups ret_call_id V.SynthRet
- ret_inst_sg.A.regions_hierarchy region_can_end compute_abs_avalues ctx
- in
-
- (* We now need to end the proper *input* abstractions - pay attention
- * to the fact that we end the *input* abstractions, not the *return*
- * abstractions (of course, the corresponding return abstractions will
- * automatically be ended, because they consumed values coming from the
- * input abstractions...) *)
- (* End the parent abstractions and the current abstraction - note that we
- * end them in an order which follows the regions hierarchy: it should lead
- * to generated code which has a better consistency between the parent
- * and children backward functions *)
- let current_abs_id =
- (T.RegionGroupId.nth inst_sg.regions_hierarchy back_id).id
- in
- let target_abs_ids = List.append parent_input_abs_ids [ current_abs_id ] in
- let cf_end_target_abs cf =
- List.fold_left
- (fun cf id -> end_abstraction config [] id cf)
- cf target_abs_ids
- in
- (* Generate the Return node *)
- let cf_return : m_fun = fun _ -> Some (SA.Return None) in
- (* Apply *)
- cf_end_target_abs cf_return ctx
- in
- cf_pop_frame cf_consume_ret ctx
-
-(** Evaluate a function with the symbolic interpreter.
-
- We return:
- - the list of symbolic values introduced for the input values (this is useful
- for the synthesis)
- - the symbolic AST generated by the symbolic execution
- *)
-let evaluate_function_symbolic (config : C.partial_config) (synthesize : bool)
- (type_context : C.type_context) (fun_context : C.fun_context)
- (global_context : C.global_context) (fdef : A.fun_decl)
- (back_id : T.RegionGroupId.id option) :
- V.symbolic_value list * SA.expression option =
- (* Debug *)
- let name_to_string () =
- Print.fun_name_to_string fdef.A.name
- ^ " ("
- ^ Print.option_to_string T.RegionGroupId.to_string back_id
- ^ ")"
- in
- log#ldebug (lazy ("evaluate_function_symbolic: " ^ name_to_string ()));
-
- (* Create the evaluation context *)
- let ctx, input_svs, inst_sg =
- initialize_symbolic_context_for_fun type_context fun_context global_context
- fdef
- in
-
- (* Create the continuation to finish the evaluation *)
- let config = C.config_of_partial C.SymbolicMode config in
- let cf_finish res ctx =
- match res with
- | Return ->
- if synthesize then
- (* There are two cases:
- * - if this is a forward translation, we retrieve the returned value.
- * - if this is a backward translation, we introduce "return"
- * abstractions to consume the return value, then end all the
- * abstractions up to the one in which we are interested.
- *)
- match back_id with
- | None ->
- (* Forward translation *)
- (* Pop the frame and retrieve the returned value at the same time*)
- let cf_pop = ctx_pop_frame config in
- (* Generate the Return node *)
- let cf_return ret_value : m_fun =
- fun _ -> Some (SA.Return (Some ret_value))
- in
- (* Apply *)
- cf_pop cf_return ctx
- | Some back_id ->
- (* Backward translation *)
- evaluate_function_symbolic_synthesize_backward_from_return config
- fdef inst_sg back_id ctx
- else None
- | Panic ->
- (* Note that as we explore all the execution branches, one of
- * the executions can lead to a panic *)
- if synthesize then Some SA.Panic else None
- | _ ->
- failwith ("evaluate_function_symbolic failed on: " ^ name_to_string ())
- in
-
- (* Evaluate the function *)
- let symbolic =
- eval_function_body config (Option.get fdef.A.body).body cf_finish ctx
- in
-
- (* Return *)
- (input_svs, symbolic)
-
-module Test = struct
- (** Test a unit function (taking no arguments) by evaluating it in an empty
- environment.
- *)
- let test_unit_function (config : C.partial_config) (crate : Crates.llbc_crate)
- (fid : A.FunDeclId.id) : unit =
- (* Retrieve the function declaration *)
- let fdef = A.FunDeclId.nth crate.functions fid in
- let body = Option.get fdef.body in
-
- (* Debug *)
- log#ldebug
- (lazy ("test_unit_function: " ^ Print.fun_name_to_string fdef.A.name));
-
- (* Sanity check - *)
- assert (List.length fdef.A.signature.region_params = 0);
- assert (List.length fdef.A.signature.type_params = 0);
- assert (body.A.arg_count = 0);
-
- (* Create the evaluation context *)
- let type_context, fun_context, global_context =
- compute_type_fun_global_contexts crate
- in
- let ctx =
- initialize_eval_context type_context fun_context global_context []
- in
-
- (* Insert the (uninitialized) local variables *)
- let ctx = C.ctx_push_uninitialized_vars ctx body.A.locals in
-
- (* Create the continuation to check the function's result *)
- let config = C.config_of_partial C.ConcreteMode config in
- let cf_check res ctx =
- match res with
- | Return ->
- (* Ok: drop the local variables and finish *)
- ctx_pop_frame config (fun _ _ -> None) ctx
- | _ ->
- failwith
- ("Unit test failed (concrete execution) on: "
- ^ Print.fun_name_to_string fdef.A.name)
- in
-
- (* Evaluate the function *)
- let _ = eval_function_body config body.body cf_check ctx in
- ()
-
- (** Small helper: return true if the function is a *transparent* unit function
- (no parameters, no arguments) - TODO: move *)
- let fun_decl_is_transparent_unit (def : A.fun_decl) : bool =
- match def.body with
- | None -> false
- | Some body ->
- body.arg_count = 0
- && List.length def.A.signature.region_params = 0
- && List.length def.A.signature.type_params = 0
- && List.length def.A.signature.inputs = 0
-
- (** Test all the unit functions in a list of function definitions *)
- let test_unit_functions (config : C.partial_config)
- (crate : Crates.llbc_crate) : unit =
- let unit_funs = List.filter fun_decl_is_transparent_unit crate.functions in
- let test_unit_fun (def : A.fun_decl) : unit =
- test_unit_function config crate def.A.def_id
- in
- List.iter test_unit_fun unit_funs
-
- (** Execute the symbolic interpreter on a function. *)
- let test_function_symbolic (config : C.partial_config) (synthesize : bool)
- (type_context : C.type_context) (fun_context : C.fun_context)
- (global_context : C.global_context) (fdef : A.fun_decl) : unit =
- (* Debug *)
- log#ldebug
- (lazy ("test_function_symbolic: " ^ Print.fun_name_to_string fdef.A.name));
-
- (* Evaluate *)
- let evaluate =
- evaluate_function_symbolic config synthesize type_context fun_context
- global_context fdef
- in
- (* Execute the forward function *)
- let _ = evaluate None in
- (* Execute the backward functions *)
- let _ =
- T.RegionGroupId.mapi
- (fun gid _ -> evaluate (Some gid))
- fdef.signature.regions_hierarchy
- in
-
- ()
-
- (** Small helper *)
- let fun_decl_is_transparent (def : A.fun_decl) : bool =
- Option.is_some def.body
-
- (** Execute the symbolic interpreter on a list of functions.
-
- TODO: for now we ignore the functions which contain loops, because
- they are not supported by the symbolic interpreter.
- *)
- let test_functions_symbolic (config : C.partial_config) (synthesize : bool)
- (crate : Crates.llbc_crate) : unit =
- (* Filter the functions which contain loops *)
- let no_loop_funs =
- List.filter
- (fun f -> not (LlbcAstUtils.fun_decl_has_loops f))
- crate.functions
- in
- (* Filter the opaque functions *)
- let no_loop_funs = List.filter fun_decl_is_transparent no_loop_funs in
- let type_context, fun_context, global_context =
- compute_type_fun_global_contexts crate
- in
- let test_fun (def : A.fun_decl) : unit =
- (* Execute the function - note that as the symbolic interpreter explores
- * all the path, some executions are expected to "panic": we thus don't
- * check the return value *)
- test_function_symbolic config synthesize type_context fun_context
- global_context def
- in
- List.iter test_fun no_loop_funs
-end
diff --git a/src/InterpreterBorrows.ml b/src/InterpreterBorrows.ml
deleted file mode 100644
index 30c3b221..00000000
--- a/src/InterpreterBorrows.ml
+++ /dev/null
@@ -1,1580 +0,0 @@
-module T = Types
-module V = Values
-module C = Contexts
-module Subst = Substitute
-module L = Logging
-module S = SynthesizeSymbolic
-open Cps
-open ValuesUtils
-open TypesUtils
-open InterpreterUtils
-open InterpreterBorrowsCore
-open InterpreterProjectors
-
-(** The local logger *)
-let log = InterpreterBorrowsCore.log
-
-(** Auxiliary function to end borrows: lookup a borrow in the environment,
- update it (by returning an updated environment where the borrow has been
- replaced by {!V.Bottom})) if we can end the borrow (for instance, it is not
- an outer borrow...) or return the reason why we couldn't update the borrow.
-
- [end_borrow] then simply performs a loop: as long as we need to end (outer)
- borrows, we end them, before finally ending the borrow we wanted to end in the
- first place.
-
- Note that it is possible to end a borrow in an abstraction, without ending
- the whole abstraction, if the corresponding loan is inside the abstraction
- as well. The [allowed_abs] parameter controls whether we allow to end borrows
- in an abstraction or not, and in which abstraction.
-*)
-let end_borrow_get_borrow (allowed_abs : V.AbstractionId.id option)
- (l : V.BorrowId.id) (ctx : C.eval_ctx) :
- (C.eval_ctx * g_borrow_content option, priority_borrows_or_abs) result =
- (* We use a reference to communicate the kind of borrow we found, if we
- * find one *)
- let replaced_bc : g_borrow_content option ref = ref None in
- let set_replaced_bc (bc : g_borrow_content) =
- assert (Option.is_none !replaced_bc);
- replaced_bc := Some bc
- in
- (* Raise an exception if:
- * - there are outer borrows
- * - if we are inside an abstraction
- * - there are inner loans
- * this exception is caught in a wrapper function *)
- let raise_if_priority (outer : V.AbstractionId.id option * borrow_ids option)
- (borrowed_value : V.typed_value option) =
- (* First, look for outer borrows or abstraction *)
- let outer_abs, outer_borrows = outer in
- (match outer_abs with
- | Some abs -> (
- if
- (* Check if we can end borrows inside this abstraction *)
- Some abs <> allowed_abs
- then raise (FoundPriority (OuterAbs abs))
- else
- match outer_borrows with
- | Some borrows -> raise (FoundPriority (OuterBorrows borrows))
- | None -> ())
- | None -> (
- match outer_borrows with
- | Some borrows -> raise (FoundPriority (OuterBorrows borrows))
- | None -> ()));
- (* Then check if there are inner loans *)
- match borrowed_value with
- | None -> ()
- | Some v -> (
- match get_first_loan_in_value v with
- | None -> ()
- | Some c -> (
- match c with
- | V.SharedLoan (bids, _) ->
- raise (FoundPriority (InnerLoans (Borrows bids)))
- | V.MutLoan bid -> raise (FoundPriority (InnerLoans (Borrow bid)))))
- in
-
- (* The environment is used to keep track of the outer loans *)
- let obj =
- object
- inherit [_] C.map_eval_ctx as super
-
- (** We reimplement {!visit_Loan} because we may have to update the
- outer borrows *)
- method! visit_Loan (outer : V.AbstractionId.id option * borrow_ids option)
- lc =
- match lc with
- | V.MutLoan bid -> V.Loan (super#visit_MutLoan outer bid)
- | V.SharedLoan (bids, v) ->
- (* Update the outer borrows before diving into the shared value *)
- let outer = update_outer_borrows outer (Borrows bids) in
- V.Loan (super#visit_SharedLoan outer bids v)
-
- method! visit_Borrow outer bc =
- match bc with
- | SharedBorrow (_, l') | InactivatedMutBorrow (_, l') ->
- (* Check if this is the borrow we are looking for *)
- if l = l' then (
- (* Check if there are outer borrows or if we are inside an abstraction *)
- raise_if_priority outer None;
- (* Register the update *)
- set_replaced_bc (Concrete bc);
- (* Update the value *)
- V.Bottom)
- else super#visit_Borrow outer bc
- | V.MutBorrow (l', bv) ->
- (* Check if this is the borrow we are looking for *)
- if l = l' then (
- (* Check if there are outer borrows or if we are inside an abstraction *)
- raise_if_priority outer (Some bv);
- (* Register the update *)
- set_replaced_bc (Concrete bc);
- (* Update the value *)
- V.Bottom)
- else
- (* Update the outer borrows before diving into the borrowed value *)
- let outer = update_outer_borrows outer (Borrow l') in
- V.Borrow (super#visit_MutBorrow outer l' bv)
-
- (** We reimplement {!visit_ALoan} because we may have to update the
- outer borrows *)
- method! visit_ALoan outer lc =
- (* Note that the children avalues are just other, independent loans,
- * so we don't need to update the outer borrows when diving in.
- * We keep track of the parents/children relationship only because we
- * need it to properly instantiate the backward functions when generating
- * the pure translation. *)
- match lc with
- | V.AMutLoan (_, _) ->
- (* Nothing special to do *)
- super#visit_ALoan outer lc
- | V.ASharedLoan (bids, v, av) ->
- (* Explore the shared value - we need to update the outer borrows *)
- let souter = update_outer_borrows outer (Borrows bids) in
- let v = super#visit_typed_value souter v in
- (* Explore the child avalue - we keep the same outer borrows *)
- let av = super#visit_typed_avalue outer av in
- (* Reconstruct *)
- V.ALoan (V.ASharedLoan (bids, v, av))
- | V.AEndedMutLoan { given_back = _; child = _; given_back_meta = _ }
- | V.AEndedSharedLoan _
- (* The loan has ended, so no need to update the outer borrows *)
- | V.AIgnoredMutLoan _ (* Nothing special to do *)
- | V.AEndedIgnoredMutLoan
- { given_back = _; child = _; given_back_meta = _ }
- (* Nothing special to do *)
- | V.AIgnoredSharedLoan _ ->
- (* Nothing special to do *)
- super#visit_ALoan outer lc
-
- method! visit_ABorrow outer bc =
- match bc with
- | V.AMutBorrow (_, bid, _) ->
- (* Check if this is the borrow we are looking for *)
- if bid = l then (
- (* When ending a mut borrow, there are two cases:
- * - in the general case, we have to end the whole abstraction
- * (and thus raise an exception to signal that to the caller)
- * - in some situations, the associated loan is inside the same
- * abstraction as the borrow. In this situation, we can end
- * the borrow without ending the whole abstraction, and we
- * simply move the child avalue around.
- *)
- (* Check there are outer borrows, or if we need to end the whole
- * abstraction *)
- raise_if_priority outer None;
- (* Register the update *)
- set_replaced_bc (Abstract bc);
- (* Update the value - note that we are necessarily in the second
- * of the two cases described above.
- * Also note that, as we are moving the borrowed value inside the
- * abstraction (and not really giving the value back to the context)
- * we do not insert {!AEndedMutBorrow} but rather {!ABottom} *)
- V.ABottom)
- else
- (* Update the outer borrows before diving into the child avalue *)
- let outer = update_outer_borrows outer (Borrow bid) in
- super#visit_ABorrow outer bc
- | V.ASharedBorrow bid ->
- (* Check if this is the borrow we are looking for *)
- if bid = l then (
- (* Check there are outer borrows, or if we need to end the whole
- * abstraction *)
- raise_if_priority outer None;
- (* Register the update *)
- set_replaced_bc (Abstract bc);
- (* Update the value - note that we are necessarily in the second
- * of the two cases described above *)
- V.ABottom)
- else super#visit_ABorrow outer bc
- | V.AIgnoredMutBorrow (_, _)
- | V.AEndedMutBorrow _
- | V.AEndedIgnoredMutBorrow
- { given_back_loans_proj = _; child = _; given_back_meta = _ }
- | V.AEndedSharedBorrow ->
- (* Nothing special to do *)
- super#visit_ABorrow outer bc
- | V.AProjSharedBorrow asb ->
- (* Check if the borrow we are looking for is in the asb *)
- if borrow_in_asb l asb then (
- (* Check there are outer borrows, or if we need to end the whole
- * abstraction *)
- raise_if_priority outer None;
- (* Register the update *)
- set_replaced_bc (Abstract bc);
- (* Update the value - note that we are necessarily in the second
- * of the two cases described above *)
- let asb = remove_borrow_from_asb l asb in
- V.ABorrow (V.AProjSharedBorrow asb))
- else (* Nothing special to do *)
- super#visit_ABorrow outer bc
-
- method! visit_abs outer abs =
- (* Update the outer abs *)
- let outer_abs, outer_borrows = outer in
- assert (Option.is_none outer_abs);
- assert (Option.is_none outer_borrows);
- let outer = (Some abs.V.abs_id, None) in
- super#visit_abs outer abs
- end
- in
- (* Catch the exceptions - raised if there are outer borrows *)
- try
- let ctx = obj#visit_eval_ctx (None, None) ctx in
- Ok (ctx, !replaced_bc)
- with FoundPriority outers -> Error outers
-
-(** Auxiliary function to end borrows. See [give_back].
-
- When we end a mutable borrow, we need to "give back" the value it contained
- to its original owner by reinserting it at the proper position.
-
- Note that this function checks that there is exactly one loan to which we
- give the value back.
- TODO: this was not the case before, so some sanity checks are not useful anymore.
- *)
-let give_back_value (config : C.config) (bid : V.BorrowId.id)
- (nv : V.typed_value) (ctx : C.eval_ctx) : C.eval_ctx =
- (* Sanity check *)
- assert (not (loans_in_value nv));
- assert (not (bottom_in_value ctx.ended_regions nv));
- (* Debug *)
- log#ldebug
- (lazy
- ("give_back_value:\n- bid: " ^ V.BorrowId.to_string bid ^ "\n- value: "
- ^ typed_value_to_string ctx nv
- ^ "\n- context:\n" ^ eval_ctx_to_string ctx ^ "\n"));
- (* We use a reference to check that we updated exactly one loan *)
- let replaced : bool ref = ref false in
- let set_replaced () =
- assert (not !replaced);
- replaced := true
- in
- (* Whenever giving back symbolic values, they shouldn't contain already ended regions *)
- let check_symbolic_no_ended = true in
- (* We sometimes need to reborrow values while giving a value back due: prepare that *)
- let allow_reborrows = true in
- let fresh_reborrow, apply_registered_reborrows =
- prepare_reborrows config allow_reborrows
- in
- (* The visitor to give back the values *)
- let obj =
- object (self)
- inherit [_] C.map_eval_ctx as super
-
- (** This is a bit annoying, but as we need the type of the value we
- are exploring, for sanity checks, we need to implement
- {!visit_typed_avalue} instead of
- overriding {!visit_ALoan} *)
- method! visit_typed_value opt_abs (v : V.typed_value) : V.typed_value =
- match v.V.value with
- | V.Loan lc ->
- let value = self#visit_typed_Loan opt_abs v.V.ty lc in
- ({ v with V.value } : V.typed_value)
- | _ -> super#visit_typed_value opt_abs v
-
- method visit_typed_Loan opt_abs ty lc =
- match lc with
- | V.SharedLoan (bids, v) ->
- (* We are giving back a value (i.e., the content of a *mutable*
- * borrow): nothing special to do *)
- V.Loan (super#visit_SharedLoan opt_abs bids v)
- | V.MutLoan bid' ->
- (* Check if this is the loan we are looking for *)
- if bid' = bid then (
- (* Sanity check *)
- let expected_ty = ty in
- if nv.V.ty <> expected_ty then (
- log#serror
- ("give_back_value: improper type:\n- expected: "
- ^ ety_to_string ctx ty ^ "\n- received: "
- ^ ety_to_string ctx nv.V.ty);
- failwith "Value given back doesn't have the proper type");
- (* Replace *)
- set_replaced ();
- nv.V.value)
- else V.Loan (super#visit_MutLoan opt_abs bid')
-
- (** This is a bit annoying, but as we need the type of the avalue we
- are exploring, in order to be able to project the value we give
- back, we need to reimplement {!visit_typed_avalue} instead of
- {!visit_ALoan} *)
- method! visit_typed_avalue opt_abs (av : V.typed_avalue) : V.typed_avalue
- =
- match av.V.value with
- | V.ALoan lc ->
- let value = self#visit_typed_ALoan opt_abs av.V.ty lc in
- ({ av with V.value } : V.typed_avalue)
- | _ -> super#visit_typed_avalue opt_abs av
-
- (** We need to inspect ignored mutable borrows, to insert loan projectors
- if necessary.
- *)
- method! visit_ABorrow (opt_abs : V.abs option) (bc : V.aborrow_content)
- : V.avalue =
- match bc with
- | V.AIgnoredMutBorrow (bid', child) ->
- if bid' = Some bid then
- (* Insert a loans projector - note that if this case happens,
- * it is necessarily because we ended a parent abstraction,
- * and the given back value is thus a symbolic value *)
- match nv.V.value with
- | V.Symbolic sv ->
- let abs = Option.get opt_abs in
- (* Remember the given back value as a meta-value
- * TODO: it is a bit annoying to have to deconstruct
- * the value... Think about a more elegant way. *)
- let given_back_meta = as_symbolic nv.value in
- (* The loan projector *)
- let given_back_loans_proj =
- mk_aproj_loans_value_from_symbolic_value abs.regions sv
- in
- (* Continue giving back in the child value *)
- let child = super#visit_typed_avalue opt_abs child in
- (* Return *)
- V.ABorrow
- (V.AEndedIgnoredMutBorrow
- { given_back_loans_proj; child; given_back_meta })
- | _ -> failwith "Unreachable"
- else
- (* Continue exploring *)
- V.ABorrow (super#visit_AIgnoredMutBorrow opt_abs bid' child)
- | _ ->
- (* Continue exploring *)
- super#visit_ABorrow opt_abs bc
-
- (** We are not specializing an already existing method, but adding a
- new method (for projections, we need type information) *)
- method visit_typed_ALoan (opt_abs : V.abs option) (ty : T.rty)
- (lc : V.aloan_content) : V.avalue =
- (* Preparing a bit *)
- let regions, ancestors_regions =
- match opt_abs with
- | None -> failwith "Unreachable"
- | Some abs -> (abs.V.regions, abs.V.ancestors_regions)
- in
- (* Rk.: there is a small issue with the types of the aloan values.
- * See the comment at the level of definition of {!typed_avalue} *)
- let borrowed_value_aty =
- let _, ty, _ = ty_get_ref ty in
- ty
- in
- match lc with
- | V.AMutLoan (bid', child) ->
- if bid' = bid then (
- (* This is the loan we are looking for: apply the projection to
- * the value we give back and replaced this mutable loan with
- * an ended loan *)
- (* Register the insertion *)
- set_replaced ();
- (* Remember the given back value as a meta-value *)
- let given_back_meta = nv in
- (* Apply the projection *)
- let given_back =
- apply_proj_borrows check_symbolic_no_ended ctx fresh_reborrow
- regions ancestors_regions nv borrowed_value_aty
- in
- (* Continue giving back in the child value *)
- let child = super#visit_typed_avalue opt_abs child in
- (* Return the new value *)
- V.ALoan (V.AEndedMutLoan { child; given_back; given_back_meta }))
- else (* Continue exploring *)
- super#visit_ALoan opt_abs lc
- | V.ASharedLoan (_, _, _) ->
- (* We are giving back a value to a *mutable* loan: nothing special to do *)
- super#visit_ALoan opt_abs lc
- | V.AEndedMutLoan { child = _; given_back = _; given_back_meta = _ }
- | V.AEndedSharedLoan (_, _) ->
- (* Nothing special to do *)
- super#visit_ALoan opt_abs lc
- | V.AIgnoredMutLoan (bid', child) ->
- (* This loan is ignored, but we may have to project on a subvalue
- * of the value which is given back *)
- if bid' = bid then
- (* Remember the given back value as a meta-value *)
- let given_back_meta = nv in
- (* Note that we replace the ignored mut loan by an *ended* ignored
- * mut loan. Also, this is not the loan we are looking for *per se*:
- * we don't register the fact that we inserted the value somewhere
- * (i.e., we don't call {!set_replaced}) *)
- let given_back =
- apply_proj_borrows check_symbolic_no_ended ctx fresh_reborrow
- regions ancestors_regions nv borrowed_value_aty
- in
- (* Continue giving back in the child value *)
- let child = super#visit_typed_avalue opt_abs child in
- V.ALoan
- (V.AEndedIgnoredMutLoan { given_back; child; given_back_meta })
- else super#visit_ALoan opt_abs lc
- | V.AEndedIgnoredMutLoan
- { given_back = _; child = _; given_back_meta = _ }
- | V.AIgnoredSharedLoan _ ->
- (* Nothing special to do *)
- super#visit_ALoan opt_abs lc
-
- method! visit_Abs opt_abs abs =
- (* We remember in which abstraction we are before diving -
- * this is necessary for projecting values: we need to know
- * over which regions to project *)
- assert (Option.is_none opt_abs);
- super#visit_Abs (Some abs) abs
- end
- in
-
- (* Explore the environment *)
- let ctx = obj#visit_eval_ctx None ctx in
- (* Check we gave back to exactly one loan *)
- assert !replaced;
- (* Apply the reborrows *)
- apply_registered_reborrows ctx
-
-(** Give back a *modified* symbolic value. *)
-let give_back_symbolic_value (_config : C.config)
- (proj_regions : T.RegionId.Set.t) (proj_ty : T.rty) (sv : V.symbolic_value)
- (nsv : V.symbolic_value) (ctx : C.eval_ctx) : C.eval_ctx =
- (* Sanity checks *)
- assert (sv.sv_id <> nsv.sv_id);
- (match nsv.sv_kind with
- | V.SynthInputGivenBack | V.SynthRetGivenBack | V.FunCallGivenBack -> ()
- | V.FunCallRet | V.SynthInput | V.Global -> failwith "Unrechable");
- (* Store the given-back value as a meta-value for synthesis purposes *)
- let mv = nsv in
- (* Substitution function, to replace the borrow projectors over symbolic values *)
- let subst (_abs : V.abs) local_given_back =
- (* See the below comments: there is something wrong here *)
- let _ = raise Errors.Unimplemented in
- (* Compute the projection over the given back value *)
- let child_proj =
- match nsv.sv_kind with
- | V.SynthRetGivenBack ->
- (* The given back value comes from the return value of the function
- we are currently synthesizing (as it is given back, it means
- we ended one of the regions appearing in the signature: we are
- currently synthesizing one of the backward functions).
-
- As we don't allow borrow overwrites on returned value, we can
- (and MUST) forget the borrows *)
- V.AIgnoredProjBorrows
- | V.FunCallGivenBack ->
- (* TODO: there is something wrong here.
- Consider this:
- {[
- abs0 {'a} { AProjLoans (s0 : &'a mut T) [] }
- abs1 {'b} { AProjBorrows (s0 : &'a mut T <: &'b mut T) }
- ]}
-
- Upon ending abs1, we give back some fresh symbolic value [s1],
- that we reinsert where the loan for [s0] is. However, the mutable
- borrow in the type [&'a mut T] was ended: we give back a value of
- type [T]! We thus *mustn't* introduce a projector here.
- *)
- V.AProjBorrows (nsv, sv.V.sv_ty)
- | _ -> failwith "Unreachable"
- in
- V.AProjLoans (sv, (mv, child_proj) :: local_given_back)
- in
- update_intersecting_aproj_loans proj_regions proj_ty sv subst ctx
-
-(** Auxiliary function to end borrows. See [give_back].
-
- This function is similar to {!give_back_value} but gives back an {!V.avalue}
- (coming from an abstraction).
-
- It is used when ending a borrow inside an abstraction, when the corresponding
- loan is inside the same abstraction (in which case we don't need to end the whole
- abstraction).
-
- REMARK: this function can't be used to give back the values borrowed by
- end abstraction when ending this abstraction. When doing this, we need
- to convert the {!V.avalue} to a {!type:V.value} by introducing the proper symbolic values.
- *)
-let give_back_avalue_to_same_abstraction (_config : C.config)
- (bid : V.BorrowId.id) (mv : V.mvalue) (nv : V.typed_avalue)
- (ctx : C.eval_ctx) : C.eval_ctx =
- (* We use a reference to check that we updated exactly one loan *)
- let replaced : bool ref = ref false in
- let set_replaced () =
- assert (not !replaced);
- replaced := true
- in
- let obj =
- object (self)
- inherit [_] C.map_eval_ctx as super
-
- (** This is a bit annoying, but as we need the type of the avalue we
- are exploring, in order to be able to project the value we give
- back, we need to reimplement {!visit_typed_avalue} instead of
- {!visit_ALoan} *)
- method! visit_typed_avalue opt_abs (av : V.typed_avalue) : V.typed_avalue
- =
- match av.V.value with
- | V.ALoan lc ->
- let value = self#visit_typed_ALoan opt_abs av.V.ty lc in
- ({ av with V.value } : V.typed_avalue)
- | _ -> super#visit_typed_avalue opt_abs av
-
- (** We are not specializing an already existing method, but adding a
- new method (for projections, we need type information) *)
- method visit_typed_ALoan (opt_abs : V.abs option) (ty : T.rty)
- (lc : V.aloan_content) : V.avalue =
- match lc with
- | V.AMutLoan (bid', child) ->
- if bid' = bid then (
- (* Sanity check - about why we need to call {!ty_get_ref}
- * (and don't do the same thing as in {!give_back_value})
- * see the comment at the level of the definition of
- * {!typed_avalue} *)
- let _, expected_ty, _ = ty_get_ref ty in
- if nv.V.ty <> expected_ty then (
- log#serror
- ("give_back_avalue_to_same_abstraction: improper type:\n\
- - expected: " ^ rty_to_string ctx ty ^ "\n- received: "
- ^ rty_to_string ctx nv.V.ty);
- failwith "Value given back doesn't have the proper type");
- (* This is the loan we are looking for: apply the projection to
- * the value we give back and replaced this mutable loan with
- * an ended loan *)
- (* Register the insertion *)
- set_replaced ();
- (* Return the new value *)
- V.ALoan
- (V.AEndedMutLoan
- { given_back = nv; child; given_back_meta = mv }))
- else (* Continue exploring *)
- super#visit_ALoan opt_abs lc
- | V.ASharedLoan (_, _, _)
- (* We are giving back a value to a *mutable* loan: nothing special to do *)
- | V.AEndedMutLoan { given_back = _; child = _; given_back_meta = _ }
- | V.AEndedSharedLoan (_, _) ->
- (* Nothing special to do *)
- super#visit_ALoan opt_abs lc
- | V.AIgnoredMutLoan (bid', child) ->
- (* This loan is ignored, but we may have to project on a subvalue
- * of the value which is given back *)
- if bid' = bid then (
- (* Note that we replace the ignored mut loan by an *ended* ignored
- * mut loan. Also, this is not the loan we are looking for *per se*:
- * we don't register the fact that we inserted the value somewhere
- * (i.e., we don't call {!set_replaced}) *)
- (* Sanity check *)
- assert (nv.V.ty = ty);
- V.ALoan
- (V.AEndedIgnoredMutLoan
- { given_back = nv; child; given_back_meta = mv }))
- else super#visit_ALoan opt_abs lc
- | V.AEndedIgnoredMutLoan
- { given_back = _; child = _; given_back_meta = _ }
- | V.AIgnoredSharedLoan _ ->
- (* Nothing special to do *)
- super#visit_ALoan opt_abs lc
- end
- in
-
- (* Explore the environment *)
- let ctx = obj#visit_eval_ctx None ctx in
- (* Check we gave back to exactly one loan *)
- assert !replaced;
- (* Return *)
- ctx
-
-(** Auxiliary function to end borrows. See [give_back].
-
- When we end a shared borrow, we need to remove the borrow id from the list
- of borrows to the shared value.
-
- Note that this function checks that there is exactly one shared loan that
- we update.
- TODO: this was not the case before, so some sanity checks are not useful anymore.
- *)
-let give_back_shared _config (bid : V.BorrowId.id) (ctx : C.eval_ctx) :
- C.eval_ctx =
- (* We use a reference to check that we updated exactly one loan *)
- let replaced : bool ref = ref false in
- let set_replaced () =
- assert (not !replaced);
- replaced := true
- in
- let obj =
- object
- inherit [_] C.map_eval_ctx as super
-
- method! visit_Loan opt_abs lc =
- match lc with
- | V.SharedLoan (bids, shared_value) ->
- if V.BorrowId.Set.mem bid bids then (
- (* This is the loan we are looking for *)
- set_replaced ();
- (* If there remains exactly one borrow identifier, we need
- * to end the loan. Otherwise, we just remove the current
- * loan identifier *)
- if V.BorrowId.Set.cardinal bids = 1 then shared_value.V.value
- else
- V.Loan
- (V.SharedLoan (V.BorrowId.Set.remove bid bids, shared_value)))
- else
- (* Not the loan we are looking for: continue exploring *)
- V.Loan (super#visit_SharedLoan opt_abs bids shared_value)
- | V.MutLoan bid' ->
- (* We are giving back a *shared* borrow: nothing special to do *)
- V.Loan (super#visit_MutLoan opt_abs bid')
-
- method! visit_ALoan opt_abs lc =
- match lc with
- | V.AMutLoan (bid, av) ->
- (* Nothing special to do (we are giving back a *shared* borrow) *)
- V.ALoan (super#visit_AMutLoan opt_abs bid av)
- | V.ASharedLoan (bids, shared_value, child) ->
- if V.BorrowId.Set.mem bid bids then (
- (* This is the loan we are looking for *)
- set_replaced ();
- (* If there remains exactly one borrow identifier, we need
- * to end the loan. Otherwise, we just remove the current
- * loan identifier *)
- if V.BorrowId.Set.cardinal bids = 1 then
- V.ALoan (V.AEndedSharedLoan (shared_value, child))
- else
- V.ALoan
- (V.ASharedLoan
- (V.BorrowId.Set.remove bid bids, shared_value, child)))
- else
- (* Not the loan we are looking for: continue exploring *)
- super#visit_ALoan opt_abs lc
- | V.AEndedMutLoan { given_back = _; child = _; given_back_meta = _ }
- (* Nothing special to do (the loan has ended) *)
- | V.AEndedSharedLoan (_, _)
- (* Nothing special to do (the loan has ended) *)
- | V.AIgnoredMutLoan (_, _)
- (* Nothing special to do (we are giving back a *shared* borrow) *)
- | V.AEndedIgnoredMutLoan
- { given_back = _; child = _; given_back_meta = _ }
- (* Nothing special to do *)
- | V.AIgnoredSharedLoan _ ->
- (* Nothing special to do *)
- super#visit_ALoan opt_abs lc
- end
- in
-
- (* Explore the environment *)
- let ctx = obj#visit_eval_ctx None ctx in
- (* Check we gave back to exactly one loan *)
- assert !replaced;
- (* Return *)
- ctx
-
-(** When copying values, we duplicate the shared borrows. This is tantamount
- to reborrowing the shared value. The following function applies this change
- to an environment by inserting a new borrow id in the set of borrows tracked
- by a shared value, referenced by the [original_bid] argument.
- *)
-let reborrow_shared (original_bid : V.BorrowId.id) (new_bid : V.BorrowId.id)
- (ctx : C.eval_ctx) : C.eval_ctx =
- (* Keep track of changes *)
- let r = ref false in
- let set_ref () =
- assert (not !r);
- r := true
- in
-
- let obj =
- object
- inherit [_] C.map_env as super
-
- method! visit_SharedLoan env bids sv =
- (* Shared loan: check if the borrow id we are looking for is in the
- set of borrow ids. If yes, insert the new borrow id, otherwise
- explore inside the shared value *)
- if V.BorrowId.Set.mem original_bid bids then (
- set_ref ();
- let bids' = V.BorrowId.Set.add new_bid bids in
- V.SharedLoan (bids', sv))
- else super#visit_SharedLoan env bids sv
-
- method! visit_ASharedLoan env bids v av =
- (* This case is similar to the {!SharedLoan} case *)
- if V.BorrowId.Set.mem original_bid bids then (
- set_ref ();
- let bids' = V.BorrowId.Set.add new_bid bids in
- V.ASharedLoan (bids', v, av))
- else super#visit_ASharedLoan env bids v av
- end
- in
-
- let env = obj#visit_env () ctx.env in
- (* Check that we reborrowed once *)
- assert !r;
- { ctx with env }
-
-(** Auxiliary function: see [end_borrow] *)
-let give_back (config : C.config) (l : V.BorrowId.id) (bc : g_borrow_content)
- (ctx : C.eval_ctx) : C.eval_ctx =
- (* Debug *)
- log#ldebug
- (lazy
- (let bc =
- match bc with
- | Concrete bc -> borrow_content_to_string ctx bc
- | Abstract bc -> aborrow_content_to_string ctx bc
- in
- "give_back:\n- bid: " ^ V.BorrowId.to_string l ^ "\n- content: " ^ bc
- ^ "\n- context:\n" ^ eval_ctx_to_string ctx ^ "\n"));
- (* This is used for sanity checks *)
- let sanity_ek =
- { enter_shared_loans = true; enter_mut_borrows = true; enter_abs = true }
- in
- match bc with
- | Concrete (V.MutBorrow (l', tv)) ->
- (* Sanity check *)
- assert (l' = l);
- assert (not (loans_in_value tv));
- (* Check that the corresponding loan is somewhere - purely a sanity check *)
- assert (Option.is_some (lookup_loan_opt sanity_ek l ctx));
- (* Update the context *)
- give_back_value config l tv ctx
- | Concrete (V.SharedBorrow (_, l') | V.InactivatedMutBorrow (_, l')) ->
- (* Sanity check *)
- assert (l' = l);
- (* Check that the borrow is somewhere - purely a sanity check *)
- assert (Option.is_some (lookup_loan_opt sanity_ek l ctx));
- (* Update the context *)
- give_back_shared config l ctx
- | Abstract (V.AMutBorrow (mv, l', av)) ->
- (* Sanity check *)
- assert (l' = l);
- (* Check that the corresponding loan is somewhere - purely a sanity check *)
- assert (Option.is_some (lookup_loan_opt sanity_ek l ctx));
- (* Update the context *)
- give_back_avalue_to_same_abstraction config l mv av ctx
- | Abstract (V.ASharedBorrow l') ->
- (* Sanity check *)
- assert (l' = l);
- (* Check that the borrow is somewhere - purely a sanity check *)
- assert (Option.is_some (lookup_loan_opt sanity_ek l ctx));
- (* Update the context *)
- give_back_shared config l ctx
- | Abstract (V.AProjSharedBorrow asb) ->
- (* Sanity check *)
- assert (borrow_in_asb l asb);
- (* Update the context *)
- give_back_shared config l ctx
- | Abstract
- ( V.AEndedMutBorrow _ | V.AIgnoredMutBorrow _ | V.AEndedIgnoredMutBorrow _
- | V.AEndedSharedBorrow ) ->
- failwith "Unreachable"
-
-(** Convert an {!type:V.avalue} to a {!type:V.value}.
-
- This function is used when ending abstractions: whenever we end a borrow
- in an abstraction, we converted the borrowed {!V.avalue} to a fresh symbolic
- {!type:V.value}, then give back this {!type:V.value} to the context.
-
- Note that some regions may have ended in the symbolic value we generate.
- For instance, consider the following function signature:
- {[
- fn f<'a>(x : &'a mut &'a mut u32);
- ]}
- When ending the abstraction, the value given back for the outer borrow
- should be ⊥. In practice, we will give back a symbolic value which can't
- be expanded (because expanding this symbolic value would require expanding
- a reference whose region has already ended).
- *)
-let convert_avalue_to_given_back_value (abs_kind : V.abs_kind)
- (av : V.typed_avalue) : V.symbolic_value =
- let sv_kind =
- match abs_kind with
- | V.FunCall -> V.FunCallGivenBack
- | V.SynthRet -> V.SynthRetGivenBack
- | V.SynthInput -> V.SynthInputGivenBack
- in
- mk_fresh_symbolic_value sv_kind av.V.ty
-
-(** End a borrow identified by its borrow id in a context.
-
- Rk.: from now onwards, the functions are written in continuation passing style.
- The reason is that when ending borrows we may end abstractions, which results
- in synthesized code.
-
- First lookup the borrow in the context and replace it with {!V.Bottom}.
- Then, check that there is an associated loan in the context. When moving
- values, before putting the value in its destination, we get an
- intermediate state where some values are "outside" the context and thus
- inaccessible. As {!give_back_value} just performs a map for instance (TODO:
- not the case anymore), we need to check independently that there is indeed a
- loan ready to receive the value we give back (note that we also have other
- invariants like: there is exacly one mutable loan associated to a mutable
- borrow, etc. but they are more easily maintained).
- Note that in theory, we shouldn't never reach a problematic state as the
- one we describe above, because when we move a value we need to end all the
- loans inside before moving it. Still, it is a very useful sanity check.
- Finally, give the values back.
-
- Of course, we end outer borrows before updating the target borrow if it
- proves necessary.
- If a borrow is inside an abstraction, we need to end the whole abstraction,
- at the exception of the case where the loan corresponding to the borrow is
- inside the same abstraction. We control this with the [allowed_abs] parameter:
- if it is not [None], we allow ending a borrow if it is inside the given
- abstraction. In practice, if the value is [Some abs_id], we should have
- checked that the corresponding loan is inside the abstraction given by
- [abs_id] before. In practice, only {!end_borrow} should call itself
- with [allowed_abs = Some ...], all the other calls should use [allowed_abs = None]:
- if you look ath the implementation details, [end_borrow] performs
- all tne necessary checks in case a borrow is inside an abstraction.
- TODO: we shouldn't allow this last case (end a borrow when the corresponding
- loan is in the same abstraction).
-
- TODO: we should split this function in two: one function which doesn't
- perform anything smart and is trusted, and another function for the
- book-keeping.
- *)
-let rec end_borrow (config : C.config) (chain : borrow_or_abs_ids)
- (allowed_abs : V.AbstractionId.id option) (l : V.BorrowId.id) : cm_fun =
- fun cf ctx ->
- (* Check that we don't loop *)
- let chain0 = chain in
- let chain = add_borrow_or_abs_id_to_chain "end_borrow: " (BorrowId l) chain in
- log#ldebug
- (lazy
- ("end borrow: " ^ V.BorrowId.to_string l ^ ":\n- original context:\n"
- ^ eval_ctx_to_string ctx));
-
- (* Utility function for the sanity checks: check that the borrow disappeared
- * from the context *)
- let ctx0 = ctx in
- let check_disappeared (ctx : C.eval_ctx) : unit =
- let _ =
- match lookup_borrow_opt ek_all l ctx with
- | None -> () (* Ok *)
- | Some _ ->
- log#lerror
- (lazy
- ("end borrow: " ^ V.BorrowId.to_string l
- ^ ": borrow didn't disappear:\n- original context:\n"
- ^ eval_ctx_to_string ctx0 ^ "\n\n- new context:\n"
- ^ eval_ctx_to_string ctx));
- failwith "Borrow not eliminated"
- in
- match lookup_loan_opt ek_all l ctx with
- | None -> () (* Ok *)
- | Some _ ->
- log#lerror
- (lazy
- ("end borrow: " ^ V.BorrowId.to_string l
- ^ ": loan didn't disappear:\n- original context:\n"
- ^ eval_ctx_to_string ctx0 ^ "\n\n- new context:\n"
- ^ eval_ctx_to_string ctx));
- failwith "Loan not eliminated"
- in
- let cf_check_disappeared : cm_fun = unit_to_cm_fun check_disappeared in
- (* The complete sanity check: also check that after we ended a borrow,
- * the invariant is preserved *)
- let cf_check : cm_fun =
- comp cf_check_disappeared (Invariants.cf_check_invariants config)
- in
-
- (* Start by getting the borrow *)
- match end_borrow_get_borrow allowed_abs l ctx with
- (* Two cases:
- * - error: we found outer borrows or inner loans (end them first)
- * - success: we didn't find outer borrows when updating (but maybe we actually
- didn't find the borrow we were looking for...)
- *)
- | Error priority -> (
- (* Debug *)
- log#ldebug
- (lazy
- ("end borrow: " ^ V.BorrowId.to_string l
- ^ ": found outer borrows/abs or inner loans:"
- ^ show_priority_borrows_or_abs priority));
- (* End the priority borrows, abstraction, then try again to end the target
- * borrow (if necessary) *)
- match priority with
- | OuterBorrows (Borrows bids) | InnerLoans (Borrows bids) ->
- (* Note that we might get there with [allowed_abs <> None]: we might
- * be trying to end a borrow inside an abstraction, but which is actually
- * inside another borrow *)
- let allowed_abs' = None in
- (* End the outer borrows *)
- let cc = end_borrows config chain allowed_abs' bids in
- (* Retry to end the borrow *)
- let cc = comp cc (end_borrow config chain0 allowed_abs l) in
- (* Check and apply *)
- comp cc cf_check cf ctx
- | OuterBorrows (Borrow bid) | InnerLoans (Borrow bid) ->
- let allowed_abs' = None in
- (* End the outer borrow *)
- let cc = end_borrow config chain allowed_abs' bid in
- (* Retry to end the borrow *)
- let cc = comp cc (end_borrow config chain0 allowed_abs l) in
- (* Check and apply *)
- comp cc cf_check cf ctx
- | OuterAbs abs_id ->
- (* The borrow is inside an asbtraction: check if the corresponding
- * loan is inside the same abstraction. If this is the case, we end
- * the borrow without ending the abstraction. If not, we need to
- * end the whole abstraction *)
- (* Note that we can lookup the loan anywhere *)
- let ek =
- {
- enter_shared_loans = true;
- enter_mut_borrows = true;
- enter_abs = true;
- }
- in
- let cf_end_abs : cm_fun =
- match lookup_loan ek l ctx with
- | AbsId loan_abs_id, _ ->
- if loan_abs_id = abs_id then
- (* Same abstraction! We can end the borrow *)
- end_borrow config chain0 (Some abs_id) l
- else
- (* Not the same abstraction: we need to end the whole abstraction.
- * By doing that we should have ended the target borrow (see the
- * below sanity check) *)
- end_abstraction config chain abs_id
- | VarId _, _ ->
- (* The loan is not inside the same abstraction (actually inside
- * a non-abstraction value): we need to end the whole abstraction *)
- end_abstraction config chain abs_id
- in
- (* Compose with a sanity check *)
- comp cf_end_abs cf_check cf ctx)
- | Ok (ctx, None) ->
- log#ldebug (lazy "End borrow: borrow not found");
- (* It is possible that we can't find a borrow in symbolic mode (ending
- * an abstraction may end several borrows at once *)
- assert (config.mode = SymbolicMode);
- (* Do a sanity check and continue *)
- cf_check cf ctx
- (* We found a borrow: give it back (i.e., update the corresponding loan) *)
- | Ok (ctx, Some bc) ->
- (* Sanity check: the borrowed value shouldn't contain loans *)
- (match bc with
- | Concrete (V.MutBorrow (_, bv)) ->
- assert (Option.is_none (get_first_loan_in_value bv))
- | _ -> ());
- (* Give back the value *)
- let ctx = give_back config l bc ctx in
- (* Do a sanity check and continue *)
- cf_check cf ctx
-
-and end_borrows (config : C.config) (chain : borrow_or_abs_ids)
- (allowed_abs : V.AbstractionId.id option) (lset : V.BorrowId.Set.t) : cm_fun
- =
- fun cf ->
- (* This is not necessary, but we prefer to reorder the borrow ids,
- * so that we actually end from the smallest id to the highest id - just
- * a matter of taste, and may make debugging easier *)
- let ids = V.BorrowId.Set.fold (fun id ids -> id :: ids) lset [] in
- List.fold_left (fun cf id -> end_borrow config chain allowed_abs id cf) cf ids
-
-and end_abstraction (config : C.config) (chain : borrow_or_abs_ids)
- (abs_id : V.AbstractionId.id) : cm_fun =
- fun cf ctx ->
- (* Check that we don't loop *)
- let chain =
- add_borrow_or_abs_id_to_chain "end_abstraction: " (AbsId abs_id) chain
- in
- (* Remember the original context for printing purposes *)
- let ctx0 = ctx in
- log#ldebug
- (lazy
- ("end_abstraction: "
- ^ V.AbstractionId.to_string abs_id
- ^ "\n- original context:\n" ^ eval_ctx_to_string ctx0));
-
- (* Lookup the abstraction *)
- let abs = C.ctx_lookup_abs ctx abs_id in
-
- (* Check that we can end the abstraction *)
- assert abs.can_end;
-
- (* End the parent abstractions first *)
- let cc = end_abstractions config chain abs.parents in
- let cc =
- comp_unit cc (fun ctx ->
- log#ldebug
- (lazy
- ("end_abstraction: "
- ^ V.AbstractionId.to_string abs_id
- ^ "\n- context after parent abstractions ended:\n"
- ^ eval_ctx_to_string ctx)))
- in
-
- (* End the loans inside the abstraction *)
- let cc = comp cc (end_abstraction_loans config chain abs_id) in
- let cc =
- comp_unit cc (fun ctx ->
- log#ldebug
- (lazy
- ("end_abstraction: "
- ^ V.AbstractionId.to_string abs_id
- ^ "\n- context after loans ended:\n" ^ eval_ctx_to_string ctx)))
- in
-
- (* End the abstraction itself by redistributing the borrows it contains *)
- let cc = comp cc (end_abstraction_borrows config chain abs_id) in
-
- (* End the regions owned by the abstraction - note that we don't need to
- * relookup the abstraction: the set of regions in an abstraction never
- * changes... *)
- let cc =
- comp_update cc (fun ctx ->
- let ended_regions =
- T.RegionId.Set.union ctx.ended_regions abs.V.regions
- in
- { ctx with ended_regions })
- in
-
- (* Remove all the references to the id of the current abstraction, and remove
- * the abstraction itself.
- * **Rk.**: this is where we synthesize the updated symbolic AST *)
- let cc = comp cc (end_abstraction_remove_from_context config abs_id) in
-
- (* Debugging *)
- let cc =
- comp_unit cc (fun ctx ->
- log#ldebug
- (lazy
- ("end_abstraction: "
- ^ V.AbstractionId.to_string abs_id
- ^ "\n- original context:\n" ^ eval_ctx_to_string ctx0
- ^ "\n\n- new context:\n" ^ eval_ctx_to_string ctx)))
- in
-
- (* Sanity check: ending an abstraction must preserve the invariants *)
- let cc = comp cc (Invariants.cf_check_invariants config) in
-
- (* Apply the continuation *)
- cc cf ctx
-
-and end_abstractions (config : C.config) (chain : borrow_or_abs_ids)
- (abs_ids : V.AbstractionId.Set.t) : cm_fun =
- fun cf ->
- (* This is not necessary, but we prefer to reorder the abstraction ids,
- * so that we actually end from the smallest id to the highest id - just
- * a matter of taste, and may make debugging easier *)
- let abs_ids = V.AbstractionId.Set.fold (fun id ids -> id :: ids) abs_ids [] in
- List.fold_left (fun cf id -> end_abstraction config chain id cf) cf abs_ids
-
-and end_abstraction_loans (config : C.config) (chain : borrow_or_abs_ids)
- (abs_id : V.AbstractionId.id) : cm_fun =
- fun cf ctx ->
- (* Lookup the abstraction *)
- let abs = C.ctx_lookup_abs ctx abs_id in
- (* End the first loan we find.
- *
- * We ignore the "ignored mut/shared loans": as we should have already ended
- * the parent abstractions, they necessarily come from children. *)
- let opt_loan = get_first_non_ignored_aloan_in_abstraction abs in
- match opt_loan with
- | None ->
- (* No loans: nothing to update *)
- cf ctx
- | Some (BorrowIds bids) ->
- (* There are loans: end the corresponding borrows, then recheck *)
- let cc : cm_fun =
- match bids with
- | Borrow bid -> end_borrow config chain None bid
- | Borrows bids -> end_borrows config chain None bids
- in
- (* Reexplore, looking for loans *)
- let cc = comp cc (end_abstraction_loans config chain abs_id) in
- (* Continue *)
- cc cf ctx
- | Some (SymbolicValue sv) ->
- (* There is a proj_loans over a symbolic value: end the proj_borrows
- * which intersect this proj_loans, then end the proj_loans itself *)
- let cc = end_proj_loans_symbolic config chain abs_id abs.regions sv in
- (* Reexplore, looking for loans *)
- let cc = comp cc (end_abstraction_loans config chain abs_id) in
- (* Continue *)
- cc cf ctx
-
-and end_abstraction_borrows (config : C.config) (chain : borrow_or_abs_ids)
- (abs_id : V.AbstractionId.id) : cm_fun =
- fun cf ctx ->
- log#ldebug
- (lazy
- ("end_abstraction_borrows: abs_id: " ^ V.AbstractionId.to_string abs_id));
- (* Note that the abstraction mustn't contain any loans *)
- (* We end the borrows, starting with the *inner* ones. This is important
- when considering nested borrows which have the same lifetime.
- TODO: is that really important? Initially, there was a concern about
- whether we should give back ⊥ or not, but everything is handled by
- the symbolic value expansion... Also, now we use the AEndedMutBorrow
- values to store the children avalues (which was not the case before - we
- initially replaced the ended mut borrows with ⊥).
- *)
- (* We explore in-depth and use exceptions. When exploring a borrow, if
- * the exploration didn't trigger an exception, it means there are no
- * inner borrows to end: we can thus trigger an exception for the current
- * borrow. *)
- let obj =
- object
- inherit [_] V.iter_abs as super
-
- method! visit_aborrow_content env bc =
- (* In-depth exploration *)
- super#visit_aborrow_content env bc;
- (* No exception was raise: we can raise an exception for the
- * current borrow *)
- match bc with
- | V.AMutBorrow (_, _, _) | V.ASharedBorrow _ ->
- (* Raise an exception *)
- raise (FoundABorrowContent bc)
- | V.AProjSharedBorrow asb ->
- (* Raise an exception only if the asb contains borrows *)
- if
- List.exists
- (fun x -> match x with V.AsbBorrow _ -> true | _ -> false)
- asb
- then raise (FoundABorrowContent bc)
- else ()
- | V.AEndedMutBorrow _ | V.AIgnoredMutBorrow _
- | V.AEndedIgnoredMutBorrow _ | V.AEndedSharedBorrow ->
- (* Nothing to do for ignored borrows *)
- ()
-
- method! visit_aproj env sproj =
- (match sproj with
- | V.AProjLoans _ -> failwith "Unexpected"
- | V.AProjBorrows (sv, proj_ty) ->
- raise (FoundAProjBorrows (sv, proj_ty))
- | V.AEndedProjLoans _ | V.AEndedProjBorrows _ | V.AIgnoredProjBorrows ->
- ());
- super#visit_aproj env sproj
-
- (** We may need to end borrows in "regular" values, because of shared values *)
- method! visit_borrow_content _ bc =
- match bc with
- | V.SharedBorrow (_, _) | V.MutBorrow (_, _) ->
- raise (FoundBorrowContent bc)
- | V.InactivatedMutBorrow _ -> failwith "Unreachable"
- end
- in
- (* Lookup the abstraction *)
- let abs = C.ctx_lookup_abs ctx abs_id in
- try
- (* Explore the abstraction, looking for borrows *)
- obj#visit_abs () abs;
- (* No borrows: nothing to update *)
- cf ctx
- with
- (* There are concrete (i.e., not symbolic) borrows: end them, then reexplore *)
- | FoundABorrowContent bc ->
- log#ldebug
- (lazy
- ("end_abstraction_borrows: found aborrow content: "
- ^ aborrow_content_to_string ctx bc));
- let ctx =
- match bc with
- | V.AMutBorrow (_mv, bid, av) ->
- (* First, convert the avalue to a (fresh symbolic) value *)
- let sv = convert_avalue_to_given_back_value abs.kind av in
- (* Replace the mut borrow to register the fact that we ended
- * it and store with it the freshly generated given back value *)
- let ended_borrow = V.ABorrow (V.AEndedMutBorrow (sv, av)) in
- let ctx = update_aborrow ek_all bid ended_borrow ctx in
- (* Give the value back *)
- let sv = mk_typed_value_from_symbolic_value sv in
- give_back_value config bid sv ctx
- | V.ASharedBorrow bid ->
- (* Replace the shared borrow to account for the fact it ended *)
- let ended_borrow = V.ABorrow V.AEndedSharedBorrow in
- let ctx = update_aborrow ek_all bid ended_borrow ctx in
- (* Give back *)
- give_back_shared config bid ctx
- | V.AProjSharedBorrow asb ->
- (* Retrieve the borrow ids *)
- let bids =
- List.filter_map
- (fun asb ->
- match asb with
- | V.AsbBorrow bid -> Some bid
- | V.AsbProjReborrows (_, _) -> None)
- asb
- in
- (* There should be at least one borrow identifier in the set, which we
- * can use to identify the whole set *)
- let repr_bid = List.hd bids in
- (* Replace the shared borrow with Bottom *)
- let ctx = update_aborrow ek_all repr_bid V.ABottom ctx in
- (* Give back the shared borrows *)
- let ctx =
- List.fold_left
- (fun ctx bid -> give_back_shared config bid ctx)
- ctx bids
- in
- (* Continue *)
- ctx
- | V.AEndedMutBorrow _ | V.AIgnoredMutBorrow _
- | V.AEndedIgnoredMutBorrow _ | V.AEndedSharedBorrow ->
- failwith "Unexpected"
- in
- (* Reexplore *)
- end_abstraction_borrows config chain abs_id cf ctx
- (* There are symbolic borrows: end them, then reexplore *)
- | FoundAProjBorrows (sv, proj_ty) ->
- log#ldebug
- (lazy
- ("end_abstraction_borrows: found aproj borrows: "
- ^ aproj_to_string ctx (V.AProjBorrows (sv, proj_ty))));
- (* Generate a fresh symbolic value *)
- let nsv = mk_fresh_symbolic_value V.FunCallGivenBack proj_ty in
- (* Replace the proj_borrows - there should be exactly one *)
- let ended_borrow = V.AEndedProjBorrows nsv in
- let ctx = update_aproj_borrows abs.abs_id sv ended_borrow ctx in
- (* Give back the symbolic value *)
- let ctx =
- give_back_symbolic_value config abs.regions proj_ty sv nsv ctx
- in
- (* Reexplore *)
- end_abstraction_borrows config chain abs_id cf ctx
- (* There are concrete (i.e., not symbolic) borrows in shared values: end them, then reexplore *)
- | FoundBorrowContent bc ->
- log#ldebug
- (lazy
- ("end_abstraction_borrows: found borrow content: "
- ^ borrow_content_to_string ctx bc));
- let ctx =
- match bc with
- | V.SharedBorrow (_, bid) -> (
- (* Replace the shared borrow with bottom *)
- match end_borrow_get_borrow (Some abs_id) bid ctx with
- | Error _ -> failwith "Unreachable"
- | Ok (ctx, _) ->
- (* Give back *)
- give_back_shared config bid ctx)
- | V.MutBorrow (bid, v) -> (
- (* Replace the mut borrow with bottom *)
- match end_borrow_get_borrow (Some abs_id) bid ctx with
- | Error _ -> failwith "Unreachable"
- | Ok (ctx, _) ->
- (* Give the value back - note that the mut borrow was below a
- * shared borrow: the value is thus unchanged *)
- give_back_value config bid v ctx)
- | V.InactivatedMutBorrow _ -> failwith "Unreachable"
- in
- (* Reexplore *)
- end_abstraction_borrows config chain abs_id cf ctx
-
-(** Remove an abstraction from the context, as well as all its references *)
-and end_abstraction_remove_from_context (_config : C.config)
- (abs_id : V.AbstractionId.id) : cm_fun =
- fun cf ctx ->
- let rec remove_from_env (env : C.env) : C.env * V.abs option =
- match env with
- | [] -> failwith "Unreachable"
- | C.Frame :: _ -> (env, None)
- | Var (bv, v) :: env ->
- let env, abs_opt = remove_from_env env in
- (Var (bv, v) :: env, abs_opt)
- | C.Abs abs :: env ->
- if abs.abs_id = abs_id then (env, Some abs)
- else
- let env, abs_opt = remove_from_env env in
- let parents = V.AbstractionId.Set.remove abs_id abs.parents in
- (C.Abs { abs with V.parents } :: env, abs_opt)
- in
- let env, abs = remove_from_env ctx.C.env in
- let ctx = { ctx with C.env } in
- let abs = Option.get abs in
- (* Apply the continuation *)
- let expr = cf ctx in
- (* Synthesize the symbolic AST *)
- S.synthesize_end_abstraction abs expr
-
-(** End a proj_loan over a symbolic value by ending the proj_borrows which
- intersect this proj_loans.
-
- Rk.:
- - if this symbolic value is primitively copiable, then:
- - either proj_borrows are only present in the concrete context
- - or there is only one intersecting proj_borrow present in an
- abstraction
- - otherwise, this symbolic value is not primitively copiable:
- - there may be proj_borrows_shared over this value
- - if we put aside the proj_borrows_shared, there should be exactly one
- intersecting proj_borrows, either in the concrete context or in an
- abstraction
-*)
-and end_proj_loans_symbolic (config : C.config) (chain : borrow_or_abs_ids)
- (abs_id : V.AbstractionId.id) (regions : T.RegionId.Set.t)
- (sv : V.symbolic_value) : cm_fun =
- fun cf ctx ->
- (* Small helpers for sanity checks *)
- let check ctx = no_aproj_over_symbolic_in_context sv ctx in
- let cf_check (cf : m_fun) : m_fun =
- fun ctx ->
- check ctx;
- cf ctx
- in
- (* Find the first proj_borrows which intersects the proj_loans *)
- let explore_shared = true in
- match lookup_intersecting_aproj_borrows_opt explore_shared regions sv ctx with
- | None ->
- (* We couldn't find any in the context: it means that the symbolic value
- * is in the concrete environment (or that we dropped it, in which case
- * it is completely absent). We thus simply need to replace the loans
- * projector with an ended projector. *)
- let ctx = update_aproj_loans_to_ended abs_id sv ctx in
- (* Sanity check *)
- check ctx;
- (* Continue *)
- cf ctx
- | Some (SharedProjs projs) ->
- (* We found projectors over shared values - split between the projectors
- which belong to the current abstraction and the others.
- The context looks like this:
- {[
- abs'0 {
- // The loan was initially like this:
- // [shared_loan lids (s <: ...) [s]]
- // but if we get there it means it was already ended:
- ended_shared_loan (s <: ...) [s]
- proj_shared_borrows [...; (s <: ...); ...]
- proj_shared_borrows [...; (s <: ...); ...]
- ...
- }
-
- abs'1 [
- proj_shared_borrows [...; (s <: ...); ...]
- ...
- }
-
- ...
-
- // No [s] outside of abstractions
-
- ]}
- *)
- let _owned_projs, external_projs =
- List.partition (fun (abs_id', _) -> abs_id' = abs_id) projs
- in
- (* End the external borrow projectors (end their abstractions) *)
- let cf_end_external : cm_fun =
- fun cf ctx ->
- let abs_ids = List.map fst external_projs in
- let abs_ids =
- List.fold_left
- (fun s id -> V.AbstractionId.Set.add id s)
- V.AbstractionId.Set.empty abs_ids
- in
- (* End the abstractions and continue *)
- end_abstractions config chain abs_ids cf ctx
- in
- (* End the internal borrows projectors and the loans projector *)
- let cf_end_internal : cm_fun =
- fun cf ctx ->
- (* All the proj_borrows are owned: simply erase them *)
- let ctx = remove_intersecting_aproj_borrows_shared regions sv ctx in
- (* End the loan itself *)
- let ctx = update_aproj_loans_to_ended abs_id sv ctx in
- (* Sanity check *)
- check ctx;
- (* Continue *)
- cf ctx
- in
- (* Compose and apply *)
- let cc = comp cf_end_external cf_end_internal in
- cc cf ctx
- | Some (NonSharedProj (abs_id', _proj_ty)) ->
- (* We found one projector of borrows in an abstraction: if it comes
- * from this abstraction, we can end it directly, otherwise we need
- * to end the abstraction where it came from first *)
- if abs_id' = abs_id then (
- (* Note that it happens when a function returns a [&mut ...] which gets
- expanded to [mut_borrow l s], and we end the borrow [l] (so [s] gets
- reinjected in the parent abstraction without having been modified).
-
- The context looks like this:
- {[
- abs'0 {
- [s <: ...]
- (s <: ...)
- }
-
- // Note that [s] can't appear in other abstractions or in the
- // regular environment (because we forbid the duplication of
- // symbolic values which contain borrows).
- ]}
- *)
- (* End the projector of borrows - TODO: not completely sure what to
- * replace it with... Maybe we should introduce an ABottomProj? *)
- let ctx = update_aproj_borrows abs_id sv V.AIgnoredProjBorrows ctx in
- (* Sanity check: no other occurrence of an intersecting projector of borrows *)
- assert (
- Option.is_none
- (lookup_intersecting_aproj_borrows_opt explore_shared regions sv ctx));
- (* End the projector of loans *)
- let ctx = update_aproj_loans_to_ended abs_id sv ctx in
- (* Sanity check *)
- check ctx;
- (* Continue *)
- cf ctx)
- else
- (* The borrows proj comes from a different abstraction: end it. *)
- let cc = end_abstraction config chain abs_id' in
- (* Retry ending the projector of loans *)
- let cc =
- comp cc (end_proj_loans_symbolic config chain abs_id regions sv)
- in
- (* Sanity check *)
- let cc = comp cc cf_check in
- (* Continue *)
- cc cf ctx
-
-let end_outer_borrow config : V.BorrowId.id -> cm_fun =
- end_borrow config [] None
-
-let end_outer_borrows config : V.BorrowId.Set.t -> cm_fun =
- end_borrows config [] None
-
-(** Helper function: see [activate_inactivated_mut_borrow].
-
- This function updates the shared loan to a mutable loan (we then update
- the borrow with another helper). Of course, the shared loan must contain
- exactly one borrow id (the one we give as parameter), otherwise we can't
- promote it. Also, the shared value mustn't contain any loan.
-
- The returned value (previously shared) is checked:
- - it mustn't contain loans
- - it mustn't contain {!V.Bottom}
- - it mustn't contain inactivated borrows
- TODO: this kind of checks should be put in an auxiliary helper, because
- they are redundant.
-
- The loan to update mustn't be a borrowed value.
- *)
-let promote_shared_loan_to_mut_loan (l : V.BorrowId.id)
- (cf : V.typed_value -> m_fun) : m_fun =
- fun ctx ->
- (* Debug *)
- log#ldebug
- (lazy
- ("promote_shared_loan_to_mut_loan:\n- loan: " ^ V.BorrowId.to_string l
- ^ "\n- context:\n" ^ eval_ctx_to_string ctx ^ "\n"));
- (* Lookup the shared loan - note that we can't promote a shared loan
- * in a shared value, but we can do it in a mutably borrowed value.
- * This is important because we can do: [let y = &two-phase ( *x );]
- *)
- let ek =
- { enter_shared_loans = false; enter_mut_borrows = true; enter_abs = false }
- in
- match lookup_loan ek l ctx with
- | _, Concrete (V.MutLoan _) ->
- failwith "Expected a shared loan, found a mut loan"
- | _, Concrete (V.SharedLoan (bids, sv)) ->
- (* Check that there is only one borrow id (l) and update the loan *)
- assert (V.BorrowId.Set.mem l bids && V.BorrowId.Set.cardinal bids = 1);
- (* We need to check that there aren't any loans in the value:
- we should have gotten rid of those already, but it is better
- to do a sanity check. *)
- assert (not (loans_in_value sv));
- (* Check there isn't {!Bottom} (this is actually an invariant *)
- assert (not (bottom_in_value ctx.ended_regions sv));
- (* Check there aren't inactivated borrows *)
- assert (not (inactivated_in_value sv));
- (* Update the loan content *)
- let ctx = update_loan ek l (V.MutLoan l) ctx in
- (* Continue *)
- cf sv ctx
- | _, Abstract _ ->
- (* I don't think it is possible to have two-phase borrows involving borrows
- * returned by abstractions. I'm not sure how we could handle that anyway. *)
- failwith
- "Can't promote a shared loan to a mutable loan if the loan is inside \
- an abstraction"
-
-(** Helper function: see {!activate_inactivated_mut_borrow}.
-
- This function updates a shared borrow to a mutable borrow.
- *)
-let promote_inactivated_borrow_to_mut_borrow (l : V.BorrowId.id) (cf : m_fun)
- (borrowed_value : V.typed_value) : m_fun =
- fun ctx ->
- (* Lookup the inactivated borrow - note that we don't go inside borrows/loans:
- there can't be inactivated borrows inside other borrows/loans
- *)
- let ek =
- { enter_shared_loans = false; enter_mut_borrows = false; enter_abs = false }
- in
- let ctx =
- match lookup_borrow ek l ctx with
- | Concrete (V.SharedBorrow _ | V.MutBorrow (_, _)) ->
- failwith "Expected an inactivated mutable borrow"
- | Concrete (V.InactivatedMutBorrow _) ->
- (* Update it *)
- update_borrow ek l (V.MutBorrow (l, borrowed_value)) ctx
- | Abstract _ ->
- (* This can't happen for sure *)
- failwith
- "Can't promote a shared borrow to a mutable borrow if the borrow is \
- inside an abstraction"
- in
- (* Continue *)
- cf ctx
-
-(** Promote an inactivated mut borrow to a mut borrow.
-
- The borrow must point to a shared value which is borrowed exactly once.
- *)
-let rec activate_inactivated_mut_borrow (config : C.config) (l : V.BorrowId.id)
- : cm_fun =
- fun cf ctx ->
- (* Lookup the value *)
- let ek =
- { enter_shared_loans = false; enter_mut_borrows = true; enter_abs = false }
- in
- match lookup_loan ek l ctx with
- | _, Concrete (V.MutLoan _) -> failwith "Unreachable"
- | _, Concrete (V.SharedLoan (bids, sv)) -> (
- (* If there are loans inside the value, end them. Note that there can't be
- inactivated borrows inside the value.
- If we perform an update, do a recursive call to lookup the updated value *)
- match get_first_loan_in_value sv with
- | Some lc ->
- (* End the loans *)
- let cc =
- match lc with
- | V.SharedLoan (bids, _) -> end_outer_borrows config bids
- | V.MutLoan bid -> end_outer_borrow config bid
- in
- (* Recursive call *)
- let cc = comp cc (activate_inactivated_mut_borrow config l) in
- (* Continue *)
- cc cf ctx
- | None ->
- (* No loan to end inside the value *)
- (* Some sanity checks *)
- log#ldebug
- (lazy
- ("activate_inactivated_mut_borrow: resulting value:\n"
- ^ typed_value_to_string ctx sv));
- assert (not (loans_in_value sv));
- assert (not (bottom_in_value ctx.ended_regions sv));
- assert (not (inactivated_in_value sv));
- (* End the borrows which borrow from the value, at the exception of
- the borrow we want to promote *)
- let bids = V.BorrowId.Set.remove l bids in
- let cc = end_outer_borrows config bids in
- (* Promote the loan - TODO: this will fail if the value contains
- * any loans. In practice, it shouldn't, but we could also
- * look for loans inside the value and end them before promoting
- * the borrow. *)
- let cc = comp cc (promote_shared_loan_to_mut_loan l) in
- (* Promote the borrow - the value should have been checked by
- {!promote_shared_loan_to_mut_loan}
- *)
- let cc =
- comp cc (fun cf borrowed_value ->
- promote_inactivated_borrow_to_mut_borrow l cf borrowed_value)
- in
- (* Continue *)
- cc cf ctx)
- | _, Abstract _ ->
- (* I don't think it is possible to have two-phase borrows involving borrows
- * returned by abstractions. I'm not sure how we could handle that anyway. *)
- failwith
- "Can't activate an inactivated mutable borrow referencing a loan inside\n\
- \ an abstraction"
diff --git a/src/InterpreterBorrowsCore.ml b/src/InterpreterBorrowsCore.ml
deleted file mode 100644
index a5501712..00000000
--- a/src/InterpreterBorrowsCore.ml
+++ /dev/null
@@ -1,1181 +0,0 @@
-(* This file defines the basic blocks to implement the semantics of borrows.
- * Note that those functions are not only used in InterpreterBorrows, but
- * also in Invariants or InterpreterProjectors *)
-
-module T = Types
-module V = Values
-module C = Contexts
-module Subst = Substitute
-module L = Logging
-open Utils
-open TypesUtils
-open InterpreterUtils
-
-(** The local logger *)
-let log = L.borrows_log
-
-(** TODO: cleanup this a bit, once we have a better understanding about
- what we need.
- TODO: I'm not sure in which file this should be moved... *)
-type exploration_kind = {
- enter_shared_loans : bool;
- enter_mut_borrows : bool;
- enter_abs : bool;
- (** Note that if we allow to enter abs, we don't check whether we enter
- mutable/shared loans or borrows: there are no use cases requiring
- a finer control. *)
-}
-(** This record controls how some generic helper lookup/update
- functions behave, by restraining the kind of therms they can enter.
-*)
-
-let ek_all : exploration_kind =
- { enter_shared_loans = true; enter_mut_borrows = true; enter_abs = true }
-
-type borrow_ids = Borrows of V.BorrowId.Set.t | Borrow of V.BorrowId.id
-[@@deriving show]
-
-exception FoundBorrowIds of borrow_ids
-
-type priority_borrows_or_abs =
- | OuterBorrows of borrow_ids
- | OuterAbs of V.AbstractionId.id
- | InnerLoans of borrow_ids
-[@@deriving show]
-
-type borrow_ids_or_symbolic_value =
- | BorrowIds of borrow_ids
- | SymbolicValue of V.symbolic_value
-[@@deriving show]
-
-let update_if_none opt x = match opt with None -> Some x | _ -> opt
-
-(** Utility exception *)
-exception FoundPriority of priority_borrows_or_abs
-
-type loan_or_borrow_content =
- | LoanContent of V.loan_content
- | BorrowContent of V.borrow_content
-[@@deriving show]
-
-type borrow_or_abs_id =
- | BorrowId of V.BorrowId.id
- | AbsId of V.AbstractionId.id
-
-type borrow_or_abs_ids = borrow_or_abs_id list
-
-let borrow_or_abs_id_to_string (id : borrow_or_abs_id) : string =
- match id with
- | AbsId id -> "abs@" ^ V.AbstractionId.to_string id
- | BorrowId id -> "l@" ^ V.BorrowId.to_string id
-
-let borrow_or_abs_ids_chain_to_string (ids : borrow_or_abs_ids) : string =
- let ids = List.rev ids in
- let ids = List.map borrow_or_abs_id_to_string ids in
- String.concat " -> " ids
-
-(** Add a borrow or abs id to a chain of ids, while checking that we don't loop *)
-let add_borrow_or_abs_id_to_chain (msg : string) (id : borrow_or_abs_id)
- (ids : borrow_or_abs_ids) : borrow_or_abs_ids =
- if List.mem id ids then
- failwith
- (msg ^ "detected a loop in the chain of ids: "
- ^ borrow_or_abs_ids_chain_to_string (id :: ids))
- else id :: ids
-
-(** Helper function.
-
- This function allows to define in a generic way a comparison of region types.
- See [projections_interesect] for instance.
-
- [default]: default boolean to return, when comparing types with no regions
- [combine]: how to combine booleans
- [compare_regions]: how to compare regions
- *)
-let rec compare_rtys (default : bool) (combine : bool -> bool -> bool)
- (compare_regions : T.RegionId.id T.region -> T.RegionId.id T.region -> bool)
- (ty1 : T.rty) (ty2 : T.rty) : bool =
- let compare = compare_rtys default combine compare_regions in
- match (ty1, ty2) with
- | T.Bool, T.Bool | T.Char, T.Char | T.Str, T.Str -> default
- | T.Integer int_ty1, T.Integer int_ty2 ->
- assert (int_ty1 = int_ty2);
- default
- | T.Adt (id1, regions1, tys1), T.Adt (id2, regions2, tys2) ->
- assert (id1 = id2);
-
- (* The check for the ADTs is very crude: we simply compare the arguments
- * two by two.
- *
- * For instance, when checking if some projections intersect, we simply
- * check if some arguments intersect. As all the type and region
- * parameters should be used somewhere in the ADT (otherwise rustc
- * generates an error), it means that it should be equivalent to checking
- * whether two fields intersect (and anyway comparing the field types is
- * difficult in case of enumerations...).
- * If we didn't have the above property enforced by the rust compiler,
- * this check would still be a reasonable conservative approximation. *)
-
- (* Check the region parameters *)
- let regions = List.combine regions1 regions2 in
- let params_b =
- List.fold_left
- (fun b (r1, r2) -> combine b (compare_regions r1 r2))
- default regions
- in
- (* Check the type parameters *)
- let tys = List.combine tys1 tys2 in
- let tys_b =
- List.fold_left
- (fun b (ty1, ty2) -> combine b (compare ty1 ty2))
- default tys
- in
- (* Combine *)
- combine params_b tys_b
- | T.Array ty1, T.Array ty2 | T.Slice ty1, T.Slice ty2 -> compare ty1 ty2
- | T.Ref (r1, ty1, kind1), T.Ref (r2, ty2, kind2) ->
- (* Sanity check *)
- assert (kind1 = kind2);
- (* Explanation for the case where we check if projections intersect:
- * the projections intersect if the borrows intersect or their contents
- * intersect. *)
- let regions_b = compare_regions r1 r2 in
- let tys_b = compare ty1 ty2 in
- combine regions_b tys_b
- | T.TypeVar id1, T.TypeVar id2 ->
- assert (id1 = id2);
- default
- | _ ->
- log#lerror
- (lazy
- ("compare_rtys: unexpected inputs:" ^ "\n- ty1: " ^ T.show_rty ty1
- ^ "\n- ty2: " ^ T.show_rty ty2));
- failwith "Unreachable"
-
-(** Check if two different projections intersect. This is necessary when
- giving a symbolic value to an abstraction: we need to check that
- the regions which are already ended inside the abstraction don't
- intersect the regions over which we project in the new abstraction.
- Note that the two abstractions have different views (in terms of regions)
- of the symbolic value (hence the two region types).
-*)
-let projections_intersect (ty1 : T.rty) (rset1 : T.RegionId.Set.t) (ty2 : T.rty)
- (rset2 : T.RegionId.Set.t) : bool =
- let default = false in
- let combine b1 b2 = b1 || b2 in
- let compare_regions r1 r2 =
- region_in_set r1 rset1 && region_in_set r2 rset2
- in
- compare_rtys default combine compare_regions ty1 ty2
-
-(** Check if the first projection contains the second projection.
- We use this function when checking invariants.
-*)
-let projection_contains (ty1 : T.rty) (rset1 : T.RegionId.Set.t) (ty2 : T.rty)
- (rset2 : T.RegionId.Set.t) : bool =
- let default = true in
- let combine b1 b2 = b1 && b2 in
- let compare_regions r1 r2 =
- region_in_set r1 rset1 || not (region_in_set r2 rset2)
- in
- compare_rtys default combine compare_regions ty1 ty2
-
-(** Lookup a loan content.
-
- The loan is referred to by a borrow id.
-
- TODO: group abs_or_var_id and g_loan_content.
- *)
-let lookup_loan_opt (ek : exploration_kind) (l : V.BorrowId.id)
- (ctx : C.eval_ctx) : (abs_or_var_id * g_loan_content) option =
- (* We store here whether we are inside an abstraction or a value - note that we
- * could also track that with the environment, it would probably be more idiomatic
- * and cleaner *)
- let abs_or_var : abs_or_var_id option ref = ref None in
-
- let obj =
- object
- inherit [_] C.iter_eval_ctx as super
-
- method! visit_borrow_content env bc =
- match bc with
- | V.SharedBorrow (mv, bid) ->
- (* Nothing specific to do *)
- super#visit_SharedBorrow env mv bid
- | V.InactivatedMutBorrow (mv, bid) ->
- (* Nothing specific to do *)
- super#visit_InactivatedMutBorrow env mv bid
- | V.MutBorrow (bid, mv) ->
- (* Control the dive *)
- if ek.enter_mut_borrows then super#visit_MutBorrow env bid mv
- else ()
-
- (** We reimplement {!visit_Loan} (rather than the more precise functions
- {!visit_SharedLoan}, etc.) on purpose: as we have an exhaustive match
- below, we are more resilient to definition updates (the compiler
- is our friend).
- *)
- method! visit_loan_content env lc =
- match lc with
- | V.SharedLoan (bids, sv) ->
- (* Check if this is the loan we are looking for, and control the dive *)
- if V.BorrowId.Set.mem l bids then
- raise (FoundGLoanContent (Concrete lc))
- else if ek.enter_shared_loans then
- super#visit_SharedLoan env bids sv
- else ()
- | V.MutLoan bid ->
- (* Check if this is the loan we are looking for *)
- if bid = l then raise (FoundGLoanContent (Concrete lc))
- else super#visit_MutLoan env bid
-
- (** Note that we don't control diving inside the abstractions: if we
- allow to dive inside abstractions, we allow to go anywhere
- (because there are no use cases requiring finer control) *)
- method! visit_aloan_content env lc =
- match lc with
- | V.AMutLoan (bid, av) ->
- if bid = l then raise (FoundGLoanContent (Abstract lc))
- else super#visit_AMutLoan env bid av
- | V.ASharedLoan (bids, v, av) ->
- if V.BorrowId.Set.mem l bids then
- raise (FoundGLoanContent (Abstract lc))
- else super#visit_ASharedLoan env bids v av
- | V.AEndedMutLoan { given_back = _; child = _; given_back_meta = _ }
- | V.AEndedSharedLoan (_, _)
- | V.AIgnoredMutLoan (_, _)
- | V.AEndedIgnoredMutLoan
- { given_back = _; child = _; given_back_meta = _ }
- | V.AIgnoredSharedLoan _ ->
- super#visit_aloan_content env lc
-
- method! visit_Var env bv v =
- assert (Option.is_none !abs_or_var);
- abs_or_var :=
- Some
- (VarId (match bv with Some bv -> Some bv.C.index | None -> None));
- super#visit_Var env bv v;
- abs_or_var := None
-
- method! visit_Abs env abs =
- assert (Option.is_none !abs_or_var);
- if ek.enter_abs then (
- abs_or_var := Some (AbsId abs.V.abs_id);
- super#visit_Abs env abs;
- abs_or_var := None)
- else ()
- end
- in
- (* We use exceptions *)
- try
- obj#visit_eval_ctx () ctx;
- None
- with FoundGLoanContent lc -> (
- match !abs_or_var with
- | Some abs_or_var -> Some (abs_or_var, lc)
- | None -> raise (Failure "Inconsistent state"))
-
-(** Lookup a loan content.
-
- The loan is referred to by a borrow id.
- Raises an exception if no loan was found.
- *)
-let lookup_loan (ek : exploration_kind) (l : V.BorrowId.id) (ctx : C.eval_ctx) :
- abs_or_var_id * g_loan_content =
- match lookup_loan_opt ek l ctx with
- | None -> failwith "Unreachable"
- | Some res -> res
-
-(** Update a loan content.
-
- The loan is referred to by a borrow id.
-
- This is a helper function: it might break invariants.
- *)
-let update_loan (ek : exploration_kind) (l : V.BorrowId.id)
- (nlc : V.loan_content) (ctx : C.eval_ctx) : C.eval_ctx =
- (* We use a reference to check that we update exactly one loan: when updating
- * inside values, we check we don't update more than one loan. Then, upon
- * returning we check that we updated at least once. *)
- let r = ref false in
- let update () : V.loan_content =
- assert (not !r);
- r := true;
- nlc
- in
-
- let obj =
- object
- inherit [_] C.map_eval_ctx as super
-
- method! visit_borrow_content env bc =
- match bc with
- | V.SharedBorrow (_, _) | V.InactivatedMutBorrow _ ->
- (* Nothing specific to do *)
- super#visit_borrow_content env bc
- | V.MutBorrow (bid, mv) ->
- (* Control the dive into mutable borrows *)
- if ek.enter_mut_borrows then super#visit_MutBorrow env bid mv
- else V.MutBorrow (bid, mv)
-
- (** We reimplement {!visit_loan_content} (rather than one of the sub-
- functions) on purpose: exhaustive matches are good for maintenance *)
- method! visit_loan_content env lc =
- match lc with
- | V.SharedLoan (bids, sv) ->
- (* Shared loan: check if this is the loan we are looking for, and
- control the dive. *)
- if V.BorrowId.Set.mem l bids then update ()
- else if ek.enter_shared_loans then
- super#visit_SharedLoan env bids sv
- else V.SharedLoan (bids, sv)
- | V.MutLoan bid ->
- (* Mut loan: checks if this is the loan we are looking for *)
- if bid = l then update () else super#visit_MutLoan env bid
-
- (** Note that once inside the abstractions, we don't control diving
- (there are no use cases requiring finer control).
- Also, as we give back a {!loan_content} (and not an {!aloan_content})
- we don't need to do reimplement the visit functions for the values
- inside the abstractions (rk.: there may be "concrete" values inside
- abstractions, so there is a utility in diving inside). *)
- method! visit_abs env abs =
- if ek.enter_abs then super#visit_abs env abs else abs
- end
- in
-
- let ctx = obj#visit_eval_ctx () ctx in
- (* Check that we updated at least one loan *)
- assert !r;
- ctx
-
-(** Update a abstraction loan content.
-
- The loan is referred to by a borrow id.
-
- This is a helper function: it might break invariants.
- *)
-let update_aloan (ek : exploration_kind) (l : V.BorrowId.id)
- (nlc : V.aloan_content) (ctx : C.eval_ctx) : C.eval_ctx =
- (* We use a reference to check that we update exactly one loan: when updating
- * inside values, we check we don't update more than one loan. Then, upon
- * returning we check that we updated at least once. *)
- let r = ref false in
- let update () : V.aloan_content =
- assert (not !r);
- r := true;
- nlc
- in
-
- let obj =
- object
- inherit [_] C.map_eval_ctx as super
-
- method! visit_aloan_content env lc =
- match lc with
- | V.AMutLoan (bid, av) ->
- if bid = l then update () else super#visit_AMutLoan env bid av
- | V.ASharedLoan (bids, v, av) ->
- if V.BorrowId.Set.mem l bids then update ()
- else super#visit_ASharedLoan env bids v av
- | V.AEndedMutLoan { given_back = _; child = _; given_back_meta = _ }
- | V.AEndedSharedLoan (_, _)
- | V.AIgnoredMutLoan (_, _)
- | V.AEndedIgnoredMutLoan
- { given_back = _; child = _; given_back_meta = _ }
- | V.AIgnoredSharedLoan _ ->
- super#visit_aloan_content env lc
-
- (** Note that once inside the abstractions, we don't control diving
- (there are no use cases requiring finer control). *)
- method! visit_abs env abs =
- if ek.enter_abs then super#visit_abs env abs else abs
- end
- in
-
- let ctx = obj#visit_eval_ctx () ctx in
- (* Check that we updated at least one loan *)
- assert !r;
- ctx
-
-(** Lookup a borrow content from a borrow id. *)
-let lookup_borrow_opt (ek : exploration_kind) (l : V.BorrowId.id)
- (ctx : C.eval_ctx) : g_borrow_content option =
- let obj =
- object
- inherit [_] C.iter_eval_ctx as super
-
- method! visit_borrow_content env bc =
- match bc with
- | V.MutBorrow (bid, mv) ->
- (* Check the borrow id and control the dive *)
- if bid = l then raise (FoundGBorrowContent (Concrete bc))
- else if ek.enter_mut_borrows then super#visit_MutBorrow env bid mv
- else ()
- | V.SharedBorrow (_, bid) ->
- (* Check the borrow id *)
- if bid = l then raise (FoundGBorrowContent (Concrete bc)) else ()
- | V.InactivatedMutBorrow (_, bid) ->
- (* Check the borrow id *)
- if bid = l then raise (FoundGBorrowContent (Concrete bc)) else ()
-
- method! visit_loan_content env lc =
- match lc with
- | V.MutLoan bid ->
- (* Nothing special to do *) super#visit_MutLoan env bid
- | V.SharedLoan (bids, sv) ->
- (* Control the dive *)
- if ek.enter_shared_loans then super#visit_SharedLoan env bids sv
- else ()
-
- method! visit_aborrow_content env bc =
- match bc with
- | V.AMutBorrow (mv, bid, av) ->
- if bid = l then raise (FoundGBorrowContent (Abstract bc))
- else super#visit_AMutBorrow env mv bid av
- | V.ASharedBorrow bid ->
- if bid = l then raise (FoundGBorrowContent (Abstract bc))
- else super#visit_ASharedBorrow env bid
- | V.AIgnoredMutBorrow (_, _)
- | V.AEndedMutBorrow _
- | V.AEndedIgnoredMutBorrow
- { given_back_loans_proj = _; child = _; given_back_meta = _ }
- | V.AEndedSharedBorrow ->
- super#visit_aborrow_content env bc
- | V.AProjSharedBorrow asb ->
- if borrow_in_asb l asb then
- raise (FoundGBorrowContent (Abstract bc))
- else ()
-
- method! visit_abs env abs =
- if ek.enter_abs then super#visit_abs env abs else ()
- end
- in
- (* We use exceptions *)
- try
- obj#visit_eval_ctx () ctx;
- None
- with FoundGBorrowContent lc -> Some lc
-
-(** Lookup a borrow content from a borrow id.
-
- Raise an exception if no loan was found
-*)
-let lookup_borrow (ek : exploration_kind) (l : V.BorrowId.id) (ctx : C.eval_ctx)
- : g_borrow_content =
- match lookup_borrow_opt ek l ctx with
- | None -> failwith "Unreachable"
- | Some lc -> lc
-
-(** Update a borrow content.
-
- The borrow is referred to by a borrow id.
-
- This is a helper function: it might break invariants.
- *)
-let update_borrow (ek : exploration_kind) (l : V.BorrowId.id)
- (nbc : V.borrow_content) (ctx : C.eval_ctx) : C.eval_ctx =
- (* We use a reference to check that we update exactly one borrow: when updating
- * inside values, we check we don't update more than one borrow. Then, upon
- * returning we check that we updated at least once. *)
- let r = ref false in
- let update () : V.borrow_content =
- assert (not !r);
- r := true;
- nbc
- in
-
- let obj =
- object
- inherit [_] C.map_eval_ctx as super
-
- method! visit_borrow_content env bc =
- match bc with
- | V.MutBorrow (bid, mv) ->
- (* Check the id and control dive *)
- if bid = l then update ()
- else if ek.enter_mut_borrows then super#visit_MutBorrow env bid mv
- else V.MutBorrow (bid, mv)
- | V.SharedBorrow (mv, bid) ->
- (* Check the id *)
- if bid = l then update () else super#visit_SharedBorrow env mv bid
- | V.InactivatedMutBorrow (mv, bid) ->
- (* Check the id *)
- if bid = l then update ()
- else super#visit_InactivatedMutBorrow env mv bid
-
- method! visit_loan_content env lc =
- match lc with
- | V.SharedLoan (bids, sv) ->
- (* Control the dive *)
- if ek.enter_shared_loans then super#visit_SharedLoan env bids sv
- else V.SharedLoan (bids, sv)
- | V.MutLoan bid ->
- (* Nothing specific to do *)
- super#visit_MutLoan env bid
-
- method! visit_abs env abs =
- if ek.enter_abs then super#visit_abs env abs else abs
- end
- in
-
- let ctx = obj#visit_eval_ctx () ctx in
- (* Check that we updated at least one borrow *)
- assert !r;
- ctx
-
-(** Update an abstraction borrow content.
-
- The borrow is referred to by a borrow id.
-
- This is a helper function: it might break invariants.
- *)
-let update_aborrow (ek : exploration_kind) (l : V.BorrowId.id) (nv : V.avalue)
- (ctx : C.eval_ctx) : C.eval_ctx =
- (* We use a reference to check that we update exactly one borrow: when updating
- * inside values, we check we don't update more than one borrow. Then, upon
- * returning we check that we updated at least once. *)
- let r = ref false in
- let update () : V.avalue =
- assert (not !r);
- r := true;
- nv
- in
-
- let obj =
- object
- inherit [_] C.map_eval_ctx as super
-
- method! visit_ABorrow env bc =
- match bc with
- | V.AMutBorrow (mv, bid, av) ->
- if bid = l then update ()
- else V.ABorrow (super#visit_AMutBorrow env mv bid av)
- | V.ASharedBorrow bid ->
- if bid = l then update ()
- else V.ABorrow (super#visit_ASharedBorrow env bid)
- | V.AIgnoredMutBorrow _ | V.AEndedMutBorrow _ | V.AEndedSharedBorrow
- | V.AEndedIgnoredMutBorrow _ ->
- super#visit_ABorrow env bc
- | V.AProjSharedBorrow asb ->
- if borrow_in_asb l asb then update ()
- else V.ABorrow (super#visit_AProjSharedBorrow env asb)
-
- method! visit_abs env abs =
- if ek.enter_abs then super#visit_abs env abs else abs
- end
- in
-
- let ctx = obj#visit_eval_ctx () ctx in
- (* Check that we updated at least one borrow *)
- assert !r;
- ctx
-
-(** Auxiliary function: see its usage in [end_borrow_get_borrow_in_value] *)
-let update_outer_borrows (outer : V.AbstractionId.id option * borrow_ids option)
- (x : borrow_ids) : V.AbstractionId.id option * borrow_ids option =
- let abs, opt = outer in
- (abs, update_if_none opt x)
-
-(** Return the first loan we find in a value *)
-let get_first_loan_in_value (v : V.typed_value) : V.loan_content option =
- let obj =
- object
- inherit [_] V.iter_typed_value
- method! visit_loan_content _ lc = raise (FoundLoanContent lc)
- end
- in
- (* We use exceptions *)
- try
- obj#visit_typed_value () v;
- None
- with FoundLoanContent lc -> Some lc
-
-(** Return the first borrow we find in a value *)
-let get_first_borrow_in_value (v : V.typed_value) : V.borrow_content option =
- let obj =
- object
- inherit [_] V.iter_typed_value
- method! visit_borrow_content _ bc = raise (FoundBorrowContent bc)
- end
- in
- (* We use exceptions *)
- try
- obj#visit_typed_value () v;
- None
- with FoundBorrowContent bc -> Some bc
-
-(** Return the first loan or borrow content we find in a value (starting with
- the outer ones).
-
- [with_borrows]:
- - if true: return the first loan or borrow we find
- - if false: return the first loan we find, do not dive into borrowed values
- *)
-let get_first_outer_loan_or_borrow_in_value (with_borrows : bool)
- (v : V.typed_value) : loan_or_borrow_content option =
- let obj =
- object
- inherit [_] V.iter_typed_value
-
- method! visit_borrow_content _ bc =
- if with_borrows then raise (FoundBorrowContent bc) else ()
-
- method! visit_loan_content _ lc = raise (FoundLoanContent lc)
- end
- in
- (* We use exceptions *)
- try
- obj#visit_typed_value () v;
- None
- with
- | FoundLoanContent lc -> Some (LoanContent lc)
- | FoundBorrowContent bc -> Some (BorrowContent bc)
-
-type gproj_borrows =
- | AProjBorrows of V.AbstractionId.id * V.symbolic_value
- | ProjBorrows of V.symbolic_value
-
-let proj_borrows_intersects_proj_loans
- (proj_borrows : T.RegionId.Set.t * V.symbolic_value * T.rty)
- (proj_loans : T.RegionId.Set.t * V.symbolic_value) : bool =
- let b_regions, b_sv, b_ty = proj_borrows in
- let l_regions, l_sv = proj_loans in
- if same_symbolic_id b_sv l_sv then
- projections_intersect l_sv.V.sv_ty l_regions b_ty b_regions
- else false
-
-(** Result of looking up aproj_borrows which intersect a given aproj_loans in
- the context.
-
- Note that because we we force the expansion of primitively copyable values
- before giving them to abstractions, we only have the following possibilities:
- - no aproj_borrows, in which case the symbolic value was either dropped
- or is in the context
- - exactly one aproj_borrows over a non-shared value
- - potentially several aproj_borrows over shared values
-
- The result contains the ids of the abstractions in which the projectors were
- found, as well as the projection types used in those abstractions.
-*)
-type looked_up_aproj_borrows =
- | NonSharedProj of V.AbstractionId.id * T.rty
- | SharedProjs of (V.AbstractionId.id * T.rty) list
-
-(** Lookup the aproj_borrows (including aproj_shared_borrows) over a
- symbolic value which intersect a given set of regions.
-
- [lookup_shared]: if [true] also explore projectors over shared values,
- otherwise ignore.
-
- This is a helper function.
-*)
-let lookup_intersecting_aproj_borrows_opt (lookup_shared : bool)
- (regions : T.RegionId.Set.t) (sv : V.symbolic_value) (ctx : C.eval_ctx) :
- looked_up_aproj_borrows option =
- let found : looked_up_aproj_borrows option ref = ref None in
- let set_non_shared ((id, ty) : V.AbstractionId.id * T.rty) : unit =
- match !found with
- | None -> found := Some (NonSharedProj (id, ty))
- | Some _ -> failwith "Unreachable"
- in
- let add_shared (x : V.AbstractionId.id * T.rty) : unit =
- match !found with
- | None -> found := Some (SharedProjs [ x ])
- | Some (SharedProjs pl) -> found := Some (SharedProjs (x :: pl))
- | Some (NonSharedProj _) -> failwith "Unreachable"
- in
- let check_add_proj_borrows (is_shared : bool) abs sv' proj_ty =
- if
- proj_borrows_intersects_proj_loans
- (abs.V.regions, sv', proj_ty)
- (regions, sv)
- then
- let x = (abs.abs_id, proj_ty) in
- if is_shared then add_shared x else set_non_shared x
- else ()
- in
- let obj =
- object
- inherit [_] C.iter_eval_ctx as super
- method! visit_abs _ abs = super#visit_abs (Some abs) abs
-
- method! visit_abstract_shared_borrows abs asb =
- (* Sanity check *)
- (match !found with
- | Some (NonSharedProj _) -> failwith "Unreachable"
- | _ -> ());
- (* Explore *)
- if lookup_shared then
- let abs = Option.get abs in
- let check asb =
- match asb with
- | V.AsbBorrow _ -> ()
- | V.AsbProjReborrows (sv', proj_ty) ->
- let is_shared = true in
- check_add_proj_borrows is_shared abs sv' proj_ty
- in
- List.iter check asb
- else ()
-
- method! visit_aproj abs sproj =
- (let abs = Option.get abs in
- match sproj with
- | AProjLoans _ | AEndedProjLoans _ | AEndedProjBorrows _
- | AIgnoredProjBorrows ->
- ()
- | AProjBorrows (sv', proj_rty) ->
- let is_shared = false in
- check_add_proj_borrows is_shared abs sv' proj_rty);
- super#visit_aproj abs sproj
- end
- in
- (* Visit *)
- obj#visit_eval_ctx None ctx;
- (* Return *)
- !found
-
-(** Lookup the aproj_borrows (not aproj_borrows_shared!) over a symbolic
- value which intersects a given set of regions.
-
- Note that there should be **at most one** (one reason is that we force
- the expansion of primitively copyable values before giving them to
- abstractions).
-
- Returns the id of the owning abstraction, and the projection type used in
- this abstraction.
-*)
-let lookup_intersecting_aproj_borrows_not_shared_opt
- (regions : T.RegionId.Set.t) (sv : V.symbolic_value) (ctx : C.eval_ctx) :
- (V.AbstractionId.id * T.rty) option =
- let lookup_shared = false in
- match lookup_intersecting_aproj_borrows_opt lookup_shared regions sv ctx with
- | None -> None
- | Some (NonSharedProj (abs_id, rty)) -> Some (abs_id, rty)
- | _ -> failwith "Unexpected"
-
-(** Similar to {!lookup_intersecting_aproj_borrows_opt}, but updates the
- values.
-
- This is a helper function: it might break invariants.
- *)
-let update_intersecting_aproj_borrows (can_update_shared : bool)
- (update_shared : V.AbstractionId.id -> T.rty -> V.abstract_shared_borrows)
- (update_non_shared : V.AbstractionId.id -> T.rty -> V.aproj)
- (regions : T.RegionId.Set.t) (sv : V.symbolic_value) (ctx : C.eval_ctx) :
- C.eval_ctx =
- (* Small helpers for sanity checks *)
- let shared = ref None in
- let add_shared () =
- match !shared with None -> shared := Some true | Some b -> assert b
- in
- let set_non_shared () =
- match !shared with
- | None -> shared := Some false
- | Some _ -> failwith "Found unexpected intersecting proj_borrows"
- in
- let check_proj_borrows is_shared abs sv' proj_ty =
- if
- proj_borrows_intersects_proj_loans
- (abs.V.regions, sv', proj_ty)
- (regions, sv)
- then (
- if is_shared then add_shared () else set_non_shared ();
- true)
- else false
- in
- (* The visitor *)
- let obj =
- object
- inherit [_] C.map_eval_ctx as super
- method! visit_abs _ abs = super#visit_abs (Some abs) abs
-
- method! visit_abstract_shared_borrows abs asb =
- (* Sanity check *)
- (match !shared with Some b -> assert b | _ -> ());
- (* Explore *)
- if can_update_shared then
- let abs = Option.get abs in
- let update (asb : V.abstract_shared_borrow) :
- V.abstract_shared_borrows =
- match asb with
- | V.AsbBorrow _ -> [ asb ]
- | V.AsbProjReborrows (sv', proj_ty) ->
- let is_shared = true in
- if check_proj_borrows is_shared abs sv' proj_ty then
- update_shared abs.abs_id proj_ty
- else [ asb ]
- in
- List.concat (List.map update asb)
- else asb
-
- method! visit_aproj abs sproj =
- match sproj with
- | AProjLoans _ | AEndedProjLoans _ | AEndedProjBorrows _
- | AIgnoredProjBorrows ->
- super#visit_aproj abs sproj
- | AProjBorrows (sv', proj_rty) ->
- let abs = Option.get abs in
- let is_shared = true in
- if check_proj_borrows is_shared abs sv' proj_rty then
- update_non_shared abs.abs_id proj_rty
- else super#visit_aproj (Some abs) sproj
- end
- in
- (* Apply *)
- let ctx = obj#visit_eval_ctx None ctx in
- (* Check that we updated the context at least once *)
- assert (Option.is_some !shared);
- (* Return *)
- ctx
-
-(** Simply calls {!update_intersecting_aproj_borrows} to update a
- proj_borrows over a non-shared value.
-
- We check that we update *at least* one proj_borrows.
-
- This is a helper function: it might break invariants.
- *)
-let update_intersecting_aproj_borrows_non_shared (regions : T.RegionId.Set.t)
- (sv : V.symbolic_value) (nv : V.aproj) (ctx : C.eval_ctx) : C.eval_ctx =
- (* Small helpers *)
- let can_update_shared = false in
- let update_shared _ _ = failwith "Unexpected" in
- let updated = ref false in
- let update_non_shared _ _ =
- (* We can update more than one borrow! *)
- updated := true;
- nv
- in
- (* Update *)
- let ctx =
- update_intersecting_aproj_borrows can_update_shared update_shared
- update_non_shared regions sv ctx
- in
- (* Check that we updated at least once *)
- assert !updated;
- (* Return *)
- ctx
-
-(** Simply calls {!update_intersecting_aproj_borrows} to remove the
- proj_borrows over shared values.
-
- This is a helper function: it might break invariants.
- *)
-let remove_intersecting_aproj_borrows_shared (regions : T.RegionId.Set.t)
- (sv : V.symbolic_value) (ctx : C.eval_ctx) : C.eval_ctx =
- (* Small helpers *)
- let can_update_shared = true in
- let update_shared _ _ = [] in
- let update_non_shared _ _ = failwith "Unexpected" in
- (* Update *)
- update_intersecting_aproj_borrows can_update_shared update_shared
- update_non_shared regions sv ctx
-
-(** Updates the proj_loans intersecting some projection.
-
- This is a helper function: it might break invariants.
-
- Note that we can update more than one projector of loans! Consider the
- following example:
- {[
- fn f<'a, 'b>(...) -> (&'a mut u32, &'b mut u32));
- fn g<'c>(&'c mut u32, &'c mut u32);
-
- let p = f(...);
- g(move p);
-
- // Symbolic context after the call to g:
- // abs'a {'a} { [s@0 <: (&'a mut u32, &'b mut u32)] }
- // abs'b {'b} { [s@0 <: (&'a mut u32, &'b mut u32)] }
- //
- // abs'c {'c} { (s@0 <: (&'c mut u32, &'c mut u32)) }
- ]}
-
- Note that for sanity, this function checks that we update *at least* one
- projector of loans.
-
- [subst]: takes as parameters the abstraction in which we perform the
- substitution and the list of given back values at the projector of
- loans where we perform the substitution (see the fields in {!V.AProjLoans}).
- Note that the symbolic value at this place is necessarily equal to [sv],
- which is why we don't give it as parameters.
- *)
-let update_intersecting_aproj_loans (proj_regions : T.RegionId.Set.t)
- (proj_ty : T.rty) (sv : V.symbolic_value)
- (subst : V.abs -> (V.msymbolic_value * V.aproj) list -> V.aproj)
- (ctx : C.eval_ctx) : C.eval_ctx =
- (* Small helpers for sanity checks *)
- let updated = ref false in
- let update abs local_given_back : V.aproj =
- (* Note that we can update more than once! *)
- updated := true;
- subst abs local_given_back
- in
- (* The visitor *)
- let obj =
- object
- inherit [_] C.map_eval_ctx as super
- method! visit_abs _ abs = super#visit_abs (Some abs) abs
-
- method! visit_aproj abs sproj =
- match sproj with
- | AProjBorrows _ | AEndedProjLoans _ | AEndedProjBorrows _
- | AIgnoredProjBorrows ->
- super#visit_aproj abs sproj
- | AProjLoans (sv', given_back) ->
- let abs = Option.get abs in
- if same_symbolic_id sv sv' then (
- assert (sv.sv_ty = sv'.sv_ty);
- if
- projections_intersect proj_ty proj_regions sv'.V.sv_ty
- abs.regions
- then update abs given_back
- else super#visit_aproj (Some abs) sproj)
- else super#visit_aproj (Some abs) sproj
- end
- in
- (* Apply *)
- let ctx = obj#visit_eval_ctx None ctx in
- (* Check that we updated the context at least once *)
- assert !updated;
- (* Return *)
- ctx
-
-(** Helper function: lookup an {!V.AProjLoans} by using an abstraction id and a
- symbolic value.
-
- We return the information from the looked up projector of loans. See the
- fields in {!V.AProjLoans} (we don't return the symbolic value, because it
- is equal to [sv]).
-
- Sanity check: we check that there is exactly one projector which corresponds
- to the couple (abstraction id, symbolic value).
- *)
-let lookup_aproj_loans (abs_id : V.AbstractionId.id) (sv : V.symbolic_value)
- (ctx : C.eval_ctx) : (V.msymbolic_value * V.aproj) list =
- (* Small helpers for sanity checks *)
- let found = ref None in
- let set_found x =
- (* There is at most one projector which corresponds to the description *)
- assert (Option.is_none !found);
- found := Some x
- in
- (* The visitor *)
- let obj =
- object
- inherit [_] C.iter_eval_ctx as super
-
- method! visit_abs _ abs =
- if abs.abs_id = abs_id then super#visit_abs (Some abs) abs else ()
-
- method! visit_aproj (abs : V.abs option) sproj =
- (match sproj with
- | AProjBorrows _ | AEndedProjLoans _ | AEndedProjBorrows _
- | AIgnoredProjBorrows ->
- super#visit_aproj abs sproj
- | AProjLoans (sv', given_back) ->
- let abs = Option.get abs in
- assert (abs.abs_id = abs_id);
- if sv'.sv_id = sv.sv_id then (
- assert (sv' = sv);
- set_found given_back)
- else ());
- super#visit_aproj abs sproj
- end
- in
- (* Apply *)
- obj#visit_eval_ctx None ctx;
- (* Return *)
- Option.get !found
-
-(** Helper function: might break invariants.
-
- Update a projector over loans. The projector is identified by a symbolic
- value and an abstraction id.
-
- Sanity check: we check that there is exactly one projector which corresponds
- to the couple (abstraction id, symbolic value).
- *)
-let update_aproj_loans (abs_id : V.AbstractionId.id) (sv : V.symbolic_value)
- (nproj : V.aproj) (ctx : C.eval_ctx) : C.eval_ctx =
- (* Small helpers for sanity checks *)
- let found = ref false in
- let update () =
- (* We update at most once *)
- assert (not !found);
- found := true;
- nproj
- in
- (* The visitor *)
- let obj =
- object
- inherit [_] C.map_eval_ctx as super
-
- method! visit_abs _ abs =
- if abs.abs_id = abs_id then super#visit_abs (Some abs) abs else abs
-
- method! visit_aproj (abs : V.abs option) sproj =
- match sproj with
- | AProjBorrows _ | AEndedProjLoans _ | AEndedProjBorrows _
- | AIgnoredProjBorrows ->
- super#visit_aproj abs sproj
- | AProjLoans (sv', _) ->
- let abs = Option.get abs in
- assert (abs.abs_id = abs_id);
- if sv'.sv_id = sv.sv_id then (
- assert (sv' = sv);
- update ())
- else super#visit_aproj (Some abs) sproj
- end
- in
- (* Apply *)
- let ctx = obj#visit_eval_ctx None ctx in
- (* Sanity check *)
- assert !found;
- (* Return *)
- ctx
-
-(** Helper function: might break invariants.
-
- Update a projector over borrows. The projector is identified by a symbolic
- value and an abstraction id.
-
- Sanity check: we check that there is exactly one projector which corresponds
- to the couple (abstraction id, symbolic value).
-
- TODO: factorize with {!update_aproj_loans}?
- *)
-let update_aproj_borrows (abs_id : V.AbstractionId.id) (sv : V.symbolic_value)
- (nproj : V.aproj) (ctx : C.eval_ctx) : C.eval_ctx =
- (* Small helpers for sanity checks *)
- let found = ref false in
- let update () =
- (* We update at most once *)
- assert (not !found);
- found := true;
- nproj
- in
- (* The visitor *)
- let obj =
- object
- inherit [_] C.map_eval_ctx as super
-
- method! visit_abs _ abs =
- if abs.abs_id = abs_id then super#visit_abs (Some abs) abs else abs
-
- method! visit_aproj (abs : V.abs option) sproj =
- match sproj with
- | AProjLoans _ | AEndedProjLoans _ | AEndedProjBorrows _
- | AIgnoredProjBorrows ->
- super#visit_aproj abs sproj
- | AProjBorrows (sv', _proj_ty) ->
- let abs = Option.get abs in
- assert (abs.abs_id = abs_id);
- if sv'.sv_id = sv.sv_id then (
- assert (sv' = sv);
- update ())
- else super#visit_aproj (Some abs) sproj
- end
- in
- (* Apply *)
- let ctx = obj#visit_eval_ctx None ctx in
- (* Sanity check *)
- assert !found;
- (* Return *)
- ctx
-
-(** Helper function: might break invariants.
-
- Converts an {!V.AProjLoans} to an {!V.AEndedProjLoans}. The projector is identified
- by a symbolic value and an abstraction id.
- *)
-let update_aproj_loans_to_ended (abs_id : V.AbstractionId.id)
- (sv : V.symbolic_value) (ctx : C.eval_ctx) : C.eval_ctx =
- (* Lookup the projector of loans *)
- let given_back = lookup_aproj_loans abs_id sv ctx in
- (* Create the new value for the projector *)
- let nproj = V.AEndedProjLoans (sv, given_back) in
- (* Insert it *)
- let ctx = update_aproj_loans abs_id sv nproj ctx in
- (* Return *)
- ctx
-
-let no_aproj_over_symbolic_in_context (sv : V.symbolic_value) (ctx : C.eval_ctx)
- : unit =
- (* The visitor *)
- let obj =
- object
- inherit [_] C.iter_eval_ctx as super
-
- method! visit_aproj env sproj =
- (match sproj with
- | AEndedProjLoans _ | AEndedProjBorrows _ | AIgnoredProjBorrows -> ()
- | AProjLoans (sv', _) | AProjBorrows (sv', _) ->
- if sv'.sv_id = sv.sv_id then raise Found else ());
- super#visit_aproj env sproj
- end
- in
- (* Apply *)
- try obj#visit_eval_ctx () ctx
- with Found -> failwith "update_aproj_loans_to_ended: failed"
-
-(** Helper function
-
- Return the loan (aloan, loan, proj_loans over a symbolic value) we find
- in an abstraction, if there is.
-
- **Remark:** we don't take the *ignored* mut/shared loans into account.
- *)
-let get_first_non_ignored_aloan_in_abstraction (abs : V.abs) :
- borrow_ids_or_symbolic_value option =
- (* Explore to find a loan *)
- let obj =
- object
- inherit [_] V.iter_abs as super
-
- method! visit_aloan_content env lc =
- match lc with
- | V.AMutLoan (bid, _) -> raise (FoundBorrowIds (Borrow bid))
- | V.ASharedLoan (bids, _, _) -> raise (FoundBorrowIds (Borrows bids))
- | V.AEndedMutLoan { given_back = _; child = _; given_back_meta = _ }
- | V.AEndedSharedLoan (_, _) ->
- super#visit_aloan_content env lc
- | V.AIgnoredMutLoan (_, _) ->
- (* Ignore *)
- super#visit_aloan_content env lc
- | V.AEndedIgnoredMutLoan
- { given_back = _; child = _; given_back_meta = _ }
- | V.AIgnoredSharedLoan _ ->
- (* Ignore *)
- super#visit_aloan_content env lc
-
- (** We may need to visit loan contents because of shared values *)
- method! visit_loan_content _ lc =
- match lc with
- | V.MutLoan _ ->
- (* The mut loan linked to the mutable borrow present in a shared
- * value in an abstraction should be in an AProjBorrows *)
- failwith "Unreachable"
- | V.SharedLoan (bids, _) -> raise (FoundBorrowIds (Borrows bids))
-
- method! visit_aproj env sproj =
- (match sproj with
- | V.AProjBorrows (_, _)
- | V.AEndedProjLoans _ | V.AEndedProjBorrows _ | V.AIgnoredProjBorrows ->
- ()
- | V.AProjLoans (sv, _) -> raise (ValuesUtils.FoundSymbolicValue sv));
- super#visit_aproj env sproj
- end
- in
- try
- (* Check if there are loans *)
- obj#visit_abs () abs;
- (* No loans *)
- None
- with
- (* There are loans *)
- | FoundBorrowIds bids -> Some (BorrowIds bids)
- | ValuesUtils.FoundSymbolicValue sv ->
- (* There are loan projections over symbolic values *)
- Some (SymbolicValue sv)
diff --git a/src/InterpreterExpansion.ml b/src/InterpreterExpansion.ml
deleted file mode 100644
index 0ca34b43..00000000
--- a/src/InterpreterExpansion.ml
+++ /dev/null
@@ -1,733 +0,0 @@
-(* This module provides the functions which handle expansion of symbolic values.
- * For now, this file doesn't handle expansion of ⊥ values because they need
- * some path utilities for replacement. We might change that in the future (by
- * using indices to identify the values for instance). *)
-
-module T = Types
-module V = Values
-module E = Expressions
-module C = Contexts
-module Subst = Substitute
-module L = Logging
-open TypesUtils
-module Inv = Invariants
-module S = SynthesizeSymbolic
-module SA = SymbolicAst
-open Cps
-open ValuesUtils
-open InterpreterUtils
-open InterpreterProjectors
-open InterpreterBorrows
-
-(** The local logger *)
-let log = L.expansion_log
-
-(** Projector kind *)
-type proj_kind = LoanProj | BorrowProj
-
-(** Auxiliary function.
- Apply a symbolic expansion to avalues in a context, targetting a specific
- kind of projectors.
-
- [proj_kind] controls whether we apply the expansion to projectors
- on loans or projectors on borrows.
-
- When dealing with reference expansion, it is necessary to first apply the
- expansion on loan projectors, then on borrow projectors. The reason is
- that reducing the borrow projectors might require to perform some reborrows,
- in which case we need to lookup the corresponding loans in the context.
-
- [allow_reborrows] controls whether we allow reborrows or not. It is useful
- only if we target borrow projectors.
-
- Also, if this function is called on an expansion for *shared references*,
- the proj borrows should already have been expanded.
-
- TODO: the way this function is used is a bit complex, especially because of
- the above condition. Maybe we should have:
- 1. a generic function to expand the loan projectors
- 2. a function to expand the borrow projectors for non-borrows
- 3. specialized functions for mut borrows and shared borrows
- Note that 2. and 3. may have a little bit of duplicated code, but hopefully
- it would make things clearer.
-*)
-let apply_symbolic_expansion_to_target_avalues (config : C.config)
- (allow_reborrows : bool) (proj_kind : proj_kind)
- (original_sv : V.symbolic_value) (expansion : V.symbolic_expansion)
- (ctx : C.eval_ctx) : C.eval_ctx =
- (* Symbolic values contained in the expansion might contain already ended regions *)
- let check_symbolic_no_ended = false in
- (* Prepare reborrows registration *)
- let fresh_reborrow, apply_registered_reborrows =
- prepare_reborrows config allow_reborrows
- in
- (* Visitor to apply the expansion *)
- let obj =
- object (self)
- inherit [_] C.map_eval_ctx as super
-
- (** When visiting an abstraction, we remember the regions it owns to be
- able to properly reduce projectors when expanding symbolic values *)
- method! visit_abs current_abs abs =
- assert (Option.is_none current_abs);
- let current_abs = Some abs in
- super#visit_abs current_abs abs
-
- (** We carefully updated {!visit_ASymbolic} so that {!visit_aproj} is called
- only on child projections (i.e., projections which appear in {!AEndedProjLoans}).
- The role of visit_aproj is then to check we don't have to expand symbolic
- values in child projections, because it should never happen
- *)
- method! visit_aproj current_abs aproj =
- (match aproj with
- | AProjLoans (sv, _) | AProjBorrows (sv, _) ->
- assert (not (same_symbolic_id sv original_sv))
- | AEndedProjLoans _ | AEndedProjBorrows _ | AIgnoredProjBorrows -> ());
- super#visit_aproj current_abs aproj
-
- method! visit_ASymbolic current_abs aproj =
- let current_abs = Option.get current_abs in
- let proj_regions = current_abs.regions in
- let ancestors_regions = current_abs.ancestors_regions in
- (* Explore in depth first - we won't update anything: we simply
- * want to check we don't have to expand inner symbolic value *)
- match (aproj, proj_kind) with
- | V.AEndedProjBorrows _, _ -> V.ASymbolic aproj
- | V.AEndedProjLoans _, _ ->
- (* Explore the given back values to make sure we don't have to expand
- * anything in there *)
- V.ASymbolic (self#visit_aproj (Some current_abs) aproj)
- | V.AProjLoans (sv, given_back), LoanProj ->
- (* Check if this is the symbolic value we are looking for *)
- if same_symbolic_id sv original_sv then (
- (* There mustn't be any given back values *)
- assert (given_back = []);
- (* Apply the projector *)
- let projected_value =
- apply_proj_loans_on_symbolic_expansion proj_regions expansion
- original_sv.V.sv_ty
- in
- (* Replace *)
- projected_value.V.value)
- else
- (* Not the searched symbolic value: nothing to do *)
- super#visit_ASymbolic (Some current_abs) aproj
- | V.AProjBorrows (sv, proj_ty), BorrowProj ->
- (* Check if this is the symbolic value we are looking for *)
- if same_symbolic_id sv original_sv then
- (* Convert the symbolic expansion to a value on which we can
- * apply a projector (if the expansion is a reference expansion,
- * convert it to a borrow) *)
- (* WARNING: we mustn't get there if the expansion is for a shared
- * reference. *)
- let expansion =
- symbolic_expansion_non_shared_borrow_to_value original_sv
- expansion
- in
- (* Apply the projector *)
- let projected_value =
- apply_proj_borrows check_symbolic_no_ended ctx fresh_reborrow
- proj_regions ancestors_regions expansion proj_ty
- in
- (* Replace *)
- projected_value.V.value
- else
- (* Not the searched symbolic value: nothing to do *)
- super#visit_ASymbolic (Some current_abs) aproj
- | V.AProjLoans _, BorrowProj
- | V.AProjBorrows (_, _), LoanProj
- | V.AIgnoredProjBorrows, _ ->
- (* Nothing to do *)
- V.ASymbolic aproj
- end
- in
- (* Apply the expansion *)
- let ctx = obj#visit_eval_ctx None ctx in
- (* Apply the reborrows *)
- apply_registered_reborrows ctx
-
-(** Auxiliary function.
- Apply a symbolic expansion to avalues in a context.
-*)
-let apply_symbolic_expansion_to_avalues (config : C.config)
- (allow_reborrows : bool) (original_sv : V.symbolic_value)
- (expansion : V.symbolic_expansion) (ctx : C.eval_ctx) : C.eval_ctx =
- let apply_expansion proj_kind ctx =
- apply_symbolic_expansion_to_target_avalues config allow_reborrows proj_kind
- original_sv expansion ctx
- in
- (* First target the loan projectors, then the borrow projectors *)
- let ctx = apply_expansion LoanProj ctx in
- let ctx = apply_expansion BorrowProj ctx in
- ctx
-
-(** Auxiliary function.
-
- Simply replace the symbolic values (*not avalues*) in the context with
- a given value. Will break invariants if not used properly.
-*)
-let replace_symbolic_values (at_most_once : bool)
- (original_sv : V.symbolic_value) (nv : V.value) (ctx : C.eval_ctx) :
- C.eval_ctx =
- (* Count *)
- let replaced = ref false in
- let replace () =
- if at_most_once then assert (not !replaced);
- replaced := true;
- nv
- in
- (* Visitor to apply the substitution *)
- let obj =
- object
- inherit [_] C.map_eval_ctx as super
-
- method! visit_Symbolic env spc =
- if same_symbolic_id spc original_sv then replace ()
- else super#visit_Symbolic env spc
- end
- in
- (* Apply the substitution *)
- let ctx = obj#visit_eval_ctx None ctx in
- (* Return *)
- ctx
-
-(** Apply a symbolic expansion to a context, by replacing the original
- symbolic value with its expanded value. Is valid only if the expansion
- is not a borrow (i.e., an adt...).
-
- This function does update the synthesis.
-*)
-let apply_symbolic_expansion_non_borrow (config : C.config)
- (original_sv : V.symbolic_value) (expansion : V.symbolic_expansion)
- (ctx : C.eval_ctx) : C.eval_ctx =
- (* Apply the expansion to non-abstraction values *)
- let nv = symbolic_expansion_non_borrow_to_value original_sv expansion in
- let at_most_once = false in
- let ctx = replace_symbolic_values at_most_once original_sv nv.V.value ctx in
- (* Apply the expansion to abstraction values *)
- let allow_reborrows = false in
- apply_symbolic_expansion_to_avalues config allow_reborrows original_sv
- expansion ctx
-
-(** Compute the expansion of an adt value.
-
- The function might return a list of values if the symbolic value to expand
- is an enumeration.
-
- [expand_enumerations] controls the expansion of enumerations: if false, it
- doesn't allow the expansion of enumerations *containing several variants*.
- *)
-let compute_expanded_symbolic_adt_value (expand_enumerations : bool)
- (kind : V.sv_kind) (def_id : T.TypeDeclId.id)
- (regions : T.RegionId.id T.region list) (types : T.rty list)
- (ctx : C.eval_ctx) : V.symbolic_expansion list =
- (* Lookup the definition and check if it is an enumeration with several
- * variants *)
- let def = C.ctx_lookup_type_decl ctx def_id in
- assert (List.length regions = List.length def.T.region_params);
- (* Retrieve, for every variant, the list of its instantiated field types *)
- let variants_fields_types =
- Subst.type_decl_get_instantiated_variants_fields_rtypes def regions types
- in
- (* Check if there is strictly more than one variant *)
- if List.length variants_fields_types > 1 && not expand_enumerations then
- raise (Failure "Not allowed to expand enumerations with several variants");
- (* Initialize the expanded value for a given variant *)
- let initialize
- ((variant_id, field_types) : T.VariantId.id option * T.rty list) :
- V.symbolic_expansion =
- let field_values =
- List.map (fun (ty : T.rty) -> mk_fresh_symbolic_value kind ty) field_types
- in
- let see = V.SeAdt (variant_id, field_values) in
- see
- in
- (* Initialize all the expanded values of all the variants *)
- List.map initialize variants_fields_types
-
-(** Compute the expansion of an Option value.
- *)
-let compute_expanded_symbolic_option_value (expand_enumerations : bool)
- (kind : V.sv_kind) (ty : T.rty) : V.symbolic_expansion list =
- assert expand_enumerations;
- let some_se =
- V.SeAdt (Some T.option_some_id, [ mk_fresh_symbolic_value kind ty ])
- in
- let none_se = V.SeAdt (Some T.option_none_id, []) in
- [ none_se; some_se ]
-
-let compute_expanded_symbolic_tuple_value (kind : V.sv_kind)
- (field_types : T.rty list) : V.symbolic_expansion =
- (* Generate the field values *)
- let field_values =
- List.map (fun sv_ty -> mk_fresh_symbolic_value kind sv_ty) field_types
- in
- let variant_id = None in
- let see = V.SeAdt (variant_id, field_values) in
- see
-
-let compute_expanded_symbolic_box_value (kind : V.sv_kind) (boxed_ty : T.rty) :
- V.symbolic_expansion =
- (* Introduce a fresh symbolic value *)
- let boxed_value = mk_fresh_symbolic_value kind boxed_ty in
- let see = V.SeAdt (None, [ boxed_value ]) in
- see
-
-let expand_symbolic_value_shared_borrow (config : C.config)
- (original_sv : V.symbolic_value) (original_sv_place : SA.mplace option)
- (ref_ty : T.rty) : cm_fun =
- fun cf ctx ->
- (* First, replace the projectors on borrows.
- * The important point is that the symbolic value to expand may appear
- * several times, if it has been copied. In this case, we need to introduce
- * one fresh borrow id per instance.
- *)
- let borrows = ref V.BorrowId.Set.empty in
- let fresh_borrow () =
- let bid' = C.fresh_borrow_id () in
- borrows := V.BorrowId.Set.add bid' !borrows;
- bid'
- in
- (* Small utility used on shared borrows in abstractions (regular borrow
- * projector and asb).
- * Returns [Some] if the symbolic value has been expanded to an asb list,
- * [None] otherwise *)
- let reborrow_ashared proj_regions (sv : V.symbolic_value) (proj_ty : T.rty) :
- V.abstract_shared_borrows option =
- if same_symbolic_id sv original_sv then
- match proj_ty with
- | T.Ref (r, ref_ty, T.Shared) ->
- (* Projector over the shared value *)
- let shared_asb = V.AsbProjReborrows (sv, ref_ty) in
- (* Check if the region is in the set of projected regions *)
- if region_in_set r proj_regions then
- (* In the set: we need to reborrow *)
- let bid = fresh_borrow () in
- Some [ V.AsbBorrow bid; shared_asb ]
- else (* Not in the set: ignore *)
- Some [ shared_asb ]
- | _ -> raise (Failure "Unexpected")
- else None
- in
- (* The fresh symbolic value for the shared value *)
- let shared_sv = mk_fresh_symbolic_value original_sv.sv_kind ref_ty in
- (* Visitor to replace the projectors on borrows *)
- let obj =
- object (self)
- inherit [_] C.map_eval_ctx as super
-
- method! visit_Symbolic env sv =
- if same_symbolic_id sv original_sv then
- let bid = fresh_borrow () in
- V.Borrow
- (V.SharedBorrow (mk_typed_value_from_symbolic_value shared_sv, bid))
- else super#visit_Symbolic env sv
-
- method! visit_Abs proj_regions abs =
- assert (Option.is_none proj_regions);
- let proj_regions = Some abs.V.regions in
- super#visit_Abs proj_regions abs
-
- method! visit_AProjSharedBorrow proj_regions asb =
- let expand_asb (asb : V.abstract_shared_borrow) :
- V.abstract_shared_borrows =
- match asb with
- | V.AsbBorrow _ -> [ asb ]
- | V.AsbProjReborrows (sv, proj_ty) -> (
- match reborrow_ashared (Option.get proj_regions) sv proj_ty with
- | None -> [ asb ]
- | Some asb -> asb)
- in
- let asb = List.concat (List.map expand_asb asb) in
- V.AProjSharedBorrow asb
-
- (** We carefully updated {!visit_ASymbolic} so that {!visit_aproj} is called
- only on child projections (i.e., projections which appear in {!AEndedProjLoans}).
- The role of visit_aproj is then to check we don't have to expand symbolic
- values in child projections, because it should never happen
- *)
- method! visit_aproj proj_regions aproj =
- (match aproj with
- | AProjLoans (sv, _) | AProjBorrows (sv, _) ->
- assert (not (same_symbolic_id sv original_sv))
- | AEndedProjLoans _ | AEndedProjBorrows _ | AIgnoredProjBorrows -> ());
- super#visit_aproj proj_regions aproj
-
- method! visit_ASymbolic proj_regions aproj =
- match aproj with
- | AEndedProjBorrows _ | AIgnoredProjBorrows ->
- (* We ignore borrows *) V.ASymbolic aproj
- | AProjLoans _ ->
- (* Loans are handled later *)
- V.ASymbolic aproj
- | AProjBorrows (sv, proj_ty) -> (
- (* Check if we need to reborrow *)
- match reborrow_ashared (Option.get proj_regions) sv proj_ty with
- | None -> super#visit_ASymbolic proj_regions aproj
- | Some asb -> V.ABorrow (V.AProjSharedBorrow asb))
- | AEndedProjLoans _ ->
- (* Sanity check: make sure there is nothing to expand inside the
- * children projections *)
- V.ASymbolic (self#visit_aproj proj_regions aproj)
- end
- in
- (* Call the visitor *)
- let ctx = obj#visit_eval_ctx None ctx in
- (* Finally, replace the projectors on loans *)
- let bids = !borrows in
- assert (not (V.BorrowId.Set.is_empty bids));
- let see = V.SeSharedRef (bids, shared_sv) in
- let allow_reborrows = true in
- let ctx =
- apply_symbolic_expansion_to_avalues config allow_reborrows original_sv see
- ctx
- in
- (* Call the continuation *)
- let expr = cf ctx in
- (* Update the synthesized program *)
- S.synthesize_symbolic_expansion_no_branching original_sv original_sv_place see
- expr
-
-(** TODO: simplify and merge with the other expansion function *)
-let expand_symbolic_value_borrow (config : C.config)
- (original_sv : V.symbolic_value) (original_sv_place : SA.mplace option)
- (region : T.RegionId.id T.region) (ref_ty : T.rty) (rkind : T.ref_kind) :
- cm_fun =
- fun cf ctx ->
- (* Check that we are allowed to expand the reference *)
- assert (not (region_in_set region ctx.ended_regions));
- (* Match on the reference kind *)
- match rkind with
- | T.Mut ->
- (* Simple case: simply create a fresh symbolic value and a fresh
- * borrow id *)
- let sv = mk_fresh_symbolic_value original_sv.sv_kind ref_ty in
- let bid = C.fresh_borrow_id () in
- let see = V.SeMutRef (bid, sv) in
- (* Expand the symbolic values - we simply perform a substitution (and
- * check that we perform exactly one substitution) *)
- let nv = symbolic_expansion_non_shared_borrow_to_value original_sv see in
- let at_most_once = true in
- let ctx =
- replace_symbolic_values at_most_once original_sv nv.V.value ctx
- in
- (* Expand the symbolic avalues *)
- let allow_reborrows = true in
- let ctx =
- apply_symbolic_expansion_to_avalues config allow_reborrows original_sv
- see ctx
- in
- (* Apply the continuation *)
- let expr = cf ctx in
- (* Update the synthesized program *)
- S.synthesize_symbolic_expansion_no_branching original_sv original_sv_place
- see expr
- | T.Shared ->
- expand_symbolic_value_shared_borrow config original_sv original_sv_place
- ref_ty cf ctx
-
-(** A small helper.
-
- Apply a branching symbolic expansion to a context and execute all the
- branches. Note that the expansion is optional for every branch (this is
- used for integer expansion: see [expand_symbolic_int]).
-
- [see_cf_l]: list of pairs (optional symbolic expansion, continuation)
-*)
-let apply_branching_symbolic_expansions_non_borrow (config : C.config)
- (sv : V.symbolic_value) (sv_place : SA.mplace option)
- (see_cf_l : (V.symbolic_expansion option * m_fun) list) : m_fun =
- fun ctx ->
- assert (see_cf_l <> []);
- (* Apply the symbolic expansion in in the context and call the continuation *)
- let resl =
- List.map
- (fun (see_opt, cf) ->
- (* Expansion *)
- let ctx =
- match see_opt with
- | None -> ctx
- | Some see -> apply_symbolic_expansion_non_borrow config sv see ctx
- in
- (* Continuation *)
- cf ctx)
- see_cf_l
- in
- (* Collect the result: either we computed no subterm, or we computed all
- * of them *)
- let subterms =
- match resl with
- | Some _ :: _ -> Some (List.map Option.get resl)
- | None :: _ ->
- List.iter (fun res -> assert (res = None)) resl;
- None
- | _ -> raise (Failure "Unreachable")
- in
- (* Synthesize and return *)
- let seel = List.map fst see_cf_l in
- S.synthesize_symbolic_expansion sv sv_place seel subterms
-
-(** Expand a symbolic boolean *)
-let expand_symbolic_bool (config : C.config) (sp : V.symbolic_value)
- (sp_place : SA.mplace option) (cf_true : m_fun) (cf_false : m_fun) : m_fun =
- fun ctx ->
- (* Compute the expanded value *)
- let original_sv = sp in
- let original_sv_place = sp_place in
- let rty = original_sv.V.sv_ty in
- assert (rty = T.Bool);
- (* Expand the symbolic value to true or false and continue execution *)
- let see_true = V.SeConcrete (V.Bool true) in
- let see_false = V.SeConcrete (V.Bool false) in
- let seel = [ (Some see_true, cf_true); (Some see_false, cf_false) ] in
- (* Apply the symbolic expansion (this also outputs the updated symbolic AST) *)
- apply_branching_symbolic_expansions_non_borrow config original_sv
- original_sv_place seel ctx
-
-(** Expand a symbolic value.
-
- [allow_branching]: if [true] we can branch (by expanding enumerations with
- stricly more than one variant), otherwise we can't.
-
- TODO: rename [sp] to [sv]
- *)
-let expand_symbolic_value (config : C.config) (allow_branching : bool)
- (sp : V.symbolic_value) (sp_place : SA.mplace option) : cm_fun =
- fun cf ctx ->
- (* Debug *)
- log#ldebug (lazy ("expand_symbolic_value:" ^ symbolic_value_to_string ctx sp));
- (* Remember the initial context for printing purposes *)
- let ctx0 = ctx in
- (* Compute the expanded value - note that when doing so, we may introduce
- * fresh symbolic values in the context (which thus gets updated) *)
- let original_sv = sp in
- let original_sv_place = sp_place in
- let rty = original_sv.V.sv_ty in
- let cc : cm_fun =
- fun cf ctx ->
- match rty with
- (* TODO: I think it is possible to factorize a lot the below match *)
- (* "Regular" ADTs *)
- | T.Adt (T.AdtId def_id, regions, types) ->
- (* Compute the expanded value *)
- let seel =
- compute_expanded_symbolic_adt_value allow_branching sp.sv_kind def_id
- regions types ctx
- in
- (* Check for branching *)
- assert (List.length seel <= 1 || allow_branching);
- (* Apply *)
- let seel = List.map (fun see -> (Some see, cf)) seel in
- apply_branching_symbolic_expansions_non_borrow config original_sv
- original_sv_place seel ctx
- (* Options *)
- | T.Adt (T.Assumed Option, regions, types) ->
- (* Sanity check *)
- assert (regions = []);
- let ty = Collections.List.to_cons_nil types in
- (* Compute the expanded value *)
- let seel =
- compute_expanded_symbolic_option_value allow_branching sp.sv_kind ty
- in
-
- (* Check for branching *)
- assert (List.length seel <= 1 || allow_branching);
- (* Apply *)
- let seel = List.map (fun see -> (Some see, cf)) seel in
- apply_branching_symbolic_expansions_non_borrow config original_sv
- original_sv_place seel ctx
- (* Tuples *)
- | T.Adt (T.Tuple, [], tys) ->
- (* Generate the field values *)
- let see = compute_expanded_symbolic_tuple_value sp.sv_kind tys in
- (* Apply in the context *)
- let ctx =
- apply_symbolic_expansion_non_borrow config original_sv see ctx
- in
- (* Call the continuation *)
- let expr = cf ctx in
- (* Update the synthesized program *)
- S.synthesize_symbolic_expansion_no_branching original_sv
- original_sv_place see expr
- (* Boxes *)
- | T.Adt (T.Assumed T.Box, [], [ boxed_ty ]) ->
- let see = compute_expanded_symbolic_box_value sp.sv_kind boxed_ty in
- (* Apply in the context *)
- let ctx =
- apply_symbolic_expansion_non_borrow config original_sv see ctx
- in
- (* Call the continuation *)
- let expr = cf ctx in
- (* Update the synthesized program *)
- S.synthesize_symbolic_expansion_no_branching original_sv
- original_sv_place see expr
- (* Borrows *)
- | T.Ref (region, ref_ty, rkind) ->
- expand_symbolic_value_borrow config original_sv original_sv_place region
- ref_ty rkind cf ctx
- (* Booleans *)
- | T.Bool ->
- assert allow_branching;
- expand_symbolic_bool config sp sp_place cf cf ctx
- | _ ->
- raise
- (Failure ("expand_symbolic_value: unexpected type: " ^ T.show_rty rty))
- in
- (* Debug *)
- let cc =
- comp_unit cc (fun ctx ->
- log#ldebug
- (lazy
- ("expand_symbolic_value: "
- ^ symbolic_value_to_string ctx0 sp
- ^ "\n\n- original context:\n" ^ eval_ctx_to_string ctx0
- ^ "\n\n- new context:\n" ^ eval_ctx_to_string ctx ^ "\n"));
- (* Sanity check: the symbolic value has disappeared *)
- assert (not (symbolic_value_id_in_ctx original_sv.V.sv_id ctx)))
- in
- (* Continue *)
- cc cf ctx
-
-(** Symbolic integers are expanded upon evaluating a [switch], when the integer
- is not an enumeration discriminant.
- Note that a discriminant is never symbolic: we evaluate discriminant values
- upon evaluating [eval_discriminant], which always generates a concrete value
- (because if we call it on a symbolic enumeration, we expand the enumeration
- *then* evaluate the discriminant). This is how we can spot "regular" switches
- over integers.
-
-
- When expanding a boolean upon evaluating an [if ... then ... else ...],
- or an enumeration just before matching over it, we can simply expand the
- boolean/enumeration (generating a list of contexts from which to execute)
- then retry evaluating the [if ... then ... else ...] or the [match]: as
- the scrutinee will then have a concrete value, the interpreter will switch
- to the proper branch.
-
- However, when expanding a "regular" integer for a switch, there is always an
- *otherwise* branch that we can take, for which the integer must remain symbolic
- (because in this branch the scrutinee can take a range of values). We thus
- can't simply expand then retry to evaluate the [switch], because then we
- would loop...
-
- For this reason, we take the list of branches to execute as parameters, and
- directly jump to those branches after the expansion, without reevaluating the
- switch. The continuation is thus for the execution *after* the switch.
-*)
-let expand_symbolic_int (config : C.config) (sv : V.symbolic_value)
- (sv_place : SA.mplace option) (int_type : T.integer_type)
- (tgts : (V.scalar_value * m_fun) list) (otherwise : m_fun) : m_fun =
- (* Sanity check *)
- assert (sv.V.sv_ty = T.Integer int_type);
- (* For all the branches of the switch, we expand the symbolic value
- * to the value given by the branch and execute the branch statement.
- * For the otherwise branch, we leave the symbolic value as it is
- * (because this branch doesn't precisely define which should be the
- * value of the scrutinee...) and simply execute the otherwise statement.
- *
- * First, generate the list of pairs:
- * (optional expansion, statement to execute)
- *)
- let tgts =
- List.map (fun (v, cf) -> (Some (V.SeConcrete (V.Scalar v)), cf)) tgts
- in
- let tgts = List.append tgts [ (None, otherwise) ] in
- (* Then expand and evaluate - this generates the proper symbolic AST *)
- apply_branching_symbolic_expansions_non_borrow config sv sv_place tgts
-
-(** See [expand_symbolic_value] *)
-let expand_symbolic_value_no_branching (config : C.config)
- (sv : V.symbolic_value) (sv_place : SA.mplace option) : cm_fun =
- let allow_branching = false in
- expand_symbolic_value config allow_branching sv sv_place
-
-(** Expand all the symbolic values which contain borrows.
- Allows us to restrict ourselves to a simpler model for the projectors over
- symbolic values.
-
- Fails if doing this requires to do a branching (because we need to expand
- an enumeration with strictly more than one variant, a slice, etc.) or if
- we need to expand a recursive type (because this leads to looping).
- *)
-let greedy_expand_symbolics_with_borrows (config : C.config) : cm_fun =
- fun cf ctx ->
- (* The visitor object, to look for symbolic values in the concrete environment *)
- let obj =
- object
- inherit [_] C.iter_eval_ctx
-
- method! visit_Symbolic _ sv =
- if ty_has_borrows ctx.type_context.type_infos sv.V.sv_ty then
- raise (FoundSymbolicValue sv)
- else ()
-
- (** Don't enter abstractions *)
- method! visit_abs _ _ = ()
- end
- in
-
- let rec expand : cm_fun =
- fun cf ctx ->
- try
- obj#visit_eval_ctx () ctx;
- (* Nothing to expand: continue *)
- cf ctx
- with FoundSymbolicValue sv ->
- (* Expand and recheck the environment *)
- log#ldebug
- (lazy
- ("greedy_expand_symbolics_with_borrows: about to expand: "
- ^ symbolic_value_to_string ctx sv));
- let cc : cm_fun =
- match sv.V.sv_ty with
- | T.Adt (AdtId def_id, _, _) ->
- (* {!expand_symbolic_value_no_branching} checks if there are branchings,
- * but we prefer to also check it here - this leads to cleaner messages
- * and debugging *)
- let def = C.ctx_lookup_type_decl ctx def_id in
- (match def.kind with
- | T.Struct _ | T.Enum ([] | [ _ ]) -> ()
- | T.Enum (_ :: _) ->
- raise
- (Failure
- ("Attempted to greedily expand a symbolic enumeration \
- with > 1 variants (option \
- [greedy_expand_symbolics_with_borrows] of [config]): "
- ^ Print.name_to_string def.name))
- | T.Opaque ->
- raise (Failure "Attempted to greedily expand an opaque type"));
- (* Also, we need to check if the definition is recursive *)
- if C.ctx_type_decl_is_rec ctx def_id then
- raise
- (Failure
- ("Attempted to greedily expand a recursive definition \
- (option [greedy_expand_symbolics_with_borrows] of \
- [config]): "
- ^ Print.name_to_string def.name))
- else expand_symbolic_value_no_branching config sv None
- | T.Adt ((Tuple | Assumed Box), _, _) | T.Ref (_, _, _) ->
- (* Ok *)
- expand_symbolic_value_no_branching config sv None
- | T.Adt (Assumed (Vec | Option), _, _) ->
- (* We can't expand those *)
- raise (Failure "Attempted to greedily expand a Vec or an Option ")
- | T.Array _ -> raise Errors.Unimplemented
- | T.Slice _ -> raise (Failure "Can't expand symbolic slices")
- | T.TypeVar _ | Bool | Char | Never | Integer _ | Str ->
- raise (Failure "Unreachable")
- in
- (* Compose and continue *)
- comp cc expand cf ctx
- in
- (* Apply *)
- expand cf ctx
-
-(** If this mode is activated through the [config], greedily expand the symbolic
- values which need to be expanded. See [config] for more information.
- *)
-let greedy_expand_symbolic_values (config : C.config) : cm_fun =
- fun cf ctx ->
- if config.greedy_expand_symbolics_with_borrows then (
- log#ldebug (lazy "greedy_expand_symbolic_values");
- greedy_expand_symbolics_with_borrows config cf ctx)
- else cf ctx
diff --git a/src/InterpreterExpressions.ml b/src/InterpreterExpressions.ml
deleted file mode 100644
index 62d9b80b..00000000
--- a/src/InterpreterExpressions.ml
+++ /dev/null
@@ -1,720 +0,0 @@
-module T = Types
-module V = Values
-module LA = LlbcAst
-open Scalars
-module E = Expressions
-open Errors
-module C = Contexts
-module Subst = Substitute
-module L = Logging
-module PV = Print.Values
-open TypesUtils
-open ValuesUtils
-module Inv = Invariants
-module S = SynthesizeSymbolic
-open Cps
-open InterpreterUtils
-open InterpreterExpansion
-open InterpreterPaths
-
-(** The local logger *)
-let log = L.expressions_log
-
-(** As long as there are symbolic values at a given place (potentially in subvalues)
- which contain borrows and are primitively copyable, expand them.
-
- We use this function before copying values.
-
- Note that the place should have been prepared so that there are no remaining
- loans.
-*)
-let expand_primitively_copyable_at_place (config : C.config)
- (access : access_kind) (p : E.place) : cm_fun =
- fun cf ctx ->
- (* Small helper *)
- let rec expand : cm_fun =
- fun cf ctx ->
- let v = read_place_unwrap config access p ctx in
- match
- find_first_primitively_copyable_sv_with_borrows
- ctx.type_context.type_infos v
- with
- | None -> cf ctx
- | Some sv ->
- let cc =
- expand_symbolic_value_no_branching config sv
- (Some (S.mk_mplace p ctx))
- in
- comp cc expand cf ctx
- in
- (* Apply *)
- expand cf ctx
-
-(** Read a place (CPS-style function).
-
- We also check that the value *doesn't contain bottoms or inactivated
- borrows.
- *)
-let read_place (config : C.config) (access : access_kind) (p : E.place)
- (cf : V.typed_value -> m_fun) : m_fun =
- fun ctx ->
- let v = read_place_unwrap config access p ctx in
- (* Check that there are no bottoms in the value *)
- assert (not (bottom_in_value ctx.ended_regions v));
- (* Check that there are no inactivated borrows in the value *)
- assert (not (inactivated_in_value v));
- (* Call the continuation *)
- cf v ctx
-
-(** Small utility.
-
- Prepare the access to a place in a right-value (typically an operand) by
- reorganizing the environment.
-
- We reorganize the environment so that:
- - we can access the place (we prepare *along* the path)
- - the value at the place itself doesn't contain loans (the [access_kind]
- controls whether we only end mutable loans, or also shared loans).
-
- We also check, after the reorganization, that the value at the place
- *doesn't contain any bottom nor inactivated borrows*.
-
- [expand_prim_copy]: if true, expand the symbolic values which are primitively
- copyable and contain borrows.
- *)
-let access_rplace_reorganize_and_read (config : C.config)
- (expand_prim_copy : bool) (access : access_kind) (p : E.place)
- (cf : V.typed_value -> m_fun) : m_fun =
- fun ctx ->
- (* Make sure we can evaluate the path *)
- let cc = update_ctx_along_read_place config access p in
- (* End the proper loans at the place itself *)
- let cc = comp cc (end_loans_at_place config access p) in
- (* Expand the copyable values which contain borrows (which are necessarily shared
- * borrows) *)
- let cc =
- if expand_prim_copy then
- comp cc (expand_primitively_copyable_at_place config access p)
- else cc
- in
- (* Read the place - note that this checks that the value doesn't contain bottoms *)
- let read_place = read_place config access p in
- (* Compose *)
- comp cc read_place cf ctx
-
-let access_rplace_reorganize (config : C.config) (expand_prim_copy : bool)
- (access : access_kind) (p : E.place) : cm_fun =
- fun cf ctx ->
- access_rplace_reorganize_and_read config expand_prim_copy access p
- (fun _v -> cf)
- ctx
-
-(** Convert an operand constant operand value to a typed value *)
-let constant_to_typed_value (ty : T.ety) (cv : V.constant_value) : V.typed_value
- =
- (* Check the type while converting - we actually need some information
- * contained in the type *)
- log#ldebug
- (lazy
- ("constant_to_typed_value:" ^ "\n- cv: " ^ PV.constant_value_to_string cv));
- match (ty, cv) with
- (* Scalar, boolean... *)
- | T.Bool, Bool v -> { V.value = V.Concrete (Bool v); ty }
- | T.Char, Char v -> { V.value = V.Concrete (Char v); ty }
- | T.Str, String v -> { V.value = V.Concrete (String v); ty }
- | T.Integer int_ty, V.Scalar v ->
- (* Check the type and the ranges *)
- assert (int_ty = v.int_ty);
- assert (check_scalar_value_in_range v);
- { V.value = V.Concrete (V.Scalar v); ty }
- (* Remaining cases (invalid) *)
- | _, _ -> failwith "Improperly typed constant value"
-
-(** Reorganize the environment in preparation for the evaluation of an operand.
-
- Evaluating an operand requires reorganizing the environment to get access
- to a given place (by ending borrows, expanding symbolic values...) then
- applying the operand operation (move, copy, etc.).
-
- Sometimes, we want to decouple the two operations.
- Consider the following example:
- {[
- context = {
- x -> shared_borrow l0
- y -> shared_loan {l0} v
- }
-
- dest <- f(move x, move y);
- ...
- ]}
- Because of the way [end_borrow] is implemented, when giving back the borrow
- [l0] upon evaluating [move y], we won't notice that [shared_borrow l0] has
- disappeared from the environment (it has been moved and not assigned yet,
- and so is hanging in "thin air").
-
- By first "preparing" the operands evaluation, we make sure no such thing
- happens. To be more precise, we make sure all the updates to borrows triggered
- by access *and* move operations have already been applied.
-
- Rk.: in the formalization, we always have an explicit "reorganization" step
- in the rule premises, before the actual operand evaluation.
-
- Rk.: doing this is actually not completely necessary because when
- generating MIR, rustc introduces intermediate assignments for all the function
- parameters. Still, it is better for soundness purposes, and corresponds to
- what we do in the formalization (because we don't enforce constraints
- in the formalization).
- *)
-let prepare_eval_operand_reorganize (config : C.config) (op : E.operand) :
- cm_fun =
- fun cf ctx ->
- let prepare : cm_fun =
- fun cf ctx ->
- match op with
- | Expressions.Constant (ty, cv) ->
- (* No need to reorganize the context *)
- constant_to_typed_value ty cv |> ignore;
- cf ctx
- | Expressions.Copy p ->
- (* Access the value *)
- let access = Read in
- (* Expand the symbolic values, if necessary *)
- let expand_prim_copy = true in
- access_rplace_reorganize config expand_prim_copy access p cf ctx
- | Expressions.Move p ->
- (* Access the value *)
- let access = Move in
- let expand_prim_copy = false in
- access_rplace_reorganize config expand_prim_copy access p cf ctx
- in
- (* Apply *)
- prepare cf ctx
-
-(** Evaluate an operand, without reorganizing the context before *)
-let eval_operand_no_reorganize (config : C.config) (op : E.operand)
- (cf : V.typed_value -> m_fun) : m_fun =
- fun ctx ->
- (* Debug *)
- log#ldebug
- (lazy
- ("eval_operand_no_reorganize: op: " ^ operand_to_string ctx op
- ^ "\n- ctx:\n" ^ eval_ctx_to_string ctx ^ "\n"));
- (* Evaluate *)
- match op with
- | Expressions.Constant (ty, cv) -> cf (constant_to_typed_value ty cv) ctx
- | Expressions.Copy p ->
- (* Access the value *)
- let access = Read in
- let cc = read_place config access p in
- (* Copy the value *)
- let copy cf v : m_fun =
- fun ctx ->
- (* Sanity checks *)
- assert (not (bottom_in_value ctx.ended_regions v));
- assert (
- Option.is_none
- (find_first_primitively_copyable_sv_with_borrows
- ctx.type_context.type_infos v));
- (* Actually perform the copy *)
- let allow_adt_copy = false in
- let ctx, v = copy_value allow_adt_copy config ctx v in
- (* Continue *)
- cf v ctx
- in
- (* Compose and apply *)
- comp cc copy cf ctx
- | Expressions.Move p ->
- (* Access the value *)
- let access = Move in
- let cc = read_place config access p in
- (* Move the value *)
- let move cf v : m_fun =
- fun ctx ->
- (* Check that there are no bottoms in the value we are about to move *)
- assert (not (bottom_in_value ctx.ended_regions v));
- let bottom : V.typed_value = { V.value = Bottom; ty = v.ty } in
- match write_place config access p bottom ctx with
- | Error _ -> failwith "Unreachable"
- | Ok ctx -> cf v ctx
- in
- (* Compose and apply *)
- comp cc move cf ctx
-
-(** Evaluate an operand.
-
- Reorganize the context, then evaluate the operand.
-
- **Warning**: this function shouldn't be used to evaluate a list of
- operands (for a function call, for instance): we must do *one* reorganization
- of the environment, before evaluating all the operands at once.
- Use [eval_operands] instead.
- *)
-let eval_operand (config : C.config) (op : E.operand)
- (cf : V.typed_value -> m_fun) : m_fun =
- fun ctx ->
- (* Debug *)
- log#ldebug
- (lazy
- ("eval_operand: op: " ^ operand_to_string ctx op ^ "\n- ctx:\n"
- ^ eval_ctx_to_string ctx ^ "\n"));
- (* We reorganize the context, then evaluate the operand *)
- comp
- (prepare_eval_operand_reorganize config op)
- (eval_operand_no_reorganize config op)
- cf ctx
-
-(** Small utility.
-
- See [prepare_eval_operand_reorganize].
- *)
-let prepare_eval_operands_reorganize (config : C.config) (ops : E.operand list)
- : cm_fun =
- fold_left_apply_continuation (prepare_eval_operand_reorganize config) ops
-
-(** Evaluate several operands. *)
-let eval_operands (config : C.config) (ops : E.operand list)
- (cf : V.typed_value list -> m_fun) : m_fun =
- fun ctx ->
- (* Prepare the operands *)
- let prepare = prepare_eval_operands_reorganize config ops in
- (* Evaluate the operands *)
- let eval =
- fold_left_list_apply_continuation (eval_operand_no_reorganize config) ops
- in
- (* Compose and apply *)
- comp prepare eval cf ctx
-
-let eval_two_operands (config : C.config) (op1 : E.operand) (op2 : E.operand)
- (cf : V.typed_value * V.typed_value -> m_fun) : m_fun =
- let eval_op = eval_operands config [ op1; op2 ] in
- let use_res cf res =
- match res with [ v1; v2 ] -> cf (v1, v2) | _ -> failwith "Unreachable"
- in
- comp eval_op use_res cf
-
-let eval_unary_op_concrete (config : C.config) (unop : E.unop) (op : E.operand)
- (cf : (V.typed_value, eval_error) result -> m_fun) : m_fun =
- (* Evaluate the operand *)
- let eval_op = eval_operand config op in
- (* Apply the unop *)
- let apply cf (v : V.typed_value) : m_fun =
- match (unop, v.V.value) with
- | E.Not, V.Concrete (Bool b) ->
- cf (Ok { v with V.value = V.Concrete (Bool (not b)) })
- | E.Neg, V.Concrete (V.Scalar sv) -> (
- let i = Z.neg sv.V.value in
- match mk_scalar sv.int_ty i with
- | Error _ -> cf (Error EPanic)
- | Ok sv -> cf (Ok { v with V.value = V.Concrete (V.Scalar sv) }))
- | E.Cast (src_ty, tgt_ty), V.Concrete (V.Scalar sv) -> (
- assert (src_ty == sv.int_ty);
- let i = sv.V.value in
- match mk_scalar tgt_ty i with
- | Error _ -> cf (Error EPanic)
- | Ok sv ->
- let ty = T.Integer tgt_ty in
- let value = V.Concrete (V.Scalar sv) in
- cf (Ok { V.ty; value }))
- | _ -> raise (Failure "Invalid input for unop")
- in
- comp eval_op apply cf
-
-let eval_unary_op_symbolic (config : C.config) (unop : E.unop) (op : E.operand)
- (cf : (V.typed_value, eval_error) result -> m_fun) : m_fun =
- fun ctx ->
- (* Evaluate the operand *)
- let eval_op = eval_operand config op in
- (* Generate a fresh symbolic value to store the result *)
- let apply cf (v : V.typed_value) : m_fun =
- fun ctx ->
- let res_sv_id = C.fresh_symbolic_value_id () in
- let res_sv_ty =
- match (unop, v.V.ty) with
- | E.Not, T.Bool -> T.Bool
- | E.Neg, T.Integer int_ty -> T.Integer int_ty
- | E.Cast (_, tgt_ty), _ -> T.Integer tgt_ty
- | _ -> raise (Failure "Invalid input for unop")
- in
- let res_sv =
- { V.sv_kind = V.FunCallRet; V.sv_id = res_sv_id; sv_ty = res_sv_ty }
- in
- (* Call the continuation *)
- let expr = cf (Ok (mk_typed_value_from_symbolic_value res_sv)) ctx in
- (* Synthesize the symbolic AST *)
- S.synthesize_unary_op unop v
- (S.mk_opt_place_from_op op ctx)
- res_sv None expr
- in
- (* Compose and apply *)
- comp eval_op apply cf ctx
-
-let eval_unary_op (config : C.config) (unop : E.unop) (op : E.operand)
- (cf : (V.typed_value, eval_error) result -> m_fun) : m_fun =
- match config.mode with
- | C.ConcreteMode -> eval_unary_op_concrete config unop op cf
- | C.SymbolicMode -> eval_unary_op_symbolic config unop op cf
-
-(** Small helper for [eval_binary_op_concrete]: computes the result of applying
- the binop *after* the operands have been successfully evaluated
- *)
-let eval_binary_op_concrete_compute (binop : E.binop) (v1 : V.typed_value)
- (v2 : V.typed_value) : (V.typed_value, eval_error) result =
- (* Equality check binops (Eq, Ne) accept values from a wide variety of types.
- * The remaining binops only operate on scalars. *)
- if binop = Eq || binop = Ne then (
- (* Equality operations *)
- assert (v1.ty = v2.ty);
- (* Equality/inequality check is primitive only for a subset of types *)
- assert (ty_is_primitively_copyable v1.ty);
- let b = v1 = v2 in
- Ok { V.value = V.Concrete (Bool b); ty = T.Bool })
- else
- (* For the non-equality operations, the input values are necessarily scalars *)
- match (v1.V.value, v2.V.value) with
- | V.Concrete (V.Scalar sv1), V.Concrete (V.Scalar sv2) -> (
- (* There are binops which require the two operands to have the same
- type, and binops for which it is not the case.
- There are also binops which return booleans, and binops which
- return integers.
- *)
- match binop with
- | E.Lt | E.Le | E.Ge | E.Gt ->
- (* The two operands must have the same type and the result is a boolean *)
- assert (sv1.int_ty = sv2.int_ty);
- let b =
- match binop with
- | E.Lt -> Z.lt sv1.V.value sv2.V.value
- | E.Le -> Z.leq sv1.V.value sv2.V.value
- | E.Ge -> Z.geq sv1.V.value sv2.V.value
- | E.Gt -> Z.gt sv1.V.value sv2.V.value
- | E.Div | E.Rem | E.Add | E.Sub | E.Mul | E.BitXor | E.BitAnd
- | E.BitOr | E.Shl | E.Shr | E.Ne | E.Eq ->
- raise (Failure "Unreachable")
- in
- Ok ({ V.value = V.Concrete (Bool b); ty = T.Bool } : V.typed_value)
- | E.Div | E.Rem | E.Add | E.Sub | E.Mul | E.BitXor | E.BitAnd | E.BitOr
- -> (
- (* The two operands must have the same type and the result is an integer *)
- assert (sv1.int_ty = sv2.int_ty);
- let res =
- match binop with
- | E.Div ->
- if sv2.V.value = Z.zero then Error ()
- else mk_scalar sv1.int_ty (Z.div sv1.V.value sv2.V.value)
- | E.Rem ->
- (* See [https://github.com/ocaml/Zarith/blob/master/z.mli] *)
- if sv2.V.value = Z.zero then Error ()
- else mk_scalar sv1.int_ty (Z.rem sv1.V.value sv2.V.value)
- | E.Add -> mk_scalar sv1.int_ty (Z.add sv1.V.value sv2.V.value)
- | E.Sub -> mk_scalar sv1.int_ty (Z.sub sv1.V.value sv2.V.value)
- | E.Mul -> mk_scalar sv1.int_ty (Z.mul sv1.V.value sv2.V.value)
- | E.BitXor -> raise Unimplemented
- | E.BitAnd -> raise Unimplemented
- | E.BitOr -> raise Unimplemented
- | E.Lt | E.Le | E.Ge | E.Gt | E.Shl | E.Shr | E.Ne | E.Eq ->
- raise (Failure "Unreachable")
- in
- match res with
- | Error _ -> Error EPanic
- | Ok sv ->
- Ok
- {
- V.value = V.Concrete (V.Scalar sv);
- ty = Integer sv1.int_ty;
- })
- | E.Shl | E.Shr -> raise Unimplemented
- | E.Ne | E.Eq -> raise (Failure "Unreachable"))
- | _ -> raise (Failure "Invalid inputs for binop")
-
-let eval_binary_op_concrete (config : C.config) (binop : E.binop)
- (op1 : E.operand) (op2 : E.operand)
- (cf : (V.typed_value, eval_error) result -> m_fun) : m_fun =
- (* Evaluate the operands *)
- let eval_ops = eval_two_operands config op1 op2 in
- (* Compute the result of the binop *)
- let compute cf (res : V.typed_value * V.typed_value) =
- let v1, v2 = res in
- cf (eval_binary_op_concrete_compute binop v1 v2)
- in
- (* Compose and apply *)
- comp eval_ops compute cf
-
-let eval_binary_op_symbolic (config : C.config) (binop : E.binop)
- (op1 : E.operand) (op2 : E.operand)
- (cf : (V.typed_value, eval_error) result -> m_fun) : m_fun =
- fun ctx ->
- (* Evaluate the operands *)
- let eval_ops = eval_two_operands config op1 op2 in
- (* Compute the result of applying the binop *)
- let compute cf ((v1, v2) : V.typed_value * V.typed_value) : m_fun =
- fun ctx ->
- (* Generate a fresh symbolic value to store the result *)
- let res_sv_id = C.fresh_symbolic_value_id () in
- let res_sv_ty =
- if binop = Eq || binop = Ne then (
- (* Equality operations *)
- assert (v1.ty = v2.ty);
- (* Equality/inequality check is primitive only for a subset of types *)
- assert (ty_is_primitively_copyable v1.ty);
- T.Bool)
- else
- (* Other operations: input types are integers *)
- match (v1.V.ty, v2.V.ty) with
- | T.Integer int_ty1, T.Integer int_ty2 -> (
- match binop with
- | E.Lt | E.Le | E.Ge | E.Gt ->
- assert (int_ty1 = int_ty2);
- T.Bool
- | E.Div | E.Rem | E.Add | E.Sub | E.Mul | E.BitXor | E.BitAnd
- | E.BitOr ->
- assert (int_ty1 = int_ty2);
- T.Integer int_ty1
- | E.Shl | E.Shr -> raise Unimplemented
- | E.Ne | E.Eq -> raise (Failure "Unreachable"))
- | _ -> raise (Failure "Invalid inputs for binop")
- in
- let res_sv =
- { V.sv_kind = V.FunCallRet; V.sv_id = res_sv_id; sv_ty = res_sv_ty }
- in
- (* Call the continuattion *)
- let v = mk_typed_value_from_symbolic_value res_sv in
- let expr = cf (Ok v) ctx in
- (* Synthesize the symbolic AST *)
- let p1 = S.mk_opt_place_from_op op1 ctx in
- let p2 = S.mk_opt_place_from_op op2 ctx in
- S.synthesize_binary_op binop v1 p1 v2 p2 res_sv None expr
- in
- (* Compose and apply *)
- comp eval_ops compute cf ctx
-
-let eval_binary_op (config : C.config) (binop : E.binop) (op1 : E.operand)
- (op2 : E.operand) (cf : (V.typed_value, eval_error) result -> m_fun) : m_fun
- =
- match config.mode with
- | C.ConcreteMode -> eval_binary_op_concrete config binop op1 op2 cf
- | C.SymbolicMode -> eval_binary_op_symbolic config binop op1 op2 cf
-
-(** Evaluate the discriminant of a concrete (i.e., non symbolic) ADT value *)
-let eval_rvalue_discriminant_concrete (config : C.config) (p : E.place)
- (cf : V.typed_value -> m_fun) : m_fun =
- (* Note that discriminant values have type [isize] *)
- (* Access the value *)
- let access = Read in
- let expand_prim_copy = false in
- let prepare =
- access_rplace_reorganize_and_read config expand_prim_copy access p
- in
- (* Read the value *)
- let read (cf : V.typed_value -> m_fun) (v : V.typed_value) : m_fun =
- (* The value may be shared: we need to ignore the shared loans *)
- let v = value_strip_shared_loans v in
- match v.V.value with
- | Adt av -> (
- match av.variant_id with
- | None ->
- raise
- (Failure
- "Invalid input for `discriminant`: structure instead of enum")
- | Some variant_id -> (
- let id = Z.of_int (T.VariantId.to_int variant_id) in
- match mk_scalar Isize id with
- | Error _ -> raise (Failure "Disciminant id out of range")
- (* Should really never happen *)
- | Ok sv ->
- cf { V.value = V.Concrete (V.Scalar sv); ty = Integer Isize }))
- | _ ->
- raise
- (Failure ("Invalid input for `discriminant`: " ^ V.show_typed_value v))
- in
- (* Compose and apply *)
- comp prepare read cf
-
-(** Evaluate the discriminant of an ADT value.
-
- Might lead to branching, if the value is symbolic.
- *)
-let eval_rvalue_discriminant (config : C.config) (p : E.place)
- (cf : V.typed_value -> m_fun) : m_fun =
- fun ctx ->
- log#ldebug (lazy "eval_rvalue_discriminant");
- (* Note that discriminant values have type [isize] *)
- (* Access the value *)
- let access = Read in
- let expand_prim_copy = false in
- let prepare =
- access_rplace_reorganize_and_read config expand_prim_copy access p
- in
- (* Read the value *)
- let read (cf : V.typed_value -> m_fun) (v : V.typed_value) : m_fun =
- fun ctx ->
- (* The value may be shared: we need to ignore the shared loans *)
- let v = value_strip_shared_loans v in
- match v.V.value with
- | Adt _ -> eval_rvalue_discriminant_concrete config p cf ctx
- | Symbolic sv ->
- (* Expand the symbolic value - may lead to branching *)
- let allow_branching = true in
- let cc =
- expand_symbolic_value config allow_branching sv
- (Some (S.mk_mplace p ctx))
- in
- (* This time the value is concrete: reevaluate *)
- comp cc (eval_rvalue_discriminant_concrete config p) cf ctx
- | _ ->
- raise
- (Failure ("Invalid input for `discriminant`: " ^ V.show_typed_value v))
- in
- (* Compose and apply *)
- comp prepare read cf ctx
-
-let eval_rvalue_ref (config : C.config) (p : E.place) (bkind : E.borrow_kind)
- (cf : V.typed_value -> m_fun) : m_fun =
- fun ctx ->
- match bkind with
- | E.Shared | E.TwoPhaseMut ->
- (* Access the value *)
- let access = if bkind = E.Shared then Read else Write in
- let expand_prim_copy = false in
- let prepare =
- access_rplace_reorganize_and_read config expand_prim_copy access p
- in
- (* Evaluate the borrowing operation *)
- let eval (cf : V.typed_value -> m_fun) (v : V.typed_value) : m_fun =
- fun ctx ->
- (* Generate the fresh borrow id *)
- let bid = C.fresh_borrow_id () in
- (* Compute the loan value, with which to replace the value at place p *)
- let nv, shared_mvalue =
- match v.V.value with
- | V.Loan (V.SharedLoan (bids, sv)) ->
- (* Shared loan: insert the new borrow id *)
- let bids1 = V.BorrowId.Set.add bid bids in
- ({ v with V.value = V.Loan (V.SharedLoan (bids1, sv)) }, sv)
- | _ ->
- (* Not a shared loan: add a wrapper *)
- let v' =
- V.Loan (V.SharedLoan (V.BorrowId.Set.singleton bid, v))
- in
- ({ v with V.value = v' }, v)
- in
- (* Update the borrowed value in the context *)
- let ctx = write_place_unwrap config access p nv ctx in
- (* Compute the rvalue - simply a shared borrow with a the fresh id.
- * Note that the reference is *mutable* if we do a two-phase borrow *)
- let rv_ty =
- T.Ref (T.Erased, v.ty, if bkind = E.Shared then Shared else Mut)
- in
- let bc =
- if bkind = E.Shared then V.SharedBorrow (shared_mvalue, bid)
- else V.InactivatedMutBorrow (shared_mvalue, bid)
- in
- let rv : V.typed_value = { V.value = V.Borrow bc; ty = rv_ty } in
- (* Continue *)
- cf rv ctx
- in
- (* Compose and apply *)
- comp prepare eval cf ctx
- | E.Mut ->
- (* Access the value *)
- let access = Write in
- let expand_prim_copy = false in
- let prepare =
- access_rplace_reorganize_and_read config expand_prim_copy access p
- in
- (* Evaluate the borrowing operation *)
- let eval (cf : V.typed_value -> m_fun) (v : V.typed_value) : m_fun =
- fun ctx ->
- (* Compute the rvalue - wrap the value in a mutable borrow with a fresh id *)
- let bid = C.fresh_borrow_id () in
- let rv_ty = T.Ref (T.Erased, v.ty, Mut) in
- let rv : V.typed_value =
- { V.value = V.Borrow (V.MutBorrow (bid, v)); ty = rv_ty }
- in
- (* Compute the value with which to replace the value at place p *)
- let nv = { v with V.value = V.Loan (V.MutLoan bid) } in
- (* Update the value in the context *)
- let ctx = write_place_unwrap config access p nv ctx in
- (* Continue *)
- cf rv ctx
- in
- (* Compose and apply *)
- comp prepare eval cf ctx
-
-let eval_rvalue_aggregate (config : C.config)
- (aggregate_kind : E.aggregate_kind) (ops : E.operand list)
- (cf : V.typed_value -> m_fun) : m_fun =
- (* Evaluate the operands *)
- let eval_ops = eval_operands config ops in
- (* Compute the value *)
- let compute (cf : V.typed_value -> m_fun) (values : V.typed_value list) :
- m_fun =
- fun ctx ->
- (* Match on the aggregate kind *)
- match aggregate_kind with
- | E.AggregatedTuple ->
- let tys = List.map (fun (v : V.typed_value) -> v.V.ty) values in
- let v = V.Adt { variant_id = None; field_values = values } in
- let ty = T.Adt (T.Tuple, [], tys) in
- let aggregated : V.typed_value = { V.value = v; ty } in
- (* Call the continuation *)
- cf aggregated ctx
- | E.AggregatedOption (variant_id, ty) ->
- (* Sanity check *)
- if variant_id == T.option_none_id then assert (values == [])
- else if variant_id == T.option_some_id then
- assert (List.length values == 1)
- else raise (Failure "Unreachable");
- (* Construt the value *)
- let aty = T.Adt (T.Assumed T.Option, [], [ ty ]) in
- let av : V.adt_value =
- { V.variant_id = Some variant_id; V.field_values = values }
- in
- let aggregated : V.typed_value = { V.value = Adt av; ty = aty } in
- (* Call the continuation *)
- cf aggregated ctx
- | E.AggregatedAdt (def_id, opt_variant_id, regions, types) ->
- (* Sanity checks *)
- let type_decl = C.ctx_lookup_type_decl ctx def_id in
- assert (List.length type_decl.region_params = List.length regions);
- let expected_field_types =
- Subst.ctx_adt_get_instantiated_field_etypes ctx def_id opt_variant_id
- types
- in
- assert (
- expected_field_types
- = List.map (fun (v : V.typed_value) -> v.V.ty) values);
- (* Construct the value *)
- let av : V.adt_value =
- { V.variant_id = opt_variant_id; V.field_values = values }
- in
- let aty = T.Adt (T.AdtId def_id, regions, types) in
- let aggregated : V.typed_value = { V.value = Adt av; ty = aty } in
- (* Call the continuation *)
- cf aggregated ctx
- in
- (* Compose and apply *)
- comp eval_ops compute cf
-
-(** Evaluate an rvalue.
-
- Transmits the computed rvalue to the received continuation.
- *)
-let eval_rvalue (config : C.config) (rvalue : E.rvalue)
- (cf : (V.typed_value, eval_error) result -> m_fun) : m_fun =
- fun ctx ->
- log#ldebug (lazy "eval_rvalue");
- (* Small helpers *)
- let wrap_in_result (cf : (V.typed_value, eval_error) result -> m_fun)
- (v : V.typed_value) : m_fun =
- cf (Ok v)
- in
- let comp_wrap f = comp f wrap_in_result cf in
- (* Delegate to the proper auxiliary function *)
- match rvalue with
- | E.Use op -> comp_wrap (eval_operand config op) ctx
- | E.Ref (p, bkind) -> comp_wrap (eval_rvalue_ref config p bkind) ctx
- | E.UnaryOp (unop, op) -> eval_unary_op config unop op cf ctx
- | E.BinaryOp (binop, op1, op2) -> eval_binary_op config binop op1 op2 cf ctx
- | E.Aggregate (aggregate_kind, ops) ->
- comp_wrap (eval_rvalue_aggregate config aggregate_kind ops) ctx
- | E.Discriminant p -> comp_wrap (eval_rvalue_discriminant config p) ctx
diff --git a/src/InterpreterPaths.ml b/src/InterpreterPaths.ml
deleted file mode 100644
index d54a046e..00000000
--- a/src/InterpreterPaths.ml
+++ /dev/null
@@ -1,801 +0,0 @@
-module T = Types
-module V = Values
-module E = Expressions
-module C = Contexts
-module Subst = Substitute
-module L = Logging
-open Cps
-open TypesUtils
-open ValuesUtils
-open InterpreterUtils
-open InterpreterBorrowsCore
-open InterpreterBorrows
-open InterpreterExpansion
-module Synth = SynthesizeSymbolic
-
-(** The local logger *)
-let log = L.paths_log
-
-(** Paths *)
-
-(** When we fail reading from or writing to a path, it might be because we
- need to update the environment by ending borrows, expanding symbolic
- values, etc. The following type is used to convey this information.
-
- TODO: compare with borrow_lres?
-*)
-type path_fail_kind =
- | FailSharedLoan of V.BorrowId.Set.t
- (** Failure because we couldn't go inside a shared loan *)
- | FailMutLoan of V.BorrowId.id
- (** Failure because we couldn't go inside a mutable loan *)
- | FailInactivatedMutBorrow of V.BorrowId.id
- (** Failure because we couldn't go inside an inactivated mutable borrow
- (which should get activated) *)
- | FailSymbolic of int * V.symbolic_value
- (** Failure because we need to enter a symbolic value (and thus need to
- expand it).
- We return the number of elements which remained in the path when we
- reached the error - this allows to retrieve the path prefix, which
- is useful for the synthesis. *)
- | FailBottom of int * E.projection_elem * T.ety
- (** Failure because we need to enter an any value - we can expand Bottom
- values if they are left values. We return the number of elements which
- remained in the path when we reached the error - this allows to
- properly update the Bottom value, if needs be.
- *)
- | FailBorrow of V.borrow_content
- (** We got stuck because we couldn't enter a borrow *)
-
-(** Result of evaluating a path (reading from a path/writing to a path)
-
- Note that when we fail, we return information used to update the
- environment, as well as the
-*)
-type 'a path_access_result = ('a, path_fail_kind) result
-(** The result of reading from/writing to a place *)
-
-type updated_read_value = { read : V.typed_value; updated : V.typed_value }
-
-type projection_access = {
- enter_shared_loans : bool;
- enter_mut_borrows : bool;
- lookup_shared_borrows : bool;
-}
-
-(** Generic function to access (read/write) the value at the end of a projection.
-
- We return the (eventually) updated value, the value we read at the end of
- the place and the (eventually) updated environment.
-
- TODO: use exceptions?
- *)
-let rec access_projection (access : projection_access) (ctx : C.eval_ctx)
- (* Function to (eventually) update the value we find *)
- (update : V.typed_value -> V.typed_value) (p : E.projection)
- (v : V.typed_value) : (C.eval_ctx * updated_read_value) path_access_result =
- (* For looking up/updating shared loans *)
- let ek : exploration_kind =
- { enter_shared_loans = true; enter_mut_borrows = true; enter_abs = true }
- in
- match p with
- | [] ->
- let nv = update v in
- (* Type checking *)
- if nv.ty <> v.ty then (
- log#lerror
- (lazy
- ("Not the same type:\n- nv.ty: " ^ T.show_ety nv.ty ^ "\n- v.ty: "
- ^ T.show_ety v.ty));
- failwith
- "Assertion failed: new value doesn't have the same type as its \
- destination");
- Ok (ctx, { read = v; updated = nv })
- | pe :: p' -> (
- (* Match on the projection element and the value *)
- match (pe, v.V.value, v.V.ty) with
- | ( Field (((ProjAdt (_, _) | ProjOption _) as proj_kind), field_id),
- V.Adt adt,
- T.Adt (type_id, _, _) ) -> (
- (* Check consistency *)
- (match (proj_kind, type_id) with
- | ProjAdt (def_id, opt_variant_id), T.AdtId def_id' ->
- assert (def_id = def_id');
- assert (opt_variant_id = adt.variant_id)
- | ProjOption variant_id, T.Assumed T.Option ->
- assert (Some variant_id = adt.variant_id)
- | _ -> failwith "Unreachable");
- (* Actually project *)
- let fv = T.FieldId.nth adt.field_values field_id in
- match access_projection access ctx update p' fv with
- | Error err -> Error err
- | Ok (ctx, res) ->
- (* Update the field value *)
- let nvalues =
- T.FieldId.update_nth adt.field_values field_id res.updated
- in
- let nadt = V.Adt { adt with V.field_values = nvalues } in
- let updated = { v with value = nadt } in
- Ok (ctx, { res with updated }))
- (* Tuples *)
- | Field (ProjTuple arity, field_id), V.Adt adt, T.Adt (T.Tuple, _, _) -> (
- assert (arity = List.length adt.field_values);
- let fv = T.FieldId.nth adt.field_values field_id in
- (* Project *)
- match access_projection access ctx update p' fv with
- | Error err -> Error err
- | Ok (ctx, res) ->
- (* Update the field value *)
- let nvalues =
- T.FieldId.update_nth adt.field_values field_id res.updated
- in
- let ntuple = V.Adt { adt with field_values = nvalues } in
- let updated = { v with value = ntuple } in
- Ok (ctx, { res with updated })
- (* If we reach Bottom, it may mean we need to expand an uninitialized
- * enumeration value *))
- | Field ((ProjAdt (_, _) | ProjTuple _ | ProjOption _), _), V.Bottom, _ ->
- Error (FailBottom (1 + List.length p', pe, v.ty))
- (* Symbolic value: needs to be expanded *)
- | _, Symbolic sp, _ ->
- (* Expand the symbolic value *)
- Error (FailSymbolic (1 + List.length p', sp))
- (* Box dereferencement *)
- | ( DerefBox,
- Adt { variant_id = None; field_values = [ bv ] },
- T.Adt (T.Assumed T.Box, _, _) ) -> (
- (* We allow moving inside of boxes. In practice, this kind of
- * manipulations should happen only inside unsage code, so
- * it shouldn't happen due to user code, and we leverage it
- * when implementing box dereferencement for the concrete
- * interpreter *)
- match access_projection access ctx update p' bv with
- | Error err -> Error err
- | Ok (ctx, res) ->
- let nv =
- {
- v with
- value =
- V.Adt { variant_id = None; field_values = [ res.updated ] };
- }
- in
- Ok (ctx, { res with updated = nv }))
- (* Borrows *)
- | Deref, V.Borrow bc, _ -> (
- match bc with
- | V.SharedBorrow (_, bid) ->
- (* Lookup the loan content, and explore from there *)
- if access.lookup_shared_borrows then
- match lookup_loan ek bid ctx with
- | _, Concrete (V.MutLoan _) -> failwith "Expected a shared loan"
- | _, Concrete (V.SharedLoan (bids, sv)) -> (
- (* Explore the shared value *)
- match access_projection access ctx update p' sv with
- | Error err -> Error err
- | Ok (ctx, res) ->
- (* Update the shared loan with the new value returned
- by {!access_projection} *)
- let ctx =
- update_loan ek bid
- (V.SharedLoan (bids, res.updated))
- ctx
- in
- (* Return - note that we don't need to update the borrow itself *)
- Ok (ctx, { res with updated = v }))
- | ( _,
- Abstract
- ( V.AMutLoan (_, _)
- | V.AEndedMutLoan
- { given_back = _; child = _; given_back_meta = _ }
- | V.AEndedSharedLoan (_, _)
- | V.AIgnoredMutLoan (_, _)
- | V.AEndedIgnoredMutLoan
- { given_back = _; child = _; given_back_meta = _ }
- | V.AIgnoredSharedLoan _ ) ) ->
- failwith "Expected a shared (abstraction) loan"
- | _, Abstract (V.ASharedLoan (bids, sv, _av)) -> (
- (* Explore the shared value *)
- match access_projection access ctx update p' sv with
- | Error err -> Error err
- | Ok (ctx, res) ->
- (* Relookup the child avalue *)
- let av =
- match lookup_loan ek bid ctx with
- | _, Abstract (V.ASharedLoan (_, _, av)) -> av
- | _ -> failwith "Unexpected"
- in
- (* Update the shared loan with the new value returned
- by {!access_projection} *)
- let ctx =
- update_aloan ek bid
- (V.ASharedLoan (bids, res.updated, av))
- ctx
- in
- (* Return - note that we don't need to update the borrow itself *)
- Ok (ctx, { res with updated = v }))
- else Error (FailBorrow bc)
- | V.InactivatedMutBorrow (_, bid) ->
- Error (FailInactivatedMutBorrow bid)
- | V.MutBorrow (bid, bv) ->
- if access.enter_mut_borrows then
- match access_projection access ctx update p' bv with
- | Error err -> Error err
- | Ok (ctx, res) ->
- let nv =
- {
- v with
- value = V.Borrow (V.MutBorrow (bid, res.updated));
- }
- in
- Ok (ctx, { res with updated = nv })
- else Error (FailBorrow bc))
- | _, V.Loan lc, _ -> (
- match lc with
- | V.MutLoan bid -> Error (FailMutLoan bid)
- | V.SharedLoan (bids, sv) ->
- (* If we can enter shared loan, we ignore the loan. Pay attention
- to the fact that we need to reexplore the *whole* place (i.e,
- we mustn't ignore the current projection element *)
- if access.enter_shared_loans then
- match access_projection access ctx update (pe :: p') sv with
- | Error err -> Error err
- | Ok (ctx, res) ->
- let nv =
- {
- v with
- value = V.Loan (V.SharedLoan (bids, res.updated));
- }
- in
- Ok (ctx, { res with updated = nv })
- else Error (FailSharedLoan bids))
- | (_, (V.Concrete _ | V.Adt _ | V.Bottom | V.Borrow _), _) as r ->
- let pe, v, ty = r in
- let pe = "- pe: " ^ E.show_projection_elem pe in
- let v = "- v:\n" ^ V.show_value v in
- let ty = "- ty:\n" ^ T.show_ety ty in
- log#serror ("Inconsistent projection:\n" ^ pe ^ "\n" ^ v ^ "\n" ^ ty);
- failwith "Inconsistent projection")
-
-(** Generic function to access (read/write) the value at a given place.
-
- We return the value we read at the place and the (eventually) updated
- environment, if we managed to access the place, or the precise reason
- why we failed.
- *)
-let access_place (access : projection_access)
- (* Function to (eventually) update the value we find *)
- (update : V.typed_value -> V.typed_value) (p : E.place) (ctx : C.eval_ctx)
- : (C.eval_ctx * V.typed_value) path_access_result =
- (* Lookup the variable's value *)
- let value = C.ctx_lookup_var_value ctx p.var_id in
- (* Apply the projection *)
- match access_projection access ctx update p.projection value with
- | Error err -> Error err
- | Ok (ctx, res) ->
- (* Update the value *)
- let ctx = C.ctx_update_var_value ctx p.var_id res.updated in
- (* Return *)
- Ok (ctx, res.read)
-
-type access_kind =
- | Read (** We can go inside borrows and loans *)
- | Write (** Don't enter shared borrows or shared loans *)
- | Move (** Don't enter borrows or loans *)
-
-let access_kind_to_projection_access (access : access_kind) : projection_access
- =
- match access with
- | Read ->
- {
- enter_shared_loans = true;
- enter_mut_borrows = true;
- lookup_shared_borrows = true;
- }
- | Write ->
- {
- enter_shared_loans = false;
- enter_mut_borrows = true;
- lookup_shared_borrows = false;
- }
- | Move ->
- {
- enter_shared_loans = false;
- enter_mut_borrows = false;
- lookup_shared_borrows = false;
- }
-
-(** Read the value at a given place.
-
- Note that we only access the value at the place, and do not check that
- the value is "well-formed" (for instance that it doesn't contain bottoms).
- *)
-let read_place (config : C.config) (access : access_kind) (p : E.place)
- (ctx : C.eval_ctx) : V.typed_value path_access_result =
- let access = access_kind_to_projection_access access in
- (* The update function is the identity *)
- let update v = v in
- match access_place access update p ctx with
- | Error err -> Error err
- | Ok (ctx1, read_value) ->
- (* Note that we ignore the new environment: it should be the same as the
- original one.
- *)
- if config.check_invariants then
- if ctx1 <> ctx then (
- let msg =
- "Unexpected environment update:\nNew environment:\n"
- ^ C.show_env ctx1.env ^ "\n\nOld environment:\n"
- ^ C.show_env ctx.env
- in
- log#serror msg;
- failwith "Unexpected environment update");
- Ok read_value
-
-let read_place_unwrap (config : C.config) (access : access_kind) (p : E.place)
- (ctx : C.eval_ctx) : V.typed_value =
- match read_place config access p ctx with
- | Error _ -> failwith "Unreachable"
- | Ok v -> v
-
-(** Update the value at a given place *)
-let write_place (_config : C.config) (access : access_kind) (p : E.place)
- (nv : V.typed_value) (ctx : C.eval_ctx) : C.eval_ctx path_access_result =
- let access = access_kind_to_projection_access access in
- (* The update function substitutes the value with the new value *)
- let update _ = nv in
- match access_place access update p ctx with
- | Error err -> Error err
- | Ok (ctx, _) ->
- (* We ignore the read value *)
- Ok ctx
-
-let write_place_unwrap (config : C.config) (access : access_kind) (p : E.place)
- (nv : V.typed_value) (ctx : C.eval_ctx) : C.eval_ctx =
- match write_place config access p nv ctx with
- | Error _ -> failwith "Unreachable"
- | Ok ctx -> ctx
-
-(** Compute an expanded ADT bottom value *)
-let compute_expanded_bottom_adt_value (tyctx : T.type_decl T.TypeDeclId.Map.t)
- (def_id : T.TypeDeclId.id) (opt_variant_id : T.VariantId.id option)
- (regions : T.erased_region list) (types : T.ety list) : V.typed_value =
- (* Lookup the definition and check if it is an enumeration - it
- should be an enumeration if and only if the projection element
- is a field projection with *some* variant id. Retrieve the list
- of fields at the same time. *)
- let def = T.TypeDeclId.Map.find def_id tyctx in
- assert (List.length regions = List.length def.T.region_params);
- (* Compute the field types *)
- let field_types =
- Subst.type_decl_get_instantiated_field_etypes def opt_variant_id types
- in
- (* Initialize the expanded value *)
- let fields = List.map mk_bottom field_types in
- let av = V.Adt { variant_id = opt_variant_id; field_values = fields } in
- let ty = T.Adt (T.AdtId def_id, regions, types) in
- { V.value = av; V.ty }
-
-(** Compute an expanded Option bottom value *)
-let compute_expanded_bottom_option_value (variant_id : T.VariantId.id)
- (param_ty : T.ety) : V.typed_value =
- (* Note that the variant can be [Some] or [None]: we expand bottom values
- * when writing to fields or setting discriminants *)
- let field_values =
- if variant_id = T.option_some_id then [ mk_bottom param_ty ]
- else if variant_id = T.option_none_id then []
- else raise (Failure "Unreachable")
- in
- let av = V.Adt { variant_id = Some variant_id; field_values } in
- let ty = T.Adt (T.Assumed T.Option, [], [ param_ty ]) in
- { V.value = av; ty }
-
-(** Compute an expanded tuple bottom value *)
-let compute_expanded_bottom_tuple_value (field_types : T.ety list) :
- V.typed_value =
- (* Generate the field values *)
- let fields = List.map mk_bottom field_types in
- let v = V.Adt { variant_id = None; field_values = fields } in
- let ty = T.Adt (T.Tuple, [], field_types) in
- { V.value = v; V.ty }
-
-(** Auxiliary helper to expand {!V.Bottom} values.
-
- During compilation, rustc desaggregates the ADT initializations. The
- consequence is that the following rust code:
- {[
- let x = Cons a b;
- ]}
-
- Looks like this in MIR:
- {[
- (x as Cons).0 = a;
- (x as Cons).1 = b;
- set_discriminant(x, 0); // If [Cons] is the variant of index 0
- ]}
-
- The consequence is that we may sometimes need to write fields to values
- which are currently {!V.Bottom}. When doing this, we first expand the value
- to, say, [Cons Bottom Bottom] (note that field projection contains information
- about which variant we should project to, which is why we *can* set the
- variant index when writing one of its fields).
-*)
-let expand_bottom_value_from_projection (config : C.config)
- (access : access_kind) (p : E.place) (remaining_pes : int)
- (pe : E.projection_elem) (ty : T.ety) (ctx : C.eval_ctx) : C.eval_ctx =
- (* Debugging *)
- log#ldebug
- (lazy
- ("expand_bottom_value_from_projection:\n" ^ "pe: "
- ^ E.show_projection_elem pe ^ "\n" ^ "ty: " ^ T.show_ety ty));
- (* Prepare the update: we need to take the proper prefix of the place
- during whose evaluation we got stuck *)
- let projection' =
- fst
- (Collections.List.split_at p.projection
- (List.length p.projection - remaining_pes))
- in
- let p' = { p with projection = projection' } in
- (* Compute the expanded value.
- The type of the {!V.Bottom} value should be a tuple or an ADT.
- Note that the projection element we got stuck at should be a
- field projection, and gives the variant id if the {!V.Bottom} value
- is an enumeration value.
- Also, the expanded value should be the proper ADT variant or a tuple
- with the proper arity, with all the fields initialized to {!V.Bottom}
- *)
- let nv =
- match (pe, ty) with
- (* "Regular" ADTs *)
- | ( Field (ProjAdt (def_id, opt_variant_id), _),
- T.Adt (T.AdtId def_id', regions, types) ) ->
- assert (def_id = def_id');
- compute_expanded_bottom_adt_value ctx.type_context.type_decls def_id
- opt_variant_id regions types
- (* Option *)
- | Field (ProjOption variant_id, _), T.Adt (T.Assumed T.Option, [], [ ty ])
- ->
- compute_expanded_bottom_option_value variant_id ty
- (* Tuples *)
- | Field (ProjTuple arity, _), T.Adt (T.Tuple, [], tys) ->
- assert (arity = List.length tys);
- (* Generate the field values *)
- compute_expanded_bottom_tuple_value tys
- | _ ->
- failwith
- ("Unreachable: " ^ E.show_projection_elem pe ^ ", " ^ T.show_ety ty)
- in
- (* Update the context by inserting the expanded value at the proper place *)
- match write_place config access p' nv ctx with
- | Ok ctx -> ctx
- | Error _ -> failwith "Unreachable"
-
-(** Update the environment to be able to read a place.
-
- When reading a place, we may be stuck along the way because some value
- is borrowed, we reach a symbolic value, etc. In this situation [read_place]
- fails while returning precise information about the failure. This function
- uses this information to update the environment (by ending borrows,
- expanding symbolic values) until we manage to fully read the place.
- *)
-let rec update_ctx_along_read_place (config : C.config) (access : access_kind)
- (p : E.place) : cm_fun =
- fun cf ctx ->
- (* Attempt to read the place: if it fails, update the environment and retry *)
- match read_place config access p ctx with
- | Ok _ -> cf ctx
- | Error err ->
- let cc =
- match err with
- | FailSharedLoan bids -> end_outer_borrows config bids
- | FailMutLoan bid -> end_outer_borrow config bid
- | FailInactivatedMutBorrow bid ->
- activate_inactivated_mut_borrow config bid
- | FailSymbolic (i, sp) ->
- (* Expand the symbolic value *)
- let proj, _ =
- Collections.List.split_at p.projection
- (List.length p.projection - i)
- in
- let prefix = { p with projection = proj } in
- expand_symbolic_value_no_branching config sp
- (Some (Synth.mk_mplace prefix ctx))
- | FailBottom (_, _, _) ->
- (* We can't expand {!V.Bottom} values while reading them *)
- failwith "Found [Bottom] while reading a place"
- | FailBorrow _ -> failwith "Could not read a borrow"
- in
- comp cc (update_ctx_along_read_place config access p) cf ctx
-
-(** Update the environment to be able to write to a place.
-
- See {!update_ctx_along_read_place}.
-*)
-let rec update_ctx_along_write_place (config : C.config) (access : access_kind)
- (p : E.place) : cm_fun =
- fun cf ctx ->
- (* Attempt to *read* (yes, *read*: we check the access to the place, and
- write to it later) the place: if it fails, update the environment and retry *)
- match read_place config access p ctx with
- | Ok _ -> cf ctx
- | Error err ->
- (* Update the context *)
- let cc =
- match err with
- | FailSharedLoan bids -> end_outer_borrows config bids
- | FailMutLoan bid -> end_outer_borrow config bid
- | FailInactivatedMutBorrow bid ->
- activate_inactivated_mut_borrow config bid
- | FailSymbolic (_pe, sp) ->
- (* Expand the symbolic value *)
- expand_symbolic_value_no_branching config sp
- (Some (Synth.mk_mplace p ctx))
- | FailBottom (remaining_pes, pe, ty) ->
- (* Expand the {!V.Bottom} value *)
- fun cf ctx ->
- let ctx =
- expand_bottom_value_from_projection config access p remaining_pes
- pe ty ctx
- in
- cf ctx
- | FailBorrow _ -> failwith "Could not write to a borrow"
- in
- (* Retry *)
- comp cc (update_ctx_along_write_place config access p) cf ctx
-
-(** Small utility used to break control-flow *)
-exception UpdateCtx of cm_fun
-
-(** End the loans at a given place: read the value, if it contains a loan,
- end this loan, repeat.
-
- This is used when reading or borrowing values. We typically
- first call {!update_ctx_along_read_place} or {!update_ctx_along_write_place}
- to get access to the value, then call this function to "prepare" the value:
- when moving values, we can't move a value which contains loans and thus need
- to end them, etc.
- *)
-let rec end_loans_at_place (config : C.config) (access : access_kind)
- (p : E.place) : cm_fun =
- fun cf ctx ->
- (* Iterator to explore a value and update the context whenever we find
- * loans.
- * We use exceptions to make it handy: whenever we update the
- * context, we raise an exception wrapping the updated context.
- * *)
- let obj =
- object
- inherit [_] V.iter_typed_value as super
-
- method! visit_borrow_content env bc =
- match bc with
- | V.SharedBorrow _ | V.MutBorrow (_, _) ->
- (* Nothing special to do *) super#visit_borrow_content env bc
- | V.InactivatedMutBorrow (_, bid) ->
- (* We need to activate inactivated borrows *)
- let cc = activate_inactivated_mut_borrow config bid in
- raise (UpdateCtx cc)
-
- method! visit_loan_content env lc =
- match lc with
- | V.SharedLoan (bids, v) -> (
- (* End the loans if we need a modification access, otherwise dive into
- the shared value *)
- match access with
- | Read -> super#visit_SharedLoan env bids v
- | Write | Move ->
- let cc = end_outer_borrows config bids in
- raise (UpdateCtx cc))
- | V.MutLoan bid ->
- (* We always need to end mutable borrows *)
- let cc = end_outer_borrow config bid in
- raise (UpdateCtx cc)
- end
- in
-
- (* First, retrieve the value *)
- match read_place config access p ctx with
- | Error _ -> failwith "Unreachable"
- | Ok v -> (
- (* Inspect the value and update the context while doing so.
- If the context gets updated: perform a recursive call (many things
- may have been updated in the context: we need to re-read the value
- at place [p] - and this value may actually not be accessible
- anymore...)
- *)
- try
- obj#visit_typed_value () v;
- (* No context update required: apply the continuation *)
- cf ctx
- with UpdateCtx cc ->
- (* We need to update the context: compose the caugth continuation with
- * a recursive call to reinspect the value *)
- comp cc (end_loans_at_place config access p) cf ctx)
-
-(** Drop (end) outer loans and borrows at a given place, which should be
- seen as an l-value (we will write to it later, but need to drop
- the borrows before writing).
-
- This is used to drop values when evaluating the drop statement or before
- writing to a place.
-
- [end_borrows]:
- - if true: end all the loans and borrows we find, starting with the outer
- ones. This is used when evaluating the [drop] statement (see [drop_value])
- - if false: only end the outer loans. This is used by [assign_to_place]
- or to drop the loans in the local variables when popping a frame.
-
- Note that we don't do what is defined in the formalization: we move the
- value to a temporary dummy value, then explore this value and end the
- loans/borrows inside as long as we find some, starting with the outer
- ones, then move the resulting value back to where it was. This shouldn't
- make any difference, really (note that the place is *inside* a borrow,
- if we end the borrow, we won't be able to reinsert the value back).
- *)
-let drop_outer_borrows_loans_at_lplace (config : C.config) (end_borrows : bool)
- (p : E.place) : cm_fun =
- fun cf ctx ->
- (* Move the current value in the place outside of this place and into
- * a dummy variable *)
- let access = Write in
- let v = read_place_unwrap config access p ctx in
- let ctx = write_place_unwrap config access p (mk_bottom v.V.ty) ctx in
- let ctx = C.ctx_push_dummy_var ctx v in
- (* Auxiliary function *)
- let rec drop : cm_fun =
- fun cf ctx ->
- (* Read the value *)
- let v = C.ctx_read_first_dummy_var ctx in
- (* Check if there are loans or borrows to end *)
- match get_first_outer_loan_or_borrow_in_value end_borrows v with
- | None ->
- (* We are done: simply call the continuation *)
- cf ctx
- | Some c ->
- (* There are: end them then retry *)
- let cc =
- match c with
- | LoanContent (V.SharedLoan (bids, _)) ->
- end_outer_borrows config bids
- | LoanContent (V.MutLoan bid)
- | BorrowContent (V.MutBorrow (bid, _) | SharedBorrow (_, bid)) ->
- end_outer_borrow config bid
- | BorrowContent (V.InactivatedMutBorrow (_, bid)) ->
- (* First activate the borrow *)
- activate_inactivated_mut_borrow config bid
- in
- (* Retry *)
- comp cc drop cf ctx
- in
- (* Apply the drop function *)
- let cc = drop in
- (* Pop the temporary value and reinsert it *)
- let cc =
- comp cc (fun cf ctx ->
- (* Pop *)
- let ctx, v = C.ctx_pop_dummy_var ctx in
- (* Reinsert *)
- let ctx = write_place_unwrap config access p v ctx in
- (* Sanity check *)
- if end_borrows then (
- assert (not (loans_in_value v));
- assert (not (borrows_in_value v)))
- else assert (not (outer_loans_in_value v));
- (* Continue *)
- cf ctx)
- in
- (* Continue *)
- cc cf ctx
-
-(** Copy a value, and return the resulting value.
-
- Note that copying values might update the context. For instance, when
- copying shared borrows, we need to insert new shared borrows in the context.
-
- Also, this function is actually more general than it should be: it can be used
- to copy concrete ADT values, while ADT copy should be done through the Copy
- trait (i.e., by calling a dedicated function). This is why we added a parameter
- to control this copy. Note that here by ADT we mean the user-defined ADTs
- (not tuples or assumed types).
-
- TODO: move
- *)
-let rec copy_value (allow_adt_copy : bool) (config : C.config)
- (ctx : C.eval_ctx) (v : V.typed_value) : C.eval_ctx * V.typed_value =
- log#ldebug
- (lazy
- ("copy_value: "
- ^ typed_value_to_string ctx v
- ^ "\n- context:\n" ^ eval_ctx_to_string ctx));
- (* Remark: at some point we rewrote this function to use iterators, but then
- * we reverted the changes: the result was less clear actually. In particular,
- * the fact that we have exhaustive matches below makes very obvious the cases
- * in which we need to fail *)
- match v.V.value with
- | V.Concrete _ -> (ctx, v)
- | V.Adt av ->
- (* Sanity check *)
- (match v.V.ty with
- | T.Adt (T.Assumed (T.Box | Vec), _, _) ->
- failwith "Can't copy an assumed value other than Option"
- | T.Adt (T.AdtId _, _, _) -> assert allow_adt_copy
- | T.Adt ((T.Assumed Option | T.Tuple), _, _) -> () (* Ok *)
- | _ -> failwith "Unreachable");
- let ctx, fields =
- List.fold_left_map
- (copy_value allow_adt_copy config)
- ctx av.field_values
- in
- (ctx, { v with V.value = V.Adt { av with field_values = fields } })
- | V.Bottom -> failwith "Can't copy ⊥"
- | V.Borrow bc -> (
- (* We can only copy shared borrows *)
- match bc with
- | SharedBorrow (mv, bid) ->
- (* We need to create a new borrow id for the copied borrow, and
- * update the context accordingly *)
- let bid' = C.fresh_borrow_id () in
- let ctx = reborrow_shared bid bid' ctx in
- (ctx, { v with V.value = V.Borrow (SharedBorrow (mv, bid')) })
- | MutBorrow (_, _) -> failwith "Can't copy a mutable borrow"
- | V.InactivatedMutBorrow _ ->
- failwith "Can't copy an inactivated mut borrow")
- | V.Loan lc -> (
- (* We can only copy shared loans *)
- match lc with
- | V.MutLoan _ -> failwith "Can't copy a mutable loan"
- | V.SharedLoan (_, sv) ->
- (* We don't copy the shared loan: only the shared value inside *)
- copy_value allow_adt_copy config ctx sv)
- | V.Symbolic sp ->
- (* We can copy only if the type is "primitively" copyable.
- * Note that in the general case, copy is a trait: copying values
- * thus requires calling the proper function. Here, we copy values
- * for very simple types such as integers, shared borrows, etc. *)
- assert (ty_is_primitively_copyable (Subst.erase_regions sp.V.sv_ty));
- (* If the type is copyable, we simply return the current value. Side
- * remark: what is important to look at when copying symbolic values
- * is symbolic expansion. The important subcase is the expansion of shared
- * borrows: when doing so, every occurrence of the same symbolic value
- * must use a fresh borrow id. *)
- (ctx, v)
-
-(** Small utility.
-
- Prepare a place which is to be used as the destination of an assignment:
- update the environment along the paths, end the loans at this place, etc.
-
- Return the updated context and the (updated) value at the end of the
- place. This value should not contain any loan or borrow (and we check
- it is the case). Note that this value is very likely to contain {!V.Bottom}
- subvalues.
-
- [end_borrows]: if false, we only end the outer loans we find. If true, we
- end all the loans and the borrows we find.
- TODO: end_borrows is not necessary anymore.
- *)
-let prepare_lplace (config : C.config) (end_borrows : bool) (p : E.place)
- (cf : V.typed_value -> m_fun) : m_fun =
- fun ctx ->
- log#ldebug
- (lazy
- ("prepare_lplace:" ^ "\n- p: " ^ place_to_string ctx p
- ^ "\n- Initial context:\n" ^ eval_ctx_to_string ctx));
- (* Access the place *)
- let access = Write in
- let cc = update_ctx_along_write_place config access p in
- (* End the borrows and loans, starting with the borrows *)
- let cc = comp cc (drop_outer_borrows_loans_at_lplace config end_borrows p) in
- (* Read the value and check it *)
- let read_check cf : m_fun =
- fun ctx ->
- let v = read_place_unwrap config access p ctx in
- (* Sanity checks *)
- if end_borrows then (
- assert (not (loans_in_value v));
- assert (not (borrows_in_value v)))
- else assert (not (outer_loans_in_value v));
- (* Continue *)
- cf v ctx
- in
- (* Compose and apply the continuations *)
- comp cc read_check cf ctx
diff --git a/src/InterpreterProjectors.ml b/src/InterpreterProjectors.ml
deleted file mode 100644
index 064b8969..00000000
--- a/src/InterpreterProjectors.ml
+++ /dev/null
@@ -1,543 +0,0 @@
-module T = Types
-module V = Values
-module E = Expressions
-module C = Contexts
-module Subst = Substitute
-module L = Logging
-open TypesUtils
-open InterpreterUtils
-open InterpreterBorrowsCore
-
-(** Auxiliary function.
-
- Apply a proj_borrows on a shared borrow.
- Note that when projecting over shared values, we generate
- {!V.abstract_shared_borrows}, not {!V.avalue}s.
-*)
-let rec apply_proj_borrows_on_shared_borrow (ctx : C.eval_ctx)
- (fresh_reborrow : V.BorrowId.id -> V.BorrowId.id)
- (regions : T.RegionId.Set.t) (v : V.typed_value) (ty : T.rty) :
- V.abstract_shared_borrows =
- (* Sanity check - TODO: move this elsewhere (here we perform the check at every
- * recursive call which is a bit overkill...) *)
- let ety = Subst.erase_regions ty in
- assert (ety = v.V.ty);
- (* Project - if there are no regions from the abstraction in the type, return [_] *)
- if not (ty_has_regions_in_set regions ty) then []
- else
- match (v.V.value, ty) with
- | V.Concrete _, (T.Bool | T.Char | T.Integer _ | T.Str) -> []
- | V.Adt adt, T.Adt (id, region_params, tys) ->
- (* Retrieve the types of the fields *)
- let field_types =
- Subst.ctx_adt_value_get_instantiated_field_rtypes ctx adt id
- region_params tys
- in
- (* Project over the field values *)
- let fields_types = List.combine adt.V.field_values field_types in
- let proj_fields =
- List.map
- (fun (fv, fty) ->
- apply_proj_borrows_on_shared_borrow ctx fresh_reborrow regions fv
- fty)
- fields_types
- in
- List.concat proj_fields
- | V.Bottom, _ -> failwith "Unreachable"
- | V.Borrow bc, T.Ref (r, ref_ty, kind) ->
- (* Retrieve the bid of the borrow and the asb of the projected borrowed value *)
- let bid, asb =
- (* Not in the set: dive *)
- match (bc, kind) with
- | V.MutBorrow (bid, bv), T.Mut ->
- (* Apply the projection on the borrowed value *)
- let asb =
- apply_proj_borrows_on_shared_borrow ctx fresh_reborrow regions
- bv ref_ty
- in
- (bid, asb)
- | V.SharedBorrow (_, bid), T.Shared ->
- (* Lookup the shared value *)
- let ek = ek_all in
- let sv = lookup_loan ek bid ctx in
- let asb =
- match sv with
- | _, Concrete (V.SharedLoan (_, sv))
- | _, Abstract (V.ASharedLoan (_, sv, _)) ->
- apply_proj_borrows_on_shared_borrow ctx fresh_reborrow
- regions sv ref_ty
- | _ -> failwith "Unexpected"
- in
- (bid, asb)
- | V.InactivatedMutBorrow _, _ ->
- failwith
- "Can't apply a proj_borrow over an inactivated mutable borrow"
- | _ -> failwith "Unreachable"
- in
- let asb =
- (* Check if the region is in the set of projected regions (note that
- * we never project over static regions) *)
- if region_in_set r regions then
- let bid' = fresh_reborrow bid in
- V.AsbBorrow bid' :: asb
- else asb
- in
- asb
- | V.Loan _, _ -> failwith "Unreachable"
- | V.Symbolic s, _ ->
- (* Check that the projection doesn't contain ended regions *)
- assert (
- not (projections_intersect s.V.sv_ty ctx.ended_regions ty regions));
- [ V.AsbProjReborrows (s, ty) ]
- | _ -> failwith "Unreachable"
-
-(** Apply (and reduce) a projector over borrows to a value.
-
- - [regions]: the regions we project
- - [v]: the value over which we project
- - [ty]: the projection type (is used to map borrows to regions, or to
- interpret the borrows as belonging to some regions...). Remember that
- [v] doesn't contain region information.
- For instance, if we have:
- [v <: ty] where:
- - [v = mut_borrow l ...]
- - [ty = Ref (r, ...)]
- then we interpret the borrow [l] as belonging to region [r]
-
- Also, when applying projections on shared values, we need to apply
- reborrows. This is a bit annoying because, with the way we compute
- the projection on borrows, we can't update the context immediately.
- Instead, we remember the list of borrows we have to insert in the
- context *afterwards*.
-
- [check_symbolic_no_ended] controls whether we check or not whether
- symbolic values don't contain already ended regions.
- This check is activated when applying projectors upon calling a function
- (because we need to check that function arguments don't contain ⊥),
- but deactivated when expanding symbolic values:
- {[
- fn f<'a,'b>(x : &'a mut u32, y : &'b mut u32) -> (&'a mut u32, &'b mut u32);
-
- let p = f(&mut x, &mut y); // p -> @s0
- assert(x == ...); // end 'a
- let z = p.1; // HERE: the symbolic expansion of @s0 contains ended regions
- ]}
-*)
-let rec apply_proj_borrows (check_symbolic_no_ended : bool) (ctx : C.eval_ctx)
- (fresh_reborrow : V.BorrowId.id -> V.BorrowId.id)
- (regions : T.RegionId.Set.t) (ancestors_regions : T.RegionId.Set.t)
- (v : V.typed_value) (ty : T.rty) : V.typed_avalue =
- (* Sanity check - TODO: move this elsewhere (here we perform the check at every
- * recursive call which is a bit overkill...) *)
- let ety = Substitute.erase_regions ty in
- assert (ety = v.V.ty);
- (* Project - if there are no regions from the abstraction in the type, return [_] *)
- if not (ty_has_regions_in_set regions ty) then { V.value = V.AIgnored; ty }
- else
- let value : V.avalue =
- match (v.V.value, ty) with
- | V.Concrete cv, (T.Bool | T.Char | T.Integer _ | T.Str) -> V.AConcrete cv
- | V.Adt adt, T.Adt (id, region_params, tys) ->
- (* Retrieve the types of the fields *)
- let field_types =
- Subst.ctx_adt_value_get_instantiated_field_rtypes ctx adt id
- region_params tys
- in
- (* Project over the field values *)
- let fields_types = List.combine adt.V.field_values field_types in
- let proj_fields =
- List.map
- (fun (fv, fty) ->
- apply_proj_borrows check_symbolic_no_ended ctx fresh_reborrow
- regions ancestors_regions fv fty)
- fields_types
- in
- V.AAdt { V.variant_id = adt.V.variant_id; field_values = proj_fields }
- | V.Bottom, _ -> failwith "Unreachable"
- | V.Borrow bc, T.Ref (r, ref_ty, kind) ->
- if
- (* Check if the region is in the set of projected regions (note that
- * we never project over static regions) *)
- region_in_set r regions
- then
- (* In the set *)
- let bc =
- match (bc, kind) with
- | V.MutBorrow (bid, bv), T.Mut ->
- (* Remember the borrowed value we are about to project as a meta-value *)
- let mv = bv in
- (* Apply the projection on the borrowed value *)
- let bv =
- apply_proj_borrows check_symbolic_no_ended ctx
- fresh_reborrow regions ancestors_regions bv ref_ty
- in
- V.AMutBorrow (mv, bid, bv)
- | V.SharedBorrow (_, bid), T.Shared -> V.ASharedBorrow bid
- | V.InactivatedMutBorrow _, _ ->
- failwith
- "Can't apply a proj_borrow over an inactivated mutable \
- borrow"
- | _ -> failwith "Unreachable"
- in
- V.ABorrow bc
- else
- (* Not in the set: ignore *)
- let bc =
- match (bc, kind) with
- | V.MutBorrow (bid, bv), T.Mut ->
- (* Apply the projection on the borrowed value *)
- let bv =
- apply_proj_borrows check_symbolic_no_ended ctx
- fresh_reborrow regions ancestors_regions bv ref_ty
- in
- (* If the borrow id is in the ancestor's regions, we still need
- * to remember it *)
- let opt_bid =
- if region_in_set r ancestors_regions then Some bid else None
- in
- (* Return *)
- V.AIgnoredMutBorrow (opt_bid, bv)
- | V.SharedBorrow (_, bid), T.Shared ->
- (* Lookup the shared value *)
- let ek = ek_all in
- let sv = lookup_loan ek bid ctx in
- let asb =
- match sv with
- | _, Concrete (V.SharedLoan (_, sv))
- | _, Abstract (V.ASharedLoan (_, sv, _)) ->
- apply_proj_borrows_on_shared_borrow ctx fresh_reborrow
- regions sv ref_ty
- | _ -> failwith "Unexpected"
- in
- V.AProjSharedBorrow asb
- | V.InactivatedMutBorrow _, _ ->
- failwith
- "Can't apply a proj_borrow over an inactivated mutable \
- borrow"
- | _ -> failwith "Unreachable"
- in
- V.ABorrow bc
- | V.Loan _, _ -> failwith "Unreachable"
- | V.Symbolic s, _ ->
- (* Check that the projection doesn't contain already ended regions,
- * if necessary *)
- if check_symbolic_no_ended then (
- let ty1 = s.V.sv_ty in
- let rset1 = ctx.ended_regions in
- let ty2 = ty in
- let rset2 = regions in
- log#ldebug
- (lazy
- ("projections_intersect:" ^ "\n- ty1: " ^ rty_to_string ctx ty1
- ^ "\n- rset1: "
- ^ T.RegionId.Set.to_string None rset1
- ^ "\n- ty2: " ^ rty_to_string ctx ty2 ^ "\n- rset2: "
- ^ T.RegionId.Set.to_string None rset2
- ^ "\n"));
- assert (not (projections_intersect ty1 rset1 ty2 rset2)));
- V.ASymbolic (V.AProjBorrows (s, ty))
- | _ ->
- log#lerror
- (lazy
- ("apply_proj_borrows: unexpected inputs:\n- input value: "
- ^ typed_value_to_string ctx v
- ^ "\n- proj rty: " ^ rty_to_string ctx ty));
- failwith "Unreachable"
- in
- { V.value; V.ty }
-
-(** Convert a symbolic expansion *which is not a borrow* to a value *)
-let symbolic_expansion_non_borrow_to_value (sv : V.symbolic_value)
- (see : V.symbolic_expansion) : V.typed_value =
- let ty = Subst.erase_regions sv.V.sv_ty in
- let value =
- match see with
- | SeConcrete cv -> V.Concrete cv
- | SeAdt (variant_id, field_values) ->
- let field_values =
- List.map mk_typed_value_from_symbolic_value field_values
- in
- V.Adt { V.variant_id; V.field_values }
- | SeMutRef (_, _) | SeSharedRef (_, _) ->
- failwith "Unexpected symbolic reference expansion"
- in
- { V.value; V.ty }
-
-(** Convert a symbolic expansion to a value.
-
- If the expansion is a mutable reference expansion, it converts it to a borrow.
- This function is meant to be used when reducing projectors over borrows,
- during a symbolic expansion.
- *)
-let symbolic_expansion_non_shared_borrow_to_value (sv : V.symbolic_value)
- (see : V.symbolic_expansion) : V.typed_value =
- match see with
- | SeMutRef (bid, bv) ->
- let ty = Subst.erase_regions sv.V.sv_ty in
- let bv = mk_typed_value_from_symbolic_value bv in
- let value = V.Borrow (V.MutBorrow (bid, bv)) in
- { V.value; ty }
- | SeSharedRef (_, _) ->
- failwith "Unexpected symbolic shared reference expansion"
- | _ -> symbolic_expansion_non_borrow_to_value sv see
-
-(** Apply (and reduce) a projector over loans to a value.
-
- TODO: detailed comments. See [apply_proj_borrows]
-*)
-let apply_proj_loans_on_symbolic_expansion (regions : T.RegionId.Set.t)
- (see : V.symbolic_expansion) (original_sv_ty : T.rty) : V.typed_avalue =
- (* Sanity check: if we have a proj_loans over a symbolic value, it should
- * contain regions which we will project *)
- assert (ty_has_regions_in_set regions original_sv_ty);
- (* Match *)
- let (value, ty) : V.avalue * T.rty =
- match (see, original_sv_ty) with
- | SeConcrete _, (T.Bool | T.Char | T.Integer _ | T.Str) ->
- (V.AIgnored, original_sv_ty)
- | SeAdt (variant_id, field_values), T.Adt (_id, _region_params, _tys) ->
- (* Project over the field values *)
- let field_values =
- List.map
- (mk_aproj_loans_value_from_symbolic_value regions)
- field_values
- in
- (V.AAdt { V.variant_id; field_values }, original_sv_ty)
- | SeMutRef (bid, spc), T.Ref (r, ref_ty, T.Mut) ->
- (* Sanity check *)
- assert (spc.V.sv_ty = ref_ty);
- (* Apply the projector to the borrowed value *)
- let child_av = mk_aproj_loans_value_from_symbolic_value regions spc in
- (* Check if the region is in the set of projected regions (note that
- * we never project over static regions) *)
- if region_in_set r regions then
- (* In the set: keep *)
- (V.ALoan (V.AMutLoan (bid, child_av)), ref_ty)
- else
- (* Not in the set: ignore *)
- (V.ALoan (V.AIgnoredMutLoan (bid, child_av)), ref_ty)
- | SeSharedRef (bids, spc), T.Ref (r, ref_ty, T.Shared) ->
- (* Sanity check *)
- assert (spc.V.sv_ty = ref_ty);
- (* Apply the projector to the borrowed value *)
- let child_av = mk_aproj_loans_value_from_symbolic_value regions spc in
- (* Check if the region is in the set of projected regions (note that
- * we never project over static regions) *)
- if region_in_set r regions then
- (* In the set: keep *)
- let shared_value = mk_typed_value_from_symbolic_value spc in
- (V.ALoan (V.ASharedLoan (bids, shared_value, child_av)), ref_ty)
- else
- (* Not in the set: ignore *)
- (V.ALoan (V.AIgnoredSharedLoan child_av), ref_ty)
- | _ -> failwith "Unreachable"
- in
- { V.value; V.ty }
-
-(** Auxiliary function. See [give_back_value].
-
- Apply reborrows to a context.
-
- The [reborrows] input is a list of pairs (shared loan id, id to insert
- in the shared loan).
- This function is used when applying projectors on shared borrows: when
- doing so, we might need to reborrow subvalues from the shared value.
- For instance:
- {[
- fn f<'a,'b,'c>(x : &'a 'b 'c u32)
- ]}
- When introducing the abstractions for 'a, 'b and 'c, we apply a projector
- on some value [shared_borrow l : &'a &'b &'c u32].
- In the 'a abstraction, this shared borrow gets projected. However, when
- reducing the projectors for the 'b and 'c abstractions, we need to make
- sure that the borrows living in regions 'b and 'c live as long as those
- regions. This is done by looking up the shared value and applying reborrows
- on the borrows we find there (note that those reborrows apply on shared
- borrows - easy - and mutable borrows - in this case, we reborrow the whole
- borrow: [mut_borrow ... ~~> shared_loan {...} (mut_borrow ...)]).
-*)
-let apply_reborrows (reborrows : (V.BorrowId.id * V.BorrowId.id) list)
- (ctx : C.eval_ctx) : C.eval_ctx =
- (* This is a bit brutal, but whenever we insert a reborrow, we remove
- * it from the list. This allows us to check that all the reborrows were
- * applied before returning.
- * We might reimplement that in a more efficient manner by using maps. *)
- let reborrows = ref reborrows in
-
- (* Check if a value is a mutable borrow, and return its identifier if
- it is the case *)
- let get_borrow_in_mut_borrow (v : V.typed_value) : V.BorrowId.id option =
- match v.V.value with
- | V.Borrow lc -> (
- match lc with
- | V.SharedBorrow (_, _) | V.InactivatedMutBorrow _ -> None
- | V.MutBorrow (id, _) -> Some id)
- | _ -> None
- in
-
- (* Add the proper reborrows to a set of borrow ids (for a shared loan) *)
- let insert_reborrows bids =
- (* Find the reborrows to apply *)
- let insert, reborrows' =
- List.partition (fun (bid, _) -> V.BorrowId.Set.mem bid bids) !reborrows
- in
- reborrows := reborrows';
- let insert = List.map snd insert in
- (* Insert the borrows *)
- List.fold_left (fun bids bid -> V.BorrowId.Set.add bid bids) bids insert
- in
-
- (* Get the list of reborrows for a given borrow id *)
- let get_reborrows_for_bid bid =
- (* Find the reborrows to apply *)
- let insert, reborrows' =
- List.partition (fun (bid', _) -> bid' = bid) !reborrows
- in
- reborrows := reborrows';
- List.map snd insert
- in
-
- let borrows_to_set bids =
- List.fold_left
- (fun bids bid -> V.BorrowId.Set.add bid bids)
- V.BorrowId.Set.empty bids
- in
-
- (* Insert reborrows for a given borrow id into a given set of borrows *)
- let insert_reborrows_for_bid bids bid =
- (* Find the reborrows to apply *)
- let insert = get_reborrows_for_bid bid in
- (* Insert the borrows *)
- List.fold_left (fun bids bid -> V.BorrowId.Set.add bid bids) bids insert
- in
-
- let obj =
- object
- inherit [_] C.map_eval_ctx as super
-
- (** We may need to reborrow mutable borrows. Note that this doesn't
- happen for aborrows *)
- method! visit_typed_value env v =
- match v.V.value with
- | V.Borrow (V.MutBorrow (bid, bv)) ->
- let insert = get_reborrows_for_bid bid in
- let nbc = super#visit_MutBorrow env bid bv in
- let nbc = { v with V.value = V.Borrow nbc } in
- if insert = [] then (* No reborrows: do nothing special *)
- nbc
- else
- (* There are reborrows: insert a shared loan *)
- let insert = borrows_to_set insert in
- let value = V.Loan (V.SharedLoan (insert, nbc)) in
- let ty = v.V.ty in
- { V.value; ty }
- | _ -> super#visit_typed_value env v
-
- (** We reimplement {!visit_loan_content} (rather than one of the sub-
- functions) on purpose: exhaustive matches are good for maintenance *)
- method! visit_loan_content env lc =
- match lc with
- | V.SharedLoan (bids, sv) ->
- (* Insert the reborrows *)
- let bids = insert_reborrows bids in
- (* Check if the contained value is a mutable borrow, in which
- * case we might need to reborrow it by adding more borrow ids
- * to the current set of borrows - by doing this small
- * manipulation here, we accumulate the borrow ids in the same
- * shared loan, right above the mutable borrow, and avoid
- * stacking shared loans (note that doing this is not a problem
- * from a soundness point of view, but it is a bit ugly...) *)
- let bids =
- match get_borrow_in_mut_borrow sv with
- | None -> bids
- | Some bid -> insert_reborrows_for_bid bids bid
- in
- (* Update and explore *)
- super#visit_SharedLoan env bids sv
- | V.MutLoan bid ->
- (* Nothing special to do *)
- super#visit_MutLoan env bid
-
- method! visit_aloan_content env lc =
- match lc with
- | V.ASharedLoan (bids, sv, av) ->
- (* Insert the reborrows *)
- let bids = insert_reborrows bids in
- (* Similarly to the non-abstraction case: check if the shared
- * value is a mutable borrow, to eventually insert more reborrows *)
- (* Update and explore *)
- let bids =
- match get_borrow_in_mut_borrow sv with
- | None -> bids
- | Some bid -> insert_reborrows_for_bid bids bid
- in
- (* Update and explore *)
- super#visit_ASharedLoan env bids sv av
- | V.AIgnoredSharedLoan _
- | V.AMutLoan (_, _)
- | V.AEndedMutLoan { given_back = _; child = _; given_back_meta = _ }
- | V.AEndedSharedLoan (_, _)
- | V.AIgnoredMutLoan (_, _)
- | V.AEndedIgnoredMutLoan
- { given_back = _; child = _; given_back_meta = _ } ->
- (* Nothing particular to do *)
- super#visit_aloan_content env lc
- end
- in
-
- (* Visit *)
- let ctx = obj#visit_eval_ctx () ctx in
- (* Check that there are no reborrows remaining *)
- assert (!reborrows = []);
- (* Return *)
- ctx
-
-(** Auxiliary function to prepare reborrowing operations (used when
- applying projectors).
-
- Returns two functions:
- - a function to generate fresh re-borrow ids, and register the reborrows
- - a function to apply the reborrows in a context
- Those functions are of course stateful.
- *)
-let prepare_reborrows (config : C.config) (allow_reborrows : bool) :
- (V.BorrowId.id -> V.BorrowId.id) * (C.eval_ctx -> C.eval_ctx) =
- let reborrows : (V.BorrowId.id * V.BorrowId.id) list ref = ref [] in
- (* The function to generate and register fresh reborrows *)
- let fresh_reborrow (bid : V.BorrowId.id) : V.BorrowId.id =
- if allow_reborrows then (
- let bid' = C.fresh_borrow_id () in
- reborrows := (bid, bid') :: !reborrows;
- bid')
- else failwith "Unexpected reborrow"
- in
- (* The function to apply the reborrows in a context *)
- let apply_registered_reborrows (ctx : C.eval_ctx) : C.eval_ctx =
- match config.C.mode with
- | C.ConcreteMode ->
- assert (!reborrows = []);
- ctx
- | C.SymbolicMode ->
- (* Apply the reborrows *)
- apply_reborrows !reborrows ctx
- in
- (fresh_reborrow, apply_registered_reborrows)
-
-let apply_proj_borrows_on_input_value (config : C.config) (ctx : C.eval_ctx)
- (regions : T.RegionId.Set.t) (ancestors_regions : T.RegionId.Set.t)
- (v : V.typed_value) (ty : T.rty) : C.eval_ctx * V.typed_avalue =
- let check_symbolic_no_ended = true in
- let allow_reborrows = true in
- (* Prepare the reborrows *)
- let fresh_reborrow, apply_registered_reborrows =
- prepare_reborrows config allow_reborrows
- in
- (* Apply the projector *)
- let av =
- apply_proj_borrows check_symbolic_no_ended ctx fresh_reborrow regions
- ancestors_regions v ty
- in
- (* Apply the reborrows *)
- let ctx = apply_registered_reborrows ctx in
- (* Return *)
- (ctx, av)
diff --git a/src/InterpreterStatements.ml b/src/InterpreterStatements.ml
deleted file mode 100644
index 4e61e683..00000000
--- a/src/InterpreterStatements.ml
+++ /dev/null
@@ -1,1370 +0,0 @@
-module T = Types
-module V = Values
-module E = Expressions
-module C = Contexts
-module Subst = Substitute
-module A = LlbcAst
-module L = Logging
-open TypesUtils
-open ValuesUtils
-module Inv = Invariants
-module S = SynthesizeSymbolic
-open Errors
-open Cps
-open InterpreterUtils
-open InterpreterProjectors
-open InterpreterExpansion
-open InterpreterPaths
-open InterpreterExpressions
-
-(** The local logger *)
-let log = L.statements_log
-
-(** Drop a value at a given place - TODO: factorize this with [assign_to_place] *)
-let drop_value (config : C.config) (p : E.place) : cm_fun =
- fun cf ctx ->
- log#ldebug
- (lazy
- ("drop_value: place: " ^ place_to_string ctx p ^ "\n- Initial context:\n"
- ^ eval_ctx_to_string ctx));
- (* Prepare the place (by ending the outer loans).
- * Note that {!prepare_lplace} will use the [Write] access kind:
- * it is ok, because when updating the value with {!Bottom} below,
- * we will use the [Move] access *)
- let end_borrows = false in
- let prepare = prepare_lplace config end_borrows p in
- (* Replace the value with {!Bottom} *)
- let replace cf (v : V.typed_value) ctx =
- (* Move the value at destination (that we will overwrite) to a dummy variable
- * to preserve the borrows *)
- let mv = read_place_unwrap config Write p ctx in
- let ctx = C.ctx_push_dummy_var ctx mv in
- (* Update the destination to ⊥ *)
- let nv = { v with value = V.Bottom } in
- let ctx = write_place_unwrap config Move p nv ctx in
- log#ldebug
- (lazy
- ("drop_value: place: " ^ place_to_string ctx p ^ "\n- Final context:\n"
- ^ eval_ctx_to_string ctx));
- cf ctx
- in
- (* Compose and apply *)
- comp prepare replace cf ctx
-
-(** Push a dummy variable to the environment *)
-let push_dummy_var (v : V.typed_value) : cm_fun =
- fun cf ctx ->
- let ctx = C.ctx_push_dummy_var ctx v in
- cf ctx
-
-(** Pop a dummy variable from the environment *)
-let pop_dummy_var (cf : V.typed_value -> m_fun) : m_fun =
- fun ctx ->
- let ctx, v = C.ctx_pop_dummy_var ctx in
- cf v ctx
-
-(** Push an uninitialized variable to the environment *)
-let push_uninitialized_var (var : A.var) : cm_fun =
- fun cf ctx ->
- let ctx = C.ctx_push_uninitialized_var ctx var in
- cf ctx
-
-(** Push a list of uninitialized variables to the environment *)
-let push_uninitialized_vars (vars : A.var list) : cm_fun =
- fun cf ctx ->
- let ctx = C.ctx_push_uninitialized_vars ctx vars in
- cf ctx
-
-(** Push a variable to the environment *)
-let push_var (var : A.var) (v : V.typed_value) : cm_fun =
- fun cf ctx ->
- let ctx = C.ctx_push_var ctx var v in
- cf ctx
-
-(** Push a list of variables to the environment *)
-let push_vars (vars : (A.var * V.typed_value) list) : cm_fun =
- fun cf ctx ->
- let ctx = C.ctx_push_vars ctx vars in
- cf ctx
-
-(** Assign a value to a given place.
-
- Note that this function first pushes the value to assign in a dummy variable,
- then prepares the destination (by ending borrows, etc.) before popping the
- dummy variable and putting in its destination (after having checked that
- preparing the destination didn't introduce ⊥).
- *)
-let assign_to_place (config : C.config) (rv : V.typed_value) (p : E.place) :
- cm_fun =
- fun cf ctx ->
- log#ldebug
- (lazy
- ("assign_to_place:" ^ "\n- rv: "
- ^ typed_value_to_string ctx rv
- ^ "\n- p: " ^ place_to_string ctx p ^ "\n- Initial context:\n"
- ^ eval_ctx_to_string ctx));
- (* Push the rvalue to a dummy variable, for bookkeeping *)
- let cc = push_dummy_var rv in
- (* Prepare the destination *)
- let end_borrows = false in
- let cc = comp cc (prepare_lplace config end_borrows p) in
- (* Retrieve the rvalue from the dummy variable *)
- let cc = comp cc (fun cf _lv -> pop_dummy_var cf) in
- (* Update the destination *)
- let move_dest cf (rv : V.typed_value) : m_fun =
- fun ctx ->
- (* Move the value at destination (that we will overwrite) to a dummy variable
- * to preserve the borrows *)
- let mv = read_place_unwrap config Write p ctx in
- let ctx = C.ctx_push_dummy_var ctx mv in
- (* Write to the destination *)
- (* Checks - maybe the bookkeeping updated the rvalue and introduced bottoms *)
- assert (not (bottom_in_value ctx.ended_regions rv));
- (* Update the destination *)
- let ctx = write_place_unwrap config Write p rv ctx in
- (* Debug *)
- log#ldebug
- (lazy
- ("assign_to_place:" ^ "\n- rv: "
- ^ typed_value_to_string ctx rv
- ^ "\n- p: " ^ place_to_string ctx p ^ "\n- Final context:\n"
- ^ eval_ctx_to_string ctx));
- (* Continue *)
- cf ctx
- in
- (* Compose and apply *)
- comp cc move_dest cf ctx
-
-(** Evaluate an assertion, when the scrutinee is not symbolic *)
-let eval_assertion_concrete (config : C.config) (assertion : A.assertion) :
- st_cm_fun =
- fun cf ctx ->
- (* There won't be any symbolic expansions: fully evaluate the operand *)
- let eval_op = eval_operand config assertion.cond in
- let eval_assert cf (v : V.typed_value) : m_fun =
- fun ctx ->
- match v.value with
- | Concrete (Bool b) ->
- (* Branch *)
- if b = assertion.expected then cf Unit ctx else cf Panic ctx
- | _ ->
- raise
- (Failure ("Expected a boolean, got: " ^ typed_value_to_string ctx v))
- in
- (* Compose and apply *)
- comp eval_op eval_assert cf ctx
-
-(** Evaluates an assertion.
-
- In the case the boolean under scrutinee is symbolic, we synthesize
- a call to [assert ...] then continue in the success branch (and thus
- expand the boolean to [true]).
- *)
-let eval_assertion (config : C.config) (assertion : A.assertion) : st_cm_fun =
- fun cf ctx ->
- (* Evaluate the operand *)
- let eval_op = eval_operand config assertion.cond in
- (* Evaluate the assertion *)
- let eval_assert cf (v : V.typed_value) : m_fun =
- fun ctx ->
- assert (v.ty = T.Bool);
- (* We make a choice here: we could completely decouple the concrete and
- * symbolic executions here but choose not to. In the case where we
- * know the concrete value of the boolean we test, we use this value
- * even if we are in symbolic mode. Note that this case should be
- * extremely rare... *)
- match v.value with
- | Concrete (Bool _) ->
- (* Delegate to the concrete evaluation function *)
- eval_assertion_concrete config assertion cf ctx
- | Symbolic sv ->
- assert (config.mode = C.SymbolicMode);
- assert (sv.V.sv_ty = T.Bool);
- (* Expand the symbolic value and call the proper continuation functions
- * for the true and false cases - TODO: call an "assert" function instead *)
- let cf_true : m_fun = fun ctx -> cf Unit ctx in
- let cf_false : m_fun = fun ctx -> cf Panic ctx in
- let expand =
- expand_symbolic_bool config sv
- (S.mk_opt_place_from_op assertion.cond ctx)
- cf_true cf_false
- in
- expand ctx
- | _ ->
- raise
- (Failure ("Expected a boolean, got: " ^ typed_value_to_string ctx v))
- in
- (* Compose and apply *)
- comp eval_op eval_assert cf ctx
-
-(** Updates the discriminant of a value at a given place.
-
- There are two situations:
- - either the discriminant is already the proper one (in which case we
- don't do anything)
- - or it is not the proper one (because the variant is not the proper
- one, or the value is actually {!V.Bottom} - this happens when
- initializing ADT values), in which case we replace the value with
- a variant with all its fields set to {!V.Bottom}.
- For instance, something like: [Cons Bottom Bottom].
- *)
-let set_discriminant (config : C.config) (p : E.place)
- (variant_id : T.VariantId.id) : st_cm_fun =
- fun cf ctx ->
- log#ldebug
- (lazy
- ("set_discriminant:" ^ "\n- p: " ^ place_to_string ctx p
- ^ "\n- variant id: "
- ^ T.VariantId.to_string variant_id
- ^ "\n- initial context:\n" ^ eval_ctx_to_string ctx));
- (* Access the value *)
- let access = Write in
- let cc = update_ctx_along_read_place config access p in
- let end_borrows = false in
- let cc = comp cc (prepare_lplace config end_borrows p) in
- (* Update the value *)
- let update_value cf (v : V.typed_value) : m_fun =
- fun ctx ->
- match (v.V.ty, v.V.value) with
- | ( T.Adt (((T.AdtId _ | T.Assumed T.Option) as type_id), regions, types),
- V.Adt av ) -> (
- (* There are two situations:
- - either the discriminant is already the proper one (in which case we
- don't do anything)
- - or it is not the proper one, in which case we replace the value with
- a variant with all its fields set to {!Bottom}
- *)
- match av.variant_id with
- | None -> raise (Failure "Found a struct value while expected an enum")
- | Some variant_id' ->
- if variant_id' = variant_id then (* Nothing to do *)
- cf Unit ctx
- else
- (* Replace the value *)
- let bottom_v =
- match type_id with
- | T.AdtId def_id ->
- compute_expanded_bottom_adt_value
- ctx.type_context.type_decls def_id (Some variant_id)
- regions types
- | T.Assumed T.Option ->
- assert (regions = []);
- compute_expanded_bottom_option_value variant_id
- (Collections.List.to_cons_nil types)
- | _ -> raise (Failure "Unreachable")
- in
- assign_to_place config bottom_v p (cf Unit) ctx)
- | ( T.Adt (((T.AdtId _ | T.Assumed T.Option) as type_id), regions, types),
- V.Bottom ) ->
- let bottom_v =
- match type_id with
- | T.AdtId def_id ->
- compute_expanded_bottom_adt_value ctx.type_context.type_decls
- def_id (Some variant_id) regions types
- | T.Assumed T.Option ->
- assert (regions = []);
- compute_expanded_bottom_option_value variant_id
- (Collections.List.to_cons_nil types)
- | _ -> raise (Failure "Unreachable")
- in
- assign_to_place config bottom_v p (cf Unit) ctx
- | _, V.Symbolic _ ->
- assert (config.mode = SymbolicMode);
- (* This is a bit annoying: in theory we should expand the symbolic value
- * then set the discriminant, because in the case the discriminant is
- * exactly the one we set, the fields are left untouched, and in the
- * other cases they are set to Bottom.
- * For now, we forbid setting the discriminant of a symbolic value:
- * setting a discriminant should only be used to initialize a value,
- * or reset an already initialized value, really. *)
- raise (Failure "Unexpected value")
- | _, (V.Adt _ | V.Bottom) -> raise (Failure "Inconsistent state")
- | _, (V.Concrete _ | V.Borrow _ | V.Loan _) ->
- raise (Failure "Unexpected value")
- in
- (* Compose and apply *)
- comp cc update_value cf ctx
-
-(** Push a frame delimiter in the context's environment *)
-let ctx_push_frame (ctx : C.eval_ctx) : C.eval_ctx =
- { ctx with env = Frame :: ctx.env }
-
-(** Push a frame delimiter in the context's environment *)
-let push_frame : cm_fun = fun cf ctx -> cf (ctx_push_frame ctx)
-
-(** Small helper: compute the type of the return value for a specific
- instantiation of a non-local function.
- *)
-let get_non_local_function_return_type (fid : A.assumed_fun_id)
- (region_params : T.erased_region list) (type_params : T.ety list) : T.ety =
- (* [Box::free] has a special treatment *)
- match (fid, region_params, type_params) with
- | A.BoxFree, [], [ _ ] -> mk_unit_ty
- | _ ->
- (* Retrieve the function's signature *)
- let sg = Assumed.get_assumed_sig fid in
- (* Instantiate the return type *)
- let tsubst =
- Subst.make_type_subst
- (List.map (fun v -> v.T.index) sg.type_params)
- type_params
- in
- Subst.erase_regions_substitute_types tsubst sg.output
-
-let move_return_value (config : C.config) (cf : V.typed_value -> m_fun) : m_fun
- =
- fun ctx ->
- let ret_vid = V.VarId.zero in
- let cc = eval_operand config (E.Move (mk_place_from_var_id ret_vid)) in
- cc cf ctx
-
-(** Pop the current frame.
-
- Drop all the local variables but the return variable, move the return
- value out of the return variable, remove all the local variables (but not the
- abstractions!) from the context, remove the {!C.Frame} indicator delimiting the
- current frame and handle the return value to the continuation.
-
- TODO: rename (remove the "ctx_")
- *)
-let ctx_pop_frame (config : C.config) (cf : V.typed_value -> m_fun) : m_fun =
- fun ctx ->
- (* Debug *)
- log#ldebug (lazy ("ctx_pop_frame:\n" ^ eval_ctx_to_string ctx));
-
- (* List the local variables, but the return variable *)
- let ret_vid = V.VarId.zero in
- let rec list_locals env =
- match env with
- | [] -> raise (Failure "Inconsistent environment")
- | C.Abs _ :: env -> list_locals env
- | C.Var (None, _) :: env -> list_locals env
- | C.Var (Some var, _) :: env ->
- let locals = list_locals env in
- if var.index <> ret_vid then var.index :: locals else locals
- | C.Frame :: _ -> []
- in
- let locals : V.VarId.id list = list_locals ctx.env in
- (* Debug *)
- log#ldebug
- (lazy
- ("ctx_pop_frame: locals in which to drop the outer loans: ["
- ^ String.concat "," (List.map V.VarId.to_string locals)
- ^ "]"));
-
- (* Move the return value out of the return variable *)
- let cc = move_return_value config in
- (* Sanity check *)
- let cc =
- comp_check_value cc (fun ret_value ctx ->
- assert (not (bottom_in_value ctx.ended_regions ret_value)))
- in
-
- (* Drop the outer *loans* we find in the local variables *)
- let cf_drop_loans_in_locals cf (ret_value : V.typed_value) : m_fun =
- (* Drop the loans *)
- let end_borrows = false in
- let locals = List.rev locals in
- let cf_drop =
- List.fold_left
- (fun cf lid ->
- drop_outer_borrows_loans_at_lplace config end_borrows
- (mk_place_from_var_id lid) cf)
- (cf ret_value) locals
- in
- (* Apply *)
- cf_drop
- in
- let cc = comp cc cf_drop_loans_in_locals in
- (* Debug *)
- let cc =
- comp_check_value cc (fun _ ctx ->
- log#ldebug
- (lazy
- ("ctx_pop_frame: after dropping outer loans in local variables:\n"
- ^ eval_ctx_to_string ctx)))
- in
-
- (* Pop the frame - we remove the [Frame] delimiter, and reintroduce all
- * the local variables (which may still contain borrow permissions - but
- * no outer loans) as dummy variables in the caller frame *)
- let rec pop env =
- match env with
- | [] -> raise (Failure "Inconsistent environment")
- | C.Abs abs :: env -> C.Abs abs :: pop env
- | C.Var (_, v) :: env -> C.Var (None, v) :: pop env
- | C.Frame :: env -> (* Stop here *) env
- in
- let cf_pop cf (ret_value : V.typed_value) : m_fun =
- fun ctx ->
- let env = pop ctx.env in
- let ctx = { ctx with env } in
- cf ret_value ctx
- in
- (* Compose and apply *)
- comp cc cf_pop cf ctx
-
-(** Pop the current frame and assign the returned value to its destination. *)
-let pop_frame_assign (config : C.config) (dest : E.place) : cm_fun =
- let cf_pop = ctx_pop_frame config in
- let cf_assign cf ret_value : m_fun =
- assign_to_place config ret_value dest cf
- in
- comp cf_pop cf_assign
-
-(** Auxiliary function - see [eval_non_local_function_call] *)
-let eval_replace_concrete (_config : C.config)
- (_region_params : T.erased_region list) (_type_params : T.ety list) : cm_fun
- =
- fun _cf _ctx -> raise Unimplemented
-
-(** Auxiliary function - see [eval_non_local_function_call] *)
-let eval_box_new_concrete (config : C.config)
- (region_params : T.erased_region list) (type_params : T.ety list) : cm_fun =
- fun cf ctx ->
- (* Check and retrieve the arguments *)
- match (region_params, type_params, ctx.env) with
- | ( [],
- [ boxed_ty ],
- Var (Some input_var, input_value) :: Var (_ret_var, _) :: C.Frame :: _ )
- ->
- (* Required type checking *)
- assert (input_value.V.ty = boxed_ty);
-
- (* Move the input value *)
- let cf_move =
- eval_operand config (E.Move (mk_place_from_var_id input_var.C.index))
- in
-
- (* Create the new box *)
- let cf_create cf (moved_input_value : V.typed_value) : m_fun =
- (* Create the box value *)
- let box_ty = T.Adt (T.Assumed T.Box, [], [ boxed_ty ]) in
- let box_v =
- V.Adt { variant_id = None; field_values = [ moved_input_value ] }
- in
- let box_v = mk_typed_value box_ty box_v in
-
- (* Move this value to the return variable *)
- let dest = mk_place_from_var_id V.VarId.zero in
- let cf_assign = assign_to_place config box_v dest in
-
- (* Continue *)
- cf_assign cf
- in
-
- (* Compose and apply *)
- comp cf_move cf_create cf ctx
- | _ -> raise (Failure "Inconsistent state")
-
-(** Auxiliary function which factorizes code to evaluate [std::Deref::deref]
- and [std::DerefMut::deref_mut] - see [eval_non_local_function_call] *)
-let eval_box_deref_mut_or_shared_concrete (config : C.config)
- (region_params : T.erased_region list) (type_params : T.ety list)
- (is_mut : bool) : cm_fun =
- fun cf ctx ->
- (* Check the arguments *)
- match (region_params, type_params, ctx.env) with
- | ( [],
- [ boxed_ty ],
- Var (Some input_var, input_value) :: Var (_ret_var, _) :: C.Frame :: _ )
- ->
- (* Required type checking. We must have:
- - input_value.ty == & (mut) Box<ty>
- - boxed_ty == ty
- for some ty
- *)
- (let _, input_ty, ref_kind = ty_get_ref input_value.V.ty in
- assert (match ref_kind with T.Shared -> not is_mut | T.Mut -> is_mut);
- let input_ty = ty_get_box input_ty in
- assert (input_ty = boxed_ty));
-
- (* Borrow the boxed value *)
- let p =
- { E.var_id = input_var.C.index; projection = [ E.Deref; E.DerefBox ] }
- in
- let borrow_kind = if is_mut then E.Mut else E.Shared in
- let rv = E.Ref (p, borrow_kind) in
- let cf_borrow = eval_rvalue config rv in
-
- (* Move the borrow to its destination *)
- let cf_move cf res : m_fun =
- match res with
- | Error EPanic ->
- (* We can't get there by borrowing a value *)
- raise (Failure "Unreachable")
- | Ok borrowed_value ->
- (* Move and continue *)
- let destp = mk_place_from_var_id V.VarId.zero in
- assign_to_place config borrowed_value destp cf
- in
-
- (* Compose and apply *)
- comp cf_borrow cf_move cf ctx
- | _ -> raise (Failure "Inconsistent state")
-
-(** Auxiliary function - see [eval_non_local_function_call] *)
-let eval_box_deref_concrete (config : C.config)
- (region_params : T.erased_region list) (type_params : T.ety list) : cm_fun =
- let is_mut = false in
- eval_box_deref_mut_or_shared_concrete config region_params type_params is_mut
-
-(** Auxiliary function - see [eval_non_local_function_call] *)
-let eval_box_deref_mut_concrete (config : C.config)
- (region_params : T.erased_region list) (type_params : T.ety list) : cm_fun =
- let is_mut = true in
- eval_box_deref_mut_or_shared_concrete config region_params type_params is_mut
-
-(** Auxiliary function - see [eval_non_local_function_call].
-
- [Box::free] is not handled the same way as the other assumed functions:
- - in the regular case, whenever we need to evaluate an assumed function,
- we evaluate the operands, push a frame, call a dedicated function
- to correctly update the variables in the frame (and mimic the execution
- of a body) and finally pop the frame
- - in the case of [Box::free]: the value given to this function is often
- of the form [Box(⊥)] because we can move the value out of the
- box before freeing the box. It makes it invalid to see box_free as a
- "regular" function: it is not valid to call a function with arguments
- which contain [⊥]. For this reason, we execute [Box::free] as drop_value,
- but this is a bit annoying with regards to the semantics...
-
- Followingly this function doesn't behave like the others: it does not expect
- a stack frame to have been pushed, but rather simply behaves like {!drop_value}.
- It thus updates the box value (by calling {!drop_value}) and updates
- the destination (by setting it to [()]).
-*)
-let eval_box_free (config : C.config) (region_params : T.erased_region list)
- (type_params : T.ety list) (args : E.operand list) (dest : E.place) : cm_fun
- =
- fun cf ctx ->
- match (region_params, type_params, args) with
- | [], [ boxed_ty ], [ E.Move input_box_place ] ->
- (* Required type checking *)
- let input_box = read_place_unwrap config Write input_box_place ctx in
- (let input_ty = ty_get_box input_box.V.ty in
- assert (input_ty = boxed_ty));
-
- (* Drop the value *)
- let cc = drop_value config input_box_place in
-
- (* Update the destination by setting it to [()] *)
- let cc = comp cc (assign_to_place config mk_unit_value dest) in
-
- (* Continue *)
- cc cf ctx
- | _ -> raise (Failure "Inconsistent state")
-
-(** Auxiliary function - see [eval_non_local_function_call] *)
-let eval_vec_function_concrete (_config : C.config) (_fid : A.assumed_fun_id)
- (_region_params : T.erased_region list) (_type_params : T.ety list) : cm_fun
- =
- fun _cf _ctx -> raise Unimplemented
-
-(** Evaluate a non-local function call in concrete mode *)
-let eval_non_local_function_call_concrete (config : C.config)
- (fid : A.assumed_fun_id) (region_params : T.erased_region list)
- (type_params : T.ety list) (args : E.operand list) (dest : E.place) : cm_fun
- =
- (* There are two cases (and this is extremely annoying):
- - the function is not box_free
- - the function is box_free
- See {!eval_box_free}
- *)
- match fid with
- | A.BoxFree ->
- (* Degenerate case: box_free *)
- eval_box_free config region_params type_params args dest
- | _ ->
- (* "Normal" case: not box_free *)
- (* Evaluate the operands *)
- (* let ctx, args_vl = eval_operands config ctx args in *)
- let cf_eval_ops = eval_operands config args in
-
- (* Evaluate the call
- *
- * Style note: at some point we used {!comp_transmit} to
- * transmit the result of {!eval_operands} above down to {!push_vars}
- * below, without having to introduce an intermediary function call,
- * but it made it less clear where the computed values came from,
- * so we reversed the modifications. *)
- let cf_eval_call cf (args_vl : V.typed_value list) : m_fun =
- (* Push the stack frame: we initialize the frame with the return variable,
- and one variable per input argument *)
- let cc = push_frame in
-
- (* Create and push the return variable *)
- let ret_vid = V.VarId.zero in
- let ret_ty =
- get_non_local_function_return_type fid region_params type_params
- in
- let ret_var = mk_var ret_vid (Some "@return") ret_ty in
- let cc = comp cc (push_uninitialized_var ret_var) in
-
- (* Create and push the input variables *)
- let input_vars =
- V.VarId.mapi_from1
- (fun id (v : V.typed_value) -> (mk_var id None v.V.ty, v))
- args_vl
- in
- let cc = comp cc (push_vars input_vars) in
-
- (* "Execute" the function body. As the functions are assumed, here we call
- * custom functions to perform the proper manipulations: we don't have
- * access to a body. *)
- let cf_eval_body : cm_fun =
- match fid with
- | A.Replace -> eval_replace_concrete config region_params type_params
- | BoxNew -> eval_box_new_concrete config region_params type_params
- | BoxDeref -> eval_box_deref_concrete config region_params type_params
- | BoxDerefMut ->
- eval_box_deref_mut_concrete config region_params type_params
- | BoxFree ->
- (* Should have been treated above *) raise (Failure "Unreachable")
- | VecNew | VecPush | VecInsert | VecLen | VecIndex | VecIndexMut ->
- eval_vec_function_concrete config fid region_params type_params
- in
-
- let cc = comp cc cf_eval_body in
-
- (* Pop the frame *)
- let cc = comp cc (pop_frame_assign config dest) in
-
- (* Continue *)
- cc cf
- in
- (* Compose and apply *)
- comp cf_eval_ops cf_eval_call
-
-(** Instantiate a function signature, introducing fresh abstraction ids and
- region ids. This is mostly used in preparation of function calls, when
- evaluating in symbolic mode of course.
-
- Note: there are no region parameters, because they should be erased.
-
- **Rk.:** this function is **stateful** and generates fresh abstraction ids
- for the region groups.
- *)
-let instantiate_fun_sig (type_params : T.ety list) (sg : A.fun_sig) :
- A.inst_fun_sig =
- (* Generate fresh abstraction ids and create a substitution from region
- * group ids to abstraction ids *)
- let rg_abs_ids_bindings =
- List.map
- (fun rg ->
- let abs_id = C.fresh_abstraction_id () in
- (rg.T.id, abs_id))
- sg.regions_hierarchy
- in
- let asubst_map : V.AbstractionId.id T.RegionGroupId.Map.t =
- List.fold_left
- (fun mp (rg_id, abs_id) -> T.RegionGroupId.Map.add rg_id abs_id mp)
- T.RegionGroupId.Map.empty rg_abs_ids_bindings
- in
- let asubst (rg_id : T.RegionGroupId.id) : V.AbstractionId.id =
- T.RegionGroupId.Map.find rg_id asubst_map
- in
- (* Generate fresh regions and their substitutions *)
- let _, rsubst, _ = Subst.fresh_regions_with_substs sg.region_params in
- (* Generate the type substitution
- * Note that we need the substitution to map the type variables to
- * {!rty} types (not {!ety}). In order to do that, we convert the
- * type parameters to types with regions. This is possible only
- * if those types don't contain any regions.
- * This is a current limitation of the analysis: there is still some
- * work to do to properly handle full type parametrization.
- * *)
- let rtype_params = List.map ety_no_regions_to_rty type_params in
- let tsubst =
- Subst.make_type_subst
- (List.map (fun v -> v.T.index) sg.type_params)
- rtype_params
- in
- (* Substitute the signature *)
- let inst_sig = Subst.substitute_signature asubst rsubst tsubst sg in
- (* Return *)
- inst_sig
-
-(** Helper
-
- Create abstractions (with no avalues, which have to be inserted afterwards)
- from a list of abs region groups.
-
- [region_can_end]: gives the region groups from which we generate functions
- which can end or not.
- *)
-let create_empty_abstractions_from_abs_region_groups (call_id : V.FunCallId.id)
- (kind : V.abs_kind) (rgl : A.abs_region_group list)
- (region_can_end : T.RegionGroupId.id -> bool) : V.abs list =
- (* We use a reference to progressively create a map from abstraction ids
- * to set of ancestor regions. Note that {!abs_to_ancestors_regions} [abs_id]
- * returns the union of:
- * - the regions of the ancestors of abs_id
- * - the regions of abs_id
- *)
- let abs_to_ancestors_regions : T.RegionId.Set.t V.AbstractionId.Map.t ref =
- ref V.AbstractionId.Map.empty
- in
- (* Auxiliary function to create one abstraction *)
- let create_abs (back_id : T.RegionGroupId.id) (rg : A.abs_region_group) :
- V.abs =
- let abs_id = rg.T.id in
- let original_parents = rg.parents in
- let parents =
- List.fold_left
- (fun s pid -> V.AbstractionId.Set.add pid s)
- V.AbstractionId.Set.empty rg.parents
- in
- let regions =
- List.fold_left
- (fun s rid -> T.RegionId.Set.add rid s)
- T.RegionId.Set.empty rg.regions
- in
- let ancestors_regions =
- List.fold_left
- (fun acc parent_id ->
- T.RegionId.Set.union acc
- (V.AbstractionId.Map.find parent_id !abs_to_ancestors_regions))
- T.RegionId.Set.empty rg.parents
- in
- let ancestors_regions_union_current_regions =
- T.RegionId.Set.union ancestors_regions regions
- in
- let can_end = region_can_end back_id in
- abs_to_ancestors_regions :=
- V.AbstractionId.Map.add abs_id ancestors_regions_union_current_regions
- !abs_to_ancestors_regions;
- (* Create the abstraction *)
- {
- V.abs_id;
- call_id;
- back_id;
- kind;
- can_end;
- parents;
- original_parents;
- regions;
- ancestors_regions;
- avalues = [];
- }
- in
- (* Apply *)
- T.RegionGroupId.mapi create_abs rgl
-
-(** Helper.
-
- Create a list of abstractions from a list of regions groups, and insert
- them in the context.
-
- [region_can_end]: gives the region groups from which we generate functions
- which can end or not.
-
- [compute_abs_avalues]: this function must compute, given an initialized,
- empty (i.e., with no avalues) abstraction, compute the avalues which
- should be inserted in this abstraction before we insert it in the context.
- Note that this function may update the context: it is necessary when
- computing borrow projections, for instance.
-*)
-let create_push_abstractions_from_abs_region_groups (call_id : V.FunCallId.id)
- (kind : V.abs_kind) (rgl : A.abs_region_group list)
- (region_can_end : T.RegionGroupId.id -> bool)
- (compute_abs_avalues :
- V.abs -> C.eval_ctx -> C.eval_ctx * V.typed_avalue list)
- (ctx : C.eval_ctx) : C.eval_ctx =
- (* Initialize the abstractions as empty (i.e., with no avalues) abstractions *)
- let empty_absl =
- create_empty_abstractions_from_abs_region_groups call_id kind rgl
- region_can_end
- in
-
- (* Compute and add the avalues to the abstractions, the insert the abstractions
- * in the context. *)
- let insert_abs (ctx : C.eval_ctx) (abs : V.abs) : C.eval_ctx =
- (* Compute the values to insert in the abstraction *)
- let ctx, avalues = compute_abs_avalues abs ctx in
- (* Add the avalues to the abstraction *)
- let abs = { abs with avalues } in
- (* Insert the abstraction in the context *)
- let ctx = { ctx with env = Abs abs :: ctx.env } in
- (* Return *)
- ctx
- in
- List.fold_left insert_abs ctx empty_absl
-
-(** Evaluate a statement *)
-let rec eval_statement (config : C.config) (st : A.statement) : st_cm_fun =
- fun cf ctx ->
- (* Debugging *)
- log#ldebug
- (lazy
- ("\n**About to evaluate statement**: [\n"
- ^ statement_to_string_with_tab ctx st
- ^ "\n]\n\n**Context**:\n" ^ eval_ctx_to_string ctx ^ "\n\n"));
-
- (* Expand the symbolic values if necessary - we need to do that before
- * checking the invariants *)
- let cc = greedy_expand_symbolic_values config in
- (* Sanity check *)
- let cc = comp cc (Inv.cf_check_invariants config) in
-
- (* Evaluate *)
- let cf_eval_st cf : m_fun =
- fun ctx ->
- match st.content with
- | A.Assign (p, rvalue) ->
- (* Evaluate the rvalue *)
- let cf_eval_rvalue = eval_rvalue config rvalue in
- (* Assign *)
- let cf_assign cf (res : (V.typed_value, eval_error) result) ctx =
- log#ldebug
- (lazy
- ("about to assign to place: " ^ place_to_string ctx p
- ^ "\n- Context:\n" ^ eval_ctx_to_string ctx));
- match res with
- | Error EPanic -> cf Panic ctx
- | Ok rv -> (
- let expr = assign_to_place config rv p (cf Unit) ctx in
- (* Update the synthesized AST - here we store meta-information.
- * We do it only in specific cases (it is not always useful, and
- * also it can lead to issues - for instance, if we borrow an
- * inactivated borrow, we later can't translate it to pure values...) *)
- match rvalue with
- | E.Use _
- | E.Ref (_, (E.Shared | E.Mut | E.TwoPhaseMut))
- | E.UnaryOp _ | E.BinaryOp _ | E.Discriminant _ | E.Aggregate _ ->
- let rp = rvalue_get_place rvalue in
- let rp =
- match rp with
- | Some rp -> Some (S.mk_mplace rp ctx)
- | None -> None
- in
- S.synthesize_assignment (S.mk_mplace p ctx) rv rp expr)
- in
-
- (* Compose and apply *)
- comp cf_eval_rvalue cf_assign cf ctx
- | A.AssignGlobal { dst; global } -> eval_global config dst global cf ctx
- | A.FakeRead p ->
- let expand_prim_copy = false in
- let cf_prepare cf =
- access_rplace_reorganize_and_read config expand_prim_copy Read p cf
- in
- let cf_continue cf v : m_fun =
- fun ctx ->
- assert (not (bottom_in_value ctx.ended_regions v));
- cf ctx
- in
- comp cf_prepare cf_continue (cf Unit) ctx
- | A.SetDiscriminant (p, variant_id) ->
- set_discriminant config p variant_id cf ctx
- | A.Drop p -> drop_value config p (cf Unit) ctx
- | A.Assert assertion -> eval_assertion config assertion cf ctx
- | A.Call call -> eval_function_call config call cf ctx
- | A.Panic -> cf Panic ctx
- | A.Return -> cf Return ctx
- | A.Break i -> cf (Break i) ctx
- | A.Continue i -> cf (Continue i) ctx
- | A.Nop -> cf Unit ctx
- | A.Sequence (st1, st2) ->
- (* Evaluate the first statement *)
- let cf_st1 = eval_statement config st1 in
- (* Evaluate the sequence *)
- let cf_st2 cf res =
- match res with
- (* Evaluation successful: evaluate the second statement *)
- | Unit -> eval_statement config st2 cf
- (* Control-flow break: transmit. We enumerate the cases on purpose *)
- | Panic | Break _ | Continue _ | Return -> cf res
- in
- (* Compose and apply *)
- comp cf_st1 cf_st2 cf ctx
- | A.Loop loop_body ->
- (* For now, we don't support loops in symbolic mode *)
- assert (config.C.mode = C.ConcreteMode);
- (* Continuation for after we evaluate the loop body: depending the result
- of doing one loop iteration:
- - redoes a loop iteration
- - exits the loop
- - other...
-
- We need a specific function because of the {!Continue} case: in case we
- continue, we might have to reevaluate the current loop body with the
- new context (and repeat this an indefinite number of times).
- *)
- let rec reeval_loop_body res : m_fun =
- match res with
- | Return | Panic -> cf res
- | Break i ->
- (* Break out of the loop by calling the continuation *)
- let res = if i = 0 then Unit else Break (i - 1) in
- cf res
- | Continue 0 ->
- (* Re-evaluate the loop body *)
- eval_statement config loop_body reeval_loop_body
- | Continue i ->
- (* Continue to an outer loop *)
- cf (Continue (i - 1))
- | Unit ->
- (* We can't get there.
- * Note that if we decide not to fail here but rather do
- * the same thing as for [Continue 0], we could make the
- * code slightly simpler: calling {!reeval_loop_body} with
- * {!Unit} would account for the first iteration of the loop.
- * We prefer to write it this way for consistency and sanity,
- * though. *)
- raise (Failure "Unreachable")
- in
- (* Apply *)
- eval_statement config loop_body reeval_loop_body ctx
- | A.Switch (op, tgts) -> eval_switch config op tgts cf ctx
- in
- (* Compose and apply *)
- comp cc cf_eval_st cf ctx
-
-and eval_global (config : C.config) (dest : V.VarId.id)
- (gid : LA.GlobalDeclId.id) : st_cm_fun =
- fun cf ctx ->
- let global = C.ctx_lookup_global_decl ctx gid in
- let place = { E.var_id = dest; projection = [] } in
- match config.mode with
- | ConcreteMode ->
- (* Treat the evaluation of the global as a call to the global body (without arguments) *)
- (eval_local_function_call_concrete config global.body_id [] [] [] place)
- cf ctx
- | SymbolicMode ->
- (* Generate a fresh symbolic value. In the translation, this fresh symbolic value will be
- * defined as equal to the value of the global (see {!S.synthesize_global_eval}). *)
- let sval =
- mk_fresh_symbolic_value V.Global (ety_no_regions_to_rty global.ty)
- in
- let cc =
- assign_to_place config (mk_typed_value_from_symbolic_value sval) place
- in
- let e = cc (cf Unit) ctx in
- S.synthesize_global_eval gid sval e
-
-(** Evaluate a switch *)
-and eval_switch (config : C.config) (op : E.operand) (tgts : A.switch_targets) :
- st_cm_fun =
- fun cf ctx ->
- (* We evaluate the operand in two steps:
- * first we prepare it, then we check if its value is concrete or
- * symbolic. If it is concrete, we can then evaluate the operand
- * directly, otherwise we must first expand the value.
- * Note that we can't fully evaluate the operand *then* expand the
- * value if it is symbolic, because the value may have been move
- * (and would thus floating in thin air...)!
- * *)
- (* Prepare the operand *)
- let cf_eval_op cf : m_fun = eval_operand config op cf in
- (* Match on the targets *)
- let cf_match (cf : st_m_fun) (op_v : V.typed_value) : m_fun =
- fun ctx ->
- match tgts with
- | A.If (st1, st2) -> (
- match op_v.value with
- | V.Concrete (V.Bool b) ->
- (* Evaluate the if and the branch body *)
- let cf_branch cf : m_fun =
- (* Branch *)
- if b then eval_statement config st1 cf
- else eval_statement config st2 cf
- in
- (* Compose the continuations *)
- cf_branch cf ctx
- | V.Symbolic sv ->
- (* Expand the symbolic boolean, and continue by evaluating
- * the branches *)
- let cf_true : m_fun = eval_statement config st1 cf in
- let cf_false : m_fun = eval_statement config st2 cf in
- expand_symbolic_bool config sv
- (S.mk_opt_place_from_op op ctx)
- cf_true cf_false ctx
- | _ -> raise (Failure "Inconsistent state"))
- | A.SwitchInt (int_ty, stgts, otherwise) -> (
- match op_v.value with
- | V.Concrete (V.Scalar sv) ->
- (* Evaluate the branch *)
- let cf_eval_branch cf =
- (* Sanity check *)
- assert (sv.V.int_ty = int_ty);
- (* Find the branch *)
- match List.find_opt (fun (svl, _) -> List.mem sv svl) stgts with
- | None -> eval_statement config otherwise cf
- | Some (_, tgt) -> eval_statement config tgt cf
- in
- (* Compose *)
- cf_eval_branch cf ctx
- | V.Symbolic sv ->
- (* Expand the symbolic value and continue by evaluating the
- * proper branches *)
- let stgts =
- List.map
- (fun (cv, tgt_st) -> (cv, eval_statement config tgt_st cf))
- stgts
- in
- (* Several branches may be grouped together: every branch is described
- * by a pair (list of values, branch expression).
- * In order to do a symbolic evaluation, we make this "flat" by
- * de-grouping the branches. *)
- let stgts =
- List.concat
- (List.map
- (fun (vl, st) -> List.map (fun v -> (v, st)) vl)
- stgts)
- in
- (* Translate the otherwise branch *)
- let otherwise = eval_statement config otherwise cf in
- (* Expand and continue *)
- expand_symbolic_int config sv
- (S.mk_opt_place_from_op op ctx)
- int_ty stgts otherwise ctx
- | _ -> raise (Failure "Inconsistent state"))
- in
- (* Compose the continuations *)
- comp cf_eval_op cf_match cf ctx
-
-(** Evaluate a function call (auxiliary helper for [eval_statement]) *)
-and eval_function_call (config : C.config) (call : A.call) : st_cm_fun =
- (* There are two cases:
- - this is a local function, in which case we execute its body
- - this is a non-local function, in which case there is a special treatment
- *)
- match call.func with
- | A.Regular fid ->
- eval_local_function_call config fid call.region_args call.type_args
- call.args call.dest
- | A.Assumed fid ->
- eval_non_local_function_call config fid call.region_args call.type_args
- call.args call.dest
-
-(** Evaluate a local (i.e., non-assumed) function call in concrete mode *)
-and eval_local_function_call_concrete (config : C.config) (fid : A.FunDeclId.id)
- (region_args : T.erased_region list) (type_args : T.ety list)
- (args : E.operand list) (dest : E.place) : st_cm_fun =
- fun cf ctx ->
- assert (region_args = []);
-
- (* Retrieve the (correctly instantiated) body *)
- let def = C.ctx_lookup_fun_decl ctx fid in
- (* We can evaluate the function call only if it is not opaque *)
- let body =
- match def.body with
- | None ->
- raise
- (Failure
- ("Can't evaluate a call to an opaque function: "
- ^ Print.name_to_string def.name))
- | Some body -> body
- in
- let tsubst =
- Subst.make_type_subst
- (List.map (fun v -> v.T.index) def.A.signature.type_params)
- type_args
- in
- let locals, body_st = Subst.fun_body_substitute_in_body tsubst body in
-
- (* Evaluate the input operands *)
- assert (List.length args = body.A.arg_count);
- let cc = eval_operands config args in
-
- (* Push a frame delimiter - we use {!comp_transmit} to transmit the result
- * of the operands evaluation from above to the functions afterwards, while
- * ignoring it in this function *)
- let cc = comp_transmit cc push_frame in
-
- (* Compute the initial values for the local variables *)
- (* 1. Push the return value *)
- let ret_var, locals =
- match locals with
- | ret_ty :: locals -> (ret_ty, locals)
- | _ -> raise (Failure "Unreachable")
- in
- let input_locals, locals =
- Collections.List.split_at locals body.A.arg_count
- in
-
- let cc = comp_transmit cc (push_var ret_var (mk_bottom ret_var.var_ty)) in
-
- (* 2. Push the input values *)
- let cf_push_inputs cf args =
- let inputs = List.combine input_locals args in
- (* Note that this function checks that the variables and their values
- * have the same type (this is important) *)
- push_vars inputs cf
- in
- let cc = comp cc cf_push_inputs in
-
- (* 3. Push the remaining local variables (initialized as {!Bottom}) *)
- let cc = comp cc (push_uninitialized_vars locals) in
-
- (* Execute the function body *)
- let cc = comp cc (eval_function_body config body_st) in
-
- (* Pop the stack frame and move the return value to its destination *)
- let cf_finish cf res =
- match res with
- | Panic -> cf Panic
- | Break _ | Continue _ | Unit -> raise (Failure "Unreachable")
- | Return ->
- (* Pop the stack frame, retrieve the return value, move it to
- * its destination and continue *)
- pop_frame_assign config dest (cf Unit)
- in
- let cc = comp cc cf_finish in
-
- (* Continue *)
- cc cf ctx
-
-(** Evaluate a local (i.e., non-assumed) function call in symbolic mode *)
-and eval_local_function_call_symbolic (config : C.config) (fid : A.FunDeclId.id)
- (region_args : T.erased_region list) (type_args : T.ety list)
- (args : E.operand list) (dest : E.place) : st_cm_fun =
- fun cf ctx ->
- (* Retrieve the (correctly instantiated) signature *)
- let def = C.ctx_lookup_fun_decl ctx fid in
- let sg = def.A.signature in
- (* Instantiate the signature and introduce fresh abstraction and region ids
- * while doing so *)
- let inst_sg = instantiate_fun_sig type_args sg in
- (* Sanity check *)
- assert (List.length args = List.length def.A.signature.inputs);
- (* Evaluate the function call *)
- eval_function_call_symbolic_from_inst_sig config (A.Regular fid) inst_sg
- region_args type_args args dest cf ctx
-
-(** Evaluate a function call in symbolic mode by using the function signature.
-
- This allows us to factorize the evaluation of local and non-local function
- calls in symbolic mode: only their signatures matter.
- *)
-and eval_function_call_symbolic_from_inst_sig (config : C.config)
- (fid : A.fun_id) (inst_sg : A.inst_fun_sig)
- (region_args : T.erased_region list) (type_args : T.ety list)
- (args : E.operand list) (dest : E.place) : st_cm_fun =
- fun cf ctx ->
- assert (region_args = []);
- (* Generate a fresh symbolic value for the return value *)
- let ret_sv_ty = inst_sg.A.output in
- let ret_spc = mk_fresh_symbolic_value V.FunCallRet ret_sv_ty in
- let ret_value = mk_typed_value_from_symbolic_value ret_spc in
- let ret_av regions =
- mk_aproj_loans_value_from_symbolic_value regions ret_spc
- in
- let args_places = List.map (fun p -> S.mk_opt_place_from_op p ctx) args in
- let dest_place = Some (S.mk_mplace dest ctx) in
-
- (* Evaluate the input operands *)
- let cc = eval_operands config args in
-
- (* Generate the abstractions and insert them in the context *)
- let abs_ids = List.map (fun rg -> rg.T.id) inst_sg.regions_hierarchy in
- let cf_call cf (args : V.typed_value list) : m_fun =
- fun ctx ->
- let args_with_rtypes = List.combine args inst_sg.A.inputs in
-
- (* Check the type of the input arguments *)
- assert (
- List.for_all
- (fun ((arg, rty) : V.typed_value * T.rty) ->
- arg.V.ty = Subst.erase_regions rty)
- args_with_rtypes);
- (* Check that the input arguments don't contain symbolic values that can't
- * be fed to functions (i.e., symbolic values output from function return
- * values and which contain borrows of borrows can't be used as function
- * inputs *)
- assert (
- List.for_all
- (fun arg ->
- not (value_has_ret_symbolic_value_with_borrow_under_mut ctx arg))
- args);
-
- (* Initialize the abstractions and push them in the context.
- * First, we define the function which, given an initialized, empty
- * abstraction, computes the avalues which should be inserted inside.
- *)
- let compute_abs_avalues (abs : V.abs) (ctx : C.eval_ctx) :
- C.eval_ctx * V.typed_avalue list =
- (* Project over the input values *)
- let ctx, args_projs =
- List.fold_left_map
- (fun ctx (arg, arg_rty) ->
- apply_proj_borrows_on_input_value config ctx abs.regions
- abs.ancestors_regions arg arg_rty)
- ctx args_with_rtypes
- in
- (* Group the input and output values *)
- (ctx, List.append args_projs [ ret_av abs.regions ])
- in
- (* Actually initialize and insert the abstractions *)
- let call_id = C.fresh_fun_call_id () in
- let region_can_end _ = true in
- let ctx =
- create_push_abstractions_from_abs_region_groups call_id V.FunCall
- inst_sg.A.regions_hierarchy region_can_end compute_abs_avalues ctx
- in
-
- (* Apply the continuation *)
- let expr = cf ctx in
-
- (* Synthesize the symbolic AST *)
- S.synthesize_regular_function_call fid call_id abs_ids type_args args
- args_places ret_spc dest_place expr
- in
- let cc = comp cc cf_call in
-
- (* Move the return value to its destination *)
- let cc = comp cc (assign_to_place config ret_value dest) in
-
- (* End the abstractions which don't contain loans and don't have parent
- * abstractions.
- * We do the general, nested borrows case here: we end abstractions, then
- * retry (because then we might end their children abstractions)
- *)
- let abs_ids = ref abs_ids in
- let rec end_abs_with_no_loans cf : m_fun =
- fun ctx ->
- (* Find the abstractions which don't contain loans *)
- let no_loans_abs, with_loans_abs =
- List.partition
- (fun abs_id ->
- (* Lookup the abstraction *)
- let abs = C.ctx_lookup_abs ctx abs_id in
- (* Check if it has parents *)
- V.AbstractionId.Set.is_empty abs.parents
- (* Check if it contains non-ignored loans *)
- && Option.is_none
- (InterpreterBorrowsCore
- .get_first_non_ignored_aloan_in_abstraction abs))
- !abs_ids
- in
- (* Check if there are abstractions to end *)
- if no_loans_abs <> [] then (
- (* Update the reference to the list of asbtraction ids, for the recursive calls *)
- abs_ids := with_loans_abs;
- (* End the abstractions which can be ended *)
- let no_loans_abs = V.AbstractionId.Set.of_list no_loans_abs in
- let cc = InterpreterBorrows.end_abstractions config [] no_loans_abs in
- (* Recursive call *)
- let cc = comp cc end_abs_with_no_loans in
- (* Continue *)
- cc cf ctx)
- else (* No abstractions to end: continue *)
- cf ctx
- in
- (* Try to end the abstractions with no loans if:
- * - the option is enabled
- * - the function returns unit
- * (see the documentation of {!config} for more information)
- *)
- let cc =
- if config.return_unit_end_abs_with_no_loans && ty_is_unit inst_sg.output
- then comp cc end_abs_with_no_loans
- else cc
- in
-
- (* Continue - note that we do as if the function call has been successful,
- * by giving {!Unit} to the continuation, because we place us in the case
- * where we haven't panicked. Of course, the translation needs to take the
- * panic case into account... *)
- cc (cf Unit) ctx
-
-(** Evaluate a non-local function call in symbolic mode *)
-and eval_non_local_function_call_symbolic (config : C.config)
- (fid : A.assumed_fun_id) (region_args : T.erased_region list)
- (type_args : T.ety list) (args : E.operand list) (dest : E.place) :
- st_cm_fun =
- fun cf ctx ->
- (* Sanity check: make sure the type parameters don't contain regions -
- * this is a current limitation of our synthesis *)
- assert (
- List.for_all
- (fun ty -> not (ty_has_borrows ctx.type_context.type_infos ty))
- type_args);
-
- (* There are two cases (and this is extremely annoying):
- - the function is not box_free
- - the function is box_free
- See {!eval_box_free}
- *)
- match fid with
- | A.BoxFree ->
- (* Degenerate case: box_free - note that this is not really a function
- * call: no need to call a "synthesize_..." function *)
- eval_box_free config region_args type_args args dest (cf Unit) ctx
- | _ ->
- (* "Normal" case: not box_free *)
- (* In symbolic mode, the behaviour of a function call is completely defined
- * by the signature of the function: we thus simply generate correctly
- * instantiated signatures, and delegate the work to an auxiliary function *)
- let inst_sig =
- match fid with
- | A.BoxFree ->
- (* should have been treated above *)
- raise (Failure "Unreachable")
- | _ -> instantiate_fun_sig type_args (Assumed.get_assumed_sig fid)
- in
-
- (* Evaluate the function call *)
- eval_function_call_symbolic_from_inst_sig config (A.Assumed fid) inst_sig
- region_args type_args args dest cf ctx
-
-(** Evaluate a non-local (i.e, assumed) function call such as [Box::deref]
- (auxiliary helper for [eval_statement]) *)
-and eval_non_local_function_call (config : C.config) (fid : A.assumed_fun_id)
- (region_args : T.erased_region list) (type_args : T.ety list)
- (args : E.operand list) (dest : E.place) : st_cm_fun =
- fun cf ctx ->
- (* Debug *)
- log#ldebug
- (lazy
- (let type_args =
- "[" ^ String.concat ", " (List.map (ety_to_string ctx) type_args) ^ "]"
- in
- let args =
- "[" ^ String.concat ", " (List.map (operand_to_string ctx) args) ^ "]"
- in
- let dest = place_to_string ctx dest in
- "eval_non_local_function_call:\n- fid:" ^ A.show_assumed_fun_id fid
- ^ "\n- type_args: " ^ type_args ^ "\n- args: " ^ args ^ "\n- dest: "
- ^ dest));
-
- match config.mode with
- | C.ConcreteMode ->
- eval_non_local_function_call_concrete config fid region_args type_args
- args dest (cf Unit) ctx
- | C.SymbolicMode ->
- eval_non_local_function_call_symbolic config fid region_args type_args
- args dest cf ctx
-
-(** Evaluate a local (i.e, not assumed) function call (auxiliary helper for
- [eval_statement]) *)
-and eval_local_function_call (config : C.config) (fid : A.FunDeclId.id)
- (region_args : T.erased_region list) (type_args : T.ety list)
- (args : E.operand list) (dest : E.place) : st_cm_fun =
- match config.mode with
- | ConcreteMode ->
- eval_local_function_call_concrete config fid region_args type_args args
- dest
- | SymbolicMode ->
- eval_local_function_call_symbolic config fid region_args type_args args
- dest
-
-(** Evaluate a statement seen as a function body (auxiliary helper for
- [eval_statement]) *)
-and eval_function_body (config : C.config) (body : A.statement) : st_cm_fun =
- fun cf ctx ->
- let cc = eval_statement config body in
- let cf_finish cf res =
- (* Note that we *don't* check the result ({!Panic}, {!Return}, etc.): we
- * delegate the check to the caller. *)
- (* Expand the symbolic values if necessary - we need to do that before
- * checking the invariants *)
- let cc = greedy_expand_symbolic_values config in
- (* Sanity check *)
- let cc = comp_check_ctx cc (Inv.check_invariants config) in
- (* Continue *)
- cc (cf res)
- in
- (* Compose and continue *)
- comp cc cf_finish cf ctx
diff --git a/src/InterpreterUtils.ml b/src/InterpreterUtils.ml
deleted file mode 100644
index e6033e9e..00000000
--- a/src/InterpreterUtils.ml
+++ /dev/null
@@ -1,245 +0,0 @@
-module T = Types
-module V = Values
-module E = Expressions
-module C = Contexts
-module Subst = Substitute
-module A = LlbcAst
-module L = Logging
-open Utils
-open TypesUtils
-module PA = Print.EvalCtxLlbcAst
-
-(** Some utilities *)
-
-let eval_ctx_to_string = Print.Contexts.eval_ctx_to_string
-let ety_to_string = PA.ety_to_string
-let rty_to_string = PA.rty_to_string
-let symbolic_value_to_string = PA.symbolic_value_to_string
-let borrow_content_to_string = PA.borrow_content_to_string
-let loan_content_to_string = PA.loan_content_to_string
-let aborrow_content_to_string = PA.aborrow_content_to_string
-let aloan_content_to_string = PA.aloan_content_to_string
-let aproj_to_string = PA.aproj_to_string
-let typed_value_to_string = PA.typed_value_to_string
-let typed_avalue_to_string = PA.typed_avalue_to_string
-let place_to_string = PA.place_to_string
-let operand_to_string = PA.operand_to_string
-let statement_to_string ctx = PA.statement_to_string ctx "" " "
-let statement_to_string_with_tab ctx = PA.statement_to_string ctx " " " "
-
-let same_symbolic_id (sv0 : V.symbolic_value) (sv1 : V.symbolic_value) : bool =
- sv0.V.sv_id = sv1.V.sv_id
-
-let mk_var (index : V.VarId.id) (name : string option) (var_ty : T.ety) : A.var
- =
- { A.index; name; var_ty }
-
-(** Small helper - TODO: move *)
-let mk_place_from_var_id (var_id : V.VarId.id) : E.place =
- { var_id; projection = [] }
-
-(** Create a fresh symbolic value *)
-let mk_fresh_symbolic_value (sv_kind : V.sv_kind) (ty : T.rty) :
- V.symbolic_value =
- let sv_id = C.fresh_symbolic_value_id () in
- let svalue = { V.sv_kind; V.sv_id; V.sv_ty = ty } in
- svalue
-
-(** Create a fresh symbolic value *)
-let mk_fresh_symbolic_typed_value (sv_kind : V.sv_kind) (rty : T.rty) :
- V.typed_value =
- let ty = Subst.erase_regions rty in
- (* Generate the fresh a symbolic value *)
- let value = mk_fresh_symbolic_value sv_kind rty in
- let value = V.Symbolic value in
- { V.value; V.ty }
-
-(** Create a typed value from a symbolic value. *)
-let mk_typed_value_from_symbolic_value (svalue : V.symbolic_value) :
- V.typed_value =
- let av = V.Symbolic svalue in
- let av : V.typed_value =
- { V.value = av; V.ty = Subst.erase_regions svalue.V.sv_ty }
- in
- av
-
-(** Create a loans projector value from a symbolic value.
-
- Checks if the projector will actually project some regions. If not,
- returns {!V.AIgnored} ([_]).
-
- TODO: update to handle 'static
- *)
-let mk_aproj_loans_value_from_symbolic_value (regions : T.RegionId.Set.t)
- (svalue : V.symbolic_value) : V.typed_avalue =
- if ty_has_regions_in_set regions svalue.sv_ty then
- let av = V.ASymbolic (V.AProjLoans (svalue, [])) in
- let av : V.typed_avalue = { V.value = av; V.ty = svalue.V.sv_ty } in
- av
- else { V.value = V.AIgnored; ty = svalue.V.sv_ty }
-
-(** Create a borrows projector from a symbolic value *)
-let mk_aproj_borrows_from_symbolic_value (proj_regions : T.RegionId.Set.t)
- (svalue : V.symbolic_value) (proj_ty : T.rty) : V.aproj =
- if ty_has_regions_in_set proj_regions proj_ty then
- V.AProjBorrows (svalue, proj_ty)
- else V.AIgnoredProjBorrows
-
-(** TODO: move *)
-let borrow_is_asb (bid : V.BorrowId.id) (asb : V.abstract_shared_borrow) : bool
- =
- match asb with
- | V.AsbBorrow bid' -> bid' = bid
- | V.AsbProjReborrows _ -> false
-
-(** TODO: move *)
-let borrow_in_asb (bid : V.BorrowId.id) (asb : V.abstract_shared_borrows) : bool
- =
- List.exists (borrow_is_asb bid) asb
-
-(** TODO: move *)
-let remove_borrow_from_asb (bid : V.BorrowId.id)
- (asb : V.abstract_shared_borrows) : V.abstract_shared_borrows =
- let removed = ref 0 in
- let asb =
- List.filter
- (fun asb ->
- if not (borrow_is_asb bid asb) then true
- else (
- removed := !removed + 1;
- false))
- asb
- in
- assert (!removed = 1);
- asb
-
-(** We sometimes need to return a value whose type may vary depending on
- whether we find it in a "concrete" value or an abstraction (ex.: loan
- contents when we perform environment lookups by using borrow ids) *)
-type ('a, 'b) concrete_or_abs = Concrete of 'a | Abstract of 'b
-
-(** Generic loan content: concrete or abstract *)
-type g_loan_content = (V.loan_content, V.aloan_content) concrete_or_abs
-
-(** Generic borrow content: concrete or abstract *)
-type g_borrow_content = (V.borrow_content, V.aborrow_content) concrete_or_abs
-
-type abs_or_var_id = AbsId of V.AbstractionId.id | VarId of V.VarId.id option
-
-(** Utility exception *)
-exception FoundBorrowContent of V.borrow_content
-
-(** Utility exception *)
-exception FoundLoanContent of V.loan_content
-
-(** Utility exception *)
-exception FoundABorrowContent of V.aborrow_content
-
-(** Utility exception *)
-exception FoundGBorrowContent of g_borrow_content
-
-(** Utility exception *)
-exception FoundGLoanContent of g_loan_content
-
-(** Utility exception *)
-exception FoundAProjBorrows of V.symbolic_value * T.rty
-
-let symbolic_value_id_in_ctx (sv_id : V.SymbolicValueId.id) (ctx : C.eval_ctx) :
- bool =
- let obj =
- object
- inherit [_] C.iter_eval_ctx as super
-
- method! visit_Symbolic _ sv =
- if sv.V.sv_id = sv_id then raise Found else ()
-
- method! visit_aproj env aproj =
- (match aproj with
- | AProjLoans (sv, _) | AProjBorrows (sv, _) ->
- if sv.V.sv_id = sv_id then raise Found else ()
- | AEndedProjLoans _ | AEndedProjBorrows _ | AIgnoredProjBorrows -> ());
- super#visit_aproj env aproj
-
- method! visit_abstract_shared_borrows _ asb =
- let visit (asb : V.abstract_shared_borrow) : unit =
- match asb with
- | V.AsbBorrow _ -> ()
- | V.AsbProjReborrows (sv, _) ->
- if sv.V.sv_id = sv_id then raise Found else ()
- in
- List.iter visit asb
- end
- in
- (* We use exceptions *)
- try
- obj#visit_eval_ctx () ctx;
- false
- with Found -> true
-
-(** Check that a symbolic value doesn't contain ended regions.
-
- Note that we don't check that the set of ended regions is empty: we
- check that the set of ended regions doesn't intersect the set of
- regions used in the type (this is more general).
-*)
-let symbolic_value_has_ended_regions (ended_regions : T.RegionId.Set.t)
- (s : V.symbolic_value) : bool =
- let regions = rty_regions s.V.sv_ty in
- not (T.RegionId.Set.disjoint regions ended_regions)
-
-(** Check if a {!type:V.value} contains [⊥].
-
- Note that this function is very general: it also checks wether
- symbolic values contain already ended regions.
- *)
-let bottom_in_value (ended_regions : T.RegionId.Set.t) (v : V.typed_value) :
- bool =
- let obj =
- object
- inherit [_] V.iter_typed_value
- method! visit_Bottom _ = raise Found
-
- method! visit_symbolic_value _ s =
- if symbolic_value_has_ended_regions ended_regions s then raise Found
- else ()
- end
- in
- (* We use exceptions *)
- try
- obj#visit_typed_value () v;
- false
- with Found -> true
-
-let value_has_ret_symbolic_value_with_borrow_under_mut (ctx : C.eval_ctx)
- (v : V.typed_value) : bool =
- let obj =
- object
- inherit [_] V.iter_typed_value
-
- method! visit_symbolic_value _ s =
- match s.sv_kind with
- | V.FunCallRet ->
- if ty_has_borrow_under_mut ctx.type_context.type_infos s.sv_ty then
- raise Found
- else ()
- | V.SynthInput | V.SynthInputGivenBack | V.FunCallGivenBack
- | V.SynthRetGivenBack ->
- ()
- | V.Global -> ()
- end
- in
- (* We use exceptions *)
- try
- obj#visit_typed_value () v;
- false
- with Found -> true
-
-(** Return the place used in an rvalue, if that makes sense.
- This is used to compute meta-data, to find pretty names.
- *)
-let rvalue_get_place (rv : E.rvalue) : E.place option =
- match rv with
- | Use (Copy p | Move p) -> Some p
- | Use (Constant _) -> None
- | Ref (p, _) -> Some p
- | UnaryOp _ | BinaryOp _ | Discriminant _ | Aggregate _ -> None
diff --git a/src/Invariants.ml b/src/Invariants.ml
deleted file mode 100644
index 4a3364a6..00000000
--- a/src/Invariants.ml
+++ /dev/null
@@ -1,794 +0,0 @@
-(* The following module defines functions to check that some invariants
- * are always maintained by evaluation contexts *)
-
-module T = Types
-module V = Values
-module E = Expressions
-module C = Contexts
-module Subst = Substitute
-module A = LlbcAst
-module L = Logging
-open Cps
-open TypesUtils
-open InterpreterUtils
-open InterpreterBorrowsCore
-
-(** The local logger *)
-let log = L.invariants_log
-
-type borrow_info = {
- loan_kind : T.ref_kind;
- loan_in_abs : bool;
- (* true if the loan was found in an abstraction *)
- loan_ids : V.BorrowId.Set.t;
- borrow_ids : V.BorrowId.Set.t;
-}
-[@@deriving show]
-
-type outer_borrow_info = {
- outer_borrow : bool;
- (* true if the value is borrowed *)
- outer_shared : bool; (* true if the value is borrowed as shared *)
-}
-
-let set_outer_mut (info : outer_borrow_info) : outer_borrow_info =
- { info with outer_borrow = true }
-
-let set_outer_shared (_info : outer_borrow_info) : outer_borrow_info =
- { outer_borrow = true; outer_shared = true }
-
-let ids_reprs_to_string (indent : string)
- (reprs : V.BorrowId.id V.BorrowId.Map.t) : string =
- V.BorrowId.Map.to_string (Some indent) V.BorrowId.to_string reprs
-
-let borrows_infos_to_string (indent : string)
- (infos : borrow_info V.BorrowId.Map.t) : string =
- V.BorrowId.Map.to_string (Some indent) show_borrow_info infos
-
-type borrow_kind = Mut | Shared | Inactivated
-
-(** Check that:
- - loans and borrows are correctly related
- - a two-phase borrow can't point to a value inside an abstraction
- *)
-let check_loans_borrows_relation_invariant (ctx : C.eval_ctx) : unit =
- (* Link all the borrow ids to a representant - necessary because of shared
- * borrows/loans *)
- let ids_reprs : V.BorrowId.id V.BorrowId.Map.t ref =
- ref V.BorrowId.Map.empty
- in
- (* Link all the id representants to a borrow information *)
- let borrows_infos : borrow_info V.BorrowId.Map.t ref =
- ref V.BorrowId.Map.empty
- in
- let context_to_string () : string =
- eval_ctx_to_string ctx ^ "- representants:\n"
- ^ ids_reprs_to_string " " !ids_reprs
- ^ "\n- info:\n"
- ^ borrows_infos_to_string " " !borrows_infos
- in
- (* Ignored loans - when we find an ignored loan while building the borrows_infos
- * map, we register it in this list; once the borrows_infos map is completely
- * built, we check that all the borrow ids of the ignored loans are in this
- * map *)
- let ignored_loans : (T.ref_kind * V.BorrowId.id) list ref = ref [] in
-
- (* first, register all the loans *)
- (* Some utilities to register the loans *)
- let register_ignored_loan (rkind : T.ref_kind) (bid : V.BorrowId.id) : unit =
- ignored_loans := (rkind, bid) :: !ignored_loans
- in
-
- let register_shared_loan (loan_in_abs : bool) (bids : V.BorrowId.Set.t) : unit
- =
- let reprs = !ids_reprs in
- let infos = !borrows_infos in
- (* Use the first borrow id as representant *)
- let repr_bid = V.BorrowId.Set.min_elt bids in
- assert (not (V.BorrowId.Map.mem repr_bid infos));
- (* Insert the mappings to the representant *)
- let reprs =
- V.BorrowId.Set.fold
- (fun bid reprs ->
- assert (not (V.BorrowId.Map.mem bid reprs));
- V.BorrowId.Map.add bid repr_bid reprs)
- bids reprs
- in
- (* Insert the loan info *)
- let info =
- {
- loan_kind = T.Shared;
- loan_in_abs;
- loan_ids = bids;
- borrow_ids = V.BorrowId.Set.empty;
- }
- in
- let infos = V.BorrowId.Map.add repr_bid info infos in
- (* Update *)
- ids_reprs := reprs;
- borrows_infos := infos
- in
-
- let register_mut_loan (loan_in_abs : bool) (bid : V.BorrowId.id) : unit =
- let reprs = !ids_reprs in
- let infos = !borrows_infos in
- (* Sanity checks *)
- assert (not (V.BorrowId.Map.mem bid reprs));
- assert (not (V.BorrowId.Map.mem bid infos));
- (* Add the mapping for the representant *)
- let reprs = V.BorrowId.Map.add bid bid reprs in
- (* Add the mapping for the loan info *)
- let info =
- {
- loan_kind = T.Mut;
- loan_in_abs;
- loan_ids = V.BorrowId.Set.singleton bid;
- borrow_ids = V.BorrowId.Set.empty;
- }
- in
- let infos = V.BorrowId.Map.add bid info infos in
- (* Update *)
- ids_reprs := reprs;
- borrows_infos := infos
- in
-
- let loans_visitor =
- object
- inherit [_] C.iter_eval_ctx as super
-
- method! visit_Var _ binder v =
- let inside_abs = false in
- super#visit_Var inside_abs binder v
-
- method! visit_Abs _ abs =
- let inside_abs = true in
- super#visit_Abs inside_abs abs
-
- method! visit_loan_content inside_abs lc =
- (* Register the loan *)
- let _ =
- match lc with
- | V.SharedLoan (bids, _) -> register_shared_loan inside_abs bids
- | V.MutLoan bid -> register_mut_loan inside_abs bid
- in
- (* Continue exploring *)
- super#visit_loan_content inside_abs lc
-
- method! visit_aloan_content inside_abs lc =
- let _ =
- match lc with
- | V.AMutLoan (bid, _) -> register_mut_loan inside_abs bid
- | V.ASharedLoan (bids, _, _) -> register_shared_loan inside_abs bids
- | V.AIgnoredMutLoan (bid, _) -> register_ignored_loan T.Mut bid
- | V.AIgnoredSharedLoan _
- | V.AEndedMutLoan { given_back = _; child = _; given_back_meta = _ }
- | V.AEndedSharedLoan (_, _)
- | V.AEndedIgnoredMutLoan
- { given_back = _; child = _; given_back_meta = _ } ->
- (* Do nothing *)
- ()
- in
- (* Continue exploring *)
- super#visit_aloan_content inside_abs lc
- end
- in
-
- (* Visit *)
- let inside_abs = false in
- loans_visitor#visit_eval_ctx inside_abs ctx;
-
- (* Then, register all the borrows *)
- (* Some utilities to register the borrows *)
- let find_info (bid : V.BorrowId.id) : borrow_info =
- (* Find the representant *)
- match V.BorrowId.Map.find_opt bid !ids_reprs with
- | Some repr_bid ->
- (* Lookup the info *)
- V.BorrowId.Map.find repr_bid !borrows_infos
- | None ->
- let err =
- "find_info: could not find the representant of borrow "
- ^ V.BorrowId.to_string bid ^ ":\nContext:\n" ^ context_to_string ()
- in
- log#serror err;
- failwith err
- in
-
- let update_info (bid : V.BorrowId.id) (info : borrow_info) : unit =
- (* Find the representant *)
- let repr_bid = V.BorrowId.Map.find bid !ids_reprs in
- (* Update the info *)
- let infos =
- V.BorrowId.Map.update repr_bid
- (fun x ->
- match x with Some _ -> Some info | None -> failwith "Unreachable")
- !borrows_infos
- in
- borrows_infos := infos
- in
-
- let register_ignored_borrow = register_ignored_loan in
-
- let register_borrow (kind : borrow_kind) (bid : V.BorrowId.id) : unit =
- (* Lookup the info *)
- let info = find_info bid in
- (* Check that the borrow kind is consistent *)
- (match (info.loan_kind, kind) with
- | T.Shared, (Shared | Inactivated) | T.Mut, Mut -> ()
- | _ -> failwith "Invariant not satisfied");
- (* An inactivated borrow can't point to a value inside an abstraction *)
- assert (kind <> Inactivated || not info.loan_in_abs);
- (* Insert the borrow id *)
- let borrow_ids = info.borrow_ids in
- assert (not (V.BorrowId.Set.mem bid borrow_ids));
- let info = { info with borrow_ids = V.BorrowId.Set.add bid borrow_ids } in
- (* Update the info in the map *)
- update_info bid info
- in
-
- let borrows_visitor =
- object
- inherit [_] C.iter_eval_ctx as super
-
- method! visit_abstract_shared_borrows _ asb =
- let visit asb =
- match asb with
- | V.AsbBorrow bid -> register_borrow Shared bid
- | V.AsbProjReborrows _ -> ()
- in
- List.iter visit asb
-
- method! visit_borrow_content env bc =
- (* Register the loan *)
- let _ =
- match bc with
- | V.SharedBorrow (_, bid) -> register_borrow Shared bid
- | V.MutBorrow (bid, _) -> register_borrow Mut bid
- | V.InactivatedMutBorrow (_, bid) -> register_borrow Inactivated bid
- in
- (* Continue exploring *)
- super#visit_borrow_content env bc
-
- method! visit_aborrow_content env bc =
- let _ =
- match bc with
- | V.AMutBorrow (_, bid, _) -> register_borrow Mut bid
- | V.ASharedBorrow bid -> register_borrow Shared bid
- | V.AIgnoredMutBorrow (Some bid, _) -> register_ignored_borrow Mut bid
- | V.AIgnoredMutBorrow (None, _)
- | V.AEndedMutBorrow _ | V.AEndedIgnoredMutBorrow _
- | V.AEndedSharedBorrow | V.AProjSharedBorrow _ ->
- (* Do nothing *)
- ()
- in
- (* Continue exploring *)
- super#visit_aborrow_content env bc
- end
- in
-
- (* Visit *)
- borrows_visitor#visit_eval_ctx () ctx;
-
- (* Debugging *)
- log#ldebug
- (lazy ("\nAbout to check context invariant:\n" ^ context_to_string ()));
-
- (* Finally, check that everything is consistant *)
- (* First, check all the ignored loans are present at the proper place *)
- List.iter
- (fun (rkind, bid) ->
- let info = find_info bid in
- assert (info.loan_kind = rkind))
- !ignored_loans;
-
- (* Then, check the borrow infos *)
- V.BorrowId.Map.iter
- (fun _ info ->
- (* Note that we can't directly compare the sets - I guess they are
- * different depending on the order in which we add the elements... *)
- assert (
- V.BorrowId.Set.elements info.loan_ids
- = V.BorrowId.Set.elements info.borrow_ids);
- match info.loan_kind with
- | T.Mut -> assert (V.BorrowId.Set.cardinal info.loan_ids = 1)
- | T.Shared -> ())
- !borrows_infos
-
-(** Check that:
- - borrows/loans can't contain ⊥ or inactivated mut borrows
- - shared loans can't contain mutable loans
- *)
-let check_borrowed_values_invariant (config : C.config) (ctx : C.eval_ctx) :
- unit =
- let visitor =
- object
- inherit [_] C.iter_eval_ctx as super
-
- method! visit_Bottom info =
- (* No ⊥ inside borrowed values *)
- assert (config.C.allow_bottom_below_borrow || not info.outer_borrow)
-
- method! visit_ABottom _info =
- (* ⊥ inside an abstraction is not the same as in a regular value *)
- ()
-
- method! visit_loan_content info lc =
- (* Update the info *)
- let info =
- match lc with
- | V.SharedLoan (_, _) -> set_outer_shared info
- | V.MutLoan _ ->
- (* No mutable loan inside a shared loan *)
- assert (not info.outer_shared);
- set_outer_mut info
- in
- (* Continue exploring *)
- super#visit_loan_content info lc
-
- method! visit_borrow_content info bc =
- (* Update the info *)
- let info =
- match bc with
- | V.SharedBorrow _ -> set_outer_shared info
- | V.InactivatedMutBorrow _ ->
- assert (not info.outer_borrow);
- set_outer_shared info
- | V.MutBorrow (_, _) -> set_outer_mut info
- in
- (* Continue exploring *)
- super#visit_borrow_content info bc
-
- method! visit_aloan_content info lc =
- (* Update the info *)
- let info =
- match lc with
- | V.AMutLoan (_, _) -> set_outer_mut info
- | V.ASharedLoan (_, _, _) -> set_outer_shared info
- | V.AEndedMutLoan { given_back = _; child = _; given_back_meta = _ }
- ->
- set_outer_mut info
- | V.AEndedSharedLoan (_, _) -> set_outer_shared info
- | V.AIgnoredMutLoan (_, _) -> set_outer_mut info
- | V.AEndedIgnoredMutLoan
- { given_back = _; child = _; given_back_meta = _ } ->
- set_outer_mut info
- | V.AIgnoredSharedLoan _ -> set_outer_shared info
- in
- (* Continue exploring *)
- super#visit_aloan_content info lc
-
- method! visit_aborrow_content info bc =
- (* Update the info *)
- let info =
- match bc with
- | V.AMutBorrow (_, _, _) -> set_outer_mut info
- | V.ASharedBorrow _ | V.AEndedSharedBorrow -> set_outer_shared info
- | V.AIgnoredMutBorrow _ | V.AEndedMutBorrow _
- | V.AEndedIgnoredMutBorrow _ ->
- set_outer_mut info
- | V.AProjSharedBorrow _ -> set_outer_shared info
- in
- (* Continue exploring *)
- super#visit_aborrow_content info bc
- end
- in
-
- (* Explore *)
- let info = { outer_borrow = false; outer_shared = false } in
- visitor#visit_eval_ctx info ctx
-
-let check_constant_value_type (cv : V.constant_value) (ty : T.ety) : unit =
- match (cv, ty) with
- | V.Scalar sv, T.Integer int_ty -> assert (sv.int_ty = int_ty)
- | V.Bool _, T.Bool | V.Char _, T.Char | V.String _, T.Str -> ()
- | _ -> failwith "Erroneous typing"
-
-let check_typing_invariant (ctx : C.eval_ctx) : unit =
- (* TODO: the type of aloans doens't make sense: they have a type
- * of the shape [& (mut) T] where they should have type [T]...
- * This messes a bit the type invariant checks when checking the
- * children. In order to isolate the problem (for future modifications)
- * we introduce function, so that we can easily spot all the involved
- * places.
- * *)
- let aloan_get_expected_child_type (ty : 'r T.ty) : 'r T.ty =
- let _, ty, _ = ty_get_ref ty in
- ty
- in
-
- let visitor =
- object
- inherit [_] C.iter_eval_ctx as super
- method! visit_abs _ abs = super#visit_abs (Some abs) abs
-
- method! visit_typed_value info tv =
- (* Check the current pair (value, type) *)
- (match (tv.V.value, tv.V.ty) with
- | V.Concrete cv, ty -> check_constant_value_type cv ty
- (* ADT case *)
- | V.Adt av, T.Adt (T.AdtId def_id, regions, tys) ->
- (* Retrieve the definition to check the variant id, the number of
- * parameters, etc. *)
- let def = C.ctx_lookup_type_decl ctx def_id in
- (* Check the number of parameters *)
- assert (List.length regions = List.length def.region_params);
- assert (List.length tys = List.length def.type_params);
- (* Check that the variant id is consistent *)
- (match (av.V.variant_id, def.T.kind) with
- | Some variant_id, T.Enum variants ->
- assert (T.VariantId.to_int variant_id < List.length variants)
- | None, T.Struct _ -> ()
- | _ -> failwith "Erroneous typing");
- (* Check that the field types are correct *)
- let field_types =
- Subst.type_decl_get_instantiated_field_etypes def av.V.variant_id
- tys
- in
- let fields_with_types =
- List.combine av.V.field_values field_types
- in
- List.iter
- (fun ((v, ty) : V.typed_value * T.ety) -> assert (v.V.ty = ty))
- fields_with_types
- (* Tuple case *)
- | V.Adt av, T.Adt (T.Tuple, regions, tys) ->
- assert (regions = []);
- assert (av.V.variant_id = None);
- (* Check that the fields have the proper values - and check that there
- * are as many fields as field types at the same time *)
- let fields_with_types = List.combine av.V.field_values tys in
- List.iter
- (fun ((v, ty) : V.typed_value * T.ety) -> assert (v.V.ty = ty))
- fields_with_types
- (* Assumed type case *)
- | V.Adt av, T.Adt (T.Assumed aty_id, regions, tys) -> (
- assert (av.V.variant_id = None || aty_id = T.Option);
- match (aty_id, av.V.field_values, regions, tys) with
- (* Box *)
- | T.Box, [ inner_value ], [], [ inner_ty ]
- | T.Option, [ inner_value ], [], [ inner_ty ] ->
- assert (inner_value.V.ty = inner_ty)
- | T.Option, _, [], [ _ ] ->
- (* Option::None: nothing to check *)
- ()
- | T.Vec, fvs, [], [ vec_ty ] ->
- List.iter
- (fun (v : V.typed_value) -> assert (v.ty = vec_ty))
- fvs
- | _ -> failwith "Erroneous type")
- | V.Bottom, _ -> (* Nothing to check *) ()
- | V.Borrow bc, T.Ref (_, ref_ty, rkind) -> (
- match (bc, rkind) with
- | V.SharedBorrow (_, bid), T.Shared
- | V.InactivatedMutBorrow (_, bid), T.Mut -> (
- (* Lookup the borrowed value to check it has the proper type *)
- let _, glc = lookup_loan ek_all bid ctx in
- match glc with
- | Concrete (V.SharedLoan (_, sv))
- | Abstract (V.ASharedLoan (_, sv, _)) ->
- assert (sv.V.ty = ref_ty)
- | _ -> failwith "Inconsistent context")
- | V.MutBorrow (_, bv), T.Mut ->
- assert (
- (* Check that the borrowed value has the proper type *)
- bv.V.ty = ref_ty)
- | _ -> failwith "Erroneous typing")
- | V.Loan lc, ty -> (
- match lc with
- | V.SharedLoan (_, sv) -> assert (sv.V.ty = ty)
- | V.MutLoan bid -> (
- (* Lookup the borrowed value to check it has the proper type *)
- let glc = lookup_borrow ek_all bid ctx in
- match glc with
- | Concrete (V.MutBorrow (_, bv)) -> assert (bv.V.ty = ty)
- | Abstract (V.AMutBorrow (_, _, sv)) ->
- assert (Subst.erase_regions sv.V.ty = ty)
- | _ -> failwith "Inconsistent context"))
- | V.Symbolic sv, ty ->
- let ty' = Subst.erase_regions sv.V.sv_ty in
- assert (ty' = ty)
- | _ -> failwith "Erroneous typing");
- (* Continue exploring to inspect the subterms *)
- super#visit_typed_value info tv
-
- (* TODO: there is a lot of duplication with {!visit_typed_value}
- * which is quite annoying. There might be a way of factorizing
- * that by factorizing the definitions of value and avalue, but
- * the generation of visitors then doesn't work properly (TODO:
- * report that). Still, it is actually not that problematic
- * because this code shouldn't change a lot in the future,
- * so the cost of maintenance should be pretty low.
- * *)
- method! visit_typed_avalue info atv =
- (* Check the current pair (value, type) *)
- (match (atv.V.value, atv.V.ty) with
- | V.AConcrete cv, ty ->
- check_constant_value_type cv (Subst.erase_regions ty)
- (* ADT case *)
- | V.AAdt av, T.Adt (T.AdtId def_id, regions, tys) ->
- (* Retrieve the definition to check the variant id, the number of
- * parameters, etc. *)
- let def = C.ctx_lookup_type_decl ctx def_id in
- (* Check the number of parameters *)
- assert (List.length regions = List.length def.region_params);
- assert (List.length tys = List.length def.type_params);
- (* Check that the variant id is consistent *)
- (match (av.V.variant_id, def.T.kind) with
- | Some variant_id, T.Enum variants ->
- assert (T.VariantId.to_int variant_id < List.length variants)
- | None, T.Struct _ -> ()
- | _ -> failwith "Erroneous typing");
- (* Check that the field types are correct *)
- let field_types =
- Subst.type_decl_get_instantiated_field_rtypes def av.V.variant_id
- regions tys
- in
- let fields_with_types =
- List.combine av.V.field_values field_types
- in
- List.iter
- (fun ((v, ty) : V.typed_avalue * T.rty) -> assert (v.V.ty = ty))
- fields_with_types
- (* Tuple case *)
- | V.AAdt av, T.Adt (T.Tuple, regions, tys) ->
- assert (regions = []);
- assert (av.V.variant_id = None);
- (* Check that the fields have the proper values - and check that there
- * are as many fields as field types at the same time *)
- let fields_with_types = List.combine av.V.field_values tys in
- List.iter
- (fun ((v, ty) : V.typed_avalue * T.rty) -> assert (v.V.ty = ty))
- fields_with_types
- (* Assumed type case *)
- | V.AAdt av, T.Adt (T.Assumed aty_id, regions, tys) -> (
- assert (av.V.variant_id = None);
- match (aty_id, av.V.field_values, regions, tys) with
- (* Box *)
- | T.Box, [ boxed_value ], [], [ boxed_ty ] ->
- assert (boxed_value.V.ty = boxed_ty)
- | _ -> failwith "Erroneous type")
- | V.ABottom, _ -> (* Nothing to check *) ()
- | V.ABorrow bc, T.Ref (_, ref_ty, rkind) -> (
- match (bc, rkind) with
- | V.AMutBorrow (_, _, av), T.Mut ->
- (* Check that the child value has the proper type *)
- assert (av.V.ty = ref_ty)
- | V.ASharedBorrow bid, T.Shared -> (
- (* Lookup the borrowed value to check it has the proper type *)
- let _, glc = lookup_loan ek_all bid ctx in
- match glc with
- | Concrete (V.SharedLoan (_, sv))
- | Abstract (V.ASharedLoan (_, sv, _)) ->
- assert (sv.V.ty = Subst.erase_regions ref_ty)
- | _ -> failwith "Inconsistent context")
- | V.AIgnoredMutBorrow (_opt_bid, av), T.Mut ->
- assert (av.V.ty = ref_ty)
- | ( V.AEndedIgnoredMutBorrow
- { given_back_loans_proj; child; given_back_meta = _ },
- T.Mut ) ->
- assert (given_back_loans_proj.V.ty = ref_ty);
- assert (child.V.ty = ref_ty)
- | V.AProjSharedBorrow _, T.Shared -> ()
- | _ -> failwith "Inconsistent context")
- | V.ALoan lc, aty -> (
- match lc with
- | V.AMutLoan (bid, child_av) | V.AIgnoredMutLoan (bid, child_av)
- -> (
- let borrowed_aty = aloan_get_expected_child_type aty in
- assert (child_av.V.ty = borrowed_aty);
- (* Lookup the borrowed value to check it has the proper type *)
- let glc = lookup_borrow ek_all bid ctx in
- match glc with
- | Concrete (V.MutBorrow (_, bv)) ->
- assert (bv.V.ty = Subst.erase_regions borrowed_aty)
- | Abstract (V.AMutBorrow (_, _, sv)) ->
- assert (
- Subst.erase_regions sv.V.ty
- = Subst.erase_regions borrowed_aty)
- | _ -> failwith "Inconsistent context")
- | V.ASharedLoan (_, sv, child_av) | V.AEndedSharedLoan (sv, child_av)
- ->
- let borrowed_aty = aloan_get_expected_child_type aty in
- assert (sv.V.ty = Subst.erase_regions borrowed_aty);
- (* TODO: the type of aloans doesn't make sense, see above *)
- assert (child_av.V.ty = borrowed_aty)
- | V.AEndedMutLoan { given_back; child; given_back_meta = _ }
- | V.AEndedIgnoredMutLoan { given_back; child; given_back_meta = _ }
- ->
- let borrowed_aty = aloan_get_expected_child_type aty in
- assert (given_back.V.ty = borrowed_aty);
- assert (child.V.ty = borrowed_aty)
- | V.AIgnoredSharedLoan child_av ->
- assert (child_av.V.ty = aloan_get_expected_child_type aty))
- | V.ASymbolic aproj, ty -> (
- let ty1 = Subst.erase_regions ty in
- match aproj with
- | V.AProjLoans (sv, _) ->
- let ty2 = Subst.erase_regions sv.V.sv_ty in
- assert (ty1 = ty2);
- (* Also check that the symbolic values contain regions of interest -
- * otherwise they should have been reduced to [_] *)
- let abs = Option.get info in
- assert (ty_has_regions_in_set abs.regions sv.V.sv_ty)
- | V.AProjBorrows (sv, proj_ty) ->
- let ty2 = Subst.erase_regions sv.V.sv_ty in
- assert (ty1 = ty2);
- (* Also check that the symbolic values contain regions of interest -
- * otherwise they should have been reduced to [_] *)
- let abs = Option.get info in
- assert (ty_has_regions_in_set abs.regions proj_ty)
- | V.AEndedProjLoans (_msv, given_back_ls) ->
- List.iter
- (fun (_, proj) ->
- match proj with
- | V.AProjBorrows (_sv, ty') -> assert (ty' = ty)
- | V.AEndedProjBorrows _ | V.AIgnoredProjBorrows -> ()
- | _ -> failwith "Unexpected")
- given_back_ls
- | V.AEndedProjBorrows _ | V.AIgnoredProjBorrows -> ())
- | V.AIgnored, _ -> ()
- | _ -> failwith "Erroneous typing");
- (* Continue exploring to inspect the subterms *)
- super#visit_typed_avalue info atv
- end
- in
- visitor#visit_eval_ctx (None : V.abs option) ctx
-
-type proj_borrows_info = {
- abs_id : V.AbstractionId.id;
- regions : T.RegionId.Set.t;
- proj_ty : T.rty;
- as_shared_value : bool; (** True if the value is below a shared borrow *)
-}
-[@@deriving show]
-
-type proj_loans_info = {
- abs_id : V.AbstractionId.id;
- regions : T.RegionId.Set.t;
-}
-[@@deriving show]
-
-type sv_info = {
- ty : T.rty;
- env_count : int;
- aproj_borrows : proj_borrows_info list;
- aproj_loans : proj_loans_info list;
-}
-[@@deriving show]
-
-(** Check the invariants over the symbolic values.
-
- - a symbolic value can't be both in proj_borrows and in the concrete env
- (this is why we preemptively expand copyable symbolic values)
- - if a symbolic value contains regions: there is at most one occurrence
- of this value in the concrete env
- - if there is an aproj_borrows in the environment, there must also be a
- corresponding aproj_loans
- - aproj_loans are mutually disjoint
- - TODO: aproj_borrows are mutually disjoint
- - the union of the aproj_loans contains the aproj_borrows applied on the
- same symbolic values
- *)
-let check_symbolic_values (_config : C.config) (ctx : C.eval_ctx) : unit =
- (* Small utility *)
- let module M = V.SymbolicValueId.Map in
- let infos : sv_info M.t ref = ref M.empty in
- let lookup_info (sv : V.symbolic_value) : sv_info =
- match M.find_opt sv.V.sv_id !infos with
- | Some info -> info
- | None ->
- { ty = sv.sv_ty; env_count = 0; aproj_borrows = []; aproj_loans = [] }
- in
- let update_info (sv : V.symbolic_value) (info : sv_info) =
- infos := M.add sv.sv_id info !infos
- in
- let add_env_sv (sv : V.symbolic_value) : unit =
- let info = lookup_info sv in
- let info = { info with env_count = info.env_count + 1 } in
- update_info sv info
- in
- let add_aproj_borrows (sv : V.symbolic_value) abs_id regions proj_ty
- as_shared_value : unit =
- let info = lookup_info sv in
- let binfo = { abs_id; regions; proj_ty; as_shared_value } in
- let info = { info with aproj_borrows = binfo :: info.aproj_borrows } in
- update_info sv info
- in
- let add_aproj_loans (sv : V.symbolic_value) abs_id regions : unit =
- let info = lookup_info sv in
- let linfo = { abs_id; regions } in
- let info = { info with aproj_loans = linfo :: info.aproj_loans } in
- update_info sv info
- in
- (* Visitor *)
- let obj =
- object
- inherit [_] C.iter_eval_ctx as super
- method! visit_abs _ abs = super#visit_abs (Some abs) abs
- method! visit_Symbolic _ sv = add_env_sv sv
-
- method! visit_abstract_shared_borrows abs asb =
- let abs = Option.get abs in
- let visit asb =
- match asb with
- | V.AsbBorrow _ -> ()
- | AsbProjReborrows (sv, proj_ty) ->
- add_aproj_borrows sv abs.abs_id abs.regions proj_ty true
- in
- List.iter visit asb
-
- method! visit_aproj abs aproj =
- (let abs = Option.get abs in
- match aproj with
- | AProjLoans (sv, _) -> add_aproj_loans sv abs.abs_id abs.regions
- | AProjBorrows (sv, proj_ty) ->
- add_aproj_borrows sv abs.abs_id abs.regions proj_ty false
- | AEndedProjLoans _ | AEndedProjBorrows _ | AIgnoredProjBorrows -> ());
- super#visit_aproj abs aproj
- end
- in
- (* Collect the information *)
- obj#visit_eval_ctx None ctx;
- log#ldebug
- (lazy
- ("check_symbolic_values: collected information:\n"
- ^ V.SymbolicValueId.Map.to_string (Some " ") show_sv_info !infos));
- (* Check *)
- let check_info _id info =
- (* TODO: check that:
- * - the borrows are mutually disjoint
- *)
- (* A symbolic value can't be both in the regular environment and inside
- * projectors of borrows in abstractions *)
- assert (info.env_count = 0 || info.aproj_borrows = []);
- (* A symbolic value containing borrows can't be duplicated (i.e., copied):
- * it must be expanded first *)
- if ty_has_borrows ctx.type_context.type_infos info.ty then
- assert (info.env_count <= 1);
- (* A duplicated symbolic value is necessarily primitively copyable *)
- assert (info.env_count <= 1 || ty_is_primitively_copyable info.ty);
-
- assert (info.aproj_borrows = [] || info.aproj_loans <> []);
- (* At the same time:
- * - check that the loans don't intersect
- * - compute the set of regions for which we project loans
- *)
- (* Check that the loan projectors contain the region projectors *)
- let loan_regions =
- List.fold_left
- (fun regions linfo ->
- let regions =
- T.RegionId.Set.fold
- (fun rid regions ->
- assert (not (T.RegionId.Set.mem rid regions));
- T.RegionId.Set.add rid regions)
- regions linfo.regions
- in
- regions)
- T.RegionId.Set.empty info.aproj_loans
- in
- (* Check that the union of the loan projectors contains the borrow projections. *)
- List.iter
- (fun binfo ->
- assert (
- projection_contains info.ty loan_regions binfo.proj_ty binfo.regions))
- info.aproj_borrows;
- ()
- in
-
- M.iter check_info !infos
-
-let check_invariants (config : C.config) (ctx : C.eval_ctx) : unit =
- if config.C.check_invariants then (
- log#ldebug (lazy "Checking invariants");
- check_loans_borrows_relation_invariant ctx;
- check_borrowed_values_invariant config ctx;
- check_typing_invariant ctx;
- check_symbolic_values config ctx)
- else log#ldebug (lazy "Not checking invariants (check is not activated)")
-
-(** Same as {!check_invariants}, but written in CPS *)
-let cf_check_invariants (config : C.config) : cm_fun =
- fun cf ctx ->
- check_invariants config ctx;
- cf ctx
diff --git a/src/LlbcAst.ml b/src/LlbcAst.ml
deleted file mode 100644
index 1b08f1ea..00000000
--- a/src/LlbcAst.ml
+++ /dev/null
@@ -1,205 +0,0 @@
-open Names
-open Types
-open Values
-open Expressions
-open Identifiers
-module FunDeclId = IdGen ()
-module GlobalDeclId = IdGen ()
-open Meta
-
-(** A variable, as used in a function definition *)
-type var = {
- index : VarId.id; (** Unique variable identifier *)
- name : string option;
- var_ty : ety;
- (** The variable type - erased type, because variables are not used
- ** in function signatures: they are only used to declare the list of
- ** variables manipulated by a function body *)
-}
-[@@deriving show]
-
-type assumed_fun_id =
- | Replace (** [core::mem::replace] *)
- | BoxNew
- | BoxDeref (** [core::ops::deref::Deref::<alloc::boxed::Box<T>>::deref] *)
- | BoxDerefMut
- (** [core::ops::deref::DerefMut::<alloc::boxed::Box<T>>::deref_mut] *)
- | BoxFree
- | VecNew
- | VecPush
- | VecInsert
- | VecLen
- | VecIndex (** [core::ops::index::Index::index<alloc::vec::Vec<T>, usize>] *)
- | VecIndexMut
- (** [core::ops::index::IndexMut::index_mut<alloc::vec::Vec<T>, usize>] *)
-[@@deriving show, ord]
-
-type fun_id = Regular of FunDeclId.id | Assumed of assumed_fun_id
-[@@deriving show, ord]
-
-type global_assignment = { dst : VarId.id; global : GlobalDeclId.id }
-[@@deriving show]
-
-type assertion = { cond : operand; expected : bool } [@@deriving show]
-
-type abs_region_group = (AbstractionId.id, RegionId.id) g_region_group
-[@@deriving show]
-
-type abs_region_groups = (AbstractionId.id, RegionId.id) g_region_groups
-[@@deriving show]
-
-(** A function signature, as used when declaring functions *)
-type fun_sig = {
- region_params : region_var list;
- num_early_bound_regions : int;
- regions_hierarchy : region_var_groups;
- type_params : type_var list;
- inputs : sty list;
- output : sty;
-}
-[@@deriving show]
-
-(** A function signature, after instantiation *)
-type inst_fun_sig = {
- regions_hierarchy : abs_region_groups;
- inputs : rty list;
- output : rty;
-}
-[@@deriving show]
-
-type call = {
- func : fun_id;
- region_args : erased_region list;
- type_args : ety list;
- args : operand list;
- dest : place;
-}
-[@@deriving show]
-
-(** Ancestor for [typed_value] iter visitor *)
-class ['self] iter_statement_base =
- object (_self : 'self)
- inherit [_] VisitorsRuntime.iter
-
- method visit_global_assignment : 'env -> global_assignment -> unit =
- fun _ _ -> ()
-
- method visit_meta : 'env -> meta -> unit = fun _ _ -> ()
- method visit_place : 'env -> place -> unit = fun _ _ -> ()
- method visit_rvalue : 'env -> rvalue -> unit = fun _ _ -> ()
- method visit_id : 'env -> VariantId.id -> unit = fun _ _ -> ()
- method visit_assertion : 'env -> assertion -> unit = fun _ _ -> ()
- method visit_operand : 'env -> operand -> unit = fun _ _ -> ()
- method visit_call : 'env -> call -> unit = fun _ _ -> ()
- method visit_integer_type : 'env -> integer_type -> unit = fun _ _ -> ()
- method visit_scalar_value : 'env -> scalar_value -> unit = fun _ _ -> ()
- end
-
-(** Ancestor for [typed_value] map visitor *)
-class ['self] map_statement_base =
- object (_self : 'self)
- inherit [_] VisitorsRuntime.map
-
- method visit_global_assignment
- : 'env -> global_assignment -> global_assignment =
- fun _ x -> x
-
- method visit_meta : 'env -> meta -> meta = fun _ x -> x
- method visit_place : 'env -> place -> place = fun _ x -> x
- method visit_rvalue : 'env -> rvalue -> rvalue = fun _ x -> x
- method visit_id : 'env -> VariantId.id -> VariantId.id = fun _ x -> x
- method visit_assertion : 'env -> assertion -> assertion = fun _ x -> x
- method visit_operand : 'env -> operand -> operand = fun _ x -> x
- method visit_call : 'env -> call -> call = fun _ x -> x
-
- method visit_integer_type : 'env -> integer_type -> integer_type =
- fun _ x -> x
-
- method visit_scalar_value : 'env -> scalar_value -> scalar_value =
- fun _ x -> x
- end
-
-type statement = {
- meta : meta; (** The statement meta-data *)
- content : raw_statement; (** The statement itself *)
-}
-
-and raw_statement =
- | Assign of place * rvalue
- | AssignGlobal of global_assignment
- | FakeRead of place
- | SetDiscriminant of place * VariantId.id
- | Drop of place
- | Assert of assertion
- | Call of call
- | Panic
- | Return
- | Break of int
- (** Break to (outer) loop. The [int] identifies the loop to break to:
- * 0: break to the first outer loop (the current loop)
- * 1: break to the second outer loop
- * ...
- *)
- | Continue of int
- (** Continue to (outer) loop. The loop identifier works
- the same way as for {!Break} *)
- | Nop
- | Sequence of statement * statement
- | Switch of operand * switch_targets
- | Loop of statement
-
-and switch_targets =
- | If of statement * statement (** Gives the "if" and "else" blocks *)
- | SwitchInt of integer_type * (scalar_value list * statement) list * statement
- (** The targets for a switch over an integer are:
- - the list [(matched values, statement to execute)]
- We need a list for the matched values in case we do something like this:
- [switch n { 0 | 1 => ..., _ => ... }]
- - the "otherwise" statement
- Also note that we precise the type of the integer (uint32, int64, etc.)
- which we switch on. *)
-[@@deriving
- show,
- visitors
- {
- name = "iter_statement";
- variety = "iter";
- ancestors = [ "iter_statement_base" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- concrete = true;
- },
- visitors
- {
- name = "map_statement";
- variety = "map";
- ancestors = [ "map_statement_base" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- concrete = true;
- }]
-
-type fun_body = {
- meta : meta;
- arg_count : int;
- locals : var list;
- body : statement;
-}
-[@@deriving show]
-
-type fun_decl = {
- def_id : FunDeclId.id;
- meta : meta;
- name : fun_name;
- signature : fun_sig;
- body : fun_body option;
- is_global_decl_body : bool;
-}
-[@@deriving show]
-
-type global_decl = {
- def_id : GlobalDeclId.id;
- meta : meta;
- body_id : FunDeclId.id;
- name : global_name;
- ty : ety;
-}
-[@@deriving show]
diff --git a/src/LlbcAstUtils.ml b/src/LlbcAstUtils.ml
deleted file mode 100644
index 46711d0a..00000000
--- a/src/LlbcAstUtils.ml
+++ /dev/null
@@ -1,73 +0,0 @@
-open LlbcAst
-open Utils
-module T = Types
-
-(** Check if a {!type:LlbcAst.statement} contains loops *)
-let statement_has_loops (st : statement) : bool =
- let obj =
- object
- inherit [_] iter_statement
- method! visit_Loop _ _ = raise Found
- end
- in
- try
- obj#visit_statement () st;
- false
- with Found -> true
-
-(** Check if a {!type:LlbcAst.fun_decl} contains loops *)
-let fun_decl_has_loops (fd : fun_decl) : bool =
- match fd.body with
- | Some body -> statement_has_loops body.body
- | None -> false
-
-let lookup_fun_sig (fun_id : fun_id) (fun_decls : fun_decl FunDeclId.Map.t) :
- fun_sig =
- match fun_id with
- | Regular id -> (FunDeclId.Map.find id fun_decls).signature
- | Assumed aid -> Assumed.get_assumed_sig aid
-
-let lookup_fun_name (fun_id : fun_id) (fun_decls : fun_decl FunDeclId.Map.t) :
- Names.fun_name =
- match fun_id with
- | Regular id -> (FunDeclId.Map.find id fun_decls).name
- | Assumed aid -> Assumed.get_assumed_name aid
-
-(** Small utility: list the transitive parents of a region var group.
- We don't do that in an efficient manner, but it doesn't matter.
-
- TODO: rename to "list_ancestors_..."
-
- This list *doesn't* include the current region.
- *)
-let rec list_parent_region_groups (sg : fun_sig) (gid : T.RegionGroupId.id) :
- T.RegionGroupId.Set.t =
- let rg = T.RegionGroupId.nth sg.regions_hierarchy gid in
- let parents =
- List.fold_left
- (fun s gid ->
- (* Compute the parents *)
- let parents = list_parent_region_groups sg gid in
- (* Parents U current region *)
- let parents = T.RegionGroupId.Set.add gid parents in
- (* Make the union with the accumulator *)
- T.RegionGroupId.Set.union s parents)
- T.RegionGroupId.Set.empty rg.parents
- in
- parents
-
-(** Small utility: same as {!list_parent_region_groups}, but returns an ordered list. *)
-let list_ordered_parent_region_groups (sg : fun_sig) (gid : T.RegionGroupId.id)
- : T.RegionGroupId.id list =
- let pset = list_parent_region_groups sg gid in
- let parents =
- List.filter
- (fun (rg : T.region_var_group) -> T.RegionGroupId.Set.mem rg.id pset)
- sg.regions_hierarchy
- in
- let parents = List.map (fun (rg : T.region_var_group) -> rg.id) parents in
- parents
-
-let fun_body_get_input_vars (fbody : fun_body) : var list =
- let locals = List.tl fbody.locals in
- Collections.List.prefix fbody.arg_count locals
diff --git a/src/LlbcOfJson.ml b/src/LlbcOfJson.ml
deleted file mode 100644
index 79c9b756..00000000
--- a/src/LlbcOfJson.ml
+++ /dev/null
@@ -1,915 +0,0 @@
-(** Functions to load LLBC ASTs from json.
-
- Initially, we used [ppx_derive_yojson] to automate this.
- However, [ppx_derive_yojson] expects formatting to be slightly
- different from what [serde_rs] generates (because it uses [Yojson.Safe.t]
- and not [Yojson.Basic.t]).
-
- TODO: we should check all that the integer values are in the proper range
- *)
-
-open Yojson.Basic
-open Names
-open OfJsonBasic
-open Identifiers
-open Meta
-module T = Types
-module V = Values
-module S = Scalars
-module E = Expressions
-module A = LlbcAst
-module TU = TypesUtils
-module AU = LlbcAstUtils
-module LocalFileId = IdGen ()
-module VirtualFileId = IdGen ()
-
-(** The default logger *)
-let log = Logging.llbc_of_json_logger
-
-(** A file identifier *)
-type file_id = LocalId of LocalFileId.id | VirtualId of VirtualFileId.id
-[@@deriving show, ord]
-
-module OrderedIdToFile : Collections.OrderedType with type t = file_id = struct
- type t = file_id
-
- let compare fid0 fid1 = compare_file_id fid0 fid1
-
- let to_string id =
- match id with
- | LocalId id -> "Local " ^ LocalFileId.to_string id
- | VirtualId id -> "Virtual " ^ VirtualFileId.to_string id
-
- let pp_t fmt x = Format.pp_print_string fmt (to_string x)
- let show_t x = to_string x
-end
-
-module IdToFile = Collections.MakeMap (OrderedIdToFile)
-
-type id_to_file_map = file_name IdToFile.t
-
-let file_id_of_json (js : json) : (file_id, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("LocalId", id) ] ->
- let* id = LocalFileId.id_of_json id in
- Ok (LocalId id)
- | `Assoc [ ("VirtualId", id) ] ->
- let* id = VirtualFileId.id_of_json id in
- Ok (VirtualId id)
- | _ -> Error "")
-
-let file_name_of_json (js : json) : (file_name, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("Virtual", name) ] ->
- let* name = string_of_json name in
- Ok (Virtual name)
- | `Assoc [ ("Local", name) ] ->
- let* name = string_of_json name in
- Ok (Local name)
- | _ -> Error "")
-
-(** Deserialize a map from file id to file name.
-
- In the serialized LLBC, the files in the loc spans are refered to by their
- ids, in order to save space. In a functional language like OCaml this is
- not necessary: we thus replace the file ids by the file name themselves in
- the AST.
- The "id to file" map is thus only used in the deserialization process.
- *)
-let id_to_file_of_json (js : json) : (id_to_file_map, string) result =
- combine_error_msgs js __FUNCTION__
- ((* The map is stored as a list of pairs (key, value): we deserialize
- * this list then convert it to a map *)
- let* key_values =
- list_of_json (pair_of_json file_id_of_json file_name_of_json) js
- in
- Ok (IdToFile.of_list key_values))
-
-let loc_of_json (js : json) : (loc, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("line", line); ("col", col) ] ->
- let* line = int_of_json line in
- let* col = int_of_json col in
- Ok { line; col }
- | _ -> Error "")
-
-let span_of_json (id_to_file : id_to_file_map) (js : json) :
- (span, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("file_id", file_id); ("beg", beg_loc); ("end", end_loc) ] ->
- let* file_id = file_id_of_json file_id in
- let file = IdToFile.find file_id id_to_file in
- let* beg_loc = loc_of_json beg_loc in
- let* end_loc = loc_of_json end_loc in
- Ok { file; beg_loc; end_loc }
- | _ -> Error "")
-
-let meta_of_json (id_to_file : id_to_file_map) (js : json) :
- (meta, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("span", span); ("generated_from_span", generated_from_span) ] ->
- let* span = span_of_json id_to_file span in
- let* generated_from_span =
- option_of_json (span_of_json id_to_file) generated_from_span
- in
- Ok { span; generated_from_span }
- | _ -> Error "")
-
-let path_elem_of_json (js : json) : (path_elem, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("Ident", name) ] ->
- let* name = string_of_json name in
- Ok (Ident name)
- | `Assoc [ ("Disambiguator", d) ] ->
- let* d = Disambiguator.id_of_json d in
- Ok (Disambiguator d)
- | _ -> Error "")
-
-let name_of_json (js : json) : (name, string) result =
- combine_error_msgs js __FUNCTION__ (list_of_json path_elem_of_json js)
-
-let fun_name_of_json (js : json) : (fun_name, string) result =
- combine_error_msgs js __FUNCTION__ (name_of_json js)
-
-let type_var_of_json (js : json) : (T.type_var, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("index", index); ("name", name) ] ->
- let* index = T.TypeVarId.id_of_json index in
- let* name = string_of_json name in
- Ok { T.index; name }
- | _ -> Error "")
-
-let region_var_of_json (js : json) : (T.region_var, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("index", index); ("name", name) ] ->
- let* index = T.RegionVarId.id_of_json index in
- let* name = string_option_of_json name in
- Ok { T.index; name }
- | _ -> Error "")
-
-let region_of_json (js : json) : (T.RegionVarId.id T.region, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `String "Static" -> Ok T.Static
- | `Assoc [ ("Var", rid) ] ->
- let* rid = T.RegionVarId.id_of_json rid in
- Ok (T.Var rid)
- | _ -> Error "")
-
-let erased_region_of_json (js : json) : (T.erased_region, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with `String "Erased" -> Ok T.Erased | _ -> Error "")
-
-let integer_type_of_json (js : json) : (T.integer_type, string) result =
- match js with
- | `String "Isize" -> Ok T.Isize
- | `String "I8" -> Ok T.I8
- | `String "I16" -> Ok T.I16
- | `String "I32" -> Ok T.I32
- | `String "I64" -> Ok T.I64
- | `String "I128" -> Ok T.I128
- | `String "Usize" -> Ok T.Usize
- | `String "U8" -> Ok T.U8
- | `String "U16" -> Ok T.U16
- | `String "U32" -> Ok T.U32
- | `String "U64" -> Ok T.U64
- | `String "U128" -> Ok T.U128
- | _ -> Error ("integer_type_of_json failed on: " ^ show js)
-
-let ref_kind_of_json (js : json) : (T.ref_kind, string) result =
- match js with
- | `String "Mut" -> Ok T.Mut
- | `String "Shared" -> Ok T.Shared
- | _ -> Error ("ref_kind_of_json failed on: " ^ show js)
-
-let assumed_ty_of_json (js : json) : (T.assumed_ty, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `String "Box" -> Ok T.Box
- | `String "Vec" -> Ok T.Vec
- | `String "Option" -> Ok T.Option
- | _ -> Error "")
-
-let type_id_of_json (js : json) : (T.type_id, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("Adt", id) ] ->
- let* id = T.TypeDeclId.id_of_json id in
- Ok (T.AdtId id)
- | `String "Tuple" -> Ok T.Tuple
- | `Assoc [ ("Assumed", aty) ] ->
- let* aty = assumed_ty_of_json aty in
- Ok (T.Assumed aty)
- | _ -> Error "")
-
-let rec ty_of_json (r_of_json : json -> ('r, string) result) (js : json) :
- ('r T.ty, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("Adt", `List [ id; regions; types ]) ] ->
- let* id = type_id_of_json id in
- let* regions = list_of_json r_of_json regions in
- let* types = list_of_json (ty_of_json r_of_json) types in
- (* Sanity check *)
- (match id with T.Tuple -> assert (List.length regions = 0) | _ -> ());
- Ok (T.Adt (id, regions, types))
- | `Assoc [ ("TypeVar", `List [ id ]) ] ->
- let* id = T.TypeVarId.id_of_json id in
- Ok (T.TypeVar id)
- | `String "Bool" -> Ok Bool
- | `String "Char" -> Ok Char
- | `String "`Never" -> Ok Never
- | `Assoc [ ("Integer", `List [ int_ty ]) ] ->
- let* int_ty = integer_type_of_json int_ty in
- Ok (T.Integer int_ty)
- | `String "Str" -> Ok Str
- | `Assoc [ ("Array", `List [ ty ]) ] ->
- let* ty = ty_of_json r_of_json ty in
- Ok (T.Array ty)
- | `Assoc [ ("Slice", `List [ ty ]) ] ->
- let* ty = ty_of_json r_of_json ty in
- Ok (T.Slice ty)
- | `Assoc [ ("Ref", `List [ region; ty; ref_kind ]) ] ->
- let* region = r_of_json region in
- let* ty = ty_of_json r_of_json ty in
- let* ref_kind = ref_kind_of_json ref_kind in
- Ok (T.Ref (region, ty, ref_kind))
- | _ -> Error "")
-
-let sty_of_json (js : json) : (T.sty, string) result =
- combine_error_msgs js __FUNCTION__ (ty_of_json region_of_json js)
-
-let ety_of_json (js : json) : (T.ety, string) result =
- combine_error_msgs js __FUNCTION__ (ty_of_json erased_region_of_json js)
-
-let field_of_json (id_to_file : id_to_file_map) (js : json) :
- (T.field, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("meta", meta); ("name", name); ("ty", ty) ] ->
- let* meta = meta_of_json id_to_file meta in
- let* name = option_of_json string_of_json name in
- let* ty = sty_of_json ty in
- Ok { T.meta; field_name = name; field_ty = ty }
- | _ -> Error "")
-
-let variant_of_json (id_to_file : id_to_file_map) (js : json) :
- (T.variant, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("meta", meta); ("name", name); ("fields", fields) ] ->
- let* meta = meta_of_json id_to_file meta in
- let* name = string_of_json name in
- let* fields = list_of_json (field_of_json id_to_file) fields in
- Ok { T.meta; variant_name = name; fields }
- | _ -> Error "")
-
-let type_decl_kind_of_json (id_to_file : id_to_file_map) (js : json) :
- (T.type_decl_kind, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("Struct", fields) ] ->
- let* fields = list_of_json (field_of_json id_to_file) fields in
- Ok (T.Struct fields)
- | `Assoc [ ("Enum", variants) ] ->
- let* variants = list_of_json (variant_of_json id_to_file) variants in
- Ok (T.Enum variants)
- | `String "Opaque" -> Ok T.Opaque
- | _ -> Error "")
-
-let region_var_group_of_json (js : json) : (T.region_var_group, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("id", id); ("regions", regions); ("parents", parents) ] ->
- let* id = T.RegionGroupId.id_of_json id in
- let* regions = list_of_json T.RegionVarId.id_of_json regions in
- let* parents = list_of_json T.RegionGroupId.id_of_json parents in
- Ok { T.id; regions; parents }
- | _ -> Error "")
-
-let region_var_groups_of_json (js : json) : (T.region_var_groups, string) result
- =
- combine_error_msgs js __FUNCTION__ (list_of_json region_var_group_of_json js)
-
-let type_decl_of_json (id_to_file : id_to_file_map) (js : json) :
- (T.type_decl, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc
- [
- ("def_id", def_id);
- ("meta", meta);
- ("name", name);
- ("region_params", region_params);
- ("type_params", type_params);
- ("regions_hierarchy", regions_hierarchy);
- ("kind", kind);
- ] ->
- let* def_id = T.TypeDeclId.id_of_json def_id in
- let* meta = meta_of_json id_to_file meta in
- let* name = name_of_json name in
- let* region_params = list_of_json region_var_of_json region_params in
- let* type_params = list_of_json type_var_of_json type_params in
- let* kind = type_decl_kind_of_json id_to_file kind in
- let* regions_hierarchy = region_var_groups_of_json regions_hierarchy in
- Ok
- {
- T.def_id;
- meta;
- name;
- region_params;
- type_params;
- kind;
- regions_hierarchy;
- }
- | _ -> Error "")
-
-let var_of_json (js : json) : (A.var, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("index", index); ("name", name); ("ty", ty) ] ->
- let* index = V.VarId.id_of_json index in
- let* name = string_option_of_json name in
- let* var_ty = ety_of_json ty in
- Ok { A.index; name; var_ty }
- | _ -> Error "")
-
-let big_int_of_json (js : json) : (V.big_int, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Int i -> Ok (Z.of_int i)
- | `String is -> Ok (Z.of_string is)
- | _ -> Error "")
-
-(** Deserialize a {!V.scalar_value} from JSON and **check the ranges**.
-
- Note that in practice we also check that the values are in range
- in the interpreter functions. Still, it doesn't cost much to be
- a bit conservative.
- *)
-let scalar_value_of_json (js : json) : (V.scalar_value, string) result =
- let res =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("Isize", `List [ bi ]) ] ->
- let* bi = big_int_of_json bi in
- Ok { V.value = bi; int_ty = Isize }
- | `Assoc [ ("I8", `List [ bi ]) ] ->
- let* bi = big_int_of_json bi in
- Ok { V.value = bi; int_ty = I8 }
- | `Assoc [ ("I16", `List [ bi ]) ] ->
- let* bi = big_int_of_json bi in
- Ok { V.value = bi; int_ty = I16 }
- | `Assoc [ ("I32", `List [ bi ]) ] ->
- let* bi = big_int_of_json bi in
- Ok { V.value = bi; int_ty = I32 }
- | `Assoc [ ("I64", `List [ bi ]) ] ->
- let* bi = big_int_of_json bi in
- Ok { V.value = bi; int_ty = I64 }
- | `Assoc [ ("I128", `List [ bi ]) ] ->
- let* bi = big_int_of_json bi in
- Ok { V.value = bi; int_ty = I128 }
- | `Assoc [ ("Usize", `List [ bi ]) ] ->
- let* bi = big_int_of_json bi in
- Ok { V.value = bi; int_ty = Usize }
- | `Assoc [ ("U8", `List [ bi ]) ] ->
- let* bi = big_int_of_json bi in
- Ok { V.value = bi; int_ty = U8 }
- | `Assoc [ ("U16", `List [ bi ]) ] ->
- let* bi = big_int_of_json bi in
- Ok { V.value = bi; int_ty = U16 }
- | `Assoc [ ("U32", `List [ bi ]) ] ->
- let* bi = big_int_of_json bi in
- Ok { V.value = bi; int_ty = U32 }
- | `Assoc [ ("U64", `List [ bi ]) ] ->
- let* bi = big_int_of_json bi in
- Ok { V.value = bi; int_ty = U64 }
- | `Assoc [ ("U128", `List [ bi ]) ] ->
- let* bi = big_int_of_json bi in
- Ok { V.value = bi; int_ty = U128 }
- | _ -> Error "")
- in
- match res with
- | Error _ -> res
- | Ok sv ->
- if not (S.check_scalar_value_in_range sv) then (
- log#serror ("Scalar value not in range: " ^ V.show_scalar_value sv);
- raise (Failure ("Scalar value not in range: " ^ V.show_scalar_value sv)));
- res
-
-let field_proj_kind_of_json (js : json) : (E.field_proj_kind, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("ProjAdt", `List [ def_id; opt_variant_id ]) ] ->
- let* def_id = T.TypeDeclId.id_of_json def_id in
- let* opt_variant_id =
- option_of_json T.VariantId.id_of_json opt_variant_id
- in
- Ok (E.ProjAdt (def_id, opt_variant_id))
- | `Assoc [ ("ProjTuple", i) ] ->
- let* i = int_of_json i in
- Ok (E.ProjTuple i)
- | `Assoc [ ("ProjOption", variant_id) ] ->
- let* variant_id = T.VariantId.id_of_json variant_id in
- Ok (E.ProjOption variant_id)
- | _ -> Error "")
-
-let projection_elem_of_json (js : json) : (E.projection_elem, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `String "Deref" -> Ok E.Deref
- | `String "DerefBox" -> Ok E.DerefBox
- | `Assoc [ ("Field", `List [ proj_kind; field_id ]) ] ->
- let* proj_kind = field_proj_kind_of_json proj_kind in
- let* field_id = T.FieldId.id_of_json field_id in
- Ok (E.Field (proj_kind, field_id))
- | _ -> Error ("projection_elem_of_json failed on:" ^ show js))
-
-let projection_of_json (js : json) : (E.projection, string) result =
- combine_error_msgs js __FUNCTION__ (list_of_json projection_elem_of_json js)
-
-let place_of_json (js : json) : (E.place, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("var_id", var_id); ("projection", projection) ] ->
- let* var_id = V.VarId.id_of_json var_id in
- let* projection = projection_of_json projection in
- Ok { E.var_id; projection }
- | _ -> Error "")
-
-let borrow_kind_of_json (js : json) : (E.borrow_kind, string) result =
- match js with
- | `String "Shared" -> Ok E.Shared
- | `String "Mut" -> Ok E.Mut
- | `String "TwoPhaseMut" -> Ok E.TwoPhaseMut
- | _ -> Error ("borrow_kind_of_json failed on:" ^ show js)
-
-let unop_of_json (js : json) : (E.unop, string) result =
- match js with
- | `String "Not" -> Ok E.Not
- | `String "Neg" -> Ok E.Neg
- | `Assoc [ ("Cast", `List [ src_ty; tgt_ty ]) ] ->
- let* src_ty = integer_type_of_json src_ty in
- let* tgt_ty = integer_type_of_json tgt_ty in
- Ok (E.Cast (src_ty, tgt_ty))
- | _ -> Error ("unop_of_json failed on:" ^ show js)
-
-let binop_of_json (js : json) : (E.binop, string) result =
- match js with
- | `String "BitXor" -> Ok E.BitXor
- | `String "BitAnd" -> Ok E.BitAnd
- | `String "BitOr" -> Ok E.BitOr
- | `String "Eq" -> Ok E.Eq
- | `String "Lt" -> Ok E.Lt
- | `String "Le" -> Ok E.Le
- | `String "Ne" -> Ok E.Ne
- | `String "Ge" -> Ok E.Ge
- | `String "Gt" -> Ok E.Gt
- | `String "Div" -> Ok E.Div
- | `String "Rem" -> Ok E.Rem
- | `String "Add" -> Ok E.Add
- | `String "Sub" -> Ok E.Sub
- | `String "Mul" -> Ok E.Mul
- | `String "Shl" -> Ok E.Shl
- | `String "Shr" -> Ok E.Shr
- | _ -> Error ("binop_of_json failed on:" ^ show js)
-
-let constant_value_of_json (js : json) : (V.constant_value, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("Scalar", scalar_value) ] ->
- let* scalar_value = scalar_value_of_json scalar_value in
- Ok (V.Scalar scalar_value)
- | `Assoc [ ("Bool", v) ] ->
- let* v = bool_of_json v in
- Ok (V.Bool v)
- | `Assoc [ ("Char", v) ] ->
- let* v = char_of_json v in
- Ok (V.Char v)
- | `Assoc [ ("String", v) ] ->
- let* v = string_of_json v in
- Ok (V.String v)
- | _ -> Error "")
-
-let operand_of_json (js : json) : (E.operand, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("Copy", place) ] ->
- let* place = place_of_json place in
- Ok (E.Copy place)
- | `Assoc [ ("Move", place) ] ->
- let* place = place_of_json place in
- Ok (E.Move place)
- | `Assoc [ ("Const", `List [ ty; cv ]) ] ->
- let* ty = ety_of_json ty in
- let* cv = constant_value_of_json cv in
- Ok (E.Constant (ty, cv))
- | _ -> Error "")
-
-let aggregate_kind_of_json (js : json) : (E.aggregate_kind, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `String "AggregatedTuple" -> Ok E.AggregatedTuple
- | `Assoc [ ("AggregatedOption", `List [ variant_id; ty ]) ] ->
- let* variant_id = T.VariantId.id_of_json variant_id in
- let* ty = ety_of_json ty in
- Ok (E.AggregatedOption (variant_id, ty))
- | `Assoc [ ("AggregatedAdt", `List [ id; opt_variant_id; regions; tys ]) ]
- ->
- let* id = T.TypeDeclId.id_of_json id in
- let* opt_variant_id =
- option_of_json T.VariantId.id_of_json opt_variant_id
- in
- let* regions = list_of_json erased_region_of_json regions in
- let* tys = list_of_json ety_of_json tys in
- Ok (E.AggregatedAdt (id, opt_variant_id, regions, tys))
- | _ -> Error "")
-
-let rvalue_of_json (js : json) : (E.rvalue, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("Use", op) ] ->
- let* op = operand_of_json op in
- Ok (E.Use op)
- | `Assoc [ ("Ref", `List [ place; borrow_kind ]) ] ->
- let* place = place_of_json place in
- let* borrow_kind = borrow_kind_of_json borrow_kind in
- Ok (E.Ref (place, borrow_kind))
- | `Assoc [ ("UnaryOp", `List [ unop; op ]) ] ->
- let* unop = unop_of_json unop in
- let* op = operand_of_json op in
- Ok (E.UnaryOp (unop, op))
- | `Assoc [ ("BinaryOp", `List [ binop; op1; op2 ]) ] ->
- let* binop = binop_of_json binop in
- let* op1 = operand_of_json op1 in
- let* op2 = operand_of_json op2 in
- Ok (E.BinaryOp (binop, op1, op2))
- | `Assoc [ ("Discriminant", place) ] ->
- let* place = place_of_json place in
- Ok (E.Discriminant place)
- | `Assoc [ ("Aggregate", `List [ aggregate_kind; ops ]) ] ->
- let* aggregate_kind = aggregate_kind_of_json aggregate_kind in
- let* ops = list_of_json operand_of_json ops in
- Ok (E.Aggregate (aggregate_kind, ops))
- | _ -> Error "")
-
-let assumed_fun_id_of_json (js : json) : (A.assumed_fun_id, string) result =
- match js with
- | `String "Replace" -> Ok A.Replace
- | `String "BoxNew" -> Ok A.BoxNew
- | `String "BoxDeref" -> Ok A.BoxDeref
- | `String "BoxDerefMut" -> Ok A.BoxDerefMut
- | `String "BoxFree" -> Ok A.BoxFree
- | `String "VecNew" -> Ok A.VecNew
- | `String "VecPush" -> Ok A.VecPush
- | `String "VecInsert" -> Ok A.VecInsert
- | `String "VecLen" -> Ok A.VecLen
- | `String "VecIndex" -> Ok A.VecIndex
- | `String "VecIndexMut" -> Ok A.VecIndexMut
- | _ -> Error ("assumed_fun_id_of_json failed on:" ^ show js)
-
-let fun_id_of_json (js : json) : (A.fun_id, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("Regular", id) ] ->
- let* id = A.FunDeclId.id_of_json id in
- Ok (A.Regular id)
- | `Assoc [ ("Assumed", fid) ] ->
- let* fid = assumed_fun_id_of_json fid in
- Ok (A.Assumed fid)
- | _ -> Error "")
-
-let assertion_of_json (js : json) : (A.assertion, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("cond", cond); ("expected", expected) ] ->
- let* cond = operand_of_json cond in
- let* expected = bool_of_json expected in
- Ok { A.cond; expected }
- | _ -> Error "")
-
-let fun_sig_of_json (js : json) : (A.fun_sig, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc
- [
- ("region_params", region_params);
- ("num_early_bound_regions", num_early_bound_regions);
- ("regions_hierarchy", regions_hierarchy);
- ("type_params", type_params);
- ("inputs", inputs);
- ("output", output);
- ] ->
- let* region_params = list_of_json region_var_of_json region_params in
- let* num_early_bound_regions = int_of_json num_early_bound_regions in
- let* regions_hierarchy = region_var_groups_of_json regions_hierarchy in
- let* type_params = list_of_json type_var_of_json type_params in
- let* inputs = list_of_json sty_of_json inputs in
- let* output = sty_of_json output in
- Ok
- {
- A.region_params;
- num_early_bound_regions;
- regions_hierarchy;
- type_params;
- inputs;
- output;
- }
- | _ -> Error "")
-
-let call_of_json (js : json) : (A.call, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc
- [
- ("func", func);
- ("region_args", region_args);
- ("type_args", type_args);
- ("args", args);
- ("dest", dest);
- ] ->
- let* func = fun_id_of_json func in
- let* region_args = list_of_json erased_region_of_json region_args in
- let* type_args = list_of_json ety_of_json type_args in
- let* args = list_of_json operand_of_json args in
- let* dest = place_of_json dest in
- Ok { A.func; region_args; type_args; args; dest }
- | _ -> Error "")
-
-let rec statement_of_json (id_to_file : id_to_file_map) (js : json) :
- (A.statement, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("meta", meta); ("content", content) ] ->
- let* meta = meta_of_json id_to_file meta in
- let* content = raw_statement_of_json id_to_file content in
- Ok { A.meta; content }
- | _ -> Error "")
-
-and raw_statement_of_json (id_to_file : id_to_file_map) (js : json) :
- (A.raw_statement, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("Assign", `List [ place; rvalue ]) ] ->
- let* place = place_of_json place in
- let* rvalue = rvalue_of_json rvalue in
- Ok (A.Assign (place, rvalue))
- | `Assoc [ ("AssignGlobal", `List [ dst; global ]) ] ->
- let* dst = V.VarId.id_of_json dst in
- let* global = A.GlobalDeclId.id_of_json global in
- Ok (A.AssignGlobal { dst; global })
- | `Assoc [ ("FakeRead", place) ] ->
- let* place = place_of_json place in
- Ok (A.FakeRead place)
- | `Assoc [ ("SetDiscriminant", `List [ place; variant_id ]) ] ->
- let* place = place_of_json place in
- let* variant_id = T.VariantId.id_of_json variant_id in
- Ok (A.SetDiscriminant (place, variant_id))
- | `Assoc [ ("Drop", place) ] ->
- let* place = place_of_json place in
- Ok (A.Drop place)
- | `Assoc [ ("Assert", assertion) ] ->
- let* assertion = assertion_of_json assertion in
- Ok (A.Assert assertion)
- | `Assoc [ ("Call", call) ] ->
- let* call = call_of_json call in
- Ok (A.Call call)
- | `String "Panic" -> Ok A.Panic
- | `String "Return" -> Ok A.Return
- | `Assoc [ ("Break", i) ] ->
- let* i = int_of_json i in
- Ok (A.Break i)
- | `Assoc [ ("Continue", i) ] ->
- let* i = int_of_json i in
- Ok (A.Continue i)
- | `String "Nop" -> Ok A.Nop
- | `Assoc [ ("Sequence", `List [ st1; st2 ]) ] ->
- let* st1 = statement_of_json id_to_file st1 in
- let* st2 = statement_of_json id_to_file st2 in
- Ok (A.Sequence (st1, st2))
- | `Assoc [ ("Switch", `List [ op; tgt ]) ] ->
- let* op = operand_of_json op in
- let* tgt = switch_targets_of_json id_to_file tgt in
- Ok (A.Switch (op, tgt))
- | `Assoc [ ("Loop", st) ] ->
- let* st = statement_of_json id_to_file st in
- Ok (A.Loop st)
- | _ -> Error "")
-
-and switch_targets_of_json (id_to_file : id_to_file_map) (js : json) :
- (A.switch_targets, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("If", `List [ st1; st2 ]) ] ->
- let* st1 = statement_of_json id_to_file st1 in
- let* st2 = statement_of_json id_to_file st2 in
- Ok (A.If (st1, st2))
- | `Assoc [ ("SwitchInt", `List [ int_ty; tgts; otherwise ]) ] ->
- let* int_ty = integer_type_of_json int_ty in
- let* tgts =
- list_of_json
- (pair_of_json
- (list_of_json scalar_value_of_json)
- (statement_of_json id_to_file))
- tgts
- in
- let* otherwise = statement_of_json id_to_file otherwise in
- Ok (A.SwitchInt (int_ty, tgts, otherwise))
- | _ -> Error "")
-
-let fun_body_of_json (id_to_file : id_to_file_map) (js : json) :
- (A.fun_body, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc
- [
- ("meta", meta);
- ("arg_count", arg_count);
- ("locals", locals);
- ("body", body);
- ] ->
- let* meta = meta_of_json id_to_file meta in
- let* arg_count = int_of_json arg_count in
- let* locals = list_of_json var_of_json locals in
- let* body = statement_of_json id_to_file body in
- Ok { A.meta; arg_count; locals; body }
- | _ -> Error "")
-
-let fun_decl_of_json (id_to_file : id_to_file_map) (js : json) :
- (A.fun_decl, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc
- [
- ("def_id", def_id);
- ("meta", meta);
- ("name", name);
- ("signature", signature);
- ("body", body);
- ] ->
- let* def_id = A.FunDeclId.id_of_json def_id in
- let* meta = meta_of_json id_to_file meta in
- let* name = fun_name_of_json name in
- let* signature = fun_sig_of_json signature in
- let* body = option_of_json (fun_body_of_json id_to_file) body in
- Ok
- { A.def_id; meta; name; signature; body; is_global_decl_body = false }
- | _ -> Error "")
-
-(* Strict type for the number of function declarations (see {!global_to_fun_id} below) *)
-type global_id_converter = { fun_count : int } [@@deriving show]
-
-(** Converts a global id to its corresponding function id.
- To do so, it adds the global id to the number of function declarations :
- We have the bijection [global_fun_id <=> global_id + fun_id_count].
-*)
-let global_to_fun_id (conv : global_id_converter) (gid : A.GlobalDeclId.id) :
- A.FunDeclId.id =
- A.FunDeclId.of_int (A.GlobalDeclId.to_int gid + conv.fun_count)
-
-(* Converts a global declaration to a function declaration.
- *)
-let global_decl_of_json (id_to_file : id_to_file_map) (js : json)
- (gid_conv : global_id_converter) :
- (A.global_decl * A.fun_decl, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc
- [
- ("def_id", def_id);
- ("meta", meta);
- ("name", name);
- ("ty", ty);
- ("body", body);
- ] ->
- let* global_id = A.GlobalDeclId.id_of_json def_id in
- let fun_id = global_to_fun_id gid_conv global_id in
- let* meta = meta_of_json id_to_file meta in
- let* name = fun_name_of_json name in
- let* ty = ety_of_json ty in
- let* body = option_of_json (fun_body_of_json id_to_file) body in
- let signature : A.fun_sig =
- {
- region_params = [];
- num_early_bound_regions = 0;
- regions_hierarchy = [];
- type_params = [];
- inputs = [];
- output = TU.ety_no_regions_to_sty ty;
- }
- in
- Ok
- ( { A.def_id = global_id; meta; body_id = fun_id; name; ty },
- {
- A.def_id = fun_id;
- meta;
- name;
- signature;
- body;
- is_global_decl_body = true;
- } )
- | _ -> Error "")
-
-let g_declaration_group_of_json (id_of_json : json -> ('id, string) result)
- (js : json) : ('id Crates.g_declaration_group, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("NonRec", `List [ id ]) ] ->
- let* id = id_of_json id in
- Ok (Crates.NonRec id)
- | `Assoc [ ("Rec", `List [ ids ]) ] ->
- let* ids = list_of_json id_of_json ids in
- Ok (Crates.Rec ids)
- | _ -> Error "")
-
-let type_declaration_group_of_json (js : json) :
- (Crates.type_declaration_group, string) result =
- combine_error_msgs js __FUNCTION__
- (g_declaration_group_of_json T.TypeDeclId.id_of_json js)
-
-let fun_declaration_group_of_json (js : json) :
- (Crates.fun_declaration_group, string) result =
- combine_error_msgs js __FUNCTION__
- (g_declaration_group_of_json A.FunDeclId.id_of_json js)
-
-let global_declaration_group_of_json (js : json) :
- (A.GlobalDeclId.id, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("NonRec", `List [ id ]) ] ->
- let* id = A.GlobalDeclId.id_of_json id in
- Ok id
- | `Assoc [ ("Rec", `List [ _ ]) ] -> Error "got mutually dependent globals"
- | _ -> Error "")
-
-let declaration_group_of_json (js : json) :
- (Crates.declaration_group, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc [ ("Type", `List [ decl ]) ] ->
- let* decl = type_declaration_group_of_json decl in
- Ok (Crates.Type decl)
- | `Assoc [ ("Fun", `List [ decl ]) ] ->
- let* decl = fun_declaration_group_of_json decl in
- Ok (Crates.Fun decl)
- | `Assoc [ ("Global", `List [ decl ]) ] ->
- let* id = global_declaration_group_of_json decl in
- Ok (Crates.Global id)
- | _ -> Error "")
-
-let length_of_json_list (js : json) : (int, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `List jsl -> Ok (List.length jsl)
- | _ -> Error ("not a list: " ^ show js))
-
-let llbc_crate_of_json (js : json) : (Crates.llbc_crate, string) result =
- combine_error_msgs js __FUNCTION__
- (match js with
- | `Assoc
- [
- ("name", name);
- ("id_to_file", id_to_file);
- ("declarations", declarations);
- ("types", types);
- ("functions", functions);
- ("globals", globals);
- ] ->
- (* We first deserialize the declaration groups (which simply contain ids)
- * and all the declarations *butù* the globals *)
- let* name = string_of_json name in
- let* id_to_file = id_to_file_of_json id_to_file in
- let* declarations =
- list_of_json declaration_group_of_json declarations
- in
- let* types = list_of_json (type_decl_of_json id_to_file) types in
- let* functions = list_of_json (fun_decl_of_json id_to_file) functions in
- (* When deserializing the globals, we split the global declarations
- * between the globals themselves and their bodies, which are simply
- * functions with no arguments. We add the global bodies to the list
- * of function declarations: the (fresh) ids we use for those bodies
- * are simply given by: [num_functions + global_id] *)
- let gid_conv = { fun_count = List.length functions } in
- let* globals =
- list_of_json
- (fun js -> global_decl_of_json id_to_file js gid_conv)
- globals
- in
- let globals, global_bodies = List.split globals in
- Ok
- {
- Crates.name;
- declarations;
- types;
- functions = functions @ global_bodies;
- globals;
- }
- | _ -> Error "")
diff --git a/src/Logging.ml b/src/Logging.ml
deleted file mode 100644
index e83f25f8..00000000
--- a/src/Logging.ml
+++ /dev/null
@@ -1,179 +0,0 @@
-module H = Easy_logging.Handlers
-module L = Easy_logging.Logging
-
-let _ = L.make_logger "MainLogger" Debug [ Cli Debug ]
-
-(** The main logger *)
-let main_log = L.get_logger "MainLogger"
-
-(** Below, we create subgloggers for various submodules, so that we can precisely
- toggle logging on/off, depending on which information we need *)
-
-(** Logger for LlbcOfJson *)
-let llbc_of_json_logger = L.get_logger "MainLogger.LlbcOfJson"
-
-(** Logger for PrePasses *)
-let pre_passes_log = L.get_logger "MainLogger.PrePasses"
-
-(** Logger for Translate *)
-let translate_log = L.get_logger "MainLogger.Translate"
-
-(** Logger for PureUtils *)
-let pure_utils_log = L.get_logger "MainLogger.PureUtils"
-
-(** Logger for SymbolicToPure *)
-let symbolic_to_pure_log = L.get_logger "MainLogger.SymbolicToPure"
-
-(** Logger for PureMicroPasses *)
-let pure_micro_passes_log = L.get_logger "MainLogger.PureMicroPasses"
-
-(** Logger for PureToExtract *)
-let pure_to_extract_log = L.get_logger "MainLogger.PureToExtract"
-
-(** Logger for Interpreter *)
-let interpreter_log = L.get_logger "MainLogger.Interpreter"
-
-(** Logger for InterpreterStatements *)
-let statements_log = L.get_logger "MainLogger.Interpreter.Statements"
-
-(** Logger for InterpreterExpressions *)
-let expressions_log = L.get_logger "MainLogger.Interpreter.Expressions"
-
-(** Logger for InterpreterPaths *)
-let paths_log = L.get_logger "MainLogger.Interpreter.Paths"
-
-(** Logger for InterpreterExpansion *)
-let expansion_log = L.get_logger "MainLogger.Interpreter.Expansion"
-
-(** Logger for InterpreterBorrows *)
-let borrows_log = L.get_logger "MainLogger.Interpreter.Borrows"
-
-(** Logger for Invariants *)
-let invariants_log = L.get_logger "MainLogger.Interpreter.Invariants"
-
-(** Terminal colors - TODO: comes from easy_logging (did not manage to reuse the module directly) *)
-type color =
- | Default
- | Black
- | Red
- | Green
- | Yellow
- | Blue
- | Magenta
- | Cyan
- | Gray
- | White
- | LRed
- | LGreen
- | LYellow
- | LBlue
- | LMagenta
- | LCyan
- | LGray
-
-(** Terminal styles - TODO: comes from easy_logging (did not manage to reuse the module directly) *)
-type format = Bold | Underline | Invert | Fg of color | Bg of color
-
-(** TODO: comes from easy_logging (did not manage to reuse the module directly) *)
-let to_fg_code c =
- match c with
- | Default -> 39
- | Black -> 30
- | Red -> 31
- | Green -> 32
- | Yellow -> 33
- | Blue -> 34
- | Magenta -> 35
- | Cyan -> 36
- | Gray -> 90
- | White -> 97
- | LRed -> 91
- | LGreen -> 92
- | LYellow -> 93
- | LBlue -> 94
- | LMagenta -> 95
- | LCyan -> 96
- | LGray -> 37
-
-(** TODO: comes from easy_logging (did not manage to reuse the module directly) *)
-let to_bg_code c =
- match c with
- | Default -> 49
- | Black -> 40
- | Red -> 41
- | Green -> 42
- | Yellow -> 43
- | Blue -> 44
- | Magenta -> 45
- | Cyan -> 46
- | Gray -> 100
- | White -> 107
- | LRed -> 101
- | LGreen -> 102
- | LYellow -> 103
- | LBlue -> 104
- | LMagenta -> 105
- | LCyan -> 106
- | LGray -> 47
-
-(** TODO: comes from easy_logging (did not manage to reuse the module directly) *)
-let style_to_codes s =
- match s with
- | Bold -> (1, 21)
- | Underline -> (4, 24)
- | Invert -> (7, 27)
- | Fg c -> (to_fg_code c, to_fg_code Default)
- | Bg c -> (to_bg_code c, to_bg_code Default)
-
-(** TODO: comes from easy_logging (did not manage to reuse the module directly)
- I made a minor modifications, though. *)
-let level_to_color (lvl : L.level) =
- match lvl with
- | L.Flash -> LMagenta
- | Error -> LRed
- | Warning -> LYellow
- | Info -> LGreen
- | Trace -> Cyan
- | Debug -> LBlue
- | NoLevel -> Default
-
-(** [format styles str] formats [str] to the given [styles] -
- TODO: comes from {{: http://ocamlverse.net/content/documentation_guidelines.html}[easy_logging]}
- (did not manage to reuse the module directly)
-*)
-let rec format styles str =
- match styles with
- | (_ as s) :: styles' ->
- let set, reset = style_to_codes s in
- Printf.sprintf "\027[%dm%s\027[%dm" set (format styles' str) reset
- | [] -> str
-
-(** TODO: comes from {{: http://ocamlverse.net/content/documentation_guidelines.html}[easy_logging]}
- (did not manage to reuse the module directly) *)
-let format_tags (tags : string list) =
- match tags with
- | [] -> ""
- | _ ->
- let elems_str = String.concat " | " tags in
- "[" ^ elems_str ^ "] "
-
-(* Change the formatters *)
-let main_logger_handler =
- (* TODO: comes from easy_logging *)
- let formatter (item : L.log_item) : string =
- let item_level_fmt =
- format [ Fg (level_to_color item.level) ] (L.show_level item.level)
- and item_msg_fmt =
- match item.level with
- | Flash -> format [ Fg Black; Bg LMagenta ] item.msg
- | _ -> item.msg
- in
-
- Format.pp_set_max_indent Format.str_formatter 200;
- Format.sprintf "@[[%-15s] %s%s@]" item_level_fmt (format_tags item.tags)
- item_msg_fmt
- in
- (* There should be exactly one handler *)
- let handlers = main_log#get_handlers in
- List.iter (fun h -> H.set_formatter h formatter) handlers;
- match handlers with [ handler ] -> handler | _ -> failwith "Unexpected"
diff --git a/src/Meta.ml b/src/Meta.ml
deleted file mode 100644
index f0e4ca04..00000000
--- a/src/Meta.ml
+++ /dev/null
@@ -1,44 +0,0 @@
-(** Meta data like code spans *)
-
-(** A line location *)
-type loc = {
- line : int; (** The (1-based) line number. *)
- col : int; (** The (0-based) column offset. *)
-}
-[@@deriving show]
-
-type file_name =
- | Virtual of string (** A remapped path (namely paths into stdlib) *)
- | Local of string
- (** A local path (a file coming from the current crate for instance) *)
-[@@deriving show]
-
-(** Span data *)
-type span = { file : file_name; beg_loc : loc; end_loc : loc } [@@deriving show]
-
-type meta = {
- span : span;
- (** The source code span.
-
- If this meta information is for a statement/terminator coming from a macro
- expansion/inlining/etc., this span is (in case of macros) for the macro
- before expansion (i.e., the location the code where the user wrote the call
- to the macro).
-
- Ex:
- {[
- // Below, we consider the spans for the statements inside `test`
-
- // the statement we consider, which gets inlined in `test`
- VV
- macro_rules! macro { ... st ... } // `generated_from_span` refers to this location
-
- fn test() {
- macro!(); // <-- `span` refers to this location
- }
- ]}
- *)
- generated_from_span : span option;
- (** Where the code actually comes from, in case of macro expansion/inlining/etc. *)
-}
-[@@deriving show]
diff --git a/src/Names.ml b/src/Names.ml
deleted file mode 100644
index a27db161..00000000
--- a/src/Names.ml
+++ /dev/null
@@ -1,80 +0,0 @@
-open Identifiers
-module Disambiguator = IdGen ()
-
-(** See the comments for [Name] *)
-type path_elem = Ident of string | Disambiguator of Disambiguator.id
-[@@deriving show, ord]
-
-(** A name such as: [std::collections::vector] (which would be represented as
- [[Ident "std"; Ident "collections"; Ident "vector"]])
-
-
- A name really is a list of strings. However, we sometimes need to
- introduce unique indices to disambiguate. This mostly happens because
- of "impl" blocks in Rust:
- {[
- impl<T> List<T> {
- ...
- }
- ]}
-
- A type in Rust can have several "impl" blocks, and those blocks can
- contain items with similar names. For this reason, we need to disambiguate
- them with unique indices. Rustc calls those "disambiguators". In rustc, this
- gives names like this:
- - [betree_main::betree::NodeIdCounter{impl#0}::new]
- - note that impl blocks can be nested, and macros sometimes generate
- weird names (which require disambiguation):
- [betree_main::betree_utils::_#1::{impl#0}::deserialize::{impl#0}]
-
- Finally, the paths used by rustc are a lot more precise and explicit than
- those we expose in LLBC: for instance, every identifier belongs to a specific
- namespace (value namespace, type namespace, etc.), and is coupled with a
- disambiguator.
-
- On our side, we want to stay high-level and simple: we use string identifiers
- as much as possible, insert disambiguators only when necessary (whenever
- we find an "impl" block, typically) and check that the disambiguator is useless
- in the other situations (i.e., the disambiguator is always equal to 0).
-
- Moreover, the items are uniquely disambiguated by their (integer) ids
- ([TypeDeclId.id], etc.), and when extracting the code we have to deal with
- name clashes anyway. Still, we might want to be more precise in the future.
-
- Also note that the first path element in the name is always the crate name.
- *)
-type name = path_elem list [@@deriving show, ord]
-
-let to_name (ls : string list) : name = List.map (fun s -> Ident s) ls
-
-type module_name = name [@@deriving show, ord]
-type type_name = name [@@deriving show, ord]
-type fun_name = name [@@deriving show, ord]
-type global_name = name [@@deriving show, ord]
-
-(** Filter the disambiguators equal to 0 in a name *)
-let filter_disambiguators_zero (n : name) : name =
- let pred (pe : path_elem) : bool =
- match pe with Ident _ -> true | Disambiguator d -> d <> Disambiguator.zero
- in
- List.filter pred n
-
-(** Filter the disambiguators in a name *)
-let filter_disambiguators (n : name) : name =
- let pred (pe : path_elem) : bool =
- match pe with Ident _ -> true | Disambiguator _ -> false
- in
- List.filter pred n
-
-let as_ident (pe : path_elem) : string =
- match pe with
- | Ident s -> s
- | Disambiguator _ -> raise (Failure "Improper variant")
-
-let path_elem_to_string (pe : path_elem) : string =
- match pe with
- | Ident s -> s
- | Disambiguator d -> "{" ^ Disambiguator.to_string d ^ "}"
-
-let name_to_string (name : name) : string =
- String.concat "::" (List.map path_elem_to_string name)
diff --git a/src/OfJsonBasic.ml b/src/OfJsonBasic.ml
deleted file mode 100644
index 07daf03d..00000000
--- a/src/OfJsonBasic.ml
+++ /dev/null
@@ -1,75 +0,0 @@
-(** This module defines various basic utilities for json deserialization.
-
- *)
-
-open Yojson.Basic
-
-type json = t
-
-let ( let* ) o f = match o with Error e -> Error e | Ok x -> f x
-
-let combine_error_msgs js msg res : ('a, string) result =
- match res with
- | Ok x -> Ok x
- | Error e -> Error ("[" ^ msg ^ "]" ^ " failed on: " ^ show js ^ "\n\n" ^ e)
-
-let bool_of_json (js : json) : (bool, string) result =
- match js with
- | `Bool b -> Ok b
- | _ -> Error ("bool_of_json: not a bool: " ^ show js)
-
-let int_of_json (js : json) : (int, string) result =
- match js with
- | `Int i -> Ok i
- | _ -> Error ("int_of_json: not an int: " ^ show js)
-
-let char_of_json (js : json) : (char, string) result =
- match js with
- | `String c ->
- if String.length c = 1 then Ok c.[0]
- else Error ("char_of_json: stricly more than one character in: " ^ show js)
- | _ -> Error ("char_of_json: not a char: " ^ show js)
-
-let rec of_json_list (a_of_json : json -> ('a, string) result) (jsl : json list)
- : ('a list, string) result =
- match jsl with
- | [] -> Ok []
- | x :: jsl' ->
- let* x = a_of_json x in
- let* jsl' = of_json_list a_of_json jsl' in
- Ok (x :: jsl')
-
-let pair_of_json (a_of_json : json -> ('a, string) result)
- (b_of_json : json -> ('b, string) result) (js : json) :
- ('a * 'b, string) result =
- match js with
- | `List [ a; b ] ->
- let* a = a_of_json a in
- let* b = b_of_json b in
- Ok (a, b)
- | _ -> Error ("pair_of_json failed on: " ^ show js)
-
-let list_of_json (a_of_json : json -> ('a, string) result) (js : json) :
- ('a list, string) result =
- combine_error_msgs js "list_of_json"
- (match js with
- | `List jsl -> of_json_list a_of_json jsl
- | _ -> Error ("not a list: " ^ show js))
-
-let string_of_json (js : json) : (string, string) result =
- match js with
- | `String str -> Ok str
- | _ -> Error ("string_of_json: not a string: " ^ show js)
-
-let option_of_json (a_of_json : json -> ('a, string) result) (js : json) :
- ('a option, string) result =
- combine_error_msgs js "option_of_json"
- (match js with
- | `Null -> Ok None
- | _ ->
- let* x = a_of_json js in
- Ok (Some x))
-
-let string_option_of_json (js : json) : (string option, string) result =
- combine_error_msgs js "string_option_of_json"
- (option_of_json string_of_json js)
diff --git a/src/PrePasses.ml b/src/PrePasses.ml
deleted file mode 100644
index a09ae476..00000000
--- a/src/PrePasses.ml
+++ /dev/null
@@ -1,54 +0,0 @@
-(** This files contains passes we apply on the AST *before* calling the
- (concrete/symbolic) interpreter on it
- *)
-
-module T = Types
-module V = Values
-module E = Expressions
-module C = Contexts
-module A = LlbcAst
-module L = Logging
-
-let log = L.pre_passes_log
-
-(** Rustc inserts a lot of drops before the assignments.
- We consider those drops are part of the assignment, and splitting the
- drop and the assignment is problematic for us because it can introduce
- [⊥] under borrows. For instance, we encountered situations like the
- following one:
-
- {[
- drop( *x ); // Illegal! Inserts a ⊥ under a borrow
- *x = move ...;
- ]}
-
- TODO: this is not necessary anymore
- *)
-let filter_drop_assigns (f : A.fun_decl) : A.fun_decl =
- (* The visitor *)
- let obj =
- object (self)
- inherit [_] A.map_statement as super
-
- method! visit_Sequence env st1 st2 =
- match (st1.content, st2.content) with
- | Drop p1, Assign (p2, _) ->
- if p1 = p2 then (self#visit_statement env st2).content
- else super#visit_Sequence env st1 st2
- | Drop p1, Sequence ({ content = Assign (p2, _); meta = _ }, _) ->
- if p1 = p2 then (self#visit_statement env st2).content
- else super#visit_Sequence env st1 st2
- | _ -> super#visit_Sequence env st1 st2
- end
- in
- (* Map *)
- let body =
- match f.body with
- | Some body -> Some { body with body = obj#visit_statement () body.body }
- | None -> None
- in
- { f with body }
-
-let apply_passes (m : Crates.llbc_crate) : Crates.llbc_crate =
- let functions = List.map filter_drop_assigns m.functions in
- { m with functions }
diff --git a/src/Print.ml b/src/Print.ml
deleted file mode 100644
index 03cab6ee..00000000
--- a/src/Print.ml
+++ /dev/null
@@ -1,1283 +0,0 @@
-open Names
-module T = Types
-module TU = TypesUtils
-module V = Values
-module VU = ValuesUtils
-module E = Expressions
-module A = LlbcAst
-module C = Contexts
-
-let option_to_string (to_string : 'a -> string) (x : 'a option) : string =
- match x with Some x -> "Some (" ^ to_string x ^ ")" | None -> "None"
-
-let name_to_string (name : name) : string = Names.name_to_string name
-let fun_name_to_string (name : fun_name) : string = name_to_string name
-let global_name_to_string (name : global_name) : string = name_to_string name
-
-(** Pretty-printing for types *)
-module Types = struct
- let type_var_to_string (tv : T.type_var) : string = tv.name
-
- let region_var_to_string (rv : T.region_var) : string =
- match rv.name with
- | Some name -> name
- | None -> T.RegionVarId.to_string rv.index
-
- let region_var_id_to_string (rid : T.RegionVarId.id) : string =
- "rv@" ^ T.RegionVarId.to_string rid
-
- let region_id_to_string (rid : T.RegionId.id) : string =
- "r@" ^ T.RegionId.to_string rid
-
- let region_to_string (rid_to_string : 'rid -> string) (r : 'rid T.region) :
- string =
- match r with Static -> "'static" | Var rid -> rid_to_string rid
-
- let erased_region_to_string (_ : T.erased_region) : string = "'_"
-
- let ref_kind_to_string (rk : T.ref_kind) : string =
- match rk with Mut -> "Mut" | Shared -> "Shared"
-
- let assumed_ty_to_string (_ : T.assumed_ty) : string = "Box"
-
- type 'r type_formatter = {
- r_to_string : 'r -> string;
- type_var_id_to_string : T.TypeVarId.id -> string;
- type_decl_id_to_string : T.TypeDeclId.id -> string;
- }
-
- type stype_formatter = T.RegionVarId.id T.region type_formatter
- type rtype_formatter = T.RegionId.id T.region type_formatter
- type etype_formatter = T.erased_region type_formatter
-
- let integer_type_to_string = function
- | T.Isize -> "isize"
- | T.I8 -> "i8"
- | T.I16 -> "i16"
- | T.I32 -> "i32"
- | T.I64 -> "i64"
- | T.I128 -> "i128"
- | T.Usize -> "usize"
- | T.U8 -> "u8"
- | T.U16 -> "u16"
- | T.U32 -> "u32"
- | T.U64 -> "u64"
- | T.U128 -> "u128"
-
- let type_id_to_string (fmt : 'r type_formatter) (id : T.type_id) : string =
- match id with
- | T.AdtId id -> fmt.type_decl_id_to_string id
- | T.Tuple -> ""
- | T.Assumed aty -> (
- match aty with
- | Box -> "alloc::boxed::Box"
- | Vec -> "alloc::vec::Vec"
- | Option -> "core::option::Option")
-
- let rec ty_to_string (fmt : 'r type_formatter) (ty : 'r T.ty) : string =
- match ty with
- | T.Adt (id, regions, tys) ->
- let is_tuple = match id with T.Tuple -> true | _ -> false in
- let params = params_to_string fmt is_tuple regions tys in
- type_id_to_string fmt id ^ params
- | T.TypeVar tv -> fmt.type_var_id_to_string tv
- | T.Bool -> "bool"
- | T.Char -> "char"
- | T.Never -> "⊥"
- | T.Integer int_ty -> integer_type_to_string int_ty
- | T.Str -> "str"
- | T.Array aty -> "[" ^ ty_to_string fmt aty ^ "; ?]"
- | T.Slice sty -> "[" ^ ty_to_string fmt sty ^ "]"
- | T.Ref (r, rty, ref_kind) -> (
- match ref_kind with
- | T.Mut ->
- "&" ^ fmt.r_to_string r ^ " mut (" ^ ty_to_string fmt rty ^ ")"
- | T.Shared ->
- "&" ^ fmt.r_to_string r ^ " (" ^ ty_to_string fmt rty ^ ")")
-
- and params_to_string (fmt : 'r type_formatter) (is_tuple : bool)
- (regions : 'r list) (types : 'r T.ty list) : string =
- let regions = List.map fmt.r_to_string regions in
- let types = List.map (ty_to_string fmt) types in
- let params = String.concat ", " (List.append regions types) in
- if is_tuple then "(" ^ params ^ ")"
- else if List.length regions + List.length types > 0 then "<" ^ params ^ ">"
- else ""
-
- let sty_to_string (fmt : stype_formatter) (ty : T.sty) : string =
- ty_to_string fmt ty
-
- let rty_to_string (fmt : rtype_formatter) (ty : T.rty) : string =
- ty_to_string fmt ty
-
- let ety_to_string (fmt : etype_formatter) (ty : T.ety) : string =
- ty_to_string fmt ty
-
- let field_to_string fmt (f : T.field) : string =
- match f.field_name with
- | Some field_name -> field_name ^ " : " ^ ty_to_string fmt f.field_ty
- | None -> ty_to_string fmt f.field_ty
-
- let variant_to_string fmt (v : T.variant) : string =
- v.variant_name ^ "("
- ^ String.concat ", " (List.map (field_to_string fmt) v.fields)
- ^ ")"
-
- let type_decl_to_string (type_decl_id_to_string : T.TypeDeclId.id -> string)
- (def : T.type_decl) : string =
- let regions = def.region_params in
- let types = def.type_params in
- let rid_to_string rid =
- match List.find_opt (fun rv -> rv.T.index = rid) regions with
- | Some rv -> region_var_to_string rv
- | None -> failwith "Unreachable"
- in
- let r_to_string = region_to_string rid_to_string in
- let type_var_id_to_string id =
- match List.find_opt (fun tv -> tv.T.index = id) types with
- | Some tv -> type_var_to_string tv
- | None -> failwith "Unreachable"
- in
- let fmt = { r_to_string; type_var_id_to_string; type_decl_id_to_string } in
- let name = name_to_string def.name in
- let params =
- if List.length regions + List.length types > 0 then
- let regions = List.map region_var_to_string regions in
- let types = List.map type_var_to_string types in
- let params = String.concat ", " (List.append regions types) in
- "<" ^ params ^ ">"
- else ""
- in
- match def.kind with
- | T.Struct fields ->
- if List.length fields > 0 then
- let fields =
- String.concat ","
- (List.map (fun f -> "\n " ^ field_to_string fmt f) fields)
- in
- "struct " ^ name ^ params ^ "{" ^ fields ^ "}"
- else "struct " ^ name ^ params ^ "{}"
- | T.Enum variants ->
- let variants =
- List.map (fun v -> "| " ^ variant_to_string fmt v) variants
- in
- let variants = String.concat "\n" variants in
- "enum " ^ name ^ params ^ " =\n" ^ variants
- | T.Opaque -> "opaque type " ^ name ^ params
-end
-
-module PT = Types (* local module *)
-
-(** Pretty-printing for values *)
-module Values = struct
- type value_formatter = {
- rvar_to_string : T.RegionVarId.id -> string;
- r_to_string : T.RegionId.id -> string;
- type_var_id_to_string : T.TypeVarId.id -> string;
- type_decl_id_to_string : T.TypeDeclId.id -> string;
- adt_variant_to_string : T.TypeDeclId.id -> T.VariantId.id -> string;
- var_id_to_string : V.VarId.id -> string;
- adt_field_names :
- T.TypeDeclId.id -> T.VariantId.id option -> string list option;
- }
-
- let value_to_etype_formatter (fmt : value_formatter) : PT.etype_formatter =
- {
- PT.r_to_string = PT.erased_region_to_string;
- PT.type_var_id_to_string = fmt.type_var_id_to_string;
- PT.type_decl_id_to_string = fmt.type_decl_id_to_string;
- }
-
- let value_to_rtype_formatter (fmt : value_formatter) : PT.rtype_formatter =
- {
- PT.r_to_string = PT.region_to_string fmt.r_to_string;
- PT.type_var_id_to_string = fmt.type_var_id_to_string;
- PT.type_decl_id_to_string = fmt.type_decl_id_to_string;
- }
-
- let value_to_stype_formatter (fmt : value_formatter) : PT.stype_formatter =
- {
- PT.r_to_string = PT.region_to_string fmt.rvar_to_string;
- PT.type_var_id_to_string = fmt.type_var_id_to_string;
- PT.type_decl_id_to_string = fmt.type_decl_id_to_string;
- }
-
- let var_id_to_string (id : V.VarId.id) : string =
- "var@" ^ V.VarId.to_string id
-
- let big_int_to_string (bi : V.big_int) : string = Z.to_string bi
-
- let scalar_value_to_string (sv : V.scalar_value) : string =
- big_int_to_string sv.value ^ ": " ^ PT.integer_type_to_string sv.int_ty
-
- let constant_value_to_string (cv : V.constant_value) : string =
- match cv with
- | Scalar sv -> scalar_value_to_string sv
- | Bool b -> Bool.to_string b
- | Char c -> String.make 1 c
- | String s -> s
-
- let symbolic_value_id_to_string (id : V.SymbolicValueId.id) : string =
- "s@" ^ V.SymbolicValueId.to_string id
-
- let symbolic_value_to_string (fmt : PT.rtype_formatter)
- (sv : V.symbolic_value) : string =
- symbolic_value_id_to_string sv.sv_id ^ " : " ^ PT.rty_to_string fmt sv.sv_ty
-
- let symbolic_value_proj_to_string (fmt : value_formatter)
- (sv : V.symbolic_value) (rty : T.rty) : string =
- symbolic_value_id_to_string sv.sv_id
- ^ " : "
- ^ PT.ty_to_string (value_to_rtype_formatter fmt) sv.sv_ty
- ^ " <: "
- ^ PT.ty_to_string (value_to_rtype_formatter fmt) rty
-
- (* TODO: it may be a good idea to try to factorize this function with
- * typed_avalue_to_string. At some point we had done it, because [typed_value]
- * and [typed_avalue] were instances of the same general type [g_typed_value],
- * but then we removed this general type because it proved to be a bad idea. *)
- let rec typed_value_to_string (fmt : value_formatter) (v : V.typed_value) :
- string =
- let ty_fmt : PT.etype_formatter = value_to_etype_formatter fmt in
- match v.value with
- | Concrete cv -> constant_value_to_string cv
- | Adt av -> (
- let field_values =
- List.map (typed_value_to_string fmt) av.field_values
- in
- match v.ty with
- | T.Adt (T.Tuple, _, _) ->
- (* Tuple *)
- "(" ^ String.concat ", " field_values ^ ")"
- | T.Adt (T.AdtId def_id, _, _) ->
- (* "Regular" ADT *)
- let adt_ident =
- match av.variant_id with
- | Some vid -> fmt.adt_variant_to_string def_id vid
- | None -> fmt.type_decl_id_to_string def_id
- in
- if List.length field_values > 0 then
- match fmt.adt_field_names def_id av.V.variant_id with
- | None ->
- let field_values = String.concat ", " field_values in
- adt_ident ^ " (" ^ field_values ^ ")"
- | Some field_names ->
- let field_values = List.combine field_names field_values in
- let field_values =
- List.map
- (fun (field, value) -> field ^ " = " ^ value ^ ";")
- field_values
- in
- let field_values = String.concat " " field_values in
- adt_ident ^ " { " ^ field_values ^ " }"
- else adt_ident
- | T.Adt (T.Assumed aty, _, _) -> (
- (* Assumed type *)
- match (aty, field_values) with
- | Box, [ bv ] -> "@Box(" ^ bv ^ ")"
- | Option, _ ->
- if av.variant_id = Some T.option_some_id then
- "@Option::Some("
- ^ Collections.List.to_cons_nil field_values
- ^ ")"
- else if av.variant_id = Some T.option_none_id then (
- assert (field_values = []);
- "@Option::None")
- else failwith "Unreachable"
- | Vec, _ -> "@Vec[" ^ String.concat ", " field_values ^ "]"
- | _ -> failwith "Inconsistent value")
- | _ -> failwith "Inconsistent typed value")
- | Bottom -> "⊥ : " ^ PT.ty_to_string ty_fmt v.ty
- | Borrow bc -> borrow_content_to_string fmt bc
- | Loan lc -> loan_content_to_string fmt lc
- | Symbolic s -> symbolic_value_to_string (value_to_rtype_formatter fmt) s
-
- and borrow_content_to_string (fmt : value_formatter) (bc : V.borrow_content) :
- string =
- match bc with
- | SharedBorrow (_, bid) -> "⌊shared@" ^ V.BorrowId.to_string bid ^ "⌋"
- | MutBorrow (bid, tv) ->
- "&mut@" ^ V.BorrowId.to_string bid ^ " ("
- ^ typed_value_to_string fmt tv
- ^ ")"
- | InactivatedMutBorrow (_, bid) ->
- "⌊inactivated_mut@" ^ V.BorrowId.to_string bid ^ "⌋"
-
- and loan_content_to_string (fmt : value_formatter) (lc : V.loan_content) :
- string =
- match lc with
- | SharedLoan (loans, v) ->
- let loans = V.BorrowId.Set.to_string None loans in
- "@shared_loan(" ^ loans ^ ", " ^ typed_value_to_string fmt v ^ ")"
- | MutLoan bid -> "⌊mut@" ^ V.BorrowId.to_string bid ^ "⌋"
-
- let abstract_shared_borrow_to_string (fmt : value_formatter)
- (abs : V.abstract_shared_borrow) : string =
- match abs with
- | AsbBorrow bid -> V.BorrowId.to_string bid
- | AsbProjReborrows (sv, rty) ->
- "{" ^ symbolic_value_proj_to_string fmt sv rty ^ "}"
-
- let abstract_shared_borrows_to_string (fmt : value_formatter)
- (abs : V.abstract_shared_borrows) : string =
- "{"
- ^ String.concat "," (List.map (abstract_shared_borrow_to_string fmt) abs)
- ^ "}"
-
- let rec aproj_to_string (fmt : value_formatter) (pv : V.aproj) : string =
- match pv with
- | AProjLoans (sv, given_back) ->
- let given_back =
- if given_back = [] then ""
- else
- let given_back = List.map snd given_back in
- let given_back = List.map (aproj_to_string fmt) given_back in
- " (" ^ String.concat "," given_back ^ ") "
- in
- "⌊"
- ^ symbolic_value_to_string (value_to_rtype_formatter fmt) sv
- ^ given_back ^ "⌋"
- | AProjBorrows (sv, rty) ->
- "(" ^ symbolic_value_proj_to_string fmt sv rty ^ ")"
- | AEndedProjLoans (_, given_back) ->
- if given_back = [] then "_"
- else
- let given_back = List.map snd given_back in
- let given_back = List.map (aproj_to_string fmt) given_back in
- "ended_aproj_loans (" ^ String.concat "," given_back ^ ")"
- | AEndedProjBorrows _mv -> "_"
- | AIgnoredProjBorrows -> "_"
-
- let rec typed_avalue_to_string (fmt : value_formatter) (v : V.typed_avalue) :
- string =
- let ty_fmt : PT.rtype_formatter = value_to_rtype_formatter fmt in
- match v.value with
- | AConcrete cv -> constant_value_to_string cv
- | AAdt av -> (
- let field_values =
- List.map (typed_avalue_to_string fmt) av.field_values
- in
- match v.ty with
- | T.Adt (T.Tuple, _, _) ->
- (* Tuple *)
- "(" ^ String.concat ", " field_values ^ ")"
- | T.Adt (T.AdtId def_id, _, _) ->
- (* "Regular" ADT *)
- let adt_ident =
- match av.variant_id with
- | Some vid -> fmt.adt_variant_to_string def_id vid
- | None -> fmt.type_decl_id_to_string def_id
- in
- if List.length field_values > 0 then
- match fmt.adt_field_names def_id av.V.variant_id with
- | None ->
- let field_values = String.concat ", " field_values in
- adt_ident ^ " (" ^ field_values ^ ")"
- | Some field_names ->
- let field_values = List.combine field_names field_values in
- let field_values =
- List.map
- (fun (field, value) -> field ^ " = " ^ value ^ ";")
- field_values
- in
- let field_values = String.concat " " field_values in
- adt_ident ^ " { " ^ field_values ^ " }"
- else adt_ident
- | T.Adt (T.Assumed aty, _, _) -> (
- (* Assumed type *)
- match (aty, field_values) with
- | Box, [ bv ] -> "@Box(" ^ bv ^ ")"
- | _ -> failwith "Inconsistent value")
- | _ -> failwith "Inconsistent typed value")
- | ABottom -> "⊥ : " ^ PT.ty_to_string ty_fmt v.ty
- | ABorrow bc -> aborrow_content_to_string fmt bc
- | ALoan lc -> aloan_content_to_string fmt lc
- | ASymbolic s -> aproj_to_string fmt s
- | AIgnored -> "_"
-
- and aloan_content_to_string (fmt : value_formatter) (lc : V.aloan_content) :
- string =
- match lc with
- | AMutLoan (bid, av) ->
- "⌊mut@" ^ V.BorrowId.to_string bid ^ ", "
- ^ typed_avalue_to_string fmt av
- ^ "⌋"
- | ASharedLoan (loans, v, av) ->
- let loans = V.BorrowId.Set.to_string None loans in
- "@shared_loan(" ^ loans ^ ", "
- ^ typed_value_to_string fmt v
- ^ ", "
- ^ typed_avalue_to_string fmt av
- ^ ")"
- | AEndedMutLoan ml ->
- "@ended_mut_loan{"
- ^ typed_avalue_to_string fmt ml.child
- ^ "; "
- ^ typed_avalue_to_string fmt ml.given_back
- ^ " }"
- | AEndedSharedLoan (v, av) ->
- "@ended_shared_loan("
- ^ typed_value_to_string fmt v
- ^ ", "
- ^ typed_avalue_to_string fmt av
- ^ ")"
- | AIgnoredMutLoan (bid, av) ->
- "@ignored_mut_loan(" ^ V.BorrowId.to_string bid ^ ", "
- ^ typed_avalue_to_string fmt av
- ^ ")"
- | AEndedIgnoredMutLoan ml ->
- "@ended_ignored_mut_loan{ "
- ^ typed_avalue_to_string fmt ml.child
- ^ "; "
- ^ typed_avalue_to_string fmt ml.given_back
- ^ "}"
- | AIgnoredSharedLoan sl ->
- "@ignored_shared_loan(" ^ typed_avalue_to_string fmt sl ^ ")"
-
- and aborrow_content_to_string (fmt : value_formatter) (bc : V.aborrow_content)
- : string =
- match bc with
- | AMutBorrow (_, bid, av) ->
- "&mut@" ^ V.BorrowId.to_string bid ^ " ("
- ^ typed_avalue_to_string fmt av
- ^ ")"
- | ASharedBorrow bid -> "⌊shared@" ^ V.BorrowId.to_string bid ^ "⌋"
- | AIgnoredMutBorrow (opt_bid, av) ->
- "@ignored_mut_borrow("
- ^ option_to_string V.BorrowId.to_string opt_bid
- ^ ", "
- ^ typed_avalue_to_string fmt av
- ^ ")"
- | AEndedMutBorrow (_mv, child) ->
- "@ended_mut_borrow(" ^ typed_avalue_to_string fmt child ^ ")"
- | AEndedIgnoredMutBorrow
- { child; given_back_loans_proj; given_back_meta = _ } ->
- "@ended_ignored_mut_borrow{ "
- ^ typed_avalue_to_string fmt child
- ^ "; "
- ^ typed_avalue_to_string fmt given_back_loans_proj
- ^ ")"
- | AEndedSharedBorrow -> "@ended_shared_borrow"
- | AProjSharedBorrow sb ->
- "@ignored_shared_borrow("
- ^ abstract_shared_borrows_to_string fmt sb
- ^ ")"
-
- let abs_to_string (fmt : value_formatter) (indent : string)
- (indent_incr : string) (abs : V.abs) : string =
- let indent2 = indent ^ indent_incr in
- let avs =
- List.map (fun av -> indent2 ^ typed_avalue_to_string fmt av) abs.avalues
- in
- let avs = String.concat ",\n" avs in
- indent ^ "abs@"
- ^ V.AbstractionId.to_string abs.abs_id
- ^ "{parents="
- ^ V.AbstractionId.Set.to_string None abs.parents
- ^ "}" ^ "{regions="
- ^ T.RegionId.Set.to_string None abs.regions
- ^ "}" ^ " {\n" ^ avs ^ "\n" ^ indent ^ "}"
-end
-
-module PV = Values (* local module *)
-
-(** Pretty-printing for contexts *)
-module Contexts = struct
- let binder_to_string (bv : C.binder) : string =
- match bv.name with
- | None -> PV.var_id_to_string bv.index
- | Some name -> name
-
- let env_elem_to_string (fmt : PV.value_formatter) (indent : string)
- (indent_incr : string) (ev : C.env_elem) : string =
- match ev with
- | Var (var, tv) ->
- let bv =
- match var with Some var -> binder_to_string var | None -> "_"
- in
- indent ^ bv ^ " -> " ^ PV.typed_value_to_string fmt tv ^ " ;"
- | Abs abs -> PV.abs_to_string fmt indent indent_incr abs
- | Frame -> failwith "Can't print a Frame element"
-
- let opt_env_elem_to_string (fmt : PV.value_formatter) (indent : string)
- (indent_incr : string) (ev : C.env_elem option) : string =
- match ev with
- | None -> indent ^ "..."
- | Some ev -> env_elem_to_string fmt indent indent_incr ev
-
- (** Filters "dummy" bindings from an environment, to gain space and clarity/
- See [env_to_string]. *)
- let filter_env (env : C.env) : C.env_elem option list =
- (* We filter:
- * - non-dummy bindings which point to ⊥
- * - dummy bindings which don't contain loans nor borrows
- * Note that the first case can sometimes be confusing: we may try to improve
- * it...
- *)
- let filter_elem (ev : C.env_elem) : C.env_elem option =
- match ev with
- | Var (Some _, tv) ->
- (* Not a dummy binding: check if the value is ⊥ *)
- if VU.is_bottom tv.value then None else Some ev
- | Var (None, tv) ->
- (* Dummy binding: check if the value contains borrows or loans *)
- if VU.borrows_in_value tv || VU.loans_in_value tv then Some ev
- else None
- | _ -> Some ev
- in
- let env = List.map filter_elem env in
- (* We collapse groups of filtered values - so that we can print one
- * single "..." for a whole group of filtered values *)
- let rec group_filtered (env : C.env_elem option list) :
- C.env_elem option list =
- match env with
- | [] -> []
- | None :: None :: env -> group_filtered (None :: env)
- | x :: env -> x :: group_filtered env
- in
- group_filtered env
-
- (** Environments can have a lot of dummy or uninitialized values: [filter]
- allows to filter them when printing, replacing groups of such bindings with
- "..." to gain space and clarity.
- *)
- let env_to_string (filter : bool) (fmt : PV.value_formatter) (env : C.env) :
- string =
- let env =
- if filter then filter_env env else List.map (fun ev -> Some ev) env
- in
- "{\n"
- ^ String.concat "\n"
- (List.map (fun ev -> opt_env_elem_to_string fmt " " " " ev) env)
- ^ "\n}"
-
- type ctx_formatter = PV.value_formatter
-
- let ctx_to_etype_formatter (fmt : ctx_formatter) : PT.etype_formatter =
- PV.value_to_etype_formatter fmt
-
- let ctx_to_rtype_formatter (fmt : ctx_formatter) : PT.rtype_formatter =
- PV.value_to_rtype_formatter fmt
-
- let type_ctx_to_adt_variant_to_string_fun
- (ctx : T.type_decl T.TypeDeclId.Map.t) :
- T.TypeDeclId.id -> T.VariantId.id -> string =
- fun def_id variant_id ->
- let def = T.TypeDeclId.Map.find def_id ctx in
- match def.kind with
- | Struct _ | Opaque -> failwith "Unreachable"
- | Enum variants ->
- let variant = T.VariantId.nth variants variant_id in
- name_to_string def.name ^ "::" ^ variant.variant_name
-
- let type_ctx_to_adt_field_names_fun (ctx : T.type_decl T.TypeDeclId.Map.t) :
- T.TypeDeclId.id -> T.VariantId.id option -> string list option =
- fun def_id opt_variant_id ->
- let def = T.TypeDeclId.Map.find def_id ctx in
- let fields = TU.type_decl_get_fields def opt_variant_id in
- (* There are two cases: either all the fields have names, or none of them
- * has names *)
- let has_names =
- List.exists (fun f -> Option.is_some f.T.field_name) fields
- in
- if has_names then
- let fields = List.map (fun f -> Option.get f.T.field_name) fields in
- Some fields
- else None
-
- let eval_ctx_to_ctx_formatter (ctx : C.eval_ctx) : ctx_formatter =
- (* We shouldn't use rvar_to_string *)
- let rvar_to_string _r = failwith "Unexpected use of rvar_to_string" in
- let r_to_string r = PT.region_id_to_string r in
-
- let type_var_id_to_string vid =
- let v = C.lookup_type_var ctx vid in
- v.name
- in
- let type_decl_id_to_string def_id =
- let def = C.ctx_lookup_type_decl ctx def_id in
- name_to_string def.name
- in
- let adt_variant_to_string =
- type_ctx_to_adt_variant_to_string_fun ctx.type_context.type_decls
- in
- let var_id_to_string vid =
- let bv = C.ctx_lookup_binder ctx vid in
- binder_to_string bv
- in
- let adt_field_names =
- type_ctx_to_adt_field_names_fun ctx.type_context.type_decls
- in
- {
- rvar_to_string;
- r_to_string;
- type_var_id_to_string;
- type_decl_id_to_string;
- adt_variant_to_string;
- var_id_to_string;
- adt_field_names;
- }
-
- (** Split an [env] at every occurrence of [Frame], eliminating those elements.
- Also reorders the frames and the values in the frames according to the
- following order:
- * frames: from the current frame to the first pushed (oldest frame)
- * values: from the first pushed (oldest) to the last pushed
- *)
- let split_env_according_to_frames (env : C.env) : C.env list =
- let rec split_aux (frames : C.env list) (curr_frame : C.env) (env : C.env) =
- match env with
- | [] ->
- if List.length curr_frame > 0 then curr_frame :: frames else frames
- | Frame :: env' -> split_aux (curr_frame :: frames) [] env'
- | ev :: env' -> split_aux frames (ev :: curr_frame) env'
- in
- let frames = split_aux [] [] env in
- frames
-
- let eval_ctx_to_string (ctx : C.eval_ctx) : string =
- let fmt = eval_ctx_to_ctx_formatter ctx in
- let ended_regions = T.RegionId.Set.to_string None ctx.ended_regions in
- let frames = split_env_according_to_frames ctx.env in
- let num_frames = List.length frames in
- let frames =
- List.mapi
- (fun i f ->
- let num_bindings = ref 0 in
- let num_dummies = ref 0 in
- let num_abs = ref 0 in
- List.iter
- (fun ev ->
- match ev with
- | C.Var (None, _) -> num_dummies := !num_abs + 1
- | C.Var (Some _, _) -> num_bindings := !num_bindings + 1
- | C.Abs _ -> num_abs := !num_abs + 1
- | _ -> raise (Failure "Unreachable"))
- f;
- "\n# Frame " ^ string_of_int i ^ ":" ^ "\n- locals: "
- ^ string_of_int !num_bindings
- ^ "\n- dummy bindings: " ^ string_of_int !num_dummies
- ^ "\n- abstractions: " ^ string_of_int !num_abs ^ "\n"
- ^ env_to_string true fmt f ^ "\n")
- frames
- in
- "# Ended regions: " ^ ended_regions ^ "\n" ^ "# " ^ string_of_int num_frames
- ^ " frame(s)\n" ^ String.concat "" frames
-end
-
-module PC = Contexts (* local module *)
-
-(** Pretty-printing for contexts (generic functions) *)
-module LlbcAst = struct
- let var_to_string (var : A.var) : string =
- match var.name with
- | None -> V.VarId.to_string var.index
- | Some name -> name
-
- type ast_formatter = {
- rvar_to_string : T.RegionVarId.id -> string;
- r_to_string : T.RegionId.id -> string;
- type_var_id_to_string : T.TypeVarId.id -> string;
- type_decl_id_to_string : T.TypeDeclId.id -> string;
- adt_variant_to_string : T.TypeDeclId.id -> T.VariantId.id -> string;
- adt_field_to_string :
- T.TypeDeclId.id -> T.VariantId.id option -> T.FieldId.id -> string option;
- var_id_to_string : V.VarId.id -> string;
- adt_field_names :
- T.TypeDeclId.id -> T.VariantId.id option -> string list option;
- fun_decl_id_to_string : A.FunDeclId.id -> string;
- global_decl_id_to_string : A.GlobalDeclId.id -> string;
- }
-
- let ast_to_ctx_formatter (fmt : ast_formatter) : PC.ctx_formatter =
- {
- PV.rvar_to_string = fmt.rvar_to_string;
- PV.r_to_string = fmt.r_to_string;
- PV.type_var_id_to_string = fmt.type_var_id_to_string;
- PV.type_decl_id_to_string = fmt.type_decl_id_to_string;
- PV.adt_variant_to_string = fmt.adt_variant_to_string;
- PV.var_id_to_string = fmt.var_id_to_string;
- PV.adt_field_names = fmt.adt_field_names;
- }
-
- let ast_to_value_formatter (fmt : ast_formatter) : PV.value_formatter =
- ast_to_ctx_formatter fmt
-
- let ast_to_etype_formatter (fmt : ast_formatter) : PT.etype_formatter =
- {
- PT.r_to_string = PT.erased_region_to_string;
- PT.type_var_id_to_string = fmt.type_var_id_to_string;
- PT.type_decl_id_to_string = fmt.type_decl_id_to_string;
- }
-
- let ast_to_rtype_formatter (fmt : ast_formatter) : PT.rtype_formatter =
- {
- PT.r_to_string = PT.region_to_string fmt.r_to_string;
- PT.type_var_id_to_string = fmt.type_var_id_to_string;
- PT.type_decl_id_to_string = fmt.type_decl_id_to_string;
- }
-
- let ast_to_stype_formatter (fmt : ast_formatter) : PT.stype_formatter =
- {
- PT.r_to_string = PT.region_to_string fmt.rvar_to_string;
- PT.type_var_id_to_string = fmt.type_var_id_to_string;
- PT.type_decl_id_to_string = fmt.type_decl_id_to_string;
- }
-
- let type_ctx_to_adt_field_to_string_fun (ctx : T.type_decl T.TypeDeclId.Map.t)
- :
- T.TypeDeclId.id -> T.VariantId.id option -> T.FieldId.id -> string option
- =
- fun def_id opt_variant_id field_id ->
- let def = T.TypeDeclId.Map.find def_id ctx in
- let fields = TU.type_decl_get_fields def opt_variant_id in
- let field = T.FieldId.nth fields field_id in
- field.T.field_name
-
- let eval_ctx_to_ast_formatter (ctx : C.eval_ctx) : ast_formatter =
- let ctx_fmt = PC.eval_ctx_to_ctx_formatter ctx in
- let adt_field_to_string =
- type_ctx_to_adt_field_to_string_fun ctx.type_context.type_decls
- in
- let fun_decl_id_to_string def_id =
- let def = C.ctx_lookup_fun_decl ctx def_id in
- fun_name_to_string def.name
- in
- let global_decl_id_to_string def_id =
- let def = C.ctx_lookup_global_decl ctx def_id in
- global_name_to_string def.name
- in
- {
- rvar_to_string = ctx_fmt.PV.rvar_to_string;
- r_to_string = ctx_fmt.PV.r_to_string;
- type_var_id_to_string = ctx_fmt.PV.type_var_id_to_string;
- type_decl_id_to_string = ctx_fmt.PV.type_decl_id_to_string;
- adt_variant_to_string = ctx_fmt.PV.adt_variant_to_string;
- var_id_to_string = ctx_fmt.PV.var_id_to_string;
- adt_field_names = ctx_fmt.PV.adt_field_names;
- adt_field_to_string;
- fun_decl_id_to_string;
- global_decl_id_to_string;
- }
-
- let fun_decl_to_ast_formatter (type_decls : T.type_decl T.TypeDeclId.Map.t)
- (fun_decls : A.fun_decl A.FunDeclId.Map.t)
- (global_decls : A.global_decl A.GlobalDeclId.Map.t) (fdef : A.fun_decl) :
- ast_formatter =
- let rvar_to_string r =
- let rvar = T.RegionVarId.nth fdef.signature.region_params r in
- PT.region_var_to_string rvar
- in
- let r_to_string r = PT.region_id_to_string r in
-
- let type_var_id_to_string vid =
- let var = T.TypeVarId.nth fdef.signature.type_params vid in
- PT.type_var_to_string var
- in
- let type_decl_id_to_string def_id =
- let def = T.TypeDeclId.Map.find def_id type_decls in
- name_to_string def.name
- in
- let adt_variant_to_string =
- PC.type_ctx_to_adt_variant_to_string_fun type_decls
- in
- let var_id_to_string vid =
- let var = V.VarId.nth (Option.get fdef.body).locals vid in
- var_to_string var
- in
- let adt_field_names = PC.type_ctx_to_adt_field_names_fun type_decls in
- let adt_field_to_string = type_ctx_to_adt_field_to_string_fun type_decls in
- let fun_decl_id_to_string def_id =
- let def = A.FunDeclId.Map.find def_id fun_decls in
- fun_name_to_string def.name
- in
- let global_decl_id_to_string def_id =
- let def = A.GlobalDeclId.Map.find def_id global_decls in
- global_name_to_string def.name
- in
- {
- rvar_to_string;
- r_to_string;
- type_var_id_to_string;
- type_decl_id_to_string;
- adt_variant_to_string;
- var_id_to_string;
- adt_field_names;
- adt_field_to_string;
- fun_decl_id_to_string;
- global_decl_id_to_string;
- }
-
- let rec projection_to_string (fmt : ast_formatter) (inside : string)
- (p : E.projection) : string =
- match p with
- | [] -> inside
- | pe :: p' -> (
- let s = projection_to_string fmt inside p' in
- match pe with
- | E.Deref -> "*(" ^ s ^ ")"
- | E.DerefBox -> "deref_box(" ^ s ^ ")"
- | E.Field (E.ProjOption variant_id, fid) ->
- assert (variant_id = T.option_some_id);
- assert (fid = T.FieldId.zero);
- "(" ^ s ^ " as Option::Some)." ^ T.FieldId.to_string fid
- | E.Field (E.ProjTuple _, fid) ->
- "(" ^ s ^ ")." ^ T.FieldId.to_string fid
- | E.Field (E.ProjAdt (adt_id, opt_variant_id), fid) -> (
- let field_name =
- match fmt.adt_field_to_string adt_id opt_variant_id fid with
- | Some field_name -> field_name
- | None -> T.FieldId.to_string fid
- in
- match opt_variant_id with
- | None -> "(" ^ s ^ ")." ^ field_name
- | Some variant_id ->
- let variant_name =
- fmt.adt_variant_to_string adt_id variant_id
- in
- "(" ^ s ^ " as " ^ variant_name ^ ")." ^ field_name))
-
- let place_to_string (fmt : ast_formatter) (p : E.place) : string =
- let var = fmt.var_id_to_string p.E.var_id in
- projection_to_string fmt var p.E.projection
-
- let unop_to_string (unop : E.unop) : string =
- match unop with
- | E.Not -> "¬"
- | E.Neg -> "-"
- | E.Cast (src, tgt) ->
- "cast<"
- ^ PT.integer_type_to_string src
- ^ ","
- ^ PT.integer_type_to_string tgt
- ^ ">"
-
- let binop_to_string (binop : E.binop) : string =
- match binop with
- | E.BitXor -> "^"
- | E.BitAnd -> "&"
- | E.BitOr -> "|"
- | E.Eq -> "=="
- | E.Lt -> "<"
- | E.Le -> "<="
- | E.Ne -> "!="
- | E.Ge -> ">="
- | E.Gt -> ">"
- | E.Div -> "/"
- | E.Rem -> "%"
- | E.Add -> "+"
- | E.Sub -> "-"
- | E.Mul -> "*"
- | E.Shl -> "<<"
- | E.Shr -> ">>"
-
- let operand_to_string (fmt : ast_formatter) (op : E.operand) : string =
- match op with
- | E.Copy p -> "copy " ^ place_to_string fmt p
- | E.Move p -> "move " ^ place_to_string fmt p
- | E.Constant (ty, cv) ->
- "("
- ^ PV.constant_value_to_string cv
- ^ " : "
- ^ PT.ety_to_string (ast_to_etype_formatter fmt) ty
- ^ ")"
-
- let rvalue_to_string (fmt : ast_formatter) (rv : E.rvalue) : string =
- match rv with
- | E.Use op -> operand_to_string fmt op
- | E.Ref (p, bk) -> (
- let p = place_to_string fmt p in
- match bk with
- | E.Shared -> "&" ^ p
- | E.Mut -> "&mut " ^ p
- | E.TwoPhaseMut -> "&two-phase " ^ p)
- | E.UnaryOp (unop, op) ->
- unop_to_string unop ^ " " ^ operand_to_string fmt op
- | E.BinaryOp (binop, op1, op2) ->
- operand_to_string fmt op1 ^ " " ^ binop_to_string binop ^ " "
- ^ operand_to_string fmt op2
- | E.Discriminant p -> "discriminant(" ^ place_to_string fmt p ^ ")"
- | E.Aggregate (akind, ops) -> (
- let ops = List.map (operand_to_string fmt) ops in
- match akind with
- | E.AggregatedTuple -> "(" ^ String.concat ", " ops ^ ")"
- | E.AggregatedOption (variant_id, _ty) ->
- if variant_id == T.option_none_id then (
- assert (ops == []);
- "@Option::None")
- else if variant_id == T.option_some_id then (
- assert (List.length ops == 1);
- let op = List.hd ops in
- "@Option::Some(" ^ op ^ ")")
- else raise (Failure "Unreachable")
- | E.AggregatedAdt (def_id, opt_variant_id, _regions, _types) ->
- let adt_name = fmt.type_decl_id_to_string def_id in
- let variant_name =
- match opt_variant_id with
- | None -> adt_name
- | Some variant_id ->
- adt_name ^ "::" ^ fmt.adt_variant_to_string def_id variant_id
- in
- let fields =
- match fmt.adt_field_names def_id opt_variant_id with
- | None -> "(" ^ String.concat ", " ops ^ ")"
- | Some field_names ->
- let fields = List.combine field_names ops in
- let fields =
- List.map
- (fun (field, value) -> field ^ " = " ^ value ^ ";")
- fields
- in
- let fields = String.concat " " fields in
- "{ " ^ fields ^ " }"
- in
- variant_name ^ " " ^ fields)
-
- let rec statement_to_string (fmt : ast_formatter) (indent : string)
- (indent_incr : string) (st : A.statement) : string =
- raw_statement_to_string fmt indent indent_incr st.content
-
- and raw_statement_to_string (fmt : ast_formatter) (indent : string)
- (indent_incr : string) (st : A.raw_statement) : string =
- match st with
- | A.Assign (p, rv) ->
- indent ^ place_to_string fmt p ^ " := " ^ rvalue_to_string fmt rv
- | A.AssignGlobal { dst; global } ->
- indent ^ fmt.var_id_to_string dst ^ " := global "
- ^ fmt.global_decl_id_to_string global
- | A.FakeRead p -> indent ^ "fake_read " ^ place_to_string fmt p
- | A.SetDiscriminant (p, variant_id) ->
- (* TODO: improve this to lookup the variant name by using the def id *)
- indent ^ "set_discriminant(" ^ place_to_string fmt p ^ ", "
- ^ T.VariantId.to_string variant_id
- ^ ")"
- | A.Drop p -> indent ^ "drop(" ^ place_to_string fmt p ^ ")"
- | A.Assert a ->
- let cond = operand_to_string fmt a.A.cond in
- if a.A.expected then indent ^ "assert(" ^ cond ^ ")"
- else indent ^ "assert(¬" ^ cond ^ ")"
- | A.Call call ->
- let ty_fmt = ast_to_etype_formatter fmt in
- let t_params =
- if List.length call.A.type_args > 0 then
- "<"
- ^ String.concat ","
- (List.map (PT.ty_to_string ty_fmt) call.A.type_args)
- ^ ">"
- else ""
- in
- let args = List.map (operand_to_string fmt) call.A.args in
- let args = "(" ^ String.concat ", " args ^ ")" in
- let name_args =
- match call.A.func with
- | A.Regular fid -> fmt.fun_decl_id_to_string fid ^ t_params
- | A.Assumed fid -> (
- match fid with
- | A.Replace -> "core::mem::replace" ^ t_params
- | A.BoxNew -> "alloc::boxed::Box" ^ t_params ^ "::new"
- | A.BoxDeref ->
- "core::ops::deref::Deref<Box" ^ t_params ^ ">::deref"
- | A.BoxDerefMut ->
- "core::ops::deref::DerefMut" ^ t_params ^ "::deref_mut"
- | A.BoxFree -> "alloc::alloc::box_free" ^ t_params
- | A.VecNew -> "alloc::vec::Vec" ^ t_params ^ "::new"
- | A.VecPush -> "alloc::vec::Vec" ^ t_params ^ "::push"
- | A.VecInsert -> "alloc::vec::Vec" ^ t_params ^ "::insert"
- | A.VecLen -> "alloc::vec::Vec" ^ t_params ^ "::len"
- | A.VecIndex ->
- "core::ops::index::Index<alloc::vec::Vec" ^ t_params
- ^ ">::index"
- | A.VecIndexMut ->
- "core::ops::index::IndexMut<alloc::vec::Vec" ^ t_params
- ^ ">::index_mut")
- in
- let dest = place_to_string fmt call.A.dest in
- indent ^ dest ^ " := move " ^ name_args ^ args
- | A.Panic -> indent ^ "panic"
- | A.Return -> indent ^ "return"
- | A.Break i -> indent ^ "break " ^ string_of_int i
- | A.Continue i -> indent ^ "continue " ^ string_of_int i
- | A.Nop -> indent ^ "nop"
- | A.Sequence (st1, st2) ->
- statement_to_string fmt indent indent_incr st1
- ^ ";\n"
- ^ statement_to_string fmt indent indent_incr st2
- | A.Switch (op, tgts) -> (
- let op = operand_to_string fmt op in
- match tgts with
- | A.If (true_st, false_st) ->
- let inner_indent = indent ^ indent_incr in
- let inner_to_string =
- statement_to_string fmt inner_indent indent_incr
- in
- let true_st = inner_to_string true_st in
- let false_st = inner_to_string false_st in
- indent ^ "if (" ^ op ^ ") {\n" ^ true_st ^ "\n" ^ indent ^ "}\n"
- ^ indent ^ "else {\n" ^ false_st ^ "\n" ^ indent ^ "}"
- | A.SwitchInt (_ty, branches, otherwise) ->
- let indent1 = indent ^ indent_incr in
- let indent2 = indent1 ^ indent_incr in
- let inner_to_string2 =
- statement_to_string fmt indent2 indent_incr
- in
- let branches =
- List.map
- (fun (svl, be) ->
- let svl =
- List.map (fun sv -> "| " ^ PV.scalar_value_to_string sv) svl
- in
- let svl = String.concat " " svl in
- indent1 ^ svl ^ " => {\n" ^ inner_to_string2 be ^ "\n"
- ^ indent1 ^ "}")
- branches
- in
- let branches = String.concat "\n" branches in
- let branches =
- branches ^ "\n" ^ indent1 ^ "_ => {\n"
- ^ inner_to_string2 otherwise ^ "\n" ^ indent1 ^ "}"
- in
- indent ^ "switch (" ^ op ^ ") {\n" ^ branches ^ "\n" ^ indent ^ "}")
- | A.Loop loop_st ->
- indent ^ "loop {\n"
- ^ statement_to_string fmt (indent ^ indent_incr) indent_incr loop_st
- ^ "\n" ^ indent ^ "}"
-
- let var_to_string (v : A.var) : string =
- match v.name with None -> PV.var_id_to_string v.index | Some name -> name
-
- let fun_decl_to_string (fmt : ast_formatter) (indent : string)
- (indent_incr : string) (def : A.fun_decl) : string =
- let sty_fmt = ast_to_stype_formatter fmt in
- let sty_to_string = PT.sty_to_string sty_fmt in
- let ety_fmt = ast_to_etype_formatter fmt in
- let ety_to_string = PT.ety_to_string ety_fmt in
- let sg = def.signature in
-
- (* Function name *)
- let name = fun_name_to_string def.A.name in
-
- (* Region/type parameters *)
- let regions = sg.region_params in
- let types = sg.type_params in
- let params =
- if List.length regions + List.length types = 0 then ""
- else
- let regions = List.map PT.region_var_to_string regions in
- let types = List.map PT.type_var_to_string types in
- "<" ^ String.concat "," (List.append regions types) ^ ">"
- in
-
- (* Return type *)
- let ret_ty = sg.output in
- let ret_ty =
- if TU.ty_is_unit ret_ty then "" else " -> " ^ sty_to_string ret_ty
- in
-
- (* We print the declaration differently if it is opaque (no body) or transparent
- * (we have access to a body) *)
- match def.body with
- | None ->
- (* Arguments *)
- let input_tys = sg.inputs in
- let args = List.map sty_to_string input_tys in
- let args = String.concat ", " args in
-
- (* Put everything together *)
- indent ^ "opaque fn " ^ name ^ params ^ "(" ^ args ^ ")" ^ ret_ty
- | Some body ->
- (* Arguments *)
- let inputs = List.tl body.locals in
- let inputs, _aux_locals =
- Collections.List.split_at inputs body.arg_count
- in
- let args = List.combine inputs sg.inputs in
- let args =
- List.map
- (fun (var, rty) -> var_to_string var ^ " : " ^ sty_to_string rty)
- args
- in
- let args = String.concat ", " args in
-
- (* All the locals (with erased regions) *)
- let locals =
- List.map
- (fun var ->
- indent ^ indent_incr ^ var_to_string var ^ " : "
- ^ ety_to_string var.var_ty ^ ";")
- body.locals
- in
- let locals = String.concat "\n" locals in
-
- (* Body *)
- let body =
- statement_to_string fmt (indent ^ indent_incr) indent_incr body.body
- in
-
- (* Put everything together *)
- indent ^ "fn " ^ name ^ params ^ "(" ^ args ^ ")" ^ ret_ty ^ " {\n"
- ^ locals ^ "\n\n" ^ body ^ "\n" ^ indent ^ "}"
-end
-
-module PA = LlbcAst (* local module *)
-
-(** Pretty-printing for ASTs (functions based on a definition context) *)
-module Module = struct
- (** This function pretty-prints a type definition by using a definition
- context *)
- let type_decl_to_string (type_context : T.type_decl T.TypeDeclId.Map.t)
- (def : T.type_decl) : string =
- let type_decl_id_to_string (id : T.TypeDeclId.id) : string =
- let def = T.TypeDeclId.Map.find id type_context in
- name_to_string def.name
- in
- PT.type_decl_to_string type_decl_id_to_string def
-
- (** Generate an [ast_formatter] by using a definition context in combination
- with the variables local to a function's definition *)
- let def_ctx_to_ast_formatter (type_context : T.type_decl T.TypeDeclId.Map.t)
- (fun_context : A.fun_decl A.FunDeclId.Map.t)
- (global_context : A.global_decl A.GlobalDeclId.Map.t) (def : A.fun_decl) :
- PA.ast_formatter =
- let rvar_to_string vid =
- let var = T.RegionVarId.nth def.signature.region_params vid in
- PT.region_var_to_string var
- in
- let r_to_string vid =
- (* TODO: we might want something more informative *)
- PT.region_id_to_string vid
- in
- let type_var_id_to_string vid =
- let var = T.TypeVarId.nth def.signature.type_params vid in
- PT.type_var_to_string var
- in
- let type_decl_id_to_string def_id =
- let def = T.TypeDeclId.Map.find def_id type_context in
- name_to_string def.name
- in
- let fun_decl_id_to_string def_id =
- let def = A.FunDeclId.Map.find def_id fun_context in
- fun_name_to_string def.name
- in
- let global_decl_id_to_string def_id =
- let def = A.GlobalDeclId.Map.find def_id global_context in
- global_name_to_string def.name
- in
- let var_id_to_string vid =
- let var = V.VarId.nth (Option.get def.body).locals vid in
- PA.var_to_string var
- in
- let adt_variant_to_string =
- PC.type_ctx_to_adt_variant_to_string_fun type_context
- in
- let adt_field_to_string =
- PA.type_ctx_to_adt_field_to_string_fun type_context
- in
- let adt_field_names = PC.type_ctx_to_adt_field_names_fun type_context in
- {
- rvar_to_string;
- r_to_string;
- type_var_id_to_string;
- type_decl_id_to_string;
- adt_variant_to_string;
- adt_field_to_string;
- var_id_to_string;
- adt_field_names;
- fun_decl_id_to_string;
- global_decl_id_to_string;
- }
-
- (** This function pretty-prints a function definition by using a definition
- context *)
- let fun_decl_to_string (type_context : T.type_decl T.TypeDeclId.Map.t)
- (fun_context : A.fun_decl A.FunDeclId.Map.t)
- (global_context : A.global_decl A.GlobalDeclId.Map.t) (def : A.fun_decl) :
- string =
- let fmt =
- def_ctx_to_ast_formatter type_context fun_context global_context def
- in
- PA.fun_decl_to_string fmt "" " " def
-
- let module_to_string (m : Crates.llbc_crate) : string =
- let types_defs_map, funs_defs_map, globals_defs_map =
- Crates.compute_defs_maps m
- in
-
- (* The types *)
- let type_decls =
- List.map (type_decl_to_string types_defs_map) m.Crates.types
- in
-
- (* The functions *)
- let fun_decls =
- List.map
- (fun_decl_to_string types_defs_map funs_defs_map globals_defs_map)
- m.Crates.functions
- in
-
- (* Put everything together *)
- let all_defs = List.append type_decls fun_decls in
- String.concat "\n\n" all_defs
-end
-
-(** Pretty-printing for LLBC ASTs (functions based on an evaluation context) *)
-module EvalCtxLlbcAst = struct
- let ety_to_string (ctx : C.eval_ctx) (t : T.ety) : string =
- let fmt = PC.eval_ctx_to_ctx_formatter ctx in
- let fmt = PC.ctx_to_etype_formatter fmt in
- PT.ety_to_string fmt t
-
- let rty_to_string (ctx : C.eval_ctx) (t : T.rty) : string =
- let fmt = PC.eval_ctx_to_ctx_formatter ctx in
- let fmt = PC.ctx_to_rtype_formatter fmt in
- PT.rty_to_string fmt t
-
- let borrow_content_to_string (ctx : C.eval_ctx) (bc : V.borrow_content) :
- string =
- let fmt = PC.eval_ctx_to_ctx_formatter ctx in
- PV.borrow_content_to_string fmt bc
-
- let loan_content_to_string (ctx : C.eval_ctx) (lc : V.loan_content) : string =
- let fmt = PC.eval_ctx_to_ctx_formatter ctx in
- PV.loan_content_to_string fmt lc
-
- let aborrow_content_to_string (ctx : C.eval_ctx) (bc : V.aborrow_content) :
- string =
- let fmt = PC.eval_ctx_to_ctx_formatter ctx in
- PV.aborrow_content_to_string fmt bc
-
- let aloan_content_to_string (ctx : C.eval_ctx) (lc : V.aloan_content) : string
- =
- let fmt = PC.eval_ctx_to_ctx_formatter ctx in
- PV.aloan_content_to_string fmt lc
-
- let aproj_to_string (ctx : C.eval_ctx) (p : V.aproj) : string =
- let fmt = PC.eval_ctx_to_ctx_formatter ctx in
- PV.aproj_to_string fmt p
-
- let symbolic_value_to_string (ctx : C.eval_ctx) (sv : V.symbolic_value) :
- string =
- let fmt = PC.eval_ctx_to_ctx_formatter ctx in
- let fmt = PC.ctx_to_rtype_formatter fmt in
- PV.symbolic_value_to_string fmt sv
-
- let typed_value_to_string (ctx : C.eval_ctx) (v : V.typed_value) : string =
- let fmt = PC.eval_ctx_to_ctx_formatter ctx in
- PV.typed_value_to_string fmt v
-
- let typed_avalue_to_string (ctx : C.eval_ctx) (v : V.typed_avalue) : string =
- let fmt = PC.eval_ctx_to_ctx_formatter ctx in
- PV.typed_avalue_to_string fmt v
-
- let place_to_string (ctx : C.eval_ctx) (op : E.place) : string =
- let fmt = PA.eval_ctx_to_ast_formatter ctx in
- PA.place_to_string fmt op
-
- let operand_to_string (ctx : C.eval_ctx) (op : E.operand) : string =
- let fmt = PA.eval_ctx_to_ast_formatter ctx in
- PA.operand_to_string fmt op
-
- let statement_to_string (ctx : C.eval_ctx) (indent : string)
- (indent_incr : string) (e : A.statement) : string =
- let fmt = PA.eval_ctx_to_ast_formatter ctx in
- PA.statement_to_string fmt indent indent_incr e
-end
diff --git a/src/PrintPure.ml b/src/PrintPure.ml
deleted file mode 100644
index a9e42f6c..00000000
--- a/src/PrintPure.ml
+++ /dev/null
@@ -1,594 +0,0 @@
-(** This module defines printing functions for the types defined in Pure.ml *)
-
-open Pure
-open PureUtils
-
-type type_formatter = {
- type_var_id_to_string : TypeVarId.id -> string;
- type_decl_id_to_string : TypeDeclId.id -> string;
-}
-
-type value_formatter = {
- type_var_id_to_string : TypeVarId.id -> string;
- type_decl_id_to_string : TypeDeclId.id -> string;
- adt_variant_to_string : TypeDeclId.id -> VariantId.id -> string;
- var_id_to_string : VarId.id -> string;
- adt_field_names : TypeDeclId.id -> VariantId.id option -> string list option;
-}
-
-let value_to_type_formatter (fmt : value_formatter) : type_formatter =
- {
- type_var_id_to_string = fmt.type_var_id_to_string;
- type_decl_id_to_string = fmt.type_decl_id_to_string;
- }
-
-(* TODO: we need to store which variables we have encountered so far, and
- remove [var_id_to_string].
-*)
-type ast_formatter = {
- type_var_id_to_string : TypeVarId.id -> string;
- type_decl_id_to_string : TypeDeclId.id -> string;
- adt_variant_to_string : TypeDeclId.id -> VariantId.id -> string;
- var_id_to_string : VarId.id -> string;
- adt_field_to_string :
- TypeDeclId.id -> VariantId.id option -> FieldId.id -> string option;
- adt_field_names : TypeDeclId.id -> VariantId.id option -> string list option;
- fun_decl_id_to_string : FunDeclId.id -> string;
- global_decl_id_to_string : GlobalDeclId.id -> string;
-}
-
-let ast_to_value_formatter (fmt : ast_formatter) : value_formatter =
- {
- type_var_id_to_string = fmt.type_var_id_to_string;
- type_decl_id_to_string = fmt.type_decl_id_to_string;
- adt_variant_to_string = fmt.adt_variant_to_string;
- var_id_to_string = fmt.var_id_to_string;
- adt_field_names = fmt.adt_field_names;
- }
-
-let ast_to_type_formatter (fmt : ast_formatter) : type_formatter =
- let fmt = ast_to_value_formatter fmt in
- value_to_type_formatter fmt
-
-let name_to_string = Print.name_to_string
-let fun_name_to_string = Print.fun_name_to_string
-let global_name_to_string = Print.global_name_to_string
-let option_to_string = Print.option_to_string
-let type_var_to_string = Print.Types.type_var_to_string
-let integer_type_to_string = Print.Types.integer_type_to_string
-let scalar_value_to_string = Print.Values.scalar_value_to_string
-
-let mk_type_formatter (type_decls : T.type_decl TypeDeclId.Map.t)
- (type_params : type_var list) : type_formatter =
- let type_var_id_to_string vid =
- let var = T.TypeVarId.nth type_params vid in
- type_var_to_string var
- in
- let type_decl_id_to_string def_id =
- let def = T.TypeDeclId.Map.find def_id type_decls in
- name_to_string def.name
- in
- { type_var_id_to_string; type_decl_id_to_string }
-
-(* TODO: there is a bit of duplication with Print.fun_decl_to_ast_formatter.
-
- TODO: use the pure defs as inputs? Note that it is a bit annoying for the
- functions (there is a difference between the forward/backward functions...)
- while we only need those definitions to lookup proper names for the def ids.
-*)
-let mk_ast_formatter (type_decls : T.type_decl TypeDeclId.Map.t)
- (fun_decls : A.fun_decl FunDeclId.Map.t)
- (global_decls : A.global_decl GlobalDeclId.Map.t)
- (type_params : type_var list) : ast_formatter =
- let type_var_id_to_string vid =
- let var = T.TypeVarId.nth type_params vid in
- type_var_to_string var
- in
- let type_decl_id_to_string def_id =
- let def = T.TypeDeclId.Map.find def_id type_decls in
- name_to_string def.name
- in
- let adt_variant_to_string =
- Print.Contexts.type_ctx_to_adt_variant_to_string_fun type_decls
- in
- let var_id_to_string vid =
- (* TODO: somehow lookup in the context *)
- "^" ^ VarId.to_string vid
- in
- let adt_field_names =
- Print.Contexts.type_ctx_to_adt_field_names_fun type_decls
- in
- let adt_field_to_string =
- Print.LlbcAst.type_ctx_to_adt_field_to_string_fun type_decls
- in
- let fun_decl_id_to_string def_id =
- let def = FunDeclId.Map.find def_id fun_decls in
- fun_name_to_string def.name
- in
- let global_decl_id_to_string def_id =
- let def = GlobalDeclId.Map.find def_id global_decls in
- global_name_to_string def.name
- in
- {
- type_var_id_to_string;
- type_decl_id_to_string;
- adt_variant_to_string;
- var_id_to_string;
- adt_field_names;
- adt_field_to_string;
- fun_decl_id_to_string;
- global_decl_id_to_string;
- }
-
-let type_id_to_string (fmt : type_formatter) (id : type_id) : string =
- match id with
- | AdtId id -> fmt.type_decl_id_to_string id
- | Tuple -> ""
- | Assumed aty -> (
- match aty with
- | State -> "State"
- | Result -> "Result"
- | Option -> "Option"
- | Vec -> "Vec")
-
-let rec ty_to_string (fmt : type_formatter) (ty : ty) : string =
- match ty with
- | Adt (id, tys) -> (
- let tys = List.map (ty_to_string fmt) tys in
- match id with
- | Tuple -> "(" ^ String.concat " * " tys ^ ")"
- | AdtId _ | Assumed _ ->
- let tys = if tys = [] then "" else " " ^ String.concat " " tys in
- type_id_to_string fmt id ^ tys)
- | TypeVar tv -> fmt.type_var_id_to_string tv
- | Bool -> "bool"
- | Char -> "char"
- | Integer int_ty -> integer_type_to_string int_ty
- | Str -> "str"
- | Array aty -> "[" ^ ty_to_string fmt aty ^ "; ?]"
- | Slice sty -> "[" ^ ty_to_string fmt sty ^ "]"
- | Arrow (arg_ty, ret_ty) ->
- ty_to_string fmt arg_ty ^ " -> " ^ ty_to_string fmt ret_ty
-
-let field_to_string fmt (f : field) : string =
- match f.field_name with
- | None -> ty_to_string fmt f.field_ty
- | Some field_name -> field_name ^ " : " ^ ty_to_string fmt f.field_ty
-
-let variant_to_string fmt (v : variant) : string =
- v.variant_name ^ "("
- ^ String.concat ", " (List.map (field_to_string fmt) v.fields)
- ^ ")"
-
-let type_decl_to_string (fmt : type_formatter) (def : type_decl) : string =
- let types = def.type_params in
- let name = name_to_string def.name in
- let params =
- if types = [] then ""
- else " " ^ String.concat " " (List.map type_var_to_string types)
- in
- match def.kind with
- | Struct fields ->
- if List.length fields > 0 then
- let fields =
- String.concat ","
- (List.map (fun f -> "\n " ^ field_to_string fmt f) fields)
- in
- "struct " ^ name ^ params ^ "{" ^ fields ^ "}"
- else "struct " ^ name ^ params ^ "{}"
- | Enum variants ->
- let variants =
- List.map (fun v -> "| " ^ variant_to_string fmt v) variants
- in
- let variants = String.concat "\n" variants in
- "enum " ^ name ^ params ^ " =\n" ^ variants
- | Opaque -> "opaque type " ^ name ^ params
-
-let var_to_varname (v : var) : string =
- match v.basename with
- | Some name -> name ^ "^" ^ VarId.to_string v.id
- | None -> "^" ^ VarId.to_string v.id
-
-let var_to_string (fmt : type_formatter) (v : var) : string =
- let varname = var_to_varname v in
- "(" ^ varname ^ " : " ^ ty_to_string fmt v.ty ^ ")"
-
-let rec mprojection_to_string (fmt : ast_formatter) (inside : string)
- (p : mprojection) : string =
- match p with
- | [] -> inside
- | pe :: p' -> (
- let s = mprojection_to_string fmt inside p' in
- match pe.pkind with
- | E.ProjOption variant_id ->
- assert (variant_id = T.option_some_id);
- assert (pe.field_id = T.FieldId.zero);
- "(" ^ s ^ "as Option::Some)." ^ T.FieldId.to_string pe.field_id
- | E.ProjTuple _ -> "(" ^ s ^ ")." ^ T.FieldId.to_string pe.field_id
- | E.ProjAdt (adt_id, opt_variant_id) -> (
- let field_name =
- match fmt.adt_field_to_string adt_id opt_variant_id pe.field_id with
- | Some field_name -> field_name
- | None -> T.FieldId.to_string pe.field_id
- in
- match opt_variant_id with
- | None -> "(" ^ s ^ ")." ^ field_name
- | Some variant_id ->
- let variant_name = fmt.adt_variant_to_string adt_id variant_id in
- "(" ^ s ^ " as " ^ variant_name ^ ")." ^ field_name))
-
-let mplace_to_string (fmt : ast_formatter) (p : mplace) : string =
- let name = match p.name with None -> "" | Some name -> name in
- (* We add the "llbc" suffix to the variable index, because meta-places
- * use indices of the variables in the original LLBC program, while
- * regular places use indices for the pure variables: we want to make
- * this explicit, otherwise it is confusing. *)
- let name = name ^ "^" ^ V.VarId.to_string p.var_id ^ "llbc" in
- mprojection_to_string fmt name p.projection
-
-let adt_variant_to_string (fmt : value_formatter) (adt_id : type_id)
- (variant_id : VariantId.id option) : string =
- match adt_id with
- | Tuple -> "Tuple"
- | AdtId def_id -> (
- (* "Regular" ADT *)
- match variant_id with
- | Some vid -> fmt.adt_variant_to_string def_id vid
- | None -> fmt.type_decl_id_to_string def_id)
- | Assumed aty -> (
- (* Assumed type *)
- match aty with
- | State ->
- (* The [State] type is opaque: we can't get there *)
- raise (Failure "Unreachable")
- | Result ->
- let variant_id = Option.get variant_id in
- if variant_id = result_return_id then "@Result::Return"
- else if variant_id = result_fail_id then "@Result::Fail"
- else
- raise (Failure "Unreachable: improper variant id for result type")
- | Option ->
- let variant_id = Option.get variant_id in
- if variant_id = option_some_id then "@Option::Some "
- else if variant_id = option_none_id then "@Option::None"
- else
- raise (Failure "Unreachable: improper variant id for result type")
- | Vec ->
- assert (variant_id = None);
- "Vec")
-
-let adt_field_to_string (fmt : value_formatter) (adt_id : type_id)
- (field_id : FieldId.id) : string =
- match adt_id with
- | Tuple ->
- raise (Failure "Unreachable")
- (* Tuples don't use the opaque field id for the field indices, but [int] *)
- | AdtId def_id -> (
- (* "Regular" ADT *)
- let fields = fmt.adt_field_names def_id None in
- match fields with
- | None -> FieldId.to_string field_id
- | Some fields -> FieldId.nth fields field_id)
- | Assumed aty -> (
- (* Assumed type *)
- match aty with
- | State | Vec ->
- (* Opaque types: we can't get there *)
- raise (Failure "Unreachable")
- | Result | Option ->
- (* Enumerations: we can't get there *)
- raise (Failure "Unreachable"))
-
-(** TODO: we don't need a general function anymore (it is now only used for
- patterns (i.e., patterns)
- *)
-let adt_g_value_to_string (fmt : value_formatter)
- (value_to_string : 'v -> string) (variant_id : VariantId.id option)
- (field_values : 'v list) (ty : ty) : string =
- let field_values = List.map value_to_string field_values in
- match ty with
- | Adt (Tuple, _) ->
- (* Tuple *)
- "(" ^ String.concat ", " field_values ^ ")"
- | Adt (AdtId def_id, _) ->
- (* "Regular" ADT *)
- let adt_ident =
- match variant_id with
- | Some vid -> fmt.adt_variant_to_string def_id vid
- | None -> fmt.type_decl_id_to_string def_id
- in
- if field_values <> [] then
- match fmt.adt_field_names def_id variant_id with
- | None ->
- let field_values = String.concat ", " field_values in
- adt_ident ^ " (" ^ field_values ^ ")"
- | Some field_names ->
- let field_values = List.combine field_names field_values in
- let field_values =
- List.map
- (fun (field, value) -> field ^ " = " ^ value ^ ";")
- field_values
- in
- let field_values = String.concat " " field_values in
- adt_ident ^ " { " ^ field_values ^ " }"
- else adt_ident
- | Adt (Assumed aty, _) -> (
- (* Assumed type *)
- match aty with
- | State ->
- (* The [State] type is opaque: we can't get there *)
- raise (Failure "Unreachable")
- | Result ->
- let variant_id = Option.get variant_id in
- if variant_id = result_return_id then
- match field_values with
- | [ v ] -> "@Result::Return " ^ v
- | _ -> raise (Failure "Result::Return takes exactly one value")
- else if variant_id = result_fail_id then (
- assert (field_values = []);
- "@Result::Fail")
- else
- raise (Failure "Unreachable: improper variant id for result type")
- | Option ->
- let variant_id = Option.get variant_id in
- if variant_id = option_some_id then
- match field_values with
- | [ v ] -> "@Option::Some " ^ v
- | _ -> raise (Failure "Option::Some takes exactly one value")
- else if variant_id = option_none_id then (
- assert (field_values = []);
- "@Option::None")
- else
- raise (Failure "Unreachable: improper variant id for result type")
- | Vec ->
- assert (variant_id = None);
- let field_values =
- List.mapi (fun i v -> string_of_int i ^ " -> " ^ v) field_values
- in
- "Vec [" ^ String.concat "; " field_values ^ "]")
- | _ ->
- let fmt = value_to_type_formatter fmt in
- raise
- (Failure
- ("Inconsistently typed value: expected ADT type but found:"
- ^ "\n- ty: " ^ ty_to_string fmt ty ^ "\n- variant_id: "
- ^ Print.option_to_string VariantId.to_string variant_id))
-
-let rec typed_pattern_to_string (fmt : ast_formatter) (v : typed_pattern) :
- string =
- match v.value with
- | PatConcrete cv -> Print.Values.constant_value_to_string cv
- | PatVar (v, None) -> var_to_string (ast_to_type_formatter fmt) v
- | PatVar (v, Some mp) ->
- let mp = "[@mplace=" ^ mplace_to_string fmt mp ^ "]" in
- "(" ^ var_to_varname v ^ " " ^ mp ^ " : "
- ^ ty_to_string (ast_to_type_formatter fmt) v.ty
- ^ ")"
- | PatDummy -> "_"
- | PatAdt av ->
- adt_g_value_to_string
- (ast_to_value_formatter fmt)
- (typed_pattern_to_string fmt)
- av.variant_id av.field_values v.ty
-
-let fun_sig_to_string (fmt : ast_formatter) (sg : fun_sig) : string =
- let ty_fmt = ast_to_type_formatter fmt in
- let type_params = List.map type_var_to_string sg.type_params in
- let inputs = List.map (ty_to_string ty_fmt) sg.inputs in
- let output = ty_to_string ty_fmt sg.output in
- let all_types = List.concat [ type_params; inputs; [ output ] ] in
- String.concat " -> " all_types
-
-let inst_fun_sig_to_string (fmt : ast_formatter) (sg : inst_fun_sig) : string =
- let ty_fmt = ast_to_type_formatter fmt in
- let inputs = List.map (ty_to_string ty_fmt) sg.inputs in
- let output = ty_to_string ty_fmt sg.output in
- let all_types = List.append inputs [ output ] in
- String.concat " -> " all_types
-
-let regular_fun_id_to_string (fmt : ast_formatter) (fun_id : A.fun_id) : string
- =
- match fun_id with
- | A.Regular fid -> fmt.fun_decl_id_to_string fid
- | A.Assumed fid -> (
- match fid with
- | A.Replace -> "core::mem::replace"
- | A.BoxNew -> "alloc::boxed::Box::new"
- | A.BoxDeref -> "core::ops::deref::Deref::deref"
- | A.BoxDerefMut -> "core::ops::deref::DerefMut::deref_mut"
- | A.BoxFree -> "alloc::alloc::box_free"
- | A.VecNew -> "alloc::vec::Vec::new"
- | A.VecPush -> "alloc::vec::Vec::push"
- | A.VecInsert -> "alloc::vec::Vec::insert"
- | A.VecLen -> "alloc::vec::Vec::len"
- | A.VecIndex -> "core::ops::index::Index<alloc::vec::Vec>::index"
- | A.VecIndexMut ->
- "core::ops::index::IndexMut<alloc::vec::Vec>::index_mut")
-
-let fun_suffix (rg_id : T.RegionGroupId.id option) : string =
- match rg_id with
- | None -> ""
- | Some rg_id -> "@" ^ T.RegionGroupId.to_string rg_id
-
-let unop_to_string (unop : unop) : string =
- match unop with
- | Not -> "¬"
- | Neg _ -> "-"
- | Cast (src, tgt) ->
- "cast<" ^ integer_type_to_string src ^ "," ^ integer_type_to_string tgt
- ^ ">"
-
-let binop_to_string = Print.LlbcAst.binop_to_string
-
-let fun_id_to_string (fmt : ast_formatter) (fun_id : fun_id) : string =
- match fun_id with
- | Regular (fun_id, rg_id) ->
- let f = regular_fun_id_to_string fmt fun_id in
- f ^ fun_suffix rg_id
- | Unop unop -> unop_to_string unop
- | Binop (binop, int_ty) ->
- binop_to_string binop ^ "<" ^ integer_type_to_string int_ty ^ ">"
-
-(** [inside]: controls the introduction of parentheses *)
-let rec texpression_to_string (fmt : ast_formatter) (inside : bool)
- (indent : string) (indent_incr : string) (e : texpression) : string =
- match e.e with
- | Var var_id ->
- let s = fmt.var_id_to_string var_id in
- if inside then "(" ^ s ^ ")" else s
- | Const cv -> Print.Values.constant_value_to_string cv
- | App _ ->
- (* Recursively destruct the app, to have a pair (app, arguments list) *)
- let app, args = destruct_apps e in
- (* Convert to string *)
- app_to_string fmt inside indent indent_incr app args
- | Abs _ ->
- let xl, e = destruct_abs_list e in
- let e = abs_to_string fmt indent indent_incr xl e in
- if inside then "(" ^ e ^ ")" else e
- | Qualif _ ->
- (* Qualifier without arguments *)
- app_to_string fmt inside indent indent_incr e []
- | Let (monadic, lv, re, e) ->
- let e = let_to_string fmt indent indent_incr monadic lv re e in
- if inside then "(" ^ e ^ ")" else e
- | Switch (scrutinee, body) ->
- let e = switch_to_string fmt indent indent_incr scrutinee body in
- if inside then "(" ^ e ^ ")" else e
- | Meta (meta, e) -> (
- let meta_s = meta_to_string fmt meta in
- let e = texpression_to_string fmt inside indent indent_incr e in
- match meta with
- | Assignment _ ->
- let e = meta_s ^ "\n" ^ indent ^ e in
- if inside then "(" ^ e ^ ")" else e
- | MPlace _ -> "(" ^ meta_s ^ " " ^ e ^ ")")
-
-and app_to_string (fmt : ast_formatter) (inside : bool) (indent : string)
- (indent_incr : string) (app : texpression) (args : texpression list) :
- string =
- (* There are two possibilities: either the [app] is an instantiated,
- * top-level qualifier (function, ADT constructore...), or it is a "regular"
- * expression *)
- let app, tys =
- match app.e with
- | Qualif qualif ->
- (* Qualifier case *)
- (* Convert the qualifier identifier *)
- let qualif_s =
- match qualif.id with
- | Func fun_id -> fun_id_to_string fmt fun_id
- | Global global_id -> fmt.global_decl_id_to_string global_id
- | AdtCons adt_cons_id ->
- let variant_s =
- adt_variant_to_string
- (ast_to_value_formatter fmt)
- adt_cons_id.adt_id adt_cons_id.variant_id
- in
- ConstStrings.constructor_prefix ^ variant_s
- | Proj { adt_id; field_id } ->
- let value_fmt = ast_to_value_formatter fmt in
- let adt_s = adt_variant_to_string value_fmt adt_id None in
- let field_s = adt_field_to_string value_fmt adt_id field_id in
- (* Adopting an F*-like syntax *)
- ConstStrings.constructor_prefix ^ adt_s ^ "?." ^ field_s
- in
- (* Convert the type instantiation *)
- let ty_fmt = ast_to_type_formatter fmt in
- let tys = List.map (ty_to_string ty_fmt) qualif.type_args in
- (* *)
- (qualif_s, tys)
- | _ ->
- (* "Regular" expression case *)
- let inside = args <> [] || (args = [] && inside) in
- (texpression_to_string fmt inside indent indent_incr app, [])
- in
- (* Convert the arguments.
- * The arguments are expressions, so indentation might get weird... (though
- * those expressions will in most cases just be values) *)
- let arg_to_string =
- let inside = true in
- let indent1 = indent ^ indent_incr in
- texpression_to_string fmt inside indent1 indent_incr
- in
- let args = List.map arg_to_string args in
- let all_args = List.append tys args in
- (* Put together *)
- let e =
- if all_args = [] then app else app ^ " " ^ String.concat " " all_args
- in
- (* Add parentheses *)
- if all_args <> [] && inside then "(" ^ e ^ ")" else e
-
-and abs_to_string (fmt : ast_formatter) (indent : string) (indent_incr : string)
- (xl : typed_pattern list) (e : texpression) : string =
- let xl = List.map (typed_pattern_to_string fmt) xl in
- let e = texpression_to_string fmt false indent indent_incr e in
- "λ " ^ String.concat " " xl ^ ". " ^ e
-
-and let_to_string (fmt : ast_formatter) (indent : string) (indent_incr : string)
- (monadic : bool) (lv : typed_pattern) (re : texpression) (e : texpression) :
- string =
- let indent1 = indent ^ indent_incr in
- let inside = false in
- let re = texpression_to_string fmt inside indent1 indent_incr re in
- let e = texpression_to_string fmt inside indent indent_incr e in
- let lv = typed_pattern_to_string fmt lv in
- if monadic then lv ^ " <-- " ^ re ^ ";\n" ^ indent ^ e
- else "let " ^ lv ^ " = " ^ re ^ " in\n" ^ indent ^ e
-
-and switch_to_string (fmt : ast_formatter) (indent : string)
- (indent_incr : string) (scrutinee : texpression) (body : switch_body) :
- string =
- let indent1 = indent ^ indent_incr in
- (* Printing can mess up on the scrutinee, because it is an expression - but
- * in most situations it will be a value or a function call, so it should be
- * ok*)
- let scrut = texpression_to_string fmt true indent1 indent_incr scrutinee in
- let e_to_string = texpression_to_string fmt false indent1 indent_incr in
- match body with
- | If (e_true, e_false) ->
- let e_true = e_to_string e_true in
- let e_false = e_to_string e_false in
- "if " ^ scrut ^ "\n" ^ indent ^ "then\n" ^ indent1 ^ e_true ^ "\n"
- ^ indent ^ "else\n" ^ indent1 ^ e_false
- | Match branches ->
- let branch_to_string (b : match_branch) : string =
- let pat = typed_pattern_to_string fmt b.pat in
- indent ^ "| " ^ pat ^ " ->\n" ^ indent1 ^ e_to_string b.branch
- in
- let branches = List.map branch_to_string branches in
- "match " ^ scrut ^ " with\n" ^ String.concat "\n" branches
-
-and meta_to_string (fmt : ast_formatter) (meta : meta) : string =
- let meta =
- match meta with
- | Assignment (lp, rv, rp) ->
- let rp =
- match rp with
- | None -> ""
- | Some rp -> " [@src=" ^ mplace_to_string fmt rp ^ "]"
- in
- "@assign(" ^ mplace_to_string fmt lp ^ " := "
- ^ texpression_to_string fmt false "" "" rv
- ^ rp ^ ")"
- | MPlace mp -> "@mplace=" ^ mplace_to_string fmt mp
- in
- "@meta[" ^ meta ^ "]"
-
-let fun_decl_to_string (fmt : ast_formatter) (def : fun_decl) : string =
- let type_fmt = ast_to_type_formatter fmt in
- let name = fun_name_to_string def.basename ^ fun_suffix def.back_id in
- let signature = fun_sig_to_string fmt def.signature in
- match def.body with
- | None -> "val " ^ name ^ " :\n " ^ signature
- | Some body ->
- let inside = false in
- let indent = " " in
- let inputs = List.map (var_to_string type_fmt) body.inputs in
- let inputs =
- if inputs = [] then indent
- else " fun " ^ String.concat " " inputs ^ " ->\n" ^ indent
- in
- let body = texpression_to_string fmt inside indent indent body.body in
- "let " ^ name ^ " :\n " ^ signature ^ " =\n" ^ inputs ^ body
diff --git a/src/Pure.ml b/src/Pure.ml
deleted file mode 100644
index 77265f75..00000000
--- a/src/Pure.ml
+++ /dev/null
@@ -1,581 +0,0 @@
-open Identifiers
-open Names
-module T = Types
-module V = Values
-module E = Expressions
-module A = LlbcAst
-module TypeDeclId = T.TypeDeclId
-module TypeVarId = T.TypeVarId
-module RegionGroupId = T.RegionGroupId
-module VariantId = T.VariantId
-module FieldId = T.FieldId
-module SymbolicValueId = V.SymbolicValueId
-module FunDeclId = A.FunDeclId
-module GlobalDeclId = A.GlobalDeclId
-
-(** We give an identifier to every phase of the synthesis (forward, backward
- for group of regions 0, etc.) *)
-module SynthPhaseId = IdGen ()
-
-(** Pay attention to the fact that we also define a {!Values.VarId} module in Values *)
-module VarId = IdGen ()
-
-type integer_type = T.integer_type [@@deriving show, ord]
-
-(** The assumed types for the pure AST.
-
- In comparison with LLBC:
- - we removed [Box] (because it is translated as the identity: [Box T == T])
- - we added:
- - [Result]: the type used in the error monad. This allows us to have a
- unified treatment of expressions (especially when we have to unfold the
- monadic binds)
- - [State]: the type of the state, when using state-error monads. Note that
- this state is opaque to Aeneas (the user can define it, or leave it as
- assumed)
- *)
-type assumed_ty = State | Result | Vec | Option [@@deriving show, ord]
-
-(* TODO: we should never directly manipulate [Return] and [Fail], but rather
- * the monadic functions [return] and [fail] (makes treatment of error and
- * state-error monads more uniform) *)
-let result_return_id = VariantId.of_int 0
-let result_fail_id = VariantId.of_int 1
-let option_some_id = T.option_some_id
-let option_none_id = T.option_none_id
-
-type type_id = AdtId of TypeDeclId.id | Tuple | Assumed of assumed_ty
-[@@deriving show, ord]
-
-(** Ancestor for iter visitor for [ty] *)
-class ['self] iter_ty_base =
- object (_self : 'self)
- inherit [_] VisitorsRuntime.iter
- method visit_id : 'env -> TypeVarId.id -> unit = fun _ _ -> ()
- method visit_type_id : 'env -> type_id -> unit = fun _ _ -> ()
- method visit_integer_type : 'env -> integer_type -> unit = fun _ _ -> ()
- end
-
-(** Ancestor for map visitor for [ty] *)
-class ['self] map_ty_base =
- object (_self : 'self)
- inherit [_] VisitorsRuntime.map
- method visit_id : 'env -> TypeVarId.id -> TypeVarId.id = fun _ id -> id
- method visit_type_id : 'env -> type_id -> type_id = fun _ id -> id
-
- method visit_integer_type : 'env -> integer_type -> integer_type =
- fun _ ity -> ity
- end
-
-type ty =
- | Adt of type_id * ty list
- (** {!Adt} encodes ADTs and tuples and assumed types.
-
- TODO: what about the ended regions? (ADTs may be parameterized
- with several region variables. When giving back an ADT value, we may
- be able to only give back part of the ADT. We need a way to encode
- such "partial" ADTs.
- *)
- | TypeVar of TypeVarId.id
- | Bool
- | Char
- | Integer of integer_type
- | Str
- | Array of ty (* TODO: this should be an assumed type?... *)
- | Slice of ty (* TODO: this should be an assumed type?... *)
- | Arrow of ty * ty
-[@@deriving
- show,
- visitors
- {
- name = "iter_ty";
- variety = "iter";
- ancestors = [ "iter_ty_base" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- concrete = true;
- polymorphic = false;
- },
- visitors
- {
- name = "map_ty";
- variety = "map";
- ancestors = [ "map_ty_base" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- concrete = true;
- polymorphic = false;
- }]
-
-type field = { field_name : string option; field_ty : ty } [@@deriving show]
-type variant = { variant_name : string; fields : field list } [@@deriving show]
-
-type type_decl_kind = Struct of field list | Enum of variant list | Opaque
-[@@deriving show]
-
-type type_var = T.type_var [@@deriving show]
-
-type type_decl = {
- def_id : TypeDeclId.id;
- name : name;
- type_params : type_var list;
- kind : type_decl_kind;
-}
-[@@deriving show]
-
-type scalar_value = V.scalar_value [@@deriving show]
-type constant_value = V.constant_value [@@deriving show]
-
-(** Because we introduce a lot of temporary variables, the list of variables
- is not fixed: we thus must carry all its information with the variable
- itself.
- *)
-type var = {
- id : VarId.id;
- basename : string option;
- (** The "basename" is used to generate a meaningful name for the variable
- (by potentially adding an index to uniquely identify it).
- *)
- ty : ty;
-}
-[@@deriving show]
-
-(* TODO: we might want to redefine field_proj_kind here, to prevent field accesses
- * on enumerations.
- * Also: tuples...
- * Rmk: projections are actually only used as meta-data.
- * *)
-type mprojection_elem = { pkind : E.field_proj_kind; field_id : FieldId.id }
-[@@deriving show]
-
-type mprojection = mprojection_elem list [@@deriving show]
-
-(** "Meta" place.
-
- Meta-data retrieved from the symbolic execution, which gives provenance
- information about the values. We use this to generate names for the variables
- we introduce.
- *)
-type mplace = {
- var_id : V.VarId.id;
- name : string option;
- projection : mprojection;
-}
-[@@deriving show]
-
-type variant_id = VariantId.id [@@deriving show]
-
-(** Ancestor for [iter_pat_var_or_dummy] visitor *)
-class ['self] iter_value_base =
- object (_self : 'self)
- inherit [_] VisitorsRuntime.iter
- method visit_constant_value : 'env -> constant_value -> unit = fun _ _ -> ()
- method visit_var : 'env -> var -> unit = fun _ _ -> ()
- method visit_mplace : 'env -> mplace -> unit = fun _ _ -> ()
- method visit_ty : 'env -> ty -> unit = fun _ _ -> ()
- method visit_variant_id : 'env -> variant_id -> unit = fun _ _ -> ()
- end
-
-(** Ancestor for [map_typed_rvalue] visitor *)
-class ['self] map_value_base =
- object (_self : 'self)
- inherit [_] VisitorsRuntime.map
-
- method visit_constant_value : 'env -> constant_value -> constant_value =
- fun _ x -> x
-
- method visit_var : 'env -> var -> var = fun _ x -> x
- method visit_mplace : 'env -> mplace -> mplace = fun _ x -> x
- method visit_ty : 'env -> ty -> ty = fun _ x -> x
- method visit_variant_id : 'env -> variant_id -> variant_id = fun _ x -> x
- end
-
-(** Ancestor for [reduce_typed_rvalue] visitor *)
-class virtual ['self] reduce_value_base =
- object (self : 'self)
- inherit [_] VisitorsRuntime.reduce
-
- method visit_constant_value : 'env -> constant_value -> 'a =
- fun _ _ -> self#zero
-
- method visit_var : 'env -> var -> 'a = fun _ _ -> self#zero
- method visit_mplace : 'env -> mplace -> 'a = fun _ _ -> self#zero
- method visit_ty : 'env -> ty -> 'a = fun _ _ -> self#zero
- method visit_variant_id : 'env -> variant_id -> 'a = fun _ _ -> self#zero
- end
-
-(** Ancestor for [mapreduce_typed_rvalue] visitor *)
-class virtual ['self] mapreduce_value_base =
- object (self : 'self)
- inherit [_] VisitorsRuntime.mapreduce
-
- method visit_constant_value : 'env -> constant_value -> constant_value * 'a
- =
- fun _ x -> (x, self#zero)
-
- method visit_var : 'env -> var -> var * 'a = fun _ x -> (x, self#zero)
-
- method visit_mplace : 'env -> mplace -> mplace * 'a =
- fun _ x -> (x, self#zero)
-
- method visit_ty : 'env -> ty -> ty * 'a = fun _ x -> (x, self#zero)
-
- method visit_variant_id : 'env -> variant_id -> variant_id * 'a =
- fun _ x -> (x, self#zero)
- end
-
-(** A pattern (which appears on the left of assignments, in matches, etc.). *)
-type pattern =
- | PatConcrete of constant_value
- (** {!PatConcrete} is necessary because we merge the switches over integer
- values and the matches over enumerations *)
- | PatVar of var * mplace option
- | PatDummy (** Ignored value: [_]. *)
- | PatAdt of adt_pattern
-
-and adt_pattern = {
- variant_id : variant_id option;
- field_values : typed_pattern list;
-}
-
-and typed_pattern = { value : pattern; ty : ty }
-[@@deriving
- show,
- visitors
- {
- name = "iter_typed_pattern";
- variety = "iter";
- ancestors = [ "iter_value_base" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- concrete = true;
- polymorphic = false;
- },
- visitors
- {
- name = "map_typed_pattern";
- variety = "map";
- ancestors = [ "map_value_base" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- concrete = true;
- polymorphic = false;
- },
- visitors
- {
- name = "reduce_typed_pattern";
- variety = "reduce";
- ancestors = [ "reduce_value_base" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- polymorphic = false;
- },
- visitors
- {
- name = "mapreduce_typed_pattern";
- variety = "mapreduce";
- ancestors = [ "mapreduce_value_base" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- polymorphic = false;
- }]
-
-type unop = Not | Neg of integer_type | Cast of integer_type * integer_type
-[@@deriving show, ord]
-
-type fun_id =
- | Regular of A.fun_id * T.RegionGroupId.id option
- (** Backward id: [Some] if the function is a backward function, [None]
- if it is a forward function.
-
- TODO: we need to redefine A.fun_id here, to add [fail] and
- [return] (important to get a unified treatment of the state-error
- monad). For now, when using the state-error monad: extraction
- works only if we unfold all the monadic let-bindings, and we
- then replace the content of the occurrences of [Return] to also
- return the state (which is really super ugly).
- *)
- | Unop of unop
- | Binop of E.binop * integer_type
-[@@deriving show, ord]
-
-(** An identifier for an ADT constructor *)
-type adt_cons_id = { adt_id : type_id; variant_id : variant_id option }
-[@@deriving show]
-
-(** Projection - For now we don't support projection of tuple fields
- (because not all the backends have syntax for this).
- *)
-type projection = { adt_id : type_id; field_id : FieldId.id } [@@deriving show]
-
-type qualif_id =
- | Func of fun_id
- | Global of GlobalDeclId.id
- | AdtCons of adt_cons_id (** A function or ADT constructor identifier *)
- | Proj of projection (** Field projector *)
-[@@deriving show]
-
-(** An instantiated qualified.
-
- Note that for now we have a clear separation between types and expressions,
- which explains why we have the [type_params] field: a function or ADT
- constructor is always fully instantiated.
- *)
-type qualif = { id : qualif_id; type_args : ty list } [@@deriving show]
-
-type var_id = VarId.id [@@deriving show]
-
-(** Ancestor for [iter_expression] visitor *)
-class ['self] iter_expression_base =
- object (_self : 'self)
- inherit [_] iter_typed_pattern
- method visit_integer_type : 'env -> integer_type -> unit = fun _ _ -> ()
- method visit_var_id : 'env -> var_id -> unit = fun _ _ -> ()
- method visit_qualif : 'env -> qualif -> unit = fun _ _ -> ()
- end
-
-(** Ancestor for [map_expression] visitor *)
-class ['self] map_expression_base =
- object (_self : 'self)
- inherit [_] map_typed_pattern
-
- method visit_integer_type : 'env -> integer_type -> integer_type =
- fun _ x -> x
-
- method visit_var_id : 'env -> var_id -> var_id = fun _ x -> x
- method visit_qualif : 'env -> qualif -> qualif = fun _ x -> x
- end
-
-(** Ancestor for [reduce_expression] visitor *)
-class virtual ['self] reduce_expression_base =
- object (self : 'self)
- inherit [_] reduce_typed_pattern
-
- method visit_integer_type : 'env -> integer_type -> 'a =
- fun _ _ -> self#zero
-
- method visit_var_id : 'env -> var_id -> 'a = fun _ _ -> self#zero
- method visit_qualif : 'env -> qualif -> 'a = fun _ _ -> self#zero
- end
-
-(** Ancestor for [mapreduce_expression] visitor *)
-class virtual ['self] mapreduce_expression_base =
- object (self : 'self)
- inherit [_] mapreduce_typed_pattern
-
- method visit_integer_type : 'env -> integer_type -> integer_type * 'a =
- fun _ x -> (x, self#zero)
-
- method visit_var_id : 'env -> var_id -> var_id * 'a =
- fun _ x -> (x, self#zero)
-
- method visit_qualif : 'env -> qualif -> qualif * 'a =
- fun _ x -> (x, self#zero)
- end
-
-(** **Rk.:** here, {!expression} is not at all equivalent to the expressions
- used in LLBC. They are lambda-calculus expressions, and are thus actually
- more general than the LLBC statements, in a sense.
- *)
-type expression =
- | Var of var_id (** a variable *)
- | Const of constant_value
- | App of texpression * texpression
- (** Application of a function to an argument.
-
- The function calls are still quite structured.
- Change that?... We might want to have a "normal" lambda calculus
- app (with head and argument): this would allow us to replace some
- field accesses with calls to projectors over fields (when there
- are clashes of field names, some provers like F* get pretty bad...)
- *)
- | Abs of typed_pattern * texpression (** Lambda abstraction: [fun x -> e] *)
- | Qualif of qualif (** A top-level qualifier *)
- | Let of bool * typed_pattern * texpression * texpression
- (** Let binding.
-
- TODO: the boolean should be replaced by an enum: sometimes we use
- the error-monad, sometimes we use the state-error monad (and we
- do this an a per-function basis! For instance, arithmetic functions
- are always in the error monad).
-
- The boolean controls whether the let is monadic or not.
- For instance, in F*:
- - non-monadic: [let x = ... in ...]
- - monadic: [x <-- ...; ...]
-
- Note that we are quite general for the left-value on purpose; this
- is used in several situations:
-
- 1. When deconstructing a tuple:
- {[
- let (x, y) = p in ...
- ]}
- (not all languages have syntax like [p.0], [p.1]... and it is more
- readable anyway).
-
- 2. When expanding an enumeration with one variant.
- In this case, {!Let} has to be understood as:
- {[
- let Cons x tl = ls in
- ...
- ]}
-
- Note that later, depending on the language we extract to, we can
- eventually update it to something like this (for F*, for instance):
- {[
- let x = Cons?.v ls in
- let tl = Cons?.tl ls in
- ...
- ]}
- *)
- | Switch of texpression * switch_body
- | Meta of (meta[@opaque]) * texpression (** Meta-information *)
-
-and switch_body = If of texpression * texpression | Match of match_branch list
-and match_branch = { pat : typed_pattern; branch : texpression }
-and texpression = { e : expression; ty : ty }
-
-(** Meta-value (converted to an expression). It is important that the content
- is opaque.
-
- TODO: is it possible to mark the whole mvalue type as opaque?
- *)
-and mvalue = (texpression[@opaque])
-
-and meta =
- | Assignment of mplace * mvalue * mplace option
- (** Meta-information stored in the AST.
-
- The first mplace stores the destination.
- The mvalue stores the value which is put in the destination
- The second (optional) mplace stores the origin.
- *)
- | MPlace of mplace (** Meta-information about the origin of a value *)
-[@@deriving
- show,
- visitors
- {
- name = "iter_expression";
- variety = "iter";
- ancestors = [ "iter_expression_base" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- concrete = true;
- },
- visitors
- {
- name = "map_expression";
- variety = "map";
- ancestors = [ "map_expression_base" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- concrete = true;
- },
- visitors
- {
- name = "reduce_expression";
- variety = "reduce";
- ancestors = [ "reduce_expression_base" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- },
- visitors
- {
- name = "mapreduce_expression";
- variety = "mapreduce";
- ancestors = [ "mapreduce_expression_base" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- }]
-
-(** Information about the "effect" of a function *)
-type fun_effect_info = {
- input_state : bool; (** [true] if the function takes a state as input *)
- output_state : bool;
- (** [true] if the function outputs a state (it then lives
- in a state monad) *)
- can_fail : bool; (** [true] if the return type is a [result] *)
-}
-
-(** Meta information about a function signature *)
-type fun_sig_info = {
- num_fwd_inputs : int;
- (** The number of input types for forward computation *)
- num_back_inputs : int option;
- (** The number of additional inputs for the backward computation (if pertinent) *)
- effect_info : fun_effect_info;
-}
-
-(** A function signature.
-
- We have the following cases:
- - forward function:
- [in_ty0 -> ... -> in_tyn -> out_ty] (* pure function *)
- `in_ty0 -> ... -> in_tyn -> result out_ty` (* error-monad *)
- `in_ty0 -> ... -> in_tyn -> state -> result (state & out_ty)` (* state-error *)
- - backward function:
- `in_ty0 -> ... -> in_tyn -> back_in0 -> ... back_inm -> (back_out0 & ... & back_outp)` (* pure function *)
- `in_ty0 -> ... -> in_tyn -> back_in0 -> ... back_inm ->
- result (back_out0 & ... & back_outp)` (* error-monad *)
- `in_ty0 -> ... -> in_tyn -> state -> back_in0 -> ... back_inm ->
- result (back_out0 & ... & back_outp)` (* state-error *)
-
- Note that a backward function never returns (i.e., updates) a state: only
- forward functions do so. Also, the state input parameter is *betwee*
- the forward inputs and the backward inputs.
-
- The function's type should be given by `mk_arrows sig.inputs sig.output`.
- We provide additional meta-information:
- - we divide between forward inputs and backward inputs (i.e., inputs specific
- to the forward functions, and additional inputs necessary if the signature is
- for a backward function)
- - we have booleans to give us the fact that the function takes a state as
- input, or can fail, etc. without having to inspect the signature
- - etc.
- *)
-type fun_sig = {
- type_params : type_var list;
- inputs : ty list;
- output : ty;
- doutputs : ty list;
- (** The "decomposed" list of outputs.
-
- In case of a forward function, the list has length = 1, for the
- type of the returned value.
-
- In case of backward function, the list contains all the types of
- all the given back values (there is at most one type per forward
- input argument).
-
- Ex.:
- {[
- fn choose<'a, T>(b : bool, x : &'a mut T, y : &'a mut T) -> &'a mut T;
- ]}
- Decomposed outputs:
- - forward function: [T]
- - backward function: [T; T] (for "x" and "y")
-
- *)
- info : fun_sig_info; (** Additional information *)
-}
-
-(** An instantiated function signature. See {!fun_sig} *)
-type inst_fun_sig = {
- inputs : ty list;
- output : ty;
- doutputs : ty list;
- info : fun_sig_info;
-}
-
-type fun_body = {
- inputs : var list;
- inputs_lvs : typed_pattern list;
- (** The inputs seen as patterns. Allows to make transformations, for example
- to replace unused variables by [_] *)
- body : texpression;
-}
-
-type fun_decl = {
- def_id : FunDeclId.id;
- back_id : T.RegionGroupId.id option;
- basename : fun_name;
- (** The "base" name of the function.
-
- The base name is the original name of the Rust function. We add suffixes
- (to identify the forward/backward functions) later.
- *)
- signature : fun_sig;
- is_global_decl_body : bool;
- body : fun_body option;
-}
diff --git a/src/PureMicroPasses.ml b/src/PureMicroPasses.ml
deleted file mode 100644
index 3edae38a..00000000
--- a/src/PureMicroPasses.ml
+++ /dev/null
@@ -1,1375 +0,0 @@
-(** The following module defines micro-passes which operate on the pure AST *)
-
-open Pure
-open PureUtils
-open TranslateCore
-module V = Values
-
-(** The local logger *)
-let log = L.pure_micro_passes_log
-
-(** A configuration to control the application of the passes *)
-type config = {
- decompose_monadic_let_bindings : bool;
- (** Some provers like F* don't support the decomposition of return values
- in monadic let-bindings:
- {[
- // NOT supported in F*
- let (x, y) <-- f ();
- ...
- ]}
-
- In such situations, we might want to introduce an intermediate
- assignment:
- {[
- let tmp <-- f ();
- let (x, y) = tmp in
- ...
- ]}
- *)
- unfold_monadic_let_bindings : bool;
- (** Controls the unfolding of monadic let-bindings to explicit matches:
-
- [y <-- f x; ...]
-
- becomes:
-
- [match f x with | Failure -> Failure | Return y -> ...]
-
-
- This is useful when extracting to F*: the support for monadic
- definitions is not super powerful.
- Note that when {!field:unfold_monadic_let_bindings} is true, setting
- {!field:decompose_monadic_let_bindings} to true and only makes the code
- more verbose.
- *)
- filter_useless_monadic_calls : bool;
- (** Controls whether we try to filter the calls to monadic functions
- (which can fail) when their outputs are not used.
-
- See the comments for {!expression_contains_child_call_in_all_paths}
- for additional explanations.
-
- TODO: rename to {!filter_useless_monadic_calls}
- *)
- filter_useless_functions : bool;
- (** If {!filter_useless_monadic_calls} is activated, some functions
- become useless: if this option is true, we don't extract them.
-
- The calls to functions which always get filtered are:
- - the forward functions with unit return value
- - the backward functions which don't output anything (backward
- functions coming from rust functions with no mutable borrows
- as input values - note that if a function doesn't take mutable
- borrows as inputs, it can't return mutable borrows; we actually
- dynamically check for that).
- *)
-}
-
-(** Small utility.
-
- We sometimes have to insert new fresh variables in a function body, in which
- case we need to make their indices greater than the indices of all the variables
- in the body.
- TODO: things would be simpler if we used a better representation of the
- variables indices...
- *)
-let get_body_min_var_counter (body : fun_body) : VarId.generator =
- (* Find the max id in the input variables - some of them may have been
- * filtered from the body *)
- let min_input_id =
- List.fold_left
- (fun id (var : var) -> VarId.max id var.id)
- VarId.zero body.inputs
- in
- let obj =
- object
- inherit [_] reduce_expression
- method zero _ = min_input_id
- method plus id0 id1 _ = VarId.max (id0 ()) (id1 ())
- (* Get the maximum *)
-
- (** For the patterns *)
- method! visit_var _ v _ = v.id
-
- (** For the rvalues *)
- method! visit_Var _ vid _ = vid
- end
- in
- (* Find the max counter in the body *)
- let id = obj#visit_expression () body.body.e () in
- VarId.generator_from_incr_id id
-
-(** "pretty-name context": see [compute_pretty_names] *)
-type pn_ctx = {
- pure_vars : string VarId.Map.t;
- (** Information about the pure variables used in the synthesized program *)
- llbc_vars : string V.VarId.Map.t;
- (** Information about the LLBC variables used in the original program *)
-}
-
-(** This function computes pretty names for the variables in the pure AST. It
- relies on the "meta"-place information in the AST to generate naming
- constraints, and then uses those to compute the names.
-
- The way it works is as follows:
- - we only modify the names of the unnamed variables
- - whenever we see an rvalue/pattern which is exactly an unnamed variable,
- and this value is linked to some meta-place information which contains
- a name and an empty path, we consider we should use this name
- - we try to propagate naming constraints on the pure variables use in the
- synthesized programs, and also on the LLBC variables from the original
- program (information about the LLBC variables is stored in the meta-places)
-
-
- Something important is that, for every variable we find, the name of this
- variable can be influenced by the information we find *below* in the AST.
-
- For instance, the following situations happen:
-
- - let's say we evaluate:
- {[
- match (ls : List<T>) {
- List::Cons(x, hd) => {
- ...
- }
- }
- ]}
-
- Actually, in MIR, we get:
- {[
- tmp := discriminant(ls);
- switch tmp {
- 0 => {
- x := (ls as Cons).0; // (i)
- hd := (ls as Cons).1; // (ii)
- ...
- }
- }
- ]}
- If [ls] maps to a symbolic value [s0] upon evaluating the match in symbolic
- mode, we expand this value upon evaluating [tmp = discriminant(ls)].
- However, at this point, we don't know which should be the names of
- the symbolic values we introduce for the fields of [Cons]!
-
- Let's imagine we have (for the [Cons] branch): [s0 ~~> Cons s1 s2].
- The assigments at (i) and (ii) lead to the following binding in the
- evaluation context:
- {[
- x -> s1
- hd -> s2
- ]}
-
- When generating the symbolic AST, we save as meta-information that we
- assign [s1] to the place [x] and [s2] to the place [hd]. This way,
- we learn we can use the names [x] and [hd] for the variables which are
- introduced by the match:
- {[
- match ls with
- | Cons x hd -> ...
- | ...
- ]}
- - Assignments:
- [let x [@mplace=lp] = v [@mplace = rp] in ...]
-
- We propagate naming information across the assignments. This is important
- because many reassignments using temporary, anonymous variables are
- introduced during desugaring.
-
- - Given back values (introduced by backward functions):
- Let's say we have the following Rust code:
- {[
- let py = id(&mut x);
- *py = 2;
- assert!(x == 2);
- ]}
-
- After desugaring, we get the following MIR:
- {[
- ^0 = &mut x; // anonymous variable
- py = id(move ^0);
- *py += 2;
- assert!(x == 2);
- ]}
-
- We want this to be translated as:
- {[
- let py = id_fwd x in
- let py1 = py + 2 in
- let x1 = id_back x py1 in // <-- x1 is "given back": doesn't appear in the original MIR
- assert(x1 = 2);
- ]}
-
- We want to notice that the value given back by [id_back] is given back for "x",
- so we should use "x" as the basename (hence the resulting name "x1"). However,
- this is non-trivial, because after desugaring the input argument given to [id]
- is not [&mut x] but [move ^0] (i.e., it comes from a temporary, anonymous
- variable). For this reason, we use the meta-place [&mut x] as the meta-place
- for the given back value (this is done during the synthesis), and propagate
- naming information *also* on the LLBC variables (which are referenced by the
- meta-places).
-
- This way, because of [^0 = &mut x], we can propagate the name "x" to the place
- [^0], then to the given back variable across the function call.
-
- *)
-let compute_pretty_names (def : fun_decl) : fun_decl =
- (* Small helpers *)
- (*
- * When we do branchings, we need to merge (the constraints saved in) the
- * contexts returned by the different branches.
- *
- * Note that by doing so, some mappings from var id to name
- * in one context may be overriden by the ones in the other context.
- *
- * This should be ok because:
- * - generally, the overriden variables should have been introduced *inside*
- * the branches, in which case we don't care
- * - or they were introduced before, in which case the naming should generally
- * be consistent? In the worse case, it isn't, but it leads only to less
- * readable code, not to unsoundness. This case should be pretty rare,
- * also.
- *)
- let merge_ctxs (ctx0 : pn_ctx) (ctx1 : pn_ctx) : pn_ctx =
- let pure_vars =
- VarId.Map.fold
- (fun id name ctx -> VarId.Map.add id name ctx)
- ctx0.pure_vars ctx1.pure_vars
- in
- let llbc_vars =
- V.VarId.Map.fold
- (fun id name ctx -> V.VarId.Map.add id name ctx)
- ctx0.llbc_vars ctx1.llbc_vars
- in
- { pure_vars; llbc_vars }
- in
- let empty_ctx =
- { pure_vars = VarId.Map.empty; llbc_vars = V.VarId.Map.empty }
- in
- let merge_ctxs_ls (ctxs : pn_ctx list) : pn_ctx =
- List.fold_left (fun ctx0 ctx1 -> merge_ctxs ctx0 ctx1) empty_ctx ctxs
- in
-
- (*
- * The way we do is as follows:
- * - we explore the expressions
- * - we register the variables introduced by the let-bindings
- * - we use the naming information we find (through the variables and the
- * meta-places) to update our context (i.e., maps from variable ids to
- * names)
- * - we use this information to update the names of the variables used in the
- * expressions
- *)
-
- (* Register a variable for constraints propagation - used when an variable is
- * introduced (left-hand side of a left binding) *)
- let register_var (ctx : pn_ctx) (v : var) : pn_ctx =
- assert (not (VarId.Map.mem v.id ctx.pure_vars));
- match v.basename with
- | None -> ctx
- | Some name ->
- let pure_vars = VarId.Map.add v.id name ctx.pure_vars in
- { ctx with pure_vars }
- in
- (* Update a variable - used to update an expression after we computed constraints *)
- let update_var (ctx : pn_ctx) (v : var) (mp : mplace option) : var =
- match v.basename with
- | Some _ -> v
- | None -> (
- match VarId.Map.find_opt v.id ctx.pure_vars with
- | Some basename -> { v with basename = Some basename }
- | None ->
- if Option.is_some mp then
- match
- V.VarId.Map.find_opt (Option.get mp).var_id ctx.llbc_vars
- with
- | None -> v
- | Some basename -> { v with basename = Some basename }
- else v)
- in
- (* Update an pattern - used to update an expression after we computed constraints *)
- let update_typed_pattern ctx (lv : typed_pattern) : typed_pattern =
- let obj =
- object
- inherit [_] map_typed_pattern
- method! visit_PatVar _ v mp = PatVar (update_var ctx v mp, mp)
- end
- in
- obj#visit_typed_pattern () lv
- in
-
- (* Register an mplace the first time we find one *)
- let register_mplace (mp : mplace) (ctx : pn_ctx) : pn_ctx =
- match (V.VarId.Map.find_opt mp.var_id ctx.llbc_vars, mp.name) with
- | None, Some name ->
- let llbc_vars = V.VarId.Map.add mp.var_id name ctx.llbc_vars in
- { ctx with llbc_vars }
- | _ -> ctx
- in
-
- (* Register the fact that [name] can be used for the pure variable identified
- * by [var_id] (will add this name in the map if the variable is anonymous) *)
- let add_pure_var_constraint (var_id : VarId.id) (name : string) (ctx : pn_ctx)
- : pn_ctx =
- let pure_vars =
- if VarId.Map.mem var_id ctx.pure_vars then ctx.pure_vars
- else VarId.Map.add var_id name ctx.pure_vars
- in
- { ctx with pure_vars }
- in
- (* Similar to [add_pure_var_constraint], but for LLBC variables *)
- let add_llbc_var_constraint (var_id : V.VarId.id) (name : string)
- (ctx : pn_ctx) : pn_ctx =
- let llbc_vars =
- if V.VarId.Map.mem var_id ctx.llbc_vars then ctx.llbc_vars
- else V.VarId.Map.add var_id name ctx.llbc_vars
- in
- { ctx with llbc_vars }
- in
- (* Add a constraint: given a variable id and an associated meta-place, try to
- * extract naming information from the meta-place and save it *)
- let add_constraint (mp : mplace) (var_id : VarId.id) (ctx : pn_ctx) : pn_ctx =
- (* Register the place *)
- let ctx = register_mplace mp ctx in
- (* Update the variable name *)
- match (mp.name, mp.projection) with
- | Some name, [] ->
- (* Check if the variable already has a name - if not: insert the new name *)
- let ctx = add_pure_var_constraint var_id name ctx in
- let ctx = add_llbc_var_constraint mp.var_id name ctx in
- ctx
- | _ -> ctx
- in
- (* Specific case of constraint on rvalues *)
- let add_right_constraint (mp : mplace) (rv : texpression) (ctx : pn_ctx) :
- pn_ctx =
- (* Register the place *)
- let ctx = register_mplace mp ctx in
- (* Add the constraint *)
- match (unmeta rv).e with Var vid -> add_constraint mp vid ctx | _ -> ctx
- in
- (* Specific case of constraint on left values *)
- let add_left_constraint (lv : typed_pattern) (ctx : pn_ctx) : pn_ctx =
- let obj =
- object (self)
- inherit [_] reduce_typed_pattern
- method zero _ = empty_ctx
- method plus ctx0 ctx1 _ = merge_ctxs (ctx0 ()) (ctx1 ())
-
- method! visit_PatVar _ v mp () =
- (* Register the variable *)
- let ctx = register_var (self#zero ()) v in
- (* Register the mplace information if there is such information *)
- match mp with Some mp -> add_constraint mp v.id ctx | None -> ctx
- end
- in
- let ctx1 = obj#visit_typed_pattern () lv () in
- merge_ctxs ctx ctx1
- in
-
- (* This is used to propagate constraint information about places in case of
- * variable reassignments: we try to propagate the information from the
- * rvalue to the left *)
- let add_left_right_constraint (lv : typed_pattern) (re : texpression)
- (ctx : pn_ctx) : pn_ctx =
- (* We propagate constraints across variable reassignments: [^0 = x],
- * if the destination doesn't have naming information *)
- match lv.value with
- | PatVar (({ id = _; basename = None; ty = _ } as lvar), lmp) ->
- if
- (* Check that there is not already a name for the variable *)
- VarId.Map.mem lvar.id ctx.pure_vars
- then ctx
- else
- (* We ignore the left meta-place information: it should have been taken
- * care of by [add_left_constraint]. We try to use the right meta-place
- * information *)
- let add (name : string) (ctx : pn_ctx) : pn_ctx =
- (* Add the constraint for the pure variable *)
- let ctx = add_pure_var_constraint lvar.id name ctx in
- (* Add the constraint for the LLBC variable *)
- match lmp with
- | None -> ctx
- | Some lmp -> add_llbc_var_constraint lmp.var_id name ctx
- in
- (* We try to use the right-place information *)
- let rmp, re = opt_unmeta_mplace re in
- let ctx =
- match rmp with
- | Some { var_id; name; projection = [] } -> (
- if Option.is_some name then add (Option.get name) ctx
- else
- match V.VarId.Map.find_opt var_id ctx.llbc_vars with
- | None -> ctx
- | Some name -> add name ctx)
- | _ -> ctx
- in
- (* We try to use the rvalue information, if it is a variable *)
- let ctx =
- match (unmeta re).e with
- | Var rvar_id -> (
- match VarId.Map.find_opt rvar_id ctx.pure_vars with
- | None -> ctx
- | Some name -> add name ctx)
- | _ -> ctx
- in
- ctx
- | _ -> ctx
- in
-
- (* *)
- let rec update_texpression (e : texpression) (ctx : pn_ctx) :
- pn_ctx * texpression =
- let ty = e.ty in
- let ctx, e =
- match e.e with
- | Var _ -> (* Nothing to do *) (ctx, e.e)
- | Const _ -> (* Nothing to do *) (ctx, e.e)
- | App (app, arg) ->
- let ctx, app = update_texpression app ctx in
- let ctx, arg = update_texpression arg ctx in
- let e = App (app, arg) in
- (ctx, e)
- | Abs (x, e) -> update_abs x e ctx
- | Qualif _ -> (* nothing to do *) (ctx, e.e)
- | Let (monadic, lb, re, e) -> update_let monadic lb re e ctx
- | Switch (scrut, body) -> update_switch_body scrut body ctx
- | Meta (meta, e) -> update_meta meta e ctx
- in
- (ctx, { e; ty })
- (* *)
- and update_abs (x : typed_pattern) (e : texpression) (ctx : pn_ctx) :
- pn_ctx * expression =
- (* We first add the left-constraint *)
- let ctx = add_left_constraint x ctx in
- (* Update the expression, and add additional constraints *)
- let ctx, e = update_texpression e ctx in
- (* Update the abstracted value *)
- let x = update_typed_pattern ctx x in
- (* Put together *)
- (ctx, Abs (x, e))
- (* *)
- and update_let (monadic : bool) (lv : typed_pattern) (re : texpression)
- (e : texpression) (ctx : pn_ctx) : pn_ctx * expression =
- (* We first add the left-constraint *)
- let ctx = add_left_constraint lv ctx in
- (* Then we try to propagate the right-constraints to the left, in case
- * the left constraints didn't give naming information *)
- let ctx = add_left_right_constraint lv re ctx in
- let ctx, re = update_texpression re ctx in
- let ctx, e = update_texpression e ctx in
- let lv = update_typed_pattern ctx lv in
- (ctx, Let (monadic, lv, re, e))
- (* *)
- and update_switch_body (scrut : texpression) (body : switch_body)
- (ctx : pn_ctx) : pn_ctx * expression =
- let ctx, scrut = update_texpression scrut ctx in
-
- let ctx, body =
- match body with
- | If (e_true, e_false) ->
- let ctx1, e_true = update_texpression e_true ctx in
- let ctx2, e_false = update_texpression e_false ctx in
- let ctx = merge_ctxs ctx1 ctx2 in
- (ctx, If (e_true, e_false))
- | Match branches ->
- let ctx_branches_ls =
- List.map
- (fun br ->
- let ctx = add_left_constraint br.pat ctx in
- let ctx, branch = update_texpression br.branch ctx in
- let pat = update_typed_pattern ctx br.pat in
- (ctx, { pat; branch }))
- branches
- in
- let ctxs, branches = List.split ctx_branches_ls in
- let ctx = merge_ctxs_ls ctxs in
- (ctx, Match branches)
- in
- (ctx, Switch (scrut, body))
- (* *)
- and update_meta (meta : meta) (e : texpression) (ctx : pn_ctx) :
- pn_ctx * expression =
- let ctx =
- match meta with
- | Assignment (mp, rvalue, rmp) ->
- let ctx = add_right_constraint mp rvalue ctx in
- let ctx =
- match (mp.projection, rmp) with
- | [], Some { var_id; name; projection = [] } -> (
- let name =
- match name with
- | Some name -> Some name
- | None -> V.VarId.Map.find_opt var_id ctx.llbc_vars
- in
- match name with
- | None -> ctx
- | Some name -> add_llbc_var_constraint mp.var_id name ctx)
- | _ -> ctx
- in
- ctx
- | MPlace mp -> add_right_constraint mp e ctx
- in
- let ctx, e = update_texpression e ctx in
- let e = mk_meta meta e in
- (ctx, e.e)
- in
-
- let body =
- match def.body with
- | None -> None
- | Some body ->
- let input_names =
- List.filter_map
- (fun (v : var) ->
- match v.basename with
- | None -> None
- | Some name -> Some (v.id, name))
- body.inputs
- in
- let ctx =
- {
- pure_vars = VarId.Map.of_list input_names;
- llbc_vars = V.VarId.Map.empty;
- }
- in
- let _, body_exp = update_texpression body.body ctx in
- Some { body with body = body_exp }
- in
- { def with body }
-
-(** Remove the meta-information *)
-let remove_meta (def : fun_decl) : fun_decl =
- match def.body with
- | None -> def
- | Some body ->
- let body = { body with body = PureUtils.remove_meta body.body } in
- { def with body = Some body }
-
-(** Inline the useless variable (re-)assignments:
-
- A lot of intermediate variable assignments are introduced through the
- compilation to MIR and by the translation itself (and the variable used
- on the left is often unnamed).
-
- Note that many of them are just variable "reassignments": [let x = y in ...].
- Some others come from ??
-
- TODO: how do we call that when we introduce intermediate variable assignments
- for the arguments of a function call?
-
- [inline_named]: if [true], inline all the assignments of the form
- [let VAR = VAR in ...], otherwise inline only the ones where the variable
- on the left is anonymous.
-
- [inline_pure]: if [true], inline all the pure assignments where the variable
- on the left is anonymous, but the assignments where the r-expression is
- a non-primitive function call (i.e.: inline the binops, ADT constructions,
- etc.).
-
- TODO: we have a smallish issue which is that rvalues should be merged with
- expressions... For now, this forces us to substitute whenever we can, but
- leave the let-bindings where they are, and eliminated them in a subsequent
- pass (if they are useless).
- *)
-let inline_useless_var_reassignments (inline_named : bool) (inline_pure : bool)
- (def : fun_decl) : fun_decl =
- let obj =
- object (self)
- inherit [_] map_expression as super
-
- (** Visit the let-bindings to filter the useless ones (and update
- the substitution map while doing so *)
- method! visit_Let (env : texpression VarId.Map.t) monadic lv re e =
- (* In order to filter, we need to check first that:
- * - the let-binding is not monadic
- * - the left-value is a variable
- *)
- match (monadic, lv.value) with
- | false, PatVar (lv_var, _) ->
- (* We can filter if: *)
- (* 1. the left variable is unnamed or [inline_named] is true *)
- let filter_left =
- match (inline_named, lv_var.basename) with
- | true, _ | _, None -> true
- | _ -> false
- in
- (* And either:
- * 2.1 the right-expression is a variable or a global *)
- let var_or_global = is_var re || is_global re in
- (* Or:
- * 2.2 the right-expression is a constant value, an ADT value,
- * a projection or a primitive function call *and* the flag
- * [inline_pure] is set *)
- let pure_re =
- is_const re
- ||
- let app, _ = destruct_apps re in
- match app.e with
- | Qualif qualif -> (
- match qualif.id with
- | AdtCons _ -> true (* ADT constructor *)
- | Proj _ -> true (* Projector *)
- | Func (Unop _ | Binop _) ->
- true (* primitive function call *)
- | Func (Regular _) -> false (* non-primitive function call *)
- | _ -> false)
- | _ -> false
- in
- let filter =
- filter_left && (var_or_global || (inline_pure && pure_re))
- in
-
- (* Update the rhs (we may perform substitutions inside, and it is
- * better to do them *before* we inline it *)
- let re = self#visit_texpression env re in
- (* Update the substitution environment *)
- let env = if filter then VarId.Map.add lv_var.id re env else env in
- (* Update the next expression *)
- let e = self#visit_texpression env e in
- (* Reconstruct the [let], only if the binding is not filtered *)
- if filter then e.e else Let (monadic, lv, re, e)
- | _ -> super#visit_Let env monadic lv re e
-
- (** Substitute the variables *)
- method! visit_Var (env : texpression VarId.Map.t) (vid : VarId.id) =
- match VarId.Map.find_opt vid env with
- | None -> (* No substitution *) super#visit_Var env vid
- | Some ne ->
- (* Substitute - note that we need to reexplore, because
- * there may be stacked substitutions, if we have:
- * var0 --> var1
- * var1 --> var2.
- *)
- self#visit_expression env ne.e
- end
- in
- match def.body with
- | None -> def
- | Some body ->
- let body =
- { body with body = obj#visit_texpression VarId.Map.empty body.body }
- in
- { def with body = Some body }
-
-(** Given a forward or backward function call, is there, for every execution
- path, a child backward function called later with exactly the same input
- list prefix? We use this to filter useless function calls: if there are
- such child calls, we can remove this one (in case its outputs are not
- used).
- We do this check because we can't simply remove function calls whose
- outputs are not used, as they might fail. However, if a function fails,
- its children backward functions then fail on the same inputs (ignoring
- the additional inputs those receive).
-
- For instance, if we have:
- {[
- fn f<'a>(x : &'a mut T);
- ]}
-
- We often have things like this in the synthesized code:
- {[
- _ <-- f x;
- ...
- nx <-- f@back'a x y;
- ...
- ]}
-
- In this situation, we can remove the call [f x].
- *)
-let expression_contains_child_call_in_all_paths (ctx : trans_ctx)
- (fun_id0 : fun_id) (tys0 : ty list) (args0 : texpression list)
- (e : texpression) : bool =
- let check_call (fun_id1 : fun_id) (tys1 : ty list) (args1 : texpression list)
- : bool =
- (* Check the fun_ids, to see if call1's function is a child of call0's function *)
- match (fun_id0, fun_id1) with
- | Regular (id0, rg_id0), Regular (id1, rg_id1) ->
- (* Both are "regular" calls: check if they come from the same rust function *)
- if id0 = id1 then
- (* Same rust functions: check the regions hierarchy *)
- let call1_is_child =
- match (rg_id0, rg_id1) with
- | None, _ ->
- (* The function used in call0 is the forward function: the one
- * used in call1 is necessarily a child *)
- true
- | Some _, None ->
- (* Opposite of previous case *)
- false
- | Some rg_id0, Some rg_id1 ->
- if rg_id0 = rg_id1 then true
- else
- (* We need to use the regions hierarchy *)
- (* First, lookup the signature of the LLBC function *)
- let sg =
- LlbcAstUtils.lookup_fun_sig id0 ctx.fun_context.fun_decls
- in
- (* Compute the set of ancestors of the function in call1 *)
- let call1_ancestors =
- LlbcAstUtils.list_parent_region_groups sg rg_id1
- in
- (* Check if the function used in call0 is inside *)
- T.RegionGroupId.Set.mem rg_id0 call1_ancestors
- in
- (* If call1 is a child, then we need to check if the input arguments
- * used in call0 are a prefix of the input arguments used in call1
- * (note call1 being a child, it will likely consume strictly more
- * given back values).
- * *)
- if call1_is_child then
- let call1_args =
- Collections.List.prefix (List.length args0) args1
- in
- let args = List.combine args0 call1_args in
- (* Note that the input values are expressions, *which may contain
- * meta-values* (which we need to ignore). *)
- let input_eq (v0, v1) =
- PureUtils.remove_meta v0 = PureUtils.remove_meta v1
- in
- (* Compare the input types and the prefix of the input arguments *)
- tys0 = tys1 && List.for_all input_eq args
- else (* Not a child *)
- false
- else (* Not the same function *)
- false
- | _ -> false
- in
-
- let visitor =
- object (self)
- inherit [_] reduce_expression
- method zero _ = false
- method plus b0 b1 _ = b0 () && b1 ()
-
- method! visit_texpression env e =
- match e.e with
- | Var _ | Const _ -> fun _ -> false
- | Let (_, _, re, e) -> (
- match opt_destruct_function_call re with
- | None -> fun () -> self#visit_texpression env e ()
- | Some (func1, tys1, args1) ->
- let call_is_child = check_call func1 tys1 args1 in
- if call_is_child then fun () -> true
- else fun () -> self#visit_texpression env e ())
- | App _ -> (
- fun () ->
- match opt_destruct_function_call e with
- | Some (func1, tys1, args1) -> check_call func1 tys1 args1
- | None -> false)
- | Abs (_, e) -> self#visit_texpression env e
- | Qualif _ ->
- (* Note that this case includes functions without arguments *)
- fun () -> false
- | Meta (_, e) -> self#visit_texpression env e
- | Switch (_, body) -> self#visit_switch_body env body
-
- method! visit_switch_body env body =
- match body with
- | If (e1, e2) ->
- fun () ->
- self#visit_texpression env e1 ()
- && self#visit_texpression env e2 ()
- | Match branches ->
- fun () ->
- List.for_all
- (fun br -> self#visit_texpression env br.branch ())
- branches
- end
- in
- visitor#visit_texpression () e ()
-
-(** Filter the useless assignments (removes the useless variables, filters
- the function calls) *)
-let filter_useless (filter_monadic_calls : bool) (ctx : trans_ctx)
- (def : fun_decl) : fun_decl =
- (* We first need a transformation on *left-values*, which filters the useless
- * variables and tells us whether the value contains any variable which has
- * not been replaced by [_] (in which case we need to keep the assignment,
- * etc.).
- *
- * This is implemented as a map-reduce.
- *
- * Returns: ( filtered_left_value, *all_dummies* )
- *
- * [all_dummies]:
- * If the returned boolean is true, it means that all the variables appearing
- * in the filtered left-value are *dummies* (meaning that if this left-value
- * appears at the left of a let-binding, this binding might potentially be
- * removed).
- *)
- let lv_visitor =
- object
- inherit [_] mapreduce_typed_pattern
- method zero _ = true
- method plus b0 b1 _ = b0 () && b1 ()
-
- method! visit_PatVar env v mp =
- if VarId.Set.mem v.id env then (PatVar (v, mp), fun _ -> false)
- else (PatDummy, fun _ -> true)
- end
- in
- let filter_typed_pattern (used_vars : VarId.Set.t) (lv : typed_pattern) :
- typed_pattern * bool =
- let lv, all_dummies = lv_visitor#visit_typed_pattern used_vars lv in
- (lv, all_dummies ())
- in
-
- (* We then implement the transformation on *expressions* through a mapreduce.
- * Note that the transformation is bottom-up.
- * The map filters the useless assignments, the reduce computes the set of
- * used variables.
- *)
- let expr_visitor =
- object (self)
- inherit [_] mapreduce_expression as super
- method zero _ = VarId.Set.empty
- method plus s0 s1 _ = VarId.Set.union (s0 ()) (s1 ())
-
- (** Whenever we visit a variable, we need to register the used variable *)
- method! visit_Var _ vid = (Var vid, fun _ -> VarId.Set.singleton vid)
-
- method! visit_expression env e =
- match e with
- | Var _ | Const _ | App _ | Qualif _
- | Switch (_, _)
- | Meta (_, _)
- | Abs _ ->
- super#visit_expression env e
- | Let (monadic, lv, re, e) ->
- (* Compute the set of values used in the next expression *)
- let e, used = self#visit_texpression env e in
- let used = used () in
- (* Filter the left values *)
- let lv, all_dummies = filter_typed_pattern used lv in
- (* Small utility - called if we can't filter the let-binding *)
- let dont_filter () =
- let re, used_re = self#visit_texpression env re in
- let used = VarId.Set.union used (used_re ()) in
- (Let (monadic, lv, re, e), fun _ -> used)
- in
- (* Potentially filter the let-binding *)
- if all_dummies then
- if not monadic then
- (* Not a monadic let-binding: simple case *)
- (e.e, fun _ -> used)
- else
- (* Monadic let-binding: trickier.
- * We can filter if the right-expression is a function call,
- * under some conditions. *)
- match (filter_monadic_calls, opt_destruct_function_call re) with
- | true, Some (func, tys, args) ->
- (* We need to check if there is a child call - see
- * the comments for:
- * [expression_contains_child_call_in_all_paths] *)
- let has_child_call =
- expression_contains_child_call_in_all_paths ctx func tys
- args e
- in
- if has_child_call then (* Filter *)
- (e.e, fun _ -> used)
- else (* No child call: don't filter *)
- dont_filter ()
- | _ ->
- (* Not a call or not allowed to filter: we can't filter *)
- dont_filter ()
- else (* There are used variables: don't filter *)
- dont_filter ()
- end
- in
- (* We filter only inside of transparent (i.e., non-opaque) definitions *)
- match def.body with
- | None -> def
- | Some body ->
- (* Visit the body *)
- let body_exp, used_vars = expr_visitor#visit_texpression () body.body in
- (* Visit the parameters - TODO: update: we can filter only if the definition
- * is not recursive (otherwise it might mess up with the decrease clauses:
- * the decrease clauses uses all the inputs given to the function, if some
- * inputs are replaced by '_' we can't give it to the function used in the
- * decreases clause).
- * For now we deactivate the filtering. *)
- let used_vars = used_vars () in
- let inputs_lvs =
- if false then
- List.map
- (fun lv -> fst (filter_typed_pattern used_vars lv))
- body.inputs_lvs
- else body.inputs_lvs
- in
- (* Return *)
- let body = { body with body = body_exp; inputs_lvs } in
- { def with body = Some body }
-
-(** Simplify the aggregated ADTs.
- Ex.:
- {[
- type struct = { f0 : nat; f1 : nat }
-
- Mkstruct x.f0 x.f1 ~~> x
- ]}
-
- TODO: introduce a notation for [{ x with field = ... }], and use it.
- *)
-let simplify_aggregates (ctx : trans_ctx) (def : fun_decl) : fun_decl =
- let expr_visitor =
- object
- inherit [_] map_expression as super
-
- (* Look for a type constructor applied to arguments *)
- method! visit_texpression env e =
- match e.e with
- | App _ -> (
- let app, args = destruct_apps e in
- match app.e with
- | Qualif
- {
- id = AdtCons { adt_id = AdtId adt_id; variant_id = None };
- type_args;
- } ->
- (* This is a struct *)
- (* Retrieve the definiton, to find how many fields there are *)
- let adt_decl =
- TypeDeclId.Map.find adt_id ctx.type_context.type_decls
- in
- let fields =
- match adt_decl.kind with
- | Enum _ | Opaque -> raise (Failure "Unreachable")
- | Struct fields -> fields
- in
- let num_fields = List.length fields in
- (* In order to simplify, there must be as many arguments as
- * there are fields *)
- assert (num_fields > 0);
- if num_fields = List.length args then
- (* We now need to check that all the arguments are of the form:
- * [x.field] for some variable [x], and where the projection
- * is for the proper ADT *)
- let to_var_proj (i : int) (arg : texpression) :
- (ty list * var_id) option =
- match arg.e with
- | App (proj, x) -> (
- match (proj.e, x.e) with
- | ( Qualif
- {
- id =
- Proj { adt_id = AdtId proj_adt_id; field_id };
- type_args = proj_type_args;
- },
- Var v ) ->
- (* We check that this is the proper ADT, and the proper field *)
- if
- proj_adt_id = adt_id
- && FieldId.to_int field_id = i
- then Some (proj_type_args, v)
- else None
- | _ -> None)
- | _ -> None
- in
- let args = List.mapi to_var_proj args in
- let args = List.filter_map (fun x -> x) args in
- (* Check that all the arguments are of the expected form *)
- if List.length args = num_fields then
- (* Check that this is the same variable we project from -
- * note that we checked above that there is at least one field *)
- let (_, x), end_args = Collections.List.pop args in
- if List.for_all (fun (_, y) -> y = x) end_args then (
- (* We can substitute *)
- (* Sanity check: all types correct *)
- assert (
- List.for_all (fun (tys, _) -> tys = type_args) args);
- { e with e = Var x })
- else super#visit_texpression env e
- else super#visit_texpression env e
- else super#visit_texpression env e
- | _ -> super#visit_texpression env e)
- | _ -> super#visit_texpression env e
- end
- in
- match def.body with
- | None -> def
- | Some body ->
- (* Visit the body *)
- let body_exp = expr_visitor#visit_texpression () body.body in
- (* Return *)
- let body = { body with body = body_exp } in
- { def with body = Some body }
-
-(** Return [None] if the function is a backward function with no outputs (so
- that we eliminate the definition which is useless).
-
- Note that the calls to such functions are filtered when translating from
- symbolic to pure. Here, we remove the definitions altogether, because they
- are now useless
- *)
-let filter_if_backward_with_no_outputs (config : config) (def : fun_decl) :
- fun_decl option =
- if
- config.filter_useless_functions && Option.is_some def.back_id
- && def.signature.output = mk_result_ty mk_unit_ty
- then None
- else Some def
-
-(** Return [false] if the forward function is useless and should be filtered.
-
- - a forward function with no output (comes from a Rust function with
- unit return type)
- - the function has mutable borrows as inputs (which is materialized
- by the fact we generated backward functions which were not filtered).
-
- In such situation, every call to the Rust function will be translated to:
- - a call to the forward function which returns nothing
- - calls to the backward functions
- As a failing backward function implies the forward function also fails,
- we can filter the calls to the forward function, which thus becomes
- useless.
- In such situation, we can remove the forward function definition
- altogether.
- *)
-let keep_forward (config : config) (trans : pure_fun_translation) : bool =
- let fwd, backs = trans in
- (* Note that at this point, the output types are no longer seen as tuples:
- * they should be lists of length 1. *)
- if
- config.filter_useless_functions
- && fwd.signature.output = mk_result_ty mk_unit_ty
- && backs <> []
- then false
- else true
-
-(** Convert the unit variables to [()] if they are used as right-values or
- [_] if they are used as left values in patterns. *)
-let unit_vars_to_unit (def : fun_decl) : fun_decl =
- (* The map visitor *)
- let obj =
- object
- inherit [_] map_expression as super
-
- (** Replace in patterns *)
- method! visit_PatVar _ v mp =
- if v.ty = mk_unit_ty then PatDummy else PatVar (v, mp)
-
- (** Replace in "regular" expressions - note that we could limit ourselves
- to variables, but this is more powerful
- *)
- method! visit_texpression env e =
- if e.ty = mk_unit_ty then mk_unit_rvalue
- else super#visit_texpression env e
- end
- in
- (* Update the body *)
- match def.body with
- | None -> def
- | Some body ->
- let body_exp = obj#visit_texpression () body.body in
- (* Update the input parameters *)
- let inputs_lvs = List.map (obj#visit_typed_pattern ()) body.inputs_lvs in
- (* Return *)
- let body = Some { body with body = body_exp; inputs_lvs } in
- { def with body }
-
-(** Eliminate the box functions like [Box::new], [Box::deref], etc. Most of them
- are translated to identity, and [Box::free] is translated to [()].
-
- Note that the box types have already been eliminated during the translation
- from symbolic to pure.
- The reason why we don't eliminate the box functions at the same time is
- that we would need to eliminate them in two different places: when translating
- function calls, and when translating end abstractions. Here, we can do
- something simpler, in one micro-pass.
- *)
-let eliminate_box_functions (_ctx : trans_ctx) (def : fun_decl) : fun_decl =
- (* The map visitor *)
- let obj =
- object
- inherit [_] map_expression as super
-
- method! visit_texpression env e =
- match opt_destruct_function_call e with
- | Some (fun_id, _tys, args) -> (
- match fun_id with
- | Regular (A.Assumed aid, rg_id) -> (
- (* Below, when dealing with the arguments: we consider the very
- * general case, where functions could be boxed (meaning we
- * could have: [box_new f x])
- * *)
- match (aid, rg_id) with
- | A.BoxNew, _ ->
- assert (rg_id = None);
- let arg, args = Collections.List.pop args in
- mk_apps arg args
- | A.BoxDeref, None ->
- (* [Box::deref] forward is the identity *)
- let arg, args = Collections.List.pop args in
- mk_apps arg args
- | A.BoxDeref, Some _ ->
- (* [Box::deref] backward is [()] (doesn't give back anything) *)
- assert (args = []);
- mk_unit_rvalue
- | A.BoxDerefMut, None ->
- (* [Box::deref_mut] forward is the identity *)
- let arg, args = Collections.List.pop args in
- mk_apps arg args
- | A.BoxDerefMut, Some _ ->
- (* [Box::deref_mut] back is almost the identity:
- * let box_deref_mut (x_init : t) (x_back : t) : t = x_back
- * *)
- let arg, args =
- match args with
- | _ :: given_back :: args -> (given_back, args)
- | _ -> failwith "Unreachable"
- in
- mk_apps arg args
- | A.BoxFree, _ ->
- assert (args = []);
- mk_unit_rvalue
- | ( ( A.Replace | A.VecNew | A.VecPush | A.VecInsert | A.VecLen
- | A.VecIndex | A.VecIndexMut ),
- _ ) ->
- super#visit_texpression env e)
- | _ -> super#visit_texpression env e)
- | _ -> super#visit_texpression env e
- end
- in
- (* Update the body *)
- match def.body with
- | None -> def
- | Some body ->
- let body = Some { body with body = obj#visit_texpression () body.body } in
- { def with body }
-
-(** Decompose the monadic let-bindings.
-
- See the explanations in [config].
- *)
-let decompose_monadic_let_bindings (_ctx : trans_ctx) (def : fun_decl) :
- fun_decl =
- match def.body with
- | None -> def
- | Some body ->
- (* Set up the var id generator *)
- let cnt = get_body_min_var_counter body in
- let _, fresh_id = VarId.mk_stateful_generator cnt in
- (* It is a very simple map *)
- let obj =
- object (self)
- inherit [_] map_expression as super
-
- method! visit_Let env monadic lv re next_e =
- if not monadic then super#visit_Let env monadic lv re next_e
- else
- (* If monadic, we need to check if the left-value is a variable:
- * - if yes, don't decompose
- * - if not, make the decomposition in two steps
- *)
- match lv.value with
- | PatVar _ ->
- (* Variable: nothing to do *)
- super#visit_Let env monadic lv re next_e
- | _ ->
- (* Not a variable: decompose *)
- (* Introduce a temporary variable to receive the value of the
- * monadic binding *)
- let vid = fresh_id () in
- let tmp : var = { id = vid; basename = None; ty = lv.ty } in
- let ltmp = mk_typed_pattern_from_var tmp None in
- let rtmp = mk_texpression_from_var tmp in
- (* Visit the next expression *)
- let next_e = self#visit_texpression env next_e in
- (* Create the let-bindings *)
- (mk_let true ltmp re (mk_let false lv rtmp next_e)).e
- end
- in
- (* Update the body *)
- let body = Some { body with body = obj#visit_texpression () body.body } in
- (* Return *)
- { def with body }
-
-(** Unfold the monadic let-bindings to explicit matches. *)
-let unfold_monadic_let_bindings (_ctx : trans_ctx) (def : fun_decl) : fun_decl =
- match def.body with
- | None -> def
- | Some body ->
- (* It is a very simple map *)
- let obj =
- object (_self)
- inherit [_] map_expression as super
-
- method! visit_Let env monadic lv re e =
- (* We simply do the following transformation:
- {[
- pat <-- re; e
-
- ~~>
-
- match re with
- | Fail err -> Fail err
- | Return pat -> e
- ]}
- *)
- (* TODO: we should use a monad "kind" instead of a boolean *)
- if not monadic then super#visit_Let env monadic lv re e
- else
- (* We don't do the same thing if we use a state-error monad or simply
- an error monad.
- Note that some functions always live in the error monad (arithmetic
- operations, for instance).
- *)
- (* TODO: this information should be computed in SymbolicToPure and
- * store in an enum ("monadic" should be an enum, not a bool). *)
- let re_ty = Option.get (opt_destruct_result re.ty) in
- assert (lv.ty = re_ty);
- let fail_pat = mk_result_fail_pattern lv.ty in
- let fail_value = mk_result_fail_texpression e.ty in
- let fail_branch = { pat = fail_pat; branch = fail_value } in
- let success_pat = mk_result_return_pattern lv in
- let success_branch = { pat = success_pat; branch = e } in
- let switch_body = Match [ fail_branch; success_branch ] in
- let e = Switch (re, switch_body) in
- (* Continue *)
- super#visit_expression env e
- end
- in
- (* Update the body *)
- let body_e = obj#visit_texpression () body.body in
- let body = { body with body = body_e } in
- (* Return *)
- { def with body = Some body }
-
-(** Apply all the micro-passes to a function.
-
- Will return [None] if the function is a backward function with no outputs.
-
- [ctx]: used only for printing.
- *)
-let apply_passes_to_def (config : config) (ctx : trans_ctx) (def : fun_decl) :
- fun_decl option =
- (* Debug *)
- log#ldebug
- (lazy
- ("PureMicroPasses.apply_passes_to_def: "
- ^ Print.fun_name_to_string def.basename
- ^ " ("
- ^ Print.option_to_string T.RegionGroupId.to_string def.back_id
- ^ ")"));
-
- (* First, find names for the variables which are unnamed *)
- let def = compute_pretty_names def in
- log#ldebug
- (lazy ("compute_pretty_name:\n\n" ^ fun_decl_to_string ctx def ^ "\n"));
-
- (* TODO: we might want to leverage more the assignment meta-data, for
- * aggregates for instance. *)
-
- (* TODO: reorder the branches of the matches/switches *)
-
- (* The meta-information is now useless: remove it.
- * Rk.: some passes below use the fact that we removed the meta-data
- * (otherwise we would have to "unmeta" expressions before matching) *)
- let def = remove_meta def in
- log#ldebug (lazy ("remove_meta:\n\n" ^ fun_decl_to_string ctx def ^ "\n"));
-
- (* Remove the backward functions with no outputs.
- * Note that the calls to those functions should already have been removed,
- * when translating from symbolic to pure. Here, we remove the definitions
- * altogether, because they are now useless *)
- let def = filter_if_backward_with_no_outputs config def in
-
- match def with
- | None -> None
- | Some def ->
- (* Convert the unit variables to [()] if they are used as right-values or
- * [_] if they are used as left values. *)
- let def = unit_vars_to_unit def in
- log#ldebug
- (lazy ("unit_vars_to_unit:\n\n" ^ fun_decl_to_string ctx def ^ "\n"));
-
- (* Inline the useless variable reassignments *)
- let inline_named_vars = true in
- let inline_pure = true in
- let def =
- inline_useless_var_reassignments inline_named_vars inline_pure def
- in
- log#ldebug
- (lazy
- ("inline_useless_var_assignments:\n\n" ^ fun_decl_to_string ctx def
- ^ "\n"));
-
- (* Eliminate the box functions - note that the "box" types were eliminated
- * during the symbolic to pure phase: see the comments for [eliminate_box_functions] *)
- let def = eliminate_box_functions ctx def in
- log#ldebug
- (lazy
- ("eliminate_box_functions:\n\n" ^ fun_decl_to_string ctx def ^ "\n"));
-
- (* Filter the useless variables, assignments, function calls, etc. *)
- let def = filter_useless config.filter_useless_monadic_calls ctx def in
- log#ldebug
- (lazy ("filter_useless:\n\n" ^ fun_decl_to_string ctx def ^ "\n"));
-
- (* Simplify the aggregated ADTs.
- Ex.:
- {[
- type struct = { f0 : nat; f1 : nat }
-
- Mkstruct x.f0 x.f1 ~~> x
- ]}
- *)
- let def = simplify_aggregates ctx def in
- log#ldebug
- (lazy ("simplify_aggregates:\n\n" ^ fun_decl_to_string ctx def ^ "\n"));
-
- (* Decompose the monadic let-bindings - F* specific
- * TODO: remove? *)
- let def =
- if config.decompose_monadic_let_bindings then (
- let def = decompose_monadic_let_bindings ctx def in
- log#ldebug
- (lazy
- ("decompose_monadic_let_bindings:\n\n"
- ^ fun_decl_to_string ctx def ^ "\n"));
- def)
- else (
- log#ldebug
- (lazy
- "ignoring decompose_monadic_let_bindings due to the configuration\n");
- def)
- in
-
- (* Unfold the monadic let-bindings *)
- let def =
- if config.unfold_monadic_let_bindings then (
- let def = unfold_monadic_let_bindings ctx def in
- log#ldebug
- (lazy
- ("unfold_monadic_let_bindings:\n\n" ^ fun_decl_to_string ctx def
- ^ "\n"));
- def)
- else (
- log#ldebug
- (lazy
- "ignoring unfold_monadic_let_bindings due to the configuration\n");
- def)
- in
-
- (* We are done *)
- Some def
-
-(** Return the forward/backward translations on which we applied the micro-passes.
-
- Also returns a boolean indicating whether the forward function should be kept
- or not (because useful/useless - [true] means we need to keep the forward
- function).
- Note that we don't "filter" the forward function and return a boolean instead,
- because this function contains useful information to extract the backward
- functions: keeping it is not necessary but more convenient.
- *)
-let apply_passes_to_pure_fun_translation (config : config) (ctx : trans_ctx)
- (trans : pure_fun_translation) : bool * pure_fun_translation =
- (* Apply the passes to the individual functions *)
- let forward, backwards = trans in
- let forward = Option.get (apply_passes_to_def config ctx forward) in
- let backwards = List.filter_map (apply_passes_to_def config ctx) backwards in
- let trans = (forward, backwards) in
- (* Compute whether we need to filter the forward function or not *)
- (keep_forward config trans, trans)
diff --git a/src/PureToExtract.ml b/src/PureToExtract.ml
deleted file mode 100644
index 77c3afd4..00000000
--- a/src/PureToExtract.ml
+++ /dev/null
@@ -1,723 +0,0 @@
-(** This module is used to extract the pure ASTs to various theorem provers.
- It defines utilities and helpers to make the work as easy as possible:
- we try to factorize as much as possible the different extractions to the
- backends we target.
- *)
-
-open Pure
-open TranslateCore
-module C = Contexts
-module RegionVarId = T.RegionVarId
-module F = Format
-
-(** The local logger *)
-let log = L.pure_to_extract_log
-
-type region_group_info = {
- id : RegionGroupId.id;
- (** The id of the region group.
- Note that a simple way of generating unique names for backward
- functions is to use the region group ids.
- *)
- region_names : string option list;
- (** The names of the region variables included in this group.
- Note that names are not always available...
- *)
-}
-
-module StringSet = Collections.MakeSet (Collections.OrderedString)
-module StringMap = Collections.MakeMap (Collections.OrderedString)
-
-type name = Names.name
-type type_name = Names.type_name
-type global_name = Names.global_name
-type fun_name = Names.fun_name
-
-(* TODO: this should a module we give to a functor! *)
-
-(** A formatter's role is twofold:
- 1. Come up with name suggestions.
- For instance, provided some information about a function (its basename,
- information about the region group, etc.) it should come up with an
- appropriate name for the forward/backward function.
-
- It can of course apply many transformations, like changing to camel case/
- snake case, adding prefixes/suffixes, etc.
-
- 2. Format some specific terms, like constants.
- *)
-type formatter = {
- bool_name : string;
- char_name : string;
- int_name : integer_type -> string;
- str_name : string;
- field_name : name -> FieldId.id -> string option -> string;
- (** Inputs:
- - type name
- - field id
- - field name
-
- Note that fields don't always have names, but we still need to
- generate some names if we want to extract the structures to records...
- We might want to extract such structures to tuples, later, but field
- access then causes trouble because not all provers accept syntax like
- [x.3] where [x] is a tuple.
- *)
- variant_name : name -> string -> string;
- (** Inputs:
- - type name
- - variant name
- *)
- struct_constructor : name -> string;
- (** Structure constructors are used when constructing structure values.
-
- For instance, in F*:
- {[
- type pair = { x : nat; y : nat }
- let p : pair = Mkpair 0 1
- ]}
-
- Inputs:
- - type name
- *)
- type_name : type_name -> string;
- (** Provided a basename, compute a type name. *)
- global_name : global_name -> string;
- (** Provided a basename, compute a global name. *)
- fun_name :
- A.fun_id ->
- fun_name ->
- int ->
- region_group_info option ->
- bool * int ->
- string;
- (** Inputs:
- - function id: this is especially useful to identify whether the
- function is an assumed function or a local function
- - function basename
- - number of region groups
- - region group information in case of a backward function
- ([None] if forward function)
- - pair:
- - do we generate the forward function (it may have been filtered)?
- - the number of extracted backward functions (not necessarily equal
- to the number of region groups, because we may have filtered
- some of them)
- TODO: use the fun id for the assumed functions.
- *)
- decreases_clause_name : A.FunDeclId.id -> fun_name -> string;
- (** Generates the name of the definition used to prove/reason about
- termination. The generated code uses this clause where needed,
- but its body must be defined by the user.
-
- Inputs:
- - function id: this is especially useful to identify whether the
- function is an assumed function or a local function
- - function basename
- *)
- var_basename : StringSet.t -> string option -> ty -> string;
- (** Generates a variable basename.
-
- Inputs:
- - the set of names used in the context so far
- - the basename we got from the symbolic execution, if we have one
- - the type of the variable (can be useful for heuristics, in order
- not to always use "x" for instance, whenever naming anonymous
- variables)
-
- Note that once the formatter generated a basename, we add an index
- if necessary to prevent name clashes: the burden of name clashes checks
- is thus on the caller's side.
- *)
- type_var_basename : StringSet.t -> string -> string;
- (** Generates a type variable basename. *)
- append_index : string -> int -> string;
- (** Appends an index to a name - we use this to generate unique
- names: when doing so, the role of the formatter is just to concatenate
- indices to names, the responsability of finding a proper index is
- delegated to helper functions.
- *)
- extract_constant_value : F.formatter -> bool -> constant_value -> unit;
- (** Format a constant value.
-
- Inputs:
- - formatter
- - [inside]: if [true], the value should be wrapped in parentheses
- if it is made of an application (ex.: [U32 3])
- - the constant value
- *)
- extract_unop :
- (bool -> texpression -> unit) ->
- F.formatter ->
- bool ->
- unop ->
- texpression ->
- unit;
- (** Format a unary operation
-
- Inputs:
- - a formatter for expressions (called on the argument of the unop)
- - extraction context (see below)
- - formatter
- - expression formatter
- - [inside]
- - unop
- - argument
- *)
- extract_binop :
- (bool -> texpression -> unit) ->
- F.formatter ->
- bool ->
- E.binop ->
- integer_type ->
- texpression ->
- texpression ->
- unit;
- (** Format a binary operation
-
- Inputs:
- - a formatter for expressions (called on the arguments of the binop)
- - extraction context (see below)
- - formatter
- - expression formatter
- - [inside]
- - binop
- - argument 0
- - argument 1
- *)
-}
-
-(** We use identifiers to look for name clashes *)
-type id =
- | GlobalId of A.GlobalDeclId.id
- | FunId of A.fun_id * RegionGroupId.id option
- | DecreasesClauseId of A.fun_id
- (** The definition which provides the decreases/termination clause.
- We insert calls to this clause to prove/reason about termination:
- the body of those clauses must be defined by the user, in the
- proper files.
- *)
- | TypeId of type_id
- | StructId of type_id
- (** We use this when we manipulate the names of the structure
- constructors.
-
- For instance, in F*:
- {[
- type pair = { x: nat; y : nat }
- let p : pair = Mkpair 0 1
- ]}
- *)
- | VariantId of type_id * VariantId.id
- (** If often happens that variant names must be unique (it is the case in
- F* ) which is why we register them here.
- *)
- | FieldId of type_id * FieldId.id
- (** If often happens that in the case of structures, the field names
- must be unique (it is the case in F* ) which is why we register
- them here.
- *)
- | TypeVarId of TypeVarId.id
- | VarId of VarId.id
- | UnknownId
- (** Used for stored various strings like keywords, definitions which
- should always be in context, etc. and which can't be linked to one
- of the above.
- *)
-[@@deriving show, ord]
-
-module IdOrderedType = struct
- type t = id
-
- let compare = compare_id
- let to_string = show_id
- let pp_t = pp_id
- let show_t = show_id
-end
-
-module IdMap = Collections.MakeMap (IdOrderedType)
-
-(** The names map stores the mappings from names to identifiers and vice-versa.
-
- We use it for lookups (during the translation) and to check for name clashes.
-
- [id_to_string] is for debugging.
- *)
-type names_map = {
- id_to_name : string IdMap.t;
- name_to_id : id StringMap.t;
- (** The name to id map is used to look for name clashes, and generate nice
- debugging messages: if there is a name clash, it is useful to know
- precisely which identifiers are mapped to the same name...
- *)
- names_set : StringSet.t;
-}
-
-let names_map_add (id_to_string : id -> string) (id : id) (name : string)
- (nm : names_map) : names_map =
- (* Check if there is a clash *)
- (match StringMap.find_opt name nm.name_to_id with
- | None -> () (* Ok *)
- | Some clash ->
- (* There is a clash: print a nice debugging message for the user *)
- let id1 = "\n- " ^ id_to_string clash in
- let id2 = "\n- " ^ id_to_string id in
- let err =
- "Name clash detected: the following identifiers are bound to the same \
- name \"" ^ name ^ "\":" ^ id1 ^ id2
- in
- log#serror err;
- failwith err);
- (* Sanity check *)
- assert (not (StringSet.mem name nm.names_set));
- (* Insert *)
- let id_to_name = IdMap.add id name nm.id_to_name in
- let name_to_id = StringMap.add name id nm.name_to_id in
- let names_set = StringSet.add name nm.names_set in
- { id_to_name; name_to_id; names_set }
-
-let names_map_add_assumed_type (id_to_string : id -> string) (id : assumed_ty)
- (name : string) (nm : names_map) : names_map =
- names_map_add id_to_string (TypeId (Assumed id)) name nm
-
-let names_map_add_assumed_struct (id_to_string : id -> string) (id : assumed_ty)
- (name : string) (nm : names_map) : names_map =
- names_map_add id_to_string (StructId (Assumed id)) name nm
-
-let names_map_add_assumed_variant (id_to_string : id -> string)
- (id : assumed_ty) (variant_id : VariantId.id) (name : string)
- (nm : names_map) : names_map =
- names_map_add id_to_string (VariantId (Assumed id, variant_id)) name nm
-
-let names_map_add_assumed_function (id_to_string : id -> string)
- (fid : A.assumed_fun_id) (rg_id : RegionGroupId.id option) (name : string)
- (nm : names_map) : names_map =
- names_map_add id_to_string (FunId (A.Assumed fid, rg_id)) name nm
-
-(** Make a (variable) basename unique (by adding an index).
-
- We do this in an inefficient manner (by testing all indices starting from
- 0) but it shouldn't be a bottleneck.
-
- Also note that at some point, we thought about trying to reuse names of
- variables which are not used anymore, like here:
- {[
- let x = ... in
- ...
- let x0 = ... in // We could use the name "x" if [x] is not used below
- ...
- ]}
-
- However it is a good idea to keep things as they are for F*: as F* is
- designed for extrinsic proofs, a proof about a function follows this
- function's structure. The consequence is that we often end up
- copy-pasting function bodies. As in the proofs (in assertions and
- when calling lemmas) we often need to talk about the "past" (i.e.,
- previous values), it is very useful to generate code where all variable
- names are assigned at most once.
-
- [append]: function to append an index to a string
- *)
-let basename_to_unique (names_set : StringSet.t)
- (append : string -> int -> string) (basename : string) : string =
- let rec gen (i : int) : string =
- let s = append basename i in
- if StringSet.mem s names_set then gen (i + 1) else s
- in
- if StringSet.mem basename names_set then gen 0 else basename
-
-(** Extraction context.
-
- Note that the extraction context contains information coming from the
- LLBC AST (not only the pure AST). This is useful for naming, for instance:
- we use the region information to generate the names of the backward
- functions, etc.
- *)
-type extraction_ctx = {
- trans_ctx : trans_ctx;
- names_map : names_map;
- fmt : formatter;
- indent_incr : int;
- (** The indent increment we insert whenever we need to indent more *)
-}
-
-(** Debugging function *)
-let id_to_string (id : id) (ctx : extraction_ctx) : string =
- let global_decls = ctx.trans_ctx.global_context.global_decls in
- let fun_decls = ctx.trans_ctx.fun_context.fun_decls in
- let type_decls = ctx.trans_ctx.type_context.type_decls in
- (* TODO: factorize the pretty-printing with what is in PrintPure *)
- let get_type_name (id : type_id) : string =
- match id with
- | AdtId id ->
- let def = TypeDeclId.Map.find id type_decls in
- Print.name_to_string def.name
- | Assumed aty -> show_assumed_ty aty
- | Tuple -> failwith "Unreachable"
- in
- match id with
- | GlobalId gid ->
- let name = (A.GlobalDeclId.Map.find gid global_decls).name in
- "global name: " ^ Print.global_name_to_string name
- | FunId (fid, rg_id) ->
- let fun_name =
- match fid with
- | A.Regular fid ->
- Print.fun_name_to_string (A.FunDeclId.Map.find fid fun_decls).name
- | A.Assumed aid -> A.show_assumed_fun_id aid
- in
- let fun_kind =
- match rg_id with
- | None -> "forward"
- | Some rg_id -> "backward " ^ RegionGroupId.to_string rg_id
- in
- "fun name (" ^ fun_kind ^ "): " ^ fun_name
- | DecreasesClauseId fid ->
- let fun_name =
- match fid with
- | A.Regular fid ->
- Print.fun_name_to_string (A.FunDeclId.Map.find fid fun_decls).name
- | A.Assumed aid -> A.show_assumed_fun_id aid
- in
- "decreases clause for function: " ^ fun_name
- | TypeId id -> "type name: " ^ get_type_name id
- | StructId id -> "struct constructor of: " ^ get_type_name id
- | VariantId (id, variant_id) ->
- let variant_name =
- match id with
- | Tuple -> failwith "Unreachable"
- | Assumed State -> failwith "Unreachable"
- | Assumed Result ->
- if variant_id = result_return_id then "@result::Return"
- else if variant_id = result_fail_id then "@result::Fail"
- else failwith "Unreachable"
- | Assumed Option ->
- if variant_id = option_some_id then "@option::Some"
- else if variant_id = option_none_id then "@option::None"
- else failwith "Unreachable"
- | Assumed Vec -> failwith "Unreachable"
- | AdtId id -> (
- let def = TypeDeclId.Map.find id type_decls in
- match def.kind with
- | Struct _ | Opaque -> failwith "Unreachable"
- | Enum variants ->
- let variant = VariantId.nth variants variant_id in
- Print.name_to_string def.name ^ "::" ^ variant.variant_name)
- in
- "variant name: " ^ variant_name
- | FieldId (id, field_id) ->
- let field_name =
- match id with
- | Tuple -> failwith "Unreachable"
- | Assumed (State | Result | Option) -> failwith "Unreachable"
- | Assumed Vec ->
- (* We can't directly have access to the fields of a vector *)
- failwith "Unreachable"
- | AdtId id -> (
- let def = TypeDeclId.Map.find id type_decls in
- match def.kind with
- | Enum _ | Opaque -> failwith "Unreachable"
- | Struct fields ->
- let field = FieldId.nth fields field_id in
- let field_name =
- match field.field_name with
- | None -> FieldId.to_string field_id
- | Some name -> name
- in
- Print.name_to_string def.name ^ "." ^ field_name)
- in
- "field name: " ^ field_name
- | UnknownId -> "keyword"
- | TypeVarId _ | VarId _ ->
- (* We should never get there: we add indices to make sure variable
- * names are unique *)
- failwith "Unreachable"
-
-let ctx_add (id : id) (name : string) (ctx : extraction_ctx) : extraction_ctx =
- (* The id_to_string function to print nice debugging messages if there are
- * collisions *)
- let id_to_string (id : id) : string = id_to_string id ctx in
- let names_map = names_map_add id_to_string id name ctx.names_map in
- { ctx with names_map }
-
-let ctx_get (id : id) (ctx : extraction_ctx) : string =
- match IdMap.find_opt id ctx.names_map.id_to_name with
- | Some s -> s
- | None ->
- log#serror ("Could not find: " ^ id_to_string id ctx);
- raise Not_found
-
-let ctx_get_global (id : A.GlobalDeclId.id) (ctx : extraction_ctx) : string =
- ctx_get (GlobalId id) ctx
-
-let ctx_get_function (id : A.fun_id) (rg : RegionGroupId.id option)
- (ctx : extraction_ctx) : string =
- ctx_get (FunId (id, rg)) ctx
-
-let ctx_get_local_function (id : A.FunDeclId.id) (rg : RegionGroupId.id option)
- (ctx : extraction_ctx) : string =
- ctx_get_function (A.Regular id) rg ctx
-
-let ctx_get_type (id : type_id) (ctx : extraction_ctx) : string =
- assert (id <> Tuple);
- ctx_get (TypeId id) ctx
-
-let ctx_get_local_type (id : TypeDeclId.id) (ctx : extraction_ctx) : string =
- ctx_get_type (AdtId id) ctx
-
-let ctx_get_assumed_type (id : assumed_ty) (ctx : extraction_ctx) : string =
- ctx_get_type (Assumed id) ctx
-
-let ctx_get_var (id : VarId.id) (ctx : extraction_ctx) : string =
- ctx_get (VarId id) ctx
-
-let ctx_get_type_var (id : TypeVarId.id) (ctx : extraction_ctx) : string =
- ctx_get (TypeVarId id) ctx
-
-let ctx_get_field (type_id : type_id) (field_id : FieldId.id)
- (ctx : extraction_ctx) : string =
- ctx_get (FieldId (type_id, field_id)) ctx
-
-let ctx_get_struct (def_id : type_id) (ctx : extraction_ctx) : string =
- ctx_get (StructId def_id) ctx
-
-let ctx_get_variant (def_id : type_id) (variant_id : VariantId.id)
- (ctx : extraction_ctx) : string =
- ctx_get (VariantId (def_id, variant_id)) ctx
-
-let ctx_get_decreases_clause (def_id : A.FunDeclId.id) (ctx : extraction_ctx) :
- string =
- ctx_get (DecreasesClauseId (A.Regular def_id)) ctx
-
-(** Generate a unique type variable name and add it to the context *)
-let ctx_add_type_var (basename : string) (id : TypeVarId.id)
- (ctx : extraction_ctx) : extraction_ctx * string =
- let name = ctx.fmt.type_var_basename ctx.names_map.names_set basename in
- let name =
- basename_to_unique ctx.names_map.names_set ctx.fmt.append_index name
- in
- let ctx = ctx_add (TypeVarId id) name ctx in
- (ctx, name)
-
-(** See {!ctx_add_type_var} *)
-let ctx_add_type_vars (vars : (string * TypeVarId.id) list)
- (ctx : extraction_ctx) : extraction_ctx * string list =
- List.fold_left_map
- (fun ctx (name, id) -> ctx_add_type_var name id ctx)
- ctx vars
-
-(** Generate a unique variable name and add it to the context *)
-let ctx_add_var (basename : string) (id : VarId.id) (ctx : extraction_ctx) :
- extraction_ctx * string =
- let name =
- basename_to_unique ctx.names_map.names_set ctx.fmt.append_index basename
- in
- let ctx = ctx_add (VarId id) name ctx in
- (ctx, name)
-
-(** See {!ctx_add_var} *)
-let ctx_add_vars (vars : var list) (ctx : extraction_ctx) :
- extraction_ctx * string list =
- List.fold_left_map
- (fun ctx (v : var) ->
- let name = ctx.fmt.var_basename ctx.names_map.names_set v.basename v.ty in
- ctx_add_var name v.id ctx)
- ctx vars
-
-let ctx_add_type_params (vars : type_var list) (ctx : extraction_ctx) :
- extraction_ctx * string list =
- List.fold_left_map
- (fun ctx (var : type_var) -> ctx_add_type_var var.name var.index ctx)
- ctx vars
-
-let ctx_add_type_decl_struct (def : type_decl) (ctx : extraction_ctx) :
- extraction_ctx * string =
- let cons_name = ctx.fmt.struct_constructor def.name in
- let ctx = ctx_add (StructId (AdtId def.def_id)) cons_name ctx in
- (ctx, cons_name)
-
-let ctx_add_type_decl (def : type_decl) (ctx : extraction_ctx) : extraction_ctx
- =
- let def_name = ctx.fmt.type_name def.name in
- let ctx = ctx_add (TypeId (AdtId def.def_id)) def_name ctx in
- ctx
-
-let ctx_add_field (def : type_decl) (field_id : FieldId.id) (field : field)
- (ctx : extraction_ctx) : extraction_ctx * string =
- let name = ctx.fmt.field_name def.name field_id field.field_name in
- let ctx = ctx_add (FieldId (AdtId def.def_id, field_id)) name ctx in
- (ctx, name)
-
-let ctx_add_fields (def : type_decl) (fields : (FieldId.id * field) list)
- (ctx : extraction_ctx) : extraction_ctx * string list =
- List.fold_left_map
- (fun ctx (vid, v) -> ctx_add_field def vid v ctx)
- ctx fields
-
-let ctx_add_variant (def : type_decl) (variant_id : VariantId.id)
- (variant : variant) (ctx : extraction_ctx) : extraction_ctx * string =
- let name = ctx.fmt.variant_name def.name variant.variant_name in
- let ctx = ctx_add (VariantId (AdtId def.def_id, variant_id)) name ctx in
- (ctx, name)
-
-let ctx_add_variants (def : type_decl)
- (variants : (VariantId.id * variant) list) (ctx : extraction_ctx) :
- extraction_ctx * string list =
- List.fold_left_map
- (fun ctx (vid, v) -> ctx_add_variant def vid v ctx)
- ctx variants
-
-let ctx_add_struct (def : type_decl) (ctx : extraction_ctx) :
- extraction_ctx * string =
- let name = ctx.fmt.struct_constructor def.name in
- let ctx = ctx_add (StructId (AdtId def.def_id)) name ctx in
- (ctx, name)
-
-let ctx_add_decrases_clause (def : fun_decl) (ctx : extraction_ctx) :
- extraction_ctx =
- let name = ctx.fmt.decreases_clause_name def.def_id def.basename in
- ctx_add (DecreasesClauseId (A.Regular def.def_id)) name ctx
-
-let ctx_add_global_decl_and_body (def : A.global_decl) (ctx : extraction_ctx) :
- extraction_ctx =
- let name = ctx.fmt.global_name def.name in
- let decl = GlobalId def.def_id in
- let body = FunId (Regular def.body_id, None) in
- let ctx = ctx_add decl (name ^ "_c") ctx in
- let ctx = ctx_add body (name ^ "_body") ctx in
- ctx
-
-let ctx_add_fun_decl (trans_group : bool * pure_fun_translation)
- (def : fun_decl) (ctx : extraction_ctx) : extraction_ctx =
- (* Sanity check: the function should not be a global body - those are handled
- * separately *)
- assert (not def.is_global_decl_body);
- (* Lookup the LLBC def to compute the region group information *)
- let def_id = def.def_id in
- let llbc_def =
- A.FunDeclId.Map.find def_id ctx.trans_ctx.fun_context.fun_decls
- in
- let sg = llbc_def.signature in
- let num_rgs = List.length sg.regions_hierarchy in
- let keep_fwd, (_, backs) = trans_group in
- let num_backs = List.length backs in
- let rg_info =
- match def.back_id with
- | None -> None
- | Some rg_id ->
- let rg = T.RegionGroupId.nth sg.regions_hierarchy rg_id in
- let regions =
- List.map
- (fun rid -> T.RegionVarId.nth sg.region_params rid)
- rg.regions
- in
- let region_names =
- List.map (fun (r : T.region_var) -> r.name) regions
- in
- Some { id = rg_id; region_names }
- in
- let def_id = A.Regular def_id in
- let name =
- ctx.fmt.fun_name def_id def.basename num_rgs rg_info (keep_fwd, num_backs)
- in
- ctx_add (FunId (def_id, def.back_id)) name ctx
-
-type names_map_init = {
- keywords : string list;
- assumed_adts : (assumed_ty * string) list;
- assumed_structs : (assumed_ty * string) list;
- assumed_variants : (assumed_ty * VariantId.id * string) list;
- assumed_functions : (A.assumed_fun_id * RegionGroupId.id option * string) list;
-}
-
-(** Initialize a names map with a proper set of keywords/names coming from the
- target language/prover. *)
-let initialize_names_map (init : names_map_init) : names_map =
- let name_to_id =
- StringMap.of_list (List.map (fun x -> (x, UnknownId)) init.keywords)
- in
- let names_set = StringSet.of_list init.keywords in
- (* We fist initialize [id_to_name] as empty, because the id of a keyword is [UnknownId].
- * Also note that we don't need this mapping for keywords: we insert keywords only
- * to check collisions. *)
- let id_to_name = IdMap.empty in
- let nm = { id_to_name; name_to_id; names_set } in
- (* For debugging - we are creating bindings for assumed types and functions, so
- * it is ok if we simply use the "show" function (those aren't simply identified
- * by numbers) *)
- let id_to_string = show_id in
- (* Then we add:
- * - the assumed types
- * - the assumed struct constructors
- * - the assumed variants
- * - the assumed functions
- *)
- let nm =
- List.fold_left
- (fun nm (type_id, name) ->
- names_map_add_assumed_type id_to_string type_id name nm)
- nm init.assumed_adts
- in
- let nm =
- List.fold_left
- (fun nm (type_id, name) ->
- names_map_add_assumed_struct id_to_string type_id name nm)
- nm init.assumed_structs
- in
- let nm =
- List.fold_left
- (fun nm (type_id, variant_id, name) ->
- names_map_add_assumed_variant id_to_string type_id variant_id name nm)
- nm init.assumed_variants
- in
- let nm =
- List.fold_left
- (fun nm (fun_id, rg_id, name) ->
- names_map_add_assumed_function id_to_string fun_id rg_id name nm)
- nm init.assumed_functions
- in
- (* Return *)
- nm
-
-let compute_type_decl_name (fmt : formatter) (def : type_decl) : string =
- fmt.type_name def.name
-
-(** A helper function: generates a function suffix from a region group
- information.
- TODO: move all those helpers.
-*)
-let default_fun_suffix (num_region_groups : int) (rg : region_group_info option)
- ((keep_fwd, num_backs) : bool * int) : string =
- (* There are several cases:
- - [rg] is [Some]: this is a forward function:
- - we add "_fwd"
- - [rg] is [None]: this is a backward function:
- - this function has one extracted backward function:
- - if the forward function has been filtered, we add "_fwd_back":
- the forward function is useless, so the unique backward function
- takes its place, in a way
- - otherwise we add "_back"
- - this function has several backward functions: we add "_back" and an
- additional suffix to identify the precise backward function
- Note that we always add a suffix (in case there are no region groups,
- we could not add the "_fwd" suffix) to prevent name clashes between
- definitions (in particular between type and function definitions).
- *)
- match rg with
- | None -> "_fwd"
- | Some rg ->
- assert (num_region_groups > 0 && num_backs > 0);
- if num_backs = 1 then
- (* Exactly one backward function *)
- if not keep_fwd then "_fwd_back" else "_back"
- else if
- (* Several region groups/backward functions:
- - if all the regions in the group have names, we use those names
- - otherwise we use an index
- *)
- List.for_all Option.is_some rg.region_names
- then
- (* Concatenate the region names *)
- "_back" ^ String.concat "" (List.map Option.get rg.region_names)
- else (* Use the region index *)
- "_back" ^ RegionGroupId.to_string rg.id
diff --git a/src/PureTypeCheck.ml b/src/PureTypeCheck.ml
deleted file mode 100644
index caad8a58..00000000
--- a/src/PureTypeCheck.ml
+++ /dev/null
@@ -1,178 +0,0 @@
-(** Module to perform type checking on the pure AST - we use this for sanity checks only *)
-
-open Pure
-open PureUtils
-
-(** Utility function, used for type checking *)
-let get_adt_field_types (type_decls : type_decl TypeDeclId.Map.t)
- (type_id : type_id) (variant_id : VariantId.id option) (tys : ty list) :
- ty list =
- match type_id with
- | Tuple ->
- (* Tuple *)
- assert (variant_id = None);
- tys
- | AdtId def_id ->
- (* "Regular" ADT *)
- let def = TypeDeclId.Map.find def_id type_decls in
- type_decl_get_instantiated_fields_types def variant_id tys
- | Assumed aty -> (
- (* Assumed type *)
- match aty with
- | State ->
- (* [State] is opaque *)
- raise (Failure "Unreachable: `State` values are opaque")
- | Result ->
- let ty = Collections.List.to_cons_nil tys in
- let variant_id = Option.get variant_id in
- if variant_id = result_return_id then [ ty ]
- else if variant_id = result_fail_id then []
- else
- raise (Failure "Unreachable: improper variant id for result type")
- | Option ->
- let ty = Collections.List.to_cons_nil tys in
- let variant_id = Option.get variant_id in
- if variant_id = option_some_id then [ ty ]
- else if variant_id = option_none_id then []
- else
- raise (Failure "Unreachable: improper variant id for result type")
- | Vec -> raise (Failure "Unreachable: `Vector` values are opaque"))
-
-type tc_ctx = {
- type_decls : type_decl TypeDeclId.Map.t; (** The type declarations *)
- global_decls : A.global_decl A.GlobalDeclId.Map.t;
- (** The global declarations *)
- env : ty VarId.Map.t; (** Environment from variables to types *)
-}
-
-let check_constant_value (v : constant_value) (ty : ty) : unit =
- match (ty, v) with
- | Integer int_ty, V.Scalar sv -> assert (int_ty = sv.V.int_ty)
- | Bool, Bool _ | Char, Char _ | Str, String _ -> ()
- | _ -> raise (Failure "Inconsistent type")
-
-let rec check_typed_pattern (ctx : tc_ctx) (v : typed_pattern) : tc_ctx =
- log#ldebug (lazy ("check_typed_pattern: " ^ show_typed_pattern v));
- match v.value with
- | PatConcrete cv ->
- check_constant_value cv v.ty;
- ctx
- | PatDummy -> ctx
- | PatVar (var, _) ->
- assert (var.ty = v.ty);
- let env = VarId.Map.add var.id var.ty ctx.env in
- { ctx with env }
- | PatAdt av ->
- (* Compute the field types *)
- let type_id, tys =
- match v.ty with
- | Adt (type_id, tys) -> (type_id, tys)
- | _ -> raise (Failure "Inconsistently typed value")
- in
- let field_tys =
- get_adt_field_types ctx.type_decls type_id av.variant_id tys
- in
- let check_value (ctx : tc_ctx) (ty : ty) (v : typed_pattern) : tc_ctx =
- if ty <> v.ty then (
- log#serror
- ("check_typed_pattern: not the same types:" ^ "\n- ty: "
- ^ show_ty ty ^ "\n- v.ty: " ^ show_ty v.ty);
- raise (Failure "Inconsistent types"));
- check_typed_pattern ctx v
- in
- (* Check the field types: check that the field patterns have the expected
- * types, and check that the field patterns themselves are well-typed *)
- List.fold_left
- (fun ctx (ty, v) -> check_value ctx ty v)
- ctx
- (List.combine field_tys av.field_values)
-
-let rec check_texpression (ctx : tc_ctx) (e : texpression) : unit =
- match e.e with
- | Var var_id -> (
- (* Lookup the variable - note that the variable may not be there,
- * if we type-check a subexpression (i.e.: if the variable is introduced
- * "outside" of the expression) - TODO: this won't happen once
- * we use a locally nameless representation *)
- match VarId.Map.find_opt var_id ctx.env with
- | None -> ()
- | Some ty -> assert (ty = e.ty))
- | Const cv -> check_constant_value cv e.ty
- | App (app, arg) ->
- let input_ty, output_ty = destruct_arrow app.ty in
- assert (input_ty = arg.ty);
- assert (output_ty = e.ty);
- check_texpression ctx app;
- check_texpression ctx arg
- | Abs (pat, body) ->
- let pat_ty, body_ty = destruct_arrow e.ty in
- assert (pat.ty = pat_ty);
- assert (body.ty = body_ty);
- (* Check the pattern and register the introduced variables at the same time *)
- let ctx = check_typed_pattern ctx pat in
- check_texpression ctx body
- | Qualif qualif -> (
- match qualif.id with
- | Func _ -> () (* TODO *)
- | Global _ -> () (* TODO *)
- | Proj { adt_id = proj_adt_id; field_id } ->
- (* Note we can only project fields of structures (not enumerations) *)
- (* Deconstruct the projector type *)
- let adt_ty, field_ty = destruct_arrow e.ty in
- let adt_id, adt_type_args =
- match adt_ty with
- | Adt (type_id, tys) -> (type_id, tys)
- | _ -> raise (Failure "Unreachable")
- in
- (* Check the ADT type *)
- assert (adt_id = proj_adt_id);
- assert (adt_type_args = qualif.type_args);
- (* Retrieve and check the expected field type *)
- let variant_id = None in
- let expected_field_tys =
- get_adt_field_types ctx.type_decls proj_adt_id variant_id
- qualif.type_args
- in
- let expected_field_ty = FieldId.nth expected_field_tys field_id in
- assert (expected_field_ty = field_ty)
- | AdtCons id -> (
- let expected_field_tys =
- get_adt_field_types ctx.type_decls id.adt_id id.variant_id
- qualif.type_args
- in
- let field_tys, adt_ty = destruct_arrows e.ty in
- assert (expected_field_tys = field_tys);
- match adt_ty with
- | Adt (type_id, tys) ->
- assert (type_id = id.adt_id);
- assert (tys = qualif.type_args)
- | _ -> raise (Failure "Unreachable")))
- | Let (monadic, pat, re, e_next) ->
- let expected_pat_ty = if monadic then destruct_result re.ty else re.ty in
- assert (pat.ty = expected_pat_ty);
- assert (e.ty = e_next.ty);
- (* Check the right-expression *)
- check_texpression ctx re;
- (* Check the pattern and register the introduced variables at the same time *)
- let ctx = check_typed_pattern ctx pat in
- (* Check the next expression *)
- check_texpression ctx e_next
- | Switch (scrut, switch_body) -> (
- check_texpression ctx scrut;
- match switch_body with
- | If (e_then, e_else) ->
- assert (scrut.ty = Bool);
- assert (e_then.ty = e.ty);
- assert (e_else.ty = e.ty);
- check_texpression ctx e_then;
- check_texpression ctx e_else
- | Match branches ->
- let check_branch (br : match_branch) : unit =
- assert (br.pat.ty = scrut.ty);
- let ctx = check_typed_pattern ctx br.pat in
- check_texpression ctx br.branch
- in
- List.iter check_branch branches)
- | Meta (_, e_next) ->
- assert (e_next.ty = e.ty);
- check_texpression ctx e_next
diff --git a/src/PureUtils.ml b/src/PureUtils.ml
deleted file mode 100644
index 39f3d76a..00000000
--- a/src/PureUtils.ml
+++ /dev/null
@@ -1,450 +0,0 @@
-open Pure
-
-(** Default logger *)
-let log = Logging.pure_utils_log
-
-(** We use this type as a key for lookups *)
-type regular_fun_id = A.fun_id * T.RegionGroupId.id option
-[@@deriving show, ord]
-
-module RegularFunIdOrderedType = struct
- type t = regular_fun_id
-
- let compare = compare_regular_fun_id
- let to_string = show_regular_fun_id
- let pp_t = pp_regular_fun_id
- let show_t = show_regular_fun_id
-end
-
-module RegularFunIdMap = Collections.MakeMap (RegularFunIdOrderedType)
-
-module FunIdOrderedType = struct
- type t = fun_id
-
- let compare = compare_fun_id
- let to_string = show_fun_id
- let pp_t = pp_fun_id
- let show_t = show_fun_id
-end
-
-module FunIdMap = Collections.MakeMap (FunIdOrderedType)
-module FunIdSet = Collections.MakeSet (FunIdOrderedType)
-
-let dest_arrow_ty (ty : ty) : ty * ty =
- match ty with
- | Arrow (arg_ty, ret_ty) -> (arg_ty, ret_ty)
- | _ -> raise (Failure "Unreachable")
-
-let compute_constant_value_ty (cv : constant_value) : ty =
- match cv with
- | V.Scalar sv -> Integer sv.V.int_ty
- | Bool _ -> Bool
- | Char _ -> Char
- | String _ -> Str
-
-let mk_typed_pattern_from_constant_value (cv : constant_value) : typed_pattern =
- let ty = compute_constant_value_ty cv in
- { value = PatConcrete cv; ty }
-
-let mk_let (monadic : bool) (lv : typed_pattern) (re : texpression)
- (next_e : texpression) : texpression =
- let e = Let (monadic, lv, re, next_e) in
- let ty = next_e.ty in
- { e; ty }
-
-(** Type substitution *)
-let ty_substitute (tsubst : TypeVarId.id -> ty) (ty : ty) : ty =
- let obj =
- object
- inherit [_] map_ty
- method! visit_TypeVar _ var_id = tsubst var_id
- end
- in
- obj#visit_ty () ty
-
-let make_type_subst (vars : type_var list) (tys : ty list) : TypeVarId.id -> ty
- =
- let ls = List.combine vars tys in
- let mp =
- List.fold_left
- (fun mp (k, v) -> TypeVarId.Map.add (k : type_var).index v mp)
- TypeVarId.Map.empty ls
- in
- fun id -> TypeVarId.Map.find id mp
-
-(** Retrieve the list of fields for the given variant of a {!Pure.type_decl}.
-
- Raises [Invalid_argument] if the arguments are incorrect.
- *)
-let type_decl_get_fields (def : type_decl)
- (opt_variant_id : VariantId.id option) : field list =
- match (def.kind, opt_variant_id) with
- | Enum variants, Some variant_id -> (VariantId.nth variants variant_id).fields
- | Struct fields, None -> fields
- | _ ->
- let opt_variant_id =
- match opt_variant_id with None -> "None" | Some _ -> "Some"
- in
- raise
- (Invalid_argument
- ("The variant id should be [Some] if and only if the definition is \
- an enumeration:\n\
- - def: " ^ show_type_decl def ^ "\n- opt_variant_id: "
- ^ opt_variant_id))
-
-(** Instantiate the type variables for the chosen variant in an ADT definition,
- and return the list of the types of its fields *)
-let type_decl_get_instantiated_fields_types (def : type_decl)
- (opt_variant_id : VariantId.id option) (types : ty list) : ty list =
- let ty_subst = make_type_subst def.type_params types in
- let fields = type_decl_get_fields def opt_variant_id in
- List.map (fun f -> ty_substitute ty_subst f.field_ty) fields
-
-let fun_sig_substitute (tsubst : TypeVarId.id -> ty) (sg : fun_sig) :
- inst_fun_sig =
- let subst = ty_substitute tsubst in
- let inputs = List.map subst sg.inputs in
- let output = subst sg.output in
- let doutputs = List.map subst sg.doutputs in
- let info = sg.info in
- { inputs; output; doutputs; info }
-
-(** Return true if a list of functions are *not* mutually recursive, false otherwise.
- This function is meant to be applied on a set of (forward, backwards) functions
- generated for one recursive function.
- The way we do the test is very simple:
- - we explore the functions one by one, in the order
- - if all functions only call functions we already explored, they are not
- mutually recursive
- *)
-let functions_not_mutually_recursive (funs : fun_decl list) : bool =
- (* Compute the set of function identifiers in the group *)
- let ids =
- FunIdSet.of_list
- (List.map
- (fun (f : fun_decl) -> Regular (A.Regular f.def_id, f.back_id))
- funs)
- in
- let ids = ref ids in
- (* Explore every body *)
- let body_only_calls_itself (fdef : fun_decl) : bool =
- (* Remove the current id from the id set *)
- ids := FunIdSet.remove (Regular (A.Regular fdef.def_id, fdef.back_id)) !ids;
-
- (* Check if we call functions from the updated id set *)
- let obj =
- object
- inherit [_] iter_expression as super
-
- method! visit_qualif env qualif =
- match qualif.id with
- | Func fun_id ->
- if FunIdSet.mem fun_id !ids then raise Utils.Found
- else super#visit_qualif env qualif
- | _ -> super#visit_qualif env qualif
- end
- in
-
- try
- match fdef.body with
- | None -> true
- | Some body ->
- obj#visit_texpression () body.body;
- true
- with Utils.Found -> false
- in
- List.for_all body_only_calls_itself funs
-
-(** We use this to check whether we need to add parentheses around expressions.
- We only look for outer monadic let-bindings.
- This is used when printing the branches of [if ... then ... else ...].
- *)
-let rec let_group_requires_parentheses (e : texpression) : bool =
- match e.e with
- | Var _ | Const _ | App _ | Abs _ | Qualif _ -> false
- | Let (monadic, _, _, next_e) ->
- if monadic then true else let_group_requires_parentheses next_e
- | Switch (_, _) -> false
- | Meta (_, next_e) -> let_group_requires_parentheses next_e
-
-let is_var (e : texpression) : bool =
- match e.e with Var _ -> true | _ -> false
-
-let as_var (e : texpression) : VarId.id =
- match e.e with Var v -> v | _ -> raise (Failure "Unreachable")
-
-let is_global (e : texpression) : bool =
- match e.e with Qualif { id = Global _; _ } -> true | _ -> false
-
-let is_const (e : texpression) : bool =
- match e.e with Const _ -> true | _ -> false
-
-(** Remove the external occurrences of {!Meta} *)
-let rec unmeta (e : texpression) : texpression =
- match e.e with Meta (_, e) -> unmeta e | _ -> e
-
-(** Remove *all* the meta information *)
-let remove_meta (e : texpression) : texpression =
- let obj =
- object
- inherit [_] map_expression as super
- method! visit_Meta env _ e = super#visit_expression env e.e
- end
- in
- obj#visit_texpression () e
-
-let mk_arrow (ty0 : ty) (ty1 : ty) : ty = Arrow (ty0, ty1)
-
-(** Construct a type as a list of arrows: ty1 -> ... tyn *)
-let mk_arrows (inputs : ty list) (output : ty) =
- let rec aux (tys : ty list) : ty =
- match tys with [] -> output | ty :: tys' -> Arrow (ty, aux tys')
- in
- aux inputs
-
-(** Destruct an [App] expression into an expression and a list of arguments.
-
- We simply destruct the expression as long as it is of the form [App (f, x)].
- *)
-let destruct_apps (e : texpression) : texpression * texpression list =
- let rec aux (args : texpression list) (e : texpression) :
- texpression * texpression list =
- match e.e with App (f, x) -> aux (x :: args) f | _ -> (e, args)
- in
- aux [] e
-
-(** Make an [App (app, arg)] expression *)
-let mk_app (app : texpression) (arg : texpression) : texpression =
- match app.ty with
- | Arrow (ty0, ty1) ->
- (* Sanity check *)
- assert (ty0 = arg.ty);
- let e = App (app, arg) in
- let ty = ty1 in
- { e; ty }
- | _ -> raise (Failure "Expected an arrow type")
-
-(** The reverse of {!destruct_apps} *)
-let mk_apps (app : texpression) (args : texpression list) : texpression =
- List.fold_left (fun app arg -> mk_app app arg) app args
-
-(** Destruct an expression into a qualif identifier and a list of arguments,
- * if possible *)
-let opt_destruct_qualif_app (e : texpression) :
- (qualif * texpression list) option =
- let app, args = destruct_apps e in
- match app.e with Qualif qualif -> Some (qualif, args) | _ -> None
-
-(** Destruct an expression into a qualif identifier and a list of arguments *)
-let destruct_qualif_app (e : texpression) : qualif * texpression list =
- Option.get (opt_destruct_qualif_app e)
-
-(** Destruct an expression into a function call, if possible *)
-let opt_destruct_function_call (e : texpression) :
- (fun_id * ty list * texpression list) option =
- match opt_destruct_qualif_app e with
- | None -> None
- | Some (qualif, args) -> (
- match qualif.id with
- | Func fun_id -> Some (fun_id, qualif.type_args, args)
- | _ -> None)
-
-let opt_destruct_result (ty : ty) : ty option =
- match ty with
- | Adt (Assumed Result, tys) -> Some (Collections.List.to_cons_nil tys)
- | _ -> None
-
-let destruct_result (ty : ty) : ty = Option.get (opt_destruct_result ty)
-
-let opt_destruct_tuple (ty : ty) : ty list option =
- match ty with Adt (Tuple, tys) -> Some tys | _ -> None
-
-let mk_abs (x : typed_pattern) (e : texpression) : texpression =
- let ty = Arrow (x.ty, e.ty) in
- let e = Abs (x, e) in
- { e; ty }
-
-let rec destruct_abs_list (e : texpression) : typed_pattern list * texpression =
- match e.e with
- | Abs (x, e') ->
- let xl, e'' = destruct_abs_list e' in
- (x :: xl, e'')
- | _ -> ([], e)
-
-let destruct_arrow (ty : ty) : ty * ty =
- match ty with
- | Arrow (ty0, ty1) -> (ty0, ty1)
- | _ -> raise (Failure "Not an arrow type")
-
-let rec destruct_arrows (ty : ty) : ty list * ty =
- match ty with
- | Arrow (ty0, ty1) ->
- let tys, out_ty = destruct_arrows ty1 in
- (ty0 :: tys, out_ty)
- | _ -> ([], ty)
-
-let get_switch_body_ty (sb : switch_body) : ty =
- match sb with
- | If (e_then, _) -> e_then.ty
- | Match branches ->
- (* There should be at least one branch *)
- (List.hd branches).branch.ty
-
-let map_switch_body_branches (f : texpression -> texpression) (sb : switch_body)
- : switch_body =
- match sb with
- | If (e_then, e_else) -> If (f e_then, f e_else)
- | Match branches ->
- Match
- (List.map
- (fun (b : match_branch) -> { b with branch = f b.branch })
- branches)
-
-let iter_switch_body_branches (f : texpression -> unit) (sb : switch_body) :
- unit =
- match sb with
- | If (e_then, e_else) ->
- f e_then;
- f e_else
- | Match branches -> List.iter (fun (b : match_branch) -> f b.branch) branches
-
-let mk_switch (scrut : texpression) (sb : switch_body) : texpression =
- (* Sanity check: the scrutinee has the proper type *)
- (match sb with
- | If (_, _) -> assert (scrut.ty = Bool)
- | Match branches ->
- List.iter
- (fun (b : match_branch) -> assert (b.pat.ty = scrut.ty))
- branches);
- (* Sanity check: all the branches have the same type *)
- let ty = get_switch_body_ty sb in
- iter_switch_body_branches (fun e -> assert (e.ty = ty)) sb;
- (* Put together *)
- let e = Switch (scrut, sb) in
- { e; ty }
-
-(** Make a "simplified" tuple type from a list of types:
- - if there is exactly one type, just return it
- - if there is > one type: wrap them in a tuple
- *)
-let mk_simpl_tuple_ty (tys : ty list) : ty =
- match tys with [ ty ] -> ty | _ -> Adt (Tuple, tys)
-
-let mk_unit_ty : ty = Adt (Tuple, [])
-
-let mk_unit_rvalue : texpression =
- let id = AdtCons { adt_id = Tuple; variant_id = None } in
- let qualif = { id; type_args = [] } in
- let e = Qualif qualif in
- let ty = mk_unit_ty in
- { e; ty }
-
-let mk_texpression_from_var (v : var) : texpression =
- let e = Var v.id in
- let ty = v.ty in
- { e; ty }
-
-let mk_typed_pattern_from_var (v : var) (mp : mplace option) : typed_pattern =
- let value = PatVar (v, mp) in
- let ty = v.ty in
- { value; ty }
-
-let mk_meta (m : meta) (e : texpression) : texpression =
- let ty = e.ty in
- let e = Meta (m, e) in
- { e; ty }
-
-let mk_mplace_texpression (mp : mplace) (e : texpression) : texpression =
- mk_meta (MPlace mp) e
-
-let mk_opt_mplace_texpression (mp : mplace option) (e : texpression) :
- texpression =
- match mp with None -> e | Some mp -> mk_mplace_texpression mp e
-
-(** Make a "simplified" tuple value from a list of values:
- - if there is exactly one value, just return it
- - if there is > one value: wrap them in a tuple
- *)
-let mk_simpl_tuple_pattern (vl : typed_pattern list) : typed_pattern =
- match vl with
- | [ v ] -> v
- | _ ->
- let tys = List.map (fun (v : typed_pattern) -> v.ty) vl in
- let ty = Adt (Tuple, tys) in
- let value = PatAdt { variant_id = None; field_values = vl } in
- { value; ty }
-
-(** Similar to {!mk_simpl_tuple_pattern} *)
-let mk_simpl_tuple_texpression (vl : texpression list) : texpression =
- match vl with
- | [ v ] -> v
- | _ ->
- (* Compute the types of the fields, and the type of the tuple constructor *)
- let tys = List.map (fun (v : texpression) -> v.ty) vl in
- let ty = Adt (Tuple, tys) in
- let ty = mk_arrows tys ty in
- (* Construct the tuple constructor qualifier *)
- let id = AdtCons { adt_id = Tuple; variant_id = None } in
- let qualif = { id; type_args = tys } in
- (* Put everything together *)
- let cons = { e = Qualif qualif; ty } in
- mk_apps cons vl
-
-let mk_adt_pattern (adt_ty : ty) (variant_id : VariantId.id)
- (vl : typed_pattern list) : typed_pattern =
- let value = PatAdt { variant_id = Some variant_id; field_values = vl } in
- { value; ty = adt_ty }
-
-let ty_as_integer (t : ty) : T.integer_type =
- match t with Integer int_ty -> int_ty | _ -> raise (Failure "Unreachable")
-
-(* TODO: move *)
-let type_decl_is_enum (def : T.type_decl) : bool =
- match def.kind with T.Struct _ -> false | Enum _ -> true | Opaque -> false
-
-let mk_state_ty : ty = Adt (Assumed State, [])
-let mk_result_ty (ty : ty) : ty = Adt (Assumed Result, [ ty ])
-
-let unwrap_result_ty (ty : ty) : ty =
- match ty with
- | Adt (Assumed Result, [ ty ]) -> ty
- | _ -> failwith "not a result type"
-
-let mk_result_fail_texpression (ty : ty) : texpression =
- let type_args = [ ty ] in
- let ty = Adt (Assumed Result, type_args) in
- let id =
- AdtCons { adt_id = Assumed Result; variant_id = Some result_fail_id }
- in
- let qualif = { id; type_args } in
- let cons_e = Qualif qualif in
- let cons_ty = ty in
- let cons = { e = cons_e; ty = cons_ty } in
- cons
-
-let mk_result_return_texpression (v : texpression) : texpression =
- let type_args = [ v.ty ] in
- let ty = Adt (Assumed Result, type_args) in
- let id =
- AdtCons { adt_id = Assumed Result; variant_id = Some result_return_id }
- in
- let qualif = { id; type_args } in
- let cons_e = Qualif qualif in
- let cons_ty = mk_arrow v.ty ty in
- let cons = { e = cons_e; ty = cons_ty } in
- mk_app cons v
-
-let mk_result_fail_pattern (ty : ty) : typed_pattern =
- let ty = Adt (Assumed Result, [ ty ]) in
- let value = PatAdt { variant_id = Some result_fail_id; field_values = [] } in
- { value; ty }
-
-let mk_result_return_pattern (v : typed_pattern) : typed_pattern =
- let ty = Adt (Assumed Result, [ v.ty ]) in
- let value =
- PatAdt { variant_id = Some result_return_id; field_values = [ v ] }
- in
- { value; ty }
-
-let opt_unmeta_mplace (e : texpression) : mplace option * texpression =
- match e.e with Meta (MPlace mp, e) -> (Some mp, e) | _ -> (None, e)
diff --git a/src/Scalars.ml b/src/Scalars.ml
deleted file mode 100644
index 03ca506c..00000000
--- a/src/Scalars.ml
+++ /dev/null
@@ -1,59 +0,0 @@
-open Types
-open Values
-
-(** The minimum/maximum values an integer type can have depending on its type *)
-
-let i8_min = Z.of_string "-128"
-let i8_max = Z.of_string "127"
-let i16_min = Z.of_string "-32768"
-let i16_max = Z.of_string "32767"
-let i32_min = Z.of_string "-2147483648"
-let i32_max = Z.of_string "2147483647"
-let i64_min = Z.of_string "-9223372036854775808"
-let i64_max = Z.of_string "9223372036854775807"
-let i128_min = Z.of_string "-170141183460469231731687303715884105728"
-let i128_max = Z.of_string "170141183460469231731687303715884105727"
-let u8_min = Z.of_string "0"
-let u8_max = Z.of_string "255"
-let u16_min = Z.of_string "0"
-let u16_max = Z.of_string "65535"
-let u32_min = Z.of_string "0"
-let u32_max = Z.of_string "4294967295"
-let u64_min = Z.of_string "0"
-let u64_max = Z.of_string "18446744073709551615"
-let u128_min = Z.of_string "0"
-let u128_max = Z.of_string "340282366920938463463374607431768211455"
-
-(** Being a bit conservative about isize/usize: depending on the system,
- the values are encoded as 32-bit values or 64-bit values - we may
- want to take that into account in the future *)
-
-let isize_min = i32_min
-let isize_max = i32_max
-let usize_min = u32_min
-let usize_max = u32_max
-
-(** Check that an integer value is in range *)
-let check_int_in_range (int_ty : integer_type) (i : big_int) : bool =
- match int_ty with
- | Isize -> Z.leq isize_min i && Z.leq i isize_max
- | I8 -> Z.leq i8_min i && Z.leq i i8_max
- | I16 -> Z.leq i16_min i && Z.leq i i16_max
- | I32 -> Z.leq i32_min i && Z.leq i i32_max
- | I64 -> Z.leq i64_min i && Z.leq i i64_max
- | I128 -> Z.leq i128_min i && Z.leq i i128_max
- | Usize -> Z.leq usize_min i && Z.leq i usize_max
- | U8 -> Z.leq u8_min i && Z.leq i u8_max
- | U16 -> Z.leq u16_min i && Z.leq i u16_max
- | U32 -> Z.leq u32_min i && Z.leq i u32_max
- | U64 -> Z.leq u64_min i && Z.leq i u64_max
- | U128 -> Z.leq u128_min i && Z.leq i u128_max
-
-(** Check that a scalar value is correct (the integer value it contains is in range) *)
-let check_scalar_value_in_range (v : scalar_value) : bool =
- check_int_in_range v.int_ty v.value
-
-(** Make a scalar value, while checking the value is in range *)
-let mk_scalar (int_ty : integer_type) (i : big_int) :
- (scalar_value, unit) result =
- if check_int_in_range int_ty i then Ok { value = i; int_ty } else Error ()
diff --git a/src/StringUtils.ml b/src/StringUtils.ml
deleted file mode 100644
index 0fd46136..00000000
--- a/src/StringUtils.ml
+++ /dev/null
@@ -1,106 +0,0 @@
-(** Utilities to work on strings, character per character.
-
- They operate on ASCII strings, and are used by the project to convert
- Rust names: Rust names are not fancy, so it shouldn't be a problem.
-
- Rk.: the poor support of OCaml for char manipulation is really annoying...
- *)
-
-let code_0 = 48
-let code_9 = 57
-let code_A = 65
-let code_Z = 90
-let code_a = 97
-let code_z = 122
-
-let is_lowercase_ascii (c : char) : bool =
- let c = Char.code c in
- code_a <= c && c <= code_z
-
-let is_uppercase_ascii (c : char) : bool =
- let c = Char.code c in
- code_A <= c && c <= code_Z
-
-let is_letter_ascii (c : char) : bool =
- is_lowercase_ascii c || is_uppercase_ascii c
-
-let is_digit_ascii (c : char) : bool =
- let c = Char.code c in
- code_0 <= c && c <= code_9
-
-let lowercase_ascii = Char.lowercase_ascii
-let uppercase_ascii = Char.uppercase_ascii
-
-(** Using buffers as per:
- {{: https://stackoverflow.com/questions/29957418/how-to-convert-char-list-to-string-in-ocaml} stackoverflow}
- *)
-let string_of_chars (chars : char list) : string =
- let buf = Buffer.create (List.length chars) in
- List.iter (Buffer.add_char buf) chars;
- Buffer.contents buf
-
-let string_to_chars (s : string) : char list =
- let length = String.length s in
- let rec apply i =
- if i = length then [] else String.get s i :: apply (i + 1)
- in
- apply 0
-
-(** This operates on ASCII *)
-let to_camel_case (s : string) : string =
- (* Note that we rebuild the string in reverse order *)
- let apply ((prev_is_under, acc) : bool * char list) (c : char) :
- bool * char list =
- if c = '_' then (true, acc)
- else
- let c = if prev_is_under then uppercase_ascii c else c in
- (false, c :: acc)
- in
- let _, chars = List.fold_left apply (true, []) (string_to_chars s) in
- string_of_chars (List.rev chars)
-
-(** This operates on ASCII *)
-let to_snake_case (s : string) : string =
- (* Note that we rebuild the string in reverse order *)
- let apply ((prev_is_low, prev_is_digit, acc) : bool * bool * char list)
- (c : char) : bool * bool * char list =
- let acc =
- if c = '_' then acc
- else if prev_is_digit then if is_letter_ascii c then '_' :: acc else acc
- else if prev_is_low then
- if (is_lowercase_ascii c || is_digit_ascii c) && c <> '_' then acc
- else '_' :: acc
- else acc
- in
- let prev_is_low = is_lowercase_ascii c in
- let prev_is_digit = is_digit_ascii c in
- let c = lowercase_ascii c in
- (prev_is_low, prev_is_digit, c :: acc)
- in
- let _, _, chars =
- List.fold_left apply (false, false, []) (string_to_chars s)
- in
- string_of_chars (List.rev chars)
-
-(** Applies a map operation.
-
- This is very inefficient, but shouldn't be used much.
- *)
-let map (f : char -> string) (s : string) : string =
- let sl = List.map f (string_to_chars s) in
- let sl = List.map string_to_chars sl in
- string_of_chars (List.concat sl)
-
-let capitalize_first_letter (s : string) : string =
- let s = string_to_chars s in
- let s = match s with [] -> s | c :: s' -> uppercase_ascii c :: s' in
- string_of_chars s
-
-(** Unit tests *)
-let _ =
- assert (to_camel_case "hello_world" = "HelloWorld");
- assert (to_snake_case "HelloWorld36Hello" = "hello_world36_hello");
- assert (to_snake_case "HELLO" = "hello");
- assert (to_snake_case "T1" = "t1");
- assert (to_camel_case "list" = "List");
- assert (to_snake_case "is_cons" = "is_cons")
diff --git a/src/Substitute.ml b/src/Substitute.ml
deleted file mode 100644
index 5e5858de..00000000
--- a/src/Substitute.ml
+++ /dev/null
@@ -1,357 +0,0 @@
-(** This file implements various substitution utilities to instantiate types,
- function bodies, etc.
- *)
-
-module T = Types
-module TU = TypesUtils
-module V = Values
-module E = Expressions
-module A = LlbcAst
-module C = Contexts
-
-(** Substitute types variables and regions in a type.
-
- TODO: we can reimplement that with visitors.
- *)
-let rec ty_substitute (rsubst : 'r1 -> 'r2)
- (tsubst : T.TypeVarId.id -> 'r2 T.ty) (ty : 'r1 T.ty) : 'r2 T.ty =
- let open T in
- let subst = ty_substitute rsubst tsubst in
- (* helper *)
- match ty with
- | Adt (def_id, regions, tys) ->
- Adt (def_id, List.map rsubst regions, List.map subst tys)
- | Array aty -> Array (subst aty)
- | Slice sty -> Slice (subst sty)
- | Ref (r, ref_ty, ref_kind) -> Ref (rsubst r, subst ref_ty, ref_kind)
- (* Below variants: we technically return the same value, but because
- one has type ['r1 ty] and the other has type ['r2 ty], we need to
- deconstruct then reconstruct *)
- | Bool -> Bool
- | Char -> Char
- | Never -> Never
- | Integer int_ty -> Integer int_ty
- | Str -> Str
- | TypeVar vid -> tsubst vid
-
-(** Convert an {!T.rty} to an {!T.ety} by erasing the region variables *)
-let erase_regions (ty : T.rty) : T.ety =
- ty_substitute (fun _ -> T.Erased) (fun vid -> T.TypeVar vid) ty
-
-(** Generate fresh regions for region variables.
-
- Return the list of new regions and appropriate substitutions from the
- original region variables to the fresh regions.
-
- TODO: simplify? we only need the subst [T.RegionVarId.id -> T.RegionId.id]
- *)
-let fresh_regions_with_substs (region_vars : T.region_var list) :
- T.RegionId.id list
- * (T.RegionVarId.id -> T.RegionId.id)
- * (T.RegionVarId.id T.region -> T.RegionId.id T.region) =
- (* Generate fresh regions *)
- let fresh_region_ids = List.map (fun _ -> C.fresh_region_id ()) region_vars in
- (* Generate the map from region var ids to regions *)
- let ls = List.combine region_vars fresh_region_ids in
- let rid_map =
- List.fold_left
- (fun mp (k, v) -> T.RegionVarId.Map.add k.T.index v mp)
- T.RegionVarId.Map.empty ls
- in
- (* Generate the substitution from region var id to region *)
- let rid_subst id = T.RegionVarId.Map.find id rid_map in
- (* Generate the substitution from region to region *)
- let rsubst r =
- match r with T.Static -> T.Static | T.Var id -> T.Var (rid_subst id)
- in
- (* Return *)
- (fresh_region_ids, rid_subst, rsubst)
-
-(** Erase the regions in a type and substitute the type variables *)
-let erase_regions_substitute_types (tsubst : T.TypeVarId.id -> T.ety)
- (ty : 'r T.region T.ty) : T.ety =
- let rsubst (_ : 'r T.region) : T.erased_region = T.Erased in
- ty_substitute rsubst tsubst ty
-
-(** Create a region substitution from a list of region variable ids and a list of
- regions (with which to substitute the region variable ids *)
-let make_region_subst (var_ids : T.RegionVarId.id list)
- (regions : 'r T.region list) : T.RegionVarId.id T.region -> 'r T.region =
- let ls = List.combine var_ids regions in
- let mp =
- List.fold_left
- (fun mp (k, v) -> T.RegionVarId.Map.add k v mp)
- T.RegionVarId.Map.empty ls
- in
- fun r ->
- match r with
- | T.Static -> T.Static
- | T.Var id -> T.RegionVarId.Map.find id mp
-
-(** Create a type substitution from a list of type variable ids and a list of
- types (with which to substitute the type variable ids) *)
-let make_type_subst (var_ids : T.TypeVarId.id list) (tys : 'r T.ty list) :
- T.TypeVarId.id -> 'r T.ty =
- let ls = List.combine var_ids tys in
- let mp =
- List.fold_left
- (fun mp (k, v) -> T.TypeVarId.Map.add k v mp)
- T.TypeVarId.Map.empty ls
- in
- fun id -> T.TypeVarId.Map.find id mp
-
-(** Instantiate the type variables in an ADT definition, and return, for
- every variant, the list of the types of its fields *)
-let type_decl_get_instantiated_variants_fields_rtypes (def : T.type_decl)
- (regions : T.RegionId.id T.region list) (types : T.rty list) :
- (T.VariantId.id option * T.rty list) list =
- let r_subst =
- make_region_subst
- (List.map (fun x -> x.T.index) def.T.region_params)
- regions
- in
- let ty_subst =
- make_type_subst (List.map (fun x -> x.T.index) def.T.type_params) types
- in
- let (variants_fields : (T.VariantId.id option * T.field list) list) =
- match def.T.kind with
- | T.Enum variants ->
- List.mapi
- (fun i v -> (Some (T.VariantId.of_int i), v.T.fields))
- variants
- | T.Struct fields -> [ (None, fields) ]
- | T.Opaque ->
- raise
- (Failure
- ("Can't retrieve the variants of an opaque type: "
- ^ Names.name_to_string def.name))
- in
- List.map
- (fun (id, fields) ->
- ( id,
- List.map (fun f -> ty_substitute r_subst ty_subst f.T.field_ty) fields
- ))
- variants_fields
-
-(** Instantiate the type variables in an ADT definition, and return the list
- of types of the fields for the chosen variant *)
-let type_decl_get_instantiated_field_rtypes (def : T.type_decl)
- (opt_variant_id : T.VariantId.id option)
- (regions : T.RegionId.id T.region list) (types : T.rty list) : T.rty list =
- let r_subst =
- make_region_subst
- (List.map (fun x -> x.T.index) def.T.region_params)
- regions
- in
- let ty_subst =
- make_type_subst (List.map (fun x -> x.T.index) def.T.type_params) types
- in
- let fields = TU.type_decl_get_fields def opt_variant_id in
- List.map (fun f -> ty_substitute r_subst ty_subst f.T.field_ty) fields
-
-(** Return the types of the properly instantiated ADT's variant, provided a
- context *)
-let ctx_adt_get_instantiated_field_rtypes (ctx : C.eval_ctx)
- (def_id : T.TypeDeclId.id) (opt_variant_id : T.VariantId.id option)
- (regions : T.RegionId.id T.region list) (types : T.rty list) : T.rty list =
- let def = C.ctx_lookup_type_decl ctx def_id in
- type_decl_get_instantiated_field_rtypes def opt_variant_id regions types
-
-(** Return the types of the properly instantiated ADT value (note that
- here, ADT is understood in its broad meaning: ADT, assumed value or tuple) *)
-let ctx_adt_value_get_instantiated_field_rtypes (ctx : C.eval_ctx)
- (adt : V.adt_value) (id : T.type_id)
- (region_params : T.RegionId.id T.region list) (type_params : T.rty list) :
- T.rty list =
- match id with
- | T.AdtId id ->
- (* Retrieve the types of the fields *)
- ctx_adt_get_instantiated_field_rtypes ctx id adt.V.variant_id
- region_params type_params
- | T.Tuple ->
- assert (List.length region_params = 0);
- type_params
- | T.Assumed aty -> (
- match aty with
- | T.Box | T.Vec ->
- assert (List.length region_params = 0);
- assert (List.length type_params = 1);
- type_params
- | T.Option ->
- assert (List.length region_params = 0);
- assert (List.length type_params = 1);
- if adt.V.variant_id = Some T.option_some_id then type_params
- else if adt.V.variant_id = Some T.option_none_id then []
- else failwith "Unrechable")
-
-(** Instantiate the type variables in an ADT definition, and return the list
- of types of the fields for the chosen variant *)
-let type_decl_get_instantiated_field_etypes (def : T.type_decl)
- (opt_variant_id : T.VariantId.id option) (types : T.ety list) : T.ety list =
- let ty_subst =
- make_type_subst (List.map (fun x -> x.T.index) def.T.type_params) types
- in
- let fields = TU.type_decl_get_fields def opt_variant_id in
- List.map
- (fun f -> erase_regions_substitute_types ty_subst f.T.field_ty)
- fields
-
-(** Return the types of the properly instantiated ADT's variant, provided a
- context *)
-let ctx_adt_get_instantiated_field_etypes (ctx : C.eval_ctx)
- (def_id : T.TypeDeclId.id) (opt_variant_id : T.VariantId.id option)
- (types : T.ety list) : T.ety list =
- let def = C.ctx_lookup_type_decl ctx def_id in
- type_decl_get_instantiated_field_etypes def opt_variant_id types
-
-(** Apply a type substitution to a place *)
-let place_substitute (_tsubst : T.TypeVarId.id -> T.ety) (p : E.place) : E.place
- =
- (* There is nothing to do *)
- p
-
-(** Apply a type substitution to an operand *)
-let operand_substitute (tsubst : T.TypeVarId.id -> T.ety) (op : E.operand) :
- E.operand =
- let p_subst = place_substitute tsubst in
- match op with
- | E.Copy p -> E.Copy (p_subst p)
- | E.Move p -> E.Move (p_subst p)
- | E.Constant (ety, cv) ->
- let rsubst x = x in
- E.Constant (ty_substitute rsubst tsubst ety, cv)
-
-(** Apply a type substitution to an rvalue *)
-let rvalue_substitute (tsubst : T.TypeVarId.id -> T.ety) (rv : E.rvalue) :
- E.rvalue =
- let op_subst = operand_substitute tsubst in
- let p_subst = place_substitute tsubst in
- match rv with
- | E.Use op -> E.Use (op_subst op)
- | E.Ref (p, bkind) -> E.Ref (p_subst p, bkind)
- | E.UnaryOp (unop, op) -> E.UnaryOp (unop, op_subst op)
- | E.BinaryOp (binop, op1, op2) ->
- E.BinaryOp (binop, op_subst op1, op_subst op2)
- | E.Discriminant p -> E.Discriminant (p_subst p)
- | E.Aggregate (kind, ops) ->
- let ops = List.map op_subst ops in
- let kind =
- match kind with
- | E.AggregatedTuple -> E.AggregatedTuple
- | E.AggregatedOption (variant_id, ty) ->
- let rsubst r = r in
- E.AggregatedOption (variant_id, ty_substitute rsubst tsubst ty)
- | E.AggregatedAdt (def_id, variant_id, regions, tys) ->
- let rsubst r = r in
- E.AggregatedAdt
- ( def_id,
- variant_id,
- regions,
- List.map (ty_substitute rsubst tsubst) tys )
- in
- E.Aggregate (kind, ops)
-
-(** Apply a type substitution to an assertion *)
-let assertion_substitute (tsubst : T.TypeVarId.id -> T.ety) (a : A.assertion) :
- A.assertion =
- { A.cond = operand_substitute tsubst a.A.cond; A.expected = a.A.expected }
-
-(** Apply a type substitution to a call *)
-let call_substitute (tsubst : T.TypeVarId.id -> T.ety) (call : A.call) : A.call
- =
- let rsubst x = x in
- let type_args = List.map (ty_substitute rsubst tsubst) call.A.type_args in
- let args = List.map (operand_substitute tsubst) call.A.args in
- let dest = place_substitute tsubst call.A.dest in
- (* Putting all the paramters on purpose: we want to get a compiler error if
- something moves - we may add a field on which we need to apply a substitution *)
- {
- func = call.A.func;
- region_args = call.A.region_args;
- A.type_args;
- args;
- dest;
- }
-
-(** Apply a type substitution to a statement *)
-let rec statement_substitute (tsubst : T.TypeVarId.id -> T.ety)
- (st : A.statement) : A.statement =
- { st with A.content = raw_statement_substitute tsubst st.content }
-
-and raw_statement_substitute (tsubst : T.TypeVarId.id -> T.ety)
- (st : A.raw_statement) : A.raw_statement =
- match st with
- | A.Assign (p, rvalue) ->
- let p = place_substitute tsubst p in
- let rvalue = rvalue_substitute tsubst rvalue in
- A.Assign (p, rvalue)
- | A.AssignGlobal g ->
- (* Globals don't have type parameters *)
- A.AssignGlobal g
- | A.FakeRead p ->
- let p = place_substitute tsubst p in
- A.FakeRead p
- | A.SetDiscriminant (p, vid) ->
- let p = place_substitute tsubst p in
- A.SetDiscriminant (p, vid)
- | A.Drop p ->
- let p = place_substitute tsubst p in
- A.Drop p
- | A.Assert assertion ->
- let assertion = assertion_substitute tsubst assertion in
- A.Assert assertion
- | A.Call call ->
- let call = call_substitute tsubst call in
- A.Call call
- | A.Panic | A.Return | A.Break _ | A.Continue _ | A.Nop -> st
- | A.Sequence (st1, st2) ->
- A.Sequence
- (statement_substitute tsubst st1, statement_substitute tsubst st2)
- | A.Switch (op, tgts) ->
- A.Switch
- (operand_substitute tsubst op, switch_targets_substitute tsubst tgts)
- | A.Loop le -> A.Loop (statement_substitute tsubst le)
-
-(** Apply a type substitution to switch targets *)
-and switch_targets_substitute (tsubst : T.TypeVarId.id -> T.ety)
- (tgts : A.switch_targets) : A.switch_targets =
- match tgts with
- | A.If (st1, st2) ->
- A.If (statement_substitute tsubst st1, statement_substitute tsubst st2)
- | A.SwitchInt (int_ty, tgts, otherwise) ->
- let tgts =
- List.map (fun (sv, st) -> (sv, statement_substitute tsubst st)) tgts
- in
- let otherwise = statement_substitute tsubst otherwise in
- A.SwitchInt (int_ty, tgts, otherwise)
-
-(** Apply a type substitution to a function body. Return the local variables
- and the body. *)
-let fun_body_substitute_in_body (tsubst : T.TypeVarId.id -> T.ety)
- (body : A.fun_body) : A.var list * A.statement =
- let rsubst r = r in
- let locals =
- List.map
- (fun v -> { v with A.var_ty = ty_substitute rsubst tsubst v.A.var_ty })
- body.A.locals
- in
- let body = statement_substitute tsubst body.body in
- (locals, body)
-
-(** Substitute a function signature *)
-let substitute_signature (asubst : T.RegionGroupId.id -> V.AbstractionId.id)
- (rsubst : T.RegionVarId.id -> T.RegionId.id)
- (tsubst : T.TypeVarId.id -> T.rty) (sg : A.fun_sig) : A.inst_fun_sig =
- let rsubst' (r : T.RegionVarId.id T.region) : T.RegionId.id T.region =
- match r with T.Static -> T.Static | T.Var rid -> T.Var (rsubst rid)
- in
- let inputs = List.map (ty_substitute rsubst' tsubst) sg.A.inputs in
- let output = ty_substitute rsubst' tsubst sg.A.output in
- let subst_region_group (rg : T.region_var_group) : A.abs_region_group =
- let id = asubst rg.id in
- let regions = List.map rsubst rg.regions in
- let parents = List.map asubst rg.parents in
- { id; regions; parents }
- in
- let regions_hierarchy = List.map subst_region_group sg.A.regions_hierarchy in
- { A.regions_hierarchy; inputs; output }
diff --git a/src/SymbolicAst.ml b/src/SymbolicAst.ml
deleted file mode 100644
index 604a7948..00000000
--- a/src/SymbolicAst.ml
+++ /dev/null
@@ -1,98 +0,0 @@
-(** The "symbolic" AST is the AST directly generated by the symbolic execution.
- It is very rough and meant to be extremely straightforward to build during
- the symbolic execution: we later apply transformations to generate the
- pure AST that we export. *)
-
-module T = Types
-module V = Values
-module E = Expressions
-module A = LlbcAst
-
-(** "Meta"-place: a place stored as meta-data.
-
- Whenever we need to introduce new symbolic variables, for instance because
- of symbolic expansions, we try to store a "place", which gives information
- about the origin of the values (this place information comes from assignment
- information, etc.).
- We later use this place information to generate meaningful name, to prettify
- the generated code.
- *)
-type mplace = {
- bv : Contexts.binder;
- (** It is important that we store the binder, and not just the variable id,
- because the most important information in a place is the name of the
- variable!
- *)
- projection : E.projection;
- (** We store the projection because we can, but it is actually not that useful *)
-}
-
-type call_id =
- | Fun of A.fun_id * V.FunCallId.id
- (** A "regular" function (i.e., a function which is not a primitive operation) *)
- | Unop of E.unop
- | Binop of E.binop
-[@@deriving show, ord]
-
-type call = {
- call_id : call_id;
- abstractions : V.AbstractionId.id list;
- type_params : T.ety list;
- args : V.typed_value list;
- args_places : mplace option list; (** Meta information *)
- dest : V.symbolic_value;
- dest_place : mplace option; (** Meta information *)
-}
-
-(** Meta information, not necessary for synthesis but useful to guide it to
- generate a pretty output.
- *)
-
-type meta =
- | Assignment of mplace * V.typed_value * mplace option
- (** We generated an assignment (destination, assigned value, src) *)
-
-(** **Rk.:** here, {!expression} is not at all equivalent to the expressions
- used in LLBC: they are a first step towards lambda-calculus expressions.
- *)
-type expression =
- | Return of V.typed_value option
- (** There are two cases:
- - the AST is for a forward function: the typed value should contain
- the value which was in the return variable
- - the AST is for a backward function: the typed value should be [None]
- *)
- | Panic
- | FunCall of call * expression
- | EndAbstraction of V.abs * expression
- | EvalGlobal of A.GlobalDeclId.id * V.symbolic_value * expression
- (** Evaluate a global to a fresh symbolic value *)
- | Expansion of mplace option * V.symbolic_value * expansion
- (** Expansion of a symbolic value.
-
- The place is "meta": it gives the path to the symbolic value (if available)
- which got expanded (this path is available when the symbolic expansion
- comes from a path evaluation, during an assignment for instance).
- We use it to compute meaningful names for the variables we introduce,
- to prettify the generated code.
- *)
- | Meta of meta * expression (** Meta information *)
-
-and expansion =
- | ExpandNoBranch of V.symbolic_expansion * expression
- (** A symbolic expansion which doesn't generate a branching.
- Includes:
- - concrete expansion
- - borrow expansion
- *Doesn't* include:
- - expansion of ADTs with one variant
- *)
- | ExpandAdt of
- (T.VariantId.id option * V.symbolic_value list * expression) list
- (** ADT expansion *)
- | ExpandBool of expression * expression
- (** A boolean expansion (i.e, an [if ... then ... else ...]) *)
- | ExpandInt of
- T.integer_type * (V.scalar_value * expression) list * expression
- (** An integer expansion (i.e, a switch over an integer). The last
- expression is for the "otherwise" branch. *)
diff --git a/src/SymbolicToPure.ml b/src/SymbolicToPure.ml
deleted file mode 100644
index de4fb4c1..00000000
--- a/src/SymbolicToPure.ml
+++ /dev/null
@@ -1,1824 +0,0 @@
-open Errors
-open LlbcAstUtils
-open Pure
-open PureUtils
-module Id = Identifiers
-module S = SymbolicAst
-module TA = TypesAnalysis
-module L = Logging
-module PP = PrintPure
-module FA = FunsAnalysis
-
-(** The local logger *)
-let log = L.symbolic_to_pure_log
-
-type config = {
- filter_useless_back_calls : bool;
- (** If [true], filter the useless calls to backward functions.
-
- The useless calls are calls to backward functions which have no outputs.
- This case happens if the original Rust function only takes *shared* borrows
- as inputs, and is thus pretty common.
-
- We are allowed to do this only because in this specific case,
- the backward function fails *exactly* when the forward function fails
- (they actually do exactly the same thing, the only difference being
- that the forward function can potentially return a value), and upon
- reaching the place where we should introduce a call to the backward
- function, we know we have introduced a call to the forward function.
-
- Also note that in general, backward functions "do more things" than
- forward functions, and have more opportunities to fail (even though
- in the generated code, backward functions should fail exactly when
- the forward functions fail).
-
- We might want to move this optimization to the micro-passes subsequent
- to the translation from symbolic to pure, but it is really super easy
- to do it when going from symbolic to pure.
- Note that we later filter the useless *forward* calls in the micro-passes,
- where it is more natural to do.
- *)
-}
-
-type type_context = {
- llbc_type_decls : T.type_decl TypeDeclId.Map.t;
- type_decls : type_decl TypeDeclId.Map.t;
- (** We use this for type-checking (for sanity checks) when translating
- values and functions.
- This map is empty when we translate the types, then contains all
- the translated types when we translate the functions.
- *)
- types_infos : TA.type_infos; (* TODO: rename to type_infos *)
-}
-
-type fun_sig_named_outputs = {
- sg : fun_sig; (** A function signature *)
- output_names : string option list;
- (** In case the signature is for a backward function, we may provides names
- for the outputs. The reason is that the outputs of backward functions
- come from (in case there are no nested borrows) borrows present in the
- inputs of the original rust function. In this situation, we can use the
- names of those inputs to name the outputs. Those names are very useful
- to generate beautiful codes (we may need to introduce temporary variables
- in the bodies of the backward functions to store the returned values, in
- which case we use those names).
- *)
-}
-
-type fun_context = {
- llbc_fun_decls : A.fun_decl A.FunDeclId.Map.t;
- fun_sigs : fun_sig_named_outputs RegularFunIdMap.t; (** *)
- fun_infos : FA.fun_info A.FunDeclId.Map.t;
-}
-
-type global_context = { llbc_global_decls : A.global_decl A.GlobalDeclId.Map.t }
-
-(** Whenever we translate a function call or an ended abstraction, we
- store the related information (this is useful when translating ended
- children abstractions).
- *)
-type call_info = {
- forward : S.call;
- forward_inputs : texpression list;
- (** Remember the list of inputs given to the forward function.
-
- Those inputs include the state input, if pertinent (in which case
- it is the last input).
- *)
- backwards : (V.abs * texpression list) T.RegionGroupId.Map.t;
- (** A map from region group id (i.e., backward function id) to
- pairs (abstraction, additional arguments received by the backward function)
-
- TODO: remove? it is also in the bs_ctx ("abstractions" field)
- *)
-}
-
-(** Body synthesis context *)
-type bs_ctx = {
- type_context : type_context;
- fun_context : fun_context;
- global_context : global_context;
- fun_decl : A.fun_decl;
- bid : T.RegionGroupId.id option; (** TODO: rename *)
- sg : fun_sig;
- (** The function signature - useful in particular to translate [Panic] *)
- sv_to_var : var V.SymbolicValueId.Map.t;
- (** Whenever we encounter a new symbolic value (introduced because of
- a symbolic expansion or upon ending an abstraction, for instance)
- we introduce a new variable (with a let-binding).
- *)
- var_counter : VarId.generator;
- state_var : VarId.id;
- (** The current state variable, in case we use a state *)
- forward_inputs : var list;
- (** The input parameters for the forward function *)
- backward_inputs : var list T.RegionGroupId.Map.t;
- (** The input parameters for the backward functions *)
- backward_outputs : var list T.RegionGroupId.Map.t;
- (** The variables that the backward functions will output *)
- calls : call_info V.FunCallId.Map.t;
- (** The function calls we encountered so far *)
- abstractions : (V.abs * texpression list) V.AbstractionId.Map.t;
- (** The ended abstractions we encountered so far, with their additional input arguments *)
-}
-
-let type_check_pattern (ctx : bs_ctx) (v : typed_pattern) : unit =
- let env = VarId.Map.empty in
- let ctx =
- {
- PureTypeCheck.type_decls = ctx.type_context.type_decls;
- global_decls = ctx.global_context.llbc_global_decls;
- env;
- }
- in
- let _ = PureTypeCheck.check_typed_pattern ctx v in
- ()
-
-let type_check_texpression (ctx : bs_ctx) (e : texpression) : unit =
- let env = VarId.Map.empty in
- let ctx =
- {
- PureTypeCheck.type_decls = ctx.type_context.type_decls;
- global_decls = ctx.global_context.llbc_global_decls;
- env;
- }
- in
- PureTypeCheck.check_texpression ctx e
-
-(* TODO: move *)
-let bs_ctx_to_ast_formatter (ctx : bs_ctx) : Print.LlbcAst.ast_formatter =
- Print.LlbcAst.fun_decl_to_ast_formatter ctx.type_context.llbc_type_decls
- ctx.fun_context.llbc_fun_decls ctx.global_context.llbc_global_decls
- ctx.fun_decl
-
-let bs_ctx_to_pp_ast_formatter (ctx : bs_ctx) : PrintPure.ast_formatter =
- let type_params = ctx.fun_decl.signature.type_params in
- let type_decls = ctx.type_context.llbc_type_decls in
- let fun_decls = ctx.fun_context.llbc_fun_decls in
- let global_decls = ctx.global_context.llbc_global_decls in
- PrintPure.mk_ast_formatter type_decls fun_decls global_decls type_params
-
-let ty_to_string (ctx : bs_ctx) (ty : ty) : string =
- let fmt = bs_ctx_to_pp_ast_formatter ctx in
- let fmt = PrintPure.ast_to_type_formatter fmt in
- PrintPure.ty_to_string fmt ty
-
-let type_decl_to_string (ctx : bs_ctx) (def : type_decl) : string =
- let type_params = def.type_params in
- let type_decls = ctx.type_context.llbc_type_decls in
- let fmt = PrintPure.mk_type_formatter type_decls type_params in
- PrintPure.type_decl_to_string fmt def
-
-let texpression_to_string (ctx : bs_ctx) (e : texpression) : string =
- let fmt = bs_ctx_to_pp_ast_formatter ctx in
- PrintPure.texpression_to_string fmt false "" " " e
-
-let fun_sig_to_string (ctx : bs_ctx) (sg : fun_sig) : string =
- let type_params = sg.type_params in
- let type_decls = ctx.type_context.llbc_type_decls in
- let fun_decls = ctx.fun_context.llbc_fun_decls in
- let global_decls = ctx.global_context.llbc_global_decls in
- let fmt =
- PrintPure.mk_ast_formatter type_decls fun_decls global_decls type_params
- in
- PrintPure.fun_sig_to_string fmt sg
-
-let fun_decl_to_string (ctx : bs_ctx) (def : Pure.fun_decl) : string =
- let type_params = def.signature.type_params in
- let type_decls = ctx.type_context.llbc_type_decls in
- let fun_decls = ctx.fun_context.llbc_fun_decls in
- let global_decls = ctx.global_context.llbc_global_decls in
- let fmt =
- PrintPure.mk_ast_formatter type_decls fun_decls global_decls type_params
- in
- PrintPure.fun_decl_to_string fmt def
-
-(* TODO: move *)
-let abs_to_string (ctx : bs_ctx) (abs : V.abs) : string =
- let fmt = bs_ctx_to_ast_formatter ctx in
- let fmt = Print.LlbcAst.ast_to_value_formatter fmt in
- let indent = "" in
- let indent_incr = " " in
- Print.Values.abs_to_string fmt indent indent_incr abs
-
-let get_instantiated_fun_sig (fun_id : A.fun_id)
- (back_id : T.RegionGroupId.id option) (tys : ty list) (ctx : bs_ctx) :
- inst_fun_sig =
- (* Lookup the non-instantiated function signature *)
- let sg =
- (RegularFunIdMap.find (fun_id, back_id) ctx.fun_context.fun_sigs).sg
- in
- (* Create the substitution *)
- let tsubst = make_type_subst sg.type_params tys in
- (* Apply *)
- fun_sig_substitute tsubst sg
-
-let bs_ctx_lookup_llbc_type_decl (id : TypeDeclId.id) (ctx : bs_ctx) :
- T.type_decl =
- TypeDeclId.Map.find id ctx.type_context.llbc_type_decls
-
-let bs_ctx_lookup_llbc_fun_decl (id : A.FunDeclId.id) (ctx : bs_ctx) :
- A.fun_decl =
- A.FunDeclId.Map.find id ctx.fun_context.llbc_fun_decls
-
-(* TODO: move *)
-let bs_ctx_lookup_local_function_sig (def_id : A.FunDeclId.id)
- (back_id : T.RegionGroupId.id option) (ctx : bs_ctx) : fun_sig =
- let id = (A.Regular def_id, back_id) in
- (RegularFunIdMap.find id ctx.fun_context.fun_sigs).sg
-
-let bs_ctx_register_forward_call (call_id : V.FunCallId.id) (forward : S.call)
- (args : texpression list) (ctx : bs_ctx) : bs_ctx =
- let calls = ctx.calls in
- assert (not (V.FunCallId.Map.mem call_id calls));
- let info =
- { forward; forward_inputs = args; backwards = T.RegionGroupId.Map.empty }
- in
- let calls = V.FunCallId.Map.add call_id info calls in
- { ctx with calls }
-
-(** [back_args]: the *additional* list of inputs received by the backward function *)
-let bs_ctx_register_backward_call (abs : V.abs) (back_args : texpression list)
- (ctx : bs_ctx) : bs_ctx * fun_id =
- (* Insert the abstraction in the call informations *)
- let back_id = abs.back_id in
- let info = V.FunCallId.Map.find abs.call_id ctx.calls in
- assert (not (T.RegionGroupId.Map.mem back_id info.backwards));
- let backwards =
- T.RegionGroupId.Map.add back_id (abs, back_args) info.backwards
- in
- let info = { info with backwards } in
- let calls = V.FunCallId.Map.add abs.call_id info ctx.calls in
- (* Insert the abstraction in the abstractions map *)
- let abstractions = ctx.abstractions in
- assert (not (V.AbstractionId.Map.mem abs.abs_id abstractions));
- let abstractions =
- V.AbstractionId.Map.add abs.abs_id (abs, back_args) abstractions
- in
- (* Retrieve the fun_id *)
- let fun_id =
- match info.forward.call_id with
- | S.Fun (fid, _) -> Regular (fid, Some abs.back_id)
- | S.Unop _ | S.Binop _ -> raise (Failure "Unreachable")
- in
- (* Update the context and return *)
- ({ ctx with calls; abstractions }, fun_id)
-
-let rec translate_sty (ty : T.sty) : ty =
- let translate = translate_sty in
- match ty with
- | T.Adt (type_id, regions, tys) -> (
- (* Can't translate types with regions for now *)
- assert (regions = []);
- let tys = List.map translate tys in
- match type_id with
- | T.AdtId adt_id -> Adt (AdtId adt_id, tys)
- | T.Tuple -> mk_simpl_tuple_ty tys
- | T.Assumed aty -> (
- match aty with
- | T.Vec -> Adt (Assumed Vec, tys)
- | T.Option -> Adt (Assumed Option, tys)
- | T.Box -> (
- (* Eliminate the boxes *)
- match tys with
- | [ ty ] -> ty
- | _ ->
- failwith
- "Box/vec/option type with incorrect number of arguments")))
- | TypeVar vid -> TypeVar vid
- | Bool -> Bool
- | Char -> Char
- | Never -> raise (Failure "Unreachable")
- | Integer int_ty -> Integer int_ty
- | Str -> Str
- | Array ty -> Array (translate ty)
- | Slice ty -> Slice (translate ty)
- | Ref (_, rty, _) -> translate rty
-
-let translate_field (f : T.field) : field =
- let field_name = f.field_name in
- let field_ty = translate_sty f.field_ty in
- { field_name; field_ty }
-
-let translate_fields (fl : T.field list) : field list =
- List.map translate_field fl
-
-let translate_variant (v : T.variant) : variant =
- let variant_name = v.variant_name in
- let fields = translate_fields v.fields in
- { variant_name; fields }
-
-let translate_variants (vl : T.variant list) : variant list =
- List.map translate_variant vl
-
-(** Translate a type def kind to IM *)
-let translate_type_decl_kind (kind : T.type_decl_kind) : type_decl_kind =
- match kind with
- | T.Struct fields -> Struct (translate_fields fields)
- | T.Enum variants -> Enum (translate_variants variants)
- | T.Opaque -> Opaque
-
-(** Translate a type definition from IM
-
- TODO: this is not symbolic to pure but IM to pure. Still, I don't see the
- point of moving this definition for now.
- *)
-let translate_type_decl (def : T.type_decl) : type_decl =
- (* Translate *)
- let def_id = def.T.def_id in
- let name = def.name in
- (* Can't translate types with regions for now *)
- assert (def.region_params = []);
- let type_params = def.type_params in
- let kind = translate_type_decl_kind def.T.kind in
- { def_id; name; type_params; kind }
-
-(** Translate a type, seen as an input/output of a forward function
- (preserve all borrows, etc.)
-*)
-
-let rec translate_fwd_ty (types_infos : TA.type_infos) (ty : 'r T.ty) : ty =
- let translate = translate_fwd_ty types_infos in
- match ty with
- | T.Adt (type_id, regions, tys) -> (
- (* Can't translate types with regions for now *)
- assert (regions = []);
- (* Translate the type parameters *)
- let t_tys = List.map translate tys in
- (* Eliminate boxes and simplify tuples *)
- match type_id with
- | AdtId _ | T.Assumed (T.Vec | T.Option) ->
- (* No general parametricity for now *)
- assert (not (List.exists (TypesUtils.ty_has_borrows types_infos) tys));
- let type_id =
- match type_id with
- | AdtId adt_id -> AdtId adt_id
- | T.Assumed T.Vec -> Assumed Vec
- | T.Assumed T.Option -> Assumed Option
- | _ -> raise (Failure "Unreachable")
- in
- Adt (type_id, t_tys)
- | Tuple ->
- (* Note that if there is exactly one type, [mk_simpl_tuple_ty] is the
- identity *)
- mk_simpl_tuple_ty t_tys
- | T.Assumed T.Box -> (
- (* We eliminate boxes *)
- (* No general parametricity for now *)
- assert (not (List.exists (TypesUtils.ty_has_borrows types_infos) tys));
- match t_tys with
- | [ bty ] -> bty
- | _ ->
- failwith
- "Unreachable: box/vec/option receives exactly one type \
- parameter"))
- | TypeVar vid -> TypeVar vid
- | Bool -> Bool
- | Char -> Char
- | Never -> raise (Failure "Unreachable")
- | Integer int_ty -> Integer int_ty
- | Str -> Str
- | Array ty ->
- assert (not (TypesUtils.ty_has_borrows types_infos ty));
- Array (translate ty)
- | Slice ty ->
- assert (not (TypesUtils.ty_has_borrows types_infos ty));
- Slice (translate ty)
- | Ref (_, rty, _) -> translate rty
-
-(** Simply calls [translate_fwd_ty] *)
-let ctx_translate_fwd_ty (ctx : bs_ctx) (ty : 'r T.ty) : ty =
- let types_infos = ctx.type_context.types_infos in
- translate_fwd_ty types_infos ty
-
-(** Translate a type, when some regions may have ended.
-
- We return an option, because the translated type may be empty.
-
- [inside_mut]: are we inside a mutable borrow?
- *)
-let rec translate_back_ty (types_infos : TA.type_infos)
- (keep_region : 'r -> bool) (inside_mut : bool) (ty : 'r T.ty) : ty option =
- let translate = translate_back_ty types_infos keep_region inside_mut in
- (* A small helper for "leave" types *)
- let wrap ty = if inside_mut then Some ty else None in
- match ty with
- | T.Adt (type_id, _, tys) -> (
- match type_id with
- | T.AdtId _ | Assumed (T.Vec | T.Option) ->
- (* Don't accept ADTs (which are not tuples) with borrows for now *)
- assert (not (TypesUtils.ty_has_borrows types_infos ty));
- let type_id =
- match type_id with
- | T.AdtId id -> AdtId id
- | T.Assumed T.Vec -> Assumed Vec
- | T.Assumed T.Option -> Assumed Option
- | T.Tuple | T.Assumed T.Box -> raise (Failure "Unreachable")
- in
- if inside_mut then
- let tys_t = List.filter_map translate tys in
- Some (Adt (type_id, tys_t))
- else None
- | Assumed T.Box -> (
- (* Don't accept ADTs (which are not tuples) with borrows for now *)
- assert (not (TypesUtils.ty_has_borrows types_infos ty));
- (* Eliminate the box *)
- match tys with
- | [ bty ] -> translate bty
- | _ ->
- failwith "Unreachable: boxes receive exactly one type parameter")
- | T.Tuple -> (
- (* Tuples can contain borrows (which we eliminated) *)
- let tys_t = List.filter_map translate tys in
- match tys_t with
- | [] -> None
- | _ ->
- (* Note that if there is exactly one type, [mk_simpl_tuple_ty]
- * is the identity *)
- Some (mk_simpl_tuple_ty tys_t)))
- | TypeVar vid -> wrap (TypeVar vid)
- | Bool -> wrap Bool
- | Char -> wrap Char
- | Never -> raise (Failure "Unreachable")
- | Integer int_ty -> wrap (Integer int_ty)
- | Str -> wrap Str
- | Array ty -> (
- assert (not (TypesUtils.ty_has_borrows types_infos ty));
- match translate ty with None -> None | Some ty -> Some (Array ty))
- | Slice ty -> (
- assert (not (TypesUtils.ty_has_borrows types_infos ty));
- match translate ty with None -> None | Some ty -> Some (Slice ty))
- | Ref (r, rty, rkind) -> (
- match rkind with
- | T.Shared ->
- (* Ignore shared references, unless we are below a mutable borrow *)
- if inside_mut then translate rty else None
- | T.Mut ->
- (* Dive in, remembering the fact that we are inside a mutable borrow *)
- let inside_mut = true in
- if keep_region r then
- translate_back_ty types_infos keep_region inside_mut rty
- else None)
-
-(** Simply calls [translate_back_ty] *)
-let ctx_translate_back_ty (ctx : bs_ctx) (keep_region : 'r -> bool)
- (inside_mut : bool) (ty : 'r T.ty) : ty option =
- let types_infos = ctx.type_context.types_infos in
- translate_back_ty types_infos keep_region inside_mut ty
-
-(** List the ancestors of an abstraction *)
-let list_ancestor_abstractions_ids (ctx : bs_ctx) (abs : V.abs) :
- V.AbstractionId.id list =
- (* We could do something more "elegant" without references, but it is
- * so much simpler to use references... *)
- let abs_set = ref V.AbstractionId.Set.empty in
- let rec gather (abs_id : V.AbstractionId.id) : unit =
- if V.AbstractionId.Set.mem abs_id !abs_set then ()
- else (
- abs_set := V.AbstractionId.Set.add abs_id !abs_set;
- let abs, _ = V.AbstractionId.Map.find abs_id ctx.abstractions in
- List.iter gather abs.original_parents)
- in
- List.iter gather abs.original_parents;
- let ids = !abs_set in
- (* List the ancestors, in the proper order *)
- let call_info = V.FunCallId.Map.find abs.call_id ctx.calls in
- List.filter
- (fun id -> V.AbstractionId.Set.mem id ids)
- call_info.forward.abstractions
-
-let list_ancestor_abstractions (ctx : bs_ctx) (abs : V.abs) :
- (V.abs * texpression list) list =
- let abs_ids = list_ancestor_abstractions_ids ctx abs in
- List.map (fun id -> V.AbstractionId.Map.find id ctx.abstractions) abs_ids
-
-(** Small utility. *)
-let get_fun_effect_info (fun_infos : FA.fun_info A.FunDeclId.Map.t)
- (fun_id : A.fun_id) (gid : T.RegionGroupId.id option) : fun_effect_info =
- match fun_id with
- | A.Regular fid ->
- let info = A.FunDeclId.Map.find fid fun_infos in
- let input_state = info.stateful in
- let output_state = input_state && gid = None in
- { can_fail = info.can_fail; input_state; output_state }
- | A.Assumed aid ->
- {
- can_fail = Assumed.assumed_can_fail aid;
- input_state = false;
- output_state = false;
- }
-
-(** Translate a function signature.
-
- Note that the function also takes a list of names for the inputs, and
- computes, for every output for the backward functions, a corresponding
- name (outputs for backward functions come from borrows in the inputs
- of the forward function).
- *)
-let translate_fun_sig (fun_infos : FA.fun_info A.FunDeclId.Map.t)
- (fun_id : A.fun_id) (types_infos : TA.type_infos) (sg : A.fun_sig)
- (input_names : string option list) (bid : T.RegionGroupId.id option) :
- fun_sig_named_outputs =
- (* Retrieve the list of parent backward functions *)
- let gid, parents =
- match bid with
- | None -> (None, T.RegionGroupId.Set.empty)
- | Some bid ->
- let parents = list_parent_region_groups sg bid in
- (Some bid, parents)
- in
- (* List the inputs for:
- * - the forward function
- * - the parent backward functions, in proper order
- * - the current backward function (if it is a backward function)
- *)
- let fwd_inputs = List.map (translate_fwd_ty types_infos) sg.inputs in
- (* For the backward functions: for now we don't supported nested borrows,
- * so just check that there aren't parent regions *)
- assert (T.RegionGroupId.Set.is_empty parents);
- (* Small helper to translate types for backward functions *)
- let translate_back_ty_for_gid (gid : T.RegionGroupId.id) : T.sty -> ty option
- =
- let rg = T.RegionGroupId.nth sg.regions_hierarchy gid in
- let regions = T.RegionVarId.Set.of_list rg.regions in
- let keep_region r =
- match r with
- | T.Static -> raise Unimplemented
- | T.Var r -> T.RegionVarId.Set.mem r regions
- in
- let inside_mut = false in
- translate_back_ty types_infos keep_region inside_mut
- in
- (* Compute the additinal inputs for the current function, if it is a backward
- * function *)
- let back_inputs =
- match gid with
- | None -> []
- | Some gid ->
- (* For now, we don't allow nested borrows, so the additional inputs to the
- backward function can only come from borrows that were returned like
- in (for the backward function we introduce for 'a):
- {[
- fn f<'a>(...) -> &'a mut u32;
- ]}
- Upon ending the abstraction for 'a, we need to get back the borrow
- the function returned.
- *)
- List.filter_map (translate_back_ty_for_gid gid) [ sg.output ]
- in
- (* Does the function take a state as input, does it return a state and can
- * it fail? *)
- let effect_info = get_fun_effect_info fun_infos fun_id bid in
- (* *)
- let state_ty = if effect_info.input_state then [ mk_state_ty ] else [] in
- (* Concatenate the inputs, in the following order:
- * - forward inputs
- * - state input
- * - backward inputs
- *)
- let inputs = List.concat [ fwd_inputs; state_ty; back_inputs ] in
- (* Outputs *)
- let output_names, doutputs =
- match gid with
- | None ->
- (* This is a forward function: there is one (unnamed) output *)
- ([ None ], [ translate_fwd_ty types_infos sg.output ])
- | Some gid ->
- (* This is a backward function: there might be several outputs.
- The outputs are the borrows inside the regions of the abstractions
- and which are present in the input values. For instance, see:
- {[
- fn f<'a>(x : &'a mut u32) -> ...;
- ]}
- Upon ending the abstraction for 'a, we give back the borrow which
- was consumed through the [x] parameter.
- *)
- let outputs =
- List.map
- (fun (name, input_ty) ->
- (name, translate_back_ty_for_gid gid input_ty))
- (List.combine input_names sg.inputs)
- in
- (* Filter *)
- let outputs =
- List.filter (fun (_, opt_ty) -> Option.is_some opt_ty) outputs
- in
- let outputs =
- List.map (fun (name, opt_ty) -> (name, Option.get opt_ty)) outputs
- in
- List.split outputs
- in
- (* Create the return type *)
- let output =
- (* Group the outputs together *)
- let output = mk_simpl_tuple_ty doutputs in
- (* Add the output state *)
- let output =
- if effect_info.output_state then mk_simpl_tuple_ty [ mk_state_ty; output ]
- else output
- in
- (* Wrap in a result type *)
- if effect_info.can_fail then mk_result_ty output else output
- in
- (* Type parameters *)
- let type_params = sg.type_params in
- (* Return *)
- let info =
- {
- num_fwd_inputs = List.length fwd_inputs;
- num_back_inputs =
- (if bid = None then None else Some (List.length back_inputs));
- effect_info;
- }
- in
- let sg = { type_params; inputs; output; doutputs; info } in
- { sg; output_names }
-
-let bs_ctx_fresh_state_var (ctx : bs_ctx) : bs_ctx * typed_pattern =
- (* Generate the fresh variable *)
- let id, var_counter = VarId.fresh ctx.var_counter in
- let var =
- { id; basename = Some ConstStrings.state_basename; ty = mk_state_ty }
- in
- let state_var = mk_typed_pattern_from_var var None in
- (* Update the context *)
- let ctx = { ctx with var_counter; state_var = id } in
- (* Return *)
- (ctx, state_var)
-
-let fresh_named_var_for_symbolic_value (basename : string option)
- (sv : V.symbolic_value) (ctx : bs_ctx) : bs_ctx * var =
- (* Generate the fresh variable *)
- let id, var_counter = VarId.fresh ctx.var_counter in
- let ty = ctx_translate_fwd_ty ctx sv.sv_ty in
- let var = { id; basename; ty } in
- (* Insert in the map *)
- let sv_to_var = V.SymbolicValueId.Map.add sv.sv_id var ctx.sv_to_var in
- (* Update the context *)
- let ctx = { ctx with var_counter; sv_to_var } in
- (* Return *)
- (ctx, var)
-
-let fresh_var_for_symbolic_value (sv : V.symbolic_value) (ctx : bs_ctx) :
- bs_ctx * var =
- fresh_named_var_for_symbolic_value None sv ctx
-
-let fresh_vars_for_symbolic_values (svl : V.symbolic_value list) (ctx : bs_ctx)
- : bs_ctx * var list =
- List.fold_left_map (fun ctx sv -> fresh_var_for_symbolic_value sv ctx) ctx svl
-
-let fresh_named_vars_for_symbolic_values
- (svl : (string option * V.symbolic_value) list) (ctx : bs_ctx) :
- bs_ctx * var list =
- List.fold_left_map
- (fun ctx (name, sv) -> fresh_named_var_for_symbolic_value name sv ctx)
- ctx svl
-
-(** This generates a fresh variable **which is not to be linked to any symbolic value** *)
-let fresh_var (basename : string option) (ty : ty) (ctx : bs_ctx) : bs_ctx * var
- =
- (* Generate the fresh variable *)
- let id, var_counter = VarId.fresh ctx.var_counter in
- let var = { id; basename; ty } in
- (* Update the context *)
- let ctx = { ctx with var_counter } in
- (* Return *)
- (ctx, var)
-
-let fresh_vars (vars : (string option * ty) list) (ctx : bs_ctx) :
- bs_ctx * var list =
- List.fold_left_map (fun ctx (name, ty) -> fresh_var name ty ctx) ctx vars
-
-let lookup_var_for_symbolic_value (sv : V.symbolic_value) (ctx : bs_ctx) : var =
- V.SymbolicValueId.Map.find sv.sv_id ctx.sv_to_var
-
-(** Peel boxes as long as the value is of the form [Box<T>] *)
-let rec unbox_typed_value (v : V.typed_value) : V.typed_value =
- match (v.value, v.ty) with
- | V.Adt av, T.Adt (T.Assumed T.Box, _, _) -> (
- match av.field_values with
- | [ bv ] -> unbox_typed_value bv
- | _ -> raise (Failure "Unreachable"))
- | _ -> v
-
-(** Translate a typed value.
-
- It is used, for instance, on values used as inputs for function calls.
-
- **IMPORTANT**: this function makes the assumption that the typed value
- doesn't contain ⊥. This means in particular that symbolic values don't
- contain ended regions.
-
- TODO: we might want to remember in the symbolic AST the set of ended
- regions, at the points where we need it, for sanity checks (though the
- sanity checks in the symbolic interpreter should be enough).
- The points where we need this set so far:
- - function call
- - end abstraction
- - return
- *)
-let rec typed_value_to_texpression (ctx : bs_ctx) (v : V.typed_value) :
- texpression =
- (* We need to ignore boxes *)
- let v = unbox_typed_value v in
- let translate = typed_value_to_texpression ctx in
- (* Translate the type *)
- let ty = ctx_translate_fwd_ty ctx v.ty in
- (* Translate the value *)
- let value =
- match v.value with
- | V.Concrete cv -> { e = Const cv; ty }
- | Adt av -> (
- let variant_id = av.variant_id in
- let field_values = List.map translate av.field_values in
- (* Eliminate the tuple wrapper if it is a tuple with exactly one field *)
- match v.ty with
- | T.Adt (T.Tuple, _, _) ->
- assert (variant_id = None);
- mk_simpl_tuple_texpression field_values
- | _ ->
- (* Retrieve the type and the translated type arguments from the
- * translated type (simpler this way) *)
- let adt_id, type_args =
- match ty with
- | Adt (type_id, tys) -> (type_id, tys)
- | _ -> raise (Failure "Unreachable")
- in
- (* Create the constructor *)
- let qualif_id = AdtCons { adt_id; variant_id = av.variant_id } in
- let qualif = { id = qualif_id; type_args } in
- let cons_e = Qualif qualif in
- let field_tys =
- List.map (fun (v : texpression) -> v.ty) field_values
- in
- let cons_ty = mk_arrows field_tys ty in
- let cons = { e = cons_e; ty = cons_ty } in
- (* Apply the constructor *)
- mk_apps cons field_values)
- | Bottom -> raise (Failure "Unreachable")
- | Loan lc -> (
- match lc with
- | SharedLoan (_, v) -> translate v
- | MutLoan _ -> raise (Failure "Unreachable"))
- | Borrow bc -> (
- match bc with
- | V.SharedBorrow (mv, _) ->
- (* The meta-value stored in the shared borrow was added especially
- * for this case (because we can't use the borrow id for lookups) *)
- translate mv
- | V.InactivatedMutBorrow (mv, _) ->
- (* Same as for shared borrows. However, note that we use inactivated borrows
- * only in meta-data: a value actually *used in the translation* can't come
- * from an unpromoted inactivated borrow *)
- translate mv
- | V.MutBorrow (_, v) ->
- (* Borrows are the identity in the extraction *)
- translate v)
- | Symbolic sv ->
- let var = lookup_var_for_symbolic_value sv ctx in
- mk_texpression_from_var var
- in
- (* Debugging *)
- log#ldebug
- (lazy
- ("typed_value_to_texpression: result:" ^ "\n- input value:\n"
- ^ V.show_typed_value v ^ "\n- translated expression:\n"
- ^ show_texpression value));
- (* Sanity check *)
- type_check_texpression ctx value;
- (* Return *)
- value
-
-(** Explore an abstraction value and convert it to a consumed value
- by collecting all the meta-values from the ended *loans*.
-
- Consumed values are rvalues, because when an abstraction ends, we
- introduce a call to a backward function in the synthesized program,
- which takes as inputs those consumed values:
- {[
- // Rust:
- fn choose<'a>(b: bool, x : &'a mut u32, y : &'a mut u32) -> &'a mut u32;
-
- // Synthesis:
- let ... = choose_back b x y nz in
- ^^
- ]}
- *)
-let rec typed_avalue_to_consumed (ctx : bs_ctx) (av : V.typed_avalue) :
- texpression option =
- let translate = typed_avalue_to_consumed ctx in
- let value =
- match av.value with
- | AConcrete _ -> raise (Failure "Unreachable")
- | AAdt adt_v -> (
- (* Translate the field values *)
- let field_values = List.filter_map translate adt_v.field_values in
- (* For now, only tuples can contain borrows *)
- let adt_id, _, _ = TypesUtils.ty_as_adt av.ty in
- match adt_id with
- | T.AdtId _ | T.Assumed (T.Box | T.Vec | T.Option) ->
- assert (field_values = []);
- None
- | T.Tuple ->
- (* Return *)
- if field_values = [] then None
- else
- (* Note that if there is exactly one field value,
- * [mk_simpl_tuple_rvalue] is the identity *)
- let rv = mk_simpl_tuple_texpression field_values in
- Some rv)
- | ABottom -> raise (Failure "Unreachable")
- | ALoan lc -> aloan_content_to_consumed ctx lc
- | ABorrow bc -> aborrow_content_to_consumed ctx bc
- | ASymbolic aproj -> aproj_to_consumed ctx aproj
- | AIgnored -> None
- in
- (* Sanity check - Rk.: we do this at every recursive call, which is a bit
- * expansive... *)
- (match value with
- | None -> ()
- | Some value -> type_check_texpression ctx value);
- (* Return *)
- value
-
-and aloan_content_to_consumed (ctx : bs_ctx) (lc : V.aloan_content) :
- texpression option =
- match lc with
- | AMutLoan (_, _) | ASharedLoan (_, _, _) -> raise (Failure "Unreachable")
- | AEndedMutLoan { child = _; given_back = _; given_back_meta } ->
- (* Return the meta-value *)
- Some (typed_value_to_texpression ctx given_back_meta)
- | AEndedSharedLoan (_, _) ->
- (* We don't dive into shared loans: there is nothing to give back
- * inside (note that there could be a mutable borrow in the shared
- * value, pointing to a mutable loan in the child avalue, but this
- * borrow is in practice immutable) *)
- None
- | AIgnoredMutLoan (_, _) ->
- (* There can be *inner* not ended mutable loans, but not outer ones *)
- raise (Failure "Unreachable")
- | AEndedIgnoredMutLoan _ ->
- (* This happens with nested borrows: we need to dive in *)
- raise Unimplemented
- | AIgnoredSharedLoan _ ->
- (* Ignore *)
- None
-
-and aborrow_content_to_consumed (_ctx : bs_ctx) (bc : V.aborrow_content) :
- texpression option =
- match bc with
- | V.AMutBorrow (_, _, _) | ASharedBorrow _ | AIgnoredMutBorrow (_, _) ->
- raise (Failure "Unreachable")
- | AEndedMutBorrow (_, _) ->
- (* We collect consumed values: ignore *)
- None
- | AEndedIgnoredMutBorrow _ ->
- (* This happens with nested borrows: we need to dive in *)
- raise Unimplemented
- | AEndedSharedBorrow | AProjSharedBorrow _ ->
- (* Ignore *)
- None
-
-and aproj_to_consumed (ctx : bs_ctx) (aproj : V.aproj) : texpression option =
- match aproj with
- | V.AEndedProjLoans (msv, []) ->
- (* The symbolic value was left unchanged *)
- let var = lookup_var_for_symbolic_value msv ctx in
- Some (mk_texpression_from_var var)
- | V.AEndedProjLoans (_, [ (mnv, child_aproj) ]) ->
- assert (child_aproj = AIgnoredProjBorrows);
- (* The symbolic value was updated *)
- let var = lookup_var_for_symbolic_value mnv ctx in
- Some (mk_texpression_from_var var)
- | V.AEndedProjLoans (_, _) ->
- (* The symbolic value was updated, and the given back values come from sevearl
- * abstractions *)
- raise Unimplemented
- | AEndedProjBorrows _ -> (* We consider consumed values *) None
- | AIgnoredProjBorrows | AProjLoans (_, _) | AProjBorrows (_, _) ->
- raise (Failure "Unreachable")
-
-(** Convert the abstraction values in an abstraction to consumed values.
-
- See [typed_avalue_to_consumed].
- *)
-let abs_to_consumed (ctx : bs_ctx) (abs : V.abs) : texpression list =
- log#ldebug (lazy ("abs_to_consumed:\n" ^ abs_to_string ctx abs));
- List.filter_map (typed_avalue_to_consumed ctx) abs.avalues
-
-let translate_mprojection_elem (pe : E.projection_elem) :
- mprojection_elem option =
- match pe with
- | Deref | DerefBox -> None
- | Field (pkind, field_id) -> Some { pkind; field_id }
-
-let translate_mprojection (p : E.projection) : mprojection =
- List.filter_map translate_mprojection_elem p
-
-(** Translate a "meta"-place *)
-let translate_mplace (p : S.mplace) : mplace =
- let var_id = p.bv.index in
- let name = p.bv.name in
- let projection = translate_mprojection p.projection in
- { var_id; name; projection }
-
-let translate_opt_mplace (p : S.mplace option) : mplace option =
- match p with None -> None | Some p -> Some (translate_mplace p)
-
-(** Explore an abstraction value and convert it to a given back value
- by collecting all the meta-values from the ended *borrows*.
-
- Given back values are patterns, because when an abstraction ends, we
- introduce a call to a backward function in the synthesized program,
- which introduces new values:
- {[
- let (nx, ny) = f_back ... in
- ^^^^^^^^
- ]}
-
- [mp]: it is possible to provide some meta-place information, to guide
- the heuristics which later find pretty names for the variables.
- *)
-let rec typed_avalue_to_given_back (mp : mplace option) (av : V.typed_avalue)
- (ctx : bs_ctx) : bs_ctx * typed_pattern option =
- let ctx, value =
- match av.value with
- | AConcrete _ -> raise (Failure "Unreachable")
- | AAdt adt_v -> (
- (* Translate the field values *)
- (* For now we forget the meta-place information so that it doesn't get used
- * by several fields (which would then all have the same name...), but we
- * might want to do something smarter *)
- let mp = None in
- let ctx, field_values =
- List.fold_left_map
- (fun ctx fv -> typed_avalue_to_given_back mp fv ctx)
- ctx adt_v.field_values
- in
- let field_values = List.filter_map (fun x -> x) field_values in
- (* For now, only tuples can contain borrows - note that if we gave
- * something like a [&mut Vec] to a function, we give give back the
- * vector value upon visiting the "abstraction borrow" node *)
- let adt_id, _, _ = TypesUtils.ty_as_adt av.ty in
- match adt_id with
- | T.AdtId _ | T.Assumed (T.Box | T.Vec | T.Option) ->
- assert (field_values = []);
- (ctx, None)
- | T.Tuple ->
- (* Return *)
- let variant_id = adt_v.variant_id in
- assert (variant_id = None);
- if field_values = [] then (ctx, None)
- else
- (* Note that if there is exactly one field value, [mk_simpl_tuple_pattern]
- * is the identity *)
- let lv = mk_simpl_tuple_pattern field_values in
- (ctx, Some lv))
- | ABottom -> raise (Failure "Unreachable")
- | ALoan lc -> aloan_content_to_given_back mp lc ctx
- | ABorrow bc -> aborrow_content_to_given_back mp bc ctx
- | ASymbolic aproj -> aproj_to_given_back mp aproj ctx
- | AIgnored -> (ctx, None)
- in
- (* Sanity check - Rk.: we do this at every recursive call, which is a bit
- * expansive... *)
- (match value with None -> () | Some value -> type_check_pattern ctx value);
- (* Return *)
- (ctx, value)
-
-and aloan_content_to_given_back (_mp : mplace option) (lc : V.aloan_content)
- (ctx : bs_ctx) : bs_ctx * typed_pattern option =
- match lc with
- | AMutLoan (_, _) | ASharedLoan (_, _, _) -> raise (Failure "Unreachable")
- | AEndedMutLoan { child = _; given_back = _; given_back_meta = _ }
- | AEndedSharedLoan (_, _) ->
- (* We consider given back values, and thus ignore those *)
- (ctx, None)
- | AIgnoredMutLoan (_, _) ->
- (* There can be *inner* not ended mutable loans, but not outer ones *)
- raise (Failure "Unreachable")
- | AEndedIgnoredMutLoan _ ->
- (* This happens with nested borrows: we need to dive in *)
- raise Unimplemented
- | AIgnoredSharedLoan _ ->
- (* Ignore *)
- (ctx, None)
-
-and aborrow_content_to_given_back (mp : mplace option) (bc : V.aborrow_content)
- (ctx : bs_ctx) : bs_ctx * typed_pattern option =
- match bc with
- | V.AMutBorrow (_, _, _) | ASharedBorrow _ | AIgnoredMutBorrow (_, _) ->
- raise (Failure "Unreachable")
- | AEndedMutBorrow (msv, _) ->
- (* Return the meta-symbolic-value *)
- let ctx, var = fresh_var_for_symbolic_value msv ctx in
- (ctx, Some (mk_typed_pattern_from_var var mp))
- | AEndedIgnoredMutBorrow _ ->
- (* This happens with nested borrows: we need to dive in *)
- raise Unimplemented
- | AEndedSharedBorrow | AProjSharedBorrow _ ->
- (* Ignore *)
- (ctx, None)
-
-and aproj_to_given_back (mp : mplace option) (aproj : V.aproj) (ctx : bs_ctx) :
- bs_ctx * typed_pattern option =
- match aproj with
- | V.AEndedProjLoans (_, child_projs) ->
- (* There may be children borrow projections in case of nested borrows,
- * in which case we need to dive in - we disallow nested borrows for now *)
- assert (
- List.for_all
- (fun (_, aproj) -> aproj = V.AIgnoredProjBorrows)
- child_projs);
- (ctx, None)
- | AEndedProjBorrows mv ->
- (* Return the meta-value *)
- let ctx, var = fresh_var_for_symbolic_value mv ctx in
- (ctx, Some (mk_typed_pattern_from_var var mp))
- | AIgnoredProjBorrows | AProjLoans (_, _) | AProjBorrows (_, _) ->
- raise (Failure "Unreachable")
-
-(** Convert the abstraction values in an abstraction to given back values.
-
- See [typed_avalue_to_given_back].
- *)
-let abs_to_given_back (mpl : mplace option list) (abs : V.abs) (ctx : bs_ctx) :
- bs_ctx * typed_pattern list =
- let avalues = List.combine mpl abs.avalues in
- let ctx, values =
- List.fold_left_map
- (fun ctx (mp, av) -> typed_avalue_to_given_back mp av ctx)
- ctx avalues
- in
- let values = List.filter_map (fun x -> x) values in
- (ctx, values)
-
-(** Simply calls [abs_to_given_back] *)
-let abs_to_given_back_no_mp (abs : V.abs) (ctx : bs_ctx) :
- bs_ctx * typed_pattern list =
- let mpl = List.map (fun _ -> None) abs.avalues in
- abs_to_given_back mpl abs ctx
-
-(** Return the ordered list of the (transitive) parents of a given abstraction.
-
- Is used for instance when collecting the input values given to all the
- parent functions, in order to properly instantiate an
- *)
-let get_abs_ancestors (ctx : bs_ctx) (abs : V.abs) :
- S.call * (V.abs * texpression list) list =
- let call_info = V.FunCallId.Map.find abs.call_id ctx.calls in
- let abs_ancestors = list_ancestor_abstractions ctx abs in
- (call_info.forward, abs_ancestors)
-
-let rec translate_expression (config : config) (e : S.expression) (ctx : bs_ctx)
- : texpression =
- match e with
- | S.Return opt_v -> translate_return opt_v ctx
- | Panic -> translate_panic ctx
- | FunCall (call, e) -> translate_function_call config call e ctx
- | EndAbstraction (abs, e) -> translate_end_abstraction config abs e ctx
- | EvalGlobal (gid, sv, e) -> translate_global_eval config gid sv e ctx
- | Expansion (p, sv, exp) -> translate_expansion config p sv exp ctx
- | Meta (meta, e) -> translate_meta config meta e ctx
-
-and translate_panic (ctx : bs_ctx) : texpression =
- (* Here we use the function return type - note that it is ok because
- * we don't match on panics which happen inside the function body -
- * but it won't be true anymore once we translate individual blocks *)
- (* If we use a state monad, we need to add a lambda for the state variable *)
- (* Note that only forward functions return a state *)
- let output_ty = mk_simpl_tuple_ty ctx.sg.doutputs in
- if ctx.sg.info.effect_info.output_state then
- (* Create the [Fail] value *)
- let ret_ty = mk_simpl_tuple_ty [ mk_state_ty; output_ty ] in
- let ret_v = mk_result_fail_texpression ret_ty in
- ret_v
- else mk_result_fail_texpression output_ty
-
-and translate_return (opt_v : V.typed_value option) (ctx : bs_ctx) : texpression
- =
- (* There are two cases:
- - either we are translating a forward function, in which case the optional
- value should be [Some] (it is the returned value)
- - or we are translating a backward function, in which case it should be [None]
- *)
- match ctx.bid with
- | None ->
- (* Forward function *)
- let v = Option.get opt_v in
- let v = typed_value_to_texpression ctx v in
- (* We may need to return a state
- * - error-monad: Return x
- * - state-error: Return (state, x)
- * *)
- if ctx.sg.info.effect_info.output_state then
- let state_var =
- {
- id = ctx.state_var;
- basename = Some ConstStrings.state_basename;
- ty = mk_state_ty;
- }
- in
- let state_rvalue = mk_texpression_from_var state_var in
- mk_result_return_texpression
- (mk_simpl_tuple_texpression [ state_rvalue; v ])
- else mk_result_return_texpression v
- | Some bid ->
- (* Backward function *)
- (* Sanity check *)
- assert (opt_v = None);
- assert (not ctx.sg.info.effect_info.output_state);
- (* We simply need to return the variables in which we stored the values
- * we need to give back.
- * See the explanations for the [SynthInput] case in [translate_end_abstraction] *)
- let backward_outputs =
- T.RegionGroupId.Map.find bid ctx.backward_outputs
- in
- let field_values = List.map mk_texpression_from_var backward_outputs in
- (* Backward functions never return a state *)
- (* TODO: we should use a [fail] function, it would be cleaner *)
- let ret_value = mk_simpl_tuple_texpression field_values in
- let ret_value = mk_result_return_texpression ret_value in
- ret_value
-
-and translate_function_call (config : config) (call : S.call) (e : S.expression)
- (ctx : bs_ctx) : texpression =
- (* Translate the function call *)
- let type_args = List.map (ctx_translate_fwd_ty ctx) call.type_params in
- let args =
- let args = List.map (typed_value_to_texpression ctx) call.args in
- let args_mplaces = List.map translate_opt_mplace call.args_places in
- List.map
- (fun (arg, mp) -> mk_opt_mplace_texpression mp arg)
- (List.combine args args_mplaces)
- in
- let dest_mplace = translate_opt_mplace call.dest_place in
- let ctx, dest = fresh_var_for_symbolic_value call.dest ctx in
- (* Retrieve the function id, and register the function call in the context
- * if necessary. *)
- let ctx, fun_id, effect_info, args, out_state =
- match call.call_id with
- | S.Fun (fid, call_id) ->
- (* Regular function call *)
- let func = Regular (fid, None) in
- (* Retrieve the effect information about this function (can fail,
- * takes a state as input, etc.) *)
- let effect_info =
- get_fun_effect_info ctx.fun_context.fun_infos fid None
- in
- (* Add the state input argument *)
- let args =
- if effect_info.input_state then
- let state_var = { e = Var ctx.state_var; ty = mk_state_ty } in
- List.append args [ state_var ]
- else args
- in
- (* Generate a fresh state variable if the function call introduces
- * a new variable *)
- let ctx, out_state =
- if effect_info.input_state then
- let ctx, var = bs_ctx_fresh_state_var ctx in
- (ctx, Some var)
- else (ctx, None)
- in
- (* Register the function call *)
- let ctx = bs_ctx_register_forward_call call_id call args ctx in
- (ctx, func, effect_info, args, out_state)
- | S.Unop E.Not ->
- let effect_info =
- { can_fail = false; input_state = false; output_state = false }
- in
- (ctx, Unop Not, effect_info, args, None)
- | S.Unop E.Neg -> (
- match args with
- | [ arg ] ->
- let int_ty = ty_as_integer arg.ty in
- (* Note that negation can lead to an overflow and thus fail (it
- * is thus monadic) *)
- let effect_info =
- { can_fail = true; input_state = false; output_state = false }
- in
- (ctx, Unop (Neg int_ty), effect_info, args, None)
- | _ -> raise (Failure "Unreachable"))
- | S.Unop (E.Cast (src_ty, tgt_ty)) ->
- (* Note that cast can fail *)
- let effect_info =
- { can_fail = true; input_state = false; output_state = false }
- in
- (ctx, Unop (Cast (src_ty, tgt_ty)), effect_info, args, None)
- | S.Binop binop -> (
- match args with
- | [ arg0; arg1 ] ->
- let int_ty0 = ty_as_integer arg0.ty in
- let int_ty1 = ty_as_integer arg1.ty in
- assert (int_ty0 = int_ty1);
- let effect_info =
- {
- can_fail = ExpressionsUtils.binop_can_fail binop;
- input_state = false;
- output_state = false;
- }
- in
- (ctx, Binop (binop, int_ty0), effect_info, args, None)
- | _ -> raise (Failure "Unreachable"))
- in
- let dest_v =
- let dest = mk_typed_pattern_from_var dest dest_mplace in
- match out_state with
- | None -> dest
- | Some out_state -> mk_simpl_tuple_pattern [ out_state; dest ]
- in
- let func = { id = Func fun_id; type_args } in
- let input_tys = (List.map (fun (x : texpression) -> x.ty)) args in
- let ret_ty =
- if effect_info.can_fail then mk_result_ty dest_v.ty else dest_v.ty
- in
- let func_ty = mk_arrows input_tys ret_ty in
- let func = { e = Qualif func; ty = func_ty } in
- let call = mk_apps func args in
- (* Translate the next expression *)
- let next_e = translate_expression config e ctx in
- (* Put together *)
- mk_let effect_info.can_fail dest_v call next_e
-
-and translate_end_abstraction (config : config) (abs : V.abs) (e : S.expression)
- (ctx : bs_ctx) : texpression =
- log#ldebug
- (lazy
- ("translate_end_abstraction: abstraction kind: "
- ^ V.show_abs_kind abs.kind));
- match abs.kind with
- | V.SynthInput ->
- (* When we end an input abstraction, this input abstraction gets back
- * the borrows which it introduced in the context through the input
- * values: by listing those values, we get the values which are given
- * back by one of the backward functions we are synthesizing. *)
- (* Note that we don't support nested borrows for now: if we find
- * an ended synthesized input abstraction, it must be the one corresponding
- * to the backward function wer are synthesizing, it can't be the one
- * for a parent backward function.
- *)
- let bid = Option.get ctx.bid in
- assert (abs.back_id = bid);
-
- (* The translation is done as follows:
- * - for a given backward function, we choose a set of variables [v_i]
- * - when we detect the ended input abstraction which corresponds
- * to the backward function, and which consumed the values [consumed_i],
- * we introduce:
- * {[
- * let v_i = consumed_i in
- * ...
- * ]}
- * Then, when we reach the [Return] node, we introduce:
- * {[
- * (v_i)
- * ]}
- * *)
- (* First, get the given back variables *)
- let given_back_variables =
- T.RegionGroupId.Map.find bid ctx.backward_outputs
- in
- (* Get the list of values consumed by the abstraction upon ending *)
- let consumed_values = abs_to_consumed ctx abs in
- (* Group the two lists *)
- let variables_values =
- List.combine given_back_variables consumed_values
- in
- (* Sanity check: the two lists match (same types) *)
- List.iter
- (fun (var, v) -> assert ((var : var).ty = (v : texpression).ty))
- variables_values;
- (* Translate the next expression *)
- let next_e = translate_expression config e ctx in
- (* Generate the assignemnts *)
- let monadic = false in
- List.fold_right
- (fun (var, value) (e : texpression) ->
- mk_let monadic (mk_typed_pattern_from_var var None) value e)
- variables_values next_e
- | V.FunCall ->
- let call_info = V.FunCallId.Map.find abs.call_id ctx.calls in
- let call = call_info.forward in
- let type_args = List.map (ctx_translate_fwd_ty ctx) call.type_params in
- (* Retrieve the original call and the parent abstractions *)
- let _forward, backwards = get_abs_ancestors ctx abs in
- (* Retrieve the values consumed when we called the forward function and
- * ended the parent backward functions: those give us part of the input
- * values (rmk: for now, as we disallow nested lifetimes, there can't be
- * parent backward functions).
- * Note that the forward inputs include the input state (if there is one). *)
- let fwd_inputs = call_info.forward_inputs in
- let back_ancestors_inputs =
- List.concat (List.map (fun (_abs, args) -> args) backwards)
- in
- (* Retrieve the values consumed upon ending the loans inside this
- * abstraction: those give us the remaining input values *)
- let back_inputs = abs_to_consumed ctx abs in
- let inputs =
- List.concat [ fwd_inputs; back_ancestors_inputs; back_inputs ]
- in
- (* Retrieve the values given back by this function: those are the output
- * values. We rely on the fact that there are no nested borrows to use the
- * meta-place information from the input values given to the forward function
- * (we need to add [None] for the return avalue) *)
- let output_mpl =
- List.append (List.map translate_opt_mplace call.args_places) [ None ]
- in
- let ctx, outputs = abs_to_given_back output_mpl abs ctx in
- (* Group the output values together (note that for now, backward functions
- * never return an output state) *)
- let output = mk_simpl_tuple_pattern outputs in
- (* Sanity check: the inputs and outputs have the proper number and the proper type *)
- let fun_id =
- match call.call_id with
- | S.Fun (fun_id, _) -> fun_id
- | Unop _ | Binop _ ->
- (* Those don't have backward functions *)
- raise (Failure "Unreachable")
- in
-
- let inst_sg =
- get_instantiated_fun_sig fun_id (Some abs.back_id) type_args ctx
- in
- log#ldebug
- (lazy
- ("\n- fun_id: " ^ A.show_fun_id fun_id ^ "\n- inputs ("
- ^ string_of_int (List.length inputs)
- ^ "): "
- ^ String.concat ", " (List.map show_texpression inputs)
- ^ "\n- inst_sg.inputs ("
- ^ string_of_int (List.length inst_sg.inputs)
- ^ "): "
- ^ String.concat ", " (List.map show_ty inst_sg.inputs)));
- List.iter
- (fun (x, ty) -> assert ((x : texpression).ty = ty))
- (List.combine inputs inst_sg.inputs);
- log#ldebug
- (lazy
- ("\n- outputs: "
- ^ string_of_int (List.length outputs)
- ^ "\n- expected outputs: "
- ^ string_of_int (List.length inst_sg.doutputs)));
- List.iter
- (fun (x, ty) -> assert ((x : typed_pattern).ty = ty))
- (List.combine outputs inst_sg.doutputs);
- (* Retrieve the function id, and register the function call in the context
- * if necessary *)
- let ctx, func = bs_ctx_register_backward_call abs back_inputs ctx in
- (* Translate the next expression *)
- let next_e = translate_expression config e ctx in
- (* Put everything together *)
- let args_mplaces = List.map (fun _ -> None) inputs in
- let args =
- List.map
- (fun (arg, mp) -> mk_opt_mplace_texpression mp arg)
- (List.combine inputs args_mplaces)
- in
- let effect_info =
- get_fun_effect_info ctx.fun_context.fun_infos fun_id (Some abs.back_id)
- in
- let input_tys = (List.map (fun (x : texpression) -> x.ty)) args in
- let ret_ty =
- if effect_info.can_fail then mk_result_ty output.ty else output.ty
- in
- let func_ty = mk_arrows input_tys ret_ty in
- let func = { id = Func func; type_args } in
- let func = { e = Qualif func; ty = func_ty } in
- let call = mk_apps func args in
- (* **Optimization**:
- * =================
- * We do a small optimization here: if the backward function doesn't
- * have any output, we don't introduce any function call.
- * See the comment in [config].
- *)
- if config.filter_useless_back_calls && outputs = [] then (
- (* No outputs - we do a small sanity check: the backward function
- * should have exactly the same number of inputs as the forward:
- * this number can be different only if the forward function returned
- * a value containing mutable borrows, which can't be the case... *)
- assert (List.length inputs = List.length fwd_inputs);
- next_e)
- else mk_let effect_info.can_fail output call next_e
- | V.SynthRet ->
- (* If we end the abstraction which consumed the return value of the function
- we are synthesizing, we get back the borrows which were inside. Those borrows
- are actually input arguments of the backward function we are synthesizing.
- So we simply need to introduce proper let bindings.
-
- For instance:
- {[
- fn id<'a>(x : &'a mut u32) -> &'a mut u32 {
- x
- }
- ]}
-
- Upon ending the return abstraction for 'a, we get back the borrow for [x].
- This new value is the second argument of the backward function:
- {[
- let id_back x nx = nx
- ]}
-
- In practice, upon ending this abstraction we introduce a useless
- let-binding:
- {[
- let id_back x nx =
- let s = nx in // the name [s] is not important (only collision matters)
- ...
- ]}
-
- This let-binding later gets inlined, during a micro-pass.
- *)
- (* First, retrieve the list of variables used for the inputs for the
- * backward function *)
- let inputs = T.RegionGroupId.Map.find abs.back_id ctx.backward_inputs in
- (* Retrieve the values consumed upon ending the loans inside this
- * abstraction: as there are no nested borrows, there should be none. *)
- let consumed = abs_to_consumed ctx abs in
- assert (consumed = []);
- (* Retrieve the values given back upon ending this abstraction - note that
- * we don't provide meta-place information, because those assignments will
- * be inlined anyway... *)
- log#ldebug (lazy ("abs: " ^ abs_to_string ctx abs));
- let ctx, given_back = abs_to_given_back_no_mp abs ctx in
- (* Link the inputs to those given back values - note that this also
- * checks we have the same number of values, of course *)
- let given_back_inputs = List.combine given_back inputs in
- (* Sanity check *)
- List.iter
- (fun ((given_back, input) : typed_pattern * var) ->
- log#ldebug
- (lazy
- ("\n- given_back ty: "
- ^ ty_to_string ctx given_back.ty
- ^ "\n- sig input ty: " ^ ty_to_string ctx input.ty));
- assert (given_back.ty = input.ty))
- given_back_inputs;
- (* Translate the next expression *)
- let next_e = translate_expression config e ctx in
- (* Generate the assignments *)
- let monadic = false in
- List.fold_right
- (fun (given_back, input_var) e ->
- mk_let monadic given_back (mk_texpression_from_var input_var) e)
- given_back_inputs next_e
-
-and translate_global_eval (config : config) (gid : A.GlobalDeclId.id)
- (sval : V.symbolic_value) (e : S.expression) (ctx : bs_ctx) : texpression =
- let ctx, var = fresh_var_for_symbolic_value sval ctx in
- let decl = A.GlobalDeclId.Map.find gid ctx.global_context.llbc_global_decls in
- let global_expr = { id = Global gid; type_args = [] } in
- (* We use translate_fwd_ty to translate the global type *)
- let ty = ctx_translate_fwd_ty ctx decl.ty in
- let gval = { e = Qualif global_expr; ty } in
- let e = translate_expression config e ctx in
- mk_let false (mk_typed_pattern_from_var var None) gval e
-
-and translate_expansion (config : config) (p : S.mplace option)
- (sv : V.symbolic_value) (exp : S.expansion) (ctx : bs_ctx) : texpression =
- (* Translate the scrutinee *)
- let scrutinee_var = lookup_var_for_symbolic_value sv ctx in
- let scrutinee = mk_texpression_from_var scrutinee_var in
- let scrutinee_mplace = translate_opt_mplace p in
- (* Translate the branches *)
- match exp with
- | ExpandNoBranch (sexp, e) -> (
- match sexp with
- | V.SeConcrete _ ->
- (* Actually, we don't *register* symbolic expansions to constant
- * values in the symbolic ADT *)
- raise (Failure "Unreachable")
- | SeMutRef (_, nsv) | SeSharedRef (_, nsv) ->
- (* The (mut/shared) borrow type is extracted to identity: we thus simply
- * introduce an reassignment *)
- let ctx, var = fresh_var_for_symbolic_value nsv ctx in
- let next_e = translate_expression config e ctx in
- let monadic = false in
- mk_let monadic
- (mk_typed_pattern_from_var var None)
- (mk_opt_mplace_texpression scrutinee_mplace scrutinee)
- next_e
- | SeAdt _ ->
- (* Should be in the [ExpandAdt] case *)
- raise (Failure "Unreachable"))
- | ExpandAdt branches -> (
- (* We don't do the same thing if there is a branching or not *)
- match branches with
- | [] -> raise (Failure "Unreachable")
- | [ (variant_id, svl, branch) ] -> (
- (* There is exactly one branch: no branching *)
- let type_id, _, _ = TypesUtils.ty_as_adt sv.V.sv_ty in
- let ctx, vars = fresh_vars_for_symbolic_values svl ctx in
- let branch = translate_expression config branch ctx in
- match type_id with
- | T.AdtId adt_id ->
- (* Detect if this is an enumeration or not *)
- let tdef = bs_ctx_lookup_llbc_type_decl adt_id ctx in
- let is_enum = type_decl_is_enum tdef in
- if is_enum then
- (* This is an enumeration: introduce an [ExpandEnum] let-binding *)
- let variant_id = Option.get variant_id in
- let lvars =
- List.map (fun v -> mk_typed_pattern_from_var v None) vars
- in
- let lv = mk_adt_pattern scrutinee.ty variant_id lvars in
- let monadic = false in
-
- mk_let monadic lv
- (mk_opt_mplace_texpression scrutinee_mplace scrutinee)
- branch
- else
- (* This is not an enumeration: introduce let-bindings for every
- * field.
- * We use the [dest] variable in order not to have to recompute
- * the type of the result of the projection... *)
- let adt_id, type_args =
- match scrutinee.ty with
- | Adt (adt_id, tys) -> (adt_id, tys)
- | _ -> raise (Failure "Unreachable")
- in
- let gen_field_proj (field_id : FieldId.id) (dest : var) :
- texpression =
- let proj_kind = { adt_id; field_id } in
- let qualif = { id = Proj proj_kind; type_args } in
- let proj_e = Qualif qualif in
- let proj_ty = mk_arrow scrutinee.ty dest.ty in
- let proj = { e = proj_e; ty = proj_ty } in
- mk_app proj scrutinee
- in
- let id_var_pairs = FieldId.mapi (fun fid v -> (fid, v)) vars in
- let monadic = false in
- List.fold_right
- (fun (fid, var) e ->
- let field_proj = gen_field_proj fid var in
- mk_let monadic
- (mk_typed_pattern_from_var var None)
- field_proj e)
- id_var_pairs branch
- | T.Tuple ->
- let vars =
- List.map (fun x -> mk_typed_pattern_from_var x None) vars
- in
- let monadic = false in
- mk_let monadic
- (mk_simpl_tuple_pattern vars)
- (mk_opt_mplace_texpression scrutinee_mplace scrutinee)
- branch
- | T.Assumed T.Box ->
- (* There should be exactly one variable *)
- let var =
- match vars with
- | [ v ] -> v
- | _ -> raise (Failure "Unreachable")
- in
- (* We simply introduce an assignment - the box type is the
- * identity when extracted ([box a == a]) *)
- let monadic = false in
- mk_let monadic
- (mk_typed_pattern_from_var var None)
- (mk_opt_mplace_texpression scrutinee_mplace scrutinee)
- branch
- | T.Assumed T.Vec ->
- (* We can't expand vector values: we can access the fields only
- * through the functions provided by the API (note that we don't
- * know how to expand a vector, because it has a variable number
- * of fields!) *)
- failwith "Can't expand a vector value"
- | T.Assumed T.Option ->
- (* We shouldn't get there in the "one-branch" case: options have
- * two variants *)
- raise (Failure "Unreachable"))
- | branches ->
- let translate_branch (variant_id : T.VariantId.id option)
- (svl : V.symbolic_value list) (branch : S.expression) :
- match_branch =
- (* There *must* be a variant id - otherwise there can't be several branches *)
- let variant_id = Option.get variant_id in
- let ctx, vars = fresh_vars_for_symbolic_values svl ctx in
- let vars =
- List.map (fun x -> mk_typed_pattern_from_var x None) vars
- in
- let pat_ty = scrutinee.ty in
- let pat = mk_adt_pattern pat_ty variant_id vars in
- let branch = translate_expression config branch ctx in
- { pat; branch }
- in
- let branches =
- List.map (fun (vid, svl, e) -> translate_branch vid svl e) branches
- in
- let e =
- Switch
- ( mk_opt_mplace_texpression scrutinee_mplace scrutinee,
- Match branches )
- in
- (* There should be at least one branch *)
- let branch = List.hd branches in
- let ty = branch.branch.ty in
- (* Sanity check *)
- assert (List.for_all (fun br -> br.branch.ty = ty) branches);
- (* Return *)
- { e; ty })
- | ExpandBool (true_e, false_e) ->
- (* We don't need to update the context: we don't introduce any
- * new values/variables *)
- let true_e = translate_expression config true_e ctx in
- let false_e = translate_expression config false_e ctx in
- let e =
- Switch
- ( mk_opt_mplace_texpression scrutinee_mplace scrutinee,
- If (true_e, false_e) )
- in
- let ty = true_e.ty in
- assert (ty = false_e.ty);
- { e; ty }
- | ExpandInt (int_ty, branches, otherwise) ->
- let translate_branch ((v, branch_e) : V.scalar_value * S.expression) :
- match_branch =
- (* We don't need to update the context: we don't introduce any
- * new values/variables *)
- let branch = translate_expression config branch_e ctx in
- let pat = mk_typed_pattern_from_constant_value (V.Scalar v) in
- { pat; branch }
- in
- let branches = List.map translate_branch branches in
- let otherwise = translate_expression config otherwise ctx in
- let pat_ty = Integer int_ty in
- let otherwise_pat : typed_pattern = { value = PatDummy; ty = pat_ty } in
- let otherwise : match_branch =
- { pat = otherwise_pat; branch = otherwise }
- in
- let all_branches = List.append branches [ otherwise ] in
- let e =
- Switch
- ( mk_opt_mplace_texpression scrutinee_mplace scrutinee,
- Match all_branches )
- in
- let ty = otherwise.branch.ty in
- assert (
- List.for_all (fun (br : match_branch) -> br.branch.ty = ty) branches);
- { e; ty }
-
-and translate_meta (config : config) (meta : S.meta) (e : S.expression)
- (ctx : bs_ctx) : texpression =
- let next_e = translate_expression config e ctx in
- let meta =
- match meta with
- | S.Assignment (lp, rv, rp) ->
- let lp = translate_mplace lp in
- let rv = typed_value_to_texpression ctx rv in
- let rp = translate_opt_mplace rp in
- Assignment (lp, rv, rp)
- in
- let e = Meta (meta, next_e) in
- let ty = next_e.ty in
- { e; ty }
-
-let translate_fun_decl (config : config) (ctx : bs_ctx)
- (body : S.expression option) : fun_decl =
- (* Translate *)
- let def = ctx.fun_decl in
- let bid = ctx.bid in
- log#ldebug
- (lazy
- ("SymbolicToPure.translate_fun_decl: "
- ^ Print.fun_name_to_string def.A.name
- ^ " ("
- ^ Print.option_to_string T.RegionGroupId.to_string bid
- ^ ")"));
-
- (* Translate the declaration *)
- let def_id = def.A.def_id in
- let basename = def.name in
- (* Lookup the signature *)
- let signature = bs_ctx_lookup_local_function_sig def_id bid ctx in
- (* Translate the body, if there is *)
- let body =
- match body with
- | None -> None
- | Some body ->
- let body = translate_expression config body ctx in
- (* Sanity check *)
- type_check_texpression ctx body;
- (* Introduce the input state, if necessary *)
- let effect_info =
- get_fun_effect_info ctx.fun_context.fun_infos (Regular def_id) bid
- in
- let input_state =
- if effect_info.input_state then
- [
- {
- id = ctx.state_var;
- basename = Some ConstStrings.state_basename;
- ty = mk_state_ty;
- };
- ]
- else []
- in
- (* Compute the list of (properly ordered) input variables *)
- let backward_inputs : var list =
- match bid with
- | None -> []
- | Some back_id ->
- let parents_ids =
- list_ordered_parent_region_groups def.signature back_id
- in
- let backward_ids = List.append parents_ids [ back_id ] in
- List.concat
- (List.map
- (fun id -> T.RegionGroupId.Map.find id ctx.backward_inputs)
- backward_ids)
- in
- let inputs =
- List.concat [ ctx.forward_inputs; input_state; backward_inputs ]
- in
- let inputs_lvs =
- List.map (fun v -> mk_typed_pattern_from_var v None) inputs
- in
- (* Sanity check *)
- log#ldebug
- (lazy
- ("SymbolicToPure.translate_fun_decl:" ^ "\n- forward_inputs: "
- ^ String.concat ", " (List.map show_var ctx.forward_inputs)
- ^ "\n- input_state: "
- ^ String.concat ", " (List.map show_var input_state)
- ^ "\n- backward_inputs: "
- ^ String.concat ", " (List.map show_var backward_inputs)
- ^ "\n- signature.inputs: "
- ^ String.concat ", " (List.map show_ty signature.inputs)));
- assert (
- List.for_all
- (fun (var, ty) -> (var : var).ty = ty)
- (List.combine inputs signature.inputs));
- Some { inputs; inputs_lvs; body }
- in
- (* Assemble the declaration *)
- let def =
- {
- def_id;
- back_id = bid;
- basename;
- signature;
- is_global_decl_body = def.is_global_decl_body;
- body;
- }
- in
- (* Debugging *)
- log#ldebug
- (lazy
- ("SymbolicToPure.translate_fun_decl: translated:\n"
- ^ fun_decl_to_string ctx def));
- (* return *)
- def
-
-let translate_type_decls (type_decls : T.type_decl list) : type_decl list =
- List.map translate_type_decl type_decls
-
-(** Translates function signatures.
-
- Takes as input a list of function information containing:
- - the function id
- - a list of optional names for the inputs
- - the function signature
-
- Returns a map from forward/backward functions identifiers to:
- - translated function signatures
- - optional names for the outputs values (we derive them for the backward
- functions)
- *)
-let translate_fun_signatures (fun_infos : FA.fun_info A.FunDeclId.Map.t)
- (types_infos : TA.type_infos)
- (functions : (A.fun_id * string option list * A.fun_sig) list) :
- fun_sig_named_outputs RegularFunIdMap.t =
- (* For every function, translate the signatures of:
- - the forward function
- - the backward functions
- *)
- let translate_one (fun_id : A.fun_id) (input_names : string option list)
- (sg : A.fun_sig) : (regular_fun_id * fun_sig_named_outputs) list =
- (* The forward function *)
- let fwd_sg =
- translate_fun_sig fun_infos fun_id types_infos sg input_names None
- in
- let fwd_id = (fun_id, None) in
- (* The backward functions *)
- let back_sgs =
- List.map
- (fun (rg : T.region_var_group) ->
- let tsg =
- translate_fun_sig fun_infos fun_id types_infos sg input_names
- (Some rg.id)
- in
- let id = (fun_id, Some rg.id) in
- (id, tsg))
- sg.regions_hierarchy
- in
- (* Return *)
- (fwd_id, fwd_sg) :: back_sgs
- in
- let translated =
- List.concat
- (List.map (fun (id, names, sg) -> translate_one id names sg) functions)
- in
- List.fold_left
- (fun m (id, sg) -> RegularFunIdMap.add id sg m)
- RegularFunIdMap.empty translated
diff --git a/src/SynthesizeSymbolic.ml b/src/SynthesizeSymbolic.ml
deleted file mode 100644
index a2256bdd..00000000
--- a/src/SynthesizeSymbolic.ml
+++ /dev/null
@@ -1,156 +0,0 @@
-module C = Collections
-module T = Types
-module V = Values
-module E = Expressions
-module A = LlbcAst
-open SymbolicAst
-
-let mk_mplace (p : E.place) (ctx : Contexts.eval_ctx) : mplace =
- let bv = Contexts.ctx_lookup_binder ctx p.var_id in
- { bv; projection = p.projection }
-
-let mk_opt_mplace (p : E.place option) (ctx : Contexts.eval_ctx) : mplace option
- =
- match p with None -> None | Some p -> Some (mk_mplace p ctx)
-
-let mk_opt_place_from_op (op : E.operand) (ctx : Contexts.eval_ctx) :
- mplace option =
- match op with
- | E.Copy p | E.Move p -> Some (mk_mplace p ctx)
- | E.Constant _ -> None
-
-let synthesize_symbolic_expansion (sv : V.symbolic_value)
- (place : mplace option) (seel : V.symbolic_expansion option list)
- (exprl : expression list option) : expression option =
- match exprl with
- | None -> None
- | Some exprl ->
- let ls = List.combine seel exprl in
- (* Match on the symbolic value type to know which can of expansion happened *)
- let expansion =
- match sv.V.sv_ty with
- | T.Bool -> (
- (* Boolean expansion: there should be two branches *)
- match ls with
- | [
- (Some (V.SeConcrete (V.Bool true)), true_exp);
- (Some (V.SeConcrete (V.Bool false)), false_exp);
- ] ->
- ExpandBool (true_exp, false_exp)
- | _ -> failwith "Ill-formed boolean expansion")
- | T.Integer int_ty ->
- (* Switch over an integer: split between the "regular" branches
- and the "otherwise" branch (which should be the last branch) *)
- let branches, otherwise = C.List.pop_last ls in
- (* For all the regular branches, the symbolic value should have
- * been expanded to a constant *)
- let get_scalar (see : V.symbolic_expansion option) : V.scalar_value
- =
- match see with
- | Some (V.SeConcrete (V.Scalar cv)) ->
- assert (cv.V.int_ty = int_ty);
- cv
- | _ -> failwith "Unreachable"
- in
- let branches =
- List.map (fun (see, exp) -> (get_scalar see, exp)) branches
- in
- (* For the otherwise branch, the symbolic value should have been left
- * unchanged *)
- let otherwise_see, otherwise = otherwise in
- assert (otherwise_see = None);
- (* Return *)
- ExpandInt (int_ty, branches, otherwise)
- | T.Adt (_, _, _) ->
- (* Branching: it is necessarily an enumeration expansion *)
- let get_variant (see : V.symbolic_expansion option) :
- T.VariantId.id option * V.symbolic_value list =
- match see with
- | Some (V.SeAdt (vid, fields)) -> (vid, fields)
- | _ -> failwith "Ill-formed branching ADT expansion"
- in
- let exp =
- List.map
- (fun (see, exp) ->
- let vid, fields = get_variant see in
- (vid, fields, exp))
- ls
- in
- ExpandAdt exp
- | T.Ref (_, _, _) -> (
- (* Reference expansion: there should be one branch *)
- match ls with
- | [ (Some see, exp) ] -> ExpandNoBranch (see, exp)
- | _ -> failwith "Ill-formed borrow expansion")
- | T.TypeVar _ | Char | Never | Str | Array _ | Slice _ ->
- failwith "Ill-formed symbolic expansion"
- in
- Some (Expansion (place, sv, expansion))
-
-let synthesize_symbolic_expansion_no_branching (sv : V.symbolic_value)
- (place : mplace option) (see : V.symbolic_expansion)
- (expr : expression option) : expression option =
- let exprl = match expr with None -> None | Some expr -> Some [ expr ] in
- synthesize_symbolic_expansion sv place [ Some see ] exprl
-
-let synthesize_function_call (call_id : call_id)
- (abstractions : V.AbstractionId.id list) (type_params : T.ety list)
- (args : V.typed_value list) (args_places : mplace option list)
- (dest : V.symbolic_value) (dest_place : mplace option)
- (expr : expression option) : expression option =
- match expr with
- | None -> None
- | Some expr ->
- let call =
- {
- call_id;
- abstractions;
- type_params;
- args;
- dest;
- args_places;
- dest_place;
- }
- in
- Some (FunCall (call, expr))
-
-let synthesize_global_eval (gid : A.GlobalDeclId.id) (dest : V.symbolic_value)
- (expr : expression option) : expression option =
- match expr with None -> None | Some e -> Some (EvalGlobal (gid, dest, e))
-
-let synthesize_regular_function_call (fun_id : A.fun_id)
- (call_id : V.FunCallId.id) (abstractions : V.AbstractionId.id list)
- (type_params : T.ety list) (args : V.typed_value list)
- (args_places : mplace option list) (dest : V.symbolic_value)
- (dest_place : mplace option) (expr : expression option) : expression option
- =
- synthesize_function_call
- (Fun (fun_id, call_id))
- abstractions type_params args args_places dest dest_place expr
-
-let synthesize_unary_op (unop : E.unop) (arg : V.typed_value)
- (arg_place : mplace option) (dest : V.symbolic_value)
- (dest_place : mplace option) (expr : expression option) : expression option
- =
- synthesize_function_call (Unop unop) [] [] [ arg ] [ arg_place ] dest
- dest_place expr
-
-let synthesize_binary_op (binop : E.binop) (arg0 : V.typed_value)
- (arg0_place : mplace option) (arg1 : V.typed_value)
- (arg1_place : mplace option) (dest : V.symbolic_value)
- (dest_place : mplace option) (expr : expression option) : expression option
- =
- synthesize_function_call (Binop binop) [] [] [ arg0; arg1 ]
- [ arg0_place; arg1_place ] dest dest_place expr
-
-let synthesize_end_abstraction (abs : V.abs) (expr : expression option) :
- expression option =
- match expr with
- | None -> None
- | Some expr -> Some (EndAbstraction (abs, expr))
-
-let synthesize_assignment (lplace : mplace) (rvalue : V.typed_value)
- (rplace : mplace option) (expr : expression option) : expression option =
- match expr with
- | None -> None
- | Some expr -> Some (Meta (Assignment (lplace, rvalue, rplace), expr))
diff --git a/src/Translate.ml b/src/Translate.ml
deleted file mode 100644
index 8f3b94c4..00000000
--- a/src/Translate.ml
+++ /dev/null
@@ -1,871 +0,0 @@
-open InterpreterStatements
-open Interpreter
-module L = Logging
-module T = Types
-module A = LlbcAst
-module SA = SymbolicAst
-module Micro = PureMicroPasses
-open PureUtils
-open TranslateCore
-
-(** The local logger *)
-let log = TranslateCore.log
-
-type config = {
- eval_config : Contexts.partial_config;
- mp_config : Micro.config;
- use_state : bool;
- (** Controls whether we need to use a state to model the external world
- (I/O, for instance).
- *)
- split_files : bool;
- (** Controls whether we split the generated definitions between different
- files for the types, clauses and functions, or if we group them in
- one file.
- *)
- test_unit_functions : bool;
- (** If true, insert tests in the generated files to check that the
- unit functions normalize to [Success _].
-
- For instance, in F* it generates code like this:
- {[
- let _ = assert_norm (FUNCTION () = Success ())
- ]}
- *)
- extract_decreases_clauses : bool;
- (** If [true], insert [decreases] clauses for all the recursive definitions.
-
- The body of such clauses must be defined by the user.
- *)
- extract_template_decreases_clauses : bool;
- (** In order to help the user, we can generate "template" decrease clauses
- (i.e., definitions with proper signatures but dummy bodies) in a
- dedicated file.
- *)
-}
-
-(** The result of running the symbolic interpreter on a function:
- - the list of symbolic values used for the input values
- - the generated symbolic AST
-*)
-type symbolic_fun_translation = V.symbolic_value list * SA.expression
-
-(** Execute the symbolic interpreter on a function to generate a list of symbolic ASTs,
- for the forward function and the backward functions.
-*)
-let translate_function_to_symbolics (config : C.partial_config)
- (trans_ctx : trans_ctx) (fdef : A.fun_decl) :
- (symbolic_fun_translation * symbolic_fun_translation list) option =
- (* Debug *)
- log#ldebug
- (lazy
- ("translate_function_to_symbolics: "
- ^ Print.fun_name_to_string fdef.A.name));
-
- let { type_context; fun_context; global_context } = trans_ctx in
- let fun_context = { C.fun_decls = fun_context.fun_decls } in
-
- match fdef.body with
- | None -> None
- | Some _ ->
- (* Evaluate *)
- let synthesize = true in
- let evaluate gid =
- let inputs, symb =
- evaluate_function_symbolic config synthesize type_context fun_context
- global_context fdef gid
- in
- (inputs, Option.get symb)
- in
- (* Execute the forward function *)
- let forward = evaluate None in
- (* Execute the backward functions *)
- let backwards =
- T.RegionGroupId.mapi
- (fun gid _ -> evaluate (Some gid))
- fdef.signature.regions_hierarchy
- in
-
- (* Return *)
- Some (forward, backwards)
-
-(** Translate a function, by generating its forward and backward translations.
-
- [fun_sigs]: maps the forward/backward functions to their signatures. In case
- of backward functions, we also provide names for the outputs.
- TODO: maybe we should introduce a record for this.
-*)
-let translate_function_to_pure (config : C.partial_config)
- (mp_config : Micro.config) (trans_ctx : trans_ctx)
- (fun_sigs : SymbolicToPure.fun_sig_named_outputs RegularFunIdMap.t)
- (pure_type_decls : Pure.type_decl Pure.TypeDeclId.Map.t) (fdef : A.fun_decl)
- : pure_fun_translation =
- (* Debug *)
- log#ldebug
- (lazy
- ("translate_function_to_pure: " ^ Print.fun_name_to_string fdef.A.name));
-
- let { type_context; fun_context; global_context } = trans_ctx in
- let def_id = fdef.def_id in
-
- (* Compute the symbolic ASTs, if the function is transparent *)
- let symbolic_trans = translate_function_to_symbolics config trans_ctx fdef in
- let symbolic_forward, symbolic_backwards =
- match symbolic_trans with
- | None -> (None, None)
- | Some (fwd, backs) -> (Some fwd, Some backs)
- in
-
- (* Convert the symbolic ASTs to pure ASTs: *)
-
- (* Initialize the context *)
- let forward_sig = RegularFunIdMap.find (A.Regular def_id, None) fun_sigs in
- let sv_to_var = V.SymbolicValueId.Map.empty in
- let var_counter = Pure.VarId.generator_zero in
- let state_var, var_counter = Pure.VarId.fresh var_counter in
- let calls = V.FunCallId.Map.empty in
- let abstractions = V.AbstractionId.Map.empty in
- let type_context =
- {
- SymbolicToPure.types_infos = type_context.type_infos;
- llbc_type_decls = type_context.type_decls;
- type_decls = pure_type_decls;
- }
- in
- let fun_context =
- {
- SymbolicToPure.llbc_fun_decls = fun_context.fun_decls;
- fun_sigs;
- fun_infos = fun_context.fun_infos;
- }
- in
- let global_context =
- { SymbolicToPure.llbc_global_decls = global_context.global_decls }
- in
- let ctx =
- {
- SymbolicToPure.bid = None;
- (* Dummy for now *)
- sg = forward_sig.sg;
- (* Will need to be updated for the backward functions *)
- sv_to_var;
- var_counter;
- state_var;
- type_context;
- fun_context;
- global_context;
- fun_decl = fdef;
- forward_inputs = [];
- (* Empty for now *)
- backward_inputs = T.RegionGroupId.Map.empty;
- (* Empty for now *)
- backward_outputs = T.RegionGroupId.Map.empty;
- (* Empty for now *)
- calls;
- abstractions;
- }
- in
-
- (* We need to initialize the input/output variables *)
- let num_forward_inputs = List.length fdef.signature.inputs in
- let add_forward_inputs input_svs ctx =
- match fdef.body with
- | None -> ctx
- | Some body ->
- let forward_input_vars = LlbcAstUtils.fun_body_get_input_vars body in
- let forward_input_varnames =
- List.map (fun (v : A.var) -> v.name) forward_input_vars
- in
- let input_svs = List.combine forward_input_varnames input_svs in
- let ctx, forward_inputs =
- SymbolicToPure.fresh_named_vars_for_symbolic_values input_svs ctx
- in
- { ctx with forward_inputs }
- in
-
- (* The symbolic to pure config *)
- let sp_config =
- {
- SymbolicToPure.filter_useless_back_calls =
- mp_config.filter_useless_monadic_calls;
- }
- in
-
- (* Translate the forward function *)
- let pure_forward =
- match symbolic_forward with
- | None -> SymbolicToPure.translate_fun_decl sp_config ctx None
- | Some (fwd_svs, fwd_ast) ->
- SymbolicToPure.translate_fun_decl sp_config
- (add_forward_inputs fwd_svs ctx)
- (Some fwd_ast)
- in
-
- (* Translate the backward functions *)
- let translate_backward (rg : T.region_var_group) : Pure.fun_decl =
- (* For the backward inputs/outputs initialization: we use the fact that
- * there are no nested borrows for now, and so that the region groups
- * can't have parents *)
- assert (rg.parents = []);
- let back_id = rg.id in
-
- match symbolic_backwards with
- | None ->
- (* Initialize the context - note that the ret_ty is not really
- * useful as we don't translate a body *)
- let backward_sg =
- RegularFunIdMap.find (A.Regular def_id, Some back_id) fun_sigs
- in
- let ctx = { ctx with bid = Some back_id; sg = backward_sg.sg } in
-
- (* Translate *)
- SymbolicToPure.translate_fun_decl sp_config ctx None
- | Some symbolic_backwards ->
- let input_svs, symbolic =
- T.RegionGroupId.nth symbolic_backwards back_id
- in
- let ctx = add_forward_inputs input_svs ctx in
- (* TODO: the computation of the backward inputs is a bit awckward... *)
- let backward_sg =
- RegularFunIdMap.find (A.Regular def_id, Some back_id) fun_sigs
- in
- (* We need to ignore the forward inputs, and the state input (if there is) *)
- let fun_info =
- SymbolicToPure.get_fun_effect_info fun_context.fun_infos
- (A.Regular def_id) (Some back_id)
- in
- let _, backward_inputs =
- Collections.List.split_at backward_sg.sg.inputs
- (num_forward_inputs + if fun_info.input_state then 1 else 0)
- in
- (* As we forbid nested borrows, the additional inputs for the backward
- * functions come from the borrows in the return value of the rust function:
- * we thus use the name "ret" for those inputs *)
- let backward_inputs =
- List.map (fun ty -> (Some "ret", ty)) backward_inputs
- in
- let ctx, backward_inputs =
- SymbolicToPure.fresh_vars backward_inputs ctx
- in
- (* The outputs for the backward functions, however, come from borrows
- * present in the input values of the rust function: for those we reuse
- * the names of the input values. *)
- let backward_outputs =
- List.combine backward_sg.output_names backward_sg.sg.doutputs
- in
- let ctx, backward_outputs =
- SymbolicToPure.fresh_vars backward_outputs ctx
- in
- let backward_inputs =
- T.RegionGroupId.Map.singleton back_id backward_inputs
- in
- let backward_outputs =
- T.RegionGroupId.Map.singleton back_id backward_outputs
- in
-
- (* Put everything in the context *)
- let ctx =
- {
- ctx with
- bid = Some back_id;
- sg = backward_sg.sg;
- backward_inputs;
- backward_outputs;
- }
- in
-
- (* Translate *)
- SymbolicToPure.translate_fun_decl sp_config ctx (Some symbolic)
- in
- let pure_backwards =
- List.map translate_backward fdef.signature.regions_hierarchy
- in
-
- (* Return *)
- (pure_forward, pure_backwards)
-
-let translate_module_to_pure (config : C.partial_config)
- (mp_config : Micro.config) (use_state : bool) (crate : Crates.llbc_crate) :
- trans_ctx * Pure.type_decl list * (bool * pure_fun_translation) list =
- (* Debug *)
- log#ldebug (lazy "translate_module_to_pure");
-
- (* Compute the type and function contexts *)
- let type_context, fun_context, global_context =
- compute_type_fun_global_contexts crate
- in
- let fun_infos =
- FA.analyze_module crate fun_context.C.fun_decls
- global_context.C.global_decls use_state
- in
- let fun_context = { fun_decls = fun_context.fun_decls; fun_infos } in
- let trans_ctx = { type_context; fun_context; global_context } in
-
- (* Translate all the type definitions *)
- let type_decls = SymbolicToPure.translate_type_decls crate.types in
-
- (* Compute the type definition map *)
- let type_decls_map =
- Pure.TypeDeclId.Map.of_list
- (List.map (fun (def : Pure.type_decl) -> (def.def_id, def)) type_decls)
- in
-
- (* Translate all the function *signatures* *)
- let assumed_sigs =
- List.map
- (fun (id, sg, _, _) ->
- (A.Assumed id, List.map (fun _ -> None) (sg : A.fun_sig).inputs, sg))
- Assumed.assumed_infos
- in
- let local_sigs =
- List.map
- (fun (fdef : A.fun_decl) ->
- let input_names =
- match fdef.body with
- | None -> List.map (fun _ -> None) fdef.signature.inputs
- | Some body ->
- List.map
- (fun (v : A.var) -> v.name)
- (LlbcAstUtils.fun_body_get_input_vars body)
- in
- (A.Regular fdef.def_id, input_names, fdef.signature))
- crate.functions
- in
- let sigs = List.append assumed_sigs local_sigs in
- let fun_sigs =
- SymbolicToPure.translate_fun_signatures fun_context.fun_infos
- type_context.type_infos sigs
- in
-
- (* Translate all the *transparent* functions *)
- let pure_translations =
- List.map
- (translate_function_to_pure config mp_config trans_ctx fun_sigs
- type_decls_map)
- crate.functions
- in
-
- (* Apply the micro-passes *)
- let pure_translations =
- List.map
- (Micro.apply_passes_to_pure_fun_translation mp_config trans_ctx)
- pure_translations
- in
-
- (* Return *)
- (trans_ctx, type_decls, pure_translations)
-
-(** Extraction context *)
-type gen_ctx = {
- crate : Crates.llbc_crate;
- extract_ctx : PureToExtract.extraction_ctx;
- trans_types : Pure.type_decl Pure.TypeDeclId.Map.t;
- trans_funs : (bool * pure_fun_translation) A.FunDeclId.Map.t;
- functions_with_decreases_clause : A.FunDeclId.Set.t;
-}
-
-type gen_config = {
- mp_config : Micro.config;
- use_state : bool;
- extract_types : bool;
- extract_decreases_clauses : bool;
- extract_template_decreases_clauses : bool;
- extract_fun_decls : bool;
- extract_transparent : bool;
- (** If [true], extract the transparent declarations, otherwise ignore. *)
- extract_opaque : bool;
- (** If [true], extract the opaque declarations, otherwise ignore. *)
- extract_state_type : bool;
- (** If [true], generate a definition/declaration for the state type *)
- interface : bool;
- (** [true] if we generate an interface file, [false] otherwise.
- For now, this only impacts whether we use [val] or [assume val] for the
- opaque definitions. In the future, we might want to extract all the
- declarations in an interface file, together with an implementation file
- if needed.
- *)
- test_unit_functions : bool;
-}
-
-(** Returns the pair: (has opaque type decls, has opaque fun decls) *)
-let module_has_opaque_decls (ctx : gen_ctx) : bool * bool =
- let has_opaque_types =
- Pure.TypeDeclId.Map.exists
- (fun _ (d : Pure.type_decl) ->
- match d.kind with Opaque -> true | _ -> false)
- ctx.trans_types
- in
- let has_opaque_funs =
- A.FunDeclId.Map.exists
- (fun _ ((_, (t_fwd, _)) : bool * pure_fun_translation) ->
- Option.is_none t_fwd.body)
- ctx.trans_funs
- in
- (has_opaque_types, has_opaque_funs)
-
-(** A generic utility to generate the extracted definitions: as we may want to
- split the definitions between different files (or not), we can control
- what is precisely extracted.
- *)
-let extract_definitions (fmt : Format.formatter) (config : gen_config)
- (ctx : gen_ctx) : unit =
- (* Export the definition groups to the file, in the proper order *)
- let export_type (qualif : ExtractToFStar.type_decl_qualif)
- (id : Pure.TypeDeclId.id) : unit =
- (* Retrive the declaration *)
- let def = Pure.TypeDeclId.Map.find id ctx.trans_types in
- (* Update the qualifier, if the type is opaque *)
- let is_opaque, qualif =
- match def.kind with
- | Enum _ | Struct _ -> (false, qualif)
- | Opaque ->
- let qualif =
- if config.interface then ExtractToFStar.TypeVal
- else ExtractToFStar.AssumeType
- in
- (true, qualif)
- in
- (* Extract, if the config instructs to do so (depending on whether the type
- * is opaque or not) *)
- if
- (is_opaque && config.extract_opaque)
- || ((not is_opaque) && config.extract_transparent)
- then ExtractToFStar.extract_type_decl ctx.extract_ctx fmt qualif def
- in
-
- (* Utility to check a function has a decrease clause *)
- let has_decreases_clause (def : Pure.fun_decl) : bool =
- A.FunDeclId.Set.mem def.def_id ctx.functions_with_decreases_clause
- in
-
- (* In case of (non-mutually) recursive functions, we use a simple procedure to
- * check if the forward and backward functions are mutually recursive.
- *)
- let export_functions (is_rec : bool)
- (pure_ls : (bool * pure_fun_translation) list) : unit =
- (* Concatenate the function definitions, filtering the useless forward
- * functions. We also make pairs: (forward function, backward function)
- * (the forward function contains useful information that we want to keep) *)
- let fls =
- List.concat
- (List.map
- (fun (keep_fwd, (fwd, back_ls)) ->
- let back_ls = List.map (fun back -> (fwd, back)) back_ls in
- if keep_fwd then (fwd, fwd) :: back_ls else back_ls)
- pure_ls)
- in
- (* Extract the decrease clauses template bodies *)
- if config.extract_template_decreases_clauses then
- List.iter
- (fun (_, (fwd, _)) ->
- let has_decr_clause = has_decreases_clause fwd in
- if has_decr_clause then
- ExtractToFStar.extract_template_decreases_clause ctx.extract_ctx fmt
- fwd)
- pure_ls;
- (* Extract the function definitions *)
- (if config.extract_fun_decls then
- (* Check if the functions are mutually recursive - this really works
- * to check if the forward and backward translations of a single
- * recursive function are mutually recursive *)
- let is_mut_rec =
- if is_rec then
- if List.length pure_ls <= 1 then
- not (PureUtils.functions_not_mutually_recursive (List.map fst fls))
- else true
- else false
- in
- List.iteri
- (fun i (fwd_def, def) ->
- let is_opaque = Option.is_none fwd_def.Pure.body in
- let qualif =
- if is_opaque then
- if config.interface then ExtractToFStar.Val
- else ExtractToFStar.AssumeVal
- else if not is_rec then ExtractToFStar.Let
- else if is_mut_rec then
- if i = 0 then ExtractToFStar.LetRec else ExtractToFStar.And
- else ExtractToFStar.LetRec
- in
- let has_decr_clause =
- has_decreases_clause def && config.extract_decreases_clauses
- in
- (* Check if the definition needs to be filtered or not *)
- if
- ((not is_opaque) && config.extract_transparent)
- || (is_opaque && config.extract_opaque)
- then
- ExtractToFStar.extract_fun_decl ctx.extract_ctx fmt qualif
- has_decr_clause def)
- fls);
- (* Insert unit tests if necessary *)
- if config.test_unit_functions then
- List.iter
- (fun (keep_fwd, (fwd, _)) ->
- if keep_fwd then
- ExtractToFStar.extract_unit_test_if_unit_fun ctx.extract_ctx fmt fwd)
- pure_ls
- in
-
- (* TODO: Check correct behaviour with opaque globals *)
- let export_global (id : A.GlobalDeclId.id) : unit =
- let global_decls = ctx.extract_ctx.trans_ctx.global_context.global_decls in
- let global = A.GlobalDeclId.Map.find id global_decls in
- let _, (body, body_backs) =
- A.FunDeclId.Map.find global.body_id ctx.trans_funs
- in
- assert (List.length body_backs = 0);
-
- let is_opaque = Option.is_none body.Pure.body in
- if
- ((not is_opaque) && config.extract_transparent)
- || (is_opaque && config.extract_opaque)
- then
- ExtractToFStar.extract_global_decl ctx.extract_ctx fmt global body
- config.interface
- in
-
- let export_state_type () : unit =
- let qualif =
- if config.interface then ExtractToFStar.TypeVal
- else ExtractToFStar.AssumeType
- in
- ExtractToFStar.extract_state_type fmt ctx.extract_ctx qualif
- in
-
- let export_decl (decl : Crates.declaration_group) : unit =
- match decl with
- | Type (NonRec id) ->
- if config.extract_types then export_type ExtractToFStar.Type id
- | Type (Rec ids) ->
- (* Rk.: we shouldn't have (mutually) recursive opaque types *)
- if config.extract_types then
- List.iteri
- (fun i id ->
- let qualif =
- if i = 0 then ExtractToFStar.Type else ExtractToFStar.And
- in
- export_type qualif id)
- ids
- | Fun (NonRec id) ->
- (* Lookup *)
- let pure_fun = A.FunDeclId.Map.find id ctx.trans_funs in
- (* Translate *)
- export_functions false [ pure_fun ]
- | Fun (Rec ids) ->
- (* General case of mutually recursive functions *)
- (* Lookup *)
- let pure_funs =
- List.map (fun id -> A.FunDeclId.Map.find id ctx.trans_funs) ids
- in
- (* Translate *)
- export_functions true pure_funs
- | Global id -> export_global id
- in
-
- (* If we need to export the state type: we try to export it after we defined
- * the type definitions, because if the user wants to define a model for the
- * type, he might want to reuse them in the state type.
- * More specifically: if we extract functions, we have no choice but to define
- * the state type before the functions, because they may reuse this state
- * type: in this case, we define/declare it at the very beginning. Otherwise,
- * we define/declare it at the very end.
- *)
- if config.extract_state_type && config.extract_fun_decls then
- export_state_type ();
- List.iter export_decl ctx.crate.declarations;
- if config.extract_state_type && not config.extract_fun_decls then
- export_state_type ()
-
-let extract_file (config : gen_config) (ctx : gen_ctx) (filename : string)
- (rust_module_name : string) (module_name : string) (custom_msg : string)
- (custom_imports : string list) (custom_includes : string list) : unit =
- (* Open the file and create the formatter *)
- let out = open_out filename in
- let fmt = Format.formatter_of_out_channel out in
-
- (* Print the headers.
- * Note that we don't use the OCaml formatter for purpose: we want to control
- * line insertion (we have to make sure that some instructions like [open MODULE]
- * are printed on one line!).
- * This is ok as long as we end up with a line break, so that the formatter's
- * internal count is consistent with the state of the file.
- *)
- (* Create the header *)
- Printf.fprintf out "(** THIS FILE WAS AUTOMATICALLY GENERATED BY AENEAS *)\n";
- Printf.fprintf out "(** [%s]%s *)\n" rust_module_name custom_msg;
- Printf.fprintf out "module %s\n" module_name;
- Printf.fprintf out "open Primitives\n";
- (* Add the custom imports *)
- List.iter (fun m -> Printf.fprintf out "open %s\n" m) custom_imports;
- (* Add the custom includes *)
- List.iter (fun m -> Printf.fprintf out "include %s\n" m) custom_includes;
- (* Z3 options - note that we use fuel 1 because it its useful for the decrease clauses *)
- Printf.fprintf out "\n#set-options \"--z3rlimit 50 --fuel 1 --ifuel 1\"\n";
-
- (* From now onwards, we use the formatter *)
- (* Set the margin *)
- Format.pp_set_margin fmt 80;
-
- (* Create a vertical box *)
- Format.pp_open_vbox fmt 0;
-
- (* Extract the definitions *)
- extract_definitions fmt config ctx;
-
- (* Close the box and end the formatting *)
- Format.pp_close_box fmt ();
- Format.pp_print_newline fmt ();
-
- (* Some logging *)
- log#linfo (lazy ("Generated: " ^ filename));
-
- (* Flush and close the file *)
- close_out out
-
-(** Translate a module and write the synthesized code to an output file.
- TODO: rename to translate_crate
- *)
-let translate_module (filename : string) (dest_dir : string) (config : config)
- (crate : Crates.llbc_crate) : unit =
- (* Translate the module to the pure AST *)
- let trans_ctx, trans_types, trans_funs =
- translate_module_to_pure config.eval_config config.mp_config
- config.use_state crate
- in
-
- (* Initialize the extraction context - for now we extract only to F* *)
- let names_map =
- PureToExtract.initialize_names_map ExtractToFStar.fstar_names_map_init
- in
- let variant_concatenate_type_name = true in
- let fstar_fmt =
- ExtractToFStar.mk_formatter trans_ctx crate.name
- variant_concatenate_type_name
- in
- let ctx =
- { PureToExtract.trans_ctx; names_map; fmt = fstar_fmt; indent_incr = 2 }
- in
-
- (* We need to compute which functions are recursive, in order to know
- * whether we should generate a decrease clause or not. *)
- let rec_functions =
- A.FunDeclId.Set.of_list
- (List.concat
- (List.map
- (fun decl ->
- match decl with Crates.Fun (Rec ids) -> ids | _ -> [])
- crate.declarations))
- in
-
- (* Register unique names for all the top-level types, globals and functions.
- * Note that the order in which we generate the names doesn't matter:
- * we just need to generate a mapping from identifier to name, and make
- * sure there are no name clashes. *)
- let ctx =
- List.fold_left
- (fun ctx def -> ExtractToFStar.extract_type_decl_register_names ctx def)
- ctx trans_types
- in
-
- let ctx =
- List.fold_left
- (fun ctx (keep_fwd, def) ->
- (* We generate a decrease clause for all the recursive functions *)
- let gen_decr_clause =
- A.FunDeclId.Set.mem (fst def).Pure.def_id rec_functions
- in
- (* Register the names, only if the function is not a global body -
- * those are handled later *)
- let is_global = (fst def).Pure.is_global_decl_body in
- if is_global then ctx
- else
- ExtractToFStar.extract_fun_decl_register_names ctx keep_fwd
- gen_decr_clause def)
- ctx trans_funs
- in
-
- let ctx =
- List.fold_left ExtractToFStar.extract_global_decl_register_names ctx
- crate.globals
- in
-
- (* Open the output file *)
- (* First compute the filename by replacing the extension and converting the
- * case (rust module names are snake case) *)
- let module_name, extract_filebasename =
- match Filename.chop_suffix_opt ~suffix:".llbc" filename with
- | None ->
- (* Note that we already checked the suffix upon opening the file *)
- failwith "Unreachable"
- | Some filename ->
- (* Retrieve the file basename *)
- let basename = Filename.basename filename in
- (* Convert the case *)
- let module_name = StringUtils.to_camel_case basename in
- (* Concatenate *)
- (module_name, Filename.concat dest_dir module_name)
- in
-
- (* Put the translated definitions in maps *)
- let trans_types =
- Pure.TypeDeclId.Map.of_list
- (List.map (fun (d : Pure.type_decl) -> (d.def_id, d)) trans_types)
- in
- let trans_funs =
- A.FunDeclId.Map.of_list
- (List.map
- (fun ((keep_fwd, (fd, bdl)) : bool * pure_fun_translation) ->
- (fd.def_id, (keep_fwd, (fd, bdl))))
- trans_funs)
- in
-
- (* Create the directory, if necessary *)
- if not (Sys.file_exists dest_dir) then (
- log#linfo (lazy ("Creating missing directory: " ^ dest_dir));
- (* Create a directory with *default* permissions *)
- Core_unix.mkdir_p dest_dir);
-
- (* Copy "Primitives.fst" - I couldn't find a "cp" function in the OCaml
- * libraries... *)
- let _ =
- let src = open_in "fstar/Primitives.fst" in
- let tgt_filename = Filename.concat dest_dir "Primitives.fst" in
- let tgt = open_out tgt_filename in
- try
- while true do
- (* We copy line by line *)
- let line = input_line src in
- Printf.fprintf tgt "%s\n" line
- done
- with End_of_file ->
- close_in src;
- close_out tgt;
- log#linfo (lazy ("Copied: " ^ tgt_filename))
- in
-
- (* Extract the file(s) *)
- let gen_ctx =
- {
- crate;
- extract_ctx = ctx;
- trans_types;
- trans_funs;
- functions_with_decreases_clause = rec_functions;
- }
- in
-
- let use_state = config.use_state in
-
- (* Extract one or several files, depending on the configuration *)
- if config.split_files then (
- let base_gen_config =
- {
- mp_config = config.mp_config;
- use_state;
- extract_types = false;
- extract_decreases_clauses = config.extract_decreases_clauses;
- extract_template_decreases_clauses = false;
- extract_fun_decls = false;
- extract_transparent = true;
- extract_opaque = false;
- extract_state_type = false;
- interface = false;
- test_unit_functions = false;
- }
- in
-
- (* Check if there are opaque types and functions - in which case we need
- * to split *)
- let has_opaque_types, has_opaque_funs = module_has_opaque_decls gen_ctx in
- let has_opaque_types = has_opaque_types || use_state in
-
- (* Extract the types *)
- (* If there are opaque types, we extract in an interface *)
- let types_filename_ext = if has_opaque_types then ".fsti" else ".fst" in
- let types_filename = extract_filebasename ^ ".Types" ^ types_filename_ext in
- let types_module = module_name ^ ".Types" in
- let types_config =
- {
- base_gen_config with
- extract_types = true;
- extract_opaque = true;
- extract_state_type = use_state;
- interface = has_opaque_types;
- }
- in
- extract_file types_config gen_ctx types_filename crate.Crates.name
- types_module ": type definitions" [] [];
-
- (* Extract the template clauses *)
- let needs_clauses_module =
- config.extract_decreases_clauses
- && not (A.FunDeclId.Set.is_empty rec_functions)
- in
- (if needs_clauses_module && config.extract_template_decreases_clauses then
- let clauses_filename = extract_filebasename ^ ".Clauses.Template.fst" in
- let clauses_module = module_name ^ ".Clauses.Template" in
- let clauses_config =
- { base_gen_config with extract_template_decreases_clauses = true }
- in
- extract_file clauses_config gen_ctx clauses_filename crate.Crates.name
- clauses_module ": templates for the decreases clauses" [ types_module ]
- []);
-
- (* Extract the opaque functions, if needed *)
- let opaque_funs_module =
- if has_opaque_funs then (
- let opaque_filename = extract_filebasename ^ ".Opaque.fsti" in
- let opaque_module = module_name ^ ".Opaque" in
- let opaque_config =
- {
- base_gen_config with
- extract_fun_decls = true;
- extract_transparent = false;
- extract_opaque = true;
- interface = true;
- }
- in
- extract_file opaque_config gen_ctx opaque_filename crate.Crates.name
- opaque_module ": opaque function definitions" [] [ types_module ];
- [ opaque_module ])
- else []
- in
-
- (* Extract the functions *)
- let fun_filename = extract_filebasename ^ ".Funs.fst" in
- let fun_module = module_name ^ ".Funs" in
- let fun_config =
- {
- base_gen_config with
- extract_fun_decls = true;
- test_unit_functions = config.test_unit_functions;
- }
- in
- let clauses_module =
- if needs_clauses_module then [ module_name ^ ".Clauses" ] else []
- in
- extract_file fun_config gen_ctx fun_filename crate.Crates.name fun_module
- ": function definitions" []
- ([ types_module ] @ opaque_funs_module @ clauses_module))
- else
- let gen_config =
- {
- mp_config = config.mp_config;
- use_state;
- extract_types = true;
- extract_decreases_clauses = config.extract_decreases_clauses;
- extract_template_decreases_clauses =
- config.extract_template_decreases_clauses;
- extract_fun_decls = true;
- extract_transparent = true;
- extract_opaque = true;
- extract_state_type = use_state;
- interface = false;
- test_unit_functions = config.test_unit_functions;
- }
- in
- (* Add the extension for F* *)
- let extract_filename = extract_filebasename ^ ".fst" in
- extract_file gen_config gen_ctx extract_filename crate.Crates.name
- module_name "" [] []
diff --git a/src/TranslateCore.ml b/src/TranslateCore.ml
deleted file mode 100644
index a658147d..00000000
--- a/src/TranslateCore.ml
+++ /dev/null
@@ -1,65 +0,0 @@
-(** Some utilities for the translation *)
-
-open InterpreterStatements
-module L = Logging
-module T = Types
-module A = LlbcAst
-module SA = SymbolicAst
-module FA = FunsAnalysis
-
-(** The local logger *)
-let log = L.translate_log
-
-type type_context = C.type_context [@@deriving show]
-
-type fun_context = {
- fun_decls : A.fun_decl A.FunDeclId.Map.t;
- fun_infos : FA.fun_info A.FunDeclId.Map.t;
-}
-[@@deriving show]
-
-type global_context = C.global_context [@@deriving show]
-
-type trans_ctx = {
- type_context : type_context;
- fun_context : fun_context;
- global_context : global_context;
-}
-
-type pure_fun_translation = Pure.fun_decl * Pure.fun_decl list
-
-let type_decl_to_string (ctx : trans_ctx) (def : Pure.type_decl) : string =
- let type_params = def.type_params in
- let type_decls = ctx.type_context.type_decls in
- let fmt = PrintPure.mk_type_formatter type_decls type_params in
- PrintPure.type_decl_to_string fmt def
-
-let type_id_to_string (ctx : trans_ctx) (def : Pure.type_decl) : string =
- let type_params = def.type_params in
- let type_decls = ctx.type_context.type_decls in
- let fmt = PrintPure.mk_type_formatter type_decls type_params in
- PrintPure.type_decl_to_string fmt def
-
-let fun_sig_to_string (ctx : trans_ctx) (sg : Pure.fun_sig) : string =
- let type_params = sg.type_params in
- let type_decls = ctx.type_context.type_decls in
- let fun_decls = ctx.fun_context.fun_decls in
- let global_decls = ctx.global_context.global_decls in
- let fmt =
- PrintPure.mk_ast_formatter type_decls fun_decls global_decls type_params
- in
- PrintPure.fun_sig_to_string fmt sg
-
-let fun_decl_to_string (ctx : trans_ctx) (def : Pure.fun_decl) : string =
- let type_params = def.signature.type_params in
- let type_decls = ctx.type_context.type_decls in
- let fun_decls = ctx.fun_context.fun_decls in
- let global_decls = ctx.global_context.global_decls in
- let fmt =
- PrintPure.mk_ast_formatter type_decls fun_decls global_decls type_params
- in
- PrintPure.fun_decl_to_string fmt def
-
-let fun_decl_id_to_string (ctx : trans_ctx) (id : A.FunDeclId.id) : string =
- Print.fun_name_to_string
- (A.FunDeclId.Map.find id ctx.fun_context.fun_decls).name
diff --git a/src/Types.ml b/src/Types.ml
deleted file mode 100644
index 326ef76f..00000000
--- a/src/Types.ml
+++ /dev/null
@@ -1,208 +0,0 @@
-open Identifiers
-open Names
-open Meta
-module TypeVarId = IdGen ()
-module TypeDeclId = IdGen ()
-module VariantId = IdGen ()
-module FieldId = IdGen ()
-
-(** Region variable ids. Used in function signatures. *)
-module RegionVarId = IdGen ()
-
-(** Region ids. Used for symbolic executions. *)
-module RegionId = IdGen ()
-
-module RegionGroupId = IdGen ()
-
-type ('id, 'name) indexed_var = {
- index : 'id; (** Unique index identifying the variable *)
- name : 'name; (** Variable name *)
-}
-[@@deriving show]
-
-type type_var = (TypeVarId.id, string) indexed_var [@@deriving show]
-type region_var = (RegionVarId.id, string option) indexed_var [@@deriving show]
-
-(** A region.
-
- Regions are used in function signatures (in which case we use region variable
- ids) and in symbolic variables and projections (in which case we use region
- ids).
- *)
-type 'rid region =
- | Static (** Static region *)
- | Var of 'rid (** Non-static region *)
-[@@deriving show, ord]
-
-(** The type of erased regions.
-
- We could use unit, but having a dedicated type makes things more explicit.
- *)
-type erased_region = Erased [@@deriving show, ord]
-
-(** A group of regions.
-
- Results from a lifetime analysis: we group the regions with the same
- lifetime together, and compute the hierarchy between the regions.
- This is necessary to introduce the proper abstraction with the
- proper constraints, when evaluating a function call in symbolic mode.
-*)
-type ('id, 'r) g_region_group = {
- id : 'id;
- regions : 'r list;
- parents : 'id list;
-}
-[@@deriving show]
-
-type ('r, 'id) g_region_groups = ('r, 'id) g_region_group list [@@deriving show]
-
-type region_var_group = (RegionGroupId.id, RegionVarId.id) g_region_group
-[@@deriving show]
-
-type region_var_groups = (RegionGroupId.id, RegionVarId.id) g_region_groups
-[@@deriving show]
-
-type integer_type =
- | Isize
- | I8
- | I16
- | I32
- | I64
- | I128
- | Usize
- | U8
- | U16
- | U32
- | U64
- | U128
-[@@deriving show, ord]
-
-let all_signed_int_types = [ Isize; I8; I16; I32; I64; I128 ]
-let all_unsigned_int_types = [ Usize; U8; U16; U32; U64; U128 ]
-let all_int_types = List.append all_signed_int_types all_unsigned_int_types
-
-type ref_kind = Mut | Shared [@@deriving show, ord]
-type assumed_ty = Box | Vec | Option [@@deriving show, ord]
-
-(** The variant id for [Option::None] *)
-let option_none_id = VariantId.of_int 0
-
-(** The variant id for [Option::Some] *)
-let option_some_id = VariantId.of_int 1
-
-(** Type identifier for ADTs.
-
- ADTs are very general in our encoding: they account for "regular" ADTs,
- tuples and also assumed types.
-*)
-type type_id = AdtId of TypeDeclId.id | Tuple | Assumed of assumed_ty
-[@@deriving show, ord]
-
-(** Ancestor for iter visitor for [ty] *)
-class ['self] iter_ty_base =
- object (_self : 'self)
- inherit [_] VisitorsRuntime.iter
- method visit_'r : 'env -> 'r -> unit = fun _ _ -> ()
- method visit_id : 'env -> TypeVarId.id -> unit = fun _ _ -> ()
- method visit_type_id : 'env -> type_id -> unit = fun _ _ -> ()
- method visit_integer_type : 'env -> integer_type -> unit = fun _ _ -> ()
- method visit_ref_kind : 'env -> ref_kind -> unit = fun _ _ -> ()
- end
-
-(** Ancestor for map visitor for [ty] *)
-class ['self] map_ty_base =
- object (_self : 'self)
- inherit [_] VisitorsRuntime.map
- method visit_'r : 'env -> 'r -> 'r = fun _ r -> r
- method visit_id : 'env -> TypeVarId.id -> TypeVarId.id = fun _ id -> id
- method visit_type_id : 'env -> type_id -> type_id = fun _ id -> id
-
- method visit_integer_type : 'env -> integer_type -> integer_type =
- fun _ ity -> ity
-
- method visit_ref_kind : 'env -> ref_kind -> ref_kind = fun _ rk -> rk
- end
-
-type 'r ty =
- | Adt of type_id * 'r list * 'r ty list
- (** {!Adt} encodes ADTs, tuples and assumed types *)
- | TypeVar of TypeVarId.id
- | Bool
- | Char
- | Never
- | Integer of integer_type
- | Str
- | Array of 'r ty (* TODO: there should be a constant with the array *)
- | Slice of 'r ty
- | Ref of 'r * 'r ty * ref_kind
-[@@deriving
- show,
- ord,
- visitors
- {
- name = "iter_ty";
- variety = "iter";
- ancestors = [ "iter_ty_base" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- concrete = true;
- polymorphic = false;
- },
- visitors
- {
- name = "map_ty";
- variety = "map";
- ancestors = [ "map_ty_base" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- concrete = true;
- polymorphic = false;
- }]
-(* TODO: group Bool, Char, etc. in Constant *)
-
-(** Generic type with regions *)
-type 'r gr_ty = 'r region ty [@@deriving show, ord]
-
-(** *S*ignature types.
-
- Used in function signatures and type definitions.
- *)
-type sty = RegionVarId.id gr_ty [@@deriving show, ord]
-
-(** Type with *R*egions.
-
- Used to project borrows/loans inside of abstractions, during symbolic
- execution.
- *)
-type rty = RegionId.id gr_ty [@@deriving show, ord]
-
-(** Type with *E*rased regions.
-
- Used in function bodies, "regular" value types, etc.
- *)
-type ety = erased_region ty [@@deriving show, ord]
-
-type field = { meta : meta; field_name : string option; field_ty : sty }
-[@@deriving show]
-
-type variant = { meta : meta; variant_name : string; fields : field list }
-[@@deriving show]
-
-type type_decl_kind =
- | Struct of field list
- | Enum of variant list
- | Opaque
- (** An opaque type: either a local type marked as opaque, or an external type *)
-[@@deriving show]
-
-type type_decl = {
- def_id : TypeDeclId.id;
- meta : meta;
- name : type_name;
- region_params : region_var list;
- type_params : type_var list;
- kind : type_decl_kind;
- regions_hierarchy : region_var_groups;
- (** Stores the hierarchy between the regions (which regions have the
- same lifetime, which lifetime should end before which other lifetime,
- etc.) *)
-}
-[@@deriving show]
diff --git a/src/TypesAnalysis.ml b/src/TypesAnalysis.ml
deleted file mode 100644
index 60ce5149..00000000
--- a/src/TypesAnalysis.ml
+++ /dev/null
@@ -1,328 +0,0 @@
-open Types
-open Crates
-
-type subtype_info = {
- under_borrow : bool; (** Are we inside a borrow? *)
- under_mut_borrow : bool; (** Are we inside a mut borrow? *)
-}
-[@@deriving show]
-
-(** See {!type_decl_info} *)
-type type_param_info = subtype_info [@@deriving show]
-
-type expl_info = subtype_info [@@deriving show]
-
-type type_borrows_info = {
- contains_static : bool;
- (** Does the type (transitively) contains a static borrow? *)
- contains_borrow : bool;
- (** Does the type (transitively) contains a borrow? *)
- contains_nested_borrows : bool;
- (** Does the type (transitively) contains nested borrows? *)
- contains_borrow_under_mut : bool;
-}
-[@@deriving show]
-
-(** Generic definition *)
-type 'p g_type_info = {
- borrows_info : type_borrows_info;
- (** Various informations about the borrows *)
- param_infos : 'p; (** Gives information about the type parameters *)
-}
-[@@deriving show]
-
-(** Information about a type definition. *)
-type type_decl_info = type_param_info list g_type_info [@@deriving show]
-
-(** Information about a type. *)
-type ty_info = type_borrows_info [@@deriving show]
-
-(** Helper definition.
-
- Allows us to factorize code: {!analyze_full_ty} is used both to analyze
- type definitions and types. *)
-type partial_type_info = type_param_info list option g_type_info
-[@@deriving show]
-
-type type_infos = type_decl_info TypeDeclId.Map.t [@@deriving show]
-
-let expl_info_init = { under_borrow = false; under_mut_borrow = false }
-
-let type_borrows_info_init : type_borrows_info =
- {
- contains_static = false;
- contains_borrow = false;
- contains_nested_borrows = false;
- contains_borrow_under_mut = false;
- }
-
-let initialize_g_type_info (param_infos : 'p) : 'p g_type_info =
- { borrows_info = type_borrows_info_init; param_infos }
-
-let initialize_type_decl_info (def : type_decl) : type_decl_info =
- let param_info = { under_borrow = false; under_mut_borrow = false } in
- let param_infos = List.map (fun _ -> param_info) def.type_params in
- initialize_g_type_info param_infos
-
-let type_decl_info_to_partial_type_info (info : type_decl_info) :
- partial_type_info =
- { borrows_info = info.borrows_info; param_infos = Some info.param_infos }
-
-let partial_type_info_to_type_decl_info (info : partial_type_info) :
- type_decl_info =
- {
- borrows_info = info.borrows_info;
- param_infos = Option.get info.param_infos;
- }
-
-let partial_type_info_to_ty_info (info : partial_type_info) : ty_info =
- info.borrows_info
-
-let analyze_full_ty (r_is_static : 'r -> bool) (updated : bool ref)
- (infos : type_infos) (ty_info : partial_type_info) (ty : 'r ty) :
- partial_type_info =
- (* Small utility *)
- let check_update_bool (original : bool) (nv : bool) : bool =
- if nv && not original then (
- updated := true;
- nv)
- else original
- in
-
- (* Update a partial_type_info, while registering if we actually performed an update *)
- let update_ty_info (ty_info : partial_type_info)
- (ty_b_info : type_borrows_info) : partial_type_info =
- let original = ty_info.borrows_info in
- let contains_static =
- check_update_bool original.contains_static ty_b_info.contains_static
- in
- let contains_borrow =
- check_update_bool original.contains_borrow ty_b_info.contains_borrow
- in
- let contains_nested_borrows =
- check_update_bool original.contains_nested_borrows
- ty_b_info.contains_nested_borrows
- in
- let contains_borrow_under_mut =
- check_update_bool original.contains_borrow_under_mut
- ty_b_info.contains_borrow_under_mut
- in
- let updated_borrows_info =
- {
- contains_static;
- contains_borrow;
- contains_nested_borrows;
- contains_borrow_under_mut;
- }
- in
- { ty_info with borrows_info = updated_borrows_info }
- in
-
- (* The recursive function which explores the type *)
- let rec analyze (expl_info : expl_info) (ty_info : partial_type_info)
- (ty : 'r ty) : partial_type_info =
- match ty with
- | Bool | Char | Never | Integer _ | Str -> ty_info
- | TypeVar var_id -> (
- (* Update the information for the proper parameter, if necessary *)
- match ty_info.param_infos with
- | None -> ty_info
- | Some param_infos ->
- let param_info = TypeVarId.nth param_infos var_id in
- (* Set [under_borrow] *)
- let under_borrow =
- check_update_bool param_info.under_borrow expl_info.under_borrow
- in
- (* Set [under_nested_borrows] *)
- let under_mut_borrow =
- check_update_bool param_info.under_mut_borrow
- expl_info.under_mut_borrow
- in
- (* Update param_info *)
- let param_info = { under_borrow; under_mut_borrow } in
- let param_infos =
- TypeVarId.update_nth param_infos var_id param_info
- in
- let param_infos = Some param_infos in
- { ty_info with param_infos })
- | Array ty | Slice ty ->
- (* Just dive in *)
- analyze expl_info ty_info ty
- | Ref (r, rty, rkind) ->
- (* Update the type info *)
- let contains_static = r_is_static r in
- let contains_borrow = true in
- let contains_nested_borrows = expl_info.under_borrow in
- let contains_borrow_under_mut = expl_info.under_mut_borrow in
- let ty_b_info =
- {
- contains_static;
- contains_borrow;
- contains_nested_borrows;
- contains_borrow_under_mut;
- }
- in
- let ty_info = update_ty_info ty_info ty_b_info in
- (* Update the exploration info *)
- let expl_info =
- {
- under_borrow = true;
- under_mut_borrow = expl_info.under_mut_borrow || rkind = Mut;
- }
- in
- (* Continue exploring *)
- analyze expl_info ty_info rty
- | Adt ((Tuple | Assumed (Box | Vec | Option)), _, tys) ->
- (* Nothing to update: just explore the type parameters *)
- List.fold_left
- (fun ty_info ty -> analyze expl_info ty_info ty)
- ty_info tys
- | Adt (AdtId adt_id, regions, tys) ->
- (* Lookup the information for this type definition *)
- let adt_info = TypeDeclId.Map.find adt_id infos in
- (* Update the type info with the information from the adt *)
- let ty_info = update_ty_info ty_info adt_info.borrows_info in
- (* Check if 'static appears in the region parameters *)
- let found_static = List.exists r_is_static regions in
- let borrows_info = ty_info.borrows_info in
- let borrows_info =
- {
- borrows_info with
- contains_static =
- check_update_bool borrows_info.contains_static found_static;
- }
- in
- let ty_info = { ty_info with borrows_info } in
- (* For every instantiated type parameter: update the exploration info
- * then explore the type *)
- let params_tys = List.combine adt_info.param_infos tys in
- let ty_info =
- List.fold_left
- (fun ty_info (param_info, ty) ->
- (* Update the type info *)
- (* Below: we use only the information which we learn only
- * by taking the type parameter into account. *)
- let contains_static = false in
- let contains_borrow = param_info.under_borrow in
- let contains_nested_borrows =
- expl_info.under_borrow && param_info.under_borrow
- in
- let contains_borrow_under_mut =
- expl_info.under_mut_borrow && param_info.under_borrow
- in
- let ty_b_info =
- {
- contains_static;
- contains_borrow;
- contains_nested_borrows;
- contains_borrow_under_mut;
- }
- in
- let ty_info = update_ty_info ty_info ty_b_info in
- (* Update the exploration info *)
- let expl_info =
- {
- under_borrow =
- expl_info.under_borrow || param_info.under_borrow;
- under_mut_borrow =
- expl_info.under_mut_borrow || param_info.under_mut_borrow;
- }
- in
- (* Continue exploring *)
- analyze expl_info ty_info ty)
- ty_info params_tys
- in
- (* Return *)
- ty_info
- in
- (* Explore *)
- analyze expl_info_init ty_info ty
-
-let type_decl_is_opaque (d : type_decl) : bool =
- match d.kind with Struct _ | Enum _ -> false | Opaque -> true
-
-let analyze_type_decl (updated : bool ref) (infos : type_infos)
- (def : type_decl) : type_infos =
- (* We analyze the type declaration only if it is not opaque (we need to explore
- * the variants of the ADTs *)
- if type_decl_is_opaque def then infos
- else
- (* Retrieve all the types of all the fields of all the variants *)
- let fields_tys : sty list =
- match def.kind with
- | Struct fields -> List.map (fun f -> f.field_ty) fields
- | Enum variants ->
- List.concat
- (List.map
- (fun v -> List.map (fun f -> f.field_ty) v.fields)
- variants)
- | Opaque -> raise (Failure "unreachable")
- in
- (* Explore the types and accumulate information *)
- let r_is_static r = r = Static in
- let type_decl_info = TypeDeclId.Map.find def.def_id infos in
- let type_decl_info = type_decl_info_to_partial_type_info type_decl_info in
- let type_decl_info =
- List.fold_left
- (fun type_decl_info ty ->
- analyze_full_ty r_is_static updated infos type_decl_info ty)
- type_decl_info fields_tys
- in
- let type_decl_info = partial_type_info_to_type_decl_info type_decl_info in
- (* Update the information for the type definition we explored *)
- let infos = TypeDeclId.Map.add def.def_id type_decl_info infos in
- (* Return *)
- infos
-
-let analyze_type_declaration_group (type_decls : type_decl TypeDeclId.Map.t)
- (infos : type_infos) (decl : type_declaration_group) : type_infos =
- (* Collect the identifiers used in the declaration group *)
- let ids = match decl with NonRec id -> [ id ] | Rec ids -> ids in
- (* Retrieve the type definitions *)
- let decl_defs = List.map (fun id -> TypeDeclId.Map.find id type_decls) ids in
- (* Initialize the type information for the current definitions *)
- let infos =
- List.fold_left
- (fun infos def ->
- TypeDeclId.Map.add def.def_id (initialize_type_decl_info def) infos)
- infos decl_defs
- in
- (* Analyze the types - this function simply computes a fixed-point *)
- let updated : bool ref = ref false in
- let rec analyze (infos : type_infos) : type_infos =
- let infos =
- List.fold_left
- (fun infos def -> analyze_type_decl updated infos def)
- infos decl_defs
- in
- if !updated then (
- updated := false;
- analyze infos)
- else infos
- in
- analyze infos
-
-(** Compute the type information for every *type definition* in a list of
- declarations. This type definition information is later used to easily
- compute the information of arbitrary types.
-
- Rk.: pay attention to the difference between type definitions and types!
- *)
-let analyze_type_declarations (type_decls : type_decl TypeDeclId.Map.t)
- (decls : type_declaration_group list) : type_infos =
- List.fold_left
- (fun infos decl -> analyze_type_declaration_group type_decls infos decl)
- TypeDeclId.Map.empty decls
-
-(** Analyze a type to check whether it contains borrows, etc., provided
- we have already analyzed the type definitions in the context.
- *)
-let analyze_ty (infos : type_infos) (ty : 'r ty) : ty_info =
- (* We don't use [updated] but need to give it as parameter *)
- let updated = ref false in
- (* We don't need to compute whether the type contains 'static or not *)
- let r_is_static _ = false in
- let ty_info = initialize_g_type_info None in
- let ty_info = analyze_full_ty r_is_static updated infos ty_info ty in
- (* Convert the ty_info *)
- partial_type_info_to_ty_info ty_info
diff --git a/src/TypesUtils.ml b/src/TypesUtils.ml
deleted file mode 100644
index 7531dd8b..00000000
--- a/src/TypesUtils.ml
+++ /dev/null
@@ -1,190 +0,0 @@
-open Types
-open Utils
-module TA = TypesAnalysis
-
-let type_decl_is_opaque (d : type_decl) : bool =
- match d.kind with Struct _ | Enum _ -> false | Opaque -> true
-
-(** Retrieve the list of fields for the given variant of a {!Types.type_decl}.
-
- Raises [Invalid_argument] if the arguments are incorrect.
- *)
-let type_decl_get_fields (def : type_decl)
- (opt_variant_id : VariantId.id option) : field list =
- match (def.kind, opt_variant_id) with
- | Enum variants, Some variant_id -> (VariantId.nth variants variant_id).fields
- | Struct fields, None -> fields
- | _ ->
- let opt_variant_id =
- match opt_variant_id with None -> "None" | Some _ -> "Some"
- in
- raise
- (Invalid_argument
- ("The variant id should be [Some] if and only if the definition is \
- an enumeration:\n\
- - def: " ^ show_type_decl def ^ "\n- opt_variant_id: "
- ^ opt_variant_id))
-
-(** Return [true] if a {!Types.ty} is actually [unit] *)
-let ty_is_unit (ty : 'r ty) : bool =
- match ty with Adt (Tuple, [], []) -> true | _ -> false
-
-let ty_is_adt (ty : 'r ty) : bool =
- match ty with Adt (_, _, _) -> true | _ -> false
-
-let ty_as_adt (ty : 'r ty) : type_id * 'r list * 'r ty list =
- match ty with
- | Adt (id, regions, tys) -> (id, regions, tys)
- | _ -> failwith "Unreachable"
-
-let ty_is_custom_adt (ty : 'r ty) : bool =
- match ty with Adt (AdtId _, _, _) -> true | _ -> false
-
-let ty_as_custom_adt (ty : 'r ty) : TypeDeclId.id * 'r list * 'r ty list =
- match ty with
- | Adt (AdtId id, regions, tys) -> (id, regions, tys)
- | _ -> failwith "Unreachable"
-
-(** The unit type *)
-let mk_unit_ty : 'r ty = Adt (Tuple, [], [])
-
-(** The usize type *)
-let mk_usize_ty : 'r ty = Integer Usize
-
-(** Deconstruct a type of the form [Box<T>] to retrieve the [T] inside *)
-let ty_get_box (box_ty : ety) : ety =
- match box_ty with
- | Adt (Assumed Box, [], [ boxed_ty ]) -> boxed_ty
- | _ -> failwith "Not a boxed type"
-
-(** Deconstruct a type of the form [&T] or [&mut T] to retrieve the [T] (and
- the borrow kind, etc.)
- *)
-let ty_get_ref (ty : 'r ty) : 'r * 'r ty * ref_kind =
- match ty with
- | Ref (r, ty, ref_kind) -> (r, ty, ref_kind)
- | _ -> failwith "Not a ref type"
-
-let mk_ref_ty (r : 'r) (ty : 'r ty) (ref_kind : ref_kind) : 'r ty =
- Ref (r, ty, ref_kind)
-
-(** Make a box type *)
-let mk_box_ty (ty : 'r ty) : 'r ty = Adt (Assumed Box, [], [ ty ])
-
-(** Make a vec type *)
-let mk_vec_ty (ty : 'r ty) : 'r ty = Adt (Assumed Vec, [], [ ty ])
-
-(** Check if a region is in a set of regions *)
-let region_in_set (r : RegionId.id region) (rset : RegionId.Set.t) : bool =
- match r with Static -> false | Var id -> RegionId.Set.mem id rset
-
-(** Return the set of regions in an rty *)
-let rty_regions (ty : rty) : RegionId.Set.t =
- let s = ref RegionId.Set.empty in
- let add_region (r : RegionId.id region) =
- match r with Static -> () | Var rid -> s := RegionId.Set.add rid !s
- in
- let obj =
- object
- inherit [_] iter_ty
- method! visit_'r _env r = add_region r
- end
- in
- (* Explore the type *)
- obj#visit_ty () ty;
- (* Return the set of accumulated regions *)
- !s
-
-let rty_regions_intersect (ty : rty) (regions : RegionId.Set.t) : bool =
- let ty_regions = rty_regions ty in
- not (RegionId.Set.disjoint ty_regions regions)
-
-(** Convert an {!Types.ety}, containing no region variables, to an {!Types.rty}
- or an {!Types.sty}.
-
- In practice, it is the identity.
- *)
-let rec ety_no_regions_to_gr_ty (ty : ety) : 'a gr_ty =
- match ty with
- | Adt (type_id, regions, tys) ->
- assert (regions = []);
- Adt (type_id, [], List.map ety_no_regions_to_gr_ty tys)
- | TypeVar v -> TypeVar v
- | Bool -> Bool
- | Char -> Char
- | Never -> Never
- | Integer int_ty -> Integer int_ty
- | Str -> Str
- | Array ty -> Array (ety_no_regions_to_gr_ty ty)
- | Slice ty -> Slice (ety_no_regions_to_gr_ty ty)
- | Ref (_, _, _) ->
- failwith
- "Can't convert a ref with erased regions to a ref with non-erased \
- regions"
-
-let ety_no_regions_to_rty (ty : ety) : rty = ety_no_regions_to_gr_ty ty
-let ety_no_regions_to_sty (ty : ety) : sty = ety_no_regions_to_gr_ty ty
-
-(** Retuns true if the type contains borrows.
-
- Note that we can't simply explore the type and look for regions: sometimes
- we erase the lists of regions (by replacing them with [[]] when using {!Types.ety},
- and when a type uses 'static this region doesn't appear in the region parameters.
- *)
-let ty_has_borrows (infos : TA.type_infos) (ty : 'r ty) : bool =
- let info = TA.analyze_ty infos ty in
- info.TA.contains_borrow
-
-(** Retuns true if the type contains nested borrows.
-
- Note that we can't simply explore the type and look for regions: sometimes
- we erase the lists of regions (by replacing them with [[]] when using {!Types.ety},
- and when a type uses 'static this region doesn't appear in the region parameters.
- *)
-let ty_has_nested_borrows (infos : TA.type_infos) (ty : 'r ty) : bool =
- let info = TA.analyze_ty infos ty in
- info.TA.contains_nested_borrows
-
-(** Retuns true if the type contains a borrow under a mutable borrow *)
-let ty_has_borrow_under_mut (infos : TA.type_infos) (ty : 'r ty) : bool =
- let info = TA.analyze_ty infos ty in
- info.TA.contains_borrow_under_mut
-
-(** Check if a {!Types.ty} contains regions from a given set *)
-let ty_has_regions_in_set (rset : RegionId.Set.t) (ty : rty) : bool =
- let obj =
- object
- inherit [_] iter_ty as super
-
- method! visit_Adt env type_id regions tys =
- List.iter (fun r -> if region_in_set r rset then raise Found) regions;
- super#visit_Adt env type_id regions tys
-
- method! visit_Ref env r ty rkind =
- if region_in_set r rset then raise Found
- else super#visit_Ref env r ty rkind
- end
- in
- try
- obj#visit_ty () ty;
- false
- with Found -> true
-
-(** Return true if a type is "primitively copyable".
- *
- * "primitively copyable" means that copying instances of this type doesn't
- * require calling dedicated functions defined through the Copy trait. It
- * is the case for types like integers, shared borrows, etc.
- *
- * Generally, ADTs are not copyable. However, some of the primitive ADTs are
- * like `Option`.
- *)
-let rec ty_is_primitively_copyable (ty : 'r ty) : bool =
- match ty with
- | Adt (Assumed Option, _, tys) -> List.for_all ty_is_primitively_copyable tys
- | Adt ((AdtId _ | Assumed (Box | Vec)), _, _) -> false
- | Adt (Tuple, _, tys) -> List.for_all ty_is_primitively_copyable tys
- | TypeVar _ | Never | Str | Array _ | Slice _ -> false
- | Bool | Char | Integer _ -> true
- | Ref (_, _, Mut) -> false
- | Ref (_, _, Shared) -> true
diff --git a/src/Utils.ml b/src/Utils.ml
deleted file mode 100644
index a285e869..00000000
--- a/src/Utils.ml
+++ /dev/null
@@ -1,6 +0,0 @@
-exception Found
-(** Utility exception
-
- When looking for something while exploring a term, it can be easier to
- just throw an exception to signal we found what we were looking for.
- *)
diff --git a/src/Values.ml b/src/Values.ml
deleted file mode 100644
index e404f40d..00000000
--- a/src/Values.ml
+++ /dev/null
@@ -1,844 +0,0 @@
-open Identifiers
-open Types
-
-(* TODO: I often write "abstract" (value, borrow content, etc.) while I should
- * write "abstraction" (because those values are not abstract, they simply are
- * inside abstractions) *)
-
-module VarId = IdGen ()
-module BorrowId = IdGen ()
-module SymbolicValueId = IdGen ()
-module AbstractionId = IdGen ()
-module FunCallId = IdGen ()
-
-(** A variable *)
-
-type big_int = Z.t
-
-let big_int_of_yojson (json : Yojson.Safe.t) : (big_int, string) result =
- match json with
- | `Int i -> Ok (Z.of_int i)
- | `Intlit is -> Ok (Z.of_string is)
- | _ -> Error "not an integer or an integer literal"
-
-let big_int_to_yojson (i : big_int) = `Intlit (Z.to_string i)
-
-let pp_big_int (fmt : Format.formatter) (bi : big_int) : unit =
- Format.pp_print_string fmt (Z.to_string bi)
-
-let show_big_int (bi : big_int) : string = Z.to_string bi
-
-(** A scalar value
-
- Note that we use unbounded integers everywhere.
- We then harcode the boundaries for the different types.
- *)
-type scalar_value = { value : big_int; int_ty : integer_type } [@@deriving show]
-
-(** A constant value *)
-type constant_value =
- | Scalar of scalar_value
- | Bool of bool
- | Char of char
- | String of string
-[@@deriving show]
-
-(** The kind of a symbolic value, which precises how the value was generated *)
-type sv_kind =
- | FunCallRet (** The value is the return value of a function call *)
- | FunCallGivenBack
- (** The value is a borrowed value given back by an abstraction
- (happens when giving a borrow to a function: when the abstraction
- introduced to model the function call ends we reintroduce a symbolic
- value in the context for the value modified by the abstraction through
- the borrow).
- *)
- | SynthInput
- (** The value is an input value of the function whose body we are
- currently synthesizing.
- *)
- | SynthRetGivenBack
- (** The value is a borrowed value that the function whose body we are
- synthesizing returned, and which was given back because we ended
- one of the lifetimes of this function (we do this to synthesize
- the backward functions).
- *)
- | SynthInputGivenBack
- (** The value was given back upon ending one of the input abstractions *)
- | Global (** The value is a global *)
-[@@deriving show]
-
-(** A symbolic value *)
-type symbolic_value = {
- sv_kind : sv_kind;
- sv_id : SymbolicValueId.id;
- sv_ty : rty;
-}
-[@@deriving show]
-
-(** Ancestor for {!typed_value} iter visitor *)
-class ['self] iter_typed_value_base =
- object (_self : 'self)
- inherit [_] VisitorsRuntime.iter
- method visit_constant_value : 'env -> constant_value -> unit = fun _ _ -> ()
- method visit_erased_region : 'env -> erased_region -> unit = fun _ _ -> ()
- method visit_symbolic_value : 'env -> symbolic_value -> unit = fun _ _ -> ()
- method visit_ety : 'env -> ety -> unit = fun _ _ -> ()
- end
-
-(** Ancestor for {!typed_value} map visitor for *)
-class ['self] map_typed_value_base =
- object (_self : 'self)
- inherit [_] VisitorsRuntime.map
-
- method visit_constant_value : 'env -> constant_value -> constant_value =
- fun _ cv -> cv
-
- method visit_erased_region : 'env -> erased_region -> erased_region =
- fun _ r -> r
-
- method visit_symbolic_value : 'env -> symbolic_value -> symbolic_value =
- fun _ sv -> sv
-
- method visit_ety : 'env -> ety -> ety = fun _ ty -> ty
- end
-
-(** An untyped value, used in the environments *)
-type value =
- | Concrete of constant_value (** Concrete (non-symbolic) value *)
- | Adt of adt_value (** Enumerations and structures *)
- | Bottom (** No value (uninitialized or moved value) *)
- | Borrow of borrow_content (** A borrowed value *)
- | Loan of loan_content (** A loaned value *)
- | Symbolic of symbolic_value
- (** Borrow projector over a symbolic value.
-
- Note that contrary to the abstraction-values case, symbolic values
- appearing in regular values are interpreted as *borrow* projectors,
- they can never be *loan* projectors.
- *)
-
-and adt_value = {
- variant_id : (VariantId.id option[@opaque]);
- field_values : typed_value list;
-}
-
-and borrow_content =
- | SharedBorrow of mvalue * (BorrowId.id[@opaque])
- (** A shared borrow.
-
- We remember the shared value which was borrowed as a meta value.
- This is necessary for synthesis: upon translating to "pure" values,
- we can't perform any lookup because we don't have an environment
- anymore. Note that it is ok to keep the shared value and copy
- the shared value this way, because shared values are immutable
- for as long as they are shared (i.e., as long as we can use the
- shared borrow).
- *)
- | MutBorrow of (BorrowId.id[@opaque]) * typed_value
- (** A mutably borrowed value. *)
- | InactivatedMutBorrow of mvalue * (BorrowId.id[@opaque])
- (** An inactivated mut borrow.
-
- This is used to model {{: https://rustc-dev-guide.rust-lang.org/borrow_check/two_phase_borrows.html} two-phase borrows}.
- When evaluating a two-phase mutable borrow, we first introduce an inactivated
- borrow which behaves like a shared borrow, until the moment we actually *use*
- the borrow: at this point, we end all the other shared borrows (or inactivated
- borrows - though there shouldn't be any other inactivated borrows if the program
- is well typed) of this value and replace the inactivated borrow with a
- mutable borrow.
-
- A simple use case of two-phase borrows:
- {[
- let mut v = Vec::new();
- v.push(v.len());
- ]}
-
- This gets desugared to (something similar to) the following MIR:
- {[
- v = Vec::new();
- v1 = &mut v;
- v2 = &v; // We need this borrow, but v has already been mutably borrowed!
- l = Vec::len(move v2);
- Vec::push(move v1, move l); // In practice, v1 gets activated only here
- ]}
-
- The meta-value is used for the same purposes as with shared borrows,
- at the exception that in case of inactivated borrows it is not
- *necessary* for the synthesis: we keep it only as meta-information.
- To be more precise:
- - when generating the synthesized program, we may need to convert
- shared borrows to pure values
- - we never need to do so for inactivated borrows: such borrows must
- be activated at the moment we use them (meaning we convert a *mutable*
- borrow to a pure value). However, we save meta-data about the assignments,
- which is used to make the code cleaner: when generating this meta-data,
- we may need to convert inactivated borrows to pure values, in which
- situation we convert the meta-value we stored in the inactivated
- borrow.
- *)
-
-and loan_content =
- | SharedLoan of (BorrowId.Set.t[@opaque]) * typed_value
- | MutLoan of (BorrowId.id[@opaque])
- (** TODO: we might want to add a set of borrow ids (useful for inactivated
- borrows, and extremely useful when giving shared values to abstractions).
- *)
-
-(** "Meta"-value: information we store for the synthesis.
-
- Note that we never automatically visit the meta-values with the
- visitors: they really are meta information, and shouldn't be considered
- as part of the environment during a symbolic execution.
-
- TODO: we may want to create wrappers, to prevent accidently mixing meta
- values and regular values.
- *)
-and mvalue = typed_value
-
-(** "Regular" typed value (we map variables to typed values) *)
-and typed_value = { value : value; ty : ety }
-[@@deriving
- show,
- visitors
- {
- name = "iter_typed_value_visit_mvalue";
- variety = "iter";
- ancestors = [ "iter_typed_value_base" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- concrete = true;
- },
- visitors
- {
- name = "map_typed_value_visit_mvalue";
- variety = "map";
- ancestors = [ "map_typed_value_base" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- concrete = true;
- }]
-
-(** We have to override the {!iter_typed_value_visit_mvalue.visit_mvalue} method,
- to ignore meta-values *)
-class ['self] iter_typed_value =
- object (_self : 'self)
- inherit [_] iter_typed_value_visit_mvalue
- method! visit_mvalue : 'env -> mvalue -> unit = fun _ _ -> ()
- end
-
-(** We have to override the {!iter_typed_value_visit_mvalue.visit_mvalue} method,
- to ignore meta-values *)
-class ['self] map_typed_value =
- object (_self : 'self)
- inherit [_] map_typed_value_visit_mvalue
- method! visit_mvalue : 'env -> mvalue -> mvalue = fun _ x -> x
- end
-
-(** "Meta"-symbolic value.
-
- See the explanations for {!mvalue}
-
- TODO: we may want to create wrappers, to prevent mixing meta values
- and regular values.
- *)
-type msymbolic_value = symbolic_value [@@deriving show]
-
-(** When giving shared borrows to functions (i.e., inserting shared borrows inside
- abstractions) we need to reborrow the shared values. When doing so, we lookup
- the shared values and apply some special projections to the shared value
- (until we can't go further, i.e., we find symbolic values which may get
- expanded upon reading them later), which don't generate avalues but
- sets of borrow ids and symbolic values.
-
- Note that as shared values can't get modified it is ok to forget the
- structure of the values we projected, and only keep the set of borrows
- (and symbolic values).
-
- TODO: we may actually need to remember the structure, in order to know
- which borrows are inside which other borrows...
-*)
-type abstract_shared_borrow =
- | AsbBorrow of (BorrowId.id[@opaque])
- | AsbProjReborrows of (symbolic_value[@opaque]) * (rty[@opaque])
-[@@deriving show]
-
-(** A set of abstract shared borrows *)
-type abstract_shared_borrows = abstract_shared_borrow list [@@deriving show]
-
-(** Ancestor for {!aproj} iter visitor *)
-class ['self] iter_aproj_base =
- object (_self : 'self)
- inherit [_] iter_typed_value
- method visit_rty : 'env -> rty -> unit = fun _ _ -> ()
-
- method visit_msymbolic_value : 'env -> msymbolic_value -> unit =
- fun _ _ -> ()
- end
-
-(** Ancestor for {!aproj} map visitor *)
-class ['self] map_aproj_base =
- object (_self : 'self)
- inherit [_] map_typed_value
- method visit_rty : 'env -> rty -> rty = fun _ ty -> ty
-
- method visit_msymbolic_value : 'env -> msymbolic_value -> msymbolic_value =
- fun _ m -> m
- end
-
-type aproj =
- | AProjLoans of symbolic_value * (msymbolic_value * aproj) list
- (** A projector of loans over a symbolic value.
-
- Note that the borrows of a symbolic value may be spread between
- different abstractions, meaning that the projector of loans might
- receive *several* (symbolic) given back values.
-
- This is the case in the following example:
- {[
- fn f<'a> (...) -> (&'a mut u32, &'a mut u32);
- fn g<'b, 'c>(p : (&'b mut u32, &'c mut u32));
-
- let p = f(...);
- g(move p);
-
- // Symbolic context after the call to g:
- // abs'a {'a} { [s@0 <: (&'a mut u32, &'a mut u32)] }
- //
- // abs'b {'b} { (s@0 <: (&'b mut u32, &'c mut u32)) }
- // abs'c {'c} { (s@0 <: (&'b mut u32, &'c mut u32)) }
- ]}
-
- Upon evaluating the call to [f], we introduce a symbolic value [s@0]
- and a projector of loans (projector loans from the region 'c).
- This projector will later receive two given back values: one for
- 'a and one for 'b.
-
- We accumulate those values in the list of projections (note that
- the meta value stores the value which was given back).
-
- We can later end the projector of loans if [s@0] is not referenced
- anywhere in the context below a projector of borrows which intersects
- this projector of loans.
- *)
- | AProjBorrows of symbolic_value * rty
- (** Note that an AProjBorrows only operates on a value which is not below
- a shared loan: under a shared loan, we use {!abstract_shared_borrow}.
-
- Also note that once given to a borrow projection, a symbolic value
- can't get updated/expanded: this means that we don't need to save
- any meta-value here.
- *)
- | AEndedProjLoans of msymbolic_value * (msymbolic_value * aproj) list
- (** An ended projector of loans over a symbolic value.
-
- See the explanations for {!AProjLoans}
-
- Note that we keep the original symbolic value as a meta-value.
- *)
- | AEndedProjBorrows of msymbolic_value
- (** The only purpose of {!AEndedProjBorrows} is to store, for synthesis
- purposes, the symbolic value which was generated and given back upon
- ending the borrow.
- *)
- | AIgnoredProjBorrows
-[@@deriving
- show,
- visitors
- {
- name = "iter_aproj";
- variety = "iter";
- ancestors = [ "iter_aproj_base" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- concrete = true;
- },
- visitors
- {
- name = "map_aproj";
- variety = "map";
- ancestors = [ "map_aproj_base" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- concrete = true;
- }]
-
-type region = RegionVarId.id Types.region [@@deriving show]
-
-(** Ancestor for {!typed_avalue} iter visitor *)
-class ['self] iter_typed_avalue_base =
- object (_self : 'self)
- inherit [_] iter_aproj
- method visit_id : 'env -> BorrowId.id -> unit = fun _ _ -> ()
- method visit_region : 'env -> region -> unit = fun _ _ -> ()
-
- method visit_abstract_shared_borrows
- : 'env -> abstract_shared_borrows -> unit =
- fun _ _ -> ()
- end
-
-(** Ancestor for {!typed_avalue} map visitor *)
-class ['self] map_typed_avalue_base =
- object (_self : 'self)
- inherit [_] map_aproj
- method visit_id : 'env -> BorrowId.id -> BorrowId.id = fun _ id -> id
- method visit_region : 'env -> region -> region = fun _ r -> r
-
- method visit_abstract_shared_borrows
- : 'env -> abstract_shared_borrows -> abstract_shared_borrows =
- fun _ asb -> asb
- end
-
-(** Abstraction values are used inside of abstractions to properly model
- borrowing relations introduced by function calls.
-
- When calling a function, we lose information about the borrow graph:
- part of it is thus "abstracted" away.
-*)
-type avalue =
- | AConcrete of constant_value
- (** TODO: remove. We actually don't use that for the synthesis, but the
- meta-values.
-
- Note that this case is not used in the projections to keep track of the
- borrow graph (because there are no borrows in "concrete" values!) but
- to correctly instantiate the backward functions (we may give back some
- values at different moments: we need to remember what those values were
- precisely). Also note that even though avalues and values are not the
- same, once values are projected to avalues, those avalues still have
- the structure of the original values (this is necessary, again, to
- correctly instantiate the backward functions)
- *)
- | AAdt of adt_avalue
- | ABottom
- | ALoan of aloan_content
- | ABorrow of aborrow_content
- | ASymbolic of aproj
- | AIgnored
- (** A value which doesn't contain borrows, or which borrows we
- don't own and thus ignore *)
-
-and adt_avalue = {
- variant_id : (VariantId.id option[@opaque]);
- field_values : typed_avalue list;
-}
-
-(** A loan content as stored in an abstraction.
-
- Note that the children avalues are independent of the parent avalues.
- For instance, the child avalue contained in an {!AMutLoan} will likely
- contain other, independent loans.
- Keeping track of the hierarchy is not necessary to maintain the borrow graph
- (which is the primary role of the abstractions), but it is necessary
- to properly instantiate the backward functions when generating the pure
- translation.
-*)
-and aloan_content =
- | AMutLoan of (BorrowId.id[@opaque]) * typed_avalue
- (** A mutable loan owned by an abstraction.
-
- Example:
- ========
- {[
- fn f<'a>(...) -> &'a mut &'a mut u32;
-
- let px = f(...);
- ]}
-
- We get (after some symbolic exansion):
- {[
- abs0 {
- a_mut_loan l0 (a_mut_loan l1)
- }
- px -> mut_borrow l0 (mut_borrow @s1)
- ]}
- *)
- | ASharedLoan of (BorrowId.Set.t[@opaque]) * typed_value * typed_avalue
- (** A shared loan owned by an abstraction.
-
- Example:
- ========
- {[
- fn f<'a>(...) -> &'a u32;
-
- let px = f(...);
- ]}
-
- We get:
- {[
- abs0 { a_shared_loan {l0} @s0 ⊥ }
- px -> shared_loan l0
- ]}
- *)
- | AEndedMutLoan of {
- child : typed_avalue;
- given_back : typed_avalue;
- given_back_meta : mvalue;
- }
- (** An ended mutable loan in an abstraction.
- We need it because abstractions must keep track of the values
- we gave back to them, so that we can correctly instantiate
- backward functions.
-
- Rk.: *DO NOT* use [visit_AEndedMutLoan]. If we update the order of
- the arguments and you forget to swap them at the level of
- [visit_AEndedMutLoan], you will not notice it.
-
- Example:
- ========
- {[
- abs0 { a_mut_loan l0 ⊥ }
- x -> mut_borrow l0 (U32 3)
- ]}
-
- After ending [l0]:
-
- {[
- abs0 { a_ended_mut_loan { given_back = U32 3; child = ⊥; }
- x -> ⊥
- ]}
- *)
- | AEndedSharedLoan of typed_value * typed_avalue
- (** Similar to {!AEndedMutLoan} but in this case there are no avalues to
- give back. We keep the shared value because it now behaves as a
- "regular" value (which contains borrows we might want to end...).
- *)
- | AIgnoredMutLoan of (BorrowId.id[@opaque]) * typed_avalue
- (** An ignored mutable loan.
-
- We need to keep track of ignored mutable loans, because we may have
- to apply projections on the values given back to those loans (say
- you have a borrow of type [&'a mut &'b mut], in the abstraction 'b,
- the outer loan is ignored, however you need to keep track of it so
- that when ending the borrow corresponding to 'a you can correctly
- project on the inner value).
-
- Example:
- ========
- {[
- fn f<'a,'b>(...) -> &'a mut &'b mut u32;
- let x = f(...);
-
- > abs'a { a_mut_loan l0 (a_ignored_mut_loan l1 ⊥) }
- > abs'b { a_ignored_mut_loan l0 (a_mut_loan l1 ⊥) }
- > x -> mut_borrow l0 (mut_borrow l1 @s1)
- ]}
- *)
- | AEndedIgnoredMutLoan of {
- child : typed_avalue;
- given_back : typed_avalue;
- given_back_meta : mvalue;
- }
- (** Similar to {!AEndedMutLoan}, for ignored loans.
-
- Rk.: *DO NOT* use [visit_AEndedIgnoredMutLoan].
- See the comment for {!AEndedMutLoan}.
- *)
- | AIgnoredSharedLoan of typed_avalue
- (** An ignored shared loan.
-
- Example:
- ========
- {[
- fn f<'a,'b>(...) -> &'a &'b u32;
- let x = f(...);
-
- > abs'a { a_shared_loan {l0} (shared_borrow l1) (a_ignored_shared_loan ⊥) }
- > abs'b { a_ignored_shared_loan (a_shared_loan {l1} @s1 ⊥) }
- > x -> shared_borrow l0
- ]}
- *)
-
-(** Note that when a borrow content is ended, it is replaced by ⊥ (while
- we need to track ended loans more precisely, especially because of their
- children values).
-
- Note that contrary to {!aloan_content}, here the children avalues are
- not independent of the parent avalues. For instance, a value
- [AMutBorrow (_, AMutBorrow (_, ...)] (ignoring the types) really is
- to be seen like a [mut_borrow ... (mut_borrow ...)].
-
- TODO: be more precise about the ignored borrows (keep track of the borrow
- ids)?
-*)
-and aborrow_content =
- | AMutBorrow of mvalue * (BorrowId.id[@opaque]) * typed_avalue
- (** A mutable borrow owned by an abstraction.
-
- Is used when an abstraction "consumes" borrows, when giving borrows
- as arguments to a function.
-
- Example:
- ========
- {[
- fn f<'a>(px : &'a mut u32);
-
- > x -> mut_loan l0
- > px -> mut_borrow l0 (U32 0)
-
- f(move px);
-
- > x -> mut_loan l0
- > px -> ⊥
- > abs0 { a_mut_borrow l0 (U32 0) }
- ]}
-
- The meta-value stores the initial value on which the projector was
- applied, which reduced to this mut borrow. This meta-information
- is only used for the synthesis.
- TODO: do we really use it actually?
- *)
- | ASharedBorrow of (BorrowId.id[@opaque])
- (** A shared borrow owned by an abstraction.
-
- Example:
- ========
- {[
- fn f<'a>(px : &'a u32);
-
- > x -> shared_loan {l0} (U32 0)
- > px -> shared_borrow l0
-
- f(move px);
-
- > x -> shared_loan {l0} (U32 0)
- > px -> ⊥
- > abs0 { a_shared_borrow l0 }
- ]}
- *)
- | AIgnoredMutBorrow of BorrowId.id option * typed_avalue
- (** An ignored mutable borrow.
-
- We need to keep track of ignored mut borrows because when ending such
- borrows, we need to project the loans of the given back value to
- insert them in the proper abstractions.
-
- Note that we need to do so only for borrows consumed by parent
- abstractions (hence the optional borrow id).
-
- TODO: the below explanations are obsolete
-
- Example:
- ========
- {[
- fn f<'a,'b>(ppx : &'a mut &'b mut u32);
-
- > x -> mut_loan l0
- > px -> mut_loan l1
- > ppx -> mut_borrow l1 (mut_borrow l0 (U32 0))
-
- f(move ppx);
-
- > x -> mut_loan l0
- > px -> mut_loan l1
- > ppx -> ⊥
- > abs'a { a_mut_borrow l1 (a_ignored_mut_borrow None (U32 0)) } // TODO: duplication
- > abs'b {parents={abs'a}} { a_ignored_mut_borrow (Some l1) (a_mut_borrow l0 (U32 0)) }
-
- ... // abs'a ends
-
- > x -> mut_loan l0
- > px -> @s0
- > ppx -> ⊥
- > abs'b {
- > a_ended_ignored_mut_borrow (a_proj_loans (@s0 <: &'b mut u32)) // <-- loan projector
- > (a_mut_borrow l0 (U32 0))
- > }
-
- ... // [@s0] gets expanded to [&mut l2 @s1]
-
- > x -> mut_loan l0
- > px -> &mut l2 @s1
- > ppx -> ⊥
- > abs'b {
- > a_ended_ignored_mut_borrow (a_mut_loan l2) // <-- loan l2 is here
- > (a_mut_borrow l0 (U32 0))
- > }
-
- ]}
-
- Note that we could use AIgnoredMutLoan in the case the borrow id is not
- None, which would allow us to simplify the rules (to not have rules
- to specifically handle the case of AIgnoredMutBorrow with Some borrow
- id) and also remove the AEndedIgnoredMutBorrow variant.
- For now, the rules are implemented and it allows us to make the avalues
- more precise and clearer, so we will keep it that way.
-
- TODO: this is annoying, we are duplicating information. Maybe we
- could introduce an "Ignored" value? We have to pay attention to
- two things:
- - introducing ⊥ when ignoring a value is not always possible, because
- we check whether the borrowed value contains ⊥ when giving back a
- borrowed value (if it is the case we give back ⊥, otherwise we
- introduce a symbolic value). This is necessary when ending nested
- borrows with the same lifetime: when ending the inner borrow we
- actually give back a value, however when ending the outer borrow
- we need to give back ⊥.
- TODO: actually we don't do that anymore, we check if the borrowed
- avalue contains ended regions (which is cleaner and more robust).
- - we may need to remember the precise values given to the
- abstraction so that we can properly call the backward functions
- when generating the pure translation.
- *)
- | AEndedMutBorrow of msymbolic_value * typed_avalue
- (** The sole purpose of {!AEndedMutBorrow} is to store the (symbolic) value
- that we gave back as a meta-value, to help with the synthesis.
-
- We also remember the child {!avalue} because this structural information
- is useful for the synthesis (but not for the symbolic execution):
- in practice the child value should only contain ended borrows, ignored
- values, bottom values, etc.
- *)
- | AEndedSharedBorrow
- (** We don't really need {!AEndedSharedBorrow}: we simply want to be
- precise, and not insert ⊥ when ending borrows.
- *)
- | AEndedIgnoredMutBorrow of {
- child : typed_avalue;
- given_back_loans_proj : typed_avalue;
- given_back_meta : msymbolic_value;
- (** [given_back_meta] is used to store the (symbolic) value we gave back
- upon ending the borrow.
-
- Rk.: *DO NOT* use [visit_AEndedIgnoredMutLoan].
- See the comment for {!AEndedMutLoan}.
- *)
- } (** See the explanations for {!AIgnoredMutBorrow} *)
- | AProjSharedBorrow of abstract_shared_borrows
- (** A projected shared borrow.
-
- When giving shared borrows as arguments to function calls, we
- introduce new borrows to keep track of the fact that the function
- might reborrow values inside. Note that as shared values are immutable,
- we don't really need to remember the structure of the shared values.
-
- Example:
- ========
- Below, when calling [f], we need to introduce one shared borrow per
- borrow in the argument.
- {[
- fn f<'a,'b>(pppx : &'a &'b &'c mut u32);
-
- > x -> mut_loan l0
- > px -> shared_loan {l1} (mut_borrow l0 (U32 0))
- > ppx -> shared_loan {l2} (shared_borrow l1)
- > pppx -> shared_borrow l2
-
- f(move pppx);
-
- > x -> mut_loan l0
- > px -> shared_loan {l1, l3, l4} (mut_borrow l0 (U32 0))
- > ppx -> shared_loan {l2} (shared_borrow l1)
- > pppx -> ⊥
- > abs'a { a_proj_shared_borrow {l2} }
- > abs'b { a_proj_shared_borrow {l3} } // l3 reborrows l1
- > abs'c { a_proj_shared_borrow {l4} } // l4 reborrows l0
- ]}
- *)
-
-(* TODO: the type of avalues doesn't make sense for loan avalues: they currently
- are typed as [& (mut) T] instead of [T]...
-*)
-and typed_avalue = { value : avalue; ty : rty }
-[@@deriving
- show,
- visitors
- {
- name = "iter_typed_avalue";
- variety = "iter";
- ancestors = [ "iter_typed_avalue_base" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- concrete = true;
- },
- visitors
- {
- name = "map_typed_avalue";
- variety = "map";
- ancestors = [ "map_typed_avalue_base" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- concrete = true;
- }]
-
-(** The kind of an abstraction, which keeps track of its origin *)
-type abs_kind =
- | FunCall (** The abstraction was introduced because of a function call *)
- | SynthInput
- (** The abstraction keeps track of the input values of the function
- we are currently synthesizing. *)
- | SynthRet
- (** The abstraction "absorbed" the value returned by the function we
- are currently synthesizing *)
-[@@deriving show]
-
-(** Abstractions model the parts in the borrow graph where the borrowing relations
- have been abstracted because of a function call.
-
- In order to model the relations between the borrows, we use "abstraction values",
- which are a special kind of value.
-*)
-type abs = {
- abs_id : (AbstractionId.id[@opaque]);
- call_id : (FunCallId.id[@opaque]);
- (** The identifier of the function call which introduced this
- abstraction. This is not used by the symbolic execution:
- this is only used for pretty-printing and debugging, in the
- symbolic AST, generated by the symbolic execution.
- *)
- back_id : (RegionGroupId.id[@opaque]);
- (** The region group id to which this abstraction is linked.
-
- In most situations, it gives the id of the backward function (hence
- the name), but it is a bit more subtle in the case of synth input
- and synth ret abstractions.
-
- This is not used by the symbolic execution: it is a utility for
- the symbolic AST, generated by the symbolic execution.
- *)
- kind : (abs_kind[@opaque]);
- can_end : (bool[@opaque]);
- (** Controls whether the region can be ended or not.
-
- This allows to "pin" some regions, and is useful when generating
- backward functions.
-
- For instance, if we have: [fn f<'a, 'b>(...) -> (&'a mut T, &'b mut T)],
- when generating the backward function for 'a, we have to make sure we
- don't need to end the return region for 'b (if it is the case, it means
- the function doesn't borrow check).
- *)
- parents : (AbstractionId.Set.t[@opaque]); (** The parent abstractions *)
- original_parents : (AbstractionId.id list[@opaque]);
- (** The original list of parents, ordered. This is used for synthesis. *)
- regions : (RegionId.Set.t[@opaque]); (** Regions owned by this abstraction *)
- ancestors_regions : (RegionId.Set.t[@opaque]);
- (** Union of the regions owned by this abstraction's ancestors (not
- including the regions of this abstraction itself) *)
- avalues : typed_avalue list; (** The values in this abstraction *)
-}
-[@@deriving
- show,
- visitors
- {
- name = "iter_abs";
- variety = "iter";
- ancestors = [ "iter_typed_avalue" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- concrete = true;
- },
- visitors
- {
- name = "map_abs";
- variety = "map";
- ancestors = [ "map_typed_avalue" ];
- nude = true (* Don't inherit {!VisitorsRuntime.iter} *);
- concrete = true;
- }]
-
-(** A symbolic expansion
-
- A symbolic expansion doesn't represent a value, but rather an operation
- that we apply to values.
-
- TODO: this should rather be name "expanded_symbolic"
- *)
-type symbolic_expansion =
- | SeConcrete of constant_value
- | SeAdt of (VariantId.id option * symbolic_value list)
- | SeMutRef of BorrowId.id * symbolic_value
- | SeSharedRef of BorrowId.Set.t * symbolic_value
diff --git a/src/ValuesUtils.ml b/src/ValuesUtils.ml
deleted file mode 100644
index 72d7abe0..00000000
--- a/src/ValuesUtils.ml
+++ /dev/null
@@ -1,121 +0,0 @@
-open Utils
-open TypesUtils
-open Types
-open Values
-module TA = TypesAnalysis
-
-(** Utility exception *)
-exception FoundSymbolicValue of symbolic_value
-
-let mk_unit_value : typed_value =
- { value = Adt { variant_id = None; field_values = [] }; ty = mk_unit_ty }
-
-let mk_typed_value (ty : ety) (value : value) : typed_value = { value; ty }
-let mk_bottom (ty : ety) : typed_value = { value = Bottom; ty }
-
-(** Box a value *)
-let mk_box_value (v : typed_value) : typed_value =
- let box_ty = mk_box_ty v.ty in
- let box_v = Adt { variant_id = None; field_values = [ v ] } in
- mk_typed_value box_ty box_v
-
-let is_bottom (v : value) : bool = match v with Bottom -> true | _ -> false
-
-let is_symbolic (v : value) : bool =
- match v with Symbolic _ -> true | _ -> false
-
-let as_symbolic (v : value) : symbolic_value =
- match v with Symbolic s -> s | _ -> failwith "Unexpected"
-
-let as_mut_borrow (v : typed_value) : BorrowId.id * typed_value =
- match v.value with
- | Borrow (MutBorrow (bid, bv)) -> (bid, bv)
- | _ -> failwith "Unexpected"
-
-(** Check if a value contains a borrow *)
-let borrows_in_value (v : typed_value) : bool =
- let obj =
- object
- inherit [_] iter_typed_value
- method! visit_borrow_content _env _ = raise Found
- end
- in
- (* We use exceptions *)
- try
- obj#visit_typed_value () v;
- false
- with Found -> true
-
-(** Check if a value contains inactivated mutable borrows *)
-let inactivated_in_value (v : typed_value) : bool =
- let obj =
- object
- inherit [_] iter_typed_value
- method! visit_InactivatedMutBorrow _env _ = raise Found
- end
- in
- (* We use exceptions *)
- try
- obj#visit_typed_value () v;
- false
- with Found -> true
-
-(** Check if a value contains a loan *)
-let loans_in_value (v : typed_value) : bool =
- let obj =
- object
- inherit [_] iter_typed_value
- method! visit_loan_content _env _ = raise Found
- end
- in
- (* We use exceptions *)
- try
- obj#visit_typed_value () v;
- false
- with Found -> true
-
-(** Check if a value contains outer loans (i.e., loans which are not in borrwed
- values. *)
-let outer_loans_in_value (v : typed_value) : bool =
- let obj =
- object
- inherit [_] iter_typed_value
- method! visit_loan_content _env _ = raise Found
- method! visit_borrow_content _ _ = ()
- end
- in
- (* We use exceptions *)
- try
- obj#visit_typed_value () v;
- false
- with Found -> true
-
-let find_first_primitively_copyable_sv_with_borrows (type_infos : TA.type_infos)
- (v : typed_value) : symbolic_value option =
- (* The visitor *)
- let obj =
- object
- inherit [_] iter_typed_value
-
- method! visit_Symbolic _ sv =
- let ty = sv.sv_ty in
- if ty_is_primitively_copyable ty && ty_has_borrows type_infos ty then
- raise (FoundSymbolicValue sv)
- else ()
- end
- in
- (* Small helper *)
- try
- obj#visit_typed_value () v;
- None
- with FoundSymbolicValue sv -> Some sv
-
-(** Strip the outer shared loans in a value.
- Ex.:
- [shared_loan {l0, l1} (3 : u32, shared_loan {l2} (4 : u32))] ~~>
- [(3 : u32, shared_loan {l2} (4 : u32))]
- *)
-let rec value_strip_shared_loans (v : typed_value) : typed_value =
- match v.value with
- | Loan (SharedLoan (_, v')) -> value_strip_shared_loans v'
- | _ -> v
diff --git a/src/driver.ml b/src/driver.ml
deleted file mode 100644
index ae9d238a..00000000
--- a/src/driver.ml
+++ /dev/null
@@ -1,208 +0,0 @@
-open Aeneas.LlbcOfJson
-open Aeneas.Logging
-open Aeneas.Print
-module T = Aeneas.Types
-module A = Aeneas.LlbcAst
-module I = Aeneas.Interpreter
-module EL = Easy_logging.Logging
-module TA = Aeneas.TypesAnalysis
-module Micro = Aeneas.PureMicroPasses
-module Print = Aeneas.Print
-module PrePasses = Aeneas.PrePasses
-module Translate = Aeneas.Translate
-
-(* This is necessary to have a backtrace when raising exceptions - for some
- * reason, the -g option doesn't work.
- * TODO: run with OCAMLRUNPARAM=b=1? *)
-let () = Printexc.record_backtrace true
-
-let usage =
- Printf.sprintf
- {|Aeneas: verification of Rust programs by translation to pure lambda calculus
-
-Usage: %s [OPTIONS] FILE
-|}
- Sys.argv.(0)
-
-let () =
- (* Measure start time *)
- let start_time = Unix.gettimeofday () in
-
- (* Read the command line arguments *)
- let dest_dir = ref "" in
- let decompose_monads = ref false in
- let unfold_monads = ref true in
- let filter_useless_calls = ref true in
- let filter_useless_functions = ref true in
- let test_units = ref false in
- let test_trans_units = ref false in
- let no_decreases_clauses = ref false in
- let no_state = ref false in
- let template_decreases_clauses = ref false in
- let no_split_files = ref false in
- let no_check_inv = ref false in
-
- let spec =
- [
- ("-dest", Arg.Set_string dest_dir, " Specify the output directory");
- ( "-decompose-monads",
- Arg.Set decompose_monads,
- " Decompose the monadic let-bindings.\n\n\
- \ Introduces a temporary variable which is later decomposed,\n\
- \ when the pattern on the left of the monadic let is not a \n\
- \ variable.\n\
- \ \n\
- \ Example:\n\
- \ `(x, y) <-- f (); ...` ~~>\n\
- \ `tmp <-- f (); let (x, y) = tmp in ...`\n\
- \ " );
- ( "-unfold-monads",
- Arg.Set unfold_monads,
- " Unfold the monadic let-bindings to matches" );
- ( "-filter-useless-calls",
- Arg.Set filter_useless_calls,
- " Filter the useless function calls, when possible" );
- ( "-filter-useless-funs",
- Arg.Set filter_useless_functions,
- " Filter the useless forward/backward functions" );
- ( "-test-units",
- Arg.Set test_units,
- " Test the unit functions with the concrete interpreter" );
- ( "-test-trans-units",
- Arg.Set test_trans_units,
- " Test the translated unit functions with the target theorem\n\
- \ prover's normalizer" );
- ( "-no-decreases-clauses",
- Arg.Set no_decreases_clauses,
- " Do not add decrease clauses to the recursive definitions" );
- ( "-no-state",
- Arg.Set no_state,
- " Do not use state-error monads, simply use error monads" );
- ( "-template-clauses",
- Arg.Set template_decreases_clauses,
- " Generate templates for the required decreases clauses, in a\n\
- \ dedicated file. Incompatible with \
- -no-decreases-clauses" );
- ( "-no-split-files",
- Arg.Set no_split_files,
- " Don't split the definitions between different files for types,\n\
- \ functions, etc." );
- ( "-no-check-inv",
- Arg.Set no_check_inv,
- " Deactivate the invariant sanity checks performed at every step of\n\
- \ evaluation. Dramatically saves speed." );
- ]
- in
- (* Sanity check: -template-clauses ==> not -no-decrease-clauses *)
- assert ((not !no_decreases_clauses) || not !template_decreases_clauses);
-
- let spec = Arg.align spec in
- let filenames = ref [] in
- let add_filename f = filenames := f :: !filenames in
- Arg.parse spec add_filename usage;
- let fail () =
- print_string usage;
- exit 1
- in
- (* Retrieve and check the filename *)
- let filename =
- match !filenames with
- | [ f ] ->
- (* TODO: update the extension *)
- if not (Filename.check_suffix f ".llbc") then (
- print_string "Unrecognized file extension";
- fail ())
- else if not (Sys.file_exists f) then (
- print_string "File not found";
- fail ())
- else f
- | _ ->
- (* For now, we only process one file at a time *)
- print_string usage;
- exit 1
- in
- (* Check the destination directory *)
- let dest_dir =
- if !dest_dir = "" then Filename.dirname filename else !dest_dir
- in
-
- (* Set up the logging - for now we use default values - TODO: use the
- * command-line arguments *)
- (* By setting a level for the main_logger_handler, we filter everything *)
- Easy_logging.Handlers.set_level main_logger_handler EL.Debug;
- main_log#set_level EL.Info;
- llbc_of_json_logger#set_level EL.Info;
- pre_passes_log#set_level EL.Info;
- interpreter_log#set_level EL.Info;
- statements_log#set_level EL.Info;
- paths_log#set_level EL.Info;
- expressions_log#set_level EL.Info;
- expansion_log#set_level EL.Info;
- borrows_log#set_level EL.Info;
- invariants_log#set_level EL.Info;
- pure_utils_log#set_level EL.Info;
- symbolic_to_pure_log#set_level EL.Info;
- pure_micro_passes_log#set_level EL.Info;
- pure_to_extract_log#set_level EL.Info;
- translate_log#set_level EL.Info;
-
- (* Load the module *)
- let json = Yojson.Basic.from_file filename in
- match llbc_crate_of_json json with
- | Error s ->
- main_log#error "error: %s\n" s;
- exit 1
- | Ok m ->
- (* Logging *)
- main_log#linfo (lazy ("Imported: " ^ filename));
- main_log#ldebug (lazy ("\n" ^ Print.Module.module_to_string m ^ "\n"));
-
- (* Apply the pre-passes *)
- let m = PrePasses.apply_passes m in
-
- (* Some options for the execution *)
- let eval_config =
- {
- C.check_invariants = not !no_check_inv;
- greedy_expand_symbolics_with_borrows = true;
- allow_bottom_below_borrow = true;
- return_unit_end_abs_with_no_loans = true;
- }
- in
-
- (* Test the unit functions with the concrete interpreter *)
- if !test_units then I.Test.test_unit_functions eval_config m;
-
- (* Evaluate the symbolic interpreter on the functions, ignoring the
- * functions which contain loops - TODO: remove *)
- let synthesize = true in
- I.Test.test_functions_symbolic eval_config synthesize m;
-
- (* Translate the functions *)
- let test_unit_functions = !test_trans_units in
- let micro_passes_config =
- {
- Micro.decompose_monadic_let_bindings = !decompose_monads;
- unfold_monadic_let_bindings = !unfold_monads;
- filter_useless_monadic_calls = !filter_useless_calls;
- filter_useless_functions = !filter_useless_functions;
- }
- in
- let trans_config =
- {
- Translate.eval_config;
- mp_config = micro_passes_config;
- split_files = not !no_split_files;
- test_unit_functions;
- extract_decreases_clauses = not !no_decreases_clauses;
- extract_template_decreases_clauses = !template_decreases_clauses;
- use_state = not !no_state;
- }
- in
- Translate.translate_module filename dest_dir trans_config m;
-
- (* Print total elapsed time *)
- log#linfo
- (lazy
- (Printf.sprintf "Total execution time: %f seconds"
- (Unix.gettimeofday () -. start_time)))
diff --git a/src/dune b/src/dune
deleted file mode 100644
index e8b53fc5..00000000
--- a/src/dune
+++ /dev/null
@@ -1,48 +0,0 @@
-;; core: for Core.Unix.mkdir_p
-
-(executable
- (name driver)
- (public_name aeneas_driver)
- (package aeneas)
- (preprocess
- (pps ppx_deriving.show ppx_deriving.ord visitors.ppx))
- (libraries ppx_deriving yojson zarith easy_logging core_unix aeneas)
- (modules driver))
-
-(library
- (name aeneas) ;; The name as used in the project
- (public_name aeneas) ;; The name as revealed to the projects importing this library
- (preprocess
- (pps ppx_deriving.show ppx_deriving.ord visitors.ppx))
- (libraries ppx_deriving yojson zarith easy_logging core_unix)
- (modules Assumed Collections ConstStrings Contexts Cps Crates Errors
- Expressions ExpressionsUtils ExtractToFStar FunsAnalysis Identifiers
- InterpreterBorrowsCore InterpreterBorrows InterpreterExpansion
- InterpreterExpressions Interpreter InterpreterPaths InterpreterProjectors
- InterpreterStatements InterpreterUtils Invariants LlbcAst LlbcAstUtils
- LlbcOfJson Logging Meta Names OfJsonBasic PrePasses Print PrintPure
- PureMicroPasses Pure PureToExtract PureTypeCheck PureUtils Scalars
- StringUtils Substitute SymbolicAst SymbolicToPure SynthesizeSymbolic
- TranslateCore Translate TypesAnalysis Types TypesUtils Utils Values
- ValuesUtils))
-
-(documentation
- (package aeneas))
-
-(env
- (dev
- (flags
- :standard
- -safe-string
- -g
- ;-dsource
- -warn-error
- -5-8-9-11-14-33-20-21-26-27-39))
- (release
- (flags
- :standard
- -safe-string
- -g
- ;-dsource
- -warn-error
- -5-8-9-11-14-33-20-21-26-27-39)))