123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515(* This file is free software, part of Logtk. See file "license" for more details. *)(** {1 Substitutions} *)moduleT=InnerTermtypeterm=T.ttypevar=T.tHVar.tmoduleVarInt=structtypet=varScoped.tletcompare=Scoped.compare(funab->CCOrd.int(HVar.ida)(HVar.idb))letequal=Scoped.equal(funab->HVar.ida=HVar.idb)lethash=Scoped.hashHVar.hashendmoduleVarWTypeInt=structtypet=varScoped.tletcompare=Scoped.compare(HVar.compareT.compare)letequal=Scoped.equal(HVar.equalT.equal)lethash=Scoped.hashHVar.hashendmoduleH=Hashtbl.Make(VarInt)moduleM=CCMap.Make(VarInt)(* module MWTy = CCMap.Make(VarWTypeInt) *)moduleIntMap=Map.Make(CCInt)(** {2 Renaming} *)moduleRenaming=structtypet=|R_none|R_someof{mutablemap:varM.t;mutablen:int;}letnone=R_nonelet[@inline]is_none=functionR_none->true|R_some_->falselet[@inline]create()=R_some{map=M.empty;n=0}(* rename variable *)letrenamer((v,_)asvar)=matchrwith|R_none->v|R_somer->tryM.findvarr.mapwithNot_found->letv'=HVar.make~ty:(HVar.tyv)r.ninr.n<-r.n+1;r.map<-M.addvarv'r.map;v'(* rename variable (after specializing its type if needed) *)letrename_with_typerenaming(v,sc_v)new_ty=letv'=renamerenaming(v,sc_v)inHVar.castv'~ty:new_tyend(* map from scoped variables, to scoped terms *)typet=T.tScoped.tM.ttypesubst=tletempty=M.emptylet[@inline]is_emptys=M.is_emptyslet[@inline]find_exnsubstv=M.findvsubstlet[@inline]findsubstv=trySome(M.findvsubst)withNot_found->Nonelet[@inline]memsubstv=M.memvsubstlet[@unroll2]recderefsubst((t,sc_t)asterm)=matchT.viewtwith|T.Varv->beginmatchfindsubst(v,sc_t)with|Somet'->derefsubstt'|None->termend|_->term(** Recursively lookup a variable in the substitution, until we get a value
that is not a variable or that is not bound *)letget_varsubstv=matchfindsubstvwith|None->None|Somet->Some(derefsubstt)exceptionInconsistentBindingofvarScoped.t*termScoped.t*termScoped.tlet()=Printexc.register_printer(function|InconsistentBinding(v,t1,t2)->letmsg=CCFormat.sprintf"@[<2>inconsistent binding@ for %a: %a@ and %a@]"(Scoped.ppT.pp_var)v(Scoped.ppT.pp)t1(Scoped.ppT.pp)t2inSomemsg|_->None)letbind:t->varScoped.t->T.tScoped.t->t=funsubstvt->assert(not(M.memvsubst));M.addvtsubstletupdate:t->varScoped.t->T.tScoped.t->t=funsubstvt->assert(M.memvsubst);M.addvtsubstlet[@inline]removesubstv=M.removevsubstletfilter_scopesubstsc=M.filter(fun(_,sc_v)_->sc=sc_v)substletmerges1s2=M.merge(funvb1b2->matchb1,b2with|None,_->b2|_,None->b1|Somet1,Somet2->ifScoped.equalT.equalt1t2thenSomet1elseraise(InconsistentBinding(v,t1,t2)))s1s2(*
let compose s1 s2 = failwith "Subst.compose: not implemented"
*)letfoldfaccsubst=M.fold(funvtacc->faccvt)substaccletiterfsubst=M.iter(funvt->fvt)subst(* set of variables bound by subst, with their scope *)letdomainsk=M.iter(funv_->kv)s(* set of terms that some variables are bound to by the substitution *)letcodomainsk=M.iter(fun_t->kt)s(* is the substitution a renaming? *)letis_renamingsubst=letrev=codomainsubst|>Iter.filter_map(fun(t,sc_t)->matchT.viewtwith|T.Varv->Some((v,sc_t),())|_->None)|>M.of_iterin(* as many variables in codomain as variables in domain *)M.cardinalrev=M.cardinalsubst(* variables introduced by the subst *)letintroducedsubstk=M.iter(fun_(t,sc_t)->T.Seq.varst(funv->k(v,sc_t)))substletnormalizesubst:t=letrecauxsct=ifT.equaltT.tTypethentelse(letty=auxsc(T.ty_exnt)inmatchT.viewtwith|T.Varv->(* follow binding if it stays in the same domain *)beginmatchfindsubst(v,sc)with|Some(u,sc')whensc=sc'->auxscu|_->T.var(HVar.cast~tyv)end|T.DBi->T.bvar~tyi|T.Constid->T.const~tyid|T.App(f,l)->T.app~ty(auxscf)(List.map(auxsc)l)|T.AppBuiltin(b,l)->T.app_builtinb~ty(List.map(auxsc)l)|T.Bind(b,varty,body)->letvarty=auxscvartyinT.bindb~ty~varty(auxscbody))inM.map(fun(t,sc)->auxsct,sc)substlet[@inline]mapfsubst=M.map(fun(t,sc)->ft,sc)substlet[@inline]filterfsubst=M.filterfsubstlet[@inline]to_itersubstk=M.iter(funvt->k(v,t))substlet[@inline]to_listsubst=M.fold(funvtacc->(v,t)::acc)subst[]letof_iter?(init=empty)seq=Iter.fold(funsubst(v,t)->bindsubstvt)initseqletof_list?(init=empty)l=matchlwith|[]->init|_::_->List.fold_left(funsubst(v,t)->bindsubstvt)initllet[@inline]equal(s1:t)s2:bool=M.equal(Scoped.equalT.equal)s1s2let[@inline]compares1s2=M.compare(Scoped.compareT.compare)s1s2let[@inline]hash(s:t):int=CCHash.(iter(pair(Scoped.hashHVar.hash)(Scoped.hashT.hash)))(M.to_iters)letpp_bindingsoutsubst=letpp_bindingout(v,t)=Format.fprintfout"@[<2>@[%a@] @<1>→@ @[%a@]@]"(Scoped.ppT.pp_var)v(Scoped.ppT.pp)tinUtil.pp_iter~sep:", "pp_bindingout(to_itersubst)letppoutsubst=Format.fprintfout"{@[<hv>%a@]}"pp_bindingssubstletto_string=CCFormat.to_stringpp(** {2 Applying a substitution} *)letapply_aux~svsubst~f_renametsc=letrecauxtsc_tdepth=ifT.is_groundtthentelse(matchT.tytwith|T.NoType->assert(T.equalT.tTypet);t|T.HasTypety->letty'=auxtysc_tdepthinletres=beginmatchT.viewtwith|T.Constid->(* regular constant *)ifT.equaltyty'thentelseT.const~ty:ty'id|T.DBi->ifT.equaltyty'thentelseT.bvar~ty:ty'i|T.Varv->(* the most interesting cases!
switch depending on whether [t] is bound by [subst] or not *)beginmatchfind_exnsubst(v,sc_t)with|(t',sc')->(* NOTE: if [t'] is not closed, we assume that it
is always replaced in a context where variables
are properly bound. Typically, that means only
in rewriting. *)(* also apply [subst] to [t'] *)letshifted=ifsv!=-1thenT.DB.shiftdeptht'elset'inauxshiftedsc'depth|exceptionNot_found->(* rename the variable using [f_rename] *)letv'=f_rename(v,sc_t)ty'inT.varv'end|T.Bind(s,varty,sub_t)->letvarty'=auxvartysc_t(depth+1)inletsub_t'=auxsub_tsc_t(depth+1)inletres=T.bind~varty:varty'~ty:ty'ssub_t'in(* Util.debugf 1 ("Before body: %a, after body: %a") *)(* (fun k -> k T.pp t T.pp res); res *)res|T.App(hd,l)->lethd'=auxhdsc_tdepthinletl'=aux_listlsc_tdepthinifT.equaltyty'&&T.equalhdhd'&&T.same_lll'thentelseT.app~ty:ty'hd'l'|T.AppBuiltin(s,l)->letl'=aux_listlsc_tdepthinifT.equaltyty'&&T.same_lll'thentelseT.app_builtin~ty:ty'sl'endinres)andaux_listlscdepth=matchlwith|[]->[]|[t1]->[auxt1scdepth]|t1::t2::l'->auxt1scdepth::auxt2scdepth::aux_listl'scdepthinauxtscsv(* Apply substitution to a term and rename variables not bound by [subst]*)letapply?(shift_vars=(-1))renamingsubst(t,sc)=ifis_emptysubst&&Renaming.is_nonerenamingthentelse(apply_aux~sv:shift_varssubst~f_rename:(Renaming.rename_with_typerenaming)tsc)(** {2 Specializations} *)moduletypeSPECIALIZED=sigtypetermtypet=substvalfind_exn:t->varScoped.t->termScoped.tvalget_var:t->varScoped.t->termScoped.toptionvalderef:t->termScoped.t->termScoped.tvalapply:?shift_vars:int->Renaming.t->t->termScoped.t->term(** Apply the substitution to the given term/type.
@param renaming used to desambiguate free variables from distinct scopes *)valbind:t->varScoped.t->termScoped.t->t(** Add [v] -> [t] to the substitution. Both terms have a context.
@raise InconsistentBinding if [v] is already bound in
the same context, to another term. *)valupdate:t->varScoped.t->termScoped.t->t(** Replaces [v] -> ? by [v] -> [t] in the substitution. Both terms have a context.
@raise InconsistentBinding if [v] is not yet bound in the same context. *)valof_list:?init:t->(varScoped.t*termScoped.t)list->tendmoduleTy:SPECIALIZEDwithtypeterm=Type.t=structtypeterm=Type.ttypet=substletderefsubstt=lett,sc=derefsubst(t:termScoped.t:>T.tScoped.t)inType.of_term_unsafet,scletget_varsubstv=leto=get_varsubstvinCCOpt.map(Scoped.mapType.of_term_unsafe)oletfind_exnsubstv=lett=find_exnsubstvinScoped.mapType.of_term_unsafetletapply?(shift_vars=(-1))renamingsubstt=Type.of_term_unsafe(apply~shift_varsrenamingsubst(t:termScoped.t:>T.tScoped.t))letbind=(bind:>t->varScoped.t->termScoped.t->t)letupdate=(update:>t->varScoped.t->termScoped.t->t)letof_list=(of_list:>?init:t->(varScoped.t*termScoped.t)list->t)endmoduleFO=structtypeterm=Term.ttypet=substletderefsubstt=lett,sc=derefsubst(t:termScoped.t:>T.tScoped.t)inTerm.of_term_unsafet,scletget_varsubstv=leto=get_varsubstvinCCOpt.map(Scoped.mapTerm.of_term_unsafe)oletfind_exnsubstv=lett=find_exnsubstvinScoped.mapTerm.of_term_unsafetletapply?(shift_vars=(-1))renamingsubstt=Term.of_term_unsafe(apply~shift_varsrenamingsubst(t:termScoped.t:>T.tScoped.t))letapply_l?(shift_vars=(-1))renamingsubst(l,sc)=List.map(funt->apply~shift_varsrenamingsubst(t,sc))lletcompose~scopes1s2=(* Format.printf "Composing: @[ %a = %a @]\n" pp s1 pp s2; *)letsubs_l1=to_lists1inletsubs_as_map=(List.map(fun((v,sc_v),(t,sc_t))->((v,sc_v),(((Lambda.snf(applyRenaming.nones2(Term.of_term_unsafet,sc_t))):term:>T.t),scope)))subs_l1)@(to_lists2)in(of_listsubs_as_map)letcanonize_neg_vars~var_set=letmax_id=T.VarSet.max_elt_optvar_setinmatchmax_idwith|Someid->letmax_id=ref(CCInt.max(HVar.idid)(-1))inT.VarSet.fold(funvsubst->letv_id=HVar.idvinifv_id<0then(matchget_varsubst((v:>InnerTerm.tHVar.t),0)with|Some_->subst|None->(incrmax_id;letrenamed_var=T.var(HVar.make~ty:(HVar.tyv)!max_id)inbindsubst((v:>InnerTerm.tHVar.t),0)(renamed_var,0)))elsesubst)var_setempty|None->emptyletcanonize_all_varst=apply(Renaming.create())empty(t,0)letbind=(bind:>t->varScoped.t->termScoped.t->t)letupdate=(update:>t->varScoped.t->termScoped.t->t)letof_list=(of_list:>?init:t->(varScoped.t*termScoped.t)list->t)letbind'=(bind:>t->Type.tHVar.tScoped.t->termScoped.t->t)letupdate'=(update:>t->Type.tHVar.tScoped.t->termScoped.t->t)letof_list'=(of_list:>?init:t->(Type.tHVar.tScoped.t*termScoped.t)list->t)(* let to_list = (to_list :> t -> (Type.t HVar.t Scoped.t * term Scoped.t) list ) *)letmapfs=map(funt->(f(Term.of_term_unsafet):term:>T.t))sletfilterfs=filter(fun(v,sc_v)(t,sc_t)->f(HVar.update_ty~f:Type.of_term_unsafev,sc_v)(Term.of_term_unsafet,sc_t))sletiterfs=iter(fun(v,sc_v)(t,sc_t)->letv=HVar.update_ty~f:Type.of_term_unsafevinlett=Term.of_term_unsafetinf(v,sc_v)(t,sc_t))sletunleak_variablessubs=letsubs_l=to_listsubsinletunleaked_l,new_sk=List.fold_right(fun((v,sc_v),(t,sc_t))(l,sk_map)->lett=Term.of_term_unsafetinUtil.debugf1" unleaking in unleak_vars : %a"(funk->kTerm.ppt);lett',sk_map=Term.DB.skolemize_loosely_bound~already_sk:sk_maptinletv'=(HVar.update_ty~f:Type.of_term_unsafev,sc_v)in(v',(t',sc_t))::l,sk_map)subs_l([],Term.IntMap.empty)inof_list'unleaked_l,List.mapsnd(Term.IntMap.bindingsnew_sk)letsubset_is_renaming~subset~res_scopesubst=tryletsubset=List.filter(funv->letder_t,der_sc=derefsubstvinifder_sc!=sndvthen(der_sc=res_scope)else(der_sc=res_scope&¬(Term.equal(fstv)der_t)))subsetinletderefed_vars=CCList.map(funv->letderefed=derefsubstvinifnot(Term.is_var(fstderefed))then(raise(invalid_arg"found a non-variable"))elsederefed)subset|>CCList.sort_uniq~cmp:(Scoped.compareTerm.compare)inList.lengthderefed_vars=List.lengthsubsetwithInvalid_argument_->falseend(** {2 Projections for proofs} *)moduleProjection=structtypet={scope:Scoped.scope;subst:subst;renaming:Renaming.t;}let[@inline]scopet=t.scopelet[@inline]substt=t.substlet[@inline]renamingt=t.renaming(* actual constructor from a substitution *)letbindings(p:t):(var*term)list=fold(funl(v,sc_v)(t,sc_t)->ifsc_v=p.scopethen(lett=applyp.renamingp.subst(t,sc_t)in(v,t)::l)elsel)[]p.substletas_inst?allow_free_db~ctx(sp:t)(vars:_HVar.tlist):(_,_)Var.Subst.t=List.map(funv->lett_v=Term.varvinlett=FO.apply(renamingsp)(substsp)((t_v,scopesp))inTerm.Conv.var_to_simple_varctxv,Term.Conv.to_simple_term?allow_free_dbctxt)vars|>Var.Subst.of_listlet[@inline]makerenaming(subst,sc):t={scope=sc;subst;renaming;}let[@inline]is_empty(p:t):bool=is_emptyp.subst&&Renaming.is_nonep.renamingletppout(p:t):unit=Format.fprintfout"%a[%d]"ppp.substp.scopeend