123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276(** Checking that a rule preserves typing (subject reduction property). *)openLplibopenTimedopenCommonopenErroropenCoreopenTermopenPrintopenParsing(** Logging function for typing. *)letlog_subj=Logger.make's'"subj""subject-reduction"letlog_subj=log_subj.pp(** [build_meta_type p k] builds the type “Πx1:A1,⋯,xk:Ak,A(k+1)” where the
type “Ai = Mi[x1,⋯,x(i-1)]” is defined as the metavariable “Mi” which has
arity “i-1” and type “Π(x1:A1) ⋯ (x(i-1):A(i-1)), TYPE”, and adds the
metavariables in [p]. *)letbuild_meta_type:problem->int->term=funpk->assert(k>=0);letxs=Array.initk(new_tvar_ind"x")inletts=Array.map_Varixsin(* We create the types for the “Mi” metavariables. *)letty_m=Array.make(k+1)_Typeinfori=0tokdoforj=(i-1)downto0doty_m.(i)<-_Prodty_m.(j)(Bindlib.bind_varxs.(j)ty_m.(i))donedone;(* We create the “Ai” terms and the “Mi” metavariables. *)letfi=letm=LibMeta.freshp(Bindlib.unboxty_m.(i))iin_Metam(Array.subts0i)inleta=Array.init(k+1)fin(* We finally construct our type. *)letres=refa.(k)infori=k-1downto0dores:=_Proda.(i)(Bindlib.bind_varxs.(i)!res)done;Bindlib.unbox!res(** [patt_to_tenv vars t] converts pattern variables of [t] into corresponding
term environment variables of [vars]. The index [i] in [Patt(Some(i),_,_)]
indicates the index of the corresponding variable in [vars]. *)letpatt_to_tenv:tevararray->term->tbox=funvars->letget_tei=matchiwith|None->assertfalse(* Cannot appear in LHS. *)|Some(i)->tryvars.(i)withInvalid_argument(_)->assertfalseinletrectranst=matchunfoldtwith|Vari(x)->_Varix|Symb(s)->_Symbs|Abst(a,b)->let(x,b)=Bindlib.unbindbin_Abst(transa)(Bindlib.bind_varx(transb))|Appl(t,u)->_Appl(transt)(transu)|Patt(i,_,a)->_TEnv(_TE_Vari(get_tei))(Array.maptransa)|Type->assertfalse(* Cannot appear in LHS. *)|Kind->assertfalse(* Cannot appear in LHS. *)|Prod(_,_)->assertfalse(* Cannot appear in LHS. *)|LLet(_,_,_)->assertfalse(* Cannot appear in LHS. *)|Plac_->assertfalse(* Cannot appear in LHS. *)|Meta(_,_)->assertfalse(* Cannot appear in LHS. *)|TEnv(_,_)->assertfalse(* Cannot appear in LHS. *)|Wild->assertfalse(* Cannot appear in LHS. *)|TRef(_)->assertfalse(* Cannot appear in LHS. *)intrans(** Mapping of pattern variable names to their reserved index. *)typeindex_tbl=(string,int)Hashtbl.t(** [symb_to_tenv pr syms htbl t] builds a RHS for the pre-rule [pr]. The term
[t] is expected to be a version of the RHS of [pr] whose term environments
have been replaced by function symbols of [syms]. This function builds the
reverse transformation, replacing symbols by the term environment variable
they stand for. Here, [htbl] should give the index in the RHS environment
for the symbols of [syms] that have corresponding [term_env] variable. The
pre-rule [pr] is provided to give access to these variables and also their
expected arities. *)letsymb_to_tenv:Scope.pre_rulePos.loc->symlist->index_tbl->term->tbox=fun{elt={pr_vars=vars;pr_arities=arities;_};pos}symshtblt->letrecsymb_to_tenvt=let(h,ts)=get_argstinletts=List.mapsymb_to_tenvtsinlet(h,ts)=matchhwith|Symb(f)whenList.memqfsyms->leti=tryHashtbl.findhtblf.sym_namewithNot_found->(* A symbol may also come from a metavariable that appeared in the
type of a metavariable that was replaced by a symbol. We do not
have concrete examples of that happening yet. *)fatalpos"Introduced symbol [%s] cannot be removed."f.sym_nameinlet(ts1,ts2)=List.cuttsarities.(i)in(_TEnv(_TE_Varivars.(i))(Array.of_listts1),ts2)|Symb(s)->(_Symbs,ts)|Vari(x)->(_Varix,ts)|Type->(_Type,ts)|Kind->(_Kind,ts)|Abst(a,b)->let(x,t)=Bindlib.unbindbinletb=Bindlib.bind_varx(symb_to_tenvt)in(_Abst(symb_to_tenva)b,ts)|Prod(a,b)->let(x,t)=Bindlib.unbindbinletb=Bindlib.bind_varx(symb_to_tenvt)in(_Prod(symb_to_tenva)b,ts)|LLet(a,t,b)->let(x,u)=Bindlib.unbindbinletb=Bindlib.bind_varx(symb_to_tenvu)in(_LLet(symb_to_tenva)(symb_to_tenvt)b,ts)|Meta(_,_)->fatalpos"A metavariable could not be instantiated in the RHS."|Plac_->fatalpos"A placeholder hasn't been instantiated in the RHS."|TEnv(_,_)->assertfalse(* TEnv have been replaced in [t]. *)|Appl(_,_)->assertfalse(* Cannot appear in RHS. *)|Patt(_,_,_)->assertfalse(* Cannot appear in RHS. *)|Wild->assertfalse(* Cannot appear in RHS. *)|TRef(_)->assertfalse(* Cannot appear in RHS. *)in_Appl_listhtsinsymb_to_tenvt(** [check_rule r] checks whether the pre-rule [r] is well-typed in
signature state [ss] and then construct the corresponding rule. Note that
[Fatal] is raised in case of error. *)letcheck_rule:Scope.pre_rulePos.loc->rule=fun({pos;elt}aspr)->letScope.{pr_sym=s;pr_lhs=lhs;pr_vars=vars;pr_rhs;pr_arities=arities;pr_xvars_nb;_}=eltin(* Check that the variables of the RHS are in the LHS. *)ifpr_xvars_nb<>0then(letxvars=Array.drop(Array.lengthvars-pr_xvars_nb)varsinfatalpos"Unknown pattern variables: %a"(Array.ppvar",")xvars);letarity=List.lengthlhsinifLogger.log_enabled()thenbegin(* The unboxing here could be harmful since it leads to [pr_rhs] being
unboxed twice. However things should be fine here since the result is
only used for printing. *)letrhs=Bindlib.(unbox(bind_mvarvarspr_rhs))inletnaive_rule={lhs;rhs;arity;arities;vars;xvars_nb=0;rule_pos=pos}inlog_subj(Color.red"%a")sym_rule(s,naive_rule);end;(* Replace [Patt] nodes of LHS with corresponding elements of [vars]. *)letlhs_vars=_Appl_Symbs(List.map(patt_to_tenvvars)lhs)inletp=new_problem()inletmetas=letfi_=letarity=arities.(i)in(*FIXME: build_meta_type should take a sort as argument as some pattern
variables are types and thus of sort KIND! *)LibMeta.freshp(build_meta_typeparity)arityinArray.mapifvarsin(* Substitute them in the LHS and in the RHS. *)letlhs_with_metas,rhs_with_metas=letlhs_rhs=Bindlib.box_pairlhs_varspr_rhsinletb=Bindlib.unbox(Bindlib.bind_mvarvarslhs_rhs)inletmeta_to_tenvm=letxs=Array.initm.meta_arity(new_tvar_ind"x")inletts=Array.map_VarixsinTE_Some(Bindlib.unbox(Bindlib.bind_mvarxs(_Metamts)))inBindlib.msubstb(Array.mapmeta_to_tenvmetas)inifLogger.log_enabled()thenlog_subj"replace pattern variables by metavariables:@ %a ↪ %a"termlhs_with_metastermrhs_with_metas;(* Infer the typing constraints of the LHS. *)matchInfer.infer_noexnp[]lhs_with_metaswith|None->fatalpos"The LHS is not typable."|Some(lhs_with_metas,ty_lhs)->(* Try to simplify constraints. Don't check typing when instantiating
a metavariable. *)ifnot(Unif.solve_noexn~type_check:falsep)thenfatalpos"The LHS is not typable.";letnorm_constr(c,t,u)=(c,Eval.snf[]t,Eval.snf[]u)inletlhs_constrs=List.mapnorm_constr!p.unsolvedinifLogger.log_enabled()thenlog_subj"@[<v>LHS type: %a@ LHS constraints: %a@ %a ↪ %a@]"termty_lhsconstrslhs_constrstermlhs_with_metastermrhs_with_metas;(* We instantiate all the uninstantiated metavariables of the LHS (including
those appearing in the types of these metavariables) using fresh function
symbols. We also keep a list of those symbols. *)letsymbols=letsymbols=Stdlib.ref[]inletrecinstantiatem=match!(m.meta_value)with|Some_->(* Instantiate recursively the meta-variables of the definition. *)lett=mk_Meta(m,Array.makem.meta_aritymk_Kind)inLibMeta.itertrueinstantiate[]t|None->(* Instantiate recursively the meta-variables of the type. *)LibMeta.itertrueinstantiate[]!(m.meta_type);(* Instantiation of [m]. *)lets=letname=Pos.none@@Printf.sprintf"$%d"m.meta_keyinTerm.create_sym(Sign.current_path())PrivatDefinEagerfalsename!(m.meta_type)[]inStdlib.(symbols:=s::!symbols);(* Build a definition for [m]. *)letxs=Array.initm.meta_arity(new_tvar_ind"x")inlets=_Symbsinletdef=Array.fold_left(funtx->_Applt(_Varix))sxsinm.meta_value:=Some(Bindlib.unbox(Bindlib.bind_mvarxsdef))inArray.iterinstantiatemetas;Stdlib.(!symbols)inifLogger.log_enabled()thenlog_subj"replace LHS metavariables by function symbols:@ %a ↪ %a"termlhs_with_metastermrhs_with_metas;(* TODO complete the constraints into a set of rewriting rule on
the function symbols of [symbols]. *)(* Compute the constraints for the RHS to have the same type as the LHS. *)letp=new_problem()inmatchInfer.check_noexnp[]rhs_with_metasty_lhswith|None->fatalpos"The RHS does not have the same type as the LHS."|Somerhs_with_metas->(* Solving the typing constraints of the RHS. *)ifnot(Unif.solve_noexnp)thenfatalpos"The rewriting rule does not preserve typing.";letrhs_constrs=List.mapnorm_constr!p.unsolvedin(* [matches p t] says if [t] is an instance of [p]. *)letmatchespt=letrecmatchessl=matchlwith|[]->()|(p,t)::l->(*if Logger.log_enabled() then
log_subj "matches [%a] [%a]" term p term t;*)matchunfoldp,unfoldtwith|Varix,_->beginmatchVarMap.find_optxswith|Someu->ifEval.eq_modulo[]tuthenmatchesslelseraiseExit|None->matches(VarMap.addxts)lend|Symbf,Symbgwhenf==g->matchessl|Appl(t1,u1),Appl(t2,u2)->matchess((t1,t2)::(u1,u2)::l)|_->raiseExitintrymatchesVarMap.empty[p,t];truewithExit->falsein(* Function saying if a constraint is an instance of another one. *)letis_inst((_c1,t1,u1)asx1)((_c2,t2,u2)asx2)=ifLogger.log_enabled()thenlog_subj"is_inst [%a] [%a]"constrx1constrx2;letconstu=add_args(mk_SymbUnif_rule.equiv)[t;u]inmatches(const1u1)(const2u2)||matches(const1u1)(consu2t2)inletis_lhs_constrrc=List.exists(funlc->is_instlcrc)lhs_constrsinletcs=List.filter(funrc->not(is_lhs_constrrc))rhs_constrsinifcs<>[]thenbeginList.iter(fatal_msg"Cannot solve %a@."constr)cs;fatalpos"Unable to prove type preservation."end;(* We build a map allowing to find a variable index from its key. *)lethtbl:index_tbl=Hashtbl.create(Array.lengthvars)inArray.iteri(funim->Hashtbl.addhtbl(Printf.sprintf"$%d"m.meta_key)i)metas;(* Replace metavariable symbols by term_env variables, and bind them. *)letrhs=symb_to_tenvprsymbolshtblrhs_with_metasin(* TODO environment minimisation ? *)(* Construct the rule. *)letrhs=Bindlib.unbox(Bindlib.bind_mvarvarsrhs)in{lhs;rhs;arity;arities;vars;xvars_nb=0;rule_pos=pos}