From 8dbfc4926113489f514b19d17510821a4059f349 Mon Sep 17 00:00:00 2001 From: Julien Signoles <julien.signoles@cea.fr> Date: Wed, 21 Dec 2011 17:40:22 +0000 Subject: [PATCH] better modularisation --- src/plugins/e-acsl/Makefile.in | 12 +- src/plugins/e-acsl/TODO | 2 +- src/plugins/e-acsl/quantif.ml | 233 +++++++++++++++++++++++++ src/plugins/e-acsl/quantif.mli | 41 +++++ src/plugins/e-acsl/typing.ml | 117 +++++++++++++ src/plugins/e-acsl/typing.mli | 38 +++++ src/plugins/e-acsl/visit.ml | 301 ++------------------------------- 7 files changed, 453 insertions(+), 291 deletions(-) create mode 100644 src/plugins/e-acsl/quantif.ml create mode 100644 src/plugins/e-acsl/quantif.mli create mode 100644 src/plugins/e-acsl/typing.ml create mode 100644 src/plugins/e-acsl/typing.mli diff --git a/src/plugins/e-acsl/Makefile.in b/src/plugins/e-acsl/Makefile.in index b088a3edf52..d1de8fd647b 100644 --- a/src/plugins/e-acsl/Makefile.in +++ b/src/plugins/e-acsl/Makefile.in @@ -33,7 +33,17 @@ PLUGIN_DIR ?=. PLUGIN_ENABLE:=@ENABLE_E_ACSL@ PLUGIN_DYNAMIC:=@DYNAMIC_E_ACSL@ PLUGIN_NAME:=E_ACSL -PLUGIN_CMO:= local_config options read_header error misc mpz env visit main +PLUGIN_CMO:= local_config \ + options \ + read_header \ + error \ + misc \ + mpz \ + env \ + typing \ + quantif \ + visit \ + main PLUGIN_HAS_MLI:=yes # Enable -warn-error, but do not distribute the plug-in with this option being diff --git a/src/plugins/e-acsl/TODO b/src/plugins/e-acsl/TODO index 196b0a77559..2b21ec90db9 100644 --- a/src/plugins/e-acsl/TODO +++ b/src/plugins/e-acsl/TODO @@ -66,7 +66,7 @@ - test sizeof.i devraient être plus précis quand logic_typing plus précis - structs - unions -- quantifications entières invalides +- inclure exemple du E-ACSL Reference Manual #################### # AVANT LA DISTRIB # diff --git a/src/plugins/e-acsl/quantif.ml b/src/plugins/e-acsl/quantif.ml new file mode 100644 index 00000000000..b78a919472f --- /dev/null +++ b/src/plugins/e-acsl/quantif.ml @@ -0,0 +1,233 @@ +(**************************************************************************) +(* *) +(* This file is part of the E-ACSL plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2011 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Cil_types +open Cil +open Cil_datatype + +let named_predicate_to_exp_ref + : (Env.t -> predicate named -> exp * Env.t) ref + = Extlib.mk_fun "named_predicate_to_exp_ref" + +let term_to_exp_ref + : (Env.t -> logic_type -> term -> exp * Env.t) ref + = Extlib.mk_fun "term_to_exp_ref" + +let compute_quantif_guards quantif bounded_vars hyps = + let error msg pp x = + let msg1 = Pretty_utils.sfprintf msg pp x in + let msg2 = + Pretty_utils.sfprintf "@[ in quantification@ %a@]" + d_predicate_named quantif + in + Error.untypable (msg1 ^ msg2) + in + let rec aux acc vars p = + match p.content with + | Pand({ content = Prel((Rlt | Rle) as r1, t11, t12) }, + { content = Prel((Rlt | Rle) as r2, t21, t22) }) -> + (match t12.term_node, t21.term_node with + | TLval(TVar x1, TNoOffset), TLval(TVar x2, TNoOffset) -> + let v, vars = match vars with + | [] -> error "@[too much constraint(s)%a@]" (fun _ () -> ()) () + | v :: tl -> + match v.lv_type with + | Ctype ty when isIntegralType ty -> v, tl + | Linteger -> v, tl + | Ctype _ | Ltype _ | Lvar _ | Lreal | Larrow _ -> + error "@[non integer variable %a@]" d_logic_var v + in + if Logic_var.equal x1 x2 && Logic_var.equal x1 v then + (t11, r1, x1, r2, t22) :: acc, vars + else + error "@[invalid binder %a@]" d_term t21 + | TLval _, _ -> error "@[invalid binder %a@]" d_term t21 + | _, _ -> error "@[invalid binder %a@]" d_term t12) + | Pand(p1, p2) -> + let acc, vars = aux acc vars p1 in + aux acc vars p2 + | _ -> error "@[invalid guard %a@]" d_predicate_named p + in + let acc, vars = aux [] bounded_vars hyps in + (match vars with + | [] -> () + | _ :: _ -> + let msg = + Pretty_utils.sfprintf + "@[unguarded variable%s %tin quantification@ %a@]" + (if List.length vars = 1 then "" else "s") + (fun fmt -> + List.iter (fun v -> Format.fprintf fmt "@[%a @]" d_logic_var v) vars) + d_predicate_named quantif + in + Error.untypable msg); + List.rev acc + +module Label_ids = + State_builder.Counter(struct let name = "E_ACSL.Label_ids" end) + +let convert env loc p bounded_vars hyps goal = + let named_predicate_to_exp = !named_predicate_to_exp_ref in + let term_to_exp = !term_to_exp_ref in + (* universal quantification over integers (or a subtype of integer) *) + let guards = compute_quantif_guards p bounded_vars hyps in + let env = List.fold_left Env.Logic_binding.add env bounded_vars in + let var_res = ref Varinfo.dummy in + let res, env = + (* variable storing the result of the \forall *) + Env.new_var env None intType + (fun v _ -> + var_res := v; + let lv = var v in + [ mkStmtOneInstr ~valid_sid:true (Set(lv, one ~loc, loc)) ]) + in + let end_loop_ref = ref dummyStmt in + let rec mk_for_loop env = function + | [] -> + (* innermost loop body: store the result in [res] and go out according + to evaluation of the goal *) + let test, env = named_predicate_to_exp (Env.push env) goal in + let then_block = mkBlock [ mkEmptyStmt ~loc () ] in + let else_block = + (* use a 'goto', not a simple 'break' in order to handle 'forall' with + multiple binders (leading to imbricated loops) *) + mkBlock + [ mkStmtOneInstr + ~valid_sid:true (Set(var !var_res, zero ~loc, loc)); + mkStmt ~valid_sid:true (Goto(end_loop_ref, loc)) ] + in + let blk, env = + Env.pop_and_get + env + (mkStmt ~valid_sid:true (If(test, then_block, else_block, loc))) + ~global_clear:false + Env.After + in + (* TODO: could be optimised if [pop_and_get] would return a list of + stmts *) + [ mkStmt ~valid_sid:true (Block blk) ], env + | (t1, rel1, logic_x, rel2, t2) :: tl -> + let body, env = mk_for_loop env tl in + let t_plus_one t = + Logic_const.term ~loc + (TBinOp(PlusA, t, Logic_const.tinteger ~loc ~ikind:IChar 1)) + Linteger + in + let t1 = match rel1 with + | Rlt -> t_plus_one t1 + | Rle -> t1 + | Rgt | Rge | Req | Rneq -> assert false + in + let bop2 = match rel2 with + | Rlt -> Lt + | Rle -> Le + | Rgt | Rge | Req | Rneq -> assert false + in + (* we increment the loop counter one more time (at the end of the + loop). Thus to prevent overflow, check the type of [t2 + 1] instead + of [t2]. *) + let ty = Typing.principal_type_from_term t1 (t_plus_one t2) in + let ty = Typing.principal_type ty logic_x.lv_type in + (* loop counter corresponding to the quantified variable *) + let var_x = Env.Logic_binding.get env logic_x in + let lv_x = var var_x in + let x = new_exp ~loc (Lval lv_x) in + let env = match ty with + | Ctype ty when isIntegralType ty -> + var_x.vtype <- ty; + env + | Linteger -> + var_x.vtype <- Mpz.t; + Env.add_stmt env (Mpz.init x) + | Ctype _ | Ltype _ | Lvar _ | Lreal | Larrow _ -> assert false + in + (* initialize the loop counter to [t1] *) + let e1, env = term_to_exp (Env.push env) ty t1 in + let init_blk, env = + Env.pop_and_get + env + (Mpz.affect lv_x x e1) + ~global_clear:false + Env.Middle + in + (* generate the guard [x bop t2] *) + let stmts_block b = [ mkStmt ~valid_sid:true (Block b) ] in + let tlv = Logic_const.tvar ~loc (cvar_to_lvar var_x) in + let guard_exp, env = + term_to_exp + (Env.push env) + (Ctype intType) + (Logic_const.term (TBinOp(bop2, tlv, t2)) ty) + in + let break_stmt = mkStmt ~valid_sid:true (Break guard_exp.eloc) in + let guard_blk, env = + Env.pop_and_get + env + (mkStmt ~valid_sid:true + (If(guard_exp, + mkBlock [ mkEmptyStmt () ], + mkBlock [ break_stmt ], + guard_exp.eloc))) + ~global_clear:false + Env.Middle + in + let guard = stmts_block guard_blk in + (* increment the loop counter [x++] *) + let incr, env = term_to_exp (Env.push env) ty (t_plus_one tlv) in + let next_blk, env = + Env.pop_and_get + env + (Mpz.affect lv_x x incr) + ~global_clear:false + Env.Middle + in + (* generate the whole loop *) + let start = stmts_block init_blk in + let next = stmts_block next_blk in + start @ + [ mkStmt ~valid_sid:true + (Loop ([], + mkBlock (guard @ body @ next), + loc, + None, + Some break_stmt)) ], + env + in + let stmts, env = mk_for_loop env guards in + let env = + Env.add_stmt env (mkStmt ~valid_sid:true (Block (mkBlock stmts))) + in + (* where to jump to go out of the loop *) + let end_loop = mkEmptyStmt ~loc () in + let label_name = "e_acsl_end_loop" ^ string_of_int (Label_ids.next ()) in + let label = Label(label_name, loc, false) in + end_loop.labels <- label :: end_loop.labels; + end_loop_ref := end_loop; + let env = Env.add_stmt env end_loop in + let env = List.fold_left Env.Logic_binding.remove env bounded_vars in + res, env + +(* +Local Variables: +compile-command: "make" +End: +*) diff --git a/src/plugins/e-acsl/quantif.mli b/src/plugins/e-acsl/quantif.mli new file mode 100644 index 00000000000..0c0361abb43 --- /dev/null +++ b/src/plugins/e-acsl/quantif.mli @@ -0,0 +1,41 @@ +(**************************************************************************) +(* *) +(* This file is part of the E-ACSL plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2011 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Cil_types + +val convert: + Env.t -> + location -> + predicate named -> + logic_var list -> + predicate named -> + predicate named -> + exp * Env.t + +val named_predicate_to_exp_ref: (Env.t -> predicate named -> exp * Env.t) ref +val term_to_exp_ref: (Env.t -> logic_type -> term -> exp * Env.t) ref + +(* +Local Variables: +compile-command: "make" +End: +*) diff --git a/src/plugins/e-acsl/typing.ml b/src/plugins/e-acsl/typing.ml new file mode 100644 index 00000000000..7c0625fce18 --- /dev/null +++ b/src/plugins/e-acsl/typing.ml @@ -0,0 +1,117 @@ +(**************************************************************************) +(* *) +(* This file is part of the E-ACSL plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2011 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Cil_types +open Cil + +let compatible_type ty ty' = + (* compatible if the two type has the same "integrality" *) + isIntegralType ty = isIntegralType ty' + +(* convert [e] corresponding to a term of type [ty] in a way that it is + compatible with the given context. *) +let context_sensitive ?loc env ctx is_mpz_string t_opt e = + let ty = typeOf e in + let mk_mpz env e = + Env.new_var env t_opt Mpz.t (fun lv v -> [ Mpz.init_set (var lv) v e ]) + in + let do_int_ctx ty' = + let e, env = if is_mpz_string then mk_mpz env e else e, env in + if Mpz.is_t ty || is_mpz_string then + (* cast the mpz into a C integer *) + let name = + if isSignedInteger ty' then "__gmpz_get_si" else "__gmpz_get_ui" + in + Options.warning + ?source:(Extlib.opt_map fst loc) + ~once:true + "@[missing guard for ensuring that the given integer is \ +C-representable@]"; + Env.new_var + env + None + ty' + (fun v _ -> [ Misc.mk_call ?loc ~result:(var v) name [ e ] ]) + else + e, env + in + match ctx with + | Ctype ty' -> do_int_ctx ty' + | Linteger -> + if Mpz.is_t ty then + e, env + else begin + (* Convert the C integer into a mpz. + Remember: very long integer constant has been temporary converted into + strings *) + assert (Options.verify + (isIntegralType ty || is_mpz_string) + "how to convert %a to an integer?" + d_type ty); + mk_mpz env e + end + | ty' when Logic_const.is_boolean_type ty' -> do_int_ctx intType + | Ltype _ | Lvar _ | Lreal | Larrow _ -> + (* not yet supported, thus cannot occur at this point *) + assert false + +let principal_type ty ty' = match ty, ty' with + | Ctype ty, Ctype ty' when isIntegralType ty -> + assert (isIntegralType ty'); + Ctype (arithmeticConversion ty ty') + | Ctype ty, Linteger | Linteger, Ctype ty when isIntegralType ty -> Linteger + (* not possible to unify cases below (see caml bts #5432) *) + | Ctype ty1, Ctype ty2 when Mpz.is_t ty1 && isIntegralType ty2 -> Linteger + | Ctype ty2, Ctype ty1 when Mpz.is_t ty1 && isIntegralType ty2 -> Linteger + | Ctype tty, Ctype tty' -> + assert (compatible_type tty tty'); + ty + | Ctype _, Linteger | Linteger, Ctype _ -> Linteger + | Linteger, Linteger -> Linteger + | (Ltype _ | Lvar _ | Lreal | Larrow _), _ + | _, (Ltype _ | Lvar _ | Lreal | Larrow _) -> + (* not yet supported, thus cannot occur at this point *) + Options.error "What is this %a here?" d_logic_type ty'; + assert false + +let principal_type_from_term t1 t2 = + let typ t = + let ty = t.term_type in + if Logic_const.is_boolean_type ty then Ctype intType + else match t.term_node, ty with + | TConst (CInt64(_, (ILongLong | IULongLong), _)), Linteger -> + (* constant potentially not representable in C *) + Linteger + | TConst (CInt64(_, k, _)), _ -> + (* C-representable constant *) + Ctype (TInt (k, [])) + | _, _ -> + (* for direct C terms, should be able to infer the corresponding C type *) + ty + in + principal_type (typ t1) (typ t2) + +(* +Local Variables: +compile-command: "make" +End: +*) diff --git a/src/plugins/e-acsl/typing.mli b/src/plugins/e-acsl/typing.mli new file mode 100644 index 00000000000..b115f54ef90 --- /dev/null +++ b/src/plugins/e-acsl/typing.mli @@ -0,0 +1,38 @@ +(**************************************************************************) +(* *) +(* This file is part of the E-ACSL plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2011 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Cil_types + +(** Typing rules. *) + +val principal_type: logic_type -> logic_type -> logic_type +val principal_type_from_term: term -> term -> logic_type + +val context_sensitive: + ?loc:location -> Env.t -> logic_type -> bool -> term option -> exp -> + exp * Env.t + +(* +Local Variables: +compile-command: "make" +End: +*) diff --git a/src/plugins/e-acsl/visit.ml b/src/plugins/e-acsl/visit.ml index ac7c267ad64..b1418f57e1f 100644 --- a/src/plugins/e-acsl/visit.ml +++ b/src/plugins/e-acsl/visit.ml @@ -24,98 +24,6 @@ open Cil_types open Cil_datatype open Cil -(* ************************************************************************** *) -(* Typing rules *) -(* ************************************************************************** *) - -let compatible_type ty ty' = - (* compatible if the two type has the same "integrality" *) - isIntegralType ty = isIntegralType ty' - -(* convert [e] corresponding to a term of type [ty] in a way that it is - compatible with the given context. *) -let context_sensitive ?loc env ctx is_mpz_string t_opt e = - let ty = typeOf e in - let mk_mpz env e = - Env.new_var env t_opt Mpz.t (fun lv v -> [ Mpz.init_set (var lv) v e ]) - in - let do_int_ctx ty' = - let e, env = if is_mpz_string then mk_mpz env e else e, env in - if Mpz.is_t ty || is_mpz_string then - (* cast the mpz into a C integer *) - let name = - if isSignedInteger ty' then "__gmpz_get_si" else "__gmpz_get_ui" - in - Options.warning - ?source:(Extlib.opt_map fst loc) - ~once:true - "@[missing guard for ensuring that the given integer is \ -C-representable@]"; - Env.new_var - env - None - ty' - (fun v _ -> [ Misc.mk_call ?loc ~result:(var v) name [ e ] ]) - else - e, env - in - match ctx with - | Ctype ty' -> do_int_ctx ty' - | Linteger -> - if Mpz.is_t ty then - e, env - else begin - (* Convert the C integer into a mpz. - Remember: very long integer constant has been temporary converted into - strings *) - assert (Options.verify - (isIntegralType ty || is_mpz_string) - "how to convert %a to an integer?" - d_type ty); - mk_mpz env e - end - | ty' when Logic_const.is_boolean_type ty' -> do_int_ctx intType - | Ltype _ | Lvar _ | Lreal | Larrow _ -> - (* not yet supported, thus cannot occur at this point *) - assert false - -let principal_type ty ty' = match ty, ty' with - | Ctype ty, Ctype ty' when isIntegralType ty -> - assert (isIntegralType ty'); - Ctype (arithmeticConversion ty ty') - | Ctype ty, Linteger | Linteger, Ctype ty when isIntegralType ty -> Linteger - (* both cases below should be unified, but it is not possible because of - caml bts #5432 *) - | Ctype ty1, Ctype ty2 when Mpz.is_t ty1 && isIntegralType ty2 -> Linteger - | Ctype ty2, Ctype ty1 when Mpz.is_t ty1 && isIntegralType ty2 -> Linteger - | Ctype tty, Ctype tty' -> - assert (compatible_type tty tty'); - ty - | Ctype _, Linteger | Linteger, Ctype _ -> Linteger - | Linteger, Linteger -> Linteger - | (Ltype _ | Lvar _ | Lreal | Larrow _), _ - | _, (Ltype _ | Lvar _ | Lreal | Larrow _) -> - (* not yet supported, thus cannot occur at this point *) - Options.error "What is this %a here?" d_logic_type ty'; - assert false - -let principal_type_from_term t1 t2 = - let typ t = - let ty = t.term_type in - if Logic_const.is_boolean_type ty then Ctype intType - else match t.term_node, ty with - | TConst (CInt64(_, (ILongLong | IULongLong), _)), Linteger -> - (* constant potentially not representable in C *) - Linteger - | TConst (CInt64(_, k, _)), _ -> - (* C-representable constant *) - Ctype (TInt (k, [])) - | _, _ -> - (* for direct C terms, should be able to infer the corresponding C type *) - ty - in - principal_type (typ t1) (typ t2) - (* ************************************************************************** *) (* Transforming terms and predicates into C expressions (if any) *) (* ************************************************************************** *) @@ -144,60 +52,6 @@ let is_representable _n k _s = match k with | ILongLong | IULongLong -> false -let compute_quantif_guards quantif bounded_vars hyps = - let error msg pp x = - let msg1 = Pretty_utils.sfprintf msg pp x in - let msg2 = - Pretty_utils.sfprintf "@[ in quantification@ %a@]" - d_predicate_named quantif - in - Error.untypable (msg1 ^ msg2) - in - let rec aux acc vars p = - match p.content with - | Pand({ content = Prel((Rlt | Rle) as r1, t11, t12) }, - { content = Prel((Rlt | Rle) as r2, t21, t22) }) -> - (match t12.term_node, t21.term_node with - | TLval(TVar x1, TNoOffset), TLval(TVar x2, TNoOffset) -> - let v, vars = match vars with - | [] -> error "@[too much constraint(s)%a@]" (fun _ () -> ()) () - | v :: tl -> - match v.lv_type with - | Ctype ty when isIntegralType ty -> v, tl - | Linteger -> v, tl - | Ctype _ | Ltype _ | Lvar _ | Lreal | Larrow _ -> - error "@[non integer variable %a@]" d_logic_var v - in - if Logic_var.equal x1 x2 && Logic_var.equal x1 v then - (t11, r1, x1, r2, t22) :: acc, vars - else - error "@[invalid binder %a@]" d_term t21 - | TLval _, _ -> error "@[invalid binder %a@]" d_term t21 - | _, _ -> error "@[invalid binder %a@]" d_term t12) - | Pand(p1, p2) -> - let acc, vars = aux acc vars p1 in - aux acc vars p2 - | _ -> error "@[invalid guard %a@]" d_predicate_named p - in - let acc, vars = aux [] bounded_vars hyps in - (match vars with - | [] -> () - | _ :: _ -> - let msg = - Pretty_utils.sfprintf - "@[unguarded variable%s %tin quantification@ %a@]" - (if List.length vars = 1 then "" else "s") - (fun fmt -> - List.iter (fun v -> Format.fprintf fmt "@[%a @]" d_logic_var v) vars) - d_predicate_named quantif - in - Error.untypable msg); - List.rev acc - - -module Label_ids = - State_builder.Counter(struct let name = "E_ACSL.Label_ids" end) - let constant_to_exp ?(loc=Location.unknown) = function | CInt64(n, k, s) -> if is_representable n k s then kinteger64_repr ?loc k n s, false @@ -301,7 +155,7 @@ and context_insensitive_term_to_exp env t = e, env, false | TBinOp(Div | Mod as bop, t1, t2) -> (* arithmetic binary operator potentially convertible into C *) - let ctx = principal_type_from_term t1 t2 in + let ctx = Typing.principal_type_from_term t1 t2 in let e1, env = term_to_exp env ctx t1 in let e2, env = term_to_exp env ctx t2 in (* guarding divisions and modulos *) @@ -438,22 +292,23 @@ and context_insensitive_term_to_exp env t = constructs. *) and term_to_exp env ctx t = let e, env, is_mpz_string = context_insensitive_term_to_exp env t in - context_sensitive ~loc:t.term_loc env ctx is_mpz_string (Some t) e + Typing.context_sensitive ~loc:t.term_loc env ctx is_mpz_string (Some t) e (* generate the C code equivalent to [t1 bop t2]. *) and comparison_to_exp ?(loc=Location.unknown) ?e1 env bop t1 t2 t_opt = let ctx = match e1 with - | None -> principal_type_from_term t1 t2 + | None -> Typing.principal_type_from_term t1 t2 | Some(_, ctx) -> (* Options.feedback "principality oriented by %a" d_logic_type ctx;*) - principal_type_from_term { t1 with term_type = ctx } t2 + Typing.principal_type_from_term { t1 with term_type = ctx } t2 in (* Options.feedback "principal type of %a and %a is %a" d_term t1 d_term t2 d_logic_type ctx;*) let e1, env = match e1 with | None -> term_to_exp env ctx t1 | Some(e1, ctx1) when Cil_datatype.Logic_type.equal ctx ctx1 -> e1, env - | Some(e1, _) -> context_sensitive ~loc:e1.eloc env ctx false (Some t1) e1 + | Some(e1, _) -> + Typing.context_sensitive ~loc:e1.eloc env ctx false (Some t1) e1 in let e2, env = term_to_exp env ctx t2 in match ctx with @@ -483,7 +338,7 @@ let rec named_predicate_to_exp env p = let e, env = comparison_to_exp ~loc env (relation_to_binop rel) t1 t2 None in - context_sensitive ~loc env (Ctype intType) false None e + Typing.context_sensitive ~loc env (Ctype intType) false None e | Pand(p1, p2) -> (* p1 && p2 <==> if p1 then p2 else false *) let e1, env1 = named_predicate_to_exp env p1 in @@ -532,143 +387,7 @@ let rec named_predicate_to_exp env p = | Pif _ -> Error.not_yet "_ ? _ : _" | Plet _ -> Error.not_yet "let _ = _ in _" | Pforall(bounded_vars, { content = Pimplies(hyps, goal) }) -> - (* universal quantification over integers (or a subtype of integer) *) - let guards = compute_quantif_guards p bounded_vars hyps in - let env = List.fold_left Env.Logic_binding.add env bounded_vars in - let var_res = ref Varinfo.dummy in - let res, env = - (* variable storing the result of the \forall *) - Env.new_var env None intType - (fun v _ -> - var_res := v; - let lv = var v in - [ mkStmtOneInstr ~valid_sid:true (Set(lv, one ~loc, loc)) ]) - in - let end_loop_ref = ref dummyStmt in - let rec mk_for_loop env = function - | [] -> - (* innermost loop body: store the result in [res] and go out according - to evaluation of the goal *) - let test, env = named_predicate_to_exp (Env.push env) goal in - let then_block = mkBlock [ mkEmptyStmt ~loc () ] in - let else_block = - (* use a 'goto', not a simple 'break' in order to handle 'forall' with - multiple binders (leading to imbricated loops) *) - mkBlock - [ mkStmtOneInstr - ~valid_sid:true (Set(var !var_res, zero ~loc, loc)); - mkStmt ~valid_sid:true (Goto(end_loop_ref, loc)) ] - in - let blk, env = - Env.pop_and_get - env - (mkStmt ~valid_sid:true (If(test, then_block, else_block, loc))) - ~global_clear:false - Env.After - in - (* TODO: could be optimised if [pop_and_get] would return a list of - stmts *) - [ mkStmt ~valid_sid:true (Block blk) ], env - | (t1, rel1, logic_x, rel2, t2) :: tl -> - let body, env = mk_for_loop env tl in - let t_plus_one t = - Logic_const.term ~loc - (TBinOp(PlusA, t, Logic_const.tinteger ~loc ~ikind:IChar 1)) - Linteger - in - let t1 = match rel1 with - | Rlt -> t_plus_one t1 - | Rle -> t1 - | Rgt | Rge | Req | Rneq -> assert false - in - let bop2 = match rel2 with - | Rlt -> Lt - | Rle -> Le - | Rgt | Rge | Req | Rneq -> assert false - in - (* we increment the loop counter one more time (at the end of the - loop). Thus to prevent overflow, check the type of [t2 + 1] instead - of [t2]. *) - let ty = principal_type_from_term t1 (t_plus_one t2) in - let ty = principal_type ty logic_x.lv_type in - (* loop counter corresponding to the quantified variable *) - let var_x = Env.Logic_binding.get env logic_x in - let lv_x = var var_x in - let x = new_exp ~loc (Lval lv_x) in - let env = match ty with - | Ctype ty when isIntegralType ty -> - var_x.vtype <- ty; - env - | Linteger -> - var_x.vtype <- Mpz.t; - Env.add_stmt env (Mpz.init x) - | Ctype _ | Ltype _ | Lvar _ | Lreal | Larrow _ -> assert false - in - (* initialize the loop counter to [t1] *) - let e1, env = term_to_exp (Env.push env) ty t1 in - let init_blk, env = - Env.pop_and_get - env - (Mpz.affect lv_x x e1) - ~global_clear:false - Env.Middle - in - (* generate the guard [x bop t2] *) - let stmts_block b = [ mkStmt ~valid_sid:true (Block b) ] in - let tlv = Logic_const.tvar ~loc (cvar_to_lvar var_x) in - let guard_exp, env = - term_to_exp - (Env.push env) - (Ctype intType) - (Logic_const.term (TBinOp(bop2, tlv, t2)) ty) - in - let break_stmt = mkStmt ~valid_sid:true (Break guard_exp.eloc) in - let guard_blk, env = - Env.pop_and_get - env - (mkStmt ~valid_sid:true - (If(guard_exp, - mkBlock [ mkEmptyStmt () ], - mkBlock [ break_stmt ], - guard_exp.eloc))) - ~global_clear:false - Env.Middle - in - let guard = stmts_block guard_blk in - (* increment the loop counter [x++] *) - let incr, env = term_to_exp (Env.push env) ty (t_plus_one tlv) in - let next_blk, env = - Env.pop_and_get - env - (Mpz.affect lv_x x incr) - ~global_clear:false - Env.Middle - in - (* generate the whole loop *) - let start = stmts_block init_blk in - let next = stmts_block next_blk in - start @ - [ mkStmt ~valid_sid:true - (Loop ([], - mkBlock (guard @ body @ next), - loc, - None, - Some break_stmt)) ], - env - in - let stmts, env = mk_for_loop env guards in - let env = - Env.add_stmt env (mkStmt ~valid_sid:true (Block (mkBlock stmts))) - in - (* where to jump to go out of the loop *) - let end_loop = mkEmptyStmt ~loc () in - let label_name = "e_acsl_end_loop" ^ string_of_int (Label_ids.next ()) in - let label = Label(label_name, loc, false) in - end_loop.labels <- label :: end_loop.labels; - end_loop_ref := end_loop; - let env = Env.add_stmt env end_loop in - let env = List.fold_left Env.Logic_binding.remove env bounded_vars in - res, env + Quantif.convert env loc p bounded_vars hyps goal | Pforall _ -> Error.not_yet "unguarded \\forall quantification" (* | Pexists(bounded_vars, { content = Pand(hyps, _goal) }) -> let guards = compute_quantif_guards p bounded_vars hyps in @@ -688,6 +407,10 @@ let rec named_predicate_to_exp env p = | Psubtype _ -> Error.not_yet "subtyping relation" (* Jessie specific *) | Pinitialized _ -> Error.not_yet "\\initialized" +let () = + Quantif.term_to_exp_ref := term_to_exp; + Quantif.named_predicate_to_exp_ref := named_predicate_to_exp + (* ************************************************************************** *) (* [convert_*] converts a given ACSL annotation into the corresponding C statement (if any) for runtime assertion checking *) -- GitLab