123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305(**********************************************************************)(* *)(* This file is part of the RFSM package *)(* *)(* Copyright (c) 2018-present, Jocelyn SEROT. All rights reserved. *)(* *)(* This source code is licensed under the license found in the *)(* LICENSE file in the root directory of this source tree. *)(* *)(**********************************************************************)(**{1 The type checker for the host language} *)moduletypeTYPING=sigmoduleHostSyntax:Syntax.SYNTAXtypeenvtypetyped_program={tp_models:HostSyntax.modellist;(* Uninstanciated models *)tp_insts:(Ident.t*HostSyntax.model)list;(* Model instances *)}(* [@@deriving show {with_path=false}] *)valmk_env:unit->envvaltype_program:env->HostSyntax.program->typed_programvalpp_env:Format.formatter->env->unitvalpp_typed_program:Format.formatter->typed_program->unitvaltype_fragment:HostSyntax.fragment->unitexceptionDuplicate_symbolofLocation.t*Ident.texceptionInvalid_stateofLocation.t*Ident.texceptionDuplicate_stateofLocation.t*Ident.texceptionNo_event_inputofLocation.texceptionIllegal_instofLocation.texceptionIllegal_state_outputofLocation.t*Ident.t*Ident.texceptionType_mismatchofLocation.t*string*HostSyntax.typendmoduleMake(HS:Syntax.SYNTAX)(GT:Guest.TYPINGwithmoduleSyntax=HS.GuestandtypeTypes.typ=HS.typ)(GS:Guest.STATICwithtypeexpr=HS.expr):TYPINGwithmoduleHostSyntax=HS=structmoduleHostSyntax=HSmoduleGuestTyping=GTmoduleA=Annottypeenv=GuestTyping.envtypetyped_program={tp_models:HostSyntax.modellist;tp_insts:(Ident.t*HostSyntax.model)list;}[@@derivingshow{with_path=false}]exceptionDuplicate_symbolofLocation.t*Ident.texceptionInvalid_stateofLocation.t*Ident.texceptionDuplicate_stateofLocation.t*Ident.texceptionNo_event_inputofLocation.texceptionIllegal_instofLocation.texceptionIllegal_state_outputofLocation.t*Ident.t*Ident.texceptionType_mismatchofLocation.t*string*HostSyntax.typletmk_env()=GuestTyping.mk_env()(* Typing FSM models *)lettype_fsm_actionenvact=(* TO FIX: the type of an action should always be "unit" ? *)letloc=act.A.locinletty=matchact.A.descwith|HostSyntax.Emits->lett=GuestTyping.lookup_var~locsenvinifGuestTyping.Types.is_event_typetthentelseraise(Type_mismatch(loc,"event",t))|HostSyntax.Assign(lval,expr)->lett=GuestTyping.type_lvalenvlvalinlett'=GuestTyping.type_expressionenvexprinGuestTyping.type_check~loctt';tinact.A.typ<-tyletcheck_type~loc~whatcheckt=ifnot@@checktthenraise(Type_mismatch(loc,what,t))lettype_fsm_event~locenvev=check_type~loc~what:"event"GuestTyping.Types.is_event_type@@GuestTyping.lookup_var~locevenvlettype_fsm_guardenvgexp=check_type~loc:gexp.Annot.loc~what:"bool"GuestTyping.Types.is_bool_type@@GuestTyping.type_expressionenvgexplettype_fsm_conditionenvA.{desc=ev,gs;loc=loc;_}=type_fsm_event~locenvev;List.iter(type_fsm_guardenv)gsletcheck_fsm_state~locA.{desc=m;_}q=letstates=List.map(functions->s.A.desc)m.HostSyntax.statesinifnot(List.mem_assocqstates)thenraise(Invalid_state(loc,q))lettype_fsm_transitionenvmA.{desc=q,cond,acts,q',_;loc=loc;_}=(* For each transition [q -> cond / acts -> q'] check that
* - [q] and [q'] are listed as states in the model declaration
* - [cond] has form [e.[guard1]...[guardn]] where [e] has type [event] and each [guardi] type [bool]
* - for each [act]=[lval:=rhs] in [acts], [type(rhs)=type(lval)] *)check_fsm_state~locmq;check_fsm_state~locmq';type_fsm_conditionenvcond;List.iter(type_fsm_actionenv)actslettype_fsm_itransitionenvmA.{desc=q,acts;loc=loc;_}=(* For the initial transition [/ acts -> q] check that
* - [q] is listed as state in model declaration
* - for each [act]=[v:=exp] in [acts], [type(v)=type(exp)] *)check_fsm_state~locmq;List.iter(type_fsm_actionenv)actslettype_fsm_state_valuationenv(m:HostSyntax.model)q(o,expr)=(* Note : [expr] is _syntactically_ limited to be a scalar constant. Hence [env] is not really needed here. *)lette=tryList.assocom.A.desc.HostSyntax.outpswithNot_found->raise(Illegal_state_output(expr.A.loc,q,o))inGuestTyping.type_check~loc:expr.A.loc(GuestTyping.type_of_type_exprenvte)(GuestTyping.type_expressionenvexpr)lettype_fsm_stateenvm{A.desc=q,ovs;_}=List.iter(type_fsm_state_valuationenvmq)ovslettype_fsm_statesenvm=letstates=m.A.desc.HostSyntax.statesinletreccheck_duplstates=matchstateswith|[]->()|{A.desc=q,_;A.loc=loc;_}::rest->ifList.exists(function{A.desc=q',_;_}->q=q')restthenraise(Duplicate_state(loc,q))elsecheck_duplrestincheck_duplstates;List.iter(type_fsm_stateenvm)stateslettype_fsm_iosenvA.{desc=m;loc=loc;_}=(* Check that there's exactly one input with type event *)letis_event_type(_,te)=GuestTyping.Types.is_event_typete.A.typinmatchList.filteris_event_typem.HostSyntax.inpswith|[]->raise(No_event_inputloc)|_->()lettype_fsm_modelenv(A.{desc=md;_}asm)=type_fsm_statesenvm;letadd_sym~scope(env,syms)(id,te)=ifList.memidsymsthenraise(Duplicate_symbol(m.A.loc,id))elseletty=GuestTyping.type_of_type_exprenvteinGuestTyping.add_var~scopeenv(id,ty),id::symsinList.iter(* TO FIX : typing the mixed [ios] list and the sorted [inps],[outps] and [inouts] lists is clearly redundant *)(fun(id,(_,te))->let_=GuestTyping.type_of_type_exprenvtein())md.HostSyntax.ios;letenv',_=(env,[])|>Ext.List.fold_leftr(add_sym~scope:Local)md.HostSyntax.inps|>Ext.List.fold_leftr(add_sym~scope:Local)md.HostSyntax.outps|>Ext.List.fold_leftr(add_sym~scope:Local)md.HostSyntax.inouts|>Ext.List.fold_leftr(add_sym~scope:Local)md.HostSyntax.vars|>Ext.List.fold_leftr(add_sym~scope:Global)md.HostSyntax.paramsintype_fsm_iosenv'm;List.iter(type_fsm_transitionenv'm)md.trans;type_fsm_itransitionenv'mmd.itrans;m(* Typing FSM instances *)lettype_fsm_instenvpA.{desc=name,model,params,args;loc=loc;_}=letopenHostSyntaxinletlookup_modelname=tryList.find(fun{A.desc=m;_}->m.name=name)p.modelswithNot_found->raise(Ident.Undefined("symbol",loc,name))inletlookup_ioname=tryList.find(fun{A.desc=(id,_,_,_);_}->id=name)p.globalswithNot_found->raise(Ident.Undefined("symbol",loc,name))inletunify_catio_catgl_cat=matchio_cat,gl_catwith|In,Output->raise(Illegal_instloc)|Out,Input->raise(Illegal_instloc)|_,_->()in(* Get associated model *)letmm=Ext.Base.clone@@lookup_modelmodelin(* Note: we need a _deep copy_ of the model so that destructive updates performed by
type-checking are applied to fresh copies *)letm=mm.A.descin(* Type-check parameters *)letbind_param(id,te)e=letty=GuestTyping.type_of_type_exprenvteinletty'=GuestTyping.type_expressionenveinGuestTyping.type_check~loc:loctyty';(id,e)inletty_params=tryList.map2bind_paramm.paramsparamswithInvalid_argument_->raise(Illegal_instloc)in(* Augment the typing environment with the bindings of parameters *)letenv'=List.fold_leftGuestTyping.add_paramenvty_paramsin(* Type the instanciated model *)let_=type_fsm_modelenv'mminletbind_arg(id,(cat,te))id'=let_,cat',te',_=(lookup_ioid').A.descinletty=te.A.typinunify_catcatcat';GuestTyping.type_check~loctyte'.A.typ;(id',ty)inlet_=tryList.map2bind_argm.iosargs;withInvalid_argument_->raise(Illegal_instloc)in(name,mm)(* Typing globals *)lettype_stimulusenvidtyst=st.A.typ<-ty;matchst.A.descwith|HostSyntax.Periodic_|HostSyntax.Sporadic_->check_type~loc:st.A.loc~what:"event"GuestTyping.Types.is_event_typety|HostSyntax.Value_changevcs->List.iter(GuestTyping.type_check~loc:st.A.locty)(List.map(function(_,e)->GuestTyping.type_expressionenve)vcs)lettype_globalenv({A.desc=id,cat,te,st;_}asgl)=letty=GuestTyping.type_of_type_exprenvteinlet_=matchstwith|Somest->type_stimulusenvidtyst|None->()ingl.A.typ<-ty(* Typing function declarations *)lettype_fun_declenv(A.{desc=fd;loc=loc;_}asf)=letopenHostSyntaxinletty_args=List.map(function(id,te)->id,GuestTyping.type_of_type_exprenvte)fd.ff_argsinletenv'=List.fold_left(GuestTyping.add_var~scope:Local)envty_argsinletty_body=GuestTyping.type_expressionenv'fd.ff_bodyinletty_result=GuestTyping.type_of_type_exprenvfd.ff_resinGuestTyping.type_check~loc:locty_bodyty_result;letty=GuestTyping.Types.mk_type_fun(List.mapsndty_args)ty_resultinf.A.typ<-ty;GuestTyping.add_var~scope:Globalenv(fd.ff_name,ty)(* Typing constant declarations *)lettype_cst_declenv(A.{desc=cd;loc=loc;_}asc)=letopenHostSyntaxinletty=GuestTyping.type_of_type_exprenvcd.cc_typinletty'=GuestTyping.type_expressionenvcd.cc_valinGuestTyping.type_check~loc:loctyty';c.A.typ<-ty;GuestTyping.add_var~scope:Globalenv(cd.cc_name,ty)letpp_envfmtenv=GuestTyping.pp_envfmtenv(* Typing programs *)lettype_programenv0p=letopenHostSyntaxinletenv1=List.fold_leftGuestTyping.type_type_declenv0p.type_declsinletenv2=List.fold_lefttype_fun_declenv1p.fun_declsinletenv=List.fold_lefttype_cst_declenv2p.cst_declsinList.iter(type_globalenv)p.globals;{tp_models=List.map(type_fsm_modelenv)p.models;tp_insts=List.map(type_fsm_instenvp)p.insts}(* Typing program fragments (server mode only) *)lettype_fragment_guardenvg=type_fsm_guardenvglettype_fragment_actionenva=type_fsm_actionenvalettype_fragment_valuationenv(o,expr)=(* Note : [expr] is _syntactically_ limited to be a scalar constant.
Hence [env] is, _here_, only used to retrieve the type of [o] *)letloc=expr.A.locinlett=GuestTyping.lookup_var~locoenvinGuestTyping.type_check~loct(GuestTyping.type_expressionenvexpr)lettype_fragment_objpenv0o=letopenHostSyntaxinletadd_symenv(id,te)=letty=GuestTyping.type_of_type_exprenvteinGuestTyping.add_var~scope:Ident.Localenv(id,ty)inletaugment_envenvios=List.fold_leftadd_symenviosinmatchowith|Guarde->type_fragment_guard(augment_envenv0(p.pf_inps@p.pf_vars))e|Actiona->type_fragment_action(augment_envenv0(p.pf_inps@p.pf_vars@p.pf_outps))a|SVal(o,e)->type_fragment_valuation(augment_envenv0p.pf_outps)(o,e)lettype_fragmentp=lettenv=mk_env()intype_fragment_objptenvp.HostSyntax.pf_objend