diff options
Diffstat (limited to 'src/Contexts.ml')
-rw-r--r-- | src/Contexts.ml | 472 |
1 files changed, 0 insertions, 472 deletions
diff --git a/src/Contexts.ml b/src/Contexts.ml deleted file mode 100644 index 510976f4..00000000 --- a/src/Contexts.ml +++ /dev/null @@ -1,472 +0,0 @@ -open Types -open Values -open LlbcAst -module V = Values -open ValuesUtils - -(** Some global counters. - - Note that those counters were initially stored in {!eval_ctx} values, - but it proved better to make them global and stateful: - - when branching (and thus executing on several paths with different - contexts) it is better to really have unique ids everywhere (and - not have fresh ids shared by several contexts even though introduced - after the branching) because at some point we might need to merge the - different contexts - - also, it is a lot more convenient to not store those counters in contexts - objects - - ============= - **WARNING**: - ============= - Pay attention when playing with closures, as you may not always generate - fresh identifiers without noticing it, especially when using type abbreviations. - For instance, consider the following: - {[ - type fun_type = unit -> ... - fn f x : fun_type = - let id = fresh_id () in - ... - - let g = f x in // <-- the fresh identifier gets generated here - let x1 = g () in // <-- no fresh generation here - let x2 = g () in - ... - ]} - - This is why, in such cases, we often introduce all the inputs, even - when they are not used (which happens!). - {[ - fn f x : fun_type = - fun .. -> - let id = fresh_id () in - ... - ]} - - Note that in practice, we never reuse closures, except when evaluating - a branching in the execution (which is fine, because the branches evaluate - independentely of each other). Still, it is always a good idea to be - defensive. - - However, the same problem arises with logging. - - Also, a more defensive way would be to not use global references, and - store the counters in the evaluation context. This is actually what was - originally done, before we updated the code to use global counters because - it proved more convenient (and even before updating the code of the - interpreter to use CPS). - *) - -let symbolic_value_id_counter, fresh_symbolic_value_id = - SymbolicValueId.fresh_stateful_generator () - -let borrow_id_counter, fresh_borrow_id = BorrowId.fresh_stateful_generator () -let region_id_counter, fresh_region_id = RegionId.fresh_stateful_generator () - -let abstraction_id_counter, fresh_abstraction_id = - AbstractionId.fresh_stateful_generator () - -let fun_call_id_counter, fresh_fun_call_id = - FunCallId.fresh_stateful_generator () - -(** We shouldn't need to reset the global counters, but it might be good to - do it from time to time, for instance every time we start evaluating/ - synthesizing a function. - - The reasons are manifold: - - it might prevent the counters from overflowing (although this seems - extremely unlikely - as a side node: we have overflow checks to make - sure the synthesis doesn't get impacted by potential overflows) - - most importantly, it allows to always manipulate low values, which - is always a lot more readable when debugging - *) -let reset_global_counters () = - symbolic_value_id_counter := SymbolicValueId.generator_zero; - borrow_id_counter := BorrowId.generator_zero; - region_id_counter := RegionId.generator_zero; - abstraction_id_counter := AbstractionId.generator_zero; - fun_call_id_counter := FunCallId.generator_zero - -(** A binder used in an environment, to map a variable to a value *) -type binder = { - index : VarId.id; (** Unique variable identifier *) - name : string option; (** Possible name *) -} -[@@deriving show] - -(** Environment value: mapping from variable to value, abstraction (only - used in symbolic mode) or stack frame delimiter. - - TODO: rename Var (-> Binding?) - *) -type env_elem = - | Var of (binder option[@opaque]) * typed_value - (** Variable binding - the binder is None if the variable is a dummy variable - (we use dummy variables to store temporaries while doing bookkeeping such - as ending borrows for instance). *) - | Abs of abs - | Frame -[@@deriving - show, - visitors - { - name = "iter_env_elem"; - variety = "iter"; - ancestors = [ "iter_abs" ]; - nude = true (* Don't inherit {!VisitorsRuntime.iter} *); - concrete = true; - }, - visitors - { - name = "map_env_elem"; - variety = "map"; - ancestors = [ "map_abs" ]; - nude = true (* Don't inherit {!VisitorsRuntime.iter} *); - concrete = true; - }] - -type env = env_elem list -[@@deriving - show, - visitors - { - name = "iter_env"; - variety = "iter"; - ancestors = [ "iter_env_elem" ]; - nude = true (* Don't inherit {!VisitorsRuntime.iter} *); - concrete = true; - }, - visitors - { - name = "map_env"; - variety = "map"; - ancestors = [ "map_env_elem" ]; - nude = true (* Don't inherit {!VisitorsRuntime.iter} *); - concrete = true; - }] - -type interpreter_mode = ConcreteMode | SymbolicMode [@@deriving show] - -type config = { - mode : interpreter_mode; - (** Concrete mode (interpreter) or symbolic mode (for synthesis) **) - check_invariants : bool; - (** Check that invariants are maintained whenever we execute a statement *) - greedy_expand_symbolics_with_borrows : bool; - (** Expand all symbolic values containing borrows upon introduction - allows - to use restrict ourselves to a simpler model for the projectors over - symbolic values. - The interpreter fails if doing this requires to do a branching (because - we need to expand an enumeration with strictly more than one variant) - or if we need to expand a recursive type (because this leads to looping). - *) - allow_bottom_below_borrow : bool; - (** Experimental. - - We sometimes want to temporarily break the invariant that there is no - bottom value below a borrow. If this value is true, we don't check - the invariant, and the rule becomes: we can't end a borrow *if* it contains - a bottom value. The consequence is that it becomes ok to temporarily - have bottom below a borrow, if we put something else inside before ending - the borrow. - - For instance, when evaluating an assignment, we move the value which - will be overwritten then do some administrative tasks with the borrows, - then move the rvalue to its destination. We currently want to be able - to check the invariants every time we end a borrow/an abstraction, - meaning at intermediate steps of the assignment where the invariants - might actually be broken. - *) - return_unit_end_abs_with_no_loans : bool; - (** If a function doesn't return any borrows, we can immediately call - its backward functions. If this option is on, whenever we call a - function *and* this function returns unit, we immediately end all the - abstractions which are introduced and don't contain loans. This can be - useful to make the code cleaner (the backward function is introduced - where the function call happened) and make sure all forward functions - with no return value are followed by a backward function. - *) -} -[@@deriving show] - -(** See {!config} *) -type partial_config = { - check_invariants : bool; - greedy_expand_symbolics_with_borrows : bool; - allow_bottom_below_borrow : bool; - return_unit_end_abs_with_no_loans : bool; -} - -let config_of_partial (mode : interpreter_mode) (config : partial_config) : - config = - { - mode; - check_invariants = config.check_invariants; - greedy_expand_symbolics_with_borrows = - config.greedy_expand_symbolics_with_borrows; - allow_bottom_below_borrow = config.allow_bottom_below_borrow; - return_unit_end_abs_with_no_loans = config.return_unit_end_abs_with_no_loans; - } - -type type_context = { - type_decls_groups : Crates.type_declaration_group TypeDeclId.Map.t; - type_decls : type_decl TypeDeclId.Map.t; - type_infos : TypesAnalysis.type_infos; -} -[@@deriving show] - -type fun_context = { fun_decls : fun_decl FunDeclId.Map.t } [@@deriving show] - -type global_context = { global_decls : global_decl GlobalDeclId.Map.t } -[@@deriving show] - -(** Evaluation context *) -type eval_ctx = { - type_context : type_context; - fun_context : fun_context; - global_context : global_context; - type_vars : type_var list; - env : env; - ended_regions : RegionId.Set.t; -} -[@@deriving show] - -let lookup_type_var (ctx : eval_ctx) (vid : TypeVarId.id) : type_var = - TypeVarId.nth ctx.type_vars vid - -let opt_binder_has_vid (bv : binder option) (vid : VarId.id) : bool = - match bv with Some bv -> bv.index = vid | None -> false - -let ctx_lookup_binder (ctx : eval_ctx) (vid : VarId.id) : binder = - (* TOOD: we might want to stop at the end of the frame *) - let rec lookup env = - match env with - | [] -> - raise (Invalid_argument ("Variable not found: " ^ VarId.to_string vid)) - | Var (var, _) :: env' -> - if opt_binder_has_vid var vid then Option.get var else lookup env' - | (Abs _ | Frame) :: env' -> lookup env' - in - lookup ctx.env - -(** TODO: make this more efficient with maps *) -let ctx_lookup_type_decl (ctx : eval_ctx) (tid : TypeDeclId.id) : type_decl = - TypeDeclId.Map.find tid ctx.type_context.type_decls - -(** TODO: make this more efficient with maps *) -let ctx_lookup_fun_decl (ctx : eval_ctx) (fid : FunDeclId.id) : fun_decl = - FunDeclId.Map.find fid ctx.fun_context.fun_decls - -(** TODO: make this more efficient with maps *) -let ctx_lookup_global_decl (ctx : eval_ctx) (gid : GlobalDeclId.id) : - global_decl = - GlobalDeclId.Map.find gid ctx.global_context.global_decls - -(** Retrieve a variable's value in an environment *) -let env_lookup_var_value (env : env) (vid : VarId.id) : typed_value = - (* We take care to stop at the end of current frame: different variables - in different frames can have the same id! - *) - let rec lookup env = - match env with - | [] -> failwith "Unexpected" - | Var (var, v) :: env' -> - if opt_binder_has_vid var vid then v else lookup env' - | Abs _ :: env' -> lookup env' - | Frame :: _ -> failwith "End of frame" - in - lookup env - -(** Retrieve a variable's value in an evaluation context *) -let ctx_lookup_var_value (ctx : eval_ctx) (vid : VarId.id) : typed_value = - env_lookup_var_value ctx.env vid - -(** Update a variable's value in an environment - - This is a helper function: it can break invariants and doesn't perform - any check. -*) -let env_update_var_value (env : env) (vid : VarId.id) (nv : typed_value) : env = - (* We take care to stop at the end of current frame: different variables - in different frames can have the same id! - *) - let rec update env = - match env with - | [] -> failwith "Unexpected" - | Var (var, v) :: env' -> - if opt_binder_has_vid var vid then Var (var, nv) :: env' - else Var (var, v) :: update env' - | Abs abs :: env' -> Abs abs :: update env' - | Frame :: _ -> failwith "End of frame" - in - update env - -let var_to_binder (var : var) : binder = { index = var.index; name = var.name } - -(** Update a variable's value in an evaluation context. - - This is a helper function: it can break invariants and doesn't perform - any check. -*) -let ctx_update_var_value (ctx : eval_ctx) (vid : VarId.id) (nv : typed_value) : - eval_ctx = - { ctx with env = env_update_var_value ctx.env vid nv } - -(** Push a variable in the context's environment. - - Checks that the pushed variable and its value have the same type (this - is important). -*) -let ctx_push_var (ctx : eval_ctx) (var : var) (v : typed_value) : eval_ctx = - assert (var.var_ty = v.ty); - let bv = var_to_binder var in - { ctx with env = Var (Some bv, v) :: ctx.env } - -(** Push a list of variables. - - Checks that the pushed variables and their values have the same type (this - is important). -*) -let ctx_push_vars (ctx : eval_ctx) (vars : (var * typed_value) list) : eval_ctx - = - assert ( - List.for_all - (fun (var, (value : typed_value)) -> var.var_ty = value.ty) - vars); - let vars = - List.map (fun (var, value) -> Var (Some (var_to_binder var), value)) vars - in - let vars = List.rev vars in - { ctx with env = List.append vars ctx.env } - -(** Push a dummy variable in the context's environment. *) -let ctx_push_dummy_var (ctx : eval_ctx) (v : typed_value) : eval_ctx = - { ctx with env = Var (None, v) :: ctx.env } - -(** Pop the first dummy variable from a context's environment. *) -let ctx_pop_dummy_var (ctx : eval_ctx) : eval_ctx * typed_value = - let rec pop_var (env : env) : env * typed_value = - match env with - | [] -> failwith "Could not find a dummy variable" - | Var (None, v) :: env -> (env, v) - | ee :: env -> - let env, v = pop_var env in - (ee :: env, v) - in - let env, v = pop_var ctx.env in - ({ ctx with env }, v) - -(** Read the first dummy variable in a context's environment. *) -let ctx_read_first_dummy_var (ctx : eval_ctx) : typed_value = - let rec read_var (env : env) : typed_value = - match env with - | [] -> failwith "Could not find a dummy variable" - | Var (None, v) :: _env -> v - | _ :: env -> read_var env - in - read_var ctx.env - -(** Push an uninitialized variable (which thus maps to {!Values.Bottom}) *) -let ctx_push_uninitialized_var (ctx : eval_ctx) (var : var) : eval_ctx = - ctx_push_var ctx var (mk_bottom var.var_ty) - -(** Push a list of uninitialized variables (which thus map to {!Values.Bottom}) *) -let ctx_push_uninitialized_vars (ctx : eval_ctx) (vars : var list) : eval_ctx = - let vars = List.map (fun v -> (v, mk_bottom v.var_ty)) vars in - ctx_push_vars ctx vars - -let env_lookup_abs (env : env) (abs_id : V.AbstractionId.id) : V.abs = - let rec lookup env = - match env with - | [] -> failwith "Unexpected" - | Var (_, _) :: env' -> lookup env' - | Abs abs :: env' -> if abs.abs_id = abs_id then abs else lookup env' - | Frame :: env' -> lookup env' - in - lookup env - -let ctx_lookup_abs (ctx : eval_ctx) (abs_id : V.AbstractionId.id) : V.abs = - env_lookup_abs ctx.env abs_id - -let ctx_type_decl_is_rec (ctx : eval_ctx) (id : TypeDeclId.id) : bool = - let decl_group = TypeDeclId.Map.find id ctx.type_context.type_decls_groups in - match decl_group with Crates.Rec _ -> true | Crates.NonRec _ -> false - -(** Visitor to iterate over the values in the *current* frame *) -class ['self] iter_frame = - object (self : 'self) - inherit [_] V.iter_abs - - method visit_Var : 'acc -> binder option -> typed_value -> unit = - fun acc _vid v -> self#visit_typed_value acc v - - method visit_Abs : 'acc -> abs -> unit = - fun acc abs -> self#visit_abs acc abs - - method visit_env_elem : 'acc -> env_elem -> unit = - fun acc em -> - match em with - | Var (vid, v) -> self#visit_Var acc vid v - | Abs abs -> self#visit_Abs acc abs - | Frame -> failwith "Unreachable" - - method visit_env : 'acc -> env -> unit = - fun acc env -> - match env with - | [] -> () - | Frame :: _ -> (* We stop here *) () - | em :: env -> - self#visit_env_elem acc em; - self#visit_env acc env - end - -(** Visitor to map over the values in the *current* frame *) -class ['self] map_frame_concrete = - object (self : 'self) - inherit [_] V.map_abs - - method visit_Var : 'acc -> binder option -> typed_value -> env_elem = - fun acc vid v -> - let v = self#visit_typed_value acc v in - Var (vid, v) - - method visit_Abs : 'acc -> abs -> env_elem = - fun acc abs -> Abs (self#visit_abs acc abs) - - method visit_env_elem : 'acc -> env_elem -> env_elem = - fun acc em -> - match em with - | Var (vid, v) -> self#visit_Var acc vid v - | Abs abs -> self#visit_Abs acc abs - | Frame -> failwith "Unreachable" - - method visit_env : 'acc -> env -> env = - fun acc env -> - match env with - | [] -> [] - | Frame :: env -> (* We stop here *) Frame :: env - | em :: env -> - let em = self#visit_env_elem acc em in - let env = self#visit_env acc env in - em :: env - end - -(** Visitor to iterate over the values in a context *) -class ['self] iter_eval_ctx = - object (_self : 'self) - inherit [_] iter_env as super - - method visit_eval_ctx : 'acc -> eval_ctx -> unit = - fun acc ctx -> super#visit_env acc ctx.env - end - -(** Visitor to map the values in a context *) -class ['self] map_eval_ctx = - object (_self : 'self) - inherit [_] map_env as super - - method visit_eval_ctx : 'acc -> eval_ctx -> eval_ctx = - fun acc ctx -> - let env = super#visit_env acc ctx.env in - { ctx with env } - end |