123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575(************************************************************************)(* * The Coq Proof Assistant / The Coq Development Team *)(* v * Copyright INRIA, CNRS and contributors *)(* <O___,, * (see version control and CREDITS file for authors & dates) *)(* \VV/ **************************************************************)(* // * This file is distributed under the terms of the *)(* * GNU Lesser General Public License Version 2.1 *)(* * (see LICENSE file for the text of the license) *)(************************************************************************)(*i*)openUtilopenNamesopenLibnamesopenTableopenMiniml(*i*)(*s Exceptions. *)exceptionFoundexceptionImpossible(*S Names operations. *)letanonymous_name=Id.of_string"x"letdummy_name=Id.of_string"_"letanonymous=Idanonymous_nameletid_of_name=function|Name.Anonymous->anonymous_name|Name.NameidwhenId.equaliddummy_name->anonymous_name|Name.Nameid->idletid_of_mlid=function|Dummy->dummy_name|Idid->id|Tmpid->idlettmp_id=function|Idid->Tmpid|a->aletis_tmp=functionTmp_->true|_->false(*S Operations upon ML types (with meta). *)letmeta_count=ref0letreset_meta_count()=meta_count:=0letnew_meta_=incrmeta_count;Tmeta{id=!meta_count;contents=None}letreceq_ml_typet1t2=matcht1,t2with|Tarr(tl1,tr1),Tarr(tl2,tr2)->eq_ml_typetl1tl2&&eq_ml_typetr1tr2|Tglob(gr1,t1),Tglob(gr2,t2)->GlobRef.CanOrd.equalgr1gr2&&List.equaleq_ml_typet1t2|Tvari1,Tvari2->Int.equali1i2|Tvar'i1,Tvar'i2->Int.equali1i2|Tmetam1,Tmetam2->eq_ml_metam1m2|Tdummyk1,Tdummyk2->k1==k2|Tunknown,Tunknown->true|Taxiom,Taxiom->true|(Tarr_|Tglob_|Tvar_|Tvar'_|Tmeta_|Tdummy_|Tunknown|Taxiom),_->falseandeq_ml_metam1m2=Int.equalm1.idm2.id&&Option.equaleq_ml_typem1.contentsm2.contents(* Simultaneous substitution of [[Tvar 1; ... ; Tvar n]] by [l] in a ML type. *)lettype_subst_listlt=letrecsubstt=matchtwith|Tvarj->List.nthl(j-1)|Tmeta{contents=None}->t|Tmeta{contents=Someu}->substu|Tarr(a,b)->Tarr(substa,substb)|Tglob(r,l)->Tglob(r,List.mapsubstl)|a->ainsubstt(* Simultaneous substitution of [[|Tvar 1; ... ; Tvar n|]] by [v] in a ML type. *)lettype_subst_vectvt=letrecsubstt=matchtwith|Tvarj->v.(j-1)|Tmeta{contents=None}->t|Tmeta{contents=Someu}->substu|Tarr(a,b)->Tarr(substa,substb)|Tglob(r,l)->Tglob(r,List.mapsubstl)|a->ainsubstt(*s From a type schema to a type. All [Tvar] become fresh [Tmeta]. *)letinstantiation(nb,t)=type_subst_vect(Array.initnbnew_meta)t(*s Occur-check of a free meta in a type *)letrectype_occursalphat=matchtwith|Tmeta{id=beta;contents=None}->Int.equalalphabeta|Tmeta{contents=Someu}->type_occursalphau|Tarr(t1,t2)->type_occursalphat1||type_occursalphat2|Tglob(r,l)->List.exists(type_occursalpha)l|(Tdummy_|Tvar_|Tvar'_|Taxiom|Tunknown)->false(*s Most General Unificator *)letrecmgu=function|Tmetam,Tmetam'whenInt.equalm.idm'.id->()|Tmetam,t|t,Tmetam->(matchm.contentswith|Someu->mgu(u,t)|Nonewhentype_occursm.idt->raiseImpossible|None->m.contents<-Somet)|Tarr(a,b),Tarr(a',b')->mgu(a,a');mgu(b,b')|Tglob(r,l),Tglob(r',l')whenGlobRef.CanOrd.equalrr'->List.itermgu(List.combinell')|Tdummy_,Tdummy_->()|Tvari,TvarjwhenInt.equalij->()|Tvar'i,Tvar'jwhenInt.equalij->()|Tunknown,Tunknown->()|Taxiom,Taxiom->()|_->raiseImpossibleletskip_typing()=lang()==Scheme||is_extrcompute()letneeds_magicp=ifskip_typing()thenfalseelsetrymgup;falsewithImpossible->trueletput_magic_ifba=ifbthenMLmagicaelsealetput_magicpa=ifneeds_magicpthenMLmagicaelsealetgeneralizablea=lang()!=Ocaml||matchawith|MLapp_->false|_->true(* TODO, this is just an approximation for the moment *)(*S ML type env. *)moduleMlenv=structletmeta_cmpmm'=comparem.idm'.idmoduleMetaset=Set.Make(structtypet=ml_metaletcompare=meta_cmpend)(* Main MLenv type. [env] is the real environment, whereas [free]
(tries to) record the free meta variables occurring in [env]. *)typet={env:ml_schemalist;mutablefree:Metaset.t}(* Empty environment. *)letempty={env=[];free=Metaset.empty}(* [get] returns a instantiated copy of the n-th most recently added
type in the environment. *)letgetmlen=assert(List.lengthmle.env>=n);instantiation(List.nthmle.env(n-1))(* [find_free] finds the free meta in a type. *)letrecfind_freeset=function|TmetamwhenOption.is_emptym.contents->Metaset.addmset|Tmeta{contents=Somet}->find_freesett|Tarr(a,b)->find_free(find_freeseta)b|Tglob(_,l)->List.fold_leftfind_freesetl|_->set(* The [free] set of an environment can be outdate after
some unifications. [clean_free] takes care of that. *)letclean_freemle=letrem=refMetaset.emptyandadd=refMetaset.emptyinletcleanm=matchm.contentswith|None->()|Someu->rem:=Metaset.addm!rem;add:=find_free!adduinMetaset.itercleanmle.free;mle.free<-Metaset.union(Metaset.diffmle.free!rem)!add(* From a type to a type schema. If a [Tmeta] is still uninstantiated
and does appears in the [mle], then it becomes a [Tvar]. *)letgeneralizationmlet=letc=ref0inletmap=ref(Int.Map.empty:intInt.Map.t)inletadd_newi=incrc;map:=Int.Map.addi!c!map;!cinletrecmeta2vart=matchtwith|Tmeta{contents=Someu}->meta2varu|Tmeta({id=i}asm)->(tryTvar(Int.Map.findi!map)withNot_found->ifMetaset.memmmle.freethentelseTvar(add_newi))|Tarr(t1,t2)->Tarr(meta2vart1,meta2vart2)|Tglob(r,l)->Tglob(r,List.mapmeta2varl)|t->tin!c,meta2vart(* Adding a type in an environment, after generalizing. *)letpush_genmlet=clean_freemle;{env=generalizationmlet::mle.env;free=mle.free}(* Adding a type with no [Tvar], hence no generalization needed. *)letpush_typemlet={env=(0,t)::mle.env;free=find_freemle.freet}(* Adding a type with no [Tvar] nor [Tmeta]. *)letpush_std_typemlet={env=(0,t)::mle.env;free=mle.free}end(*S Operations upon ML types (without meta). *)(*s Does a section path occur in a ML type ? *)letrectype_mem_knkn=function|Tmeta{contents=Somet}->type_mem_knknt|Tglob(r,l)->occur_kn_in_refknr||List.exists(type_mem_knkn)l|Tarr(a,b)->(type_mem_knkna)||(type_mem_knknb)|_->false(*s Greatest variable occurring in [t]. *)lettype_maxvart=letrecparsen=function|Tmeta{contents=Somet}->parsent|Tvari->maxin|Tarr(a,b)->parse(parsena)b|Tglob(_,l)->List.fold_leftparsenl|_->ninparse0t(*s From [a -> b -> c] to [[a;b],c]. *)letrectype_decomp=function|Tmeta{contents=Somet}->type_decompt|Tarr(a,b)->letl,h=type_decompbina::l,h|a->[],a(*s The converse: From [[a;b],c] to [a -> b -> c]. *)letrectype_recomp(l,t)=matchlwith|[]->t|a::l->Tarr(a,type_recomp(l,t))(*s Translating [Tvar] to [Tvar'] to avoid clash. *)letrecvar2var'=function|Tmeta{contents=Somet}->var2var't|Tvari->Tvar'i|Tarr(a,b)->Tarr(var2var'a,var2var'b)|Tglob(r,l)->Tglob(r,List.mapvar2var'l)|a->atypeabbrev_map=GlobRef.t->ml_typeoption(*s Delta-reduction of type constants everywhere in a ML type [t].
[env] is a function of type [ml_type_env]. *)lettype_expandenvt=letrecexpand=function|Tmeta{contents=Somet}->expandt|Tglob(r,l)->(matchenvrwith|Somemlt->expand(type_subst_listlmlt)|None->Tglob(r,List.mapexpandl))|Tarr(a,b)->Tarr(expanda,expandb)|a->ainifTable.type_expand()thenexpandtelsetlettype_simpl=type_expand(fun_->None)(*s Generating a signature from a ML type. *)lettype_to_signenvt=matchtype_expandenvtwith|Tdummydwhennot(conservative_types())->Killd|_->Keeplettype_to_signatureenvt=letrecf=function|Tmeta{contents=Somet}->ft|Tarr(Tdummyd,b)whennot(conservative_types())->Killd::fb|Tarr(_,b)->Keep::fb|_->[]inf(type_expandenvt)letisKill=functionKill_->true|_->falseletisTdummy=functionTdummy_->true|_->falseletisMLdummy=functionMLdummy_->true|_->falseletsign_of_id=function|Dummy->KillKprop|(Id_|Tmp_)->Keep(* Classification of signatures *)typesign_kind=|EmptySig|NonLogicalSig(* at least a [Keep] *)|SafeLogicalSig(* only [Kill Ktype] *)|UnsafeLogicalSig(* No [Keep], not all [Kill Ktype] *)letrecsign_kind=function|[]->EmptySig|Keep::_->NonLogicalSig|Killk::s->matchk,sign_kindswith|_,NonLogicalSig->NonLogicalSig|Ktype,(SafeLogicalSig|EmptySig)->SafeLogicalSig|_,_->UnsafeLogicalSig(* Removing the final [Keep] in a signature *)letrecsign_no_final_keeps=function|[]->[]|k::s->matchk,sign_no_final_keepsswith|Keep,[]->[]|k,l->k::l(*s Removing [Tdummy] from the top level of a ML type. *)lettype_expunge_from_signenvst=letrecexpungest=matchs,twith|[],_->t|Keep::s,Tarr(a,b)->Tarr(a,expungesb)|Kill_::s,Tarr(a,b)->expungesb|_,Tmeta{contents=Somet}->expungest|_,Tglob(r,l)->(matchenvrwith|Somemlt->expunges(type_subst_listlmlt)|None->assertfalse)|_->assertfalseinlett=expunge(sign_no_final_keepss)tiniflang()!=Haskell&&sign_kinds==UnsafeLogicalSigthenTarr(TdummyKprop,t)elsetlettype_expungeenvt=type_expunge_from_signenv(type_to_signatureenvt)t(*S Generic functions over ML ast terms. *)letmlappfa=ifList.is_emptyathenfelseMLapp(f,a)(** Equality *)leteq_ml_identi1i2=matchi1,i2with|Dummy,Dummy->true|Idid1,Idid2->Id.equalid1id2|Tmpid1,Tmpid2->Id.equalid1id2|Dummy,(Id_|Tmp_)|Id_,(Dummy|Tmp_)|Tmp_,(Dummy|Id_)->falseletreceq_ml_astt1t2=matcht1,t2with|MLreli1,MLreli2->Int.equali1i2|MLapp(f1,t1),MLapp(f2,t2)->eq_ml_astf1f2&&List.equaleq_ml_astt1t2|MLlam(na1,t1),MLlam(na2,t2)->eq_ml_identna1na2&&eq_ml_astt1t2|MLletin(na1,c1,t1),MLletin(na2,c2,t2)->eq_ml_identna1na2&&eq_ml_astc1c2&&eq_ml_astt1t2|MLglobgr1,MLglobgr2->GlobRef.CanOrd.equalgr1gr2|MLcons(t1,gr1,c1),MLcons(t2,gr2,c2)->eq_ml_typet1t2&&GlobRef.CanOrd.equalgr1gr2&&List.equaleq_ml_astc1c2|MLtuplet1,MLtuplet2->List.equaleq_ml_astt1t2|MLcase(t1,c1,p1),MLcase(t2,c2,p2)->eq_ml_typet1t2&&eq_ml_astc1c2&&Array.equaleq_ml_branchp1p2|MLfix(i1,id1,t1),MLfix(i2,id2,t2)->Int.equali1i2&&Array.equalId.equalid1id2&&Array.equaleq_ml_astt1t2|MLexne1,MLexne2->String.equale1e2|MLdummyk1,MLdummyk2->k1==k2|MLaxiom_,MLaxiom_->true(* ignore the name of the axiom *)|MLmagict1,MLmagict2->eq_ml_astt1t2|MLuinti1,MLuinti2->Uint63.equali1i2|MLfloatf1,MLfloatf2->Float64.equalf1f2|MLstrings1,MLstrings2->Pstring.equals1s2|MLparray(t1,def1),MLparray(t2,def2)->Array.equaleq_ml_astt1t2&&eq_ml_astdef1def2|(MLrel_|MLapp_|MLlam_|MLletin_|MLglob_|MLcons_|MLtuple_|MLcase_|MLfix_|MLexn_|MLdummy_|MLaxiom_|MLmagic_|MLuint_|MLfloat_|MLstring_|MLparray_),_->falseandeq_ml_patternp1p2=matchp1,p2with|Pcons(gr1,p1),Pcons(gr2,p2)->GlobRef.CanOrd.equalgr1gr2&&List.equaleq_ml_patternp1p2|Ptuplep1,Ptuplep2->List.equaleq_ml_patternp1p2|Preli1,Preli2->Int.equali1i2|Pwild,Pwild->true|Pusualgr1,Pusualgr2->GlobRef.CanOrd.equalgr1gr2|_->falseandeq_ml_branch(id1,p1,t1)(id2,p2,t2)=List.equaleq_ml_identid1id2&&eq_ml_patternp1p2&&eq_ml_astt1t2(*s [ast_iter_rel f t] applies [f] on every [MLrel] in t. It takes care
of the number of bingings crossed before reaching the [MLrel]. *)letast_iter_relf=letrecitern=function|MLreli->f(i-n)|MLlam(_,a)->iter(n+1)a|MLletin(_,a,b)->iterna;iter(n+1)b|MLcase(_,a,v)->iterna;Array.iter(fun(l,_,t)->iter(n+(List.lengthl))t)v|MLfix(_,ids,v)->letk=Array.lengthidsinArray.iter(iter(n+k))v|MLapp(a,l)->iterna;List.iter(itern)l|MLcons(_,_,l)|MLtuplel->List.iter(itern)l|MLmagica->iterna|MLparray(t,def)->Array.iter(itern)t;iterndef|MLglob_|MLexn_|MLdummy_|MLaxiom_|MLuint_|MLfloat_|MLstring_->()initer0(*s Map over asts. *)letast_map_branchf(c,ids,a)=(c,ids,fa)(* Warning: in [ast_map] we assume that [f] does not change the type
of [MLcons] and of [MLcase] heads *)letast_mapf=function|MLlam(i,a)->MLlam(i,fa)|MLletin(i,a,b)->MLletin(i,fa,fb)|MLcase(typ,a,v)->MLcase(typ,fa,Array.map(ast_map_branchf)v)|MLfix(i,ids,v)->MLfix(i,ids,Array.mapfv)|MLapp(a,l)->MLapp(fa,List.mapfl)|MLcons(typ,c,l)->MLcons(typ,c,List.mapfl)|MLtuplel->MLtuple(List.mapfl)|MLmagica->MLmagic(fa)|MLparray(t,def)->MLparray(Array.mapft,fdef)|MLrel_|MLglob_|MLexn_|MLdummy_|MLaxiom_|MLuint_|MLfloat_|MLstring_asa->a(*s Map over asts, with binding depth as parameter. *)letast_map_lift_branchfn(ids,p,a)=(ids,p,f(n+(List.lengthids))a)(* Same warning as for [ast_map]... *)letast_map_liftfn=function|MLlam(i,a)->MLlam(i,f(n+1)a)|MLletin(i,a,b)->MLletin(i,fna,f(n+1)b)|MLcase(typ,a,v)->MLcase(typ,fna,Array.map(ast_map_lift_branchfn)v)|MLfix(i,ids,v)->letk=Array.lengthidsinMLfix(i,ids,Array.map(f(k+n))v)|MLapp(a,l)->MLapp(fna,List.map(fn)l)|MLcons(typ,c,l)->MLcons(typ,c,List.map(fn)l)|MLtuplel->MLtuple(List.map(fn)l)|MLmagica->MLmagic(fna)|MLparray(t,def)->MLparray(Array.map(fn)t,fndef)|MLrel_|MLglob_|MLexn_|MLdummy_|MLaxiom_|MLuint_|MLfloat_|MLstring_asa->a(*s Iter over asts. *)letast_iter_branchf(c,ids,a)=faletast_iterf=function|MLlam(i,a)->fa|MLletin(i,a,b)->fa;fb|MLcase(_,a,v)->fa;Array.iter(ast_iter_branchf)v|MLfix(i,ids,v)->Array.iterfv|MLapp(a,l)->fa;List.iterfl|MLcons(_,_,l)|MLtuplel->List.iterfl|MLmagica->fa|MLparray(t,def)->Array.iterft;fdef|MLrel_|MLglob_|MLexn_|MLdummy_|MLaxiom_|MLuint_|MLfloat_|MLstring_->()(*S Operations concerning De Bruijn indices. *)(*s [ast_occurs k t] returns [true] if [(Rel k)] occurs in [t]. *)letast_occurskt=tryast_iter_rel(funi->ifInt.equalikthenraiseFound)t;falsewithFound->true(*s [occurs_itvl k k' t] returns [true] if there is a [(Rel i)]
in [t] with [k<=i<=k'] *)letast_occurs_itvlkk't=tryast_iter_rel(funi->if(k<=i)&&(i<=k')thenraiseFound)t;falsewithFound->true(* Number of occurrences of [Rel 1] in [t], with special treatment of match:
occurrences in different branches aren't added, but we rather use max. *)letnb_occur_match=letrecnbk=function|MLreli->ifInt.equalikthen1else0|MLcase(_,a,v)->(nbka)+Array.fold_left(funr(ids,_,a)->maxr(nb(k+(List.lengthids))a))0v|MLletin(_,a,b)->(nbka)+(nb(k+1)b)|MLfix(_,ids,v)->letk=k+(Array.lengthids)inArray.fold_left(funra->r+(nbka))0v|MLlam(_,a)->nb(k+1)a|MLapp(a,l)->List.fold_left(funra->r+(nbka))(nbka)l|MLcons(_,_,l)|MLtuplel->List.fold_left(funra->r+(nbka))0l|MLmagica->nbka|MLparray(t,def)->Array.fold_left(funra->r+(nbka))0t+nbkdef|MLglob_|MLexn_|MLdummy_|MLaxiom_|MLuint_|MLfloat_|MLstring_->0innb1(* Replace unused variables by _ *)letdump_unused_varsa=letrecrenenva=matchawith|MLreli->let()=(List.nthenv(i-1)):=trueina|MLlam(id,b)->letocc_id=reffalseinletb'=ren(occ_id::env)binif!occ_idthenifb'==bthenaelseMLlam(id,b')elseMLlam(Dummy,b')|MLletin(id,b,c)->letocc_id=reffalseinletb'=renenvbinletc'=ren(occ_id::env)cinif!occ_idthenifb'==b&&c'==cthenaelseMLletin(id,b',c')else(* 'let' without occurrence: shouldn't happen after simpl *)MLletin(Dummy,b',c')|MLcase(t,e,br)->lete'=renenveinletbr'=Array.Smart.map(ren_branchenv)brinife'==e&&br'==brthenaelseMLcase(t,e',br')|MLfix(i,ids,v)->letenv'=List.init(Array.lengthids)(fun_->reffalse)@envinletv'=Array.Smart.map(renenv')vinifv'==vthenaelseMLfix(i,ids,v')|MLapp(b,l)->letb'=renenvbandl'=List.Smart.map(renenv)linifb'==b&&l'==lthenaelseMLapp(b',l')|MLcons(t,r,l)->letl'=List.Smart.map(renenv)linifl'==lthenaelseMLcons(t,r,l')|MLtuplel->letl'=List.Smart.map(renenv)linifl'==lthenaelseMLtuplel'|MLmagicb->letb'=renenvbinifb'==bthenaelseMLmagicb'|MLparray(t,def)->lett'=Array.Smart.map(renenv)tinletdef'=renenvdefinifdef'==def&&t'==tthenaelseMLparray(t',def')|MLglob_|MLexn_|MLdummy_|MLaxiom_|MLuint_|MLfloat_|MLstring_->aandren_branchenv((ids,p,b)astr)=letoccs=List.map(fun_->reffalse)idsinletb'=ren(List.rev_appendoccsenv)binletids'=List.map2(funidocc->if!occthenidelseDummy)idsoccsinifb'==b&&List.equaleq_ml_identidsids'thentrelse(ids',p,b')inren[]a(*s Lifting on terms.
[ast_lift k t] lifts the binding depth of [t] across [k] bindings. *)letast_liftkt=letrecliftrecn=function|MLreliasa->ifi-n<1thenaelseMLrel(i+k)|a->ast_map_liftliftrecnainifInt.equalk0thentelseliftrec0tletast_popt=ast_lift(-1)t(*s [permut_rels k k' c] translates [Rel 1 ... Rel k] to [Rel (k'+1) ...
Rel (k'+k)] and [Rel (k+1) ... Rel (k+k')] to [Rel 1 ... Rel k'] *)letpermut_relskk'=letrecpermutn=function|MLreliasa->leti'=i-ninifi'<1||i'>k+k'thenaelseifi'<=kthenMLrel(i+k')elseMLrel(i-k)|a->ast_map_liftpermutnainpermut0(*s Substitution. [ml_subst e t] substitutes [e] for [Rel 1] in [t].
Lifting (of one binder) is done at the same time. *)letast_subste=letrecsubstn=function|MLreliasa->leti'=i-ninifInt.equali'1thenast_liftneelseifi'<1thenaelseMLrel(i-1)|a->ast_map_liftsubstnainsubst0(*s Generalized substitution.
[gen_subst v d t] applies to [t] the substitution coded in the
[v] array: [(Rel i)] becomes [v.(i-1)]. [d] is the correction applies
to [Rel] greater than [Array.length v]. *)letgen_substvdt=letrecsubstn=function|MLreliasa->leti'=i-ninifi'<1thenaelseifi'<=Array.lengthvthenmatchv.(i'-1)with|None->assertfalse|Someu->ast_liftnuelseMLrel(i+d)|a->ast_map_liftsubstnainsubst0t(*S Operations concerning match patterns *)letis_basic_pattern=function|Prel_|Pwild->true|Pusual_|Pcons_|Ptuple_->falselethas_deep_patternbr=letdeep=function|Pcons(_,l)|Ptuplel->not(List.for_allis_basic_patternl)|Pusual_|Prel_|Pwild->falseinArray.exists(function(_,pat,_)->deeppat)brletis_regular_matchbr=ifArray.is_emptybrthenfalse(* empty match becomes MLexn *)elsetryletget_r(ids,pat,c)=matchpatwith|Pusualr->r|Pcons(r,l)->letis_reli=functionPrelj->Int.equalij|_->falseinifnot(List.for_all_iis_rel1(List.revl))thenraiseImpossible;r|_->raiseImpossibleinletind=matchget_rbr.(0)with|GlobRef.ConstructRef(ind,_)->ind|_->raiseImpossibleinletis_refitr=matchget_rtrwith|GlobRef.ConstructRef(ind',j)->Ind.CanOrd.equalindind'&&Int.equalj(i+1)|_->falseinArray.for_all_iis_ref0brwithImpossible->false(*S Operations concerning lambdas. *)(*s [collect_lams MLlam(id1,...MLlam(idn,t)...)] returns
[[idn;...;id1]] and the term [t]. *)letcollect_lams=letreccollectacc=function|MLlam(id,t)->collect(id::acc)t|x->acc,xincollect[](*s [collect_n_lams] does the same for a precise number of [MLlam]. *)letcollect_n_lams=letreccollectaccnt=ifInt.equaln0thenacc,telsematchtwith|MLlam(id,t)->collect(id::acc)(n-1)t|_->assertfalseincollect[](*s [remove_n_lams] just removes some [MLlam]. *)letrecremove_n_lamsnt=ifInt.equaln0thentelsematchtwith|MLlam(_,t)->remove_n_lams(n-1)t|_->assertfalse(*s [nb_lams] gives the number of head [MLlam]. *)letrecnb_lams=function|MLlam(_,t)->succ(nb_lamst)|_->0(*s [named_lams] does the converse of [collect_lams]. *)letrecnamed_lamsidsa=matchidswith|[]->a|id::ids->named_lamsids(MLlam(id,a))(*s The same for a specific identifier (resp. anonymous, dummy) *)letrecmany_lamsida=function|0->a|n->many_lamsid(MLlam(id,a))(predn)letanonym_tmp_lamsan=many_lams(Tmpanonymous_name)anletdummy_lamsan=many_lamsDummyan(*s mixed according to a signature. *)letrecanonym_or_dummy_lamsa=function|[]->a|Keep::s->MLlam(anonymous,anonym_or_dummy_lamsas)|Kill_::s->MLlam(Dummy,anonym_or_dummy_lamsas)(*S Operations concerning eta. *)(*s The following function creates [MLrel n;...;MLrel 1] *)letreceta_argsn=ifInt.equaln0then[]else(MLreln)::(eta_args(predn))(*s Same, but filtered by a signature. *)letreceta_args_signn=function|[]->[]|Keep::s->(MLreln)::(eta_args_sign(n-1)s)|Kill_::s->eta_args_sign(n-1)s(*s This one tests [MLrel (n+k); ... ;MLrel (1+k)] *)letrectest_eta_args_liftkn=function|[]->Int.equaln0|MLrelm::q->Int.equal(k+n)m&&(test_eta_args_liftk(predn)q)|_->false(*s Computes an eta-reduction. *)leteta_rede=letids,t=collect_lamseinletn=List.lengthidsinifInt.equaln0theneelsematchtwith|MLapp(f,a)->letm=List.lengthainletids,body,args=ifInt.equalmnthen[],f,aelseifm<nthenList.skipnmids,f,aelse(* m > n *)leta1,a2=List.chop(m-n)ain[],MLapp(f,a1),a2inletp=List.lengthargsiniftest_eta_args_lift0pargs&¬(ast_occurs_itvl1pbody)thennamed_lamsids(ast_lift(-p)body)elsee|_->e(* Performs an eta-reduction when the core is atomic and value,
or otherwise returns None *)letatomic_eta_rede=letids,t=collect_lamseinletn=List.lengthidsinmatchtwith|MLapp(f,a)whentest_eta_args_lift0na->(matchfwith|MLrelkwhenk>n->Some(MLrel(k-n))|MLglob_|MLdummy_->Somef|_->None)|_->None(*s Computes all head linear beta-reductions possible in [(t a)].
Non-linear head beta-redex become let-in. *)letreclinear_beta_redat=matcha,twith|[],_->t|a0::a,MLlam(id,t)->(matchnb_occur_matchtwith|0->linear_beta_reda(ast_popt)|1->linear_beta_reda(ast_substa0t)|_->leta=List.map(ast_lift1)ainMLletin(id,a0,linear_beta_redat))|_->MLapp(t,a)letrectmp_head_lams=function|MLlam(id,t)->MLlam(tmp_idid,tmp_head_lamst)|e->e(*s Applies a substitution [s] of constants by their body, plus
linear beta reductions at modified positions.
Moreover, we mark some lambdas as suitable for later linear
reduction (this helps the inlining of recursors).
*)letrecast_glob_substst=matchtwith|MLapp((MLglob((GlobRef.ConstRefkn)asrefe))asf,a)->leta=List.map(fune->tmp_head_lams(ast_glob_substse))ain(trylinear_beta_reda(Refmap'.findrefes)withNot_found->MLapp(f,a))|MLglob((GlobRef.ConstRefkn)asrefe)->(tryRefmap'.findrefeswithNot_found->t)|_->ast_map(ast_glob_substs)t(*S Auxiliary functions used in simplification of ML cases. *)(* Factorisation of some match branches into a common "x -> f x"
branch may break types sometimes. Example: [type 'x a = A].
Then [let id = function A -> A] has type ['x a -> 'y a],
which is incompatible with the type of [let id x = x].
We now check that the type arguments of the inductive are
preserved by our transformation.
TODO: this verification should be done someday modulo
expansion of type definitions.
*)(*s [branch_as_function b typ (l,p,c)] tries to see branch [c]
as a function [f] applied to [MLcons(r,l)]. For that it transforms
any [MLcons(r,l)] in [MLrel 1] and raises [Impossible]
if any variable in [l] occurs outside such a [MLcons] *)letbranch_as_funtyp(l,p,c)=letnargs=List.lengthlinletcons=matchpwith|Pusualr->MLcons(typ,r,eta_argsnargs)|Pcons(r,pl)->letpat2rel=functionPreli->MLreli|_->raiseImpossibleinMLcons(typ,r,List.mappat2relpl)|_->raiseImpossibleinletrecgenrecn=function|MLreliasc->leti'=i-ninifi'<1thencelseifi'>nargsthenMLrel(i-nargs+1)elseraiseImpossible|MLcons_ascons'wheneq_ml_astcons'(ast_liftncons)->MLrel(n+1)|a->ast_map_liftgenrecnaingenrec0c(*s [branch_as_cst (l,p,c)] tries to see branch [c] as a constant
independent from the pattern [MLcons(r,l)]. For that is raises [Impossible]
if any variable in [l] occurs in [c], and otherwise returns [c] lifted to
appear like a function with one arg (for uniformity with [branch_as_fun]).
NB: [MLcons(r,l)] might occur nonetheless in [c], but only when [l] is
empty, i.e. when [r] is a constant constructor
*)letbranch_as_cst(l,_,c)=letn=List.lengthlinifast_occurs_itvl1ncthenraiseImpossible;ast_lift(1-n)c(* A branch [MLcons(r,l)->c] can be seen at the same time as a function
branch and a constant branch, either because:
- [MLcons(r,l)] doesn't occur in [c]. For example : "A -> B"
- this constructor is constant (i.e. [l] is empty). For example "A -> A"
When searching for the best factorisation below, we'll try both.
*)(* The following structure allows recording which element occurred
at what position, and then finally return the most frequent
element and its positions. *)letcensus_add,census_max,census_clean=leth=ref[]inletclearf()=h:=[]inletrecaddkv=function|[]->raiseNot_found|(k',s)asp::l->ifeq_ml_astkk'then(k',Int.Set.addvs)::lelsep::addkvlinletaddfki=tryh:=addki!hwithNot_found->h:=(k,Int.Set.singletoni)::!hinletmaxf()=letlen=ref0andlst=refInt.Set.emptyandelm=ref(MLaxiom"should not appear")inList.iter(fun(e,s)->letn=Int.Set.cardinalsinifn>!lenthenbeginlen:=n;lst:=s;elm:=eend)!h;(!elm,!lst)in(addf,maxf,clearf)(* [factor_branches] return the longest possible list of branches
that have the same factorization, either as a function or as a
constant.
*)letis_opt_pat(_,p,_)=matchpwith|Prel_|Pwild->true|_->falseletfactor_branchesotypbr=ifArray.existsis_opt_patbrthenNone(* already optimized *)elsebegincensus_clean();fori=0toArray.lengthbr-1doifo.opt_case_idrthen(trycensus_add(branch_as_funtypbr.(i))iwithImpossible->());ifo.opt_case_cstthen(trycensus_add(branch_as_cstbr.(i))iwithImpossible->());done;letbr_factor,br_set=census_max()incensus_clean();letn=Int.Set.cardinalbr_setinifInt.equaln0thenNoneelseifArray.lengthbr>=2&&n<2thenNoneelseSome(br_factor,br_set)end(*s If all branches are functions, try to permute the case and the functions. *)letrecmerge_idsidsids'=matchids,ids'with|[],l->l|l,[]->l|i::ids,i'::ids'->(ifi==Dummytheni'elsei)::(merge_idsidsids')letis_exn=functionMLexn_->true|_->falseletpermut_case_funbracc=letnb=refmax_intinArray.iter(fun(_,_,t)->letids,c=collect_lamstinletn=List.lengthidsinif(n<!nb)&&(not(is_exnc))thennb:=n)br;ifInt.equal!nbmax_int||Int.equal!nb0then([],br)elsebeginletbr=Array.copybrinletids=ref[]infori=0toArray.lengthbr-1dolet(l,p,t)=br.(i)inletlocal_nb=nb_lamstiniflocal_nb<!nbthen(* t = MLexn ... *)br.(i)<-(l,p,remove_n_lamslocal_nbt)elsebeginletlocal_ids,t=collect_n_lams!nbtinids:=merge_ids!idslocal_ids;br.(i)<-(l,p,permut_rels!nb(List.lengthl)t)enddone;(!ids,br)end(*S Generalized iota-reduction. *)(* Definition of a generalized iota-redex: it's a [MLcase(e,br)]
where the head [e] is a [MLcons] or made of [MLcase]'s with
[MLcons] as leaf branches.
A generalized iota-redex is transformed into beta-redexes. *)(* In [iota_red], we try to simplify a [MLcase(_,MLcons(typ,r,a),br)].
Argument [i] is the branch we consider, we should lift what
comes from [br] by [lift] *)letreciota_rediliftbr((typ,r,a)ascons)=ifi>=Array.lengthbrthenraiseImpossible;let(ids,p,c)=br.(i)inmatchpwith|Pusualr'|Pcons(r',_)whennot(GlobRef.CanOrd.equalr'r)->iota_red(i+1)liftbrcons|Pusualr'->letc=named_lams(List.revids)cinletc=ast_liftliftcinMLapp(c,a)|Prel1whenInt.equal(List.lengthids)1->letc=MLlam(List.hdids,c)inletc=ast_liftliftcinMLapp(c,[MLcons(typ,r,a)])|PwildwhenList.is_emptyids->ast_liftliftc|_->raiseImpossible(* TODO: handle some more cases *)(* [iota_gen] is an extension of [iota_red] where we allow to
traverse matches in the head of the first match *)letiota_genbrhd=letreciotak=function|MLcons(typ,r,a)->iota_red0kbr(typ,r,a)|MLcase(typ,e,br')->letnew_br=Array.map(fun(i,p,c)->(i,p,iota(k+(List.lengthi))c))br'inMLcase(typ,e,new_br)|_->raiseImpossibleiniota0hdletis_atomic=function|MLrel_|MLglob_|MLexn_|MLdummy_->true|_->falseletis_imm_apply=functionMLapp(MLrel1,_)->true|_->false(** Program creates a let-in named "program_branch_NN" for each branch of match.
Unfolding them leads to more natural code (and more dummy removal) *)letis_program_branch=function|Tmp_|Dummy->false|Idid->lets=Id.to_stringidintryScanf.sscanfs"program_branch_%d%!"(fun_->true)withScanf.Scan_failure_|End_of_file->falseletexpand_linear_letoide=o.opt_lin_let||is_tmpid||is_program_branchid||is_imm_applye(*S The main simplification function. *)(* Some beta-iota reductions + simplifications. *)letrecunmagic=functionMLmagice->unmagice|e->eletis_magic=functionMLmagic_->true|_->falseletmagic_hda=matchawith|MLmagic_::_->a|e::a->MLmagice::a|[]->assertfalseletrecsimplo=function|MLapp(f,[])->simplof|MLapp(MLapp(f,a),a')->simplo(MLapp(f,a@a'))|MLapp(f,a)->(* When the head of the application is magic, no need for magic on args *)leta=ifis_magicfthenList.mapunmagicaelseainsimpl_appo(List.map(simplo)a)(simplof)|MLcase(typ,e,br)->letbr=Array.map(fun(l,p,t)->(l,p,simplot))brinsimpl_caseotypbr(simploe)|MLletin(Dummy,_,e)->simplo(ast_pope)|MLletin(id,c,e)->lete=simploeinif(is_atomicc)||(is_atomice)||(letn=nb_occur_matchein(Int.equaln0||(Int.equaln1&&expand_linear_letoide)))thensimplo(ast_substce)elseMLletin(id,simploc,e)|MLfix(i,ids,c)->letn=Array.lengthidsinifast_occurs_itvl1nc.(i)thenMLfix(i,ids,Array.map(simplo)c)elsesimplo(ast_lift(-n)c.(i))(* Dummy fixpoint *)|MLmagic(MLmagic_ase)->simploe|MLmagic(MLapp(f,l))->simplo(MLapp(MLmagicf,l))|MLmagic(MLletin(id,c,e))->simplo(MLletin(id,c,MLmagice))|MLmagic(MLcase(typ,e,br))->letbr'=Array.map(fun(ids,p,c)->(ids,p,MLmagicc))brinsimplo(MLcase(typ,e,br'))|MLmagic(MLdummy_ase)whenlang()==Haskell->e|MLmagic(MLexn_ase)->e|MLlam_ase->(matchatomic_eta_redewith|Somee'->e'|None->ast_map(simplo)e)|a->ast_map(simplo)a(* invariant : list [a] of arguments is non-empty *)andsimpl_appoa=function|MLlam(Dummy,t)->simplo(MLapp(ast_popt,List.tla))|MLlam(id,t)->(* Beta redex *)(matchnb_occur_matchtwith|0->simplo(MLapp(ast_popt,List.tla))|1when(is_tmpid||o.opt_lin_beta)->simplo(MLapp(ast_subst(List.hda)t,List.tla))|_->leta'=List.map(ast_lift1)(List.tla)insimplo(MLletin(id,List.hda,MLapp(t,a'))))|MLmagic(MLlam(id,t))->(* When we've at least one argument, we permute the magic
and the lambda, to simplify things a bit (see #2795).
Alas, the 1st argument must also be magic then. *)simpl_appo(magic_hda)(MLlam(id,MLmagict))|MLletin(id,e1,e2)wheno.opt_let_app->(* Application of a letin: we push arguments inside *)MLletin(id,e1,simplo(MLapp(e2,List.map(ast_lift1)a)))|MLcase(typ,e,br)wheno.opt_case_app->(* Application of a case: we push arguments inside *)letbr'=Array.map(fun(l,p,t)->letk=List.lengthlinleta'=List.map(ast_liftk)ain(l,p,simplo(MLapp(t,a'))))brinsimplo(MLcase(typ,e,br'))|(MLdummy_|MLexn_)ase->e(* We just discard arguments in those cases. *)|f->MLapp(f,a)(* Invariant : all empty matches should now be [MLexn] *)andsimpl_caseotypbre=try(* Generalized iota-redex *)ifnoto.opt_case_iotthenraiseImpossible;simplo(iota_genbre)withImpossible->(* Swap the case and the lam if possible *)letids,br=ifo.opt_case_funthenpermut_case_funbr[]else[],brinletn=List.lengthidsinifnot(Int.equaln0)thensimplo(named_lamsids(MLcase(typ,ast_liftne,br)))else(* Can we merge several branches as the same constant or function ? *)iflang()==Scheme||is_custom_matchbrthenMLcase(typ,e,br)elsematchfactor_branchesotypbrwith|Some(f,ints)whenInt.equal(Int.Set.cardinalints)(Array.lengthbr)->(* If all branches have been factorized, we remove the match *)simplo(MLletin(Tmpanonymous_name,e,f))|Some(f,ints)->letlast_br=ifast_occurs1fthen([Tmpanonymous_name],Prel1,f)else([],Pwild,ast_popf)inletbrl=Array.to_listbrinletbrl_opt=List.filteri(funi_->not(Int.Set.memiints))brlinletbrl_opt=brl_opt@[last_br]inMLcase(typ,e,Array.of_listbrl_opt)|None->MLcase(typ,e,br)(*S Local prop elimination. *)(* We try to eliminate as many [prop] as possible inside an [ml_ast]. *)(*s In a list, it selects only the elements corresponding to a [Keep]
in the boolean list [l]. *)letrecselect_via_bllargs=matchl,argswith|[],_->args|Keep::l,a::args->a::(select_via_bllargs)|Kill_::l,a::args->select_via_bllargs|_->assertfalse(*s [kill_some_lams] removes some head lambdas according to the signature [bl].
This list is build on the identifier list model: outermost lambda
is on the right.
[Rels] corresponding to removed lambdas are not supposed to occur
(except maybe in the case of Kimplicit), and
the other [Rels] are made correct via a [gen_subst].
Output is not directly a [ml_ast], compose with [named_lams] if needed. *)letis_impl_kill=functionKill(Kimplicit_)->true|_->falseletkill_some_lamsbl(ids,c)=letn=List.lengthblinletn'=List.fold_left(funnb->ifb==Keepthen(n+1)elsen)0blinifInt.equalnn'thenids,celseifInt.equaln'0&¬(List.existsis_impl_killbl)then[],ast_lift(-n)celsebeginletv=Array.makenNoneinletrecparse_idsij=function|[]->()|Keep::l->v.(i)<-Some(MLrelj);parse_ids(i+1)(j+1)l|Kill(Kimplicit_ask)::l->v.(i)<-Some(MLdummyk);parse_ids(i+1)jl|Kill_::l->parse_ids(i+1)jlinparse_ids01bl;select_via_blblids,gen_substv(n'-n)cend(*s [kill_dummy_lams] uses the last function to kill the lambdas corresponding
to a [dummy_name]. It can raise [Impossible] if there is nothing to do, or
if there is no lambda left at all. In addition, it now accepts a signature
that may mention some implicits. *)letrecmerge_implicitsidss=matchids,swith|[],_->[]|_,[]->List.mapsign_of_idids|Dummy::ids,_::s->KillKprop::merge_implicitsidss|_::ids,(Kill(Kimplicit_)ask)::s->k::merge_implicitsidss|_::ids,_::s->Keep::merge_implicitsidssletkill_dummy_lamssignc=letids,c=collect_lamscinletbl=merge_implicitsids(List.revsign)inifnot(List.memqKeepbl)thenraiseImpossible;letrecfst_killn=function|[]->raiseImpossible|Kill_::bl->n|Keep::bl->fst_kill(n+1)blinletskip=max0((fst_kill0bl)-1)inletids_skip,ids=List.chopskipidsinlet_,bl=List.chopskipblinletc=named_lamsids_skipcinletids',c=kill_some_lamsbl(ids,c)in(ids,bl),named_lamsids'c(*s [eta_expansion_sign] takes a function [fun idn ... id1 -> c]
and a signature [s] and builds a eta-long version. *)(* For example, if [s = [Keep;Keep;Kill Prop;Keep]] then the output is :
[fun idn ... id1 x x _ x -> (c' 4 3 __ 1)] with [c' = lift 4 c] *)leteta_expansion_signs(ids,c)=letrecabsidsrelsi=function|[]->leta=List.rev_map(functionMLrelx->MLrel(i-x)|a->a)relsinids,MLapp(ast_lift(i-1)c,a)|Keep::l->abs(anonymous::ids)(MLreli::rels)(i+1)l|Killk::l->abs(Dummy::ids)(MLdummyk::rels)(i+1)linabsids[]1s(*s If [s = [b1; ... ; bn]] then [case_expunge] decomposes [e]
in [n] lambdas (with eta-expansion if needed) and removes all dummy lambdas
corresponding to [Kill _] in [s]. *)letcase_expungese=letm=List.lengthsinletn=nb_lamseinletp=ifm<=nthencollect_n_lamsmeelseeta_expansion_sign(List.skipnns)(collect_lamse)inkill_some_lams(List.revs)p(*s [term_expunge] takes a function [fun idn ... id1 -> c]
and a signature [s] and remove dummy lams. The difference
with [case_expunge] is that we here leave one dummy lambda
if all lambdas are logical dummy and the target language is strict. *)letterm_expunges(ids,c)=ifList.is_emptysthencelseletids,c=kill_some_lams(List.revs)(ids,c)inifList.is_emptyids&&lang()!=Haskell&&sign_kinds==UnsafeLogicalSigthenMLlam(Dummy,ast_lift1c)elsenamed_lamsidsc(*s [kill_dummy_args (ids,bl) r t] looks for occurrences of [MLrel r] in [t]
and purge the args of [MLrel r] corresponding to a [Kill] in [bl].
It makes eta-expansion if needed. *)letkill_dummy_args(ids,bl)rt=letm=List.lengthidsinletsign=List.revblinletrecfoundn=function|MLrelr'whenInt.equalr'(r+n)->true|MLmagice->foundne|_->falseinletreckillrecn=function|MLapp(e,a)whenfoundne->letk=max0(m-(List.lengtha))inleta=List.map(killrecn)ainleta=List.map(ast_liftk)ainleta=select_via_blsign(a@(eta_argsk))innamed_lams(List.firstnkids)(MLapp(ast_liftke,a))|ewhenfoundne->leta=select_via_blsign(eta_argsm)innamed_lamsids(MLapp(ast_liftme,a))|e->ast_map_liftkillrecneinkillrec0t(*s The main function for local [dummy] elimination. *)letsign_of_argsa=List.map(functionMLdummyk->Killk|_->Keep)aletreckill_dummy=function|MLfix(i,fi,c)->(tryletk,c=kill_dummy_fixic[]inast_subst(MLfix(i,fi,c))(kill_dummy_argsk1(MLrel1))withImpossible->MLfix(i,fi,Array.mapkill_dummyc))|MLapp(MLfix(i,fi,c),a)->leta=List.mapkill_dummyain(* Heuristics: if some arguments are implicit args, we try to
eliminate the corresponding arguments of the fixpoint *)(tryletk,c=kill_dummy_fixic(sign_of_argsa)inletfake=MLapp(MLrel1,List.map(ast_lift1)a)inletfake'=kill_dummy_argsk1fakeinast_subst(MLfix(i,fi,c))fake'withImpossible->MLapp(MLfix(i,fi,Array.mapkill_dummyc),a))|MLletin(id,MLfix(i,fi,c),e)->(tryletk,c=kill_dummy_fixic[]inlete=kill_dummy(kill_dummy_argsk1e)inMLletin(id,MLfix(i,fi,c),e)withImpossible->MLletin(id,MLfix(i,fi,Array.mapkill_dummyc),kill_dummye))|MLletin(id,c,e)->(tryletk,c=kill_dummy_lams[](kill_dummy_hdc)inlete=kill_dummy(kill_dummy_argsk1e)inletc=kill_dummycinifis_atomiccthenast_substceelseMLletin(id,c,e)withImpossible->MLletin(id,kill_dummyc,kill_dummye))|a->ast_mapkill_dummya(* Similar function, but acting only on head lambdas and let-ins *)andkill_dummy_hd=function|MLlam(id,e)->MLlam(id,kill_dummy_hde)|MLletin(id,c,e)->(tryletk,c=kill_dummy_lams[](kill_dummy_hdc)inlete=kill_dummy_hd(kill_dummy_argsk1e)inletc=kill_dummycinifis_atomiccthenast_substceelseMLletin(id,c,e)withImpossible->MLletin(id,kill_dummyc,kill_dummy_hde))|a->aandkill_dummy_fixics=letn=Array.lengthcinletk,ci=kill_dummy_lamss(kill_dummy_hdc.(i))inletc=Array.copycinc.(i)<-ci;forj=0to(n-1)doc.(j)<-kill_dummy(kill_dummy_argsk(n-i)c.(j))done;k,c(*s Putting things together. *)letnormalizea=leto=optims()inletrecnorma=leta'=ifo.opt_kill_dumthenkill_dummy(simploa)elsesimploainifeq_ml_astaa'thenaelsenorma'innorma(*S Special treatment of fixpoint for pretty-printing purpose. *)letgeneral_optimize_fixfidsnargsmc=letv=Array.initn(funi->i)inletauxi=function|MLreljwhenv.(j-1)>=0->ifast_occurs(j+1)cthenraiseImpossibleelsev.(j-1)<-(-i-1)|_->raiseImpossibleinList.iteriauxargs;letargs_f=List.rev_map(funi->MLrel(i+m+1))(Array.to_listv)inletnew_f=anonym_tmp_lams(MLapp(MLrel(n+m+1),args_f))minletnew_c=named_lamsids(normalize(MLapp((ast_substnew_fc),args)))inMLfix(0,[|f|],[|new_c|])letoptimize_fixa=ifnot(optims()).opt_fix_funthenaelseletids,a'=collect_lamsainletn=List.lengthidsinifInt.equaln0thenaelsematcha'with|MLfix(_,[|f|],[|c|])->letnew_f=MLapp(MLrel(n+1),eta_argsn)inletnew_c=named_lamsids(normalize(ast_substnew_fc))inMLfix(0,[|f|],[|new_c|])|MLapp(a',args)->letm=List.lengthargsin(matcha'with|MLfix(_,_,_)when(test_eta_args_lift0nargs)&¬(ast_occurs_itvl1ma')->a'|MLfix(_,[|f|],[|c|])->(trygeneral_optimize_fixfidsnargsmcwithImpossible->a)|_->a)|_->a(*S Inlining. *)(* Utility functions used in the decision of inlining. *)letml_size_branchsizepv=Array.fold_left(funa(_,_,t)->a+sizet)0pvletrecml_size=function|MLapp(t,l)->List.lengthl+ml_sizet+ml_size_listl|MLlam(_,t)->1+ml_sizet|MLcons(_,_,l)|MLtuplel->ml_size_listl|MLcase(_,t,pv)->1+ml_sizet+ml_size_branchml_sizepv|MLfix(_,_,f)->ml_size_arrayf|MLletin(_,_,t)->ml_sizet|MLmagict->ml_sizet|MLparray(t,def)->ml_size_arrayt+ml_sizedef|MLglob_|MLrel_|MLexn_|MLdummy_|MLaxiom_|MLuint_|MLfloat_|MLstring_->0andml_size_listl=List.fold_left(funat->a+ml_sizet)0landml_size_arraya=Array.fold_left(funat->a+ml_sizet)0aletis_fix=functionMLfix_->true|_->false(*s Strictness *)(* A variable is strict if the evaluation of the whole term implies
the evaluation of this variable. Non-strict variables can be found
behind Match, for example. Expanding a term [t] is a good idea when
it begins by at least one non-strict lambda, since the corresponding
argument to [t] might be unevaluated in the expanded code. *)exceptionToplevelletliftnl=List.map((+)n)lletpopnl=List.map(funx->ifx<=nthenraiseToplevelelsex-n)l(* This function returns a list of de Bruijn indices of non-strict variables,
or raises [Toplevel] if it has an internal non-strict variable.
In fact, not all variables are checked for strictness, only the ones which
de Bruijn index is in the candidates list [cand]. The flag [add] controls
the behaviour when going through a lambda: should we add the corresponding
variable to the candidates? We use this flag to check only the external
lambdas, those that will correspond to arguments. *)letrecnon_strictsaddcand=function|MLlam(id,t)->letcand=lift1candinletcand=ifaddthen1::candelsecandinpop1(non_strictsaddcandt)|MLreln->List.filter(funm->not(Int.equalmn))cand|MLapp(t,l)->letcand=non_strictsfalsecandtinList.fold_left(non_strictsfalse)candl|MLcons(_,_,l)->List.fold_left(non_strictsfalse)candl|MLletin(_,t1,t2)->letcand=non_strictsfalsecandt1inpop1(non_strictsadd(lift1cand)t2)|MLfix(_,i,f)->letn=Array.lengthiinletcand=liftncandinletcand=Array.fold_left(non_strictsfalse)candfinpopncand|MLcase(_,t,v)->(* The only interesting case: for a variable to be non-strict, *)(* it is sufficient that it appears non-strict in at least one branch, *)(* so we make an union (in fact a merge). *)letcand=non_strictsfalsecandtinArray.fold_left(func(i,_,t)->letn=List.lengthiinletcand=liftncandinletcand=popn(non_strictsaddcandt)inList.mergeInt.comparecandc)[]v(* [merge] may duplicates some indices, but I don't mind. *)|MLmagict->non_strictsaddcandt|_->cand(* The real test: we are looking for internal non-strict variables, so we start
with no candidates, and the only positive answer is via the [Toplevel]
exception. *)letis_not_strictt=trylet_=non_strictstrue[]tinfalsewithToplevel->true(*s Inlining decision *)(* [inline_test] answers the following question:
If we could inline [t] (the user said nothing special),
should we inline ?
We expand small terms with at least one non-strict
variable (i.e. a variable that may not be evaluated).
Furthermore we don't expand fixpoints.
Moreover, as mentioned by X. Leroy (bug #2241),
inlining a constant from inside an opaque module might
break types. To avoid that, we require below that
both [r] and its body are globally visible. This isn't
fully satisfactory, since [r] might not be visible (functor),
and anyway it might be interesting to inline [r] at least
inside its own structure. But to be safe, we adopt this
restriction for the moment.
*)openDeclareopsletinline_testrt=ifnot(auto_inline())thenfalseelseletc=matchrwithGlobRef.ConstRefc->c|_->assertfalseinlethas_body=Environ.mem_constantc(Global.env())&&constant_has_body(Global.lookup_constantc)inhas_body&&(lett1=eta_redtinlett2=snd(collect_lamst1)innot(is_fixt2)&&ml_sizet<12&&is_not_strictt)letcon_of_strings=letd,id=Libnames.split_dirpath(dirpath_of_strings)inConstant.make2(ModPath.MPfiled)(Label.of_idid)letmanual_inline_set=List.fold_right(funx->Cset_env.add(con_of_stringx))["Coq.Init.Wf.well_founded_induction_type";"Coq.Init.Wf.well_founded_induction";"Coq.Init.Wf.Acc_iter";"Coq.Init.Wf.Fix_F";"Coq.Init.Wf.Fix";"Coq.Init.Datatypes.andb";"Coq.Init.Datatypes.orb";"Coq.Init.Logic.eq_rec_r";"Coq.Init.Logic.eq_rect_r";"Coq.Init.Specif.proj1_sig";]Cset_env.emptyletmanual_inline=function|GlobRef.ConstRefc->Cset_env.memcmanual_inline_set|_->false(* If the user doesn't say he wants to keep [t], we inline in two cases:
\begin{itemize}
\item the user explicitly requests it
\item [expansion_test] answers that the inlining is a good idea, and
we are free to act (AutoInline is set)
\end{itemize} *)letinlinert=not(to_keepr)(* The user DOES want to keep it *)&¬(is_inline_customr)&&(to_inliner(* The user DOES want to inline it *)||(lang()!=Haskell&&(is_projectionr||is_recursorr||manual_inliner||inline_testrt)))