From 4849b736e9b86b08c690152c734abf50d2021559 Mon Sep 17 00:00:00 2001
From: Son Ho
Date: Wed, 30 Nov 2022 21:37:11 +0100
Subject: Make more progress on the joins

---
 compiler/InterpreterLoops.ml | 269 ++++++++++++++++++++-----------------------
 1 file changed, 127 insertions(+), 142 deletions(-)

diff --git a/compiler/InterpreterLoops.ml b/compiler/InterpreterLoops.ml
index 85174aaf..cc8cdcff 100644
--- a/compiler/InterpreterLoops.ml
+++ b/compiler/InterpreterLoops.ml
@@ -20,15 +20,6 @@ open InterpreterExpressions
 (** The local logger *)
 let log = L.loops_log
 
-(* TODO: some fields are actually useless *)
-type cnt_thresholds = {
-  aid : V.AbstractionId.id;
-  sid : V.SymbolicValueId.id;
-  bid : V.BorrowId.id;
-  did : C.DummyVarId.id;
-  rid : T.RegionId.id;
-}
-
 type updt_env_kind =
   | AbsInLeft of V.AbstractionId.id
   | LoanInLeft of V.BorrowId.id
@@ -1054,116 +1045,139 @@ let collapse_ctx_with_merge (loop_id : V.LoopId.id)
   try collapse_ctx loop_id (Some merge_funcs) old_ids ctx
   with ValueMatchFailure _ -> raise (Failure "Unexpected")
 
