diff options
Diffstat (limited to '')
-rw-r--r-- | compiler/InterpreterBorrows.ml | 133 | ||||
-rw-r--r-- | compiler/InterpreterBorrows.mli | 13 |
2 files changed, 98 insertions, 48 deletions
diff --git a/compiler/InterpreterBorrows.ml b/compiler/InterpreterBorrows.ml index a1cb4ec3..b2a46b1e 100644 --- a/compiler/InterpreterBorrows.ml +++ b/compiler/InterpreterBorrows.ml @@ -1633,7 +1633,7 @@ let destructure_abs (abs_kind : V.abs_kind) (can_end : bool) (* Accumulator to store the destructured values *) let avalues = ref [] in (* Utility function to store a value in the accumulator *) - let push_avalue av = avalues := av :: !avalues in + let push_avalue av = avalues := List.append !avalues [ av ] in (* We use this function to make sure we never register values (i.e., loans or borrows) when we shouldn't - see it as a sanity check: for now, we don't allow nested borrows, which means we should register @@ -1665,21 +1665,22 @@ let destructure_abs (abs_kind : V.abs_kind) (can_end : bool) let ignored = mk_aignored child_av.V.ty in let value = V.ALoan (V.ASharedLoan (bids, sv, ignored)) in push { V.value; ty }; + (* Explore the child *) + list_avalues false push_fail child_av; (* Push the avalues introduced because we decomposed the inner loans - note that we pay attention to the order in which we introduce the avalues: we want to push them *after* the outer loan. If we didn't want that, we would have implemented [list_values] in exactly the same way as [list_avalues] (i.e., with a similar signature) *) - List.iter push avl; - (* Explore the child *) - list_avalues false push_fail child_av + List.iter push avl | V.AMutLoan (bid, child_av) -> + (* Explore the child *) + list_avalues false push_fail child_av; + (* Explore the whole loan *) let ignored = mk_aignored child_av.V.ty in let value = V.ALoan (V.AMutLoan (bid, ignored)) in - push { V.value; ty }; - (* Explore the child *) - list_avalues false push_fail child_av + push { V.value; ty } | V.AIgnoredMutLoan (opt_bid, child_av) -> (* We don't support nested borrows for now *) assert (not (ty_has_borrows ctx.type_context.type_infos child_av.ty)); @@ -1702,11 +1703,12 @@ let destructure_abs (abs_kind : V.abs_kind) (can_end : bool) (* Explore the borrow content *) match bc with | V.AMutBorrow (bid, child_av) -> + (* Explore the child *) + list_avalues false push_fail child_av; + (* Explore the borrow *) let ignored = mk_aignored child_av.V.ty in let value = V.ABorrow (V.AMutBorrow (bid, ignored)) in - push { V.value; ty }; - (* Explore the child *) - list_avalues false push_fail child_av + push { V.value; ty } | V.ASharedBorrow _ -> (* Nothing specific to do: keep the value as it is *) push av @@ -1761,12 +1763,27 @@ let destructure_abs (abs_kind : V.abs_kind) (can_end : bool) let rty = ety_no_regions_to_rty ty in let av : V.typed_avalue = assert (not (value_has_loans_or_borrows ctx sv.V.value)); + (* We introduce fresh ids for the symbolic values *) + let mk_value_with_fresh_sids (v : V.typed_value) : V.typed_value + = + let visitor = + object + inherit [_] V.map_typed_avalue + + method! visit_symbolic_value_id _ _ = + C.fresh_symbolic_value_id () + end + in + visitor#visit_typed_value () v + in + let sv = mk_value_with_fresh_sids sv in + (* Create the new avalue *) let value = V.ALoan (V.ASharedLoan (bids, sv, mk_aignored rty)) in { V.value; ty = rty } in - let avl = List.append avl [ av ] in + let avl = List.append [ av ] avl in (avl, sv) else (avl, { v with V.value = V.Loan (V.SharedLoan (bids, sv)) }) | MutLoan _ -> raise (Failure "Unreachable")) @@ -1779,7 +1796,7 @@ let destructure_abs (abs_kind : V.abs_kind) (can_end : bool) (* Destructure the avalues *) List.iter (list_avalues true push_avalue) abs0.V.avalues; - let avalues = List.rev !avalues in + let avalues = !avalues in (* Update *) { abs0 with V.avalues; kind = abs_kind; can_end } @@ -1969,14 +1986,15 @@ type merge_abstraction_info = { We compute the list of loan/borrow contents, maps from borrow/loan ids to borrow/loan contents, etc. - Note that this function is very specific to [merge_abstractions]: we + Note that this function is very specific to [merge_into_abstraction]: we have strong assumptions about the shape of the abstraction, and in particular that: - its values don't contain nested borrows - all the borrows are destructured (for instance, shared loans can't contain shared loans). *) -let compute_merge_abstractions_info (abs : V.abs) : merge_abstraction_info = +let compute_merge_abstraction_info (ctx : C.eval_ctx) (abs : V.abs) : + merge_abstraction_info = let loans : V.loan_id_set ref = ref V.BorrowId.Set.empty in let borrows : V.borrow_id_set ref = ref V.BorrowId.Set.empty in let borrows_loans : borrow_or_loan_id list ref = ref [] in @@ -2086,9 +2104,9 @@ let compute_merge_abstractions_info (abs : V.abs) : merge_abstraction_info = raise (Failure "Unreachable")); super#visit_aborrow_content env bc - method! visit_symbolic_value _ _ = - (* Sanity check *) - raise (Failure "Unreachable") + method! visit_symbolic_value _ sv = + (* Sanity check: no borrows *) + assert (not (symbolic_value_has_borrows ctx sv)) end in @@ -2165,12 +2183,12 @@ type merge_duplicates_funcs = { Merge two abstractions into one, without updating the context. *) -let merge_abstractions_aux (abs_kind : V.abs_kind) (can_end : bool) +let merge_into_abstraction_aux (abs_kind : V.abs_kind) (can_end : bool) (merge_funs : merge_duplicates_funcs option) (ctx : C.eval_ctx) (abs0 : V.abs) (abs1 : V.abs) : V.abs = log#ldebug (lazy - ("merge_abstractions_aux:\n- abs0:\n" ^ abs_to_string ctx abs0 + ("merge_into_abstraction_aux:\n- abs0:\n" ^ abs_to_string ctx abs0 ^ "\n\n- abs1:\n" ^ abs_to_string ctx abs1)); (* Check that the abstractions are destructured *) @@ -2187,7 +2205,7 @@ let merge_abstractions_aux (abs_kind : V.abs_kind) (can_end : bool) loan_to_content = loan_to_content0; borrow_to_content = borrow_to_content0; } = - compute_merge_abstractions_info abs0 + compute_merge_abstraction_info ctx abs0 in let { @@ -2197,7 +2215,7 @@ let merge_abstractions_aux (abs_kind : V.abs_kind) (can_end : bool) loan_to_content = loan_to_content1; borrow_to_content = borrow_to_content1; } = - compute_merge_abstractions_info abs1 + compute_merge_abstraction_info ctx abs1 in (* Sanity check: there is no loan/borrows which appears in both abstractions, @@ -2227,7 +2245,16 @@ let merge_abstractions_aux (abs_kind : V.abs_kind) (can_end : bool) let merged_borrows = ref V.BorrowId.Set.empty in let merged_loans = ref V.BorrowId.Set.empty in let avalues = ref [] in - let push_avalue av = avalues := av :: !avalues in + let push_avalue av = + log#ldebug + (lazy + ("merge_into_abstraction_aux: push_avalue: " + ^ typed_avalue_to_string ctx av)); + avalues := av :: !avalues + in + let push_opt_avalue av = + match av with None -> () | Some av -> push_avalue av + in let intersect = V.BorrowId.Set.union @@ -2251,6 +2278,7 @@ let merge_abstractions_aux (abs_kind : V.abs_kind) (can_end : bool) let set_loan_as_merged id = merged_loans := V.BorrowId.Set.add id !merged_loans in + let set_loans_as_merged ids = V.BorrowId.Set.iter set_loan_as_merged ids in (* Some utility functions *) (* Merge two aborrow contents - note that those contents must have the same id *) @@ -2284,13 +2312,13 @@ let merge_abstractions_aux (abs_kind : V.abs_kind) (can_end : bool) in let merge_aloan_contents (ty0 : T.rty) (lc0 : V.aloan_content) (ty1 : T.rty) - (lc1 : V.aloan_content) : V.typed_avalue = + (lc1 : V.aloan_content) : V.typed_avalue option = match (lc0, lc1) with | V.AMutLoan (id, child0), V.AMutLoan (_, child1) -> (* Register the loan id *) set_loan_as_merged id; (* Merge *) - (Option.get merge_funs).merge_amut_loans id ty0 child0 ty1 child1 + Some ((Option.get merge_funs).merge_amut_loans id ty0 child0 ty1 child1) | ASharedLoan (ids0, sv0, child0), ASharedLoan (ids1, sv1, child1) -> (* Filter the ids *) let ids0 = filter_bids ids0 in @@ -2298,14 +2326,30 @@ let merge_abstractions_aux (abs_kind : V.abs_kind) (can_end : bool) (* Check that the sets of ids are the same - if it is not the case, it means we actually need to merge more than 2 avalues: we ignore this case for now *) - assert (ids0 = ids1); + assert (V.BorrowId.Set.equal ids0 ids1); let ids = ids0 in - assert (not (V.BorrowId.Set.is_empty ids)); - (* Register the loan ids *) - V.BorrowId.Set.iter set_loan_as_merged ids; - (* Merge *) - (Option.get merge_funs).merge_ashared_loans ids ty0 sv0 child0 ty1 sv1 - child1 + if V.BorrowId.Set.is_empty ids then ( + (* If the set of ids is empty, we can eliminate this shared loan. + For now, we check that we can eliminate the whole shared value + altogether. + A more complete approach would be to explore the value and introduce + as many shared loans/borrows/etc. as necessary for the sub-values. + For now, we check that there are no such values that we would need + to preserve (in practice it works because we destructure the + shared values in the abstractions, and forbid nested borrows). + *) + assert (not (value_has_loans_or_borrows ctx sv0.V.value)); + assert (not (value_has_loans_or_borrows ctx sv0.V.value)); + assert (is_aignored child0.V.value); + assert (is_aignored child1.V.value); + None) + else ( + (* Register the loan ids *) + set_loans_as_merged ids; + (* Merge *) + Some + ((Option.get merge_funs).merge_ashared_loans ids ty0 sv0 child0 ty1 + sv1 child1)) | _ -> (* Unreachable because those cases are ignored (ended/ignored borrows) or inconsistent *) @@ -2316,7 +2360,7 @@ let merge_abstractions_aux (abs_kind : V.abs_kind) (can_end : bool) to register the merged loan ids: the caller doesn't do it (contrary to the borrow case) *) let merge_g_loan_contents (lc0 : g_loan_content_with_ty) - (lc1 : g_loan_content_with_ty) : V.typed_avalue = + (lc1 : g_loan_content_with_ty) : V.typed_avalue option = match (lc0, lc1) with | Concrete _, Concrete _ -> (* This can not happen: the values should have been destructured *) @@ -2339,7 +2383,7 @@ let merge_abstractions_aux (abs_kind : V.abs_kind) (can_end : bool) | BorrowId bid -> log#ldebug (lazy - ("merge_abstractions_aux: merging borrow " + ("merge_into_abstraction_aux: merging borrow " ^ V.BorrowId.to_string bid)); (* Check if the borrow has already been merged - this can happen @@ -2386,7 +2430,7 @@ let merge_abstractions_aux (abs_kind : V.abs_kind) (can_end : bool) else ( log#ldebug (lazy - ("merge_abstractions_aux: merging loan " + ("merge_into_abstraction_aux: merging loan " ^ V.BorrowId.to_string bid)); (* Check if we need to filter it *) @@ -2397,7 +2441,7 @@ let merge_abstractions_aux (abs_kind : V.abs_kind) (can_end : bool) let lc0 = V.BorrowId.Map.find_opt bid loan_to_content0 in let lc1 = V.BorrowId.Map.find_opt bid loan_to_content1 in (* Merge *) - let av : V.typed_avalue = + let av : V.typed_avalue option = match (lc0, lc1) with | None, Some lc | Some lc, None -> ( match lc with @@ -2414,8 +2458,11 @@ let merge_abstractions_aux (abs_kind : V.abs_kind) (can_end : bool) assert ( not (value_has_loans_or_borrows ctx sv.V.value)); let lc = V.ASharedLoan (bids, sv, child) in - { V.value = V.ALoan lc; ty } - | V.AMutLoan _ -> { V.value = V.ALoan lc; ty } + set_loans_as_merged bids; + Some { V.value = V.ALoan lc; ty } + | V.AMutLoan _ -> + set_loan_as_merged bid; + Some { V.value = V.ALoan lc; ty } | V.AEndedMutLoan _ | V.AEndedSharedLoan _ | V.AIgnoredMutLoan _ | V.AEndedIgnoredMutLoan _ | V.AIgnoredSharedLoan _ -> @@ -2426,7 +2473,7 @@ let merge_abstractions_aux (abs_kind : V.abs_kind) (can_end : bool) merge_g_loan_contents lc0 lc1 | None, None -> raise (Failure "Unreachable") in - push_avalue av)) + push_opt_avalue av)) borrows_loans; (* Reverse the avalues (we visited the loans/borrows in order, but pushed @@ -2488,7 +2535,7 @@ let ctx_merge_regions (ctx : C.eval_ctx) (rid : T.RegionId.id) let env = Substitute.env_subst_rids rsubst ctx.env in { ctx with C.env } -let merge_abstractions (abs_kind : V.abs_kind) (can_end : bool) +let merge_into_abstraction (abs_kind : V.abs_kind) (can_end : bool) (merge_funs : merge_duplicates_funcs option) (ctx : C.eval_ctx) (abs_id0 : V.AbstractionId.id) (abs_id1 : V.AbstractionId.id) : C.eval_ctx * V.AbstractionId.id = @@ -2497,10 +2544,12 @@ let merge_abstractions (abs_kind : V.abs_kind) (can_end : bool) let abs1 = C.ctx_lookup_abs ctx abs_id1 in (* Merge them *) - let nabs = merge_abstractions_aux abs_kind can_end merge_funs ctx abs0 abs1 in + let nabs = + merge_into_abstraction_aux abs_kind can_end merge_funs ctx abs0 abs1 + in - (* Update the environment: replace the first abstraction with the result of the merge, - remove the second abstraction *) + (* Update the environment: replace the abstraction 1 with the result of the merge, + remove the abstraction 0 *) let ctx = fst (C.ctx_subst_abs ctx abs_id1 nabs) in let ctx = fst (C.ctx_remove_abs ctx abs_id0) in diff --git a/compiler/InterpreterBorrows.mli b/compiler/InterpreterBorrows.mli index 9e407399..31b67bd7 100644 --- a/compiler/InterpreterBorrows.mli +++ b/compiler/InterpreterBorrows.mli @@ -132,7 +132,7 @@ val abs_is_destructured : bool -> C.eval_ctx -> V.abs -> bool val convert_value_to_abstractions : V.abs_kind -> bool -> bool -> C.eval_ctx -> V.typed_value -> V.abs list -(** See {!merge_abstractions}. +(** See {!merge_into_abstraction}. Rem.: it may be more idiomatic to have a functor, but this seems a bit heavyweight, though. @@ -199,9 +199,10 @@ type merge_duplicates_funcs = { (** Merge an abstraction into another abstraction. - We insert the result of the merge in place of the first abstraction (this - helps preserving the structure of the environment, when computing loop - fixed points for instance). + We insert the result of the merge in place of the second abstraction (and in + particular, we don't simply push the merged abstraction at the end of the + environment: this helps preserving the structure of the environment, when + computing loop fixed points for instance). When we merge two abstractions together, we remove the loans/borrows which appear in one and whose associated loans/borrows appear in the @@ -236,7 +237,7 @@ type merge_duplicates_funcs = { abs'0 { mut_borrow l0, mut_loan l1 } // mut_borrow l0 ! abs'1 { mut_borrow l0, mut_loan l2 } // mut_borrow l0 ! ]} - If you want to forbid this, provide [None]. In that case, [merge_abstractions] + If you want to forbid this, provide [None]. In that case, [merge_into_abstraction] actually simply performs some sort of a union. - [ctx] @@ -246,7 +247,7 @@ type merge_duplicates_funcs = { We return the updated context as well as the id of the new abstraction which results from the merge. *) -val merge_abstractions : +val merge_into_abstraction : V.abs_kind -> bool -> merge_duplicates_funcs option -> |