123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614(*
Copyright (c) 2001-2002,
John Kodumal <jkodumal@eecs.berkeley.edu>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The names of the contributors may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*)openGoblintCil(***********************************************************************)(* *)(* Exceptions *)(* *)(***********************************************************************)exceptionInconsistent(* raised if constraint system is inconsistent *)exceptionWellFormed(* raised if types are not well-formed *)exceptionNoContentsexceptionAPFound(* raised if an alias pair is found, a control
flow exception *)moduleU=UrefmoduleS=SetpmoduleH=HashtblmoduleQ=Queue(** Subtyping kinds *)typepolarity=Pos|Neg|Sub(** Path kinds, for CFL reachability *)typepkind=Positive|Negative|Match|Seed(** Context kinds -- open or closed *)typecontext=Open|Closed(* A configuration is a context (open or closed) coupled with a pair
of stamps representing a state in the cartesian product DFA. *)typeconfiguration=context*int*intmoduleConfigHash=structtypet=configurationletequaltt'=t=t'lethasht=Hashtbl.hashtendmoduleCH=H.Make(ConfigHash)typeconfig_map=unitCH.t(** Generic bounds *)type'abound={index:int;info:'aU.uref}(** For label paths. *)type'apath={kind:pkind;reached_global:bool;head:'aU.uref;tail:'aU.uref}moduleBound=structtype'at='aboundletcompare(x:'at)(y:'at)=ifU.equal(x.info,y.info)thenx.index-y.indexelseStdlib.compare(U.derefx.info)(U.derefy.info)endmodulePath=structtype'at='apathletcompare(x:'at)(y:'at)=ifU.equal(x.head,y.head)thenbeginifU.equal(x.tail,y.tail)thenbeginifx.reached_global=y.reached_globalthenStdlib.comparex.kindy.kindelseStdlib.comparex.reached_globaly.reached_globalendelseStdlib.compare(U.derefx.tail)(U.derefy.tail)endelseStdlib.compare(U.derefx.head)(U.derefy.head)endmoduleB=S.Make(Bound)moduleP=S.Make(Path)type'aboundset='aB.ttype'apathset='aP.t(** Constants, which identify elements in points-to sets *)(* jk : I'd prefer to make this an 'a constant and specialize it to varinfo
for use with the Cil frontend, but for now, this will do *)typeconstant=int*string*Cil.varinfomoduleConstant=structtypet=constantletcompare(xid,_,_)(yid,_,_)=xid-yidendmoduleC=Set.Make(Constant)(** Sets of constants. Set union is used when two labels containing
constant sets are unified *)typeconstantset=C.ttypelblinfo={mutablel_name:string;(** either empty or a singleton, the initial location for this label *)loc:constantset;(** Name of this label *)l_stamp:int;(** Unique integer for this label *)mutablel_global:bool;(** True if this location is globally accessible *)mutablealiases:constantset;(** Set of constants (tags) for checking aliases *)mutablep_lbounds:lblinfoboundset;(** Set of umatched (p) lower bounds *)mutablen_lbounds:lblinfoboundset;(** Set of unmatched (n) lower bounds *)mutablep_ubounds:lblinfoboundset;(** Set of umatched (p) upper bounds *)mutablen_ubounds:lblinfoboundset;(** Set of unmatched (n) upper bounds *)mutablem_lbounds:lblinfoboundset;(** Set of matched (m) lower bounds *)mutablem_ubounds:lblinfoboundset;(** Set of matched (m) upper bounds *)mutablem_upath:lblinfopathset;mutablem_lpath:lblinfopathset;mutablen_upath:lblinfopathset;mutablen_lpath:lblinfopathset;mutablep_upath:lblinfopathset;mutablep_lpath:lblinfopathset;mutablel_seeded:bool;mutablel_ret:bool;mutablel_param:bool;}(** Constructor labels *)andlabel=lblinfoU.uref(** The type of lvalues. *)typelvalue={l:label;contents:tau}andvinfo={v_stamp:int;v_name:string;mutablev_hole:(int,unit)H.t;mutablev_global:bool;mutablev_mlbs:tinfoboundset;mutablev_mubs:tinfoboundset;mutablev_plbs:tinfoboundset;mutablev_pubs:tinfoboundset;mutablev_nlbs:tinfoboundset;mutablev_nubs:tinfoboundset}andrinfo={r_stamp:int;rl:label;points_to:tau;mutabler_global:bool;}andfinfo={f_stamp:int;fl:label;ret:tau;mutableargs:taulist;mutablef_global:bool;}andpinfo={p_stamp:int;ptr:tau;lam:tau;mutablep_global:bool;}andtinfo=Varofvinfo|Refofrinfo|Funoffinfo|Pairofpinfoandtau=tinfoU.ureftypetconstraint=Unificationoftau*tau|Leqoftau*(int*polarity)*tau(** Association lists, used for printing recursive types. The first element
is a type that has been visited. The second element is the string
representation of that type (so far). If the string option is set, then
this type occurs within itself, and is associated with the recursive var
name stored in the option. When walking a type, add it to an association
list.
Example : suppose we have the constraint 'a = ref('a). The type is unified
via cyclic unification, and would loop infinitely if we attempted to print
it. What we want to do is print the type u rv. ref(rv). This is accomplished
in the following manner:
-- ref('a) is visited. It is not in the association list, so it is added
and the string "ref(" is stored in the second element. We recurse to print
the first argument of the constructor.
-- In the recursive call, we see that 'a (or ref('a)) is already in the
association list, so the type is recursive. We check the string option,
which is None, meaning that this is the first recurrence of the type. We
create a new recursive variable, rv and set the string option to 'rv. Next,
we prepend u rv. to the string representation we have seen before, "ref(",
and return "rv" as the string representation of this type.
-- The string so far is "u rv.ref(". The recursive call returns, and we
complete the type by printing the result of the call, "rv", and ")"
In a type where the recursive variable appears twice, e.g. 'a = pair('a,'a),
the second time we hit 'a, the string option will be set, so we know to
reuse the same recursive variable name.
*)typeassociation=tau*stringref*stringoptionrefmodulePathHash=structtypet=intlistletequaltt'=t=t'lethasht=Hashtbl.hashtendmodulePH=H.Make(PathHash)(***********************************************************************)(* *)(* Global Variables *)(* *)(***********************************************************************)(** Print the instantiations constraints. *)letprint_constraints:boolref=reffalse(** If true, print all constraints (including induced) and show
additional debug output. *)letdebug=reffalse(** Just debug all the constraints (including induced) *)letdebug_constraints=reffalse(** Debug smart alias queries *)letdebug_aliases=reffalseletsmart_aliases=reffalse(** If true, make the flow step a no-op *)letno_flow=reffalse(** If true, disable subtyping (unification at all levels) *)letno_sub=reffalse(** If true, treat indexed edges as regular subtyping *)letanalyze_mono=reftrue(** A list of equality constraints. *)leteq_worklist:tconstraintQ.t=Q.create()(** A list of leq constraints. *)letleq_worklist:tconstraintQ.t=Q.create()letpath_worklist:(lblinfopath)Q.t=Q.create()letpath_hash:(lblinfopath)PH.t=PH.create32(** A count of the constraints introduced from the AST. Used for debugging. *)lettoplev_count=ref0(** A hashtable containing stamp pairs of labels that must be aliased. *)letcached_aliases:(int*int,unit)H.t=H.create64(** A hashtable mapping pairs of tau's to their join node. *)letjoin_cache:(int*int,tau)H.t=H.create64(***********************************************************************)(* *)(* Utility Functions *)(* *)(***********************************************************************)letfind=U.derefletdies=Printf.printf"*******\nAssertion failed: %s\n*******\n"s;assertfalseletfresh_appsite:(unit->int)=letappsite_index=ref0infun()->incrappsite_index;!appsite_index(** Generate a unique integer. *)letfresh_index:(unit->int)=letcounter=ref0infun()->incrcounter;!counterletfresh_stamp:(unit->int)=letstamp=ref0infun()->incrstamp;!stamp(** Return a unique integer representation of a tau *)letget_stamp(t:tau):int=matchfindtwithVarv->v.v_stamp|Refr->r.r_stamp|Pairp->p.p_stamp|Funf->f.f_stamp(** Consistency checks for inferred types *)letpair_or_var(t:tau)=matchfindtwithPair_->true|Var_->true|_->falseletref_or_var(t:tau)=matchfindtwithRef_->true|Var_->true|_->falseletfun_or_var(t:tau)=matchfindtwithFun_->true|Var_->true|_->false(** Apply [f] structurally down [t]. Guaranteed to terminate, even if [t]
is recursive *)letiter_tauft=letvisited:(int,tau)H.t=H.create4inletreciter_tau't=ifH.memvisited(get_stampt)then()elsebeginft;H.addvisited(get_stampt)t;matchU.dereftwithPairp->iter_tau'p.ptr;iter_tau'p.lam|Funf->List.iteriter_tau'(f.args);iter_tau'f.ret|Refr->iter_tau'r.points_to|_->()endiniter_tau't(* Extract a label's bounds according to [positive] and [upper]. *)letget_bounds(p:polarity)(upper:bool)(l:label):lblinfoboundset=letli=findlinmatchpwithPos->ifupperthenli.p_uboundselseli.p_lbounds|Neg->ifupperthenli.n_uboundselseli.n_lbounds|Sub->ifupperthenli.m_uboundselseli.m_lboundsletequal_tau(t:tau)(t':tau)=get_stampt=get_stampt'letget_label_stamp(l:label):int=(findl).l_stamp(** Return true if [t] is global (treated monomorphically) *)letget_global(t:tau):bool=matchfindtwithVarv->v.v_global|Refr->r.r_global|Pairp->p.p_global|Funf->f.f_globalletis_ret_labell=(findl).l_ret||(findl).l_global(* todo - check *)letis_param_labell=(findl).l_param||(findl).l_globalletis_global_labell=(findl).l_globalletis_seeded_labell=(findl).l_seededletset_global_label(l:label)(b:bool):unit=assert((not(is_global_labell))||b);(U.derefl).l_global<-b(** Aliases for set_global *)letglobal_tau=get_global(** Get_global for lvalues *)letglobal_lvaluelv=get_globallv.contents(***********************************************************************)(* *)(* Printing Functions *)(* *)(***********************************************************************)letstring_of_configuration(c,i,i')=letcontext=matchcwithOpen->"O"|Closed->"C"inPrintf.sprintf"(%s,%d,%d)"contextii'letstring_of_polarityp=matchpwithPos->"+"|Neg->"-"|Sub->"M"(** Convert a label to a string, short representation *)letstring_of_label(l:label):string="\""^(findl).l_name^"\""(** Return true if the element [e] is present in the association list,
according to uref equality *)letrecassoc_list_mem(e:tau)(l:associationlist)=matchlwith|[]->None|(h,s,so)::t->ifU.equal(h,e)thenSome(s,so)elseassoc_list_memet(** Given a tau, create a unique recursive variable name. This should always
return the same name for a given tau *)letfresh_recvar_name(t:tau):string=matchfindtwithPairp->"rvp"^string_of_intp.p_stamp|Refr->"rvr"^string_of_intr.r_stamp|Funf->"rvf"^string_of_intf.f_stamp|_->die"fresh_recvar_name"(** Return a string representation of a tau, using association lists. *)letstring_of_tau(t:tau):string=lettau_map:associationlistref=ref[]inletrecstring_of_tau't=matchassoc_list_memt!tau_mapwithSome(s,so)->(* recursive type. see if a var name has been set *)beginmatch!sowithNone->letrv=fresh_recvar_nametins:="u "^rv^"."^!s;so:=Somerv;rv|Somerv->rvend|None->(* type's not recursive. Add it to the assoc list and cont. *)lets=ref""andso:stringoptionref=refNoneintau_map:=(t,s,so)::!tau_map;beginmatchfindtwithVarv->s:=v.v_name;|Pairp->assert(ref_or_varp.ptr);assert(fun_or_varp.lam);s:="{";s:=!s^string_of_tau'p.ptr;s:=!s^",";s:=!s^string_of_tau'p.lam;s:=!s^"}"|Refr->assert(pair_or_varr.points_to);s:="ref(|";s:=!s^string_of_labelr.rl;s:=!s^"|,";s:=!s^string_of_tau'r.points_to;s:=!s^")"|Funf->assert(pair_or_varf.ret);letrecstring_of_args=functionh::[]->assert(pair_or_varh);s:=!s^string_of_tau'h|h::t->assert(pair_or_varh);s:=!s^string_of_tau'h^",";string_of_argst|[]->()ins:="fun(|";s:=!s^string_of_labelf.fl;s:=!s^"|,";s:=!s^"<";ifList.lengthf.args>0thenstring_of_argsf.argselses:=!s^"void";s:=!s^">,";s:=!s^string_of_tau'f.ret;s:=!s^")"end;tau_map:=List.tl!tau_map;!sinstring_of_tau't(** Convert an lvalue to a string *)letstring_of_lvalue(lv:lvalue):string=letcontents=string_of_taulv.contentsandl=string_of_labellv.linassert(pair_or_varlv.contents);(* do a consistency check *)Printf.sprintf"[%s]^(%s)"contentslletprint_path(p:lblinfopath):unit=letstring_of_pkind=functionPositive->"p"|Negative->"n"|Match->"m"|Seed->"s"inPrintf.printf"%s --%s--> %s (%d) : "(string_of_labelp.head)(string_of_pkindp.kind)(string_of_labelp.tail)(PathHash.hashp)letprint_constraint(c:tconstraint)=matchcwithUnification(t,t')->letlhs=string_of_tautandrhs=string_of_taut'inPrintf.printf"%s == %s\n"lhsrhs|Leq(t,(i,p),t')->letlhs=string_of_tautandrhs=string_of_taut'inPrintf.printf"%s <={%d,%s} %s\n"lhsi(string_of_polarityp)rhs(***********************************************************************)(* *)(* Type Operations -- these do not create any constraints *)(* *)(***********************************************************************)(** Create an lvalue with label [lbl] and tau contents [t]. *)letmake_lval(lbl,t:label*tau):lvalue={l=lbl;contents=t}letmake_label_int(is_global:bool)(name:string)(vio:Cil.varinfooption):label=letlocc=matchviowithSomevi->C.add(fresh_index(),name,vi)C.empty|None->C.emptyinU.uref{l_name=name;l_global=is_global;l_stamp=fresh_stamp();loc=locc;aliases=locc;p_ubounds=B.empty;p_lbounds=B.empty;n_ubounds=B.empty;n_lbounds=B.empty;m_ubounds=B.empty;m_lbounds=B.empty;m_upath=P.empty;m_lpath=P.empty;n_upath=P.empty;n_lpath=P.empty;p_upath=P.empty;p_lpath=P.empty;l_seeded=false;l_ret=false;l_param=false}(** Create a new label with name [name]. Also adds a fresh constant
with name [name] to this label's aliases set. *)letmake_label(is_global:bool)(name:string)(vio:Cil.varinfooption):label=make_label_intis_globalnamevio(** Create a new label with an unspecified name and an empty alias set. *)letfresh_label(is_global:bool):label=letindex=fresh_index()inmake_label_intis_global("l_"^string_of_intindex)None(** Create a fresh bound (edge in the constraint graph). *)letmake_bound(i,a:int*label):lblinfobound={index=i;info=a}letmake_tau_bound(i,a:int*tau):tinfobound={index=i;info=a}(** Create a fresh named variable with name '[name]. *)letmake_var(b:bool)(name:string):tau=U.uref(Var{v_name=("'"^name);v_hole=H.create8;v_stamp=fresh_index();v_global=b;v_mlbs=B.empty;v_mubs=B.empty;v_plbs=B.empty;v_pubs=B.empty;v_nlbs=B.empty;v_nubs=B.empty})(** Create a fresh unnamed variable (name will be 'fv). *)letfresh_var(is_global:bool):tau=make_varis_global("fv"^string_of_int(fresh_index()))(** Create a fresh unnamed variable (name will be 'fi). *)letfresh_var_i(is_global:bool):tau=make_varis_global("fi"^string_of_int(fresh_index()))(** Create a Fun constructor. *)letmake_fun(lbl,a,r:label*(taulist)*tau):tau=U.uref(Fun{fl=lbl;f_stamp=fresh_index();f_global=false;args=a;ret=r})(** Create a Ref constructor. *)letmake_ref(lbl,pt:label*tau):tau=U.uref(Ref{rl=lbl;r_stamp=fresh_index();r_global=false;points_to=pt})(** Create a Pair constructor. *)letmake_pair(p,f:tau*tau):tau=U.uref(Pair{ptr=p;p_stamp=fresh_index();p_global=false;lam=f})(** Copy the toplevel constructor of [t], putting fresh variables in each
argument of the constructor. *)letcopy_toplevel(t:tau):tau=matchfindtwithPair_->make_pair(fresh_var_ifalse,fresh_var_ifalse)|Ref_->make_ref(fresh_labelfalse,fresh_var_ifalse)|Funf->letfresh_fn=fun_->fresh_var_ifalseinmake_fun(fresh_labelfalse,Util.list_mapfresh_fnf.args,fresh_var_ifalse)|_->die"copy_toplevel"letpad_args2(fi,tlr:finfo*taulistref):unit=letpadding=ref(List.lengthfi.args-List.length!tlr)inif!padding==0then()elseif!padding>0thenfor_=1to!paddingdotlr:=!tlr@[fresh_varfalse]doneelsebeginpadding:=-(!padding);for_=1to!paddingdofi.args<-fi.args@[fresh_varfalse]doneend(***********************************************************************)(* *)(* Constraint Generation/ Resolution *)(* *)(***********************************************************************)(** Make the type a global type *)letset_global(t:tau)(b:bool):unit=letset_global_downt=matchfindtwithVarv->v.v_global<-true|Refr->set_global_labelr.rltrue|Funf->set_global_labelf.fltrue|_->()inif!debug&&bthenPrintf.printf"Set global: %s\n"(string_of_taut);assert((not(get_globalt))||b);ifbtheniter_tauset_global_downt;matchfindtwithVarv->v.v_global<-b|Refr->r.r_global<-b|Pairp->p.p_global<-b|Funf->f.f_global<-bletrecunify_int(t,t':tau*tau):unit=ifequal_tautt'then()elseletti,ti'=findt,findt'inU.unifycombine(t,t');matchti,ti'withVarv,Varv'->set_globalt'(v.v_global||get_globalt');merge_vholes(v,v');merge_vlbs(v,v');merge_vubs(v,v')|Varv,_->set_globalt'(v.v_global||get_globalt');trigger_vholevt';notify_vlbstv;notify_vubstv|_,Varv->set_globalt(v.v_global||get_globalt);trigger_vholevt;notify_vlbst'v;notify_vubst'v|Refr,Refr'->set_globalt(r.r_global||r'.r_global);unify_ref(r,r')|Funf,Funf'->set_globalt(f.f_global||f'.f_global);unify_fun(f,f')|Pairp,Pairp'->()|_->raiseInconsistentandnotify_vlbs(t:tau)(vi:vinfo):unit=letnotifypbounds=List.iter(funb->add_constraint(Unification(b.info,copy_toplevelt));add_constraint(Leq(b.info,(b.index,p),t)))boundsinnotifySub(B.elementsvi.v_mlbs);notifyPos(B.elementsvi.v_plbs);notifyNeg(B.elementsvi.v_nlbs)andnotify_vubs(t:tau)(vi:vinfo):unit=letnotifypbounds=List.iter(funb->add_constraint(Unification(b.info,copy_toplevelt));add_constraint(Leq(t,(b.index,p),b.info)))boundsinnotifySub(B.elementsvi.v_mubs);notifyPos(B.elementsvi.v_pubs);notifyNeg(B.elementsvi.v_nubs)andunify_ref(ri,ri':rinfo*rinfo):unit=add_constraint(Unification(ri.points_to,ri'.points_to))andunify_fun(fi,fi':finfo*finfo):unit=letrecunion_args=function_,[]->false|[],_->true|h::t,h'::t'->add_constraint(Unification(h,h'));union_args(t,t')inunify_label(fi.fl,fi'.fl);add_constraint(Unification(fi.ret,fi'.ret));ifunion_args(fi.args,fi'.args)thenfi.args<-fi'.args;andunify_label(l,l':label*label):unit=letpick_name(li,li':lblinfo*lblinfo)=ifString.lengthli.l_name>1&&String.sub(li.l_name)02="l_"thenli.l_name<-li'.l_nameelse()inletcombine_label(li,li':lblinfo*lblinfo):lblinfo=letrm_selfb=not(li.l_stamp=get_label_stampb.info)inpick_name(li,li');li.l_global<-li.l_global||li'.l_global;li.aliases<-C.unionli.aliasesli'.aliases;li.p_ubounds<-B.unionli.p_uboundsli'.p_ubounds;li.p_lbounds<-B.unionli.p_lboundsli'.p_lbounds;li.n_ubounds<-B.unionli.n_uboundsli'.n_ubounds;li.n_lbounds<-B.unionli.n_lboundsli'.n_lbounds;li.m_ubounds<-B.unionli.m_ubounds(B.filterrm_selfli'.m_ubounds);li.m_lbounds<-B.unionli.m_lbounds(B.filterrm_selfli'.m_lbounds);li.m_upath<-P.unionli.m_upathli'.m_upath;li.m_lpath<-P.unionli.m_lpathli'.m_lpath;li.n_upath<-P.unionli.n_upathli'.n_upath;li.n_lpath<-P.unionli.n_lpathli'.n_lpath;li.p_upath<-P.unionli.p_upathli'.p_upath;li.p_lpath<-P.unionli.p_lpathli'.p_lpath;li.l_seeded<-li.l_seeded||li'.l_seeded;li.l_ret<-li.l_ret||li'.l_ret;li.l_param<-li.l_param||li'.l_param;liinif!debug_constraintsthenPrintf.printf"%s == %s\n"(string_of_labell)(string_of_labell');U.unifycombine_label(l,l')andmerge_vholes(vi,vi':vinfo*vinfo):unit=H.iter(funi->fun_->H.replacevi'.v_holei())vi.v_holeandmerge_vlbs(vi,vi':vinfo*vinfo):unit=vi'.v_mlbs<-B.unionvi.v_mlbsvi'.v_mlbs;vi'.v_plbs<-B.unionvi.v_plbsvi'.v_plbs;vi'.v_nlbs<-B.unionvi.v_nlbsvi'.v_nlbsandmerge_vubs(vi,vi':vinfo*vinfo):unit=vi'.v_mubs<-B.unionvi.v_mubsvi'.v_mubs;vi'.v_pubs<-B.unionvi.v_pubsvi'.v_pubs;vi'.v_nubs<-B.unionvi.v_nubsvi'.v_nubsandtrigger_vhole(vi:vinfo)(t:tau)=letadd_self_loops(t:tau):unit=matchfindtwithVarv->H.iter(funi->fun_->H.replacev.v_holei())vi.v_hole|Refr->H.iter(funi->fun_->leq_label(r.rl,(i,Pos),r.rl);leq_label(r.rl,(i,Neg),r.rl))vi.v_hole|Funf->H.iter(funi->fun_->leq_label(f.fl,(i,Pos),f.fl);leq_label(f.fl,(i,Neg),f.fl))vi.v_hole|_->()initer_tauadd_self_loopst(** Pick the representative info for two tinfo's. This function prefers the
first argument when both arguments are the same structure, but when
one type is a structure and the other is a var, it picks the structure.
All other actions (e.g., updating the info) is done in unify_int *)andcombine(ti,ti':tinfo*tinfo):tinfo=matchti,ti'withVar_,_->ti'|_,_->tiandleq_int(t,(i,p),t'):unit=ifequal_tautt'then()elseletti,ti'=findt,findt'inmatchti,ti'withVarv,Varv'->beginmatchpwithPos->v.v_pubs<-B.add(make_tau_bound(i,t'))v.v_pubs;v'.v_plbs<-B.add(make_tau_bound(i,t))v'.v_plbs|Neg->v.v_nubs<-B.add(make_tau_bound(i,t'))v.v_nubs;v'.v_nlbs<-B.add(make_tau_bound(i,t))v'.v_nlbs|Sub->v.v_mubs<-B.add(make_tau_bound(i,t'))v.v_mubs;v'.v_mlbs<-B.add(make_tau_bound(i,t))v'.v_mlbsend|Varv,_->add_constraint(Unification(t,copy_toplevelt'));add_constraint(Leq(t,(i,p),t'))|_,Varv->add_constraint(Unification(t',copy_toplevelt));add_constraint(Leq(t,(i,p),t'))|Refr,Refr'->leq_ref(r,(i,p),r')|Funf,Funf'->add_constraint(Unification(t,t'))|Pairpr,Pairpr'->add_constraint(Leq(pr.ptr,(i,p),pr'.ptr));add_constraint(Leq(pr.lam,(i,p),pr'.lam))|_->raiseInconsistentandleq_ref(ri,(i,p),ri'):unit=letadd_self_loops(t:tau):unit=matchfindtwithVarv->H.replacev.v_holei()|Refr->leq_label(r.rl,(i,Pos),r.rl);leq_label(r.rl,(i,Neg),r.rl)|Funf->leq_label(f.fl,(i,Pos),f.fl);leq_label(f.fl,(i,Neg),f.fl)|_->()initer_tauadd_self_loopsri.points_to;add_constraint(Unification(ri.points_to,ri'.points_to));leq_label(ri.rl,(i,p),ri'.rl)andleq_label(l,(i,p),l'):unit=if!debug_constraintsthenPrintf.printf"%s <={%d,%s} %s\n"(string_of_labell)i(string_of_polarityp)(string_of_labell');letli,li'=findl,findl'inmatchpwithPos->li.l_ret<-true;li.p_ubounds<-B.add(make_bound(i,l'))li.p_ubounds;li'.p_lbounds<-B.add(make_bound(i,l))li'.p_lbounds|Neg->li'.l_param<-true;li.n_ubounds<-B.add(make_bound(i,l'))li.n_ubounds;li'.n_lbounds<-B.add(make_bound(i,l))li'.n_lbounds|Sub->ifU.equal(l,l')then()elsebeginli.m_ubounds<-B.add(make_bound(0,l'))li.m_ubounds;li'.m_lbounds<-B.add(make_bound(0,l))li'.m_lboundsendandadd_constraint_int(c:tconstraint)(toplev:bool)=if!debug_constraints&&toplevthenbeginPrintf.printf"%d:>"!toplev_count;print_constraintc;incrtoplev_countendelseif!debug_constraintsthenprint_constraintcelse();beginmatchcwithUnification_->Q.addceq_worklist|Leq_->Q.addcleq_worklistend;solve_constraints()andadd_constraint(c:tconstraint)=add_constraint_intcfalseandadd_toplev_constraint(c:tconstraint)=if!print_constraints&¬!debug_constraintsthenbeginPrintf.printf"%d:>"!toplev_count;incrtoplev_count;print_constraintcendelse();add_constraint_intctrueandfetch_constraint():tconstraintoption=trySome(Q.takeeq_worklist)withQ.Empty->(trySome(Q.takeleq_worklist)withQ.Empty->None)(** The main solver loop. *)andsolve_constraints():unit=matchfetch_constraint()withSomec->beginmatchcwithUnification(t,t')->unify_int(t,t')|Leq(t,(i,p),t')->if!no_subthenunify_int(t,t')elseif!analyze_monothenleq_int(t,(0,Sub),t')elseleq_int(t,(i,p),t')end;solve_constraints()|None->()(***********************************************************************)(* *)(* Interface Functions *)(* *)(***********************************************************************)(** Return the contents of the lvalue. *)letrvalue(lv:lvalue):tau=lv.contents(** Dereference the rvalue. If it does not have enough structure to support
the operation, then the correct structure is added via new unification
constraints. *)letrecderef(t:tau):lvalue=matchU.dereftwithPairp->beginmatchU.derefp.ptrwithVar_->letis_global=global_taup.ptrinletpoints_to=fresh_varis_globalinletl=fresh_labelis_globalinletr=make_ref(l,points_to)inadd_toplev_constraint(Unification(p.ptr,r));make_lval(l,points_to)|Refr->make_lval(r.rl,r.points_to)|_->raiseWellFormedend|Varv->letis_global=global_tautinadd_toplev_constraint(Unification(t,make_pair(fresh_varis_global,fresh_varis_global)));dereft|_->raiseWellFormed(** Form the union of [t] and [t'], if it doesn't exist already. *)letjoin(t:tau)(t':tau):tau=tryH.findjoin_cache(get_stampt,get_stampt')withNot_found->lett''=fresh_varfalseinadd_toplev_constraint(Leq(t,(0,Sub),t''));add_toplev_constraint(Leq(t',(0,Sub),t''));H.addjoin_cache(get_stampt,get_stampt')t'';t''(** Form the union of a list [tl], expected to be the initializers of some
structure or array type. *)letjoin_inits(tl:taulist):tau=lett'=fresh_varfalseinList.iter(funt->add_toplev_constraint(Leq(t,(0,Sub),t')))tl;t'(** Take the address of an lvalue. Does not add constraints. *)letaddress(lv:lvalue):tau=make_pair(make_ref(lv.l,lv.contents),fresh_varfalse)(** For this version of golf, instantiation is handled at [apply] *)letinstantiate(lv:lvalue)(i:int):lvalue=lv(** Constraint generated from assigning [t] to [lv]. *)letassign(lv:lvalue)(t:tau):unit=add_toplev_constraint(Leq(t,(0,Sub),lv.contents))letassign_ret(i:int)(lv:lvalue)(t:tau):unit=add_toplev_constraint(Leq(t,(i,Pos),lv.contents))(** Project out the first (ref) component or a pair. If the argument [t] has
no discovered structure, raise NoContents. *)letproj_ref(t:tau):tau=matchU.dereftwithPairp->p.ptr|Varv->raiseNoContents|_->raiseWellFormed(* Project out the second (fun) component of a pair. If the argument [t] has
no discovered structure, create it on the fly by adding constraints. *)letproj_fun(t:tau):tau=matchU.dereftwithPairp->p.lam|Varv->letp,f=fresh_varfalse,fresh_varfalseinadd_toplev_constraint(Unification(t,make_pair(p,f)));f|_->raiseWellFormedletget_finfo(t:tau):finfo=matchU.dereftwithFunf->f|_->raiseWellFormed(** Function type [t] is applied to the arguments [actuals]. Unifies the
actuals with the formals of [t]. If no functions have been discovered for
[t] yet, create a fresh one and unify it with t. The result is the return
value of the function plus the index of this application site. *)letapply(t:tau)(al:taulist):(tau*int)=leti=fresh_appsite()inletf=proj_funtinletactuals=refalinletfi,ret=matchU.dereffwithFunfi->fi,fi.ret|Varv->letnew_l,new_ret,new_args=fresh_labelfalse,fresh_varfalse,Util.list_map(function_->fresh_varfalse)!actualsinletnew_fun=make_fun(new_l,new_args,new_ret)inadd_toplev_constraint(Unification(new_fun,f));(get_finfonew_fun,new_ret)|_->raiseWellFormedinpad_args2(fi,actuals);List.iter2(funactual->funformal->add_toplev_constraint(Leq(actual,(i,Neg),formal)))!actualsfi.args;(ret,i)(** Create a new function type with name [name], list of formal arguments
[formals], and return value [ret]. Adds no constraints. *)letmake_function(name:string)(formals:lvaluelist)(ret:tau):tau=letf=make_fun(make_labelfalsenameNone,Util.list_map(funx->rvaluex)formals,ret)inmake_pair(fresh_varfalse,f)(** Create an lvalue. If [is_global] is true, the lvalue will be treated
monomorphically. *)letmake_lvalue(is_global:bool)(name:string)(vio:Cil.varinfooption):lvalue=if!debug&&is_globalthenPrintf.printf"Making global lvalue : %s\n"nameelse();make_lval(make_labelis_globalnamevio,make_varis_globalname)(** Create a fresh non-global named variable. *)letmake_fresh(name:string):tau=make_varfalsename(** The default type for constants. *)letbottom():tau=make_varfalse"bottom"(** Unify the result of a function with its return value. *)letreturn(t:tau)(t':tau)=add_toplev_constraint(Leq(t',(0,Sub),t))(***********************************************************************)(* *)(* Query/Extract Solutions *)(* *)(***********************************************************************)letmake_summary=leq_labelletpath_signaturekll'b:intlist=letksig=matchkwithPositive->1|Negative->2|_->3in[ksig;get_label_stampl;get_label_stampl';ifbthen1else0]letmake_path(k,l,l',b)=letpsig=path_signaturekll'binifPH.mempath_hashpsigthen()elseletnew_path={kind=k;head=l;tail=l';reached_global=b}andli,li'=findl,findl'inPH.addpath_hashpsignew_path;Q.addnew_pathpath_worklist;beginmatchkwithPositive->li.p_upath<-P.addnew_pathli.p_upath;li'.p_lpath<-P.addnew_pathli'.p_lpath|Negative->li.n_upath<-P.addnew_pathli.n_upath;li'.n_lpath<-P.addnew_pathli'.n_lpath|_->li.m_upath<-P.addnew_pathli.m_upath;li'.m_lpath<-P.addnew_pathli'.m_lpathend;if!debugthenbeginprint_string"Discovered path : ";print_pathnew_path;print_newline()endletbackwards_tabulate(l:label):unit=letrecloop()=letrule1p=if!debugthenprint_endline"rule1";B.iter(funlb->make_path(Match,lb.info,p.tail,p.reached_global||is_global_labelp.head))(findp.head).m_lboundsandrule2p=if!debugthenprint_endline"rule2";B.iter(funlb->make_path(Negative,lb.info,p.tail,p.reached_global||is_global_labelp.head))(findp.head).n_lboundsandrule2mp=if!debugthenprint_endline"rule2m";B.iter(funlb->make_path(Match,lb.info,p.tail,p.reached_global||is_global_labelp.head))(findp.head).n_lboundsandrule3p=if!debugthenprint_endline"rule3";B.iter(funlb->make_path(Positive,lb.info,p.tail,p.reached_global||is_global_labelp.head))(findp.head).p_lboundsandrule4p=if!debugthenprint_endline"rule4";B.iter(funlb->make_path(Negative,lb.info,p.tail,p.reached_global||is_global_labelp.head))(findp.head).m_lboundsandrule5p=if!debugthenprint_endline"rule5";B.iter(funlb->make_path(Positive,lb.info,p.tail,p.reached_global||is_global_labelp.head))(findp.head).m_lboundsandrule6p=if!debugthenprint_endline"rule6";B.iter(funlb->ifis_seeded_labellb.infothen()elsebegin(findlb.info).l_seeded<-true;(* set seeded *)make_path(Seed,lb.info,lb.info,is_global_labellb.info)end)(findp.head).p_lboundsandrule7p=if!debugthenprint_endline"rule7";ifnot(is_ret_labelp.tail&&is_param_labelp.head)then()elseB.iter(funlb->B.iter(funub->iflb.index=ub.indexthenbeginif!debugthenPrintf.printf"New summary : %s %s\n"(string_of_labellb.info)(string_of_labelub.info);make_summary(lb.info,(0,Sub),ub.info);(* rules 1, 4, and 5 *)P.iter(funubp->(* rule 1 *)make_path(Match,lb.info,ubp.tail,ubp.reached_global))(findub.info).m_upath;P.iter(funubp->(* rule 4 *)make_path(Negative,lb.info,ubp.tail,ubp.reached_global))(findub.info).n_upath;P.iter(funubp->(* rule 5 *)make_path(Positive,lb.info,ubp.tail,ubp.reached_global))(findub.info).p_upathend)(findp.tail).p_ubounds)(findp.head).n_lboundsinletmatched_backward_rulesp=rule1p;ifp.reached_globalthenrule2mpelserule2p;rule3p;rule6p;rule7pandnegative_backward_rulesp=rule2p;rule3p;rule4p;rule6p;rule7pandpositive_backward_rulesp=rule3p;rule5p;rule6p;rule7pin(* loop *)ifQ.is_emptypath_worklistthen()elseletp=Q.takepath_worklistinif!debugthenbeginprint_string"Processing path: ";print_pathp;print_newline()end;beginmatchp.kindwithPositive->ifis_global_labelp.tailthenmatched_backward_rulespelsepositive_backward_rulesp|Negative->negative_backward_rulesp|_->matched_backward_rulespend;loop()in(* backwards_tabulate *)if!debugthenbeginPrintf.printf"Tabulating for %s..."(string_of_labell);ifis_global_labellthenprint_string"(global)";print_newline()end;make_path(Seed,l,l,is_global_labell);loop()letcollect_ptsets(l:label):constantset=(* todo -- cache aliases *)letli=findlandcollectinits=P.fold(funxa->C.uniona(findx.head).aliases)sinitinbackwards_tabulatel;collect(collect(collectli.aliasesli.m_lpath)li.n_lpath)li.p_lpathletextract_ptlabel(lv:lvalue):labeloption=trymatchfind(proj_reflv.contents)withVarv->None|Refr->Somer.rl;|_->raiseWellFormedwithNoContents->Noneletpoints_to_aux(t:tau):constantlist=trymatchfind(proj_reft)withVarv->[]|Refr->C.elements(collect_ptsetsr.rl)|_->raiseWellFormedwithNoContents->[]letpoints_to_names(lv:lvalue):stringlist=Util.list_map(fun(_,str,_)->str)(points_to_auxlv.contents)letpoints_to(lv:lvalue):Cil.varinfolist=letrecget_vinfosl:Cil.varinfolist=matchlwith|(_,_,h)::t->h::get_vinfost|[]->[]inget_vinfos(points_to_auxlv.contents)letepoints_to(t:tau):Cil.varinfolist=letrecget_vinfosl:Cil.varinfolist=matchlwith|(_,_,h)::t->h::get_vinfost|[]->[]inget_vinfos(points_to_auxt)letsmart_alias_query(l:label)(l':label):bool=(* Set of dead configurations *)letdead_configs:config_map=CH.create16in(* the set of discovered configurations *)letdiscovered:config_map=CH.create16inletfilter_match(i:int)=B.filter(fun(b:lblinfobound)->i=b.index)inletrecsimulatecll'=letconfig=(c,get_label_stampl,get_label_stampl')inifU.equal(l,l')thenbeginif!debugthenPrintf.printf"%s and %s are aliased\n"(string_of_labell)(string_of_labell');raiseAPFoundendelseifCH.memdiscoveredconfigthen()elsebeginif!debug_aliasesthenPrintf.printf"Exploring configuration %s\n"(string_of_configurationconfig);CH.adddiscoveredconfig();B.iter(funlb->simulateclb.infol')(get_boundsSubfalsel);(* epsilon closure of l *)B.iter(funlb->simulatecllb.info)(get_boundsSubfalsel');(* epsilon closure of l' *)B.iter(funlb->letmatching=filter_matchlb.index(get_boundsPosfalsel')inB.iter(funb->simulateClosedlb.infob.info)matching;ifis_global_labell'then(* positive self-loops on l' *)simulateClosedlb.infol')(get_boundsPosfalsel);(* positive transitions on l *)ifis_global_labellthenB.iter(funlb->simulateClosedllb.info)(get_boundsPosfalsel');(* positive self-loops on l *)beginmatchcwith(* negative transitions on l, only if Open *)Open->B.iter(funlb->letmatching=filter_matchlb.index(get_boundsNegfalsel')inB.iter(funb->simulateOpenlb.infob.info)matching;ifis_global_labell'then(* neg self-loops on l' *)simulateOpenlb.infol')(get_boundsNegfalsel);ifis_global_labellthenB.iter(funlb->simulateOpenllb.info)(get_boundsNegfalsel')(* negative self-loops on l *)|_->()end;(* if we got this far, then the configuration was not used *)CH.adddead_configsconfig();endintrybeginifH.memcached_aliases(get_label_stampl,get_label_stampl')thentrueelsebeginsimulateOpenll';if!debugthenPrintf.printf"%s and %s are NOT aliased\n"(string_of_labell)(string_of_labell');falseendendwithAPFound->CH.iter(funconfig->fun_->ifnot(CH.memdead_configsconfig)thenH.addcached_aliases(get_label_stampl,get_label_stampl')())discovered;true(** todo : uses naive alias query for now *)letmay_alias(t1:tau)(t2:tau):bool=tryletl1=matchfind(proj_reft1)withRefr->r.rl|Varv->raiseNoContents|_->raiseWellFormedandl2=matchfind(proj_reft2)withRefr->r.rl|Varv->raiseNoContents|_->raiseWellFormedinnot(C.is_empty(C.inter(collect_ptsetsl1)(collect_ptsetsl2)))withNoContents->falseletalias_query(b:bool)(lvl:lvaluelist):int*int=letnaive_count=ref0inletsmart_count=ref0inletlbls=Util.list_mapextract_ptlabellvlin(* label option list *)letptsets=Util.list_map(functionSomel->collect_ptsetsl|None->C.empty)lblsinletrecord_aliasslos'lo'=matchlo,lo'withSomel,Somel'->if!debug_aliasesthenPrintf.printf"Checking whether %s and %s are aliased...\n"(string_of_labell)(string_of_labell');ifC.is_empty(C.interss')then()elsebeginincrnaive_count;if!smart_aliases&&smart_alias_queryll'thenincrsmart_countend|_->()inletreccheck_aliassetslabels=matchsets,labelswiths::st,l::lt->List.iter2(record_aliassl)ptsetslbls;check_aliasstlt|[],[]->()|_->die"check_alias"incheck_aliasptsetslbls;(!naive_count,!smart_count)letalias_frequency(lvl:(lvalue*bool)list):int*int=letextract_lbl(lv,b:lvalue*bool)=(lv.l,b)inletnaive_count=ref0inletsmart_count=ref0inletlbls=Util.list_mapextract_lbllvlinletptsets=Util.list_map(fun(lbl,b)->ifbthen(findlbl).loc(* symbol access *)elsecollect_ptsetslbl)lblsinletrecord_aliass(l,b)s'(l',b')=if!debug_aliasesthenPrintf.printf"Checking whether %s and %s are aliased...\n"(string_of_labell)(string_of_labell');ifC.is_empty(C.interss')then()elsebeginif!debug_aliasesthenPrintf.printf"%s and %s are aliased naively...\n"(string_of_labell)(string_of_labell');incrnaive_count;if!smart_aliasesthenifb||b'||smart_alias_queryll'thenincrsmart_countelsePrintf.printf"%s and %s are not aliased by smart queries...\n"(string_of_labell)(string_of_labell');endinletreccheck_aliassetslabels=matchsets,labelswiths::st,l::lt->List.iter2(record_aliassl)ptsetslbls;check_aliasstlt|[],[]->()|_->die"check_alias"incheck_aliasptsetslbls;(!naive_count,!smart_count)(** an interface for extracting abstract locations from this analysis *)typeabsloc=labelletabsloc_of_lvalue(l:lvalue):absloc=l.lletabsloc_eq(a1,a2)=smart_alias_querya1a2letabsloc_print_name=reftrueletd_absloc()(p:absloc)=leta=findpinif!absloc_print_namethenPretty.dprintf"%s"a.l_nameelsePretty.dprintf"%d"a.l_stampletphonyAddrOf(lv:lvalue):lvalue=make_lval(fresh_labeltrue,addresslv)(* transitive closure of points to, starting from l *)letrectauPointsTo(l:tau):absloclist=matchfindlwithVar_->[]|Refr->r.rl::tauPointsTor.points_to|_->[]letabsloc_points_to(l:lvalue):absloclist=tauPointsTol.contents(** The following definitions are only introduced for the
compatability with Olf. *)exceptionUnknownLocationletfinished_constraints()=()letapply_undefined(_:taulist)=(fresh_vartrue,0)letassign_undefined(_:lvalue)=()letabsloc_epoints_to=tauPointsTo