From 2e10b36d8cce6fff951fc825df23b96f0e38ba4a Mon Sep 17 00:00:00 2001 From: Valentin Perrelle <valentin.perrelle@cea.fr> Date: Mon, 1 Aug 2022 22:48:26 +0200 Subject: [PATCH] [Kernel] Split logic_interp into three independent modules --- src/kernel_services/analysis/logic_interp.ml | 381 +----------------- src/kernel_services/analysis/logic_interp.mli | 12 - .../ast_queries/logic_parse_string.ml | 195 +++++++++ .../ast_queries/logic_parse_string.mli | 44 ++ src/kernel_services/ast_queries/logic_to_c.ml | 215 ++++++++++ .../ast_queries/logic_to_c.mli | 59 +++ src/kernel_services/plugin_entry_points/db.ml | 26 +- 7 files changed, 527 insertions(+), 405 deletions(-) create mode 100644 src/kernel_services/ast_queries/logic_parse_string.ml create mode 100644 src/kernel_services/ast_queries/logic_parse_string.mli create mode 100644 src/kernel_services/ast_queries/logic_to_c.ml create mode 100644 src/kernel_services/ast_queries/logic_to_c.mli diff --git a/src/kernel_services/analysis/logic_interp.ml b/src/kernel_services/analysis/logic_interp.ml index e76accf2cc9..924a9b2894e 100644 --- a/src/kernel_services/analysis/logic_interp.ml +++ b/src/kernel_services/analysis/logic_interp.ml @@ -24,365 +24,7 @@ open Cil open Cil_types open Cil_datatype -exception Error of Cil_types.location * string -exception Unbound of string - -let find_var kf kinstr ?label var = - let vi = - try - let vi = Globals.Vars.find_from_astinfo var (VLocal kf) in - (match kinstr with - | Kglobal -> vi (* don't refine search: the Kglobal here - does not indicate the function contract, but merely - the fact that we do not have any information about - the targeted program point. Hence, no scope check - can be performed or we might reject many legitimate - terms and predicates. - *) - | Kstmt stmt -> - let scope = - match label with - | None | Some "Here" | Some "Post" | Some "Old" -> stmt - | Some "Pre" -> raise Not_found (* no local variable in scope. *) - | Some "Init" -> raise Not_found (* no local variable in scope. *) - | Some "LoopEntry" | Some "LoopCurrent" -> - if not (Kernel_function.stmt_in_loop kf stmt) then - Kernel.fatal - "Use of LoopEntry or LoopCurrent outside of a loop"; - Kernel_function.find_enclosing_loop kf stmt - | Some l -> - (try let s = Kernel_function.find_label kf l in !s - with Not_found -> - Kernel.fatal - "Use of label %s that does not exist in function %a" - l Kernel_function.pretty kf) - in - if Kernel_function.var_is_in_scope scope vi then vi - else raise Not_found) - with Not_found -> - try - Globals.Vars.find_from_astinfo var (VFormal kf) - with Not_found -> - Globals.Vars.find_from_astinfo var VGlobal - in - cvar_to_lvar vi - -(** Create a logic typer, the interpretation being done for the given - kernel_function and stmt (the stmt is used check that loop invariants - are allowed). *) -(* It is theoretically possible to use a first-class module instead, but the - required signatures are not exported in Logic_typing. *) -module DefaultLT (X: - sig - val kf: Kernel_function.t - val kinstr: Cil_types.kinstr - end) = - Logic_typing.Make - (struct - let anonCompFieldName = Cabs2cil.anonCompFieldName - let conditionalConversion = Cabs2cil.logicConditionalConversion - - let is_loop () = - match X.kinstr with - | Kglobal -> false - | Kstmt s -> Kernel_function.stmt_in_loop X.kf s - - let find_macro _ = raise Not_found - - let find_var ?label var = find_var X.kf X.kinstr ?label var - - let find_enum_tag x = - try - Globals.Types.find_enum_tag x - with Not_found -> - (* The ACSL typer tries to parse a string, first as a variable, - then as an enum. We report the "Unbound variable" message - here, as it is nicer for the user. However, this short-circuits - the later stages of resolution, for example global logic - variables. *) - raise (Unbound ("Unbound variable " ^ x)) - - let find_comp_field info s = - let field = Cil.getCompField info s in - Field(field,NoOffset) - - let find_type = Globals.Types.find_type - - let find_label s = Kernel_function.find_label X.kf s - include Logic_env - - let add_logic_function = - add_logic_function_gen Logic_utils.is_same_logic_profile - - let remove_logic_info = - remove_logic_info_gen Logic_utils.is_same_logic_profile - - let integral_cast ty t = - raise - (Failure - (Format.asprintf - "term %a has type %a, but %a is expected." - Printer.pp_term t - Printer.pp_logic_type Linteger - Printer.pp_typ ty)) - - let error loc msg = - Pretty_utils.ksfprintf (fun e -> raise (Error (loc, e))) msg - - let on_error f rollback x = - try f x with Error (loc,msg) as exn -> rollback (loc,msg); raise exn - - end) - -(** Set up the parser for the infamous 'C hack' needed to parse typedefs *) -let sync_typedefs () = - Logic_env.reset_typenames (); - Globals.Types.iter_types - (fun name _ ns -> - if ns = Logic_typing.Typedef then Logic_env.add_typename name) - -let wrap f parsetree loc = - match parsetree with - | None -> raise (Error (loc, "Syntax error in annotation")) - | Some annot -> try f annot with Unbound s -> raise (Error (loc, s)) - -let code_annot kf stmt s = - sync_typedefs (); - let module LT = DefaultLT(struct - let kf = kf - let kinstr = Kstmt stmt - end) in - let loc = Stmt.loc stmt in - let pa = - Option.bind - (Logic_lexer.annot (fst loc,s)) - (function (_, Logic_ptree.Acode_annot (_,a)) -> Some a | _ -> None) - in - let parse pa = - LT.code_annot - (Stmt.loc stmt) - (Logic_utils.get_behavior_names (Annotations.funspec kf)) - (Ctype (Kernel_function.get_return_type kf)) pa - in - wrap parse pa loc - -let default_term_env () = - Logic_typing.append_here_label (Logic_typing.Lenv.empty()) - -let term kf ?(loc=Location.unknown) ?(env=default_term_env ()) s = - sync_typedefs (); - let module LT = DefaultLT(struct - let kf = kf - let kinstr = Kglobal - end) in - let pa_expr = Option.map snd (Logic_lexer.lexpr (fst loc, s)) in - let parse pa_expr = LT.term env pa_expr in - wrap parse pa_expr loc - -let term_lval kf ?(loc=Location.unknown) ?(env=default_term_env ()) s = - match (term kf ~loc ~env s).term_node with - | TLval lv -> lv - | _ -> raise (Error (loc, "Syntax error (expecting an lvalue)")) - -let predicate kf ?(loc=Location.unknown) ?(env=default_term_env ()) s = - sync_typedefs (); - let module LT = DefaultLT(struct - let kf = kf - let kinstr = Kglobal - end) in - let pa_expr = Option.map snd (Logic_lexer.lexpr (fst loc, s)) in - let parse pa_expr = LT.predicate env pa_expr in - wrap parse pa_expr loc - -let error_lval () = raise Db.Properties.Interp.No_conversion - -let rec logic_type_to_typ = function - | Ctype typ -> typ - | Linteger -> TInt(ILongLong,[]) (*TODO: to have an unlimited integer type - in the logic interpretation*) - | Lreal -> TFloat(FDouble,[]) (* TODO: handle reals, not floats... *) - | Ltype({lt_name = name},[]) when name = Utf8_logic.boolean -> - TInt(ILongLong,[]) - | Ltype({lt_name = "set"},[t]) -> logic_type_to_typ t - | Ltype _ | Lvar _ | Larrow _ -> error_lval () - - -(* Expect conversion to be possible on all sub-terms, otherwise raise an error. *) - -let logic_var_to_var { lv_origin = lv } = - match lv with - | None -> error_lval () - | Some lv -> lv - -let create_const_list loc kind low high = - let rec aux acc i = - if Integer.lt i low then acc - else - aux (new_exp ~loc (Const (CInt64 (i,kind,None)))::acc) (Integer.pred i) - in aux [] high - -let range low high = - let loc = fst low.eloc, snd high.eloc in - match (Cil.constFold true low).enode, (Cil.constFold true high).enode with - Const(CInt64(low,kind,_)), Const(CInt64(high,_,_)) -> - create_const_list loc kind low high - | _ -> error_lval() - -let singleton f loc = - match f loc with - [ l ] -> l - | _ -> error_lval() - -let rec loc_lval_to_lval ~result (lh, lo) = - Extlib.product - (fun x y -> (x,y)) - (loc_lhost_to_lhost ~result lh) - (loc_offset_to_offset ~result lo) - -and loc_lhost_to_lhost ~result = function - | TVar lvar -> [Var (logic_var_to_var lvar)] - | TMem lterm -> List.map (fun x -> Mem x) (loc_to_exp ~result lterm) - | TResult _ -> - ( match result with - None -> error_lval() - | Some v -> [Var v]) - -and loc_offset_to_offset ~result = function - | TNoOffset -> [NoOffset] - | TModel _ -> error_lval () - | TField (fi, lo) -> - List.map (fun x -> Field (fi,x)) (loc_offset_to_offset ~result lo) - | TIndex (lexp, lo) -> - Extlib.product - (fun x y -> Index(x,y)) - (loc_to_exp ~result lexp) - (loc_offset_to_offset ~result lo) - -and loc_to_exp ~result {term_node = lnode ; term_type = ltype; term_loc = loc} = - match lnode with - | TLval lv -> - List.map (fun x -> new_exp ~loc (Lval x)) (loc_lval_to_lval ~result lv) - | TAddrOf lv -> - List.map (fun x -> new_exp ~loc (AddrOf x)) (loc_lval_to_lval ~result lv) - | TStartOf lv -> - List.map (fun x -> new_exp ~loc (StartOf x)) (loc_lval_to_lval ~result lv) - | TSizeOfE lexp -> - List.map (fun x -> new_exp ~loc (SizeOfE x)) (loc_to_exp ~result lexp) - | TAlignOfE lexp -> - List.map (fun x -> new_exp ~loc (AlignOfE x)) (loc_to_exp ~result lexp) - | TUnOp (unop, lexp) -> - List.map - (fun x -> new_exp ~loc (UnOp (unop, x, logic_type_to_typ ltype))) - (loc_to_exp ~result lexp) - | TBinOp (binop, lexp1, lexp2) -> - Extlib.product - (fun x y -> new_exp ~loc (BinOp (binop, x,y, logic_type_to_typ ltype))) - (loc_to_exp ~result lexp1) - (loc_to_exp ~result lexp2) - | TSizeOfStr string -> [new_exp ~loc (SizeOfStr string)] - | TConst constant -> - (* TODO: Very likely to fail on large integer and incorrect on reals not - representable as floats *) - [new_exp ~loc (Const (Logic_utils.lconstant_to_constant constant))] - | TCastE (typ, lexp) -> - List.map - (fun x -> new_exp ~loc (CastE (typ, x))) (loc_to_exp ~result lexp) - | TAlignOf typ -> [new_exp ~loc (AlignOf typ)] - | TSizeOf typ -> [new_exp ~loc (SizeOf typ)] - | Trange (Some low, Some high) -> - let low = singleton (loc_to_exp ~result) low in - let high = singleton (loc_to_exp ~result) high in - range low high - | Tunion l -> List.concat (List.map (loc_to_exp ~result) l) - | Tempty_set -> [] - | Tinter _ | Tcomprehension _ -> error_lval() - | Tat ({term_node = TAddrOf (TVar _, TNoOffset)} as taddroflval, _) -> - loc_to_exp ~result taddroflval - | TLogic_coerce(Linteger, t) when Logic_typing.is_integral_type t.term_type -> - loc_to_exp ~result t - | TLogic_coerce(Lreal, t) when Logic_typing.is_integral_type t.term_type -> - List.map - (fun x -> new_exp ~loc (CastE (logic_type_to_typ Lreal, x))) - (loc_to_exp ~result t) - | TLogic_coerce(Lreal, t) when Logic_typing.is_arithmetic_type t.term_type -> - loc_to_exp ~result t - | TLogic_coerce (set, t) - when - Logic_const.is_set_type set && - Logic_utils.is_same_type - (Logic_typing.type_of_set_elem set) t.term_type -> - loc_to_exp ~result t - | Tnull -> [ Cil.mkCast ~newt:(TPtr(TVoid [], [])) (Cil.zero ~loc) ] - - (* additional constructs *) - | Tapp _ | Tlambda _ | Trange _ | Tlet _ - | TDataCons _ - | Tif _ - | Tat _ - | Tbase_addr _ - | Toffset _ - | Tblock_length _ - | TUpdate _ | Ttypeof _ | Ttype _ - | TLogic_coerce _ - -> error_lval () - -let rec loc_to_lval ~result t = - match t.term_node with - | TLval lv -> loc_lval_to_lval ~result lv - | TAddrOf lv -> loc_lval_to_lval ~result lv - | TStartOf lv -> loc_lval_to_lval ~result lv - | Tunion l1 -> List.concat (List.map (loc_to_lval ~result) l1) - | Tempty_set -> [] - (* coercions to arithmetic types cannot be lval. We only have to consider - a coercion to set here. - *) - | TLogic_coerce(set, t) when - Logic_typing.is_set_type set && - Logic_utils.is_same_type - (Logic_typing.type_of_set_elem set) t.term_type -> - loc_to_lval ~result t - | Tinter _ -> error_lval() (* TODO *) - | Tcomprehension _ -> error_lval() - | TSizeOfE _ | TAlignOfE _ | TUnOp _ | TBinOp _ | TSizeOfStr _ - | TConst _ | TCastE _ | TAlignOf _ | TSizeOf _ | Tapp _ | Tif _ - | Tat _ | Toffset _ | Tbase_addr _ | Tblock_length _ | Tnull | Trange _ - | TDataCons _ | TUpdate _ | Tlambda _ - | Ttypeof _ | Ttype _ | Tlet _ | TLogic_coerce _ -> - error_lval () - -let loc_to_offset ~result loc = - let rec aux h = - function - TLval(h',o) | TStartOf (h',o) -> - (match h with None -> Some h', loc_offset_to_offset ~result o - | Some h when Logic_utils.is_same_lhost h h' -> - Some h, loc_offset_to_offset ~result o - | Some _ -> error_lval() - ) - | Tat ({ term_node = TLval(TResult _,_)} as lv, BuiltinLabel Post) -> - aux h lv.term_node - | Tunion locs -> List.fold_left - (fun (b,l) x -> - let (b,l') = aux b x.term_node in b, l @ l') (h,[]) locs - | Tempty_set -> h,[] - | Trange _ | TAddrOf _ | Tat _ - | TSizeOfE _ | TAlignOfE _ | TUnOp _ | TBinOp _ | TSizeOfStr _ - | TConst _ | TCastE _ | TAlignOf _ | TSizeOf _ | Tapp _ | Tif _ - | Toffset _ | Tbase_addr _ | Tblock_length _ | Tnull - | TDataCons _ | TUpdate _ | Tlambda _ - | Ttypeof _ | Ttype _ | Tcomprehension _ | Tinter _ | Tlet _ - | TLogic_coerce _ - -> error_lval () - in snd (aux None loc.term_node) - -let term_lval_to_lval ~result = singleton (loc_lval_to_lval ~result) - -let term_to_lval ~result = singleton (loc_to_lval ~result) - -let term_to_exp ~result = singleton (loc_to_exp ~result) - -let term_offset_to_offset ~result = singleton (loc_offset_to_offset ~result) - +exception Error = Logic_parse_string.Error (** Utilities to identify [Locations.Zone.t] involved into @@ -1071,21 +713,6 @@ let to_result_from_pred p = let () = - Db.Properties.Interp.code_annot := code_annot; - Db.Properties.Interp.term_lval := term_lval; - Db.Properties.Interp.term := term; - Db.Properties.Interp.predicate := predicate; - - Db.Properties.Interp.term_lval_to_lval := term_lval_to_lval; - Db.Properties.Interp.term_to_exp := term_to_exp; - - Db.Properties.Interp.term_to_lval := term_to_lval; - Db.Properties.Interp.term_offset_to_offset := term_offset_to_offset; - - Db.Properties.Interp.loc_to_lval := loc_to_lval; - Db.Properties.Interp.loc_to_offset := loc_to_offset; - Db.Properties.Interp.loc_to_exp := loc_to_exp; - Db.Properties.Interp.To_zone.code_annot_filter := To_zone.code_annot_filter; Db.Properties.Interp.To_zone.mk_ctx_func_contrat := To_zone.mk_ctx_func_contrat; @@ -1102,9 +729,3 @@ let () = Db.Properties.Interp.To_zone.from_func_annots := To_zone.from_func_annots; Db.Properties.Interp.to_result_from_pred := to_result_from_pred; - -(* -Local Variables: -compile-command: "make -C ../../.." -End: -*) diff --git a/src/kernel_services/analysis/logic_interp.mli b/src/kernel_services/analysis/logic_interp.mli index 09165c84558..5020269dbe0 100644 --- a/src/kernel_services/analysis/logic_interp.mli +++ b/src/kernel_services/analysis/logic_interp.mli @@ -20,12 +20,6 @@ (* *) (**************************************************************************) -(** All the interesting functions of this module are exported through - {!Db.Interp}. *) - -(* TODO: remove the module Properties from Db and export directly the - functions from here. *) - open Cil_types module To_zone : sig @@ -35,9 +29,3 @@ module To_zone : sig end exception Error of location * string - -(* -Local Variables: -compile-command: "make -C ../../.." -End: -*) diff --git a/src/kernel_services/ast_queries/logic_parse_string.ml b/src/kernel_services/ast_queries/logic_parse_string.ml new file mode 100644 index 00000000000..1b1308c18a1 --- /dev/null +++ b/src/kernel_services/ast_queries/logic_parse_string.ml @@ -0,0 +1,195 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2022 *) +(* 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 +open Cil_types +open Cil_datatype + +exception Error of Cil_types.location * string +exception Unbound of string + +let find_var kf kinstr ?label var = + let vi = + try + let vi = Globals.Vars.find_from_astinfo var (VLocal kf) in + (match kinstr with + | Kglobal -> vi (* don't refine search: the Kglobal here + does not indicate the function contract, but merely + the fact that we do not have any information about + the targeted program point. Hence, no scope check + can be performed or we might reject many legitimate + terms and predicates. + *) + | Kstmt stmt -> + let scope = + match label with + | None | Some "Here" | Some "Post" | Some "Old" -> stmt + | Some "Pre" -> raise Not_found (* no local variable in scope. *) + | Some "Init" -> raise Not_found (* no local variable in scope. *) + | Some "LoopEntry" | Some "LoopCurrent" -> + if not (Kernel_function.stmt_in_loop kf stmt) then + Kernel.fatal + "Use of LoopEntry or LoopCurrent outside of a loop"; + Kernel_function.find_enclosing_loop kf stmt + | Some l -> + (try let s = Kernel_function.find_label kf l in !s + with Not_found -> + Kernel.fatal + "Use of label %s that does not exist in function %a" + l Kernel_function.pretty kf) + in + if Kernel_function.var_is_in_scope scope vi then vi + else raise Not_found) + with Not_found -> + try + Globals.Vars.find_from_astinfo var (VFormal kf) + with Not_found -> + Globals.Vars.find_from_astinfo var VGlobal + in + cvar_to_lvar vi + +(** Create a logic typer, the interpretation being done for the given + kernel_function and stmt (the stmt is used check that loop invariants + are allowed). *) +(* It is theoretically possible to use a first-class module instead, but the + required signatures are not exported in Logic_typing. *) +module DefaultLT (X: + sig + val kf: Kernel_function.t + val kinstr: Cil_types.kinstr + end) = + Logic_typing.Make + (struct + let anonCompFieldName = Cabs2cil.anonCompFieldName + let conditionalConversion = Cabs2cil.logicConditionalConversion + + let is_loop () = + match X.kinstr with + | Kglobal -> false + | Kstmt s -> Kernel_function.stmt_in_loop X.kf s + + let find_macro _ = raise Not_found + + let find_var ?label var = find_var X.kf X.kinstr ?label var + + let find_enum_tag x = + try + Globals.Types.find_enum_tag x + with Not_found -> + (* The ACSL typer tries to parse a string, first as a variable, + then as an enum. We report the "Unbound variable" message + here, as it is nicer for the user. However, this short-circuits + the later stages of resolution, for example global logic + variables. *) + raise (Unbound ("Unbound variable " ^ x)) + + let find_comp_field info s = + let field = Cil.getCompField info s in + Field(field,NoOffset) + + let find_type = Globals.Types.find_type + + let find_label s = Kernel_function.find_label X.kf s + include Logic_env + + let add_logic_function = + add_logic_function_gen Logic_utils.is_same_logic_profile + + let remove_logic_info = + remove_logic_info_gen Logic_utils.is_same_logic_profile + + let integral_cast ty t = + raise + (Failure + (Format.asprintf + "term %a has type %a, but %a is expected." + Printer.pp_term t + Printer.pp_logic_type Linteger + Printer.pp_typ ty)) + + let error loc msg = + Pretty_utils.ksfprintf (fun e -> raise (Error (loc, e))) msg + + let on_error f rollback x = + try f x with Error (loc,msg) as exn -> rollback (loc,msg); raise exn + + end) + +(** Set up the parser for the infamous 'C hack' needed to parse typedefs *) +let sync_typedefs () = + Logic_env.reset_typenames (); + Globals.Types.iter_types + (fun name _ ns -> + if ns = Logic_typing.Typedef then Logic_env.add_typename name) + +let wrap f parsetree loc = + match parsetree with + | None -> raise (Error (loc, "Syntax error in annotation")) + | Some annot -> try f annot with Unbound s -> raise (Error (loc, s)) + +let code_annot kf stmt s = + sync_typedefs (); + let module LT = DefaultLT(struct + let kf = kf + let kinstr = Kstmt stmt + end) in + let loc = Stmt.loc stmt in + let pa = + Option.bind + (Logic_lexer.annot (fst loc,s)) + (function (_, Logic_ptree.Acode_annot (_,a)) -> Some a | _ -> None) + in + let parse pa = + LT.code_annot + (Stmt.loc stmt) + (Logic_utils.get_behavior_names (Annotations.funspec kf)) + (Ctype (Kernel_function.get_return_type kf)) pa + in + wrap parse pa loc + +let default_term_env () = + Logic_typing.append_here_label (Logic_typing.Lenv.empty()) + +let term kf ?(loc=Location.unknown) ?(env=default_term_env ()) s = + sync_typedefs (); + let module LT = DefaultLT(struct + let kf = kf + let kinstr = Kglobal + end) in + let pa_expr = Option.map snd (Logic_lexer.lexpr (fst loc, s)) in + let parse pa_expr = LT.term env pa_expr in + wrap parse pa_expr loc + +let term_lval kf ?(loc=Location.unknown) ?(env=default_term_env ()) s = + match (term kf ~loc ~env s).term_node with + | TLval lv -> lv + | _ -> raise (Error (loc, "Syntax error (expecting an lvalue)")) + +let predicate kf ?(loc=Location.unknown) ?(env=default_term_env ()) s = + sync_typedefs (); + let module LT = DefaultLT(struct + let kf = kf + let kinstr = Kglobal + end) in + let pa_expr = Option.map snd (Logic_lexer.lexpr (fst loc, s)) in + let parse pa_expr = LT.predicate env pa_expr in + wrap parse pa_expr loc diff --git a/src/kernel_services/ast_queries/logic_parse_string.mli b/src/kernel_services/ast_queries/logic_parse_string.mli new file mode 100644 index 00000000000..0083f8bfb0f --- /dev/null +++ b/src/kernel_services/ast_queries/logic_parse_string.mli @@ -0,0 +1,44 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2022 *) +(* 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 + +exception Error of Cil_types.location * string +exception Unbound of string + +(** For the three functions below, [env] can be used to specify which + logic labels are parsed. By default, only [Here] is accepted. All + the C labels inside the function are also accepted, regardless of + [env]. [loc] is used as the source for the beginning of the string. + All three functions may raise {!Logic_interp.Error} or + {!Parsing.Parse_error}. *) + +val code_annot : kernel_function -> stmt -> string -> code_annotation +val term_lval : + kernel_function -> ?loc:location -> ?env:Logic_typing.Lenv.t -> string -> + Cil_types.term_lval +val term : + kernel_function -> ?loc:location -> ?env:Logic_typing.Lenv.t -> string -> + Cil_types.term +val predicate : + kernel_function -> ?loc:location -> ?env:Logic_typing.Lenv.t -> string -> + Cil_types.predicate diff --git a/src/kernel_services/ast_queries/logic_to_c.ml b/src/kernel_services/ast_queries/logic_to_c.ml new file mode 100644 index 00000000000..bf728a97492 --- /dev/null +++ b/src/kernel_services/ast_queries/logic_to_c.ml @@ -0,0 +1,215 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2022 *) +(* 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 +open Cil_types + +exception No_conversion + +let error_lval () = raise No_conversion + +let rec logic_type_to_typ = function + | Ctype typ -> typ + | Linteger -> TInt(ILongLong,[]) (*TODO: to have an unlimited integer type + in the logic interpretation*) + | Lreal -> TFloat(FDouble,[]) (* TODO: handle reals, not floats... *) + | Ltype({lt_name = name},[]) when name = Utf8_logic.boolean -> + TInt(ILongLong,[]) + | Ltype({lt_name = "set"},[t]) -> logic_type_to_typ t + | Ltype _ | Lvar _ | Larrow _ -> error_lval () + + +(* Expect conversion to be possible on all sub-terms, otherwise raise an error. *) + +let logic_var_to_var { lv_origin = lv } = + match lv with + | None -> error_lval () + | Some lv -> lv + +let create_const_list loc kind low high = + let rec aux acc i = + if Integer.lt i low then acc + else + aux (new_exp ~loc (Const (CInt64 (i,kind,None)))::acc) (Integer.pred i) + in aux [] high + +let range low high = + let loc = fst low.eloc, snd high.eloc in + match (Cil.constFold true low).enode, (Cil.constFold true high).enode with + Const(CInt64(low,kind,_)), Const(CInt64(high,_,_)) -> + create_const_list loc kind low high + | _ -> error_lval() + +let singleton f loc = + match f loc with + [ l ] -> l + | _ -> error_lval() + +let rec loc_lval_to_lval ~result (lh, lo) = + Extlib.product + (fun x y -> (x,y)) + (loc_lhost_to_lhost ~result lh) + (loc_offset_to_offset ~result lo) + +and loc_lhost_to_lhost ~result = function + | TVar lvar -> [Var (logic_var_to_var lvar)] + | TMem lterm -> List.map (fun x -> Mem x) (loc_to_exp ~result lterm) + | TResult _ -> + ( match result with + None -> error_lval() + | Some v -> [Var v]) + +and loc_offset_to_offset ~result = function + | TNoOffset -> [NoOffset] + | TModel _ -> error_lval () + | TField (fi, lo) -> + List.map (fun x -> Field (fi,x)) (loc_offset_to_offset ~result lo) + | TIndex (lexp, lo) -> + Extlib.product + (fun x y -> Index(x,y)) + (loc_to_exp ~result lexp) + (loc_offset_to_offset ~result lo) + +and loc_to_exp ~result {term_node = lnode ; term_type = ltype; term_loc = loc} = + match lnode with + | TLval lv -> + List.map (fun x -> new_exp ~loc (Lval x)) (loc_lval_to_lval ~result lv) + | TAddrOf lv -> + List.map (fun x -> new_exp ~loc (AddrOf x)) (loc_lval_to_lval ~result lv) + | TStartOf lv -> + List.map (fun x -> new_exp ~loc (StartOf x)) (loc_lval_to_lval ~result lv) + | TSizeOfE lexp -> + List.map (fun x -> new_exp ~loc (SizeOfE x)) (loc_to_exp ~result lexp) + | TAlignOfE lexp -> + List.map (fun x -> new_exp ~loc (AlignOfE x)) (loc_to_exp ~result lexp) + | TUnOp (unop, lexp) -> + List.map + (fun x -> new_exp ~loc (UnOp (unop, x, logic_type_to_typ ltype))) + (loc_to_exp ~result lexp) + | TBinOp (binop, lexp1, lexp2) -> + Extlib.product + (fun x y -> new_exp ~loc (BinOp (binop, x,y, logic_type_to_typ ltype))) + (loc_to_exp ~result lexp1) + (loc_to_exp ~result lexp2) + | TSizeOfStr string -> [new_exp ~loc (SizeOfStr string)] + | TConst constant -> + (* TODO: Very likely to fail on large integer and incorrect on reals not + representable as floats *) + [new_exp ~loc (Const (Logic_utils.lconstant_to_constant constant))] + | TCastE (typ, lexp) -> + List.map + (fun x -> new_exp ~loc (CastE (typ, x))) (loc_to_exp ~result lexp) + | TAlignOf typ -> [new_exp ~loc (AlignOf typ)] + | TSizeOf typ -> [new_exp ~loc (SizeOf typ)] + | Trange (Some low, Some high) -> + let low = singleton (loc_to_exp ~result) low in + let high = singleton (loc_to_exp ~result) high in + range low high + | Tunion l -> List.concat (List.map (loc_to_exp ~result) l) + | Tempty_set -> [] + | Tinter _ | Tcomprehension _ -> error_lval() + | Tat ({term_node = TAddrOf (TVar _, TNoOffset)} as taddroflval, _) -> + loc_to_exp ~result taddroflval + | TLogic_coerce(Linteger, t) when Logic_typing.is_integral_type t.term_type -> + loc_to_exp ~result t + | TLogic_coerce(Lreal, t) when Logic_typing.is_integral_type t.term_type -> + List.map + (fun x -> new_exp ~loc (CastE (logic_type_to_typ Lreal, x))) + (loc_to_exp ~result t) + | TLogic_coerce(Lreal, t) when Logic_typing.is_arithmetic_type t.term_type -> + loc_to_exp ~result t + | TLogic_coerce (set, t) + when + Logic_const.is_set_type set && + Logic_utils.is_same_type + (Logic_typing.type_of_set_elem set) t.term_type -> + loc_to_exp ~result t + | Tnull -> [ Cil.mkCast ~newt:(TPtr(TVoid [], [])) (Cil.zero ~loc) ] + + (* additional constructs *) + | Tapp _ | Tlambda _ | Trange _ | Tlet _ + | TDataCons _ + | Tif _ + | Tat _ + | Tbase_addr _ + | Toffset _ + | Tblock_length _ + | TUpdate _ | Ttypeof _ | Ttype _ + | TLogic_coerce _ + -> error_lval () + +let rec loc_to_lval ~result t = + match t.term_node with + | TLval lv -> loc_lval_to_lval ~result lv + | TAddrOf lv -> loc_lval_to_lval ~result lv + | TStartOf lv -> loc_lval_to_lval ~result lv + | Tunion l1 -> List.concat (List.map (loc_to_lval ~result) l1) + | Tempty_set -> [] + (* coercions to arithmetic types cannot be lval. We only have to consider + a coercion to set here. + *) + | TLogic_coerce(set, t) when + Logic_typing.is_set_type set && + Logic_utils.is_same_type + (Logic_typing.type_of_set_elem set) t.term_type -> + loc_to_lval ~result t + | Tinter _ -> error_lval() (* TODO *) + | Tcomprehension _ -> error_lval() + | TSizeOfE _ | TAlignOfE _ | TUnOp _ | TBinOp _ | TSizeOfStr _ + | TConst _ | TCastE _ | TAlignOf _ | TSizeOf _ | Tapp _ | Tif _ + | Tat _ | Toffset _ | Tbase_addr _ | Tblock_length _ | Tnull | Trange _ + | TDataCons _ | TUpdate _ | Tlambda _ + | Ttypeof _ | Ttype _ | Tlet _ | TLogic_coerce _ -> + error_lval () + +let loc_to_offset ~result loc = + let rec aux h = + function + TLval(h',o) | TStartOf (h',o) -> + (match h with None -> Some h', loc_offset_to_offset ~result o + | Some h when Logic_utils.is_same_lhost h h' -> + Some h, loc_offset_to_offset ~result o + | Some _ -> error_lval() + ) + | Tat ({ term_node = TLval(TResult _,_)} as lv, BuiltinLabel Post) -> + aux h lv.term_node + | Tunion locs -> List.fold_left + (fun (b,l) x -> + let (b,l') = aux b x.term_node in b, l @ l') (h,[]) locs + | Tempty_set -> h,[] + | Trange _ | TAddrOf _ | Tat _ + | TSizeOfE _ | TAlignOfE _ | TUnOp _ | TBinOp _ | TSizeOfStr _ + | TConst _ | TCastE _ | TAlignOf _ | TSizeOf _ | Tapp _ | Tif _ + | Toffset _ | Tbase_addr _ | Tblock_length _ | Tnull + | TDataCons _ | TUpdate _ | Tlambda _ + | Ttypeof _ | Ttype _ | Tcomprehension _ | Tinter _ | Tlet _ + | TLogic_coerce _ + -> error_lval () + in snd (aux None loc.term_node) + +let term_lval_to_lval ~result = singleton (loc_lval_to_lval ~result) + +let term_to_lval ~result = singleton (loc_to_lval ~result) + +let term_to_exp ~result = singleton (loc_to_exp ~result) + +let term_offset_to_offset ~result = singleton (loc_offset_to_offset ~result) diff --git a/src/kernel_services/ast_queries/logic_to_c.mli b/src/kernel_services/ast_queries/logic_to_c.mli new file mode 100644 index 00000000000..9ceaacced24 --- /dev/null +++ b/src/kernel_services/ast_queries/logic_to_c.mli @@ -0,0 +1,59 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2022 *) +(* 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 + +exception No_conversion + +val logic_type_to_typ : logic_type -> typ +val logic_var_to_var : logic_var -> varinfo + +val loc_lval_to_lval : result:varinfo option -> term_lval -> lval list +val loc_lhost_to_lhost : result:varinfo option -> term_lhost -> lhost list +val loc_offset_to_offset : result:varinfo option -> term_offset -> offset list + +val loc_to_exp : result:varinfo option -> term -> exp list +(** @return a list of C expressions. + @raise No_conversion if the argument is not a valid set of + expressions. *) + +val loc_to_lval : result:varinfo option -> term -> lval list +(** @return a list of C locations. + @raise No_conversion if the argument is not a valid set of + left values. *) + +val loc_to_offset : result:varinfo option -> term -> offset list +(** @return a list of C offset provided the term denotes locations who + have all the same base address. + @raise No_conversion if the given term does not match the precondition *) + +val term_lval_to_lval : result:varinfo option -> term_lval -> lval +(** @raise No_conversion if the argument is not a left value. *) + +val term_to_lval : result:varinfo option -> term -> lval +(** @raise No_conversion if the argument is not a left value. *) + +val term_to_exp : result:varinfo option -> term -> exp +(** @raise No_conversion if the argument is not a valid expression. *) + +val term_offset_to_offset : result:varinfo option -> term_offset -> offset +(** @raise No_conversion if the argument is not a valid offset. *) diff --git a/src/kernel_services/plugin_entry_points/db.ml b/src/kernel_services/plugin_entry_points/db.ml index 539411cd3e6..87865669f87 100644 --- a/src/kernel_services/plugin_entry_points/db.ml +++ b/src/kernel_services/plugin_entry_points/db.ml @@ -694,25 +694,25 @@ module Properties = struct module Interp = struct - exception No_conversion + exception No_conversion = Logic_to_c.No_conversion (** Interpretation and conversions of of formulas *) - let code_annot = mk_fun "Properties.Interp.code_annot" - let term_lval = mk_fun "Properties.Interp.term_lval" - let term = mk_fun "Properties.Interp.term" - let predicate = mk_fun "Properties.Interp.predicate" - let term_lval_to_lval = mk_resultfun "Properties.Interp.term_lval_to_lval" - let term_to_exp = mk_resultfun "Properties.Interp.term_to_exp" - let term_to_lval = mk_resultfun "Properties.Interp.term_to_lval" - let loc_to_lval = mk_resultfun "Properties.Interp.loc_to_lval" + let code_annot = ref Logic_parse_string.code_annot + let term_lval = ref Logic_parse_string.term_lval + let term = ref Logic_parse_string.term + let predicate = ref Logic_parse_string.predicate + + let term_lval_to_lval = ref Logic_to_c.term_lval_to_lval + let term_to_exp = ref Logic_to_c.term_to_exp + let term_to_lval = ref Logic_to_c.term_to_lval + let loc_to_lval = ref Logic_to_c.loc_to_lval (* loc_to_loc and loc_to_locs are defined in Value/Eval_logic, not in Logic_interp *) let loc_to_loc = mk_resultfun "Properties.Interp.loc_to_loc" let loc_to_loc_under_over = mk_resultfun "Properties.Interp.loc_to_loc_with_deps" - let loc_to_offset = mk_resultfun "Properties.Interp.loc_to_offset" - let loc_to_exp = mk_resultfun "Properties.Interp.loc_to_exp" - let term_offset_to_offset = - mk_resultfun "Properties.Interp.term_offset_to_offset" + let loc_to_offset = ref Logic_to_c.loc_to_offset + let loc_to_exp = ref Logic_to_c.loc_to_exp + let term_offset_to_offset = ref Logic_to_c.term_offset_to_offset module To_zone : sig (** The signature of the mli is copy pasted here because of -- GitLab