summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/InterpreterBorrows.ml105
1 files changed, 35 insertions, 70 deletions
diff --git a/compiler/InterpreterBorrows.ml b/compiler/InterpreterBorrows.ml
index c8a7ae7f..288ebc27 100644
--- a/compiler/InterpreterBorrows.ml
+++ b/compiler/InterpreterBorrows.ml
@@ -23,10 +23,11 @@ let log = InterpreterBorrowsCore.log
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.
+ [allowed_abs]: if not [None], allows to get a borrow in the given
+ abstraction without ending the whole abstraction first. This parameter
+ allows us to use {!end_borrow} as an auxiliary function for
+ {!end_abstraction} (we end all the borrows in the abstraction one by one
+ before removing the abstraction from the context).
*)
let end_borrow_get_borrow (allowed_abs : V.AbstractionId.id option)
(l : V.BorrowId.id) (ctx : C.eval_ctx) :
@@ -789,38 +790,20 @@ let convert_avalue_to_given_back_value (abs_kind : V.abs_kind)
(** End a borrow identified by its borrow id in a context.
+ This function **preserves invariants** provided [allowed_abs] is [None]: if the
+ borrow is inside another borrow/an abstraction, we end the outer borrow/abstraction
+ first, etc.
+
+ [allowed_abs]: see the comment for {!end_borrow_get_borrow}.
+
+ [chain]: contains the list of borrows/abstraction ids on which {!end_borrow}
+ and {!end_abstraction} were called, to remember the chain of calls. This is
+ useful for debugging purposes, and also for sanity checks to detect cycles
+ (which shouldn't happen if the environment is well-formed).
+
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).
+ The reason is that when ending borrows we may end abstractions, which requires
+ generating code for the translation (and we do this in CPS).
TODO: we should split this function in two: one function which doesn't
perform anything smart and is trusted, and another function for the
@@ -871,13 +854,20 @@ let rec end_borrow (config : C.config) (chain : borrow_or_abs_ids)
comp cf_check_disappeared (Invariants.cf_check_invariants config)
in
- (* Start by getting the borrow *)
+ (* Start by ending the borrow itself (we lookup it up and replace it with [Bottom] *)
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: we found outer borrows (the borrow is inside a borrowed value) or
+ inner loans (the borrow contains loans)
+ - success: we didn't find outer borrows when updating (but maybe we actually
+ didn't find the borrow we were looking for...). The borrow was successfully
+ replaced with [Bottom], and we can proceed to ending the corresponding loan.
+
+ Note that if [allowed_abs] is [Some abs_id] and the borrow is inside the
+ abstraction identified by [abs_id], the abstraction is ignored (i.e.:
+ {!end_borrow_get_borrow} won't return [Error] because of the abstraction
+ itself).
+ *)
| Error priority -> (
(* Debug *)
log#ldebug
@@ -885,7 +875,7 @@ let rec end_borrow (config : C.config) (chain : borrow_or_abs_ids)
("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
+ (* End the priority borrows, abstractions, then try again to end the target
* borrow (if necessary) *)
match priority with
| OuterBorrows (Borrows bids) | InnerLoans (Borrows bids) ->
@@ -908,34 +898,8 @@ let rec end_borrow (config : C.config) (chain : borrow_or_abs_ids)
(* 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 _ | DummyVarId _), _ ->
- (* 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
+ (* The borrow is inside an abstraction: end the whole abstraction *)
+ let cf_end_abs = end_abstraction config chain abs_id in
(* Compose with a sanity check *)
comp cf_end_abs cf_check cf ctx)
| Ok (ctx, None) ->
@@ -945,7 +909,8 @@ let rec end_borrow (config : C.config) (chain : borrow_or_abs_ids)
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) *)
+ (* We found a borrow and replaced it with [Bottom]: 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