123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850(* elpi: embedded lambda prolog interpreter *)(* license: GNU Lesser General Public License Version 2.1 or later *)(* ------------------------------------------------------------------------- *)openElpi_util.UtilopenElpi_parser.AstopenCompiler_datamoduleC=ConstantsmoduleUF=Symbol.UFmoduleS=Elpi_runtime.Data.Global_symbolsmoduleFormat=structincludeFormatleteprintf=fune->Format.ifprintfFormat.err_formattereendleterror~locmsg=error~loc("DetCheck: "^msg)typedtype=|Det(** -> for deterministic preds *)|Rel(** -> for relational preds *)|Expofdtypelist(** -> for kinds like list, int, string *)|BVarofF.t(** -> in predicates like: std.exists or in parametric type abbreviations. *)|ArrowofMode.t*Structured.variadic*dtype*dtype(** -> abstractions *)|Any[@@derivingshow,ord]moduleGood_call:sig(* NOTE:
For a constructor with a non-polymorphic type, the inferred determinacy of the term
must match the expected one exactly.
For a constructor with a polymorphic type, the inferred determinacy of the term
can differ from the expected one.
For example, see test90.elpi, test91.elpi, test92.elpi,
test93.elpi, and test94.elpi.
*)typeoffending_term={exp:dtype;found:dtype;term:ScopedTerm.t}typet[@@derivingshow]valinit:unit->tvalmake:exp:dtype->found:dtype->ScopedTerm.t->tvalis_good:t->boolvalis_wrong:t->boolvalget:t->offending_termlistvalset:t->t->unitvalset_wrong:t->exp:dtype->found:dtype->ScopedTerm.t->unitvalset_good:t->unitvalprepend:t->exp:dtype->found:dtype->ScopedTerm.t->tend=structtypeoffending_term={exp:dtype;found:dtype;term:ScopedTerm.t}typet=offending_termlistrefletinit():t=ref[]letmake~exp~foundterm:t=ref@@[{exp;found;term}]letis_good(x:t)=!x=[]letis_wrong(x:t)=!x<>[]letget(x:t)=!xletset(t1:t)(t2:t)=t1:=!t2letset_wrong(t1:t)~exp~foundterm=t1:=[{exp;found;term}]letprepend(t1:t)~exp~foundterm=match!t1with|x::_whenx.term==term->t1|_->t1:={exp;found;term}::!t1;t1letset_goodt=t:=[]letshow(x:t)=match!xwith[]->"true"|e::_->Format.asprintf"false (%a)"Loc.ppe.term.locletppfmtx=Format.fprintffmt"%s"(showx)endexceptionDetErrorof(ScopedTerm.constoption*Good_call.t)exceptionFatalDetErrorof(ScopedTerm.constoption*Good_call.t)exceptionRelationalBodyof(ScopedTerm.constoption*Good_call.t)exceptionCastErrorof(ScopedTerm.constoption*Good_call.t)exceptionKErrorof([`UVarofScopedTerm.uvar|`ConstofScopedTerm.const]*Good_call.t)exceptionLoadFlexClauseofScopedTerm.tletrecpp_dtypefmt=function|Det->Format.fprintffmt"Det"|Rel->Format.fprintffmt"Rel"|BVarb->Format.fprintffmt"BVar %a"F.ppb|Any->Format.fprintffmt"Any"|Arrow(m,Variadic,l,r)->Format.fprintffmt"(Variadic %a %a-> %a)"pp_dtypelMode.prettympp_dtyper|Arrow(m,_,l,r)->Format.fprintffmt"(%a %a-> %a)"pp_dtypelMode.prettympp_dtyper|Expl->Format.fprintffmt"Exp [%a]"(pplistpp_dtype", ")ltypet=(TypeAssignment.skema*Loc.t)F.Map.t[@@derivingshow,ord]letarrm~vab=Arrow(m,v,a,b)letis_exp=functionExp_->true|_->falseletchoose_variadicvfullright=ifv=Structured.VariadicthenfullelserightmoduleCompilation=structlettype_ass_2func~loc~type_abbrevs:envt:dtype=letrectype2func_app~loccargs=matchF.Map.find_optcenvwith|None->Exp(List.map(type_ass_2func~loc)args)|Some(f,_)->letta_app=TypeAssignment.applyfargsintype_ass_2func~locta_appandtype_ass_2func~loc=function|TypeAssignment.PropFunction->Det|PropRelation->Rel|Any->Any|Consn->type2func_app~locn[]|App(n,x,xs)->type2func_app~locn(x::xs)|Arr(TypeAssignment.MValm,v,l,r)->arrm~v(type_ass_2func~locl)(type_ass_2func~locr)|Arr(MRefm,v,l,r)whenMutableOnce.is_setm->type_ass_2func~loc(Arr(MutableOnce.getm,v,l,r))(*
The typical example for the following case is a predicate quantified by a pi,
an example can be found in tests/sources/findall.elpi
*)|Arr(MRef_,v,l,r)->arr~vOutput(type_ass_2func~locl)(type_ass_2func~locr)|UVara->ifMutableOnce.is_setathen(type_ass_2func~loc(TypeAssignment.derefa))elseBVar(MutableOnce.get_namea)intype_ass_2func~loctlettype_ass_2func_mut~loc~(type_abbrevs:t)t=type_ass_2func~loc~type_abbrevs(TypeAssignment.dereft)endmoduleAux=structletcheck_variadicf1f2moded1v1l1r1d2v2l2r2=match(v1,v2)with|Structured.(Variadic,Variadic)->Arrow(mode,v1,f1l1l2,f2r1r2)|NotVariadic,NotVariadic->Arrow(mode,v1,f1l1l2,f2r1r2)|Variadic,NotVariadic->Arrow(mode,v1,f1l1l2,f2d1r2)|NotVariadic,Variadic->Arrow(mode,v1,f1l1l2,f2r1d2)let(<=)m1m2~positive~d1~d2=letopenModeinmatchm1,m2with|Input,Input->Input,notpositive,(d2,d1)|Output,Input->(ifpositivethenOutputelseInput),notpositive,(ifpositivethend2,d1elsed1,d2)|Output,Output->Output,positive,(d1,d2)|Input,Output->(ifnotpositivethenOutputelseInput),positive,(ifpositivethend1,d2elsed2,d1)letrecmin_max~positive~loc~d1~d2f1f2=iff1=d1||f2=d1thend1elsematch(f1,f2)with|Det,Det->Det|Rel,Rel->Rel|a,(Any|BVar_)|(Any|BVar_),a->a|Exp[((Det|Rel|Exp_)asx)],(Det|Rel)->min_max~positive~loc~d1~d2xf2|(Det|Rel),Exp[((Det|Rel|Exp_)asx)]->min_max~positive~loc~d1~d2f1x|Expl1,Expl2->(tryExp(List.map2(min_max~positive~loc~d1~d2)l1l2)withInvalid_argument_->error~loc(Format.asprintf"min_max invalid exp_length: %a != %a"pp_dtypef1pp_dtypef2))(* | Arrow (m1, _, _, _), Arrow (m2, _, _, _) when m1 <> m2 -> error ~loc "Mode mismatch" *)|Arrow(m1,v1,l1,r1),Arrow(m2,v2,l2,r2)->letm,negative,(d1',d2')=(m1<=m2)~positive~d1~d2incheck_variadic(min_max~positive:negative~loc~d1:d1'~d2:d2')(min_max~positive~loc~d1~d2)mf1v1l1r1f2v2l2r2(* | Arrow (Output, v1, l1, r1), Arrow (_, v2, l2, r2) ->
check_variadic (min_max ~positive ~loc ~d1 ~d2) (min_max ~positive ~loc ~d1 ~d2) Output f1 v1 l1 r1 f2 v2 l2 r2 *)|Arrow(_,Variadic,_,r),f2->min_max~positive~loc~d1~d2rf2|f2,Arrow(_,Variadic,_,r)->min_max~positive~loc~d1~d2rf2|_->Format.asprintf"min between %a and %a"pp_dtypef1pp_dtypef2|>error~locletmin=min_max~positive:true~d1:Det~d2:Relletmax=min_max~positive:true~d1:Rel~d2:Detletrecminimize_maximize~loc~d1~d2d=matchdwith|Det|Rel->d1|Expl->Exp(List.map(minimize_maximize~loc~d1~d2)l)|Arrow(Input,v,l,r)->Arrow(Input,v,minimize_maximize~loc~d1:d2~d2:d1l,minimize_maximize~loc~d1~d2r)|Arrow(Output,v,l,r)->Arrow(Output,v,minimize_maximize~loc~d1~d2l,minimize_maximize~loc~d1~d2r)|Any->Any|BVarb->BVarb(* let minimize = minimize_maximize ~d1:Det ~d2:Rel *)letmaximize=minimize_maximize~d1:Rel~d2:Detletis_maximized~locd=d=maximize~locdletwrong_type~locab=error~loc@@Format.asprintf"Typing invariant broken: %a <<= %a\n%!"pp_dtypeapp_dtypebletwrong_bvars~locv1v2=error~loc@@Format.asprintf"<<=: TC did not unify two unif vars (%a and %a)"F.ppv1F.ppv2let(<<=)~locab=letrecchoose_dir~loct1t2=functionMode.Input->aux~loct2t1|Mode.Output->aux~loct1t2andaux~locab=match(a,b)with|_,Any->true|Any,_->b=maximize~locb(* TC may accept A = any, so we do too *)|BVarv1,BVarv2->F.equalv1v2||wrong_bvars~locv1v2|BVar_,_|_,BVar_->wrong_type~locab|Expl1,Expl2->(tryList.for_all2(aux~loc)l1l2withInvalid_argument_->wrong_type~locab)|Exp[((Det|Rel|Exp_)asx)],(Det|Rel)->aux~locxb|(Det|Rel),Exp[((Det|Rel|Exp_)asx)]->aux~locax|Arrow(m1,NotVariadic,l1,r1),Arrow(_,NotVariadic,l2,r2)->choose_dir~locl1l2m1&&auxr1r2~loc|Arrow(m1,NotVariadic,l1,r1),Arrow(_,Variadic,l2,_)->choose_dir~locl1l2m1&&auxr1b~loc|Arrow(m1,Variadic,l1,_),Arrow(_,NotVariadic,l2,r2)->choose_dir~locl1l2m1&&auxar2~loc|Arrow(m1,Variadic,l1,r1),Arrow(_,Variadic,l2,r2)->choose_dir~locl1l2m1&&auxr1r2~loc(* Left is variadic, Right is not an arrow -> we eat the arrow and continue *)|Arrow(_,Variadic,_,r),d->auxrd~loc(* Left is not an arrow, Right is variadic -> we eat the arrow and continue *)|d,Arrow(_,Variadic,_,r)->auxdr~loc|Arrow_,_|_,Arrow_|Exp_,_->wrong_type~locab(* Below base case *)|_,Rel->true(* rel is the sup *)|Rel,_->false|Det,_->true(* det is the inf *)inletres=auxab~locinresendlet(<<=)=Aux.(<<=)(*
Module for mapping variable names to their determinacy.
When a variable is added to the map and it already exists,
its dtype is updated to the minimum value between the
old and the new value.
*)moduleEnvMaker(M:Map.S):sigtypet[@@derivingshow]valadd:new_:bool->loc:Loc.t->v:dtype->t->M.key->tvalget:t->M.key->dtypeoptionvalclone:t->tvalempty:tvaliter:(M.key->dtype->unit)->t->unitend=structtypet=dtyperefM.t[@@derivingshow]letadd~new_~loc~(v:dtype)(env:t)(n:M.key):t=ifnew_thenM.addn(refv)envelsematchM.find_optnenvwith|None->M.addn(refv)env|Somev'->v':=Aux.min~locv!v';envletget(env:t)(k:M.key)=Option.map(!)(M.find_optkenv)letempty=M.emptyletclone:t->t=M.map(funv->ref!v)letiterf(t:t)=M.iter(funkv->fk!v)tendmoduleUvar=EnvMaker(F.Map)moduleBVar=structincludeEnvMaker(Scope.Map)letadd_oname~new_~loconamefctx=Format.eprintf"add_oname: %a@."(Format.pp_print_option(funfmt{ScopedTerm.scope=s;name=n;ty=tya}->Format.fprintffmt"@[<hov 2>%a@ with tya:@ %a@ and compiled@ %a@ -- [old: %a])]"F.ppn(TypeAssignment.pretty_mut_once)(TypeAssignment.dereftya)pp_dtype(ftya)(Format.pp_print_optionpp_dtype)(getctx(n,s))))oname;matchonamewithNone->ctx|Some{ScopedTerm.scope;name;ty=tya}->add~new_~locctx(name,scope)~v:(ftya)endletget_const_dtype~type_abbrevs~ctx~var~loc{ScopedTerm.scope=t;name;ty=tya}=letget_ctx=function|None->error~loc(Format.asprintf"DetCheck: Bound var %a should be in the local map"F.ppname)|Somee->einletget_con_x=Compilation.type_ass_2func_mut~loc~type_abbrevstyainletdet_head=matchtwithScope.Boundb->get_ctx@@BVar.getctx(name,b)|Globalg->get_cong.resolved_toinFormat.eprintf"Type of %a is %a@."F.ppnamepp_dtypedet_head;(* Format.eprintf "The functionality of %a is %a (its type is %a)@ Full type is %a@." F.pp name pp_dtype det_head
TypeAssignment.pretty_mut_once_raw (TypeAssignment.deref tya) (MutableOnce.pp (TypeAssignment.pp) ) tya; *)det_headletget_uvar_dtype~type_abbrevs~ctx~var~loc{ScopedTerm.name;ty=tya}=letget_var=functionNone->Aux.maximize~loc(Compilation.type_ass_2func_mut~loc~type_abbrevstya)|Somee->einletdet_head=get_var@@Uvar.getvarnameinFormat.eprintf"Type of %a is %a@."F.ppnamepp_dtypedet_head;(* Format.eprintf "The functionality of %a is %a (its type is %a)@ Full type is %a@." F.pp name pp_dtype det_head
TypeAssignment.pretty_mut_once_raw (TypeAssignment.deref tya) (MutableOnce.pp (TypeAssignment.pp) ) tya; *)det_headletget_dtype~type_abbrevs~ctx~var~loc=function|`UVarx->get_uvar_dtype~type_abbrevs~ctx~var~locx|`Constx->get_const_dtype~type_abbrevs~ctx~var~locxletspill_err~loc=anomaly~loc"Everything should have already been spilled"letsame_symb~typessymbsymb'=matchsymb'with|Scope.Global{resolved_to=x}->beginmatchSymbolResolver.resolved_totypesxwith|Somesymb'->TypingEnv.same_symboltypessymbsymb'|_->falseend|_->falseletget_user_type~type_abbrevs~types~locs=matchs.ScopedTerm.scopewith|Scope.Global{resolved_to=x}->letopenTypeAssignmentinletrecmk_args=functionTyt->[]|Lam(_,b)->Any::mk_argsbinletapplyty=TypeAssignment.applyty(mk_argsty)inletcompileTypingEnv.{ty}=Compilation.type_ass_2func~loc~type_abbrevs(applyty)inletto_dtypex=Option.mapcompile@@TypingEnv.resolve_symbol_optxtypesinOption.bind(SymbolResolver.resolved_totypesx)to_dtype|Bound_->Noneletcheck_clause,check_chr_guard_and_newgoal=lethas_undeclared_signature~types~unknown{ScopedTerm.scope=b;name=f}=matchF.Map.find_optfunknownwithSome(_,symb)->same_symb~typessymbb|_->falseinletis_global~types{ScopedTerm.scope=b}name=same_symb~typesnamebinletis_quantifier~typesx=is_global~typesxS.pi||is_global~typesxS.sigmainletundecl_disclaimer~types~unknown=function|Some({ScopedTerm.name;ty}aspred_name)whenhas_undeclared_signature~types~unknownpred_name->Format.asprintf"@[<hov 2>Predicate %a has no declared signature,@ providing one could fix the following error@ (inferred \
signature: %a)@]@\n\
DetCheck: "F.ppnameTypeAssignment.pretty_mut_once(TypeAssignment.deref@@ty)|_->""inletis_builtin~typess=matchs.ScopedTerm.scopewith|Scope.Global{resolved_to}->SymbolResolver.is_resolved_to_builtintypesresolved_to|_->falseinletcnt=ref0inletemit()=incrcnt;F.from_string("*dummy"^string_of_int!cnt)inletrecget_tl=functionArrow(_,_,_,r)->get_tlr|e->einletis_cut~typesScopedTerm.{it}=matchitwithApp(b,[])->is_global~typesbS.cut|_->falseinletrecreplace_signature_tgt~loc~with_d'=function|[]->with_|_::xs->matchd'with|Arrow(_,Variadic,_,_)->replace_signature_tgt~loc~with_d'xs|Arrow(m,NotVariadic,l,r)->Arrow(m,NotVariadic,l,replace_signature_tgt~loc~with_rxs)|_->error~loc@@Format.asprintf"replace_signature_tgt: Type error: found %a "pp_dtyped'inletrecinfer~type_abbrevs~types~ctx~var~expt:dtype*Good_call.t=letrecinfer_fold~was_input~was_data~loc~user_dtypectxdhdtl=Format.eprintf"Starting infer fold at %a with dtype:@[%a@] and user_dtype:@[%a@]@."Loc.pplocpp_dtyped(Format.pp_print_optionpp_dtype)user_dtype;letb=Good_call.init()inletsplit_user_dtype=function|None->Any,None|SomeAny->Any,None|Some(Arrow(m,_,l,r))->l,Somer|Somed->error~loc@@Format.asprintf"DetCheck: found dtype:%a"pp_dtypedinletrecaux~user_dtypedtl:(dtype*Good_call.t)=Format.eprintf"@[<hov 2>In recursive call for infer.aux with head-term@ @[%a@], good_call is %a -- and dtype@ @[%a@] and user_dytpe is @[%a@]@]@."(Format.pp_print_optionScopedTerm.pretty)(List.nth_opttl0)Good_call.ppbpp_dtyped(Format.pp_print_optionpp_dtype)user_dtype;match(d,tl)with|Arrow(_,Variadic,_,t),[]->(t,b)|t,[]->((ifGood_call.is_wrongbthenAux.maximize~loctelset),b)|Arrow(_,v,_,r),_::tlwhenGood_call.is_wrongb->aux~user_dtype:(choose_variadicvuser_dtypeNone)(choose_variadicvdr)tl|Arrow(Input,v,l,r),h::tl->letl_user,r_user=split_user_dtypeuser_dtypeinletloc=h.locinletdy,b'=infer~was_input:true~exp:lctxhinFormat.eprintf"infer.aux in Input branch with dtype:%a and t:%a@."pp_dtypelScopedTerm.prettyh;Format.eprintf"end infer.aux for term %a with b':%a and not ((dy <<= l) ~loc):%b and was_data:%b@."ScopedTerm.prettyhGood_call.ppb'(not((dy<<=l)~loc))was_data;ifGood_call.is_wrongb'thenbeginletmax_exp=Aux.maximize~locdyinifnot((max_exp<<=l_user)~loc)thenraise(KError(hd,b'))elseifnot((max_exp<<=l)~loc)thenGood_call.setbb'endelseifnot((dy<<=l_user)~loc)then(raise(KError(hd,(Good_call.set_wrongb~exp:l_user~found:dyh;b))))elseifnot((dy<<=l)~loc)then((* If preconditions are not satisfied, we stop and return bottom *)Good_call.set_wrongb~exp:l~found:dyh;Format.eprintf"Invalid determinacy set b to wrong (%a)@."Good_call.ppb);aux~user_dtype:(choose_variadicvuser_dtyper_user)(choose_variadicvdr)tl|Arrow(Output,v,l,r),hd::tl->ifwas_datathenaux~user_dtype(Arrow(Input,v,l,r))(hd::tl)elseletl_user,r_user=split_user_dtypeuser_dtypeinaux~user_dtype:(choose_variadicvuser_dtyper_user)(choose_variadicvdr)tl|(BVar_|Any),_->(d,Good_call.init())|(Det|Rel|Exp_),_::_->Format.asprintf"deduce: Type error, found dtype %a and arguments %a"pp_dtyped(pplistScopedTerm.pretty",")tl|>anomaly~locinaux~user_dtypedtlandinfer_app~exp~was_inputctx~locttystl=letwas_data=is_exp(Compilation.type_ass_2func_mut~loc~type_abbrevsty)||is_builtin~typessinletuser_dtype=ifwas_datathenget_user_type~type_abbrevs~types~locselseNoneinFormat.eprintf"Is_exp: %b@."was_data;letdtype=get_const_dtype~type_abbrevs~ctx~var~locsin(* TODO: here: if is wrong then also the app is wrong... *)let(dt,gcasr)=infer_fold~was_input~was_data~user_dtype~locctxdtype(`Consts)tlinifGood_call.is_wronggcthenGood_call.prependgc~exp~found:dtt|>ignore;randinfer_var~exp~was_inputctx~locttystl=letwas_data=is_exp(Compilation.type_ass_2func_mut~loc~type_abbrevsty)inFormat.eprintf"Is_exp: %b@."was_data;letdtype=get_uvar_dtype~type_abbrevs~ctx~var~locsin(* TODO: here: if is wrong then also the app is wrong... *)let(dt,gcasr)=infer_fold~was_input~was_data~user_dtype:None~locctxdtype(`UVars)tlinifGood_call.is_wronggcthenGood_call.prependgc~exp~found:dtt|>ignore;randinfer_and~was_inputctx~locargs(_,rasdr)=matchargswith|[]->dr|x::xswhenis_cut~typesx->Good_call.set_goodr;infer_and~was_inputctx~locxs(Det,r)|x::xs->let(d,gc)=infer~exp:Det~was_inputctxxinifd=Relthen(Good_call.set_wronggc~exp:Det~found:Relx;infer_and~was_inputctx~locxs(d,gc))elseifGood_call.is_wronggctheninfer_and~was_inputctx~locxs(d,gc)elseinfer_and~was_inputctx~locxsdrandinfer~was_input~expctxScopedTerm.({it;ty;loc}ast):dtype*Good_call.t=matchitwith|UVar(b,xs)->infer_var~exp~was_input~locctxttybxs|App(q,[{it=Lam(b,_,bo)}])whenis_quantifier~typesq->infer~exp~was_input(BVar.add_oname~new_:false~locb(funx->Compilation.type_ass_2func_mut~loc~type_abbrevsx)ctx)bo|App(g,x::xs)whenis_global~typesgS.and_->Format.eprintf"Calling deduce on a comma separated list of subgoals@.";infer_and~was_inputctx~loc(x::xs)(Det,Good_call.init())|App(b,xs)->infer_app~exp~was_input~locctxttybxs|Impl(L2R,_,c,b)->check_clause~type_abbrevs~types~ctx~varc|>ignore;infer~exp~was_inputctxb|Impl(L2RBang,_,c,b)->check_clause~type_abbrevs~types~ctx~var~has_tail_cut:truec|>ignore;infer~exp~was_inputctxb|Impl(R2L,_,_,_)->Format.eprintf"Recursive call to check clause@.";(check_clause~type_abbrevs~types~ctx~vart,Good_call.init())|Lam_->(trylet_=check_lam~type_abbrevs~types~ctx~vartin(Compilation.type_ass_2func_mut~loc~type_abbrevsty,Good_call.init())withFatalDetError(_,b1)|DetError(_,b1)|RelationalBody(_,b1)->(Compilation.type_ass_2func_mut~loc~type_abbrevsty,b1))|Discard_->Format.eprintf"Calling type_ass_2func_mut in Discard@.";(Compilation.type_ass_2func_mut~loc~type_abbrevsty,Good_call.init())|CData_->(Exp[],Good_call.init())|Cast(t,_)->letd,good=infer~exp~was_inputctxtinifGood_call.is_wronggoodthenraise(FatalDetError(None,good));(d,good)|Spill(_,_)->spill_err~locinlet((det,gc)asr)=infer~exp~was_input:falsectxtinFormat.eprintf"Result of infer for %a is (%a,%a)@."ScopedTerm.prettytpp_dtypedetGood_call.ppgc;randinfer_output~type_abbrevs~types~pred_name~ctx~varScopedTerm.{it;loc}=Format.eprintf"Calling deduce output on %a@."ScopedTerm.pretty_it;letrecauxdargs=match(d,args)with|Arrow(Input,v,_,r),_::tl->aux(choose_variadicvdr)tl|Arrow(Output,v,l,r),hd::tl->letdet,gc=infer~type_abbrevs~types~exp:l~ctx~varhdinFormat.eprintf"Inference of %a gives (%a,%a)@."ScopedTerm.prettyhdpp_dtypedetGood_call.ppgc;ifGood_call.is_wronggc&&Aux.is_maximized~loclthenaux(choose_variadicvdr)tlelseifGood_call.is_goodgc&&(det<<=l)~locthenaux(choose_variadicvdr)tlelseifGood_call.is_goodgcthenraise(DetError(Somepred_name,Good_call.make~exp:l~found:dethd))elseraise(DetError(Somepred_name,gc))|_->()inmatchitwith(* TODO: add case for pi, comma and = *)|App(b,xs)->aux(get_const_dtype~type_abbrevs~ctx~var~locb)xs|UVar(b,xs)->aux(get_uvar_dtype~type_abbrevs~ctx~var~locb)xs|_->anomaly~loc@@Format.asprintf"Invalid term in deduce output %a."ScopedTerm.pretty_itandassume~type_abbrevs~types?(was_input=false)~ctx~vardt=Format.eprintf"Calling assume on %a with det : %a@."ScopedTerm.prettytpp_dtyped;letvar=refvarinletadd~loc~vvname=Format.eprintf"Permanently adding %a : %a@."F.ppvnamepp_dtypev;letm=Uvar.add~new_:false~loc!varvname~vinvar:=minletrecassume_fold~was_input~was_data~locctxd(tl:ScopedTerm.tlist):unit=match(d,tl)with|_,[]->()|Arrow(Input,v,l,r),h::tl->assume~was_input:truectxlh;assume_fold~was_input~was_data~locctx(choose_variadicvdr)tl|Arrow(Output,v,l,r),h::tl->ifwas_input&&was_datathenassume~was_inputctxlh;assume_fold~was_input~was_data~locctx(choose_variadicvdr)tl|(BVar_|Any),_->()|(Det|Rel|Exp_),_::_->Format.asprintf"assume: Type error, found dtype %a and arguments %a@."pp_dtyped(pplistScopedTerm.pretty",")tl|>anomaly~locandassume_app~was_inputctx~locd({ScopedTerm.scope=t;name}ass)tl=Format.eprintf"Calling assume_app on: %a with dtype %a with args [%a] and@."F.ppnamepp_dtyped(pplist~boxed:trueScopedTerm.pretty" ; ")tl;matchtwith|Scope.Boundb->assume_bound_varb~ctx~locdstl|_->letdet_head=get_const_dtype~type_abbrevs~ctx~var:!var~locsinassume_fold~was_input~was_data:(is_expd)~locctxdet_headtl;Format.eprintf"The map after call to assume_app is %a@."Uvar.pp!varandassume_bound_var~ctx~locbd({ScopedTerm.name}ass)tl=letdtype=get_uvar_dtype~type_abbrevs~ctx~var:!var~locsinFormat.eprintf"Dtype of %a is %a@."F.ppnamepp_dtypedtype;letd'=replace_signature_tgtdtypetl~loc~with_:dinFormat.eprintf"d' is %a@."pp_dtyped';BVar.add~new_:falsectx~v:d'~loc(name,b)|>ignoreandassume_var~ctx~locd({ScopedTerm.name}ass)tl=letdtype=get_uvar_dtype~type_abbrevs~ctx~var:!var~locsinFormat.eprintf"Dtype of %a is %a@."F.ppnamepp_dtypedtype;letd'=replace_signature_tgtdtypetl~loc~with_:dinFormat.eprintf"d' is %a@."pp_dtyped';add~loc~v:d'nameandassume~was_inputctxdScopedTerm.({loc;it}):unit=Format.eprintf"Assume of %a with dtype %a (was_input:%b)@."ScopedTerm.pretty_itpp_dtypedwas_input;matchitwith|App(q,[{it=Lam(b,_,bo)}])whenis_quantifier~typesq->assume~was_input(BVar.add_oname~new_:false~locb(funx->Compilation.type_ass_2func_mut~loc~type_abbrevsx)ctx)dbo|UVar(b,tl)->assume_var~loc~ctxdbtl|App(b,xs)->assume_app~was_inputctx~locdbxs|Discard_->()|Impl(L2R,_,h,b)->check_clause~type_abbrevs~types~ctx~var:!varh|>ignore;assume~was_inputctxdb|Impl(L2RBang,_,h,b)->check_clause~type_abbrevs~types~ctx~var:!var~has_tail_cut:trueh|>ignore;assume~was_inputctxdb|Impl(R2L,_,{it=App(b,xs)},bo)->letdtype,var'=assume_hd~type_abbrevs~types~loc~ctx~var:!varbtxsinUvar.iter(funkv->add~loc~vk)var';assume~was_input:falsectx(get_tldtype)bo|Impl(R2L,_,_,_B)->()|Lam(oname,_,c)->(matchdwith|Arrow(Input,NotVariadic,l,r)->letctx=BVar.add_oname~new_:true~loconame(fun_->l)ctxinassume~was_inputctxrc|Arrow(Output,NotVariadic,l,r)->letctx=BVar.add_oname~new_:true~loconame(fun_->l)ctxinassume~was_inputctxrc|Any->()|_->anomaly~loc(Format.asprintf"Found lambda term with dtype %a"pp_dtyped))|CData_->()|Spill_->spill_err~loc|Cast(t,_)->assume~was_inputctxdtinassume~was_inputctxdt;!varandassume_output~type_abbrevs~types~ctx~vardtl:Uvar.t=letrecassume_outputdargsvar=match(d,args)with|Arrow(Input,v,_,r),_::tl->assume_output(choose_variadicvdr)tlvar|Arrow(Output,v,l,r),hd::tl->Format.eprintf"Call assume of %a with dtype:%a@."ScopedTerm.prettyhdpp_dtypel;letvar=assume~type_abbrevs~types~was_input:true~ctx~varlhdinassume_output(choose_variadicvdr)tlvar|_->varinassume_outputdtlvarandassume_input~type_abbrevs~types~ctx~vardtl:Uvar.t=letrecassume_inputdargsvar=match(d,args)with|Arrow(Input,v,l,r),hd::tl->Format.eprintf"Call assume of %a with dtype:%a@."ScopedTerm.prettyhdpp_dtypel;letvar=assume~type_abbrevs~types~was_input:true~ctx~varlhdinassume_input(choose_variadicvdr)tlvar|Arrow(Output,v,_,r),_::tl->assume_input(choose_variadicvdr)tlvar|_->varinassume_inputdtlvar(* returns the updated environment, the dtype of the term *)andcheck~type_abbrevs~types~ctx~var(d:dtype)t:Uvar.t*(dtype*_)=letvar=refvarinletreccheck_appctx~loc(d:dtype)btltm=Format.eprintf"@[<hov 2>-- Entering check_app with term@ @[%a@]@]@."ScopedTerm.prettytm;letd',gc=infer~type_abbrevs~types~exp:d~ctx~var:!vartminFormat.eprintf"-- Checked term dtype is %a and gc is %a@."pp_dtyped'Good_call.ppgc;ifGood_call.is_goodgcthen(letdet=get_dtype~type_abbrevs~ctx~var:!varb~locinFormat.eprintf"Assuming output of %a with dtype : %a@."ScopedTerm.prettytmpp_dtypedet;var:=assume_output~type_abbrevs~types~ctx~var:!vardettl);Format.eprintf"In check_app before result, comparing %a with %a (expected %a)@."ScopedTerm.prettytmpp_dtyped'pp_dtyped;ifGood_call.is_goodgcthenif(d'<<=d)~locthen(Aux.max~loc(get_tld)(get_tld'),gc)else(Rel,(Good_call.set_wrong~exp:d~found:d'gctm;gc))else(Rel,gc)andcheck_commactx~loc(d,bad_atom)args=matchargswith|[]->(d,bad_atom)|x::xswhenis_cut~typesx->Good_call.set_goodbad_atom;check_commactx~loc(Det,bad_atom)xs|x::xs->Format.eprintf"Checking comma with first term %a@."ScopedTerm.prettyx;letd1,bad_atom1=check~ctxdxin(* we save the loc of the last offending atom *)(* let bad_atom = *)ifGood_call.is_goodbad_atomthenifGood_call.is_wrongbad_atom1thenGood_call.setbad_atombad_atom1elseifd1=RelthenGood_call.set_wrongbad_atom~exp:Det~found:Relx;(* bad_atom1 else bad_atom in *)(* Format.eprintf "Loc:%a --> Badatom is %a@." Loc.pp bad_atom.loc ScopedTerm.pretty bad_atom; *)check_commactx~loc(d1,bad_atom)xsandcheck~ctx(d:dtype)ScopedTerm.({it;loc}ast):dtype*Good_call.t=matchitwith|Impl(L2R,_,h,b)->check_clause~type_abbrevs~types~ctx~var:!varh|>ignore;check~ctxdb|Impl(L2RBang,_,h,b)->check_clause~type_abbrevs~types~ctx~var:!var~has_tail_cut:trueh|>ignore;check~ctxdb|App(b,[])whenis_global~typesbS.cut->(Det,Good_call.init())|App(q,[{it=Lam(b,_,bo)}])whenis_quantifier~typesq->check~ctx:(BVar.add_oname~new_:true~locb(Compilation.type_ass_2func_mut~loc~type_abbrevs)ctx)dbo(* Cons and nil case may appear in prop position for example in : `f :- [print a, print b, a].` *)|App(b,[x;xs])whenis_global~typesbS.cons->check~ctx(check~ctxdx|>fst)xs|App(b,[])whenis_global~typesbS.nil->(d,Good_call.init())|App(b,x::xs)whenis_global~typesbS.and_->check_commactx~loc(d,Good_call.init())(x::xs)(* smarter than paper, we assume the min of the inference of both. Equivalent
to elaboration t = s ---> eq1 t s, eq1 s t
with func eq1 A -> A. *)|App(b,[l;r])whenis_global~typesbS.eq->letd1,gc=infer~type_abbrevs~types~exp:Any~ctx~var:!varlin(ifGood_call.is_goodgcthenletd2,gc=infer~type_abbrevs~types~exp:Any~ctx~var:!varrinifGood_call.is_goodgcthen(Format.eprintf"In equality calling min between the two terms %a and %a@."ScopedTerm.prettylScopedTerm.prettyr;letm=Aux.min~locd1d2inFormat.eprintf"The minimum between %a and %a is %a@."pp_dtyped1pp_dtyped2pp_dtypem;var:=assume~type_abbrevs~types~ctx~var:!varml;var:=assume~type_abbrevs~types~ctx~var:!varmr));(d,Good_call.init())(* Const are checked due to test68.elpi and test69.elpi *)|UVar(b,xs)->check_appctx~locd(`UVarb)xst|App(b,xs)->check_appctx~locd(`Constb)xst|Cast(b,_)->(tryletd,_=check~ctxdbinletd'=Compilation.type_ass_2func_mut~loc~type_abbrevsb.tyinifnot((d<<=d')~loc)thenraise(CastError(None,Good_call.make~exp:d'~found:db));(d,Good_call.init())withDetErrorx->raise(FatalDetErrorx))|Spill_->spill_err~loc|CData_->anomaly~loc"Found CData in prop position"|Lam_->anomaly~loc"Lambda-abstractions are not props"|Discard_->anomaly~loc"Discard found in prop position"|Impl(R2L,_,_,_)->anomaly~loc"Found clause in prop position"in(!var,check~ctxdt)andcheck_lam~type_abbrevs~types~ctx~vart:dtype=Format.eprintf"check_lam: t = %a@."ScopedTerm.prettyt;letget_tanargs=letta_sk,_=F.Map.findntype_abbrevsinletty=TypeAssignment.applyta_skargsinTypeAssignment.createtyinletotype2term~loctyb=letty=TypeAssignment.createtyinletit=matchbwithNone->ScopedTerm.Discard{heapify=false}|Some{ScopedTerm.scope=a;name=b;ty=c;loc}->App({ScopedTerm.scope=Bounda;name=b;ty=c;loc},[])inScopedTerm.{it;ty;loc}inletbuild_clauseargs~ctx~loc~tybody=letnew_pred=emit()inletargs=List.revargsinletb={ScopedTerm.scope=Scope.Global{resolved_to=SymbolResolver.make();escape_ns=false};name=new_pred;ty=t.ty;loc=t.loc}inletpred_hd=ScopedTerm.(App(b,args))in(* let ctx = BVar.add ~loc ctx (new_pred, elpi_language) ~v:(Compilation.type_ass_2func_mut ~loc env t.ty) in *)letclause=ScopedTerm.{ty;it=Impl(R2L,loc,{it=pred_hd;ty;loc},body);loc}inFormat.eprintf"Clause is %a@."ScopedTerm.prettyclause;(clause,ctx)inletadd_partial_app(ScopedTerm.{it;loc}ast)argsty=letargs=List.revargsinmatchargswith|[]->t|hd::tl->(matchitwith|App(b,xs)->{it=App(b,xs@args);ty;loc}|UVar(b,xs)->{it=UVar(b,xs@args);ty;loc}|_->assertfalse)inletrecaux~type_abbrevs~types~ctx~args~parial_app(ScopedTerm.{it;loc;ty}ast):dtype=match(TypeAssignment.derefty,it)with|Prop_,Impl(R2L,_,_,bo)->assert(parial_app=[]);letclause,ctx=build_clauseargs~ctx~loc~tyboincheck_clause~type_abbrevs~types~ctx~varclause|Prop_,_->lett=add_partial_apptparial_apptyinletclause,ctx=build_clauseargs~ctx~loc~tytinFormat.eprintf"check_lam: built clause is = %a@."ScopedTerm.prettyclause;check_clause~type_abbrevs~types~ctx~varclause|Consb,_whenF.Map.membtype_abbrevs->aux~type_abbrevs~types~parial_app~ctx~args{twithty=get_tab[]}|App(b,x,xs),_whenF.Map.membtype_abbrevs->aux~type_abbrevs~types~parial_app~ctx~args{twithty=get_tab(x::xs)}|Cons_,_->Exp[](* Below: TODO: check that args have the type expected, for example list prop vs list func *)|App(_,x,xs),_->Exp(List.map(Compilation.type_ass_2func~type_abbrevs~loc)(x::xs))(* If we reach a Uvar | Any case no check is needed, i.e. we don't know *)|((UVar_|Any)ast),_->Compilation.type_ass_2func~type_abbrevs~loct|Arr(_,_,l,_),Lam(b,_,bo)->aux~type_abbrevs~types~parial_app~ctx:(BVar.add_oname~new_:true~locb(funt->Aux.maximize~loc(Compilation.type_ass_2func_mut~loc~type_abbrevst))ctx)~args:(otype2term~loclb::args)bo|Arr(_,Structured.Variadic,_,r),_->letb=Some{ScopedTerm.scope=elpi_language;name=emit();ty=TypeAssignment.creater;loc}inaux~type_abbrevs~types~parial_app~ctx:(BVar.add_oname~new_:true~locb(funt->Aux.maximize~loc(Compilation.type_ass_2func_mut~loc~type_abbrevst))ctx)~args{twithty=TypeAssignment.creater}|Arr(_,_,l,r),_->(* Partial app: type is Arr but body is not Lam *)letb=Some{ScopedTerm.scope=elpi_language;name=emit();ty=TypeAssignment.createl;loc}inletnt=otype2term~loclbinaux~type_abbrevs~types~parial_app:(nt::parial_app)~ctx:(BVar.add_oname~new_:true~locb(funt->Aux.maximize~loc(Compilation.type_ass_2func_mut~loc~type_abbrevst))ctx)~args:(nt::args){twithty=TypeAssignment.creater}inaux~type_abbrevs~types~ctx~args:[]~parial_app:[]t(* returns the determinacy of the clause and if the clause has a cut in it *)andassume_hd~type_abbrevs~typesb~ctx~var~loc(tm:ScopedTerm.t)tl=letdet_hd=get_const_dtype~type_abbrevs~ctx~var~locbinFormat.eprintf"assume_input for term %a with det %a@."ScopedTerm.prettytmpp_dtypedet_hd;(det_hd,assume_input~type_abbrevs~types~ctx~vardet_hdtl)andcheck_clause~type_abbrevs~types?(_is_toplevel=false)~ctx~var?(has_tail_cut=false)ScopedTerm.({loc}ast):dtype=letctx=ref(BVar.clonectx)inletvar=Uvar.clonevarinletrecauxScopedTerm.{it;loc}=matchitwith|Impl(R2L,_,({it=App(b,xs)}ashd),bo)->(b,assume_hd~type_abbrevs~types~loc~ctx:!ctx~var:varbhdxs,hd,Somebo)|Impl(R2L,_,({it=UVar_}),_)->raise(LoadFlexClauset)(* For clauses with quantified unification variables *)|App(n,[{it=Lam(oname,_,body)}])whenis_quantifier~typesn->ctx:=BVar.add_oname~new_:true~loconame(Compilation.type_ass_2func_mut~loc~type_abbrevs)!ctx;auxbody|App(b,xs)->(b,assume_hd~type_abbrevs~types~loc~ctx:!ctx~var:varbtxs,t,None)|UVar_->raise(LoadFlexClauset)|_->anomaly~loc@@Format.asprintf"Found term %a in prop position"ScopedTerm.pretty_itintryletpred_name,(det_hd,var),hd,body=auxtinFormat.eprintf"Var is %a@."Uvar.ppvar;letvar,(det_body,err_atom)=Option.(map(check~type_abbrevs~types~ctx:!ctx~varDet)body|>value~default:(var,(Det,Good_call.init())))inletdet_body=ifhas_tail_cutthenDetelsedet_bodyinFormat.eprintf"** END CHECKING THE CLAUSE @.";Format.eprintf"The var map is %a and det_body is %a@."Uvar.ppvarpp_dtypedet_body;letdet_pred=get_tldet_hdinifnot@@(det_body<<=det_pred)~locthenraise(RelationalBody(Somepred_name,Good_call.prepend~exp:det_pred~found:det_bodyerr_atom(Option.getbody)));Format.eprintf"** Start checking outputs@.";infer_output~type_abbrevs~types~pred_name~ctx:!ctx~varhd;det_predwithLoadFlexClauset->ifnothas_tail_cutthenwarn~loc:t.loc~id:FlexClause(Format.asprintf"skipping static analysis of flexible rule: %a"ScopedTerm.prettyt);Detinleterrgcf=letpp_btiGood_call.{exp;found;term}=letstart=ifi=0then"Offending term"else"Contained in"inFormat.asprintf"%s: (@[%a@]) \n - Inferred: %a \n - Expected: %a"startScopedTerm.prettytermpp_dtypefoundpp_dtypeexpinletl=Good_call.getgc|>List.revinassert(l<>[]);error~loc:(List.hdl).term.loc@@String.concat"\n"(f(List.hdl)::List.mapipp_btl)inletwith_error_handling~types~unknownfx=tryfxwith|FatalDetError(pred_name,gc)|DetError(pred_name,gc)->letfGood_call.{exp;found;term}=Format.asprintf"%sInvalid determinacy of output term %a.\n Expected: %a\n Found: %a"(undecl_disclaimer~types~unknownpred_name)ScopedTerm.prettytermpp_dtypeexppp_dtypefoundinerrgcf|KError(`UVarpred_name,gc)->letfGood_call.{exp;found;term}=Format.asprintf"Invalid determinacy of constructor/builtin argument %a.\n Expected: %a\n Found: %a"ScopedTerm.prettytermpp_dtypeexppp_dtypefoundinerrgcf|KError(`Constpred_name,gc)->letfGood_call.{exp;found;term}=Format.asprintf"%sInvalid determinacy of constructor/builtin argument %a.\n Expected: %a\n Found: %a"(undecl_disclaimer~types~unknown(Somepred_name))ScopedTerm.prettytermpp_dtypeexppp_dtypefoundinerrgcf|CastError(_,gc)->letfGood_call.{exp;found;term}=Format.asprintf"Cast error on term %a.\n Expected: %a\n Found: %a"ScopedTerm.prettytermpp_dtypeexppp_dtypefoundinerrgcf|RelationalBody(pred_name,gc)->letfGood_call.{term}=Format.asprintf"%s@[<hov 2>Found relational atom@ @[<hov 2>(%a)@]@ in the body of function@ %a.@]"(undecl_disclaimer~types~unknownpred_name)ScopedTerm.prettytermF.pp(let{ScopedTerm.name=n}=Option.getpred_nameinn)inerrgcfinletcheck_clause~type_abbrevs~types~unknown(t:ScopedTerm.t):unit=let_:dtype=with_error_handling~types~unknown(check_clause~type_abbrevs~types~_is_toplevel:true~ctx:BVar.empty~var:Uvar.empty)tin()inletcheck_chr_guard_and_newgoal~type_abbrevs~types~unknown~guard~newgoal:unit=letvar=Uvar.emptyinletvar=matchguardwith|Someguard->fst@@with_error_handling~types~unknown(check~type_abbrevs~types~ctx:BVar.empty~varRel)guard|None->varinlet_,(_,gc)=with_error_handling~types~unknown(check~type_abbrevs~types~ctx:BVar.empty~varDet)newgoalinifGood_call.is_wronggcthenletfGood_call.{term}=Format.asprintf"@[<hov 2>Found relational new goal in CHR rule:@ @[<hov 2>(%a)@]@]"ScopedTerm.prettyterminerrgcfincheck_clause,check_chr_guard_and_newgoal