summaryrefslogtreecommitdiff
path: root/src/InterpreterExpressions.ml
diff options
context:
space:
mode:
Diffstat (limited to 'src/InterpreterExpressions.ml')
-rw-r--r--src/InterpreterExpressions.ml720
1 files changed, 0 insertions, 720 deletions
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