123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221(**************************************************************************)(* *)(* OCaml *)(* *)(* Stephen Dolan, Jane Street, London *)(* Zesen Qian, Jane Street, London *)(* *)(* Copyright 2024 Jane Street Group LLC *)(* *)(* All rights reserved. This file is distributed under the terms of *)(* the GNU Lesser General Public License version 2.1, with the *)(* special exception on linking described in the file LICENSE. *)(* *)(**************************************************************************)openAllowanceopenSolver_intfmoduleFmt=Format_docmoduleMisc=structincludeMiscmoduleStdlib=Misc_stdlibincludeMisc_stdlibendmoduleMagic_equal(X:Equal):Equalwithtype('a,'b,'c)t=('a,'b,'c)X.t=structtype('a,'b,'d)t=('a,'b,'d)X.tletequal:typea0a1bl0l1r0r1.(a0,b,l0*r0)t->(a1,b,l1*r1)t->(a0,a1)Misc.eqoption=funx0x1->ifObj.reprx0=Obj.reprx1thenSome(Obj.magicMisc.Refl)elseNoneend[@@inline]moduleSolver_mono(H:Hint)(C:Lattices_mono)=structtype('a,'d)hint=|Apply:'dH.Morph.t*('b,'a,'d)C.morph*('b,'d)ahint->('a,'d)hint|Const:'dH.Const.t->('a,'d)hint|Branch:'dbranch*('a,'d)ahint*('a,'d)ahint->('a,'d)hintconstraint'd=_*_[@@ocaml.warning"-62"]and('a,'d)ahint='a*('a,'d)hintconstraint'd=_*_type'aerror={left:('a,left_only)ahint;right:('a,right_only)ahint}(** Hint used internally by the solver. This can be converted to [ahint] by
[populate]. *)moduleComp_hint=structmoduleMorph_hint=struct(** [('a, 'b, 'd) t] is a hint for a morphism from ['a] to ['b] with
allowance ['d] *)type('a,'b,'d)t=|Base:'dH.Morph.t*('a,'b,'d)C.morph->('a,'b,'d)t|Compose:('b,'c,'l*'r)t*('a,'b,'l*'r)t->('a,'c,'l*'r)t|Id:('a,'a,'l*'r)t(** Short-hand for [Base (H.id, C.id)] to save memory *)constraint'd=_*_[@@ocaml.warning"-62"]letrecleft_adjoint:typeabl.H.Pinpoint.t->bC.obj->(a,b,l*allowed)t->H.Pinpoint.t*aC.obj*(b,a,allowed*disallowed)t=funppb_obj->function|Id->pp,b_obj,Id|Base(small_morph_hint,morph)->letpp,small_morph_hint=H.Morph.left_adjointppsmall_morph_hintin(pp,C.srcb_objmorph,Base(small_morph_hint,C.left_adjointb_objmorph))|Compose(f_morph_hint,g_morph_hint)->letmid_pp,mid,f_morph_hint_adj=left_adjointppb_objf_morph_hintinletsrc_pp,src,g_morph_hint_adj=left_adjointmid_ppmidg_morph_hintinsrc_pp,src,Compose(g_morph_hint_adj,f_morph_hint_adj)letrecright_adjoint:typeabr.H.Pinpoint.t->bC.obj->(a,b,allowed*r)t->H.Pinpoint.t*aC.obj*(b,a,disallowed*allowed)t=funppb_obj->function|Id->pp,b_obj,Id|Base(small_morph_hint,morph)->letpp,small_morph_hint=H.Morph.right_adjointppsmall_morph_hintin(pp,C.srcb_objmorph,Base(small_morph_hint,C.right_adjointb_objmorph))|Compose(f_morph_hint,g_morph_hint)->letmid_pp,mid,f_morph_hint_adj=right_adjointppb_objf_morph_hintinletsrc_pp,src,g_morph_hint_adj=right_adjointmid_ppmidg_morph_hintinsrc_pp,src,Compose(g_morph_hint_adj,f_morph_hint_adj)includeMagic_allow_disallow(structtype('a,'b,'d)sided=('a,'b,'d)tconstraint'd='l*'rletrecallow_left:typelabr.(a,b,allowed*r)t->(a,b,l*r)t=funh->matchhwith|Id->Id|Base(morph_hint,morph)->Base(H.Morph.allow_leftmorph_hint,C.allow_leftmorph)|Compose(a_morph_hint,b_morph_hint)->Compose(allow_lefta_morph_hint,allow_leftb_morph_hint)letrecallow_right:typeablr.(a,b,l*allowed)t->(a,b,l*r)t=funh->matchhwith|Id->Id|Base(morph_hint,morph)->Base(H.Morph.allow_rightmorph_hint,C.allow_rightmorph)|Compose(a_morph_hint,b_morph_hint)->Compose(allow_righta_morph_hint,allow_rightb_morph_hint)letrecdisallow_left:typeablr.(a,b,l*r)t->(a,b,disallowed*r)t=funh->matchhwith|Id->Id|Base(morph_hint,morph)->Base(H.Morph.disallow_leftmorph_hint,C.disallow_leftmorph)|Compose(a_morph_hint,b_morph_hint)->Compose(disallow_lefta_morph_hint,disallow_leftb_morph_hint)letrecdisallow_right:typeablr.(a,b,l*r)t->(a,b,l*disallowed)t=funh->matchhwith|Id->Id|Base(morph_hint,morph)->Base(H.Morph.disallow_rightmorph_hint,C.disallow_rightmorph)|Compose(a_morph_hint,b_morph_hint)->Compose(disallow_righta_morph_hint,disallow_rightb_morph_hint)end)letrecpopulate:typebalr.aC.obj->(b,a,l*r)t->(bC.obj->(b,l*r)ahint)->(a,l*r)ahint=funobj_ahintcont->matchhintwith|Id->contobj_a|Base(morph_hint,morph)->letobj_b=C.srcobj_amorphinletahint=contobj_binleta=C.applyobj_amorph(fstahint)ina,Apply(morph_hint,morph,ahint)|Compose(h1,h2)->populateobj_ah1(funobj_mid->populateobj_midh2cont)endtype('a,'d)t=|Apply:('b,'a,'d)Morph_hint.t*('b,'d)t->('a,'d)t|Const:'dH.Const.t*'a->('a,'d)t|Branch:'dbranch*('a,'d)t*('a,'d)t->('a,'d)t|Min:('a,'l*disallowed)t(** Short-hand for [Const (H.Const.min, C.min) to save memory] *)|Max:('a,disallowed*'r)t(** Short-hand for [Const (H.Const.max, C.max) to save memory] *)|Unknown:'a->('a,'l*'r)t(** Short-hand for [Const (H.Const.unknown, a) to save memory] *)constraint'd=_*_[@@ocaml.warning"-62"]includeMagic_allow_disallow(structtype('a,_,'d)sided=('a,'d)tconstraint'd='l*'rletrecallow_left:typealr.(a,allowed*r)t->(a,l*r)t=function|Apply(f_hint,h)->Apply(Morph_hint.allow_leftf_hint,allow_lefth)|Const(h,c)->Const(H.Const.allow_lefth,c)|Branch(Join,h1,h2)->Branch(Join,allow_lefth1,allow_lefth2)|Min->Min|Unknownc->Unknowncletrecallow_right:typealr.(a,l*allowed)t->(a,l*r)t=function|Apply(f_hint,h)->Apply(Morph_hint.allow_rightf_hint,allow_righth)|Const(h,c)->Const(H.Const.allow_righth,c)|Branch(Meet,h1,h2)->Branch(Meet,allow_righth1,allow_righth2)|Max->Max|Unknownc->Unknowncletrecdisallow_left:typealr.(a,l*r)t->(a,disallowed*r)t=function|Apply(f_hint,h)->Apply(Morph_hint.disallow_leftf_hint,disallow_lefth)|Const(h,c)->Const(H.Const.disallow_lefth,c)|Branch(Join,h1,h2)->Branch(Join,disallow_lefth1,disallow_lefth2)|Branch(Meet,h1,h2)->Branch(Meet,disallow_lefth1,disallow_lefth2)|Min->Min|Max->Max|Unknownc->Unknowncletrecdisallow_right:typealr.(a,l*r)t->(a,l*disallowed)t=function|Apply(f_hint,h)->Apply(Morph_hint.disallow_rightf_hint,disallow_righth)|Const(h,c)->Const(H.Const.disallow_righth,c)|Branch(Join,h1,h2)->Branch(Join,disallow_righth1,disallow_righth2)|Branch(Meet,h1,h2)->Branch(Meet,disallow_righth1,disallow_righth2)|Min->Min|Max->Max|Unknownc->Unknowncend)(** This is for removing compositions. This function doesn't contain any
[assert false] as it is just for a straightforward transformation *)letrecpopulate:typealr.aC.obj->(a,l*r)t->(a,l*r)ahint=funobj_a->function|Min->C.minobj_a,ConstH.Const.min|Max->C.maxobj_a,ConstH.Const.max|Unknownc->c,ConstH.Const.unknown|Const(const_hint,const)->const,Constconst_hint|Apply(morph_hint,hint)->Morph_hint.populateobj_amorph_hint(funsrc->populatesrchint)|Branch(b,hint1,hint2)->(letahint1=populateobj_ahint1inletahint2=populateobj_ahint2inmatchbwith|Join->leta=C.joinobj_a(fstahint1)(fstahint2)ina,Branch(Join,ahint1,ahint2)|Meet->leta=C.meetobj_a(fstahint1)(fstahint2)ina,Branch(Meet,ahint1,ahint2))endtypeany_morph=Any_morph:('a,'b,'d)C.morph->any_morphmoduleVarMap=Map.Make(structtypet=int*any_morphletcompare(i1,m1)(i2,m2)=matchInt.comparei1i2with0->comparem1m2|i->iend)(** Map the function to the list, and returns the first [Error] found; Returns
[Ok ()] if no error. *)letfind_error(f:'x->('a,'b)Result.t)(t:'xVarMap.t):('a,'b)Result.t=letr=ref(Ok())inlet_=VarMap.for_all(fun_x->matchfxwith|Ok()->true|Error_ase->r:=e;false)tin!rletvar_map_to_listt=VarMap.fold(fun_axs->a::xs)t[]type'avar={mutablevlower:'almorphvarVarMap.t;(** A list of variables directly under the current variable. Each is a
pair [f] [v], and we have [f v <= u] where [u] is the current
variable. TODO: consider using hashset for quicker deduplication
*)mutableupper:'a;(** The precise upper bound of the variable *)mutableupper_hint:('a,right_only)Comp_hint.t;(** Hints for [upper] *)mutablelower:'a;(** The *conservative* lower bound of the variable. Why conservative:
if a user calls [submode c u] where [c] is some constant and [u]
some variable, we can modify [u.lower] of course. Idealy we should
also modify all [v.lower] where [v] is variable above [u].
However, we only have [vlower] not [vupper]. Therefore, the
[lower] of higher variables are not updated immediately, hence
conservative. Those [lower] of higher variables can be made
precise later on demand, see [zap_to_floor_var_aux].
One might argue for an additional [vupper] field, so that [lower]
are always precise. While this might be doable, we note that the
"hotspot" of the mode solver is to detect conflict, which is
already achieved without precise [lower]. Adding [vupper] and
keeping [lower] precise will come at extra cost. *)(* To summarize, INVARIANT:
- For any variable [v], we have [v.lower <= v.upper].
- Variables that have been fully constrained will have
[v.lower = v.upper]. Note that adding a boolean field indicating that
won't help much.
- For any [v] and [f u \in v.vlower], we have [f u.upper <= v.upper], but not
necessarily [f u.lower <= v.lower]. *)mutablelower_hint:('a,left_only)Comp_hint.t;(** Hints for [lower] *)id:int(** For identification/printing *)}and'blmorphvar=('b,left_only)morphvarand('b,'d)morphvar=|Amorphvar:'avar*('a,'b,'d)C.morph*('a,'b,'d)Comp_hint.Morph_hint.t->('b,'d)morphvarconstraint'd=_*_[@@ocaml.warning"-62"]typeanyvar=Var:'avar->anyvar[@@unboxed]letget_key(Amorphvar(v,m,_))=v.id,Any_morphmmoduleVarSet=Set.Make(Int)typechange=|Cupper:'avar*'a*('a,right_only)Comp_hint.t->change|Clower:'avar*'a*('a,left_only)Comp_hint.t->change|Cvlower:'avar*'almorphvarVarMap.t->changetypechanges=changelistletundo_change=function|Cupper(v,upper,upper_hint)->v.upper<-upper;v.upper_hint<-upper_hint|Clower(v,lower,lower_hint)->v.lower<-lower;v.lower_hint<-lower_hint|Cvlower(v,vlower)->v.vlower<-vlowerletempty_changes=[]letundo_changesl=List.iterundo_changel(** [append_changes l0 l1] returns a log that's equivalent to [l0] followed by
[l1]. *)letappend_changesl0l1=l1@l0type('a,'d)mode=|Amode:'a*('a,'l*_)Comp_hint.t*('a,_*'r)Comp_hint.t->('a,'l*'r)mode|Amodevar:('a,'d)morphvar->('a,'d)mode|Amodejoin:'a*('a,'l*disallowed)Comp_hint.t*('a,'l*disallowed)morphvarVarMap.t->('a,'l*disallowed)mode(** [Amodejoin a c [mv0, mv1, ..]] represents
[a join mv0 join mv1 join ..] with the hint [c] for [a] (the
morphvars have their own hints). *)|Amodemeet:'a*('a,disallowed*'r)Comp_hint.t*('a,disallowed*'r)morphvarVarMap.t->('a,disallowed*'r)mode(** [Amodemeet a c [mv0, mv1, ..]] represents
[a meet mv0 meet mv1 meet ..] with the hint [c] for [a] (the
morphvars have their own hints). *)constraint'd=_*_[@@ocaml.warning"-62"](** Prints a mode variable, including the set of variables below it
(recursively). To handle cycles, [traversed] is the set of variables that
we have already printed and will be skipped. An example of cycle:
Consider a lattice A containing three elements 0, 1, and 2 with the linear
lattice structure: 0 < 1 < 2. Furthermore, we define a morphism
{v
f : A -> A
f 0 = 0
f 1 = 2
f 2 = 2
v}
Note that f has a left right, which allows us to write f on the LHS of
submode. Say we create a unconstrained variable [x], and invoke submode:
[f x <= x] this would result in adding (f, x) into the [vlower] of [x].
That is, there will be a self-loop on [x]. *)letrecprint_var:typea.?traversed:VarSet.t->aC.obj->_->avar->_=fun?traversedobjppfv->Fmt.fprintfppf"modevar#%x[%a .. %a]"v.id(C.printobj)v.lower(C.printobj)v.upper;matchtraversedwith|None->()|Sometraversed->ifVarSet.memv.idtraversedthen()elselettraversed=VarSet.addv.idtraversedinletp=print_morphvar~traversedobjinFmt.fprintfppf"{%a}"(Fmt.pp_print_listp)(var_map_to_listv.vlower)andprint_morphvar:typealr.?traversed:VarSet.t->aC.obj->_->(a,l*r)morphvar->_=fun?traverseddstppf(Amorphvar(v,f,_))->letsrc=C.srcdstfinFmt.fprintfppf"%a(%a)"(C.print_morphdst)f(print_var?traversedsrc)vletprint_raw:typealr.?verbose:bool->aC.obj->Fmt.formatter->(a,l*r)mode->unit=fun?(verbose=false)(obj:aC.obj)ppfm->lettraversed=ifverbosethenSomeVarSet.emptyelseNoneinmatchmwith|Amode(a,_,_)->C.printobjppfa|Amodevarmv->print_morphvar?traversedobjppfmv|Amodejoin(a,_,mvs)->Fmt.fprintfppf"join(%a,%a)"(C.printobj)a(Fmt.pp_print_list~pp_sep:(funppf()->Fmt.fprintfppf",")(print_morphvar?traversedobj))(var_map_to_listmvs)|Amodemeet(a,_,mvs)->Fmt.fprintfppf"meet(%a,%a)"(C.printobj)a(Fmt.pp_print_list~pp_sep:(funppf()->Fmt.fprintfppf",")(print_morphvar?traversedobj))(var_map_to_listmvs)moduleMorphvar=Magic_allow_disallow(structtype('a,_,'d)sided=('a,'d)morphvarconstraint'd='l*'rletallow_left:typealr.(a,allowed*r)morphvar->(a,l*r)morphvar=function|Amorphvar(v,m,h)->Amorphvar(v,C.allow_leftm,Comp_hint.Morph_hint.allow_lefth)letallow_right:typealr.(a,l*allowed)morphvar->(a,l*r)morphvar=function|Amorphvar(v,m,h)->Amorphvar(v,C.allow_rightm,Comp_hint.Morph_hint.allow_righth)letdisallow_left:typealr.(a,l*r)morphvar->(a,disallowed*r)morphvar=function|Amorphvar(v,m,h)->Amorphvar(v,C.disallow_leftm,Comp_hint.Morph_hint.disallow_lefth)letdisallow_right:typealr.(a,l*r)morphvar->(a,l*disallowed)morphvar=function|Amorphvar(v,m,h)->Amorphvar(v,C.disallow_rightm,Comp_hint.Morph_hint.disallow_righth)end)includeMagic_allow_disallow(structtype('a,_,'d)sided=('a,'d)modeconstraint'd='l*'rletallow_left:typealr.(a,allowed*r)mode->(a,l*r)mode=function|Amode(c,h_lower,h_upper)->Amode(c,Comp_hint.allow_lefth_lower,h_upper)|Amodevarmv->Amodevar(Morphvar.allow_leftmv)|Amodejoin(c,h,mvs)->Amodejoin(c,Comp_hint.allow_lefth,VarMap.mapMorphvar.allow_leftmvs)letallow_right:typealr.(a,l*allowed)mode->(a,l*r)mode=function|Amode(c,h_lower,h_upper)->Amode(c,h_lower,Comp_hint.allow_righth_upper)|Amodevarmv->Amodevar(Morphvar.allow_rightmv)|Amodemeet(c,h,mvs)->Amodemeet(c,Comp_hint.allow_righth,VarMap.mapMorphvar.allow_rightmvs)letdisallow_left:typealr.(a,l*r)mode->(a,disallowed*r)mode=function|Amode(c,h_lower,h_upper)->Amode(c,Comp_hint.disallow_lefth_lower,h_upper)|Amodevarmv->Amodevar(Morphvar.disallow_leftmv)|Amodejoin(c,h,mvs)->Amodejoin(c,Comp_hint.disallow_lefth,VarMap.mapMorphvar.disallow_leftmvs)|Amodemeet(c,h,mvs)->Amodemeet(c,h,VarMap.mapMorphvar.disallow_leftmvs)letdisallow_right:typealr.(a,l*r)mode->(a,l*disallowed)mode=function|Amode(c,h_lower,h_upper)->Amode(c,h_lower,Comp_hint.disallow_righth_upper)|Amodevarmv->Amodevar(Morphvar.disallow_rightmv)|Amodejoin(c,h,mvs)->Amodejoin(c,h,VarMap.mapMorphvar.disallow_rightmvs)|Amodemeet(c,h,mvs)->Amodemeet(c,Comp_hint.disallow_righth,VarMap.mapMorphvar.disallow_rightmvs)end)letmlowerdst(Amorphvar(var,morph,_hint))=C.applydstmorphvar.lowerletmlower_hint(Amorphvar(var,_morph,hint)):_Comp_hint.t=Apply(Comp_hint.Morph_hint.disallow_righthint,Comp_hint.disallow_rightvar.lower_hint)letmupperdst(Amorphvar(var,morph,_hint))=C.applydstmorphvar.upperletmupper_hint(Amorphvar(var,_morph,hint)):_Comp_hint.t=Apply(Comp_hint.Morph_hint.disallow_lefthint,Comp_hint.disallow_leftvar.upper_hint)letmin(typea)(obj:aC.obj)=letc=C.minobjinAmode(c,Min,Unknownc)letmax(typea)(obj:aC.obj)=letc=C.maxobjinAmode(c,Unknownc,Max)letof_const_obj?hinta=lethint:_Comp_hint.t=matchhintwithNone->Unknowna|Someh->Const(h,a)inAmode(a,hint,hint)letapply_morphvardstmorphmorph_hint(Amorphvar(var,morph',morph'_hint))=Amorphvar(var,C.composedstmorphmorph',Compose(morph_hint,morph'_hint))letapply:typeablr.bC.obj->?hint:(l*r)H.Morph.t->(a,b,l*r)C.morph->(a,l*r)mode->(b,l*r)mode=fundst?(hint=H.Morph.unknown)morphm->lethint=Comp_hint.Morph_hint.Base(hint,morph)inmatchmwith|Amode(a,a_hint_lower,a_hint_upper)->Amode(C.applydstmorpha,Apply(Comp_hint.Morph_hint.disallow_righthint,Comp_hint.disallow_righta_hint_lower),Apply(Comp_hint.Morph_hint.disallow_lefthint,Comp_hint.disallow_lefta_hint_upper))|Amodevarmv->Amodevar(apply_morphvardstmorphhintmv)|Amodejoin(a,a_hint,vs)->lethint=Comp_hint.Morph_hint.disallow_righthintinletvs=VarMap.fold(fun_mvacc->letmv=apply_morphvardstmorphhintmvinVarMap.add(get_keymv)mvacc)vsVarMap.emptyinAmodejoin(C.applydstmorpha,Apply(hint,a_hint),vs)|Amodemeet(a,a_hint,vs)->lethint=Comp_hint.Morph_hint.disallow_lefthintinletvs=VarMap.fold(fun_mvacc->letmv=apply_morphvardstmorphhintmvinVarMap.add(get_keymv)mvacc)vsVarMap.emptyinAmodemeet(C.applydstmorpha,Apply(hint,a_hint),vs)moduleUnhint=structtype('a,'d)t=|Unhint:('b,'a,'d)C.morph*('b,'d)mode->('a,'d)tconstraint'd='l*'r[@@ocaml.warning"-62"]letunhint:typealr.(a,l*r)mode->(a,l*r)t=funm->Unhint(C.id,m)lethint:typealr.aC.obj->?hint:(l*r)H.Morph.t->(a,l*r)t->(a,l*r)mode=fundst?hint(Unhint(morph,m))->applydst?hintmorphmletapply:typeablr.bC.obj->(a,b,l*r)C.morph->(a,l*r)t->(b,l*r)t=fundstmorph(Unhint(f,m))->Unhint(C.composedstmorphf,m)endlethint_biased_joinobjaa_hintbb_hint=(* A version of [hint_join] that assumes that a <= b is false,
so won't perform this check. *)ifC.leobjbathena_hintelseComp_hint.Branch(Join,a_hint,b_hint)lethint_joinobjaa_hintbb_hint=(* This just provides a minor optimization to avoid having unnecessarily-nested Branch hints.
When a join, [join a b], is performed, the hint we use for the result should be a [Branch]
if both arguments are relevant, but if we have that [a <= b] in the lattice structure then
we don't really need to consider the hint for [a], and vice versa for [b]'s hint. *)ifC.leobjabthenb_hintelsehint_biased_joinobjaa_hintbb_hintlethint_biased_meetobjaa_hintbb_hint=(* A version of [hint_meet] that assumes that a <= b is false,
so won't perform this check. *)ifC.leobjbathenb_hintelseComp_hint.Branch(Meet,a_hint,b_hint)lethint_meetobjaa_hintbb_hint=(* Comments for [hint_join] apply here similarly but the other way round
(since this is for meet, not join) *)ifC.leobjabthena_hintelsehint_biased_meetobjaa_hintbb_hint(** Calling [update_lower ~log obj v a a_hint] assumes that
[not (a <= v.lower)]. Arguments are not checked and used directly. They
must satisfy the INVARIANT listed above. *)letupdate_lower(typea)~log(obj:aC.obj)vaa_hint=(matchlogwith|None->()|Somelog->log:=Clower(v,v.lower,v.lower_hint)::!log);v.lower<-C.joinobjv.lowera;v.lower_hint<-hint_biased_joinobjaa_hintv.lowerv.lower_hint(** Calling [update_upper ~log obj v a a_hint] assumes that
[not (v.upper <= a)]. Arguments are not checked and used directly. They
must satisfy the INVARIANT listed above. *)letupdate_upper(typea)~log(obj:aC.obj)vaa_hint=(matchlogwith|None->()|Somelog->log:=Cupper(v,v.upper,v.upper_hint)::!log);v.upper<-C.meetobjv.uppera;v.upper_hint<-hint_biased_meetobjv.upperv.upper_hintaa_hint(** Arguments are not checked and used directly. They must satisfy the
INVARIANT listed above. *)letset_vlower~logvvlower=(matchlogwith|None->()|Somelog->log:=Cvlower(v,v.vlower)::!log);v.vlower<-vlowerletsubmode_cv:typea.log:_->H.Pinpoint.t->aC.obj->a->(a,left_only)Comp_hint.t->avar->(unit,a*(a,right_only)Comp_hint.t)Result.t=fun(typea)~log_pp(obj:aC.obj)a'a'_hintv->ifC.leobja'v.lowerthenOk()elseifnot(C.leobja'v.upper)thenError(v.upper,v.upper_hint)else(update_lower~logobjva'a'_hint;ifC.leobjv.upperv.lowerthenset_vlower~logvVarMap.empty;Ok())letsubmode_cmv:typeal.log:_->H.Pinpoint.t->aC.obj->a->(a,left_only)Comp_hint.t->(a,l*allowed)morphvar->(unit,a*(a,right_only)Comp_hint.t)Result.t=fun~logppobjaa_hint(Amorphvar(v,f,f_hint)asmv)->letmlower=mlowerobjmvinletmupper=mupperobjmvinletmupper_hint=mupper_hintmvinifC.leobjamlowerthenOk()elseifnot(C.leobjamupper)thenError(mupper,mupper_hint)else(* At this point we know [a <= f v], therefore [a] is in the downward
closure of [f]'s image. Therefore, asking [a <= f v] is equivalent to
asking [f' a <= v]. *)letf'=C.left_adjointobjfinletsrc_pp,src,f'_hint=Comp_hint.Morph_hint.left_adjointppobjf_hintinleta'=C.applysrcf'ainleta'_hint=Comp_hint.Apply(f'_hint,a_hint)insubmode_cv~logsrc_ppsrca'a'_hintv|>Result.get_ok;Ok()(** Returns [Ok ()] if success; [Error x] if failed, and [x] is the next best
(read: strictly higher) guess to replace the constant argument that MIGHT
succeed. *)letrecsubmode_vc:typea.log:_->H.Pinpoint.t->aC.obj->avar->a->(a,right_only)Comp_hint.t->(unit,a*(a,left_only)Comp_hint.t)Result.t=fun(typea)~logpp(obj:aC.obj)va'a'_hint->ifC.leobjv.uppera'thenOk()elseifnot(C.leobjv.lowera')thenError(v.lower,v.lower_hint)else(update_upper~logobjva'a'_hint;letr=v.vlower|>find_error(funmu->letr=submode_mvc~logppobjmua'a'_hintin(ifResult.is_okrthen(* Optimization: update [v.lower] based on [mlower u].*)letmu_lower=mlowerobjmuinletmu_lower_hint=mlower_hintmuinifnot(C.leobjmu_lowerv.lower)thenupdate_lower~logobjvmu_lowermu_lower_hint);r)inifC.leobjv.upperv.lowerthenset_vlower~logvVarMap.empty;r)andsubmode_mvc:'a'r.log:changelistrefoption->H.Pinpoint.t->'aC.obj->('a,allowed*'r)morphvar->'a->('a,right_only)Comp_hint.t->(unit,'a*('a,left_only)Comp_hint.t)Result.t=fun~logppobj(Amorphvar(v,f,f_hint)asmv)aa_hint->(* See [submode_cmv] for why we need the following seemingly redundant
lines. *)letmupper=mupperobjmvinletmlower=mlowerobjmvinletmlower_hint=mlower_hintmvinifC.leobjmupperathenOk()elseifnot(C.leobjmlowera)thenError(mlower,mlower_hint)elseletf'=C.right_adjointobjfinletsrc_pp,src,f'_hint=Comp_hint.Morph_hint.right_adjointppobjf_hintinleta'=C.applysrcf'ainleta'_hint=Comp_hint.Apply(f'_hint,a_hint)in(* If [mlower] was precise, then the check
[not (C.le obj (mlower obj mv) a)] should guarantee the following call
to return [Ok ()]. However, [mlower] is not precise *)(* not using [Result.map_error] to avoid allocating closure *)matchsubmode_vc~logsrc_ppsrcva'a'_hintwith|Ok()->Ok()|Error(e,e_hint)->Error(C.applyobjfe,Apply(Comp_hint.Morph_hint.disallow_rightf_hint,e_hint))leteq_morphvar:typeal0r0l1r1.(a,l0*r0)morphvar->(a,l1*r1)morphvar->bool=fun(Amorphvar(v0,f0,_f0_hint)asmv0)(Amorphvar(v1,f1,_f1_hint)asmv1)->(* To align l0/l1, r0/r1; The existing disallow_left/right] is for [mode],
not [morphvar]. *)Morphvar.(disallow_left(disallow_rightmv0)==disallow_left(disallow_rightmv1))||matchC.eq_morphf0f1withNone->false|SomeRefl->v0==v1letsubmode_mvmv(typea)~log(pp:H.Pinpoint.t)(dst:aC.obj)(Amorphvar(v,f,f_hint)asmv)(Amorphvar(u,g,g_hint)asmu)=ifC.ledst(mupperdstmv)(mlowerdstmu)thenOk()elseifeq_morphvarmvmuthenOk()elseletmuupper=mupperdstmuinletmuupper_hint=mupper_hintmuin(* The call f v <= g u translates to three steps:
1. f v <= g u.upper
2. f v.lower <= g u
3. adding g' (f v) to the u.vlower, where g' is the left adjoint of g.
*)matchsubmode_mvc~logppdstmvmuuppermuupper_hintwith|Error(a,a_hint)->Error(a,a_hint,muupper,muupper_hint)|Ok()->(letmvlower=mlowerdstmvinletmvlower_hint=mlower_hintmvinmatchsubmode_cmv~logppdstmvlowermvlower_hintmuwith|Error(a,a_hint)->Error(mvlower,mvlower_hint,a,a_hint)|Ok()->(* At this point, we know that [f v <= g u.upper], which means [f v]
lies within the downward closure of [g]'s image. Therefore, asking [f
v <= g u] is equivalent to asking [g' f v <= u] *)letg'=C.left_adjointdstginlet_,src,g'_hint=Comp_hint.Morph_hint.left_adjointppdstg_hintinletg'f=C.composesrcg'(C.disallow_rightf)inletg'f_hint=Comp_hint.Morph_hint.Compose(g'_hint,Comp_hint.Morph_hint.disallow_rightf_hint)inletx=Amorphvar(v,g'f,g'f_hint)inletkey=get_keyxinifnot(VarMap.memkeyu.vlower)thenset_vlower~logu(VarMap.addkeyxu.vlower);Ok())letvars=ref(0,[])letfresh?upper?upper_hint?lower?lower_hint?vlowerobj=letid,l=!varsinletupper,upper_hint=matchupper,upper_hintwith|None,None->C.maxobj,Comp_hint.Max|None,Some_->assertfalse|Someupper,None->upper,Comp_hint.Unknownupper|Someupper,Someupper_hint->upper,upper_hintinletlower,lower_hint=matchlower,lower_hintwith|None,None->C.minobj,Comp_hint.Min|None,Some_->assertfalse|Somelower,None->lower,Comp_hint.Unknownlower|Somelower,Somelower_hint->lower,lower_hintinletvlower=Option.valuevlower~default:VarMap.emptyinletvar={upper;upper_hint;lower;lower_hint;vlower;id}invars:=id+1,Varvar::l;varletunhint_morphvar(Amorphvar(v,f,_))=Amorphvar(v,f,Comp_hint.Morph_hint.Base(H.Morph.unknown,f))letunhint_varv=v.upper_hint<-Comp_hint.Unknownv.upper;v.lower_hint<-Comp_hint.Unknownv.lower;v.vlower<-VarMap.mapunhint_morphvarv.vlowerleterase_hints()=let_,l=!varsinList.iter(fun(Varv)->unhint_varv)ltype('a,'d)hint_raw=('a,'d)Comp_hint.ttype'aerror_raw={left:'a;left_hint:('a,left_only)hint_raw;right:'a;right_hint:('a,right_only)hint_raw}letsubmode(typearl)(pp:H.Pinpoint.t)(obj:aC.obj)(a:(a,allowed*r)mode)(b:(a,l*allowed)mode)~log=letsubmode_cc~log:__ppobjleftleft_hintrightright_hint=ifC.leobjleftrightthenOk()elseError{left;left_hint;right;right_hint}inletsubmode_mvc~logppobjvrightright_hint=Result.map_error(fun(left,left_hint)->{left;left_hint;right;right_hint})(submode_mvc~logppobjvrightright_hint)inletsubmode_cmv~logppobjleftleft_hintv=Result.map_error(fun(right,right_hint)->{left;left_hint;right;right_hint})(submode_cmv~logppobjleftleft_hintv)inletsubmode_mvmv~logppobjvu=Result.map_error(fun(left,left_hint,right,right_hint)->{left;left_hint;right;right_hint})(submode_mvmv~logppobjvu)inmatcha,bwith|(Amode(left,left_hint_lower,_left_hint_upper),Amode(right,_right_hint_lower,right_hint_upper))->submode_cc~logppobjleft(Comp_hint.disallow_rightleft_hint_lower)right(Comp_hint.disallow_leftright_hint_upper)|Amodevarv,Amode(right,_right_hint_lower,right_hint_upper)->submode_mvc~logppobjvright(Comp_hint.disallow_leftright_hint_upper)|Amode(left,left_hint_lower,_left_hint_upper),Amodevarv->submode_cmv~logppobjleft(Comp_hint.disallow_rightleft_hint_lower)v|Amodevarv,Amodevaru->submode_mvmv~logppobjvu|Amode(a,a_hint_lower,_a_hint_upper),Amodemeet(b,b_hint,mvs)->Result.bind(submode_cc~logppobja(Comp_hint.disallow_righta_hint_lower)bb_hint)(fun()->find_error(funmv->submode_cmv~logppobja(Comp_hint.disallow_righta_hint_lower)mv)mvs)|Amodevarmv,Amodemeet(b,b_hint,mvs)->Result.bind(submode_mvc~logppobjmvbb_hint)(fun()->find_error(funmv'->submode_mvmv~logppobjmvmv')mvs)|Amodejoin(a,a_hint,mvs),Amode(b,_b_hint_lower,b_hint_upper)->Result.bind(submode_cc~logppobjaa_hintb(Comp_hint.disallow_leftb_hint_upper))(fun()->find_error(funmv'->submode_mvc~logppobjmv'b(Comp_hint.disallow_leftb_hint_upper))mvs)|Amodejoin(a,a_hint,mvs),Amodevarmv->Result.bind(submode_cmv~logppobjaa_hintmv)(fun()->find_error(funmv'->submode_mvmv~logppobjmv'mv)mvs)|Amodejoin(a,a_hint,mvs),Amodemeet(b,b_hint,mus)->(* TODO: mabye create a intermediate variable? *)Result.bind(submode_cc~logppobjaa_hintbb_hint)(fun()->Result.bind(find_error(funmv->submode_mvc~logppobjmvbb_hint)mvs)(fun()->Result.bind(find_error(funmu->submode_cmv~logppobjaa_hintmu)mus)(fun()->find_error(funmu->find_error(funmv->submode_mvmv~logppobjmvmu)mvs)mus)))letpopulate_hintobjahint=letahint=Comp_hint.populateobjhintinassert(Misc.Le_result.equal~le:(C.leobj)(fstahint)a);ahintletpopulate_errorobj{left;left_hint;right;right_hint}=letleft=populate_hintobjleftleft_hintinletright=populate_hintobjrightright_hintin{left;right}letcons_dedupxxs=VarMap.add(get_keyx)xxsletunion_prefer_leftt0t1=VarMap.union(fun_a_b->Somea)t0t1letjoin(typear)objl=letrecloop:a->(a,allowed*disallowed)Comp_hint.t->(a,allowed*disallowed)morphvarVarMap.t->(a,allowed*r)modelist->(a,allowed*disallowed)mode=funaa_hint_lowermvsrest->ifC.leobj(C.maxobj)athen(* In this case, [a] is the maximum element, and we can use
[a_hint_lower] as the output's lower hint *)Amode(a,a_hint_lower,Max)elsematchrestwith|[]->Amodejoin(a,a_hint_lower,mvs)|mv::xs->(matchdisallow_rightmvwith|Amode(b,b_hint_lower,_b_hint_upper)->loop(C.joinobjab)(hint_joinobjaa_hint_lowerb(Comp_hint.disallow_rightb_hint_lower))mvsxs(* some minor optimization: if [a] is lower than [mlower mv], we
should keep the latter instead. This helps to fail early in
[submode] *)|Amodevarmv->letmvlower=mlowerobjmvinloop(C.joinobjamvlower)(hint_joinobjaa_hint_lowermvlower(mlower_hintmv))(cons_dedupmvmvs)xs|Amodejoin(b,b_hint,mvs')->loop(C.joinobjab)(hint_joinobjaa_hint_lowerbb_hint)(union_prefer_leftmvs'mvs)xs)inloop(C.minobj)MinVarMap.emptylletmeet(typeal)objl=letrecloop:a->(a,disallowed*allowed)Comp_hint.t->(a,disallowed*allowed)morphvarVarMap.t->(a,l*allowed)modelist->(a,disallowed*allowed)mode=funaa_hint_uppermvsrest->ifC.leobja(C.minobj)then(* In this case, [a] is the minimum element, and we can use [a_hint_upper]
as the output's upper hint *)Amode(a,Min,a_hint_upper)elsematchrestwith|[]->Amodemeet(a,a_hint_upper,mvs)|mv::xs->(matchdisallow_leftmvwith|Amode(b,_b_hint_lower,b_hint_upper)->loop(C.meetobjab)(hint_meetobjaa_hint_upperb(Comp_hint.disallow_leftb_hint_upper))mvsxs(* some minor optimization: if [a] is higher than [mupper mv], we should keep the latter instead. This helps to fail early in [submode_log] *)|Amodevarmv->letmvupper=mupperobjmvinloop(C.meetobjamvupper)(hint_meetobjaa_hint_uppermvupper(mupper_hintmv))(cons_dedupmvmvs)xs|Amodemeet(b,b_hint,mvs')->loop(C.meetobjab)(hint_meetobjaa_hint_upperbb_hint)(union_prefer_leftmvs'mvs)xs)inloop(C.maxobj)MaxVarMap.emptylletget_loose_ceil:typealr.aC.obj->(a,l*r)mode->a=funobjm->matchmwith|Amode(a,_a_hint_lower,_a_hint_upper)->a|Amodevarmv->mupperobjmv|Amodemeet(a,_a_hint,mvs)->VarMap.fold(fun_mvacc->C.meetobjacc(mupperobjmv))mvsa|Amodejoin(a,_a_hint,mvs)->VarMap.fold(fun_mvacc->C.joinobjacc(mupperobjmv))mvsaletget_loose_floor:typealr.aC.obj->(a,l*r)mode->a=funobjm->matchmwith|Amode(a,_a_hint_lower,_a_hint_upper)->a|Amodevarmv->mlowerobjmv|Amodejoin(a,_a_hint,mvs)->VarMap.fold(fun_mvacc->C.joinobjacc(mupperobjmv))mvsa|Amodemeet(a,_a_hint,mvs)->VarMap.fold(fun_mvacc->C.meetobjacc(mlowerobjmv))mvsa(* Due to our biased implementation, the ceil is precise. *)letget_ceil=get_loose_ceilletzap_to_ceil:typeal.aC.obj->(a,l*allowed)mode->log:_->a=funobjm~log->letceil=get_ceilobjmin(* We want a hint to explain why [ceil] is high. However, we only have hint
for why [ceil] is low. There is no good hint to use. *)submodeH.Pinpoint.unknownobj(Amode(ceil,Unknownceil,Unknownceil))m~log|>Result.get_ok;ceil(** Zap [mv] to its lower bound. Returns the [log] of the zapping, in case the
caller are only interested in the lower bound and wants to reverse the
zapping.
As mentioned in [var], [mlower mv] is not precise; to get the precise
lower bound of [mv], we call [submode mv (mlower mv)]. This will propagate
to all its children, which might fail because some children's lower bound
[a] is more up-to-date than [mv]. In that case, we call [submode mv a]. We
repeat this process until no failure, and we will get the precise lower
bound.
The loop is guaranteed to terminate, because for each iteration our
guessed lower bound is strictly higher; and all lattices are finite. *)letzap_to_floor_morphvar_aux(typear)(obj:aC.obj)(mv:(a,allowed*r)morphvar)=letreclooplower=letlog=refempty_changesin(* We want a hint for why [lower] is low, but we only have hint for why [lower] is
high. There is no good hint to use. *)letr=submode_mvc~log:(Somelog)H.Pinpoint.unknownobjmvlower(Unknownlower)inmatchrwith|Ok()->!log,lower|Error(a,_)->undo_changes!log;loop(C.joinobjalower)inloop(mlowerobjmv)(** Zaps a morphvar to its floor and returns the floor. [commit] could be
[Some log], in which case the zapping is appended to [log]; it could also
be [None], in which case the zapping is reverted. The latter is useful
when the caller only wants to know the floor without zapping. *)letzap_to_floor_morphvarobjmv~commit=letlog_,lower=zap_to_floor_morphvar_auxobjmvin(matchcommitwith|None->undo_changeslog_|Somelog->log:=append_changes!loglog_);lowerletzap_to_floor:typear.aC.obj->(a,allowed*r)mode->log:_->a=funobjm~log->matchmwith|Amode(a,_a_hint_lower,_a_hint_upper)->a|Amodevarmv->zap_to_floor_morphvarobjmv~commit:log|Amodejoin(a,_a_hint,mvs)->letfloor=VarMap.fold(fun_mvacc->C.joinobjacc(zap_to_floor_morphvarobjmv~commit:None))mvsainVarMap.iter(fun_mv->(* We want a hint for why [floor] is low. However, we only have hint
for why [floor] is high. There is no hint to use. *)submode_mvcH.Pinpoint.unknownobjmvfloor(Unknownfloor)~log|>Result.get_ok)mvs;floorletget_floor:typear.aC.obj->(a,allowed*r)mode->a=funobjm->matchmwith|Amode(a,_a_hint_lower,_a_hint_upper)->a|Amodevarmv->zap_to_floor_morphvarobjmv~commit:None|Amodejoin(a,_a_hint,mvs)->VarMap.fold(fun_mvacc->C.joinobjacc(zap_to_floor_morphvarobjmv~commit:None))mvsaletto_const_exnobjm=letfloor=get_floorobjminletceil=get_ceilobjminifC.leobjceilfloorthenceilelseMisc.fatal_errorf"mode is not tight: floor = %a, ceil = %a"(Fmt.compat(C.printobj))floor(Fmt.compat(C.printobj))ceilletprint:typealr.?verbose:bool->aC.obj->Fmt.formatter->(a,l*r)mode->unit=fun?verbose(obj:aC.obj)ppfm->letceil=get_loose_ceilobjminletfloor=get_loose_floorobjminifC.leobjceilfloorthenC.printobjppfceilelseprint_raw?verboseobjppfmletnewvarobj=Amodevar(Amorphvar(freshobj,C.id,Id))letnewvar_above(typear)(obj:aC.obj)(m:(a,allowed*r)mode)=matchdisallow_rightmwith|Amode(a,a_hint_lower,_a_hint_upper)->ifC.leobj(C.maxobj)athenAmode(a,Comp_hint.allow_lefta_hint_lower,Max),falseelse(Amodevar(Amorphvar(fresh~lower:a~lower_hint:(Comp_hint.disallow_righta_hint_lower)obj,C.id,Id)),true)|Amodevarmv->(* [~lower] is not precise (because [mlower mv] is not precise), but
it doesn't need to be *)(Amodevar(Amorphvar(fresh~lower:(mlowerobjmv)~lower_hint:(mlower_hintmv)~vlower:(VarMap.singleton(get_keymv)mv)obj,C.id,Id)),true)|Amodejoin(a,a_hint,mvs)->(* [~lower] is not precise here, but it doesn't need to be *)(Amodevar(Amorphvar(fresh~lower:a~lower_hint:a_hint~vlower:mvsobj,C.id,Id)),true)letnewvar_below(typeal)(obj:aC.obj)(m:(a,l*allowed)mode)=matchdisallow_leftmwith|Amode(a,_a_hint_lower,a_hint_upper)->ifC.leobja(C.minobj)thenAmode(a,Min,Comp_hint.allow_righta_hint_upper),falseelse(Amodevar(Amorphvar(fresh~upper:a~upper_hint:(Comp_hint.disallow_lefta_hint_upper)obj,C.id,Id)),true)|Amodevarmv->letu=freshobjinletmu=Amorphvar(u,C.id,Id)insubmode_mvmvH.Pinpoint.unknownobj~log:Nonemumv|>Result.get_ok;allow_left(Amodevarmu),true|Amodemeet(a,a_hint,mvs)->letu=freshobjinletmu=Amorphvar(u,C.id,Id)insubmode_mvcH.Pinpoint.unknownobj~log:Nonemuaa_hint|>Result.get_ok;VarMap.iter(fun_mv->submode_mvmvH.Pinpoint.unknownobj~log:Nonemumv|>Result.get_ok)mvs;allow_left(Amodevarmu),trueend[@@inlinealways]