-(*(** Apply substitutions in the first abstraction, then join the abstractions together.
-
-    TODO: remove?
- *)
-let subst_join_abstractions (loop_id : V.LoopId.id) (thresh : cnt_thresholds)
-    (rid_map : T.RegionId.InjSubst.t) (bid_map : V.BorrowId.InjSubst.t)
-    (sid_map : V.SymbolicValueId.InjSubst.t) (ctx : C.eval_ctx) (abs0 : V.abs)
-    (abs1 : V.abs) : V.abs =
-  (* Apply the substitutions in the first abstraction *)
-  let rsubst id =
-    assert (T.RegionId.Ord.compare id thresh.rid >= 0);
-    T.RegionId.InjSubst.find_with_default id id rid_map
-  in
-  let rvsubst id = id in
-  let tsubst id = id in
-  let ssubst id =
-    assert (V.SymbolicValueId.Ord.compare id thresh.sid >= 0);
-    V.SymbolicValueId.InjSubst.find_with_default id id sid_map
-  in
-  let bsubst id =
-    assert (V.BorrowId.Ord.compare id thresh.bid >= 0);
-    V.BorrowId.InjSubst.find_with_default id id bid_map
-  in
-  let asubst id = id in
-  let abs0 =
-    Substitute.abs_subst_ids rsubst rvsubst tsubst ssubst bsubst asubst abs0
-  in
-
-  (* Merge the two abstractions *)
-  let abs_kind = V.Loop loop_id in
-  let can_end = false in
-merge_abstractions abs_kind can_end None ctx abs0 abs1*)
-
-(*
-(** Merge a borrow with the abstraction containing the associated loan, where
-    the abstraction must be a *loop abstraction* (we don't synthesize code during
-    the operation).
+(** Join two contexts *)
+let join_ctxs (config : C.config) (loop_id : V.LoopId.id)
+    (old_ids : InterpreterBorrowsCore.ctx_ids) (ctx0 : C.eval_ctx)
+    (ctx1 : C.eval_ctx) : ctx_or_update =
+  let env0 = List.rev ctx0.env in
+  let env1 = List.rev ctx1.env in
+
+  (* We need to pick a context for some functions like [match_typed_values]:
+     the context is only used to lookup module data, so we can pick whichever
+     we want.
+     TODO: this is not very clean. Maybe we should just carry this data around.
+  *)
+  let ctx = ctx0 in
 
-    For instance:
-    {[
-      abs'0 { mut_loan l0 }
-      x -> mut_borrow l0 sx
+  let nabs = ref [] in
 
-            ~~>
+  (* Explore the environments.
 
-      abs'0 {}
-      x -> ⊥
-    ]}
- *)
-let merge_borrow_with_parent_loop_abs (config : C.config) (bid : V.BorrowId.id)
-    (ctx : C.eval_ctx) : C.eval_ctx =
-  (* TODO: use the function from InterpreterBorrows *)
-  failwith "Unimplemented"
-(* (* Lookup the borrow *)
-     let ek =
-       { enter_shared_loans = false; enter_mut_borrows = false; enter_abs = false }
-     in
-     (* TODO: use [end_borrow_get_borrow]? *)
-     match lookup_borrow ek bid ctx with
-     | None -> ctx
-   | Some b -> failwith "Unimplemented"*)
-
-(** See {!merge_borrow_with_parent_loop_abs} *)
-let rec merge_borrows_with_parent_loop_abs (config : C.config)
-    (bids : V.BorrowId.Set.t) (ctx : C.eval_ctx) : C.eval_ctx =
-  V.BorrowId.Set.fold
-    (fun id ctx -> merge_borrow_with_parent_loop_abs config id ctx)
-    bids ctx
- *)
+     Note that they should have the same prefixes (they should start with
+     old dummy values, old abstractions and bindings which have the same ids).
+     Those old values and abstractions should actually be equal between the
+     two environments.
 
-(*
-(* TODO: we probably don't need an [match_ctx], and actually we might not use
-   the bounds propertly.
-   TODO: remove
-*)
-let match_ctx_with_target_old (config : C.config) (tgt_ctx : C.eval_ctx) :
-    cm_fun =
- fun cf src_ctx ->
-  (* We first reorganize [ctx] so that we can match it with [tgt_ctx] *)
-  (* First, collect all the borrows and abstractions which are in [ctx]
-     and not in [tgt_ctx]: we need to end them *)
-  let src_ids = InterpreterBorrowsCore.compute_context_ids src_ctx in
-  let tgt_ids = InterpreterBorrowsCore.compute_context_ids tgt_ctx in
-  let bids = V.BorrowId.Set.diff src_ids.bids tgt_ids.bids in
-  let aids = V.AbstractionId.Set.diff src_ids.aids tgt_ids.aids in
-  (* End those borrows and abstractions *)
-  let cc = InterpreterBorrows.end_borrows config bids in
-  let cc = comp cc (InterpreterBorrows.end_abstractions config aids) in
-  (* In the target context, merge all the borrows introduced by the loop with
-     their parent abstractions
+     Rem.: this is important to make the match easy (we take care of preserving
+     the structure of the environments, and ending the proper borrows/abstractions,
+     etc.). We could also implement a more general join.
   *)
-  let tgt_ctx =
-    merge_borrows_with_parent_loop_abs config
-      (V.BorrowId.Set.diff tgt_ids.bids src_ids.bids)
-      tgt_ctx
+  let join_suffixes (env0 : C.env) (env1 : C.env) : C.env =
+    (* Sanity check: there are no values/abstractions which should be in the prefix *)
+    let check_valid (ee : C.env_elem) : unit =
+      match ee with
+      | C.Var (C.VarBinder _, _) ->
+          (* Variables are necessarily in the prefix *)
+          raise (Failure "Unreachable")
+      | Var (C.DummyBinder did, _) ->
+          assert (not (C.DummyVarId.Set.mem did old_ids.dids))
+      | Abs abs ->
+          assert (not (V.AbstractionId.Set.mem abs.abs_id old_ids.aids))
+      | Frame -> ()
+    in
+    List.iter check_valid env0;
+    List.iter check_valid env1;
+    (* Concatenate the suffixes and append the abstractions introduced while
+       joining the prefixes *)
+    let absl = List.map (fun abs -> C.Abs abs) !nabs in
+    List.concat [ env0; env1; absl ]
   in
-  (* Replace the source context with the target context - TODO: explain
-     why this works *)
-  let cf_apply_match : cm_fun = fun cf _ -> cf tgt_ctx in
-  let cc = comp cc cf_apply_match in
-  (* Sanity check on the resulting context *)
-  let cc = comp_check_ctx cc Inv.check_invariants in
-  (* Apply and continue *)
-  cc cf src_ctx
-*)
 
-(** Join two contexts *)
-let join_ctxs (_config : C.config) (_old_ids : InterpreterBorrowsCore.ctx_ids)
-    (_ctx0 : C.eval_ctx) (_ctx1 : C.eval_ctx) : ctx_or_update =
-  raise (Failure "Unimplemented")
+  let module S : MatchJoinState = struct
+    (* The context is only used to lookup module data: we can pick whichever we want *)
+    let ctx = ctx
+    let loop_id = loop_id
+    let nabs = nabs
+  end in
+  let module JM = MakeJoinMatcher (S) in
+  let module M = Match (JM) in
+  (* Rem.: this function raises exceptions *)
+  let rec join_prefixes (env0 : C.env) (env1 : C.env) : C.env =
+    match (env0, env1) with
+    | ( (C.Var (C.DummyBinder b0, v0) as var) :: env0',
+        C.Var (C.DummyBinder b1, v1) :: env1' ) ->
+        (* Two cases: the dummy value is an old value, in which case the bindings
+           must be the same and we must join their values. Otherwise, it means we
+           are not in the prefix anymore *)
+        if C.DummyVarId.Set.mem b0 old_ids.dids then (
+          (* Still in the prefix: the values must match *)
+          assert (b0 = b1);
+          assert (v0 = v1);
+          (* Continue *)
+          var :: join_prefixes env0' env1')
+        else (* Not in the prefix anymore *)
+          join_suffixes env0 env1
+    | C.Var (C.VarBinder b0, v0) :: env0', C.Var (C.VarBinder b1, v1) :: env1'
+      ->
+        (* Variable bindings *must* be in the prefix and consequently their
+           ids must be the same *)
+        assert (b0 = b1);
+        (* Match the values *)
+        let b = b0 in
+        let v = M.match_typed_values ctx v0 v1 in
+        let var = C.Var (C.VarBinder b, v) in
+        (* Continue *)
+        var :: join_prefixes env0' env1'
+    | (C.Abs abs0 as abs) :: env0', C.Abs abs1 :: env1' ->
+        (* Same as for the dummy values: there are two cases *)
+        if V.AbstractionId.Set.mem abs0.abs_id old_ids.aids then (
+          (* Still in the prefix: the abstractions must be the same *)
+          assert (abs0 = abs1);
+          (* Continue *)
+          abs :: join_suffixes env0' env1')
+        else (* Not in the prefix anymore *)
+          join_suffixes env0 env1
+    | _ ->
+        (* The elements don't match: we are not in the prefix anymore *)
+        join_suffixes env0 env1
+  in
+
+  try
+    let env = List.rev (join_prefixes env0 env1) in
+
+    (* Construct the joined context - of course, the type, fun, etc. contexts
+     * should be the same in the two contexts *)
+    let {
+      C.type_context;
+      fun_context;
+      global_context;
+      type_vars;
+      env = _;
+      ended_regions = ended_regions0;
+    } =
+      ctx0
+    in
+    let {
+      C.type_context = _;
+      fun_context = _;
+      global_context = _;
+      type_vars = _;
+      env = _;
+      ended_regions = ended_regions1;
+    } =
+      ctx1
+    in
+    let ended_regions = T.RegionId.Set.union ended_regions0 ended_regions1 in
+    Ok
+      {
+        C.type_context;
+        fun_context;
+        global_context;
+        type_vars;
+        env;
+        ended_regions;
+      }
+  with ValueMatchFailure e -> Error e
 
 (** Join the context at the entry of the loop with the contexts upon reentry
     (upon reaching the [Continue] statement - the goal is to compute a fixed
@@ -1195,7 +1209,7 @@ let loop_join_origin_with_continue_ctxs (config : C.config)
   *)
   let joined_ctx = ref old_ctx in
   let rec join_one_aux (ctx : C.eval_ctx) : unit =
-    match join_ctxs config old_ids !joined_ctx ctx with
+    match join_ctxs config loop_id old_ids !joined_ctx ctx with
     | Ok nctx -> joined_ctx := nctx
     | Error err ->
         let ctx =
@@ -1229,28 +1243,6 @@ let loop_join_origin_with_continue_ctxs (config : C.config)
   (* # Return *)
   !joined_ctx
 
-(*
-(** Join a context at the entry of a loop with a context upon reaching
-    a continue in this loop.
- *)
-let loop_join_entry_ctx_with_continue_ctx (_ctx0 : C.eval_ctx)
-    (_ctx1 : C.eval_ctx) : ctx_or_update =
-  failwith "Unimplemented"
-
-(** See {!loop_join_entry_ctx_with_continue_ctx} *)
-let rec loop_join_entry_ctx_with_continue_ctxs (ctx0 : C.eval_ctx)
-    (ctxs : C.eval_ctx list) : ctx_or_update =
-  match ctxs with
-  | [] -> Ok ctx0
-  | ctx1 :: ctxs -> (
-      let res = loop_join_entry_ctx_with_continue_ctx ctx0 ctx1 in
-      match res with
-      | Error _ ->
-          (* TODO: apply the error *)
-          raise (Failure "Unimplemented")
-      | Ok ctx0 -> loop_join_entry_ctx_with_continue_ctxs ctx0 ctxs)
- *)
-
 let compute_loop_entry_fixed_point (config : C.config) (loop_id : V.LoopId.id)
     (eval_loop_body : st_cm_fun) (ctx0 : C.eval_ctx) : C.eval_ctx =
   (* The continuation for when we exit the loop - we register the
@@ -1276,13 +1268,6 @@ let compute_loop_entry_fixed_point (config : C.config) (loop_id : V.LoopId.id)
         raise (Failure "Nested loops are not supported for now")
   in
   (* Join the contexts at the loop entry *)
-  (* TODO: return result:
-   * - end borrows in ctx0
-   * - ok: return joined env
-   * TODO: keep initial env somewhere?
-   * TODO: the returned env is an eval_ctx or smth else?
-   * Maybe simply keep track of existentially quantified variables?
-   *)
   let join_ctxs (ctx0 : C.eval_ctx) : C.eval_ctx =
     let ctx1 = loop_join_origin_with_continue_ctxs config loop_id ctx0 !ctxs in
     ctxs := [];
-- 
cgit v1.2.3