1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165(* This file is free software, part of Logtk. See file "license" for more details. *)(* {1 Type inference} *)(** Reference:
https://en.wikipedia.org/wiki/Hindley-Milner
*)modulePT=STermmoduleT=TypedSTermmoduleLoc=ParseLocationmoduleErr=CCResultmoduleSubst=Var.SubstmoduleFmt=CCFormatletprof_infer=ZProf.make"TypeInference.infer"letsection=Util.Section.(make"ty-infer")type'aor_error=('a,string)CCResult.ttypetype_=TypedSTerm.ttypeuntyped=STerm.ttypetyped=TypedSTerm.ttypeloc=ParseLocation.texceptionErrorofstringlet()=Printexc.register_printer(function|Errormsg->Some(CCFormat.sprintf"@[@{<Red>type inference error@}:@ %s@]"msg)|_->None)leterror_on_incomplete_match_=reffalselet()=Options.add_opts["--require-exhaustive-matches",Arg.Seterror_on_incomplete_match_," fail if pattern matches are not exhaustive";"--no-require-exhautive-matches",Arg.Clearerror_on_incomplete_match_," accept non-exhaustive pattern matches";](* error-raising function *)leterror_?locmsg=CCFormat.ksprintfmsg~f:(funmsg->letmsg=matchlocwith|None->msg|Somel->Util.err_spf"@[<hv>%s@ at %a@]"msgLoc.pplinraise(Errormsg))(* unify within the context's substitution. Wraps {!Unif.Ty.unification}
by returning a nicer exception in case of failure *)letunify?locty1ty2=Util.debugf~section5"@[<hv2>unify types@ `@[%a@]`@ and `@[%a@]`@]"(funk->kT.ppty1T.ppty2);tryT.unify~allow_open:true?locty1ty2withT.UnifyFailure_ase->error_?loc"@[%s@]"(Printexc.to_stringe)moduleTyBuiltin=structleta=Var.of_string~ty:T.Ty.tType"α"leta_=T.Ty.varaletprop2=T.Ty.([prop;prop]==>prop)letprop1=T.Ty.([prop]==>prop)letprop2poly=T.Ty.(foralla([a_;a_]==>prop))letty_1_to_int=T.Ty.(foralla([a_]==>int))letty2op=T.Ty.(foralla([a_;a_]==>a_))letty1op=T.Ty.(foralla([a_]==>a_))letty2op_to_i=T.Ty.([int;int]==>int)lethobinder=T.Ty.(foralla([[a_]==>prop]==>prop))letty_exn=function|Builtin.True->T.Ty.prop|Builtin.False->T.Ty.prop|Builtin.Eq->prop2poly|Builtin.Neq->prop2poly|Builtin.Not->prop1|Builtin.Imply->prop2|Builtin.And->prop2|Builtin.Or->prop2|Builtin.Equiv->prop2|Builtin.Xor->prop2|Builtin.ForallConst->hobinder|Builtin.ExistsConst->hobinder|Builtin.Less->prop2poly|Builtin.Lesseq->prop2poly|Builtin.Greater->prop2poly|Builtin.Greatereq->prop2poly|Builtin.Uminus->ty1op|Builtin.Sum->ty2op|Builtin.Difference->ty2op|Builtin.Product->ty2op|Builtin.Quotient->ty2op|Builtin.Quotient_e->ty2op_to_i|Builtin.Quotient_f->ty2op_to_i|Builtin.Quotient_t->ty2op_to_i|Builtin.Remainder_e->ty2op_to_i|Builtin.Remainder_f->ty2op_to_i|Builtin.Remainder_t->ty2op_to_i|Builtin.Floor->ty1op|Builtin.Ceiling->ty1op|Builtin.Round->ty1op|Builtin.Truncate->ty1op|Builtin.To_int->T.Ty.(foralla([a_]==>int))|Builtin.To_rat->T.Ty.(foralla([a_]==>rat))|Builtin.Is_int->T.Ty.(foralla([a_]==>prop))|Builtin.Is_rat->T.Ty.(foralla([a_]==>prop))|_->invalid_arg"TyBuiltin.ty_exn"lettyx=trySome(ty_exnx)with_->Noneend(** {2 Typing context} *)moduleCtx=structtypeenv=(string,[`Varoftype_Var.t|`IDofID.t*type_])Hashtbl.ttypet={default:type_;env:env;(* map names to variables or IDs *)def_as_rewrite:bool;(* if true, definitions are rewrite rules *)on_var:[`Default|`Infer];(* what to do for variables without type annotation *)on_undef:[`Warn|`Fail|`Guess];(* what to do when we meet an undefined symbol *)on_shadow:[`Warn|`Ignore];(* what to do when an identifier is re-declared *)implicit_ty_args:bool;(* add implicit type arguments in applications? *)mutablenew_metas:T.meta_varlist;(* variables that should be generalized in the global scope
or bound to [default], if they are not bound *)mutablelocal_vars:T.tVar.tlist;(* free variables in the local scope *)mutabledatatypes:(ID.t*type_*(type_*(ID.t*type_))list)listID.Tbl.t;(* datatype ID -> list of cstors *)mutablenew_types:(ID.t*type_)list;(* list of symbols whose type has been inferred recently *)}letcreate?(def_as_rewrite=true)?(default=T.Ty.term)?(on_var=`Infer)?(on_undef=`Guess)?(on_shadow=`Warn)~implicit_ty_args()=letctx={default;def_as_rewrite;on_var;on_undef;on_shadow;implicit_ty_args;env=Hashtbl.create32;datatypes=ID.Tbl.create32;new_metas=[];local_vars=[];new_types=[];}inctxletcopyt={twithenv=Hashtbl.copyt.env;}(* enter new scope for the variables with those names *)letwith_varsctxvs~f=letnames=List.map(funv->letname=Var.namevinHashtbl.addctx.envname(`Varv);name)vsintryletx=f()inList.iter(Hashtbl.removectx.env)names;xwithe->List.iter(Hashtbl.removectx.env)names;raiseeletwith_varctxv~f=with_varsctx[v]~f(* only specialize/generalize variable if it's not bound *)letbind_metactx(v,r,k)=match!r,kwith|None,`BindDefault->letty=ctx.defaultinUtil.debugf~section50"@[<2>specialize type meta_var %a to@ @[%a@]@]"(funk->kVar.ppvT.ppty);r:=Somety|None,`Generalize->letv'=Var.copyvinUtil.debugf~section50"@[<2>generalize type meta_var %a@]"(funk->kVar.ppv);r:=Some(T.varv')|None,`NoBind->assertfalse|Some_,_->()letexit_scopectx=List.iter(funv->Hashtbl.removectx.env(Var.namev))ctx.local_vars;ctx.local_vars<-[];(* try to bind the variable (generalizing or binding to default).
Will fail if already bound to something else, which is fine. *)List.iter(bind_metactx)ctx.new_metas;ctx.new_metas<-[];()letdeclare?locctxsty=letname=ID.namesinletdoit=matchCCHashtbl.getctx.envnamewith|None->true|Some(`ID(_,ty_old)|`Var{Var.ty=ty_old;_})->beginmatchctx.on_shadowwith|`Ignore->(* ignore decl, but ensure the two types are the same *)Util.debugf~section50"ignore duplicate declaration of `%a`"(funk->kID.pps);T.unify?locty_oldty;false|`Warn->Util.warnf"@[<2>shadowing identifier %s@]"name;trueendinifdoitthen(Util.debugf~section30"@{<yellow>declare@} %a:@ @[%a@]"(funk->kID.ppsT.ppty);Hashtbl.addctx.envname(`ID(s,ty)))letdefault_destctx=matchctx.on_varwith|`Default->`BindDefault|`Infer->`Generalize(* generate fresh type var. *)letfresh_ty_meta_varctx?(dest=default_destctx)():T.meta_var=letv=Var.gensym~ty:T.tType()inletr=refNoneinletmeta=v,r,destinctx.new_metas<-meta::ctx.new_metas;meta(* generate [n] fresh type meta vars *)letrecfresh_ty_meta_vars?destctxn=ifn=0then[]elsefresh_ty_meta_var?destctx()::fresh_ty_meta_vars?destctx(n-1)(* Fresh function type with [arity] arguments. Type meta-vars should
not be generalized but bound to default. *)letfresh_fun_ty?(dest=`BindDefault)~arityctx=letret=fresh_ty_meta_varctx~dest()inletnew_vars=fresh_ty_meta_vars~destctxarityinletty=T.Ty.fun_(List.map(funv->T.Ty.metav)new_vars)(T.Ty.metaret)intyletfind_close_namesctx(s:string):stringlist=CCHashtbl.keysctx.env|>Iter.filter(funs'->CCString.edit_distancess'<2)|>Iter.to_rev_list|>CCList.sort_uniq~cmp:String.compareletpp_namesout=function|[]->()|l->Fmt.fprintfout" (did you mean any of [@[%a@]]?)"(Util.pp_listFmt.string)l(* Does the identifier represent a (TPTP) distinct object? *)letis_distinct__sattrs=List.exists(functionPT.Attr_distinct_const->true)attrsletget_id_?loc~arity~attrsctxname=trymatchHashtbl.findctx.envnamewith|`ID(id,ty)->id,ty|`Var_->error_?loc"@[<2>expected `%s` to be a constant, not a variable@]"namewithNot_found->letty=fresh_fun_ty~arityctxinbeginmatchctx.on_undefwith|`Fail->error_?loc"unknown identifier %s"name|`Guess->()|`Warn->Util.warnf"@[<2>unknown constant %s@,%a,@ will create one with type @[%a@]@]"namepp_names(find_close_namesctxname)T.ppty;end;letid=ID.makenameinifis_distinct_nameattrsthenID.set_payloadidID.Attr_distinct;Hashtbl.addctx.envname(`ID(id,ty));ctx.new_types<-(id,ty)::ctx.new_types;id,ty(* in ZF, variables might be constant, there is no syntactic difference *)letget_var_?locctxv=letmk_freshv=letdest=default_destctxinletty_v=fresh_ty_meta_var~destctx()inletv'=Var.of_string~ty:(T.Ty.metaty_v)vinctx.local_vars<-v'::ctx.local_vars;v'inmatchvwith|PT.Wildcard->`Var(mk_fresh"_")|PT.Vv->tryHashtbl.findctx.envvwithNot_found->beginmatchctx.on_undefwith|`Fail->error_?loc"unknown variable %s@,%a"vpp_names(find_close_namesctxv)|`Guess->()|`Warn->Util.warnf"@[<2>create implicit variable %s@,%a%a@]"vpp_names(find_close_namesctxv)Loc.pp_optloc;end;letv'=mk_freshvinHashtbl.addctx.envv(`Varv');`Varv'letpop_new_typesctx=letl=ctx.new_typesinctx.new_types<-[];lend(** {2 Hindley-Milner} *)(* fails if [ty] is not a prenex type *)letcheck_ty_prenex?locty=ifnot@@T.Ty.is_prenextythen(error_?loc"non-prenex type %a"T.ppty)letcheck_ty_quantifier_free?locty=ifnot@@T.Ty.is_quantifier_freetythen(error_?loc"type %a@ must be quantifier-free"T.ppty)(* convert the typed variables into proper variables [vars'], call [f vars'],
and then exit the scope of [vars'] *)letwith_typed_vars_?loc~infer_tyctxvars~f=letrecauxaccl=matchlwith|[]->f(List.revacc)|(v,o)::l'->letty=matchowith|None->T.Ty.meta(Ctx.fresh_ty_meta_var~dest:(Ctx.default_destctx)ctx())|Somety->infer_ty?locctxtyinletv=matchvwith|PT.Wildcard->Var.of_string~ty"_"|PT.Vv->Var.of_string~tyvin(* enter [v] before dealing with next variables, for they might depend
on it (e.g. [forall (a:type)(l:list a). ...]) *)Ctx.with_varctxv~f:(fun()->aux(v::acc)l')inaux[]varsletwith_typed_var_?loc~infer_tyctxv~f=with_typed_vars_?loc~infer_tyctx[v]~f:(function|[v]->fv|_->assertfalse)letapply_unify~allow_open?locctxtyl=T.apply_unifytyl?loc~allow_open~gen_fresh_meta:(Ctx.fresh_ty_meta_varctx~dest:`Generalize)(* convert a prolog term into a type *)letrecinfer_ty_?locctxty=letrecauxty=matchPT.viewtywith|PT.AppBuiltin(Builtin.TyInt,[])->T.Ty.int|PT.AppBuiltin(Builtin.TyRat,[])->T.Ty.rat|PT.AppBuiltin(Builtin.TyReal,[])->T.Ty.real|PT.AppBuiltin(Builtin.Term,[])->T.Ty.term|PT.AppBuiltin(Builtin.Prop,[])->T.Ty.prop|PT.AppBuiltin(Builtin.TType,[])->T.Ty.tType|PT.AppBuiltin(Builtin.Arrow,ret::args)->letret=auxretinletargs=List.mapauxargsinT.Ty.fun_?locargsret|PT.AppBuiltin(Builtin.HasType,[t;ty])->(* cast *)lett=auxtinletty=auxtyinunify?loctyT.Ty.tType;unify?loc(T.ty_exnt)ty;t|PT.Varv->beginmatchCtx.get_var_ctxvwith|`Varv->unify?loc(Var.tyv)T.Ty.tType;T.Ty.var?locv|`ID(id,ty)->unify?loctyT.Ty.tType;T.Ty.constidend|PT.Constf->(* constant type *)letid,ty=Ctx.get_id_?locctx~arity:0~attrs:ty.PT.attrsfinunify?loctyT.Ty.tType;T.Ty.constid|PT.App(f,l)->beginmatchPT.viewfwith|PT.VarPT.Wildcard->error_?loc"wildcard function: not supported"|PT.Var(PT.Vname)|PT.Constname->letid,ty=Ctx.get_id_?locctx~attrs:ty.PT.attrs~arity:(List.lengthl)nameinaux_appidtyl|_->error_?loc"@[<2>cannot apply non-constant@ `@[%a@]`@]"PT.ppfend|PT.Bind(Binder.ForallTy,vars,body)->with_typed_vars_?loc~infer_ty:infer_ty_ctxvars~f:(funvars'->(* be sure those are type variables *)List.iter(funv->unifyT.tType(Var.tyv))vars';letbody'=auxbodyinT.Ty.forall_lvars'body')|PT.AppBuiltin(Builtin.Wildcard,[])->Ctx.fresh_ty_meta_varctx~dest:`Generalize()|>T.meta|_->error_?loc"@[<2>`@[%a@]`@ is not a valid type@]"PT.pptyandaux_appidtyl=unify?loc(T.Ty.returnsty)T.Ty.tType;letl=List.mapauxlin(* ensure that the type is well-typed (!) *)letty_res=apply_unifyctx~allow_open:false?loctylinunify?locty_resT.Ty.tType;T.Ty.appidlinauxty(* XXX: hack: need to define {!with_typed_vars_} before {!infer_ty_}
so that it generalizes return type properly *)letwith_non_inferred_typed_vars?locctxvars~f=with_typed_vars_?loc~infer_ty:infer_ty_ctxvars~fletinfer_ty_exnctxty=infer_ty_?loc:(PT.locty)ctxtyletinfer_tyctxty=tryErr.return(infer_ty_exnctxty)withe->Err.of_exn_tracee(* add type variables if needed, to apply [some_fun:ty_fun] to [l] *)letadd_implicit_paramsctxty_funl=ifctx.Ctx.implicit_ty_argsthen(lettyvars,args,_=T.Ty.unfoldty_funinletl'=ifList.lengthl=List.lengthargsthenList.map(fun_->PT.wildcard)tyvarselse[]inl'@l)elselletmk_metasctxn=CCList.initn(fun_->T.Ty.meta(Ctx.fresh_ty_meta_var~dest:`Generalizectx()))(* apply type to the relevant number of metas; return the resulting type *)letapply_ty_to_metas?locctx(ty:T.Ty.t):T.Ty.tlist*T.Ty.t=letty_vars,_,_=T.Ty.unfoldtyinletmetas=mk_metasctx(List.lengthty_vars)inletty=apply_unifyctx~allow_open:true?loctymetasinmetas,ty(* infer a type for [t], possibly updating [ctx] and binding meta-variables. *)letrecinfer_rec?locctx(t:PT.t):T.t=letopenLoc.Infixinletloc=PT.loct<+>locinlett'=matchPT.viewtwith|PT.Varname->beginmatchCtx.get_var_ctxnamewith|`Varv->T.varv|`ID(id,ty_id)->(* implicit parameters, e.g. for [nil] *)letl=add_implicit_paramsctxty_id[]|>List.map(infer_rec?locctx)inletty=apply_unifyctx?loc~allow_open:truety_idlinT.app?loc~ty(T.const?loc~ty:ty_idid)lend|PT.Consts->letid,ty_id=Ctx.get_id_?loc~arity:0~attrs:t.PT.attrsctxsin(* implicit parameters, e.g. for [nil] *)letl=add_implicit_paramsctxty_id[]|>List.map(infer_rec?locctx)inletty=apply_unifyctx?loc~allow_open:truety_idlinT.app?loc~ty(T.const?loc~ty:ty_idid)l|PT.App({PT.term=PT.Varv;_},l)->beginmatchCtx.get_var_?locctxvwith|`ID(id,ty)->infer_app?locctxidtyl|`Varv->letl=add_implicit_paramsctx(Var.tyv)lin(* infer types for arguments *)letl=List.map(infer_rec?locctx)linUtil.debugf~section50"@[<2>apply@ @[<2>%a:@,%a@]@ to [@[<2>@[%a@]]:@,[@[%a@]@]]@]"(funk->kVar.ppvT.pp(Var.tyv)(Util.pp_listT.pp)l(Util.pp_listT.pp)(List.mapT.ty_exnl));letty=apply_unifyctx?loc~allow_open:true(Var.tyv)linT.app?loc~ty(T.var?locv)lend|PT.App({PT.term=PT.Consts;_},l)->letid,ty_s=Ctx.get_id_?loc~arity:(List.lengthl)~attrs:t.PT.attrsctxsininfer_app?locctxidty_sl|PT.App(f,l)->(* higher order application *)letf=infer_rec?locctxfinletl=List.map(infer_rec?locctx)linUtil.debugf~section50"@[<2>apply@ @[<2>%a:@,%a@]@ to [@[<2>@[%a@]]:@,[@[%a@]@]]@]"(funk->kT.ppfT.pp(T.ty_exnf)(Util.pp_listT.pp)l(Util.pp_listT.pp)(List.mapT.ty_exnl));letty=apply_unifyctx?loc~allow_open:true(T.ty_exnf)linT.app?loc~tyfl|PT.Ite(a,b,c)->leta=infer_prop_?locctxainletb=infer_rec?locctxbinletc=infer_rec?locctxcinunify?loc(T.ty_exnb)(T.ty_exnc);T.ite?locabc|PT.Let(l,u)->(* deal with pairs in [l] one by one *)letrecaux=function|[]->infer_rec?locctxu|(v,t)::tail->lett=infer_rec?locctxtinwith_typed_var_ctx?loc~infer_ty:(fun?loc:__ty->ty)(v,Some(T.ty_exnt))~f:(funv->letbody=auxtailinT.let_?loc[v,t]body)inauxl|PT.Match(u,l)->letu=infer_rec?locctxuinletty_u=T.ty_exnuin(* find the datatype corresponding to [u] *)letdata=tryletty_id=T.head_exnty_uinID.Tbl.findctx.Ctx.datatypesty_idwithNot_found->error_?loc"type `@[%a@]` is not a known datatype"T.ppty_uinletl=infer_match?locctx~ty_matched:ty_utdatalinT.match_?locul|PT.List[]->letv=Ctx.fresh_ty_meta_var~dest:`Generalizectx()inletty=T.Ty.multiset(T.Ty.metav)inT.multiset?loc~ty[]|PT.List(t::l)->lett=infer_rec?locctxtinletl=List.map(infer_rec?locctx)linletty=T.Ty.multiset(T.ty_exnt)inT.multiset?loc~ty(t::l)|PT.Record(l,rest)->(* infer types of fields *)letty_l,l=List.map(fun(n,t)->lett'=infer_rec?locctxtin(n,T.ty_exnt'),(n,t'))l|>List.splitinletrest=CCOpt.map(funs->matchCtx.get_var_ctxswith|`Varv->v|`ID(id,_)->error_?loc"row variable cannot be a constant %a"ID.ppid)restinletty=T.Ty.record_flattenty_l~rest:(CCOpt.mapVar.tyrest)inT.record~ty?locl~rest|PT.AppBuiltin(Builtin.Wildcard,[])->(* make a new TYPE variable *)letv=Ctx.fresh_ty_meta_var~dest:`Generalizectx()inT.Ty.metav|PT.AppBuiltin(Builtin.Arrow,ret::args)->letret=infer_ty_exnctxretinletargs=List.map(infer_ty_exnctx)argsinT.Ty.fun_?locargsret|PT.AppBuiltin(Builtin.True,[])->T.Form.true_|PT.AppBuiltin(Builtin.False,[])->T.Form.false_|PT.AppBuiltin(Builtin.And,l)whenList.lengthl>=2->letl=List.map(infer_prop_?locctx)linT.Form.and_?locl|PT.AppBuiltin(Builtin.Or,l)whenList.lengthl>=2->letl=List.map(infer_prop_?locctx)linT.Form.or_?locl|PT.AppBuiltin(((Builtin.Equiv|Builtin.Xor|Builtin.Imply)asconn),[a;b])->leta=infer_prop_?locctxaandb=infer_prop_?locctxbinbeginmatchconnwith|Builtin.Equiv->T.Form.equiv?locab|Builtin.Xor->T.Form.xor?locab|Builtin.Imply->T.Form.imply?locab|_->assertfalseend|PT.AppBuiltin(Builtin.Not,[a])->leta=infer_prop_?locctxainT.Form.not_?loca|PT.AppBuiltin((Builtin.Eq|Builtin.Neq)asconn,[a;b])->(* a ?= b *)leta=infer_rec?locctxainletb=infer_rec?locctxbinunify?loc(T.ty_exna)(T.ty_exnb);ifT.Ty.returns_tType(T.ty_exna)thenerror_?loc"(in)equation @[%a@] ?= @[%a@] between types is forbidden"T.ppaT.ppb;beginmatchconnwith|Builtin.Eq->ifT.Ty.is_prop(T.ty_exna)&&(CCOpt.is_none(T.heada)||CCOpt.is_none(T.headb))thenT.Form.equivabelseT.Form.eqab|Builtin.Neq->ifT.Ty.is_prop(T.ty_exna)&&(CCOpt.is_none(T.heada)||CCOpt.is_none(T.headb))thenT.Form.xorabelseT.Form.neqab|_->assertfalseend|PT.Bind(((Binder.Forall|Binder.Exists)asbinder),vars,f')->with_non_inferred_typed_vars?locctxvars~f:(funvars'->letf'=infer_prop_ctxf'inmatchbinderwith|Binder.Forall->T.Form.forall_lvars'f'|Binder.Exists->T.Form.exists_lvars'f'|_->assertfalse)|PT.Bind(Binder.Lambda,vars,t')->with_non_inferred_typed_vars?locctxvars~f:(funvars'->lett'=infer_rec?locctxt'inletty=T.Ty.fun_?loc(List.mapVar.tyvars')(T.ty_exnt')incheck_ty_quantifier_free?locty;T.bind_list?loc~tyBinder.Lambdavars't')|PT.Bind(Binder.ForallTy,vars,t')->with_non_inferred_typed_vars?locctxvars~f:(funvars'->lett'=infer_rec?locctxt'inT.Ty.forall_l?locvars't')|PT.AppBuiltin(Builtin.Int_asb,[])->T.builtin~ty:T.Ty.intb|PT.AppBuiltin(Builtin.Rat_asb,[])->T.builtin~ty:T.Ty.ratb|PT.AppBuiltin(Builtin.Real_asb,[])->T.builtin~ty:T.Ty.realb|PT.AppBuiltin(Builtin.TyInt,[])->T.Ty.int|PT.AppBuiltin(Builtin.TyRat,[])->T.Ty.rat|PT.AppBuiltin(Builtin.Term,[])->T.Ty.term|PT.AppBuiltin(Builtin.Prop,[])->T.Ty.prop|PT.AppBuiltin(Builtin.TType,[])->T.Ty.tType|PT.AppBuiltin(Builtin.HasType,[t;ty])->(* cast *)lett=infer_rec?locctxtinletty=infer_ty_exnctxtyinunify?loc(T.ty_exnt)ty;t|PT.AppBuiltin(Builtin.HasType,l)->error_?loc"ill-formed has_type@ [@[<hv>%a@]]"(Util.pp_listPT.pp)l|PT.AppBuiltin(Builtin.Distinct,([]|[_]))->T.Form.true_|PT.AppBuiltin(Builtin.Distinct,l)->(* [distinct(l)] is boolean typed *)letl=List.map(infer_rec?locctx)linletx=matchlwithx::_->x|_->assertfalseinList.iter(funy->unify?loc(T.ty_exnx)(T.ty_exny))l;T.app_builtin?loc~ty:T.Ty.propBuiltin.Distinctl|PT.AppBuiltin(b,l)->beginmatchTyBuiltin.tybwith|None->error_?loc"@[<2>unexpected builtin in@ `@[%a@]`, expected term@]"PT.ppt|Somety_b->leti,j=T.Ty.arityty_bin(* some builtin are ad-hoc polymorphic (eq, $less, ...) so
we need to add wildcards *)letl=List.map(infer_rec?locctx)linletl=ifi>0&&List.lengthl=jthen(Util.debugf~section50"@[<2>add %d implicit type arguments to@ `@[<1>%a@ (%a)@]`@]"(funk->kiBuiltin.ppb(Util.pp_listT.pp)l);letmetas=Ctx.fresh_ty_meta_vars~dest:`Generalizectxiinletmetas=List.map(T.Ty.meta?loc)metasinmetas@l)elselinletty=apply_unifyctx~allow_open:true?locty_blinT.app_builtin?loc~tyblendinUtil.debugf~section50"@[<hv>typing of `@[%a@]`@ yields @[<2>`@[%a@]`@ : `@[%a@]`@]@]"(funk->kPT.pptT.ppt'T.pp(T.ty_exnt'));t'andinfer_app?locctxidty_id(l:PT.tlist):T.t=letl=add_implicit_paramsctxty_idlin(* infer types for arguments *)letl=List.map(infer_rec?locctx)linUtil.debugf~section50"@[<2>apply@ @[<2>%a:@,%a@]@ to [@[<2>@[%a@]]:@,[@[%a@]@]]@]"(funk->kID.ppidT.ppty_id(Util.pp_listT.pp)l(Util.pp_listT.pp)(List.mapT.ty_exnl));letty=apply_unifyctx?loc~allow_open:truety_idlinT.app?loc~ty(T.const?loc~ty:ty_idid)l(* replace a match with possibly a "default" case into a completely
defined match *)andinfer_match?locctx~ty_matchedtdata(l:PT.match_branchlist):(T.match_cstor*type_Var.tlist*T.t)list=letty_ret=refNonein(* check consistency of types in every branch *)letcheck_tyty:unit=match!ty_retwith|None->ty_ret:=Somety|Somety'->unify?locty'tyinletseen=ref[]inletseen_default=reffalseinletl=CCList.flat_map(funb->beginmatchbwith|PT.Match_defaultrhs->seen_default:=true;letrhs=infer_rec?locctxrhsincheck_ty(T.ty_exnrhs);(* now cover every missing case *)CCList.filter_map(fun(c_id,c_ty,_)->ifList.exists(ID.equalc_id)!seenthenNoneelse(letty_params,c_ty_applied=apply_ty_to_metas?locctxc_tyinlet_vars,ty_args,ty_ret=T.Ty.unfoldc_ty_appliedinassert(_vars=[]);unify?locty_retty_matched;letvars=List.mapi(funity->Var.makef~ty"x_%d"i)ty_argsinletcstor={T.cstor_id=c_id;cstor_ty=c_ty;cstor_args=ty_params;}inSome(cstor,vars,rhs)))data|PT.Match_case(s,vars,rhs)->letc_id,c_ty=Ctx.get_id_?loc~attrs:t.PT.attrs~arity:(List.lengthvars)ctxsinifList.exists(ID.equalc_id)!seenthen(error_?loc"duplicate branch for constructor `%a`"ID.ppc_id);ifList.for_all(fun(cstor,_,_)->not(ID.equalc_idcstor))datathen(error_?loc"symbol `%a` not a suitable constructor"ID.ppc_id);seen:=c_id::!seen;(* apply [ty_s] to some meta variables *)letty_params,c_ty_applied=apply_ty_to_metas?locctxc_tyinlet_vars,ty_s_args,ty_ret_s=T.Ty.unfoldc_ty_appliedinassert(_vars=[]);unify?locty_ret_sty_matched;ifList.lengthty_s_args<>List.lengthvarsthen(error_?loc"constructor `%a`@ expected %d arguments,@ got %d"ID.ppc_id(List.lengthty_s_args)(List.lengthvars););with_typed_vars_?loc~infer_ty:(fun?loc:__ty->ty)ctx(List.map2(funvty_arg->v,Somety_arg)varsty_s_args)~f:(funvars->(* now we have everything in scope, we can convert [rhs] *)letrhs=infer_rec?locctxrhsincheck_ty(T.ty_exnrhs);letcstor={T.cstor_id=c_id;cstor_ty=c_ty;cstor_args=ty_params;}in[cstor,vars,rhs])end)linletmissing=CCList.filter_map(fun(id,_,_)->ifList.exists(fun(c,_,_)->ID.equalidc.T.cstor_id)lthenNoneelseSomeid)datainbeginmatchmissingwith|[]->l|_::_->if!error_on_incomplete_match_then(error_?loc"missing cases in match: (@[%a@])@ :in `%a`"(Util.pp_listID.pp)missingPT.ppt)else(Util.warnf"%a@,missing cases in match: (@[%a@])@ :in `%a`"Loc.pp_optloc(Util.pp_listID.pp)missingPT.ppt;l)end(* infer a term, and force its type to [prop] *)andinfer_prop_?locctxt:T.t=lett=infer_rec?locctxtinunify?loc(T.ty_exnt)T.Ty.prop;tletinfer_exnctxt=ZProf.enter_profprof_infer;Util.debugf~section50"@[<2>infer type of@ `@[%a@]`@]"(funk->kPT.ppt);trylett=infer_recctxtinZProf.exit_profprof_infer;twithe->ZProf.exit_profprof_infer;raiseeletinferctxt=tryErr.return(infer_exnctxt)withe->Err.of_exn_traceeletinfer_clause_exnctxc=ZProf.enter_profprof_infer;tryletc=List.map(infer_prop_ctx)cinCtx.exit_scopectx;ZProf.exit_profprof_infer;cwithe->ZProf.exit_profprof_infer;raiseeletinfer_prop_exnctxt=lett'=infer_exnctxtinunify?loc:(PT.loct)(T.ty_exnt')T.prop;t'letconstrain_term_term_exn?locctxt1t2=lett1=infer_exnctxt1inlett2=infer_exnctxt2inunify?loc(T.ty_exnt1)(T.ty_exnt2)letconstrain_term_term?locctxt1t2=tryErr.return(constrain_term_term_exn?locctxt1t2)withe->Err.of_exn_traceeletconstrain_term_type_exn?locctxtty=lett=infer_exnctxtinunify?locty(T.ty_exnt)letconstrain_term_type?locctxtty=tryErr.return(constrain_term_type_exn?locctxtty)withe->Err.of_exn_tracee(** {2 Statements} *)typetyped_statement=(typed,typed,type_)Statement.tmoduleA=UntypedASTmoduleStmt=Statementletcheck_vars_rhs?locboundrhs=letvars_rhs=T.Seq.free_varsrhs|>Var.Set.of_iterin(* check that all variables of [rhs] are within [lhs] *)letonly_in_rhs=Var.Set.diffvars_rhsboundinifnot(Var.Set.is_emptyonly_in_rhs)then(error_?loc"variables @[%a@]@ occur in RHS/cond `@[%a@]`@ but are not bound"Var.Set.pponly_in_rhsT.pprhs;)(* check that [vars rhs] subseteq [vars lhs] *)letcheck_vars_eqn?locboundlhsrhs=letvars_lhs=T.Seq.free_varslhs|>Var.Set.of_iterin(* check that all variables in [lhs] are bound *)letnot_bound=Var.Set.diffvars_lhsboundinifnot(Var.Set.is_emptynot_bound)thenerror_?loc"variables @[%a@] are not bound"Var.Set.ppnot_bound;check_vars_rhs?locvars_lhsrhs;()(* decompose [t] as [forall vars. id args = rhs]
or [forall vars. lhs <=> rhs]
@return [id, ty, args, rhs] or [lhs,rhs]
@param bound the set of bound variables so far *)letrecas_def?loc?of_boundt=letfail()=error_?loc"expected `forall <vars>. <lhs> =/<=> <rhs>`"andyield_termidtyargsrhs=letvars=Iter.of_listargs|>Iter.flat_mapT.Seq.free_vars|>Var.Set.add_seqbound|>Var.Set.to_list|>T.sort_ty_vars_firstin(* check that we talk about the same ID *)beginmatchof_with|Someid'whennot(ID.equalidid')->error_?loc"rule `%a`@ for `%a` has head symbol `%a`@ \
every rule in the definition of `%a` \
must start with `%a`"T.pptID.ppidID.ppid'ID.ppidID.ppid;|_->()end;check_ty_prenex?locty;ifT.Ty.returns_tTypetythen(error_?loc"in definition of %a,@ equality between types is forbidden"ID.ppid;);Stmt.Def_term{vars;id;ty;args;rhs;as_form=t}andyield_proplhsrhspol=letvars=SLiteral.to_iterlhs|>Iter.flat_mapT.Seq.free_vars|>Var.Set.add_seqbound|>Var.Set.to_list|>T.sort_ty_vars_firstinassert(T.Ty.is_prop(T.ty_exnrhs));beginmatchlhswith|SLiteral.Atom(t,_)->beginmatchT.headt,of_with|Someid,Someid'whennot(ID.equalid'id)->error_?loc"rule `%a`@ must have `%a` as head symbol, not `%a`"T.pptID.ppid'ID.ppid|_->()end|_->()end;Stmt.Def_form{vars;lhs;rhs=[rhs];polarity=pol;as_form=[t]}inbeginmatchT.viewtwith|T.Bind(Binder.Forall,v,t)->as_def?loc(Var.Set.addboundv)t|T.AppBuiltin((Builtin.Equiv|Builtin.Imply)asop,[lhs;rhs])->(* check that LHS is a literal, and that all free variables
of RHS occur in LHS (bound variables are ok though) *)check_vars_eqn?locboundlhsrhs;letlhs=SLiteral.of_formlhsinletpol=ifop=Builtin.Equivthen`Equivelse`Implyinyield_proplhsrhspol|T.AppBuiltin(Builtin.Eq,[lhs;rhs])->check_vars_eqn?locboundlhsrhs;beginmatchT.viewlhswith|T.Constid->letty=T.ty_exnlhsinyield_termidty[]rhs|T.App(f,args)->beginmatchT.viewfwith|T.Constid->letty=T.ty_exnfinyield_termidtyargsrhs|_->fail()end|_->fail()end|T.AppBuiltin(Builtin.Not,[lhs])->letrhs=T.Form.false_incheck_vars_eqn?locboundlhsrhs;letlhs=SLiteral.of_formlhsinyield_proplhsrhs`Equiv|_whenT.Ty.is_prop(T.ty_exnt)->letrhs=T.Form.true_incheck_vars_eqn?locboundtrhs;letlhs=SLiteral.of_formtinyield_proplhsrhs`Equiv|_->fail()endletinfer_defs?locctx(l:A.deflist):(_,_,_)Stmt.deflist=(* first, declare all *)letdecls=List.map(fund->letid=ID.maked.A.def_idinletty=infer_ty_exnctxd.A.def_tyincheck_ty_prenex?locty;(* cannot return [Type] *)ifT.Ty.returns_tTypetythen(error_?loc"in definition of %a,@ equality between types is forbidden"ID.ppid;);Ctx.declare?locctxidty;id,ty,d.A.def_rules)lin(* now, infer type of each definition *)List.map(fun(id,ty,rules)->letrules=List.map(funr->letr=infer_prop_exnctxrinas_def?loc~of_:idVar.Set.emptyr)rulesinStmt.mk_def~rewrite:ctx.Ctx.def_as_rewriteidtyrules)decls(* see whether attributes contain some hints of notation for this ID *)letset_notationidattrs:unit=List.iter(function|A.A_app("infix",[A.A_quoteds])->ID.set_payloadid(ID.Attr_infixs)|A.A_app("prefix",[A.A_quoteds])->ID.set_payloadid(ID.Attr_prefixs)|_->())attrsletread_attrs~fileattrs=letmoduleA=UntypedASTinletattrs=Stmt.conv_attrsattrsandname=CCList.find_map(function|A.A_app("name",[(A.A_quoteds|A.A_app(s,[]))])->Somes|_->None)attrsinProof.Src.from_file?namefile,attrsletinfer_statement_exn?(file="<no file>")ctxst=Util.debugf~section30"@[<2>infer types for @{<yellow>statement@}@ `@[%a@]`@]"(funk->kA.pp_statementst);(* auxiliary statements *)letsrc,attrs=read_attrs~filest.A.attrsinletloc=st.A.locinletst=matchst.A.stmtwith|A.Include_->error_?loc"remaining include statement"|A.Decl(s,ty)->(* new type
TODO: warning if it shadows? *)letid=ID.makesinletty=infer_ty_exnctxtyincheck_ty_prenex?locty;Ctx.declare?locctxidty;set_notationidst.A.attrs;Stmt.ty_decl~attrs~proof:(Proof.Step.introsrcProof.R_decl)idty|A.Defl->letl=infer_defs?locctxlinList.iter(fund->set_notationd.Stmt.def_idst.A.attrs)l;Stmt.def~attrs~proof:(Proof.Step.introsrcProof.R_def)l|A.Rewritet->lett=infer_prop_ctxtinletdef=as_def?locVar.Set.emptytinStmt.rewrite~proof:(Proof.Step.introsrcProof.R_def)def|A.Datal->(* declare the inductive types *)letdata_types=List.map(fund->letdata_ty=ID.maked.A.data_namein(* the type [data_ty : type -> type -> ... -> type] *)letty_of_data_ty=T.Ty.fun_(List.map(fun_->T.Ty.tType)d.A.data_vars)T.Ty.tTypeinCtx.declare?locctxdata_tyty_of_data_ty;data_ty,ty_of_data_ty)lin(* now we can infer the types of each constructor *)letl'=List.map2(fund(data_ty,ty_of_data_ty)->(* locally, declare type variables *)with_non_inferred_typed_vars?locctx(List.map(funv->PT.Vv,SomePT.tType)d.A.data_vars)~f:(funty_vars->(* return type of every constructor: [data_ty ty_vars] *)letty_ret=T.Ty.appdata_ty(List.map(T.Ty.var?loc:None)ty_vars)in(* infer type of constructors *)letcstors=List.map(fun(name,args)->letc_id=ID.makenamein(* type of c: forall ty_vars. ty_args -> ty_ret *)letargs=List.mapi(funi(p,ty)->letty=infer_ty_exnctxtyin(* type of projector *)letp_ty=T.Ty.forall_lty_vars(T.Ty.fun_[ty_ret]ty)andp_id=matchpwith|Somep->ID.makep|None->(* create projector *)ID.makef"proj_%a_%d"ID.ppc_idiinCtx.declare?locctxp_idp_ty;ty,(p_id,p_ty))argsinletty_args=List.mapfstargsinletty_c=T.Ty.forall_lty_vars(T.Ty.fun_ty_argsty_ret)inCtx.declare?locctxc_idty_c;(* TODO: check absence of other type variables in ty_c *)c_id,ty_c,args)d.A.data_cstorsinID.Tbl.addctx.Ctx.datatypesdata_tycstors;Stmt.mk_datadata_tyty_of_data_ty~args:ty_varscstors))ldata_typesinCtx.exit_scopectx;Stmt.data~attrs~proof:(Proof.Step.introsrcProof.R_def)l'|A.Assertt->lett=infer_prop_exnctxtinStmt.assert_~attrs~proof:(Proof.Step.introsrcProof.R_assert)t|A.Lemmat->lett=infer_prop_exnctxtinStmt.lemma~attrs~proof:(Proof.Step.introsrcProof.R_lemma)[t]|A.Goalt->lett=infer_prop_exnctxtinStmt.goal~attrs~proof:(Proof.Step.introsrcProof.R_goal)tin(* be sure to bind the remaining meta variables *)Ctx.exit_scopectx;letaux=Ctx.pop_new_typesctx|>List.map(fun(id,ty)->letproof=Proof.Step.intro(Proof.Src.internal[])Proof.R_declinStmt.ty_decl~proofidty)inst,auxletinfer_statements_exn?def_as_rewrite?on_var?on_undef?on_shadow?ctx?file~implicit_ty_argsseq=letctx=matchctxwith|None->Ctx.create?def_as_rewrite?on_var?on_undef?on_shadow~implicit_ty_args()|Somec->cinletres=CCVector.create()inIter.iter(funst->(* add declarations first *)letst,aux=infer_statement_exn?filectxstinList.iter(CCVector.pushres)aux;CCVector.pushresst)seq;CCVector.freezeresletinfer_statements?def_as_rewrite?on_var?on_undef?on_shadow?ctx?file~implicit_ty_argsseq=tryErr.return(infer_statements_exn?def_as_rewrite?on_var?on_undef?on_shadow?ctx?file~implicit_ty_argsseq)withe->Err.of_exn_tracee