···466466- #9003: Start compilation from Emit when the input file is in Linear IR format.
467467 (Greta Yorsh, review by Jérémie Dimino, Gabriel Scherer and Frédéric Bour)
468468469469+- #9963: Centralized tracking of frontend's global state
470470+ (Frédéric Bour and Thomas Refis, review by Gabriel Scherer)
471471+469472### Build system:
470473471474- #7121, #9558: Always the autoconf-discovered ld in PACKLD. For
···2020open Types
2121open Btype
22222323+open Local_store
2424+2325type type_replacement =
2426 | Path of Path.t
2527 | Type_function of { params : type_expr list; body : type_expr }
···124126125127(* Special type ids for saved signatures *)
126128127127-let new_id = ref (-1)
129129+let new_id = s_ref (-1)
128130let reset_for_saving () = new_id := -1
129131130132let newpersty desc =
+6
utils/config.mli
···244244245245val config_var : string -> string option
246246(** the configuration value of a variable, if it exists *)
247247+248248+(**/**)
249249+250250+val merlin : bool
251251+252252+(**/**)
···1212(* *)
1313(**************************************************************************)
14141515+open Local_store
1616+1517module SMap = Misc.Stdlib.String.Map
16181719(* Mapping from basenames to full filenames *)
1820type registry = string SMap.t ref
19212020-let files : registry = ref SMap.empty
2121-let files_uncap : registry = ref SMap.empty
2222+let files : registry = s_ref SMap.empty
2323+let files_uncap : registry = s_ref SMap.empty
22242325module Dir = struct
2426 type t = {
···4244 { path; files = Array.to_list (readdir_compat path) }
4345end
44464545-let dirs = ref []
4747+let dirs = s_ref []
46484749let reset () =
5050+ assert (not Config.merlin || Local_store.is_bound ());
4851 files := SMap.empty;
4952 files_uncap := SMap.empty;
5053 dirs := []
···6467 name already exists in the cache simply by adding entries in reverse
6568 order. *)
6669let add dir =
7070+ assert (not Config.merlin || Local_store.is_bound ());
6771 let new_files, new_files_uncap =
6872 add_to_maps (Filename.concat dir.Dir.path)
6973 dir.Dir.files !files !files_uncap
···7781 List.iter add !dirs
78827983let remove_dir dir =
8484+ assert (not Config.merlin || Local_store.is_bound ());
8085 let new_dirs = List.filter (fun d -> Dir.path d <> dir) !dirs in
8186 if List.compare_lengths new_dirs !dirs <> 0 then begin
8287 reset ();
···8893 add a basename to the cache if it is not already present in the cache, in
8994 order to enforce left-to-right precedence. *)
9095let add dir =
9696+ assert (not Config.merlin || Local_store.is_bound ());
9197 let new_files, new_files_uncap =
9298 add_to_maps (Filename.concat dir.Dir.path) dir.Dir.files
9399 SMap.empty SMap.empty
···102108let is_basename fn = Filename.basename fn = fn
103109104110let find fn =
111111+ assert (not Config.merlin || Local_store.is_bound ());
105112 if is_basename fn then
106113 SMap.find fn !files
107114 else
108115 Misc.find_in_path (get_paths ()) fn
109116110117let find_uncap fn =
118118+ assert (not Config.merlin || Local_store.is_bound ());
111119 if is_basename fn then
112120 SMap.find (String.uncapitalize_ascii fn) !files_uncap
113121 else
+74
utils/local_store.ml
···11+(**************************************************************************)
22+(* *)
33+(* OCaml *)
44+(* *)
55+(* Frederic Bour, Tarides *)
66+(* Thomas Refis, Tarides *)
77+(* *)
88+(* Copyright 2020 Tarides *)
99+(* *)
1010+(* All rights reserved. This file is distributed under the terms of *)
1111+(* the GNU Lesser General Public License version 2.1, with the *)
1212+(* special exception on linking described in the file LICENSE. *)
1313+(* *)
1414+(**************************************************************************)
1515+1616+type ref_and_reset =
1717+ | Table : { ref: 'a ref; init: unit -> 'a } -> ref_and_reset
1818+ | Ref : { ref: 'a ref; mutable snapshot: 'a } -> ref_and_reset
1919+2020+type bindings = {
2121+ mutable refs: ref_and_reset list;
2222+ mutable frozen : bool;
2323+ mutable is_bound: bool;
2424+}
2525+2626+let global_bindings =
2727+ { refs = []; is_bound = false; frozen = false }
2828+2929+let is_bound () = global_bindings.is_bound
3030+3131+let reset () =
3232+ assert (is_bound ());
3333+ List.iter (function
3434+ | Table { ref; init } -> ref := init ()
3535+ | Ref { ref; snapshot } -> ref := snapshot
3636+ ) global_bindings.refs
3737+3838+let s_table create size =
3939+ let init () = create size in
4040+ let ref = ref (init ()) in
4141+ assert (not global_bindings.frozen);
4242+ global_bindings.refs <- (Table { ref; init }) :: global_bindings.refs;
4343+ ref
4444+4545+let s_ref k =
4646+ let ref = ref k in
4747+ assert (not global_bindings.frozen);
4848+ global_bindings.refs <-
4949+ (Ref { ref; snapshot = k }) :: global_bindings.refs;
5050+ ref
5151+5252+type slot = Slot : { ref : 'a ref; mutable value : 'a } -> slot
5353+type store = slot list
5454+5555+let fresh () =
5656+ let slots =
5757+ List.map (function
5858+ | Table { ref; init } -> Slot {ref; value = init ()}
5959+ | Ref r ->
6060+ if not global_bindings.frozen then r.snapshot <- !(r.ref);
6161+ Slot { ref = r.ref; value = r.snapshot }
6262+ ) global_bindings.refs
6363+ in
6464+ global_bindings.frozen <- true;
6565+ slots
6666+6767+let with_store slots f =
6868+ assert (not global_bindings.is_bound);
6969+ global_bindings.is_bound <- true;
7070+ List.iter (fun (Slot {ref;value}) -> ref := value) slots;
7171+ Fun.protect f ~finally:(fun () ->
7272+ List.iter (fun (Slot s) -> s.value <- !(s.ref)) slots;
7373+ global_bindings.is_bound <- false;
7474+ )
+66
utils/local_store.mli
···11+(**************************************************************************)
22+(* *)
33+(* OCaml *)
44+(* *)
55+(* Frederic Bour, Tarides *)
66+(* Thomas Refis, Tarides *)
77+(* *)
88+(* Copyright 2020 Tarides *)
99+(* *)
1010+(* All rights reserved. This file is distributed under the terms of *)
1111+(* the GNU Lesser General Public License version 2.1, with the *)
1212+(* special exception on linking described in the file LICENSE. *)
1313+(* *)
1414+(**************************************************************************)
1515+1616+(** This module provides some facilities for creating references (and hash
1717+ tables) which can easily be snapshoted and restored to an arbitrary version.
1818+1919+ It is used throughout the frontend (read: typechecker), to register all
2020+ (well, hopefully) the global state. Thus making it easy for tools like
2121+ Merlin to go back and forth typechecking different files. *)
2222+2323+(** {1 Creators} *)
2424+2525+val s_ref : 'a -> 'a ref
2626+(** Similar to {!ref}, except the allocated reference is registered into the
2727+ store. *)
2828+2929+val s_table : ('a -> 'b) -> 'a -> 'b ref
3030+(** Used to register hash tables. Those also need to be placed into refs to be
3131+ easily swapped out, but one can't just "snapshot" the initial value to
3232+ create fresh instances, so instead an initializer is required.
3333+3434+ Use it like this:
3535+ {[
3636+ let my_table = s_table Hashtbl.create 42
3737+ ]}
3838+*)
3939+4040+(** {1 State management}
4141+4242+ Note: all the following functions are currently unused inside the compiler
4343+ codebase. Merlin is their only user at the moment. *)
4444+4545+type store
4646+4747+val fresh : unit -> store
4848+(** Returns a fresh instance of the store.
4949+5050+ The first time this function is called, it snapshots the value of all the
5151+ registered references, later calls to [fresh] will return instances
5252+ initialized to those values. *)
5353+5454+val with_store : store -> (unit -> 'a) -> 'a
5555+(** [with_scope s f] resets all the registered references to the value they have
5656+ in [s] for the run of [f].
5757+ If [f] updates any of the registered refs, [s] is updated to remember those
5858+ changes. *)
5959+6060+val reset : unit -> unit
6161+(** Resets all the references to the initial snapshot (i.e. to the same values
6262+ that new instances start with). *)
6363+6464+val is_bound : unit -> bool
6565+(** Returns [true] when a scope is active (i.e. when called from the callback
6666+ passed to {!with_scope}), [false] otherwise. *)