123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354(** Generation of induction principles.
We only consider first-order dependent types (no functional
arguments). Polymorphic types can be encoded by using codes. In case of a
mutually defined types, we generate an induction for each type. A single
induction principle can be defined from those individual induction
principles by using a conjunction operator.
In the OCaml code, the prefix "ind" is used for inductive types. The prefix
"rec" is used for recursors, aka induction principles. *)openLplibopenTimedopenCommonopenPosopenErroropenCoreopenTermopenPrintopenParsingopenSyntax(** Logging function for generating of inductive principle. *)letlog_ind=Logger.make'g'"indu""induction principles generation"letlog_ind=log_ind.pp(** Type for inductive type definitions. *)typeinductive=(sym*symlist)list(** Builtin configuration for induction. *)typeconfig={symb_Prop:sym(** : TYPE. Type of propositions. *);symb_prf:sym(** : Prop → TYPE.
Interpretation of propositions as types. *)}(** [get_config ss pos] build the configuration using [ss]. *)letget_config:Sig_state.t->Pos.popt->config=funsspos->letbuiltin=Builtin.getssposin{symb_Prop=builtin"Prop";symb_prf=builtin"P"}(** [prf_of p c ts t] returns the term [c.symb_prf (p t1 ... tn t)] where ts =
[ts1;...;tsn]. *)letprf_of:config->tvar->tboxlist->tbox->tbox=funcptst->_Appl_Symbc.symb_prf[_Appl(_Appl_list(_Varip)ts)t](** compute safe prefixes for predicate and constructor argument variables. *)letgen_safe_prefixes:inductive->string*string*string=letletterc=matchcwith'a'|'p'|'x'->true|_->falseinfunind_list->letclashing_names=letadd_name_from_symsetsym=lets=sym.sym_nameinifs<>""&&letters.[0]thenExtra.StrSet.addssetelsesetinletadd_names_from_indset(ind_sym,cons_sym_list)=letset=add_name_from_symsetind_syminList.fold_leftadd_name_from_symsetcons_sym_listinList.fold_leftadd_names_from_indExtra.StrSet.emptyind_listinleta_str=Extra.get_safe_prefix"a"clashing_namesinletp_str=Extra.get_safe_prefix"p"clashing_namesinletx_str=Extra.get_safe_prefix"x"clashing_namesina_str,p_str,x_str(** Type of maps associating to every inductive type some data useful for
generating the induction principles. *)typedata={ind_var:tvar(** predicate variable *);ind_type:tbox(** predicate variable type *);ind_conclu:tbox(** induction principle conclusion *)}typeind_pred_map=(sym*data)list(** [ind_typ_with_codom pos ind_sym env codom x_str a] assumes that [a] is of
the form [Π(i1:a1),...,Π(in:an), TYPE]. It then generates a [tbox] similar
to this type except that [TYPE] is replaced by [codom [i1;...;in]]. The
string [x_str] is used as prefix for the variables [ik]. *)letind_typ_with_codom:popt->sym->Env.t->(tboxlist->tbox)->string->term->tbox=funposind_symenvcodomx_stra->letrecaux:tvarlist->int->term->tbox=funxska->matchget_argsawith|(Type,_)->codom(List.rev_map_Varixs)|(Prod(a,b),_)->let(x,b)=LibTerm.unbind_name(x_str^string_of_intk)bin_Prod(lifta)(Bindlib.bind_varx(aux(x::xs)(k+1)b))|_->fatalpos"The type of %a is not supported"symind_syminaux(List.map(fun(_,(v,_,_))->v)env)0a(** [create_ind_pred_map pos c arity ind_list a_str p_str x_str] builds an
[ind_pred_map] from [ind_list]. The resulting list is in reverse order wrt
[ind_list]. [a_str] is used as prefix for parameter names, [p_str] is used
as prefix for predicate variable names, and [x_str] is used as prefix for
the names of the variable arguments of predicate variables. *)letcreate_ind_pred_map:popt->config->int->inductive->string->string->string->tvararray*Env.t*ind_pred_map=funposcarityind_lista_strp_strx_str->(* create parameters *)letvs=Array.initarity(new_tvar_inda_str)inletenv=matchind_listwith|[]->assertfalse(* there must be at least one type definition *)|(ind_sym,_)::_->fst(Env.of_prod_using[]vs!(ind_sym.sym_type))in(* create the ind_pred_map *)letcreate_sym_pred_datai(ind_sym,_)=(* predicate variable *)letind_var=new_tvar_indp_striin(* predicate type *)letcodomts=_Impl(_Appl_Symbind_symts)(_Symbc.symb_Prop)inleta=snd(Env.of_prod_using[]vs!(ind_sym.sym_type))inletind_type=ind_typ_with_codomposind_symenvcodomx_strain(* predicate conclusion *)letcodomts=letx=new_tvarx_strinlett=Bindlib.bind_varx(prf_ofcind_var(List.remove_headsarityts)(_Varix))in_Prod(_Appl_Symbind_symts)tinletind_conclu=ind_typ_with_codomposind_symenvcodomx_strain(ind_sym,{ind_var;ind_type;ind_conclu})invs,env,List.rev_mapicreate_sym_pred_dataind_list(** [fold_cons_type] generates some value of type ['c] by traversing the
structure of [cons_sym.sym_type] and accumulating some data of type ['a].
[pos] is the position of the inductive command.
[ind_pred_map] is defined above.
[x_str] is a string used as prefix for generating variable names when
deconstructing a product with [LibTerm.unbind_name].
[ind_sym] is an inductive type symbol of [ind_pred_map].
[cons_sym] is a constructor symbol of [ind_sym].
[inj_var] injects traversed bound variables into the type ['var].
[init] is the initial value of type ['a].
The traversal is made by the function [fold : (xs : 'var list) -> (acc :
'a) -> term -> 'c] below. It keeps track of the variables [xs] we went
through (the last variable comes first) and some accumulator [acc] set to
[init] at the beginning.
During the traversal, there are several possible cases:
1) If the argument is a product of the form [Π x:t, u] with [t] of the form
[Π y₁:a₁, …, Π yₙ:aₙ, s ts] and [s] mapped to [p] in [ind_pred_map], then
the result is [rec_dom t x' v next] where [x' = inj_var (length xs) x], [v
= aux env s p ts x'], [env = y₁:a₁; …; yₙ:aₙ] and [next = fold (x'::xs)
acc' u] is the result of the traversal of [u] with the list of variables
extended with [x] and the accumulator [acc' = acc_rec_dom acc x' v].
2) If the type argument is a product [Πx:t, u] not of the previous form,
then the result is [nonrec_dom t x' next] where [next = fold (x'::xs) acc'
u] and [acc' = acc_nonrec_dom acc x'].
3) If the type argument is of the form [ind_sym ts], then the result is
[codom ts xs acc].
4) Any other case is an error. *)letfold_cons_type(pos:popt)(ind_pred_map:ind_pred_map)(x_str:string)(ind_sym:sym)(vs:tvararray)(cons_sym:sym)(inj_var:int->tvar->'var)(init:'a)(aux:Env.t->sym->tvar->termlist->'var->'aux)(acc_rec_dom:'a->'var->'aux->'a)(rec_dom:term->'var->'aux->'c->'c)(acc_nonrec_dom:'a->'var->'a)(nonrec_dom:term->'var->'c->'c)(codom:'varlist->'a->tvar->termlist->'c):'c=letrecfold:'varlist->int->'a->term->'c=funxsnacct->matchget_argstwith|(Symb(s),ts)->ifs==ind_symthenletd=List.assqind_symind_pred_mapincodomxsaccd.ind_vartselsefatalpos"%a is not a constructor of %a"symcons_symsymind_sym|(Prod(t,u),_)->letx,u=LibTerm.unbind_name(x_str^string_of_intn)uinletx=inj_var(Array.lengthvs+n)xinbeginletenv,b=Env.of_prod[]"y"tinmatchget_argsbwith|(Symb(s),ts)->beginmatchList.assq_optsind_pred_mapwith|Somed->letv=auxenvsd.ind_vartsxinletnext=fold(x::xs)(n+1)(acc_rec_domaccxv)uinrec_domtxvnext|None->letnext=fold(x::xs)(n+1)(acc_nonrec_domaccx)uinnonrec_domtxnextend|_->fatalpos"The type of %a is not supported"symcons_symend|_->fatalpos"The type of %a is not supported"symcons_syminlet_,t=Env.of_prod_using[]vs!(cons_sym.sym_type)infold(List.mapiinj_var(Array.to_listvs))0initt(** [gen_rec_type c pos ind_list vs env ind_pred_map x_str] generates the
induction principles for each type in the inductive definition [ind_list]
defined at the position [pos] whose parameters are [vs]. [x_str] is
a string used for prefixing variable names that are generated. Remark: in
the generated induction principles, each recursive argument is followed by
its induction hypothesis. For instance, with [inductive T:TYPE := c:
T->T->T], we get [ind_T: Π p:T -> Prop, (Π x₀:T, π(p x₀)-> Π x₁:T, π(p
x₁)-> π(p (c x₀ x₁)) -> Π x:T, π(p x)]. *)letgen_rec_types:config->popt->inductive->tvararray->Env.t->ind_pred_map->string->termlist=funcposind_listvsenvind_pred_mapx_str->letn=Array.lengthvsin(* [case_of ind_sym cons_sym] creates the clause for the constructor
[cons_sym] in the induction principle of [ind_sym]. *)letcase_of:sym->sym->tbox=funind_symcons_sym->(* 'var = tvar, 'a = unit, 'aux = unit, 'c = tbox *)(* the accumulator is not used *)letinj_var_x=xinletinit=()in(* aux computes the induction hypothesis *)letauxenv_ptsx=letv=Env.appl(_Varix)envinletv=prf_ofcp(List.maplift(List.remove_headsnts))vinEnv.to_prod_boxenvvinletacc_rec_dom___=()inletrec_domtxvnext=_Prod(liftt)(Bindlib.bind_varx(_Implvnext))inletacc_nonrec_dom__=()inletnonrec_domtxnext=_Prod(liftt)(Bindlib.bind_varxnext)inletcodomxs_pts=prf_ofcp(List.maplift(List.remove_headsnts))(_Appl_Symbcons_sym(List.rev_map_Varixs))infold_cons_typeposind_pred_mapx_strind_symvscons_syminj_varinitauxacc_rec_domrec_domacc_nonrec_domnonrec_domcodomin(* Generates an induction principle for each type. *)letgen_rec_type(_,d)=letadd_clause_consind_symcons_symt=_Impl(case_ofind_symcons_sym)tinletadd_clauses_ind(ind_sym,cons_sym_list)t=List.fold_right(add_clause_consind_sym)cons_sym_listtinletrec_typ=List.fold_rightadd_clauses_indind_listd.ind_concluinletadd_quantifiert(_,d)=_Prodd.ind_type(Bindlib.bind_vard.ind_vart)inletrec_typ=List.fold_leftadd_quantifierrec_typind_pred_mapinBindlib.unbox(Env.to_prod_boxenvrec_typ)inList.mapgen_rec_typeind_pred_map(** [rec_name ind_sym] returns the name of the induction principle associated
to the inductive type symbol [ind_sym]. *)letrec_nameind_sym=Escape.add_prefix"ind_"ind_sym.sym_name(** [iter_rec_rules pos ind_list vs rec_sym_list ind_pred_map] generates the
recursor rules for the inductive type definition [ind_list] as position
[pos] with parameters [vs], recursors are [rec_sym_list] and
[ind_pred_map].
For instance, [inductive T : Π(i1:A1),..,Π(im:Am), TYPE := c1:T1 | .. |
cn:Tn] generates a rule for each constructor. If [Ti = Π x1:B1,..,Π
xk:Bk,T] then the rule for ci is [ind_T p pc1 .. pcn _ .. _ (ci x1 .. xk)
--> pci x1 t1? ... xk tk?] with m underscores, [tj? = ind_T p pc1 .. pcn _
.. _ xj] if [Bj = T v1 ... vm], and nothing otherwise. *)letiter_rec_rules:popt->inductive->tvararray->ind_pred_map->(p_rule->unit)->unit=funposind_listvsind_pred_mapf->(* Rules are declared after recursor declarations. *)letrules_pos=shift(List.lengthind_list+1)(end_pospos)inletn=Array.lengthvsin(* variable name used for a recursor case argument *)letcase_arg_namecons_sym=cons_sym.sym_namein(* [arec sym_ind ts t] generates the application of the recursor of
[ind_sym] to the type parameters [ts] and the constructor argument
[t]. *)letarec:sym->termlist->p_term->p_term=funsym_indtst->(* Note: there cannot be name clashes between pattern variable names and
function symbol names since pattern variables are prefixed by $. *)letapatttn=P.applt(P.patt0n)inlethead=P.iden(rec_namesym_ind)in(* add a wildcard for each parameter *)lethead=P.appl_wildheadnin(* add a predicate variable for each inductive type *)lethead=letapred(_,d)t=apattt(Bindlib.name_ofd.ind_var)inList.fold_rightapredind_pred_mapheadin(* add a case variable for each constructor *)letacasetcons_sym=apattt(case_arg_namecons_sym)inletacasest(_,cons_sym_list)=List.fold_leftacasetcons_sym_listinlethead=List.fold_leftacasesheadind_listinP.appl(P.appl_wildhead(List.lengthts-n))tin(* [gen_rule_cons ind_sym rec_sym cons_sym] generates the p_rule of the
recursor [rec_sym] of the inductive type [ind_sym] for the constructor
[cons_sym]. *)letgen_rule_cons:sym->sym->p_rule=funind_symcons_sym->(* 'var = p_term, 'aux = p_term, 'a = p_term, 'c = p_rule *)(* the accumulator is used to compute the RHS *)letinj_varn_=P.patt0("x"^string_of_intn)inletinit=P.patt0(case_arg_namecons_sym)in(* aux computes the recursive call *)letauxenvs_tsx=letenv_appltenv=List.fold_right(fun(_,(x,_,_))t->P.applt(P.varx))envtinletadd_abstt(_,(x,_,_))=P.abst(Some(Pos.none(Bindlib.name_ofx)))tinList.fold_leftadd_abst(arecsts(env_applxenv))envinletacc_rec_domaccxaux=P.appl(P.applaccx)auxinletrec_dom___next=nextinletacc_nonrec_domax=P.applaxinletnonrec_dom__next=nextinletcodomxsrhs_ts=letcons_arg=P.appl_list(P.idencons_sym.sym_name)(List.revxs)inPos.makerules_pos(arecind_symtscons_arg,rhs)infold_cons_typeposind_pred_map""ind_symvscons_syminj_varinitauxacc_rec_domrec_domacc_nonrec_domnonrec_domcodomin(* Iterate [f] over every rule. *)letg(ind_sym,cons_sym_list)=List.iter(funcons_sym->f(gen_rule_consind_symcons_sym))cons_sym_listinList.itergind_list