123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298(** Implementation of the why3 tactic. *)openLplibopenExtraopenTimedopenCommonopenErroropenCoreopenTermopenPrint(** Logging function for external prover calling with Why3. *)letlog_why3=Logger.make'w'"why3""why3 provers"letlog_why3=log_why3.pp(** [default_prover] contains the name of the current prover. Note that it can
be changed by using the "set prover <string>" command. *)letdefault_prover:stringref=ref"Alt-Ergo"(** [timeout] is the current time limit (in seconds) for a Why3 prover to find
a proof. It can be changed with "set prover <int>". *)lettimeout:intref=ref2(** [why3_config] is the Why3 configuration read from the configuration file
(["~/.why3.conf"] usually). For more information, visit the Why3
documentation at http://why3.lri.fr/api/Whyconf.html. *)letwhy3_config:Why3.Whyconf.config=letcfg=Why3.Whyconf.init_configNoneinletprovers=Why3.Whyconf.get_proverscfginConsole.out2"provers available for why3:";letproverp_=Console.out2"%a"Why3.Whyconf.print_proverpinWhy3.Whyconf.Mprover.iterproverprovers;cfg(** [why3_main] is the main section of the Why3 configuration. *)letwhy3_main:Why3.Whyconf.main=Why3.Whyconf.get_mainwhy3_configletwhy3_libdir:string=Why3.Whyconf.libdirwhy3_mainletwhy3_datadir:string=Why3.Whyconf.datadirwhy3_main(** [why3_env] is the initialized Why3 environment. *)letwhy3_env:Why3.Env.env=Why3.Env.create_env(Why3.Whyconf.loadpathwhy3_main)(** Builtin configuration for propositional logic. *)typeconfig={symb_P:sym(** Encoding of propositions. *);symb_T:sym(** Encoding of types. *);symb_or:sym(** Disjunction(∨) symbol. *);symb_and:sym(** Conjunction(∧) symbol. *);symb_imp:sym(** Implication(⇒) symbol. *);symb_bot:sym(** Bot(⊥) symbol. *);symb_top:sym(** Top(⊤) symbol. *);symb_not:sym(** Not(¬) symbol. *);symb_ex:sym(** Exists(∃) symbol. *);symb_all:sym(** Forall(∀) symbol. *)}(** [get_config ss pos] build the configuration using [ss]. *)letget_config:Sig_state.t->Pos.popt->config=funsspos->letbuiltin=Builtin.getssposin{symb_P=builtin"P";symb_T=builtin"T";symb_or=builtin"or";symb_and=builtin"and";symb_imp=builtin"imp";symb_bot=builtin"bot";symb_top=builtin"top";symb_not=builtin"not";symb_ex=builtin"ex";symb_all=builtin"all"}(** A map from lambdapi terms to Why3 constants. *)typecnst_table=(term*Why3.Term.lsymbol)list(** Table of declared types, these types may be variables or constants. *)moduleTyTable:sigtypetvalempty:tvalty_of_term:t->term->t*Why3.Ty.ty(** [ty_of_term tbl te] fetches the type associated to term [te] in
table [tbl] if it exists, or it creates such a type and return
the created type and the new table. *)(** {b FIXME} the translation of types {!ty_of_term} is correct on
non empty types only. *)valfold_left:do_var:('a->term->Why3.Ty.tvsymbol->'a)->do_sym:('a->term->Why3.Ty.tysymbol->'a)->'a->t->'a(** [fold_left ~do_var ~do_sym init tbl] folds over the table [tbl]
with initial value [init] using [do_var] to operate on type
variables and [do_sym] to operate on type symbols. *)end=struct(* The module is created to hide the type [sym_or_var] *)typesym_or_var=TySymofWhy3.Ty.tysymbol|TyVarofWhy3.Ty.tvsymboltypet=(term*sym_or_var)listletempty=[]letty_of_termtblte=matchList.assoc_eq(Eval.eq_modulo[])tetblwith|TySyms->(tbl,Why3.Ty.ty_apps[])|TyVarv->(tbl,Why3.Ty.ty_varv)|exceptionNot_found->((* FIXME generate a new goal to ensure that types translated as
fresh constants are inhabited. *)matchget_argstewith|Symbs,[]->letid=Why3.Ident.id_freshs.sym_nameinletsym=Why3.(Ty.create_tysymbolid[]Ty.NoDef)in((te,TySymsym)::tbl,Why3.Ty.ty_appsym[])|Varix,[]->letsym=Why3.Ty.tv_of_string(Bindlib.name_ofx)in((te,TyVarsym)::tbl,Why3.Ty.ty_varsym)|_->letid=Why3.Ident.id_fresh"ty"inletsym=Why3.(Ty.create_tysymbolid[]Ty.NoDef)in((te,TySymsym)::tbl,Why3.Ty.ty_appsym[]))letfold_left~do_var~do_symacctbl=letfacc(t,s_or_v)=matchs_or_vwith|TySyms->do_symaccts|TyVarv->do_varacctvinList.fold_leftfacctblend(** [new_axiom_name ()] generates a fresh axiom name. *)letnew_axiom_name:unit->string=letcounter=ref0infun_->incrcounter;"Why3axiom_"^(string_of_int!counter)(** [translate_term cfg tbl t] translates the given lambdapi term [t] into the
correspondig Why3 term, using the configuration [cfg] and constants in the
association list [tbl]. *)lettranslate_term:config->cnst_table->TyTable.t->term->(cnst_table*TyTable.t*Why3.Term.term)option=funcfgtblty_tblt->letrectranslate_proptblty_tblt=matchget_argstwith|Symb(s),[t1;t2]whens==cfg.symb_and->let(tbl,ty_tbl,t1)=translate_proptblty_tblt1inlet(tbl,ty_tbl,t2)=translate_proptblty_tblt2in(tbl,ty_tbl,Why3.Term.t_andt1t2)|Symb(s),[t1;t2]whens==cfg.symb_or->let(tbl,ty_tbl,t1)=translate_proptblty_tblt1inlet(tbl,ty_tbl,t2)=translate_proptblty_tblt2in(tbl,ty_tbl,Why3.Term.t_ort1t2)|Symb(s),[t1;t2]whens==cfg.symb_imp->let(tbl,ty_tbl,t1)=translate_proptblty_tblt1inlet(tbl,ty_tbl,t2)=translate_proptblty_tblt2in(tbl,ty_tbl,Why3.Term.t_impliest1t2)|Symb(s),[t]whens==cfg.symb_not->let(tbl,ty_tbl,t)=translate_proptblty_tbltin(tbl,ty_tbl,Why3.Term.t_nott)|Symb(s),[]whens==cfg.symb_bot->(tbl,ty_tbl,Why3.Term.t_false)|Symb(s),[]whens==cfg.symb_top->(tbl,ty_tbl,Why3.Term.t_true)|Symb(s),[a;Abst(_,t)]whens==cfg.symb_ex||s==cfg.symb_all->let(ty_tbl,ty)=TyTable.ty_of_termty_tblainletx,t=Bindlib.unbindtinlet(tbl,ty_tbl,t)=translate_proptblty_tbltinlettquant=letid=Why3.Ident.id_fresh(Bindlib.name_ofx)inletvid=Why3.(Term.create_vsymbolid)tyinletclose=ifs==cfg.symb_exthenWhy3.Term.t_exists_closeelseWhy3.Term.t_forall_close(* Other cases excluded by pattern matching *)inclose[vid][]tin(tbl,ty_tbl,tquant)|_->(* If the term [p] is mapped in [tbl] then use it. *)tryletsym=List.assoc_eq(Eval.eq_modulo[])ttblin(tbl,ty_tbl,Why3.Term.ps_appsym[])withNot_found->(* Otherwise generate a new constant in why3. *)letsym=Why3.Term.create_psymbol(Why3.Ident.id_fresh"P")[]in((t,sym)::tbl,ty_tbl,Why3.Term.ps_appsym[])inmatchget_argstwith|(Symb(s),[t])whens==cfg.symb_P->Some(translate_proptblty_tblt)|_->None(** [encode ss pos hs g] translates the hypotheses [hs] and the goal [g]
into Why3 terms, to construct a Why3 task. *)letencode:Sig_state.t->Pos.popt->Env.env->term->Why3.Task.task=funssposhsg->letcfg=get_configssposinlet(constants,types,hypothesis)=lettranslate_hyp(tbl,ty_tbl,map)(name,(_,hyp,_))=matchtranslate_termcfgtblty_tbl(Bindlib.unboxhyp)with|Some(tbl,ty_tbl,why3_hyp)->(tbl,ty_tbl,StrMap.addnamewhy3_hypmap)|None->(tbl,ty_tbl,map)inList.fold_lefttranslate_hyp([],TyTable.empty,StrMap.empty)hsin(* Translate the goal term. *)let(tbl,ty_tbl,why3_term)=matchtranslate_termcfgconstantstypesgwith|Some(tbl,ty_tbl,why3_term)->(tbl,ty_tbl,why3_term)|None->fatalpos"The goal [%a] is not of the form [P _]"termgin(* Add the declaration of every constant in a task. *)letfntsk(_,t)=Why3.Task.add_param_decltsktinlettsk=List.fold_leftfnNonetblin(* Same for types. *)lettsk=letdo_symtsk_tys=Why3.Task.add_ty_decltsktysinletdo_vartsk__=tskinTyTable.fold_left~do_var~do_symtskty_tblin(* Add the declaration of every hypothesis in the task. *)letfnnamettsk=letaxiom=Why3.Decl.create_prsymbol(Why3.Ident.id_freshname)inWhy3.Task.add_prop_decltskWhy3.Decl.Paxiomaxiomtinlettsk=StrMap.foldfnhypothesistskin(* Add the goal itself. *)letgoal=Why3.Decl.create_prsymbol(Why3.Ident.id_fresh"main_goal")in(* Return the task that contains the encoded lambdapi formula in Why3. *)Why3.Task.add_prop_decltskWhy3.Decl.Pgoalgoalwhy3_term(** [run_task tsk pos prover_name] Run the task [tsk] with the specified
prover named [prover_name] and return the answer of the prover. *)letrun_task:Why3.Task.task->Pos.popt->string->bool=funtskposprover_name->(* Filter the set of why3 provers. *)letfilter=Why3.Whyconf.parse_filter_proverprover_nameinletprovers=Why3.Whyconf.filter_proverswhy3_configfilterin(* Fail if we did not find a matching prover. *)ifWhy3.Whyconf.Mprover.is_emptyproversthenbeginfatal_msg"prover \"%s\" not found.@."prover_name;letprovers=Why3.Whyconf.get_proverswhy3_configinlet_=ifWhy3.Whyconf.Mprover.is_emptyproversthenfatal_msg"There are no available Why3 provers.@."elseletfnp_=fatal_msg" - %a@."Why3.Whyconf.print_proverpinfatal_msg"The available Why3 provers are:@.";Why3.Whyconf.Mprover.iterfnproversinfatal_msg"Why3 configuration read from \"%s\".@."(Why3.Whyconf.get_conf_filewhy3_config);fatal_msg"Your prover might not be installed or detected, ";fatalpos"remember to run [why3 config detect]."end;(* Return the prover configuration and load the driver. *)letprover=snd(Why3.Whyconf.Mprover.max_bindingprovers)inletdriver=tryWhy3.Whyconf.(load_driverwhy3_mainwhy3_envprover)withe->fatalpos"Failed to load driver for \"%s\": %a"prover.prover.prover_nameWhy3.Exn_printer.exn_printerein(* Actually run the prover. *)letcommand=prover.Why3.Whyconf.commandandlimit={Why3.Call_provers.empty_limitwithlimit_time=!timeout}inletcall=Why3.Driver.prove_task~command~libdir:why3_libdir~datadir:why3_datadir~limitdrivertskinWhy3.Call_provers.((wait_on_callcall).pr_answer=Valid)(** [handle ss pos prover_name g] runs the Why3 prover corresponding
to the name [prover_name] (if given or a default one otherwise)
on the goal [g].
If the prover succeeded to prove the goal, then this is reflected by a new
axiom that is added to signature [ss]. *)lethandle:Sig_state.t->Pos.popt->stringoption->Proof.goal_typ->term=funssposprover_name{goal_meta=m;goal_hyps=hyps;goal_type=trm}->(* Get the name of the prover. *)letprover_name=Option.get!default_proverprover_nameinifLogger.log_enabled()thenlog_why3"running with prover \"%s\""prover_name;(* Encode the goal in Why3. *)lettsk=encodessposhypstrmin(* Run the task with the prover named [prover_name]. *)ifnot(run_tasktskposprover_name)thenfatalpos"\"%s\" did not find a proof"prover_name;(* Create a new axiom name that represents the proved goal. *)letaxiom_name=new_axiom_name()in(* Add the axiom to the current signature. *)leta=Sign.add_symbolss.signaturePublicDefinEagertrue(Pos.makeposaxiom_name)!(m.meta_type)[]inifLogger.log_enabled()thenlog_why3"axiom %a created"uidaxiom_name;(* Return the variable terms of each item in the context. *)letterms=List.rev_map(fun(_,(x,_,_))->mk_Varix)hypsin(* Apply the instance of the axiom with context. *)add_args(mk_Symba)terms