diff --git a/src/kernel_services/ast_queries/logic_utils.ml b/src/kernel_services/ast_queries/logic_utils.ml index fb00eb02fa931eef53384306ea221720419d599f..c861c16b52339f693172cfef5e51f30950ce8aeb 100644 --- a/src/kernel_services/ast_queries/logic_utils.ml +++ b/src/kernel_services/ast_queries/logic_utils.ml @@ -230,7 +230,8 @@ let mk_cast ?(loc=Cil_datatype.Location.unknown) ?(force=false) newt t = let typ = Cil.type_remove_attributes_for_logic_type newt in term ~loc (TCastE (typ, t)) (Ctype typ) in - let rec aux1 = function + let rec aux1 typ t = + match typ with | Ctype oldt -> if not (need_logic_cast oldt newt) && not force then t else begin @@ -246,15 +247,21 @@ let mk_cast ?(loc=Cil_datatype.Location.unknown) ?(force=false) newt t = | _, TConst (Integer (i,_)) when Integer.is_zero i -> mk_cast t' | _ -> mk_cast t ) - | Ltype (tdef,_) as ty when is_unrollable_ltdef tdef -> aux2 (unroll_ltdef ty) + | Ltype (tdef,_) as ty when is_unrollable_ltdef tdef -> + aux2 (unroll_ltdef ty) | _ -> mk_cast t in aux2 t'.term_type | _ -> (* Do not remove old cast because they are conversions !!! *) mk_cast t end - | Ltype (tdef,_) as ty when is_unrollable_ltdef tdef -> aux1 (unroll_ltdef ty) + | Ltype (tdef,_) as ty when is_unrollable_ltdef tdef -> + aux1 (unroll_ltdef ty) t + | Linteger | Lreal -> + (match t.term_node with + | TLogic_coerce (_,t') -> aux1 t'.term_type t' + | _ -> mk_cast t) | _ -> mk_cast t - in aux1 t.term_type + in aux1 t.term_type t let real_of_float s f = { r_literal = s ; r_nearest = f ; r_upper = f ; r_lower = f } @@ -334,39 +341,44 @@ let is_zero_comparable t = | Ltype _ -> false | Lvar _ | Larrow _ -> false -let scalar_term_to_predicate t = +let scalar_term_conversion conversion t = let loc = t.term_loc in - let conversion zero = prel ~loc (Cil_types.Rneq, t, zero) in - let arith_conversion () = conversion (Cil.lzero ~loc ()) in - let ptr_conversion () = conversion (Logic_const.term ~loc Tnull t.term_type) + let arith_conversion () = conversion ~loc false t (Cil.lzero ~loc ()) in + let ptr_conversion () = + conversion ~loc false t (Logic_const.term ~loc Tnull t.term_type) in match unroll_type t.term_type with | Ctype (TInt _) -> arith_conversion () | Ctype (TFloat _) -> - conversion - (Logic_const.treal_zero ~loc ~ltyp:t.term_type ()) + conversion ~loc false t (Logic_const.treal_zero ~loc ~ltyp:t.term_type ()) | Ctype (TPtr _) -> ptr_conversion () | Ctype (TArray _) -> ptr_conversion () (* Could be transformed to \true: an array is never \null *) | Ctype (TFun _) -> ptr_conversion () (* decay as pointer *) | Linteger -> arith_conversion () - | Lreal -> conversion (Logic_const.treal_zero ~loc ()) + | Lreal -> conversion ~loc false t (Logic_const.treal_zero ~loc ()) | Ltype ({lt_name = name},[]) when name = Utf8_logic.boolean -> let ctrue = Logic_env.Logic_ctor_info.find "\\true" in - prel ~loc - (Cil_types.Req,t, - { term_node = TDataCons(ctrue,[]); - term_loc = loc; - term_type = Ltype(ctrue.ctor_type,[]); - term_name = []; - }) + conversion ~loc true t (term ~loc (TDataCons(ctrue,[])) boolean_type) | Ltype _ | Lvar _ | Larrow _ | Ctype (TVoid _ | TNamed _ | TComp _ | TEnum _ | TBuiltin_va_list _) -> Kernel.fatal - "Cannot convert to predicate a term of type %a" + "Cannot convert a term of type %a" Cil_printer.pp_logic_type t.term_type +let scalar_term_to_predicate = + let conversion ~loc is_eq t1 t2 = + let op = if is_eq then Req else Rneq in prel ~loc (op, t1, t2) in + scalar_term_conversion conversion + +let scalar_term_to_boolean = + let conversion ~loc is_eq t1 t2 = + let op = if is_eq then Eq else Ne in + term ~loc (TBinOp(op,t1,t2)) Logic_const.boolean_type + in + scalar_term_conversion conversion + let rec expr_to_term ~cast e = let e_typ = unrollType (Cil.typeOf e) in let loc = e.eloc in @@ -403,6 +415,21 @@ let rec expr_to_term ~cast e = end | UnOp (op, u, _) -> let u' = expr_to_term_coerce ~cast u in + let u' = + match op with + | Cil_types.LNot -> + (match u'.term_node with + | TCastE(_, t) when is_boolean_type t.term_type -> t + | _ when is_boolean_type u'.term_type -> u' + | _ when is_zero_comparable u' -> + scalar_term_to_boolean u' + | _ -> + Kernel.fatal + "expr_to_term: unexpected argument of ! operator %a, \ + converted to %a" + Cil_printer.pp_exp u Cil_printer.pp_term u') + | _ -> u' + in (* See comments for binop case above. *) let tcast = match op, cast with | Cil_types.LNot, _ -> Some Logic_const.boolean_type @@ -419,14 +446,14 @@ let rec expr_to_term ~cast e = | Lval lv -> TLval (lval_to_term_lval ~cast lv) | Info (e,_) -> (expr_to_term ~cast e).term_node in - if cast then Logic_const.term ~loc result (Ctype e_typ) + let tres = Logic_const.term ~loc result (Ctype e_typ) in + if cast then tres else match e.enode with - | Const _ | Lval _ | CastE _ -> - (* all immediate values keep their C type by default, and are only lifted - to integer/real if needed. *) - Logic_const.term ~loc result (Ctype e_typ) - | _ -> Logic_const.term ~loc result (typ_to_logic_type e_typ) + (* all immediate values keep their C type by default, and are only lifted + to integer/real if needed. *) + | Const _ | Lval _ | CastE _ -> tres + | _ -> numeric_coerce (typ_to_logic_type e_typ) tres and expr_to_term_coerce ~cast e = let t = expr_to_term ~cast e in diff --git a/tests/spec/expr_to_term.i b/tests/spec/expr_to_term.i index 12078604eca0f6f0bf50d630d939b9539203ff83..5320e071398288731edd84d484de97ffc5cf4677 100644 --- a/tests/spec/expr_to_term.i +++ b/tests/spec/expr_to_term.i @@ -1,6 +1,6 @@ /* run.config -EXECNOW: make -s @PTEST_DIR@/@PTEST_NAME@.cmxs -OPT: -no-autoload-plugins -load-module @PTEST_DIR@/@PTEST_NAME@.cmxs -print +MODULE: @PTEST_DIR@/@PTEST_NAME@.cmxs +OPT: -print */ int x[10]; @@ -28,3 +28,8 @@ int main() { s.z = 3; t = 4; } + +/*@ ensures int_eq((int)!t,(int)5); */ +int g() { + if (!t) return 2; else return 3; +} diff --git a/tests/spec/expr_to_term.ml b/tests/spec/expr_to_term.ml index 87798a466f270dc60bd17a10d194ab3e64498898..17478ac46f188ed3bd618d92ccdab4ea2d51c863 100644 --- a/tests/spec/expr_to_term.ml +++ b/tests/spec/expr_to_term.ml @@ -6,6 +6,7 @@ let check_expr_term check fct s e = let exp = match s.skind with | Instr (Set (lv,_,loc)) -> Cil.new_exp ~loc (Lval lv) + | If (c,_,_,_) -> c | _ -> Kernel.fatal "Unexpected statement %a" Printer.pp_stmt s in let term = @@ -14,16 +15,16 @@ let check_expr_term check fct s e = | _ -> Kernel.fatal "Unexpected ensures %a" Printer.pp_post_cond e in let term' = Logic_utils.expr_to_term ~cast:false exp in + let term' = Logic_utils.mk_cast Cil.intType term' in if check && not (Cil_datatype.Term.equal term term') then Kernel.fatal - "translation of C expression %a inconsistent with logic term %a" - Printer.pp_exp exp Printer.pp_term term; + "translation of C expression %a is %a, inconsistent with logic term %a" + Printer.pp_exp exp Printer.pp_term term' Printer.pp_term term; let p = List.hd (Logic_env.find_all_logic_functions "int_eq") in let app = Logic_const.papp (p,[],[term;term']) in let post = Logic_const.new_predicate app in Annotations.add_ensures emitter fct [Normal,post] - let treat_fct check fct = let stmts = (Kernel_function.get_definition fct).sbody.bstmts in let stmts = @@ -31,6 +32,7 @@ let treat_fct check fct = (function { skind = Instr (Set (lv,_,_)) } -> (match lv with (Var v,_) -> v.vglob | _ -> true) + | { skind = If _ } -> true | _ -> false) stmts in @@ -49,8 +51,10 @@ let treat_fct check fct = let compute () = let main = Globals.Functions.find_by_name "main" in let f = Globals.Functions.find_by_name "f" in + let g = Globals.Functions.find_by_name "g" in treat_fct true main; - treat_fct false f + treat_fct false f; + treat_fct true g let () = Db.Main.extend compute diff --git a/tests/spec/oracle/expr_to_term.res.oracle b/tests/spec/oracle/expr_to_term.res.oracle index ed2717ba97a03b3e45decf484c8d857f0519c5b6..a378207a1b9cec63fef9d84d30052cc663076a14 100644 --- a/tests/spec/oracle/expr_to_term.res.oracle +++ b/tests/spec/oracle/expr_to_term.res.oracle @@ -40,4 +40,21 @@ int main(void) return __retres; } +/*@ ensures int_eq((int)(!(t ≢ 0)), (int)(!(t ≢ 0))); + ensures int_eq((int)(!(t ≢ 0)), (int)5); + */ +int g(void) +{ + int __retres; + if (! t) { + __retres = 2; + goto return_label; + } + else { + __retres = 3; + goto return_label; + } + return_label: return __retres; +} +