123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320moduleS=SubstmoduleLL=OSeqmoduleT=TermmoduleU=UnifmoduleQ=CCDequemodulePUP=PragUnifParamsmoduletypePARAMETERS=sigexceptionNotInFragmentexceptionNotUnifiabletypeflag_typevalinit_flag:flag_typevalflex_state:Flex_state.tvalidentify_scope:T.tScoped.t->T.tScoped.t->T.t*T.t*Scoped.scope*S.tvalfrag_algs:unit->(T.tScoped.t->T.tScoped.t->S.t->S.tlist)listvalpb_oracle:(T.tScoped.t->T.tScoped.t->flag_type->S.t->Scoped.scope->(S.t*flag_type)optionLL.t)valoracle_composer:(Subst.toptionOSeq.t)OSeq.t->Subst.toptionOSeq.tend(* Given a sequence of sequences (i.e., a generator) A
take one element from the A[0],
then one element from A[1] and A[0]
then one element from A[2],A[1] and A[0], etc.. *)lettake_fairgens=(* Take one element from A[0],A[1],...,A[k-1] *)letrectake_first_kkaccgens=ifk=0then(acc,gens)else(matchgens()with|OSeq.Nil->(acc,OSeq.empty)|OSeq.Cons(x,xs)->beginmatchx()with|OSeq.Nil->take_first_k(k-1)accxs|OSeq.Cons(y,ys)->lettaken,new_gens=take_first_k(k-1)(OSeq.consyacc)xsin(taken,OSeq.consysnew_gens)end)in(* Take one element from A[0],A[1],...A[i-1]
and then take one element from A[0],A[1],...,A[i] *)letrecauxigens=lettaken,new_gens=take_first_kiOSeq.emptygensinifOSeq.is_emptynew_gensthentakenelse(OSeq.appendtaken(function()->aux(i+1)new_gens()))inaux1gensmoduleMake(P:PARAMETERS)=structexceptionPolymorphismDetectedletrecnfapply_monosubst(t,sc)=letpref,tt=T.open_funtinlett'=beginmatchT.viewttwith|T.Var_->ifnot(Type.is_ground(T.tytt))then(raisePolymorphismDetected);letu,_=S.FO.derefsubst(tt,sc)inifT.equalttuthenuelsenfapply_monosubst(u,sc)|T.App(f0,l)->letf=nfapply_monosubst(f0,sc)inlett=ifT.equalf0fthenttelseT.appflinletu=Lambda.whnftinifT.equaltuthentelsenfapply_monosubst(u,sc)|_->ttendinifT.equalttt'thentelseT.fun_lpreft'(* apply a substitution, possibly eta-expand because
a type substitution might introduce a need for expansion and reduce to whnf *)letnfapplysu=Lambda.whnf@@S.FO.applyS.Renaming.nonesuletnormalizesu=tryifnot(Type.is_ground(T.ty(fstu)))thenraisePolymorphismDetected;nfapply_monosuwithPolymorphismDetected->nfapplysuleteta_expand_otf~subst~scopepref1pref2t1t2=letdo_exp_otfntypest=letremaining=CCList.dropntypesinassert(List.lengthremaining!=0);letnum_vars=List.lengthremaininginletvars=List.mapi(funity->(* let ty = S.Ty.apply S.Renaming.none subst (ty,scope) in *)T.bvar~ty(num_vars-1-i))remaininginletshifted=T.DB.shiftnum_varstinT.appshiftedvarsinifList.lengthpref1=List.lengthpref2then(t1,t2,pref1)else(letn1,n2=List.lengthpref1,List.lengthpref2inifn1<n2then(do_exp_otfn1pref2t1,t2,pref2)else(t1,do_exp_otfn2pref1t2,pref1))letdifferent_rigid_headsst=not@@T.is_vars&¬@@T.is_vart&&matchT.viewswith|T.DB_->not@@T.is_bvart|T.Const_->not@@T.is_constt|T.AppBuiltin_->not@@T.is_appbuiltint|_->falseletdo_unif~bind_cntproblemsubstunifscope=letdelayres=letskipper=int_of_float(Flex_state.get_exnPUP.k_skip_multiplierP.flex_state)inif!bind_cntmodskipper==0then(letnone_count=!bind_cnt/skipperinOSeq.append(OSeq.takenone_count(OSeq.repeatNone))res)elseresinletrecaux?(root=false)substproblem=letdecomposeargs_largs_rrestflag=letreczipped_with_flag=function|[],[]->[]|x::xs,y::ys->(x,y,flag)::(zipped_with_flag(xs,ys))|_,_->invalid_arg"lists must be of the same size."inletnew_args=zipped_with_flag(args_l,args_r)inletto_classify,rest=ifList.lengthrest<=15thennew_args@rest,[]elsenew_args,restinletsort_class=List.sort(fun(l,r,_)(l',r',_)->letl,l'=CCPair.map_same(funt->T.head_term@@snd@@T.open_funt)(l,l')inletr,r'=CCPair.map_same(funt->T.head_term@@snd@@T.open_funt)(r,r')inif(not(Term.is_appl)||not(Term.is_appr))&&(not(Term.is_appl')||not(Term.is_appr'))then0elseifnot(Term.is_appl)||not(Term.is_appr)then-1elseifnot(Term.is_appl')||not(Term.is_appr')then1elseTerm.ho_weightl+Term.ho_weightr-Term.ho_weightl'-Term.ho_weightr')inletclassify_ones=letrecfollow_bindingst=lethd=T.head_term@@snd@@(T.open_funt)inletderefed,_=Subst.FO.derefsubst(hd,unifscope)inifT.equalhdderefedthenhdelsefollow_bindingsderefedinlethd=follow_bindingssinifT.is_consthdthen`ConstelseifT.is_varhdthen`Var(* when it is bound variable, we do not know what will
happen when it is reduced *)else`Unknownin(* classifies the pairs as (rigid-rigid, flex-rigid, and flex-flex *)letrecclassify=function|((lhs,rhs,flag)ascstr)::xs->letrr,fr,unsure,ff=classifyxsinbeginmatchclassify_onelhs,classify_onerhswith|`Const,`Const->cstr::rr,fr,unsure,ff|_,`Const|`Const,_->rr,cstr::fr,unsure,ff|_,`Unknown|`Unknown,_->rr,fr,cstr::unsure,ff|`Var,`Var->rr,fr,unsure,cstr::ffend|[]->([],[],[],[])inletrr,fr,unsure,ff=classifyto_classifyinifFlex_state.get_exnPragUnifParams.k_sort_constraintsP.flex_statethensort_classrr@sort_classfr@sort_classunsure@rest@sort_classffelserr@fr@unsure@rest@ffinletdecompose_and_cont?(inc_step=0)args_largs_rrestflagsubst=letnew_prob=decomposeargs_largs_rrestflaginauxsubstnew_probinmatchproblemwith|[]->OSeq.return(Somesubst)|(lhs,rhs,flag)::rest->matchPatternUnif.unif_simple~subst~scope:unifscope(T.of_ty(T.tylhs))(T.of_ty(T.tyrhs))with|None->OSeq.empty|Somesubst->letsubst=Unif_subst.substsubstinletlhs=normalizesubst(lhs,unifscope)andrhs=normalizesubst(rhs,unifscope)inlet(pref_lhs,body_lhs)=T.open_funlhsand(pref_rhs,body_rhs)=T.open_funrhsinletbody_lhs,body_rhs,_=eta_expand_otf~subst~scope:unifscopepref_lhspref_rhsbody_lhsbody_rhsinlet(hd_lhs,args_lhs),(hd_rhs,args_rhs)=T.as_appbody_lhs,T.as_appbody_rhsinifT.equalbody_lhsbody_rhsthen(auxsubstrest)else(matchT.viewhd_lhs,T.viewhd_rhswith|T.DBi,T.DBj->ifi=jthendecompose_and_contargs_lhsargs_rhsrestflagsubstelseOSeq.empty|T.Constf,T.Constg->ifID.equalfg&&List.lengthargs_lhs=List.lengthargs_rhsthendecompose_and_contargs_lhsargs_rhsrestflagsubstelseOSeq.empty|T.AppBuiltin(b1,args1),T.AppBuiltin(b2,args2)->letargs_lhs=args_lhs@args1andargs_rhs=args_rhs@args2inifBuiltin.equalb1b2&&List.lengthargs_lhs=List.lengthargs_rhsthen(letargs_lhs,args_rhs=Unif.norm_logical_disagreementsb1args_lhsargs_rhsindecompose_and_cont(args_lhs)(args_rhs)restflagsubst)elseOSeq.empty|_whendifferent_rigid_headshd_lhshd_rhs->OSeq.empty|_->tryletmgu=CCList.find_map(funalg->trySome(alg(lhs,unifscope)(rhs,unifscope)subst)with|P.NotInFragment->None|P.NotUnifiable->raiseUnif.Fail)(P.frag_algs())inmatchmguwith|Somesubsts->(* We assume that the substitution was augmented so that it is mgu for
lhs and rhs *)CCList.map(funsub()->auxsubrest())substs|>OSeq.of_list|>P.oracle_composer|None->letargs_unif=ifT.is_varhd_lhs&&T.is_varhd_rhs&&T.equalhd_lhshd_rhsthendecompose_and_contargs_lhsargs_rhsrestflagsubstelseOSeq.emptyinletall_oracles=P.pb_oracle(body_lhs,unifscope)(body_rhs,unifscope)flagsubstunifscopeinletoracle_unifs=OSeq.map(funsub_flag_opt->matchsub_flag_optwith|None->OSeq.returnNone|Some(sub',flag')->tryletsubst'=Subst.mergesubstsub'inincrbind_cnt;delay(fun()->auxsubst'((lhs,rhs,flag')::rest)())withSubst.InconsistentBinding_->OSeq.empty)all_oracles|>P.oracle_composerinletinterleaved=OSeq.interleaveoracle_unifsargs_unifinif!bind_cnt=0&&rootthen(OSeq.consNoneinterleaved)elseinterleavedwithUnif.Fail->OSeq.empty)inaux~root:truesubstproblemlettry_lfho_unif((s,_)ast0)((t,_)ast1)=(* term is eligible for LFHO unif if it has applied variables
but has no lambdas -- we want to avoid FO terms to avoid
returning same unifier twice (since our unif algo will also
compute FO unifier) *)leteligible_for_lfhot=letexceptionLambdaFoundinletno_lamst=ifIter.existsT.is_fun(T.Seq.subterms~include_builtin:truet)thenraiseLambdaFoundelsetrueinletrecauxt=matchT.viewtwith|T.App(hd,args)whenT.is_consthd->List.for_allno_lamsargs|T.App(hd,args)->List.existsaux(hd::args)|T.AppBuiltin(_,args)->List.existsauxargs|T.Fun_->raiseLambdaFound(* unif algo can easily take care of naked vars and consts *)|_->falseintryauxtwithLambdaFound->falseinifFlex_state.get_exnPUP.k_try_lfhoP.flex_state&&eligible_for_lfhos&&eligible_for_lfhotthen(tryOSeq.return(Some(Unif_subst.subst(Unif.FO.unify_fullt0t1)))withUnif.Fail->OSeq.empty)elseOSeq.emptyletunify_scopedt0st1s=letlhs,rhs,unifscope,subst=P.identify_scopet0st1sinletbind_cnt=ref0intryOSeq.append(try_lfho_unift0st1s)(do_unif~bind_cnt[(lhs,rhs,P.init_flag)]substunifscope)|>OSeq.map(funopt->CCOpt.map(funsubst->letnormt=T.normalize_bools@@Lambda.eta_expand@@Lambda.snftinletl=norm@@S.FO.applySubst.Renaming.nonesubstt0sinletr=norm@@S.FO.applySubst.Renaming.nonesubstt1sinifnot((T.equallr)&&(Type.equal(Term.tyl)(Term.tyr)))then(CCFormat.printf"subst:@[%a@]@."Subst.ppsubst;CCFormat.printf"orig:@[%a@]@.=?=@.@[%a@]@."(Scoped.ppT.pp)t0s(Scoped.ppT.pp)t1s;CCFormat.printf"new:@[%a:%a@]@.=?=@.@[%a:%a@]@."T.pplType.pp(T.tyl)T.pprType.pp(T.tyr);assert(false));subst)opt)withUnif.Fail->OSeq.emptyend