123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219(**************************************************************************)(* *)(* This file is part of the Frama-C's MetACSL plug-in. *)(* *)(* Copyright (C) 2018-2025 *)(* 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 LICENSE) *)(* *)(**************************************************************************)openCil_types(* Check if a logic variable corresponds to a C variable in the source code
* (to easily discard a separation between it and something else if there is no
* origin)
*)letis_not_orig_variablelv=lv.lv_name="__retres"||String.starts_with~prefix:"__fc_"lv.lv_name||matchlv.lv_originwith|Somev->notv.vsource||v.vtemp||Ast_info.start_with_frama_c_builtinv.vname|None->true(*
* Returns true if two tlvals are obviously \separated
* That is, if they are both named variables with different names or with non-overlapping
* offsets
*)letneq_lvaltl1tl2=let(h1,of1),(h2,of2)=tl1,tl2inletrecoffset_neqo1o2=match(o1,o2)with|TNoOffset,_|_,TNoOffset->false|TField(d1,tt1),TField(d2,tt2)->ifCil_datatype.Compinfo.equald1.fcompd2.fcomp&&d1.fcomp.cstructthenifCil_datatype.Fieldinfo.equald1d2thenoffset_neqtt1tt2elsetrueelsefalse|TModel(d1,tt1),TModel(d2,tt2)->ifLogic_utils.is_same_model_infod1d2thenoffset_neqtt1tt2elsetrue|TIndex_,TIndex_->false(* Could be improved *)|_->truein(* TODO correct management of type inclusion / structure !
* struct S { int a, char b; } s;
* struct S* sp;
* \untouched(s); \untouched(sp)
* long* p = &(s.b);
* *p = 42; //SHOUD EMIT ALARM FOR BOTH s AND sp
* Q: Is there a field type of S that can be casted to the type of p
*)(* For now, never discard when the *target* (a) is a struct/union *)(*let st2 = Cil.isStructOrUnionType ft2 in*)match(h1,h2)with|TVarlv,_whenis_not_orig_variablelv->true|_,TVarlvwhenis_not_orig_variablelv->true|TVarl1,TVarl2->not(Logic_utils.is_same_varl1l2)||offset_neqof1of2|_->false(*
Assuming t is a term representing an address,
returns the lval it is an address of
*)letget_addressed_lvalt=matcht.term_nodewith|TAddrOfl->l|TStartOfl->l|_->(TMemt,TNoOffset)(*
If t is explicitely the address of an object,
returns that object.
*)letget_addressed_var_optt=matcht.term_nodewith|TAddrOfl->Somel|TStartOfl->Somel|_->None(*
* Simplifies \separated predicates to \true or \false when possible, and
* propagates through common logic operators. Also simplifies equality and
* difference when terms are the same
*)classsimplifier_visitor=object(_)inheritVisitor.frama_c_inplacemethod!vpredicate_node=function|Pseparated[t1;t2]whenLogic_utils.is_same_termt1t2->Cil.ChangeToPfalse|Pseparated[t1;t2]->letl1=get_addressed_lvalt1inletl2=get_addressed_lvalt2inifneq_lvall1l2thenCil.ChangeToPtrueelseCil.DoChildren|Prel(Rneq,t1,t2)whenLogic_utils.is_same_termt1t2->Cil.ChangeToPfalse|Prel(Req,t1,t2)whenLogic_utils.is_same_termt1t2->Cil.ChangeToPtrue|Prel((Req|Rneq)asrel,t1,t2)whenOption.is_some(get_addressed_var_optt1)&&Option.is_some(get_addressed_var_optt2)->letl1=Option.get(get_addressed_var_optt1)inletl2=Option.get(get_addressed_var_optt2)inifneq_lvall1l2thenifrel=RneqthenCil.ChangeToPtrueelseCil.ChangeToPfalseelseCil.DoChildren|Pnot_->Cil.DoChildrenPost(function|PnottwhenLogic_utils.is_trivially_truet->Pfalse|PnottwhenLogic_utils.is_trivially_falset->Ptrue|p->p)|Pforall_->Cil.DoChildrenPost(function|Pforall(_,p)whenLogic_utils.is_trivially_truep->Ptrue|Pforall(_,p)whenLogic_utils.is_trivially_falsep->Pfalse|p->p)|Plet(_,_)->Cil.DoChildrenPost(function|Plet(_,p)whenLogic_utils.is_trivially_truep->Ptrue|Plet(_,p)whenLogic_utils.is_trivially_falsep->Pfalse|p->p)|Pimplies_->Cil.DoChildrenPost(function|Pimplies(p1,_)whenLogic_utils.is_trivially_falsep1->Ptrue|Pimplies(_,p2)whenLogic_utils.is_trivially_truep2->Ptrue|Pimplies(p1,p2)whenLogic_utils.is_trivially_truep1&&Logic_utils.is_trivially_falsep2->Pfalse|Pimplies(p1,p2)whenLogic_utils.is_trivially_falsep2->Pnotp1|Pimplies(p1,p2)whenLogic_utils.is_trivially_truep1->p2.pred_content|p->p)|Pand_->Cil.DoChildrenPost(function|Pand(p1,_)whenLogic_utils.is_trivially_falsep1->Pfalse|Pand(_,p2)whenLogic_utils.is_trivially_falsep2->Pfalse|Pand(p1,p2)whenLogic_utils.is_trivially_truep1&&Logic_utils.is_trivially_truep2->Ptrue|Pand(p1,p2)whenLogic_utils.is_trivially_truep1->p2.pred_content|Pand(p1,p2)whenLogic_utils.is_trivially_truep2->p1.pred_content|p->p)|Por_->Cil.DoChildrenPost(function|Por(p1,_)whenLogic_utils.is_trivially_truep1->Ptrue|Por(_,p2)whenLogic_utils.is_trivially_truep2->Ptrue|Por(p1,p2)whenLogic_utils.is_trivially_falsep1&&Logic_utils.is_trivially_falsep2->Pfalse|Por(p1,p2)whenLogic_utils.is_trivially_falsep1->p2.pred_content|Por(p1,p2)whenLogic_utils.is_trivially_falsep2->p1.pred_content|p->p)|_->Cil.DoChildrenendletsimplifypred=letvis=newsimplifier_visitorinVisitor.visitFramacPredicatevispred(*
* Given a predicate and the statement (and kf) it will be attached to,
* return another predicate where every quantified, local or formal logic
* variable whose name conflict with a C name already in scope has been alpha
* converted
*)letremove_alpha_conflictspredkfstmt=ifstmt.ghostthenpredelseletenclosing=Kernel_function.find_all_enclosing_blocksstmtin(* List of local variables in scope and formal variables *)letreserved=List.map(funv->v.vname)@@List.fold_left(funnamesblock->(block.blocals@block.bstatics)@names)(Kernel_function.get_formalskf)enclosinginletvisitor=object(self)inheritVisitor.frama_c_inplace(* Names that cannot be used *)valmutablereserved_up=reserved(* New names created for reserved names *)valmutableassoc=[](* Name that can be used but not when creating new names *)valmutablepreserve=[]methodcommonlv=matchlv.lv_kindwith|LVQuant|LVLocal|LVFormal->letname=lv.lv_nameinifList.memnamereserved_upthenmatchList.assoc_optnameassocwith|Somelv->Cil.ChangeTolv|None->letrecfind_valid_suffixcur=letconc=name^(string_of_intcur)inifList.memconc(reserved_up@preserve)thenfind_valid_suffix@@cur+1elseconcinletnn=find_valid_suffix1inletnew_lv={lvwithlv_name=nn}inassoc<-(name,new_lv)::assoc;reserved_up<-nn::reserved_up;Cil.ChangeTonew_lvelse(preserve<-name::preserve;Cil.SkipChildren)|_->Cil.SkipChildrenmethod!vlogic_var_decl=self#commonmethod!vlogic_var_use=self#commonendinVisitor.visitFramacPredicate(visitor:>Visitor.frama_c_visitor)pred