123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755(**************************************************************************)(* *)(* This file is part of Frama-C. *)(* *)(* Copyright (C) 2007-2023 *)(* 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). *)(* *)(**************************************************************************)openCil_typesopenVa_typesopenOptionsmoduleList=Extends.ListmoduleTyp=Extends.Typletpp_prototypenamefmttparams=Format.fprintffmt"%s(%a)"name(Pretty_utils.pp_list~sep:", "Printer.pp_typ)tparamsletpp_overloadnamefmtl=letprototypes=List.mapfstlinPretty_utils.pp_list~sep:"@\n"(pp_prototypename)fmtprototypes(* State to store the number of fallback functions generated for a particular
function name. This number is used to generate a unique function name. *)moduleFallback_counts=State_builder.Hashtbl(Datatype.String.Hashtbl)(Datatype.Int)(structletsize=17letname="fallback_counts"letdependencies=[Options.Enabled.self;Options.Strict.self]end)(* ************************************************************************ *)(* Call translation *)(* ************************************************************************ *)exceptionTranslate_call_exnofvarinfo(* Extended integer types (e.g. int8_t, uint_least16_t, int_fast32_t)
do not have their own character modifiers, but instead use macros that are
converted into "standard" modifiers (e.g. "%hhd", "%hu", "%d", etc.).
Therefore, we cannot enforce their types the same way as for e.g. size_t,
which has its own modifier. We weaken the check, allowing a different name
but still requiring same size and signedness. *)letextended_integer_typenames=["int8_t";"uint8_t";"int_least8_t";"uint_least8_t";"int_fast8_t";"uint_fast8_t";"int16_t";"uint16_t";"int_least16_t";"uint_least16_t";"int_fast16_t";"uint_fast16_t";"int32_t";"uint32_t";"int_least32_t";"uint_least32_t";"int_fast32_t";"uint_fast32_t";"int64_t";"uint64_t";"int_least64_t";"uint_least64_t";"int_fast64_t";"uint_fast64_t"]letis_extended_integer_typet=matchtwith|TNamed(ti,_)->List.memti.tnameextended_integer_typenames|_->falseletintegral_repikind=Cil.bitsSizeOfIntikind,Cil.isSignedikindletexposet=Cil.type_remove_attributes_for_c_cast(Cil.unrollTypet)(* From most permissive to least permissive *)typecastability=Strict(* strictly allowed by the C standard *)|Tolerated(* tolerated in practice *)|NonPortable(* non-portable minor deviation *)|NonStrict(* only allowed in non-strict mode *)|Never(* never allowed *)letcan_castgivenexpected=matchexposegiven,exposeexpectedwith|t1,t2whenCil_datatype.Typ.equalt1t2->Strict|(TInt(i1,a1)|TEnum({ekind=i1},a1)),(TInt(i2,a2)|TEnum({ekind=i2},a2))->ifintegral_repi1<>integral_repi2||not(Cil_datatype.Attributes.equala1a2)thenNeverelseifis_extended_integer_typegiventhenToleratedelseifi1=i2thenNonPortableelseNonStrict|TPtr_,TPtr_->Strict|_,_->Neverletdoes_fitexptyp=matchCil.constFoldToIntexp,Cil.unrollTypetypwith|Somei,(TInt(ekind,_)|TEnum({ekind},_))->Cil.fitsInIntekindi|_->false(* Variant of [pp_typ] which details the underlying type for enums *)letpretty_typfmtt=matchCil.unrollTypetwith|TEnum(ei,_)->Format.fprintffmt"%a (%a)"Printer.pp_typtPrinter.pp_typ(TInt(ei.ekind,[]))|_->Printer.pp_typfmtt(* cast the i-th argument exp to paramtyp *)letcast_argiparamtypexp=letargtyp=Cil.typeOfexpinifnot(does_fitexpparamtyp)thenbeginmatchcan_castargtypparamtypwith|Strict|Tolerated->()|(NonPortable|NonStrict)whennot(Strict.get())->()|NonPortable->Self.warning~current:true~wkey:wkey_typing"Possible portability issues with enum type for argument %d \
(use -variadic-no-strict to avoid this warning)."(i+1)|NonStrict|Never->Self.warning~current:true~wkey:wkey_typing"Incorrect type for argument %d. \
The argument will be cast from %a to %a."(i+1)pretty_typargtyppretty_typparamtypend;Cil.mkCast~check:false~force:false~newt:paramtypexp(* cast a list of args to the tparams list of types and remove unused args *)letmatch_args~calleetparamsargs=(* Remove unused arguments *)letparamcount=List.lengthtparamsandargcount=List.lengthargsinifargcount>paramcountthenSelf.warning~current:true~wkey:wkey_typing"Too many arguments: expected %d, given %d. \
Superfluous arguments will be removed."paramcountargcountelseifargcount<paramcountthen(Self.warning~current:true~wkey:wkey_typing"Not enough arguments: expected %d, given %d."paramcountargcount;raise(Translate_call_exncallee));(* Translate params *)letnew_args,unused_args=List.breakparamcountargsinList.mapi2cast_argtparamsnew_args,unused_args(* translate a call by applying argument matching/pruning and changing
callee *)letmatch_call~buildernew_calleenew_tparamsargs=letmoduleBuild=(valbuilder:Builder.S)inletnew_args,unused_args=match_args~callee:new_calleenew_tparamsargsinBuild.start_translation();Build.(List.iterpure(of_exp_listunused_args));Build.(translated_call(varnew_callee)(of_exp_listnew_args))(* ************************************************************************ *)(* Fallback calls *)(* ************************************************************************ *)letfallback_fun_call~builder~calleevfargs=letmoduleBuild=(valbuilder:Builder.S)in(* Choose function name *)letname=callee.vnameinletvorig_name=callee.vorig_nameinletcount=tryFallback_counts.findnamewithNot_found->0inletcount=count+1inFallback_counts.replacenamecount;letnew_name=name^"_fallback_"^(string_of_intcount)in(* Start building the function *)letloc=vf.vf_decl.vdeclinletnew_callee=Build.open_function~loc~vorig_namenew_namein(* Set function return type and attributes *)letret_typ,params,_,attrs=Cil.splitFunctionTypevf.vf_original_typeinBuild.set_return_type'ret_typ;List.iterBuild.add_attributeattrs;Build.add_stdlib_generated();(* Add parameters *)letfixed_params_count=Typ.params_countvf.vf_original_typeinletadd_static_param(name,typ,attributes)=ignore(Build.parameter~attributestypname)andadd_variadic_paramie=lettyp=Cil.typeOfeinignore(Build.parametertyp("param"^string_of_inti))inList.iteradd_static_param(Option.getparams);List.iteriadd_variadic_param(List.dropfixed_params_countargs);(* Build the default behaviour *)Build.finish_declaration();(* Store the translation *)Replacements.add(Build.cil_varinfonew_callee)vf.vf_decl;(* Translate the call *)Build.start_translation();Self.result~current:true~level:2"Fallback translation of call %s to a call to the specialized version %a."vf.vf_decl.vorig_nameBuild.prettynew_callee;Build.(translated_callnew_callee(List.mapof_expargs))(* ************************************************************************ *)(* Aggregator calls *)(* ************************************************************************ *)letfind_nullexp_list=List.ifind(fune->Cil.isZero(Cil.constFoldfalsee))exp_listletaggregator_call~builderaggregatorvfargs=letmoduleBuild=(valbuilder:Builder.S)inlet{a_target;a_pos;a_type;a_param}=aggregatorinletname=vf.vf_decl.vorig_nameandtparams=Typ.params_typesa_target.vtypeandpname,ptyp=a_paramin(* Check argument count *)letargcount=List.lengthargsandparamcount=List.lengthtparamsinifargcount<paramcountthenbeginSelf.warning~current:true~wkey:wkey_typing"Not enough arguments: expected %d, given %d."paramcountargcount;raise(Translate_call_exnvf.vf_decl);end;(* Compute the size of the aggregation *)letsize=matcha_typewith|EndedByNull->begintryfind_null(List.dropa_posargs)+1withNot_found->Self.warning~current:true"Failed to find a sentinel (NULL pointer) in the argument list.";raise(Translate_call_exnvf.vf_decl);endin(* Convert arguments *)lettparams_left=List.takea_postparamsinlettparams_right=List.drop(a_pos+1)tparamsinletnew_tparams=tparams_left@List.makesizeptyp@tparams_rightinletnew_args,unused_args=match_args~callee:vf.vf_declnew_tparamsargsin(* Split the arguments *)letargs_left,args_rem=List.breaka_posnew_argsinletargs_middle,args_right=List.breaksizeargs_remin(* Create the call code *)Self.result~current:true~level:2"Translating call to %s to a call to %s."namea_target.vorig_name;letpname=ifpname=""then"param"elsepnameinBuild.start_translation();letinit=matchargs_middlewith(* C standard forbids arrays of size 0 *)|[]->[Build.(of_init(Cil.makeZeroInit~locptyp))]|l->List.mapBuild.of_explinletsize=List.lengthinitinletvaggr=Build.(local(array~size(of_ctypptyp))pname~init)inletnew_args=args_left@[Build.(cil_exp~loc(addrvaggr))]@args_rightinletnew_args,_=match_args~callee:vf.vf_decltparamsnew_argsinBuild.(List.iterpure(of_exp_listunused_args));Build.(translated_call(vara_target)(of_exp_listnew_args))(* ************************************************************************ *)(* Overloads calls *)(* ************************************************************************ *)letreccheck_arg_matchingexpectedgiven=matchCil.unrollTypegiven,Cil.unrollTypeexpectedwith|(TInt_|TEnum_),(TInt_|TEnum_)->true|TPtr_,_whenCil.isVoidPtrTypeexpected->true|TPtr(t1,_),TPtr(t2,_)->check_arg_matchingt1t2|_,_->not(Cil.need_castgivenexpected)letreccheck_call_matchingtparamstargs=matchtparams,targswith|[],[]->true|[],_(* too many args: this is allowed by the standard (the extra arguments
are ignored), but in practice this leads to disambiguation issues in
some cases (e.g. last argument is 0 instead of NULL), so we prefer to
be strict *)(* Not enough input args *)|_,[]->false|a1::l1,a2::l2->check_arg_matchinga1a2&&check_call_matchingl1l2letfilter_matching_prototypesoverloadargs=(* Find suitable candidates for this call *)lettargs=List.mapCil.typeOfargsinletcheck(tparams,_vi)=check_call_matchingtparamstargsinList.filtercheckoverloadletoverloaded_call~builderoverloadvfargs=letname=vf.vf_decl.vorig_namein(* Find the matching prototype *)lettparams,new_callee=matchfilter_matching_prototypesoverloadargswith|[]->(* No matching prototype *)Self.warning~current:true~wkey:wkey_prototype"@[No matching prototype found for this call to %s.@.\
Expected candidates:@.\
@[<v> %a@]@.\
Given arguments:@.\
@[<v> %a@]"name(pp_overloadname)overload(pp_prototypename)(List.mapCil.typeOfargs);raise(Translate_call_exnvf.vf_decl);|[(tparams,vi)]->(* Exactly one matching prototype *)tparams,vi|l->(* Several matching prototypes *)Self.warning~current:true~wkey:wkey_prototype"Ambiguous call to %s. Matching candidates are: \
%a"name(pp_overloadname)l;raise(Translate_call_exnvf.vf_decl);in(* Store the translation *)Replacements.addnew_calleevf.vf_decl;(* Rebuild the call *)Self.result~current:true~level:2"Translating call to the specialized version %a."(pp_prototypename)tparams;match_call~buildernew_calleetparamsargs(* ************************************************************************ *)(* Format functions calls *)(* ************************************************************************ *)(* --- Specification building --- *)letrecstatic_stringa=matcha.enodewith|Const(CStrs)->Some(Format_string.Strings)|Const(CWStrs)->Some(Format_string.WStrings)|CastE(_,e)->static_stringe|_->Noneletfind_globalenvname=trySome(Environment.find_globalenvname)withNot_found->Self.warning~once:true~wkey:wkey_libc_framac"Unable to locate global %s which should be in the Frama-C LibC. \
Correct specifications can't be generated."name;Noneletfind_predicatename=matchLogic_env.find_all_logic_functionsnamewith|f::_q->f(* TODO: should we warn in case of overloading? *)|[]->Self.warning~once:true~wkey:wkey_libc_framac"Unable to locate ACSL predicate %s which should be in the Frama-C LibC. \
Correct specifications can't be generated."name;raiseNot_foundletfind_fieldenvstructnamefieldname=tryletcompinfo=Environment.find_structenvstructnameinCil.getCompFieldcompinfofieldnamewithNot_found->Self.warning~once:true"Unable to locate %s field %s."structnamefieldname;raiseNot_foundletfind_predicate_by_widthtypnarrow_namewide_name=matchCil.unrollTypeDeeptypwith|TPtr(TInt(IChar,_),_)->find_predicatenarrow_name|TPtr(t,_)when(* drop attributes to remove 'const' qualifiers and fc_stdlib attributes *)Cil_datatype.Typ.equal(Cil.typeDeepDropAllAttributes(Cil.unrollTypeDeept))Cil.theMachine.Cil.wcharType->find_predicatewide_name|_->Self.warning~current:true~wkey:wkey_typing"expected single/wide character pointer type, got %a (%a, unrolled %a)"Printer.pp_typtypCil_types_debug.pp_typtypCil_types_debug.pp_typ(Cil.unrollTypeDeeptyp);raiseNot_foundletvalid_read_stringtyp=find_predicate_by_widthtyp"valid_read_string""valid_read_wstring"letvalid_read_nstringtyp=find_predicate_by_widthtyp"valid_read_nstring""valid_read_nwstring"letformat_lengthtyp=find_predicate_by_widthtyp"format_length""wformat_length"letbuild_specialized_fun~builderenvvfformat_funtvparams=letopenFormat_typesinletmoduleBuild=(valbuilder:Builder.S)in(* Choose function name *)letname=vf.vf_decl.vorig_nameinvf.vf_specialization_count<-vf.vf_specialization_count+1;letnew_name=name^"_va_"^(string_of_intvf.vf_specialization_count)in(* Start building the function *)letloc=vf.vf_decl.vdeclinletfunvar=Build.open_function~loc~vorig_name:namenew_namein(* Set function return type and attributes *)letret_typ,params,_,attrs=Cil.splitFunctionTypevf.vf_original_typeinBuild.set_return_type'ret_typ;List.iterBuild.add_attributeattrs;Build.add_stdlib_generated();(* Add parameters *)letfresh_param_name=letcounter=ref0infun()->letp="param"^string_of_int!counterinincrcounter;pinletadd_static_param(name,typ,attributes)=(* create a new name for parameters which does not have one *)letname=ifname=""thenfresh_param_name()elsenameinBuild.parameter~attributestypnameandadd_variadic_param(typ,_dir)=lettyp=ifCil.isIntegralTypetypthenCil.integralPromotiontypelsetypinBuild.parametertyp(fresh_param_name())inletsformals=List.mapadd_static_param(Option.getparams)andvformals=List.mapadd_variadic_paramtvparamsin(* Spec *)letsources=ref[]anddests=ref[]inletadd_source?(indirect=false)s=lets=(s:>Build.source)insources:=(ifindirectthenBuild.indirectselses)::!sourcesandadd_destd=dests:=(d:>Build.exp)::!destsin(* Add the lval to the list of sources/dests *)letadd_lval~indirectlval=function|(`ArgIn|`ArgInArray_)->add_source~indirectlval|(`ArgOut|`ArgOutArray)->add_destlval|`ArgInOut->add_source~indirectlval;add_destlvalinletadd_var?pos~indirect(v:Build.var)dir=letty=Build.cil_typeofvin(* Use the appropriate logical lval *)letlval=matchdirwith|`ArgIn->(v:>Build.lval)|(`ArgInArray_|`ArgOutArray)->Build.(indexvwhole_right)|(`ArgOut|`ArgInOut)->Build.(memv)inadd_lval~indirectlvaldir;(* Build requires/ensures *)trymatchdirwith|`ArgInArrayNone->Build.(requires(app(valid_read_stringty)[here][v]))|`ArgInArray(Someprecision)->letnterm=matchprecisionwith|PStar->letsize_arg=List.nthvformals(Option.getpos-1)inBuild.(castintegersize_arg)|PIntn->Build.of_intninBuild.(requires(app(valid_read_nstringty)[here][(v:>Build.exp);nterm]))|`ArgOut->Build.(requires(validv));Build.(ensures(initializedv))|_->()withNot_found->()(* Predicate not found *)in(* Build variadic parameter source/dest list *)letl=List.combinevformalstvparamsinList.iteri(funpos(v,(_,dir))->add_var~indirect:false~posvdir)l;(* Add format source and additional parameters *)letfmt=List.nthsformalsformat_fun.f_format_posinadd_var~indirect:truefmt(`ArgInArrayNone);(* Add buffer source/dest *)letadd_streamv=(* assigns stream->__fc_FILE_data
\from stream->__fc_FILE_data, __fc_FILE_id *)tryletf_data=find_fieldenv"__fc_FILE""__fc_FILE_data"andf_id=find_fieldenv"__fc_FILE""__fc_FILE_id"inadd_lval~indirect:falseBuild.(fieldvf_data)`ArgInOut;add_lval~indirect:trueBuild.(fieldvf_id)`ArgInwithNot_found->add_var~indirect:falsev`ArgInOutin(* Add a bounded buffer *)letadd_bufferbuffersize=add_var~indirect:truesize`ArgIn;(* this is an snprintf-like function; compute and add its precondition:
\valid(s + (0..n-1)) || \valid(s + (0..format_length(format)-1)) *)letvalid_rangelength=Build.(valid(buffer+(zero--(length-one))))inletleft_pred=valid_rangesizeintryletflen=format_length(Build.cil_typeofbuffer)inletright_pred=Build.(valid_range(appflen[here][fmt]))inBuild.(requires(logorleft_predright_pred))with|Not_found->Build.requiresleft_pred|Build.NotAFunctionli->Self.abort~current:true"%a should be a logic function, not a predicate"Printer.pp_logic_varli.l_var_infoinbeginmatchformat_fun.f_buffer,format_fun.f_kindwith|StdIO,ScanfLike->beginmatchfind_globalenv"__fc_stdin"with|Somevi->add_stream(Build.varvi)|None->()end|StdIO,PrintfLike->beginmatchfind_globalenv"__fc_stdout"with|Somevi->add_stream(Build.varvi)|None->()end|Arg(i,_),ScanfLike->add_var~indirect:true(List.nthsformalsi)(`ArgInArrayNone)|Arg(i,size_pos),PrintfLike->add_var~indirect:true(List.nthsformalsi)(`ArgOutArray);beginmatchsize_poswith|Somen->add_buffer(List.nthsformalsi)(List.nthsformalsn)|None->()end|Streami,_->add_stream(List.nthsformalsi)|Filei,_->letfile=List.nthsformalsiinadd_var~indirect:truefile`ArgIn;|Syslog,_->()end;(* assign \result \from indirect:sources *)ifnot(Cil.isVoidTyperet_typ)thenBuild.(assigns[result](List.mapindirect!sources));(* assigns dests \from sources *)Build.assigns!dests!sources;(* Build the default behaviour *)Build.finish_declaration();Build.cil_varinfofunvar(* --- Call translation --- *)letformat_of_ikind=function|IBool->Some`hh,`u|IChar->None,`c|ISChar->Some`hh,`d|IUChar->Some`hh,`u|IInt->None,`d|IUInt->None,`u|IShort->Some`h,`d|IUShort->Some`h,`u|ILong->Some`l,`d|IULong->Some`l,`u|ILongLong->Some`ll,`d|IULongLong->Some`ll,`uletformat_of_fkindk=function|FFloat->None,`f|FDouble->(matchkwith|Format_types.PrintfLike->None,`f|ScanfLike->Some`l,`f)|FLongDouble->Some`L,`fletrecformat_of_typevfkt=matchtwith|TInt(ikind,_)|TEnum({ekind=ikind},_)->format_of_ikindikind|TFloat(fkind,_)->format_of_fkindkfkind|TPtr(_,_)->(* technically, we might still want to write/read the actual pointer,
but this is not the most likely possibility. *)ifCil.isCharPtrTypetthenNone,`selseNone,`p|TNamed({tname;ttype},_)->(matchtnamewith|"size_t"->Some`z,`u|"ptrdiff_t"->Some`t,`d|"intmax_t"->Some`j,`d|"uintmax_t"->Some`j,`u(* not really standard, but that's what glibc does. *)|"ssize_t"->Some`z,`d|_->format_of_typevfkttype)(* in the case of a scanf-like function, it might happen
that we pass a void* whose actual type is coherent with
the format string itself, but this can't really be checked
here.
*)|TVoid_->raise(Translate_call_exnvf.vf_decl)(* these cases should not happen anyway *)|TComp_|TFun_|TArray_|TBuiltin_va_list_->raise(Translate_call_exnvf.vf_decl)letinfer_format_from_argsvfformat_funargs=letargs=List.drop(format_fun.f_format_pos+1)argsinletf_format(l,s)=Format_types.(Specification{f_flags=[];f_field_width=None;f_precision=None;f_length_modifier=l;f_conversion_specifier=s;f_capitalize=false;})inlets_format(l,s)=Format_types.(Specification{s_assignment_suppression=false;s_field_width=None;s_length_modifier=l;s_conversion_specifier=s;})inlettreat_one_argarg=lett=Cil.typeOfarginlett=matchformat_fun.f_kindwith|PrintfLike->t|ScanfLike->ifnot(Cil.isPointerTypet)thenbeginletsource=fstarg.elocinSelf.warning~source~wkey:wkey_typing"Expecting pointer as parameter of scanf function. \
Argument %a has type %a"Printer.pp_expargPrinter.pp_typt;raise(Translate_call_exnvf.vf_decl);end;Cil.typeOf_pointedtinformat_of_typevfformat_fun.f_kindtinletformat=List.maptreat_one_argargsinmatchformat_fun.f_kindwith|PrintfLike->Format_types.FFormat(List.mapf_formatformat)|ScanfLike->Format_types.SFormat(List.maps_formatformat)letformat_fun_call~builderenvformat_funvfargs=(* Extract the format if possible *)letformat=tryletformat_arg=List.nthargsformat_fun.f_format_posinmatchstatic_stringformat_argwith|None->Self.warning~current:true"Call to function %s with non-static format argument: \
assuming that parameters are coherent with the format, and that \
no %%n specifiers are present in the actual string."vf.vf_decl.vorig_name;infer_format_from_argsvfformat_funargs|Somes->Format_parser.parse_formatformat_fun.f_kindswith|Format_parser.Invalid_format->raise(Translate_call_exnvf.vf_decl)in(* Try to type expected parameters if possible *)letfind_typedef=Environment.find_typeenvinlettvparams=tryFormat_typer.type_format~find_typedefformatwithFormat_typer.Type_not_foundtype_name->Self.warning~current:true"Unable to find type %s in the source code which should be used in \
this call:@ no specification will be generated.@ \
Note that due to cleanup, the type may have been defined in the \
original code but not used anywhere."type_name;raise(Translate_call_exnvf.vf_decl)in(* Create the new callee *)letnew_callee=build_specialized_fun~builderenvvfformat_funtvparamsin(* Store the translation *)Replacements.addnew_calleevf.vf_decl;(* Translate the call *)Self.result~current:true~level:2"Translating call to %s to a call to the specialized version %s."vf.vf_decl.vorig_namenew_callee.vname;lettparams=Typ.params_typesnew_callee.vtypeinmatch_call~buildernew_calleetparamsargs