123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118moduleT=TermmoduleUS=Unif_substexceptionNotUnif=PatternUnif.NotUnifiableexceptionDontKnow=PatternUnif.NotInFragment(*
an example showing that variable appearing under a rigid symbol can still
unify: F =?= \u. u (F (\vw.v)) (G F)
A unifier is {F |-> \u.u Y Z; G |-> Z}
Nonunifiable rigid path is the one with ends up with nonapplied variable,
or which is for the one that has an empty prefix for the original rhs
*)letnorm_deref=PatternUnif.norm_deref(* If there is a nonunifiable rigid path raises NotUnif
If the variable occurs on a flex path or unifiable rigid path returns None
Otherwise, variable does not occur and it returns the term var needs to be bound to *)letpath_check~subst~scopevart=letpref,_=T.open_funtinletno_prefix=CCList.is_emptyprefinletrecaux~depth~under_vart=lett=norm_derefsubst(t,scope)inmatchT.viewtwith|T.App(hd,args)whenT.is_varhd->assert(not(CCList.is_emptyargs));assert(not(US.FO.memsubst(T.as_var_exnhd,scope)));ifT.equalhdvarthen(ifunder_var||notno_prefixthenNoneelseraiseNotUnif)else(CCOpt.map(funargs'->ifT.same_largsargs'thentelseT.apphdargs')(aux_l~depth~under_var:trueargs))|T.App(hd,args)->assert(not(T.is_funhd));beginmatchaux~depth~under_varhdwith|None->None|Somehd'->CCOpt.map(funargs'->ifT.same_largsargs'&&T.equalhdhd'thentelseT.apphd'args')(aux_l~depth~under_varargs)end|T.AppBuiltin(b,args)->CCOpt.map(funargs'->ifT.same_largsargs'thentelseT.app_builtin~ty:(T.tyt)bargs')(aux_l~depth~under_varargs)|T.Var_->assert(not(US.FO.memsubst(T.as_var_exnt,scope)));ifT.equalvartthen(ifunder_var||Type.is_fun(T.tyt)thenNoneelseraiseNotUnif)elseSomet|T.Fun_->letpref_tys,body'=T.open_funtinletdepth_inc=List.lengthpref_tysinbeginmatchaux~depth:(depth+depth_inc)~under_varbody'with|None->None|Somet'->ifT.equalt'tthenSometelseSome(T.fun_lpref_tyst')end|T.DBiwheni>=depth->ifunder_varthenNoneelseraiseNotUnif|_->Sometandaux_l~depth~under_varargs=matchargswith|[]->Some[]|x::xs->letxs'=aux_l~depth~under_varxsinmatchaux~depth~under_varxwith|None->ignore(xs');None|Somet->CCOpt.map(funts->t::ts)xs'inaux~depth:0~under_var:falsetletunify_scoped?(subst=US.empty)?(counter=ref0)t0_st1_s=letdriverstscopesubst=lets,t=Lambda.eta_reduce@@norm_derefsubst(s,scope),Lambda.eta_reduce@@norm_derefsubst(t,scope)inifT.is_vars&&T.is_vartthen(ifT.equalstthensubstelseUS.FO.bindsubst(T.as_var_exns,scope)(t,scope))elseifnot(T.is_vars)&¬(T.is_vart)then(raiseDontKnow)else(letvar,rigid=ifT.is_varsthens,telset,sinmatchpath_check~subst~scopevarrigidwith|None->raiseDontKnow|Somerigid->assert(T.DB.is_closedrigid);US.FO.bindsubst(T.as_var_exnvar,scope)(rigid,scope))inletres=ifUS.is_emptysubstthen(lett0',t1',scope,subst=US.FO.rename_to_new_scope~countert0_st1_sindrivert0't1'scopesubst)else(ifScoped.scopet0_s!=Scoped.scopet1_sthen(raise(Invalid_argument"scopes should be the same"))else(lett0',t1'=fstt0_s,fstt1_sindrivert0't1'(Scoped.scopet0_s)subst))inletno_renaming=Subst.Renaming.noneinletl=Lambda.eta_reduce@@Lambda.snf@@Subst.FO.applyno_renaming(US.substres)t0_sinletr=Lambda.eta_reduce@@Lambda.snf@@Subst.FO.applyno_renaming(US.substres)t1_sinifnot((T.equallr)&&(Type.equal(Term.tyl)(Term.tyr)))then(CCFormat.printf"orig:@[%a@]=?=@[%a@]@."(Scoped.ppT.pp)t0_s(Scoped.ppT.pp)t1_s;CCFormat.printf"before:@[%a@]@."US.ppsubst;CCFormat.printf"after:@[%a@]@."US.ppres;assert(false););res