123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688open!BaseopenPpxlib(* Copies source syntax to be used in generated code. Strips attributes and ensures all
locations are marked as "ghost". *)letcopy=objectinheritAst_traverse.mapmethod!locationloc={locwithloc_ghost=true}method!attributes_=[]end;;leterror~locfmt=Location.raise_errorf~loc(Stdlib.(^^)"ppx_globalize: "fmt)letis_global_fieldld=matchld.pld_mutablewith|Mutable->true|Immutable->(matchPpxlib_jane.Ast_builder.Default.get_label_declaration_modalityldwith|SomeGlobal,_->true|None,_->false);;(* Check if types are really recursive ignoring global and mutable
fields *)classis_recursiverec_flagdecls=objectinherittype_is_recursiverec_flagdeclsassupermethod!label_declarationld=ifis_global_fieldldthen()elsesuper#label_declarationldendletreally_recursiverec_flagdecls=(newis_recursiverec_flagdecls)#go()(* The name of the globalize function for a given type name as a string *)letglobalize_nametype_name=ifString.equaltype_name"t"then"globalize"else"globalize_"^type_name;;(* The name of the globalize function for a given type name as a
longident *)letglobalize_lid=function|Lidentname->Lident(globalize_namename)|Ldot(p,name)->Ldot(p,globalize_namename)|Lapply_->assertfalse;;moduleEnv:sig(* A mapping from type variables to globalize functions *)typet(* An empty mapping *)valempty:ttypevar=|Universal|Globalizeofexpression(* Lookup a globalize function *)vallookup:t->string->varoption(* Create a mapping for the type parameters of a type
declaration. Returns both the mapping and a list of names that
should be bound to the globalize functions of the parameters of the
type *)valof_type_decl:(moduleAst_builder.S)->type_declaration->t*stringlist(* Update a mapping for the body of a variant constructor. In the
non-GADT case the mapping is unchanged. In the GADT case we need to
build the mapping by looking at the result type of the
constructor. *)valenter_constructor_declaration:(moduleAst_builder.S)->t->constructor_declaration->t(* Update a mapping for the body of a polytype. *)valenter_poly:(moduleAst_builder.S)->t->stringloclist->tend=structtypevar=|Universal|Globalizeofexpressiontypet={vars:varMap.M(String).t;params:stringlist}letempty={vars=Map.empty(moduleString);params=[]}letlookuptname=Map.findt.varsnameletof_type_declbuilderdecl=letopen(valbuilder:Ast_builder.S)inletvars=Map.empty(moduleString)inletparams=[]inlett=List.fold_rightdecl.ptype_params~init:{vars;params}~f:(fun(typ,_){vars;params}->letvars,sym=matchtyp.ptyp_descwith|Ptyp_varname->letprefix="_globalize_"^nameinletsym=gen_symbol~prefix()inletvars=Map.add_exnvars~key:name~data:(Globalize(evarsym))invars,sym|_->letprefix="_globalize_param"inletsym=gen_symbol~prefix()invars,syminletparams=sym::paramsin{vars;params})int,t.params;;(* This is for GADTs; it finds the indices (as opposed to the
parameters) of the type and makes them their own globalize
parameters. Given a definition like:
{[
type ('a, 'b) t =
| Foo : ... -> ('c, 'd) t
]}
we are making a mapping from ['c] and ['d] to the globalize
functions of the first and second parameters. [params] has the list
of globalize functions so we just fold2 along that list and the
list of arguments to [t] in the result type adding mappings for
it. This completely replaces the outer mapping, which would have
mapped ['a] and ['b] to those parameters.
If the index is not a variable, or if the variable has already
appeared for another index, then we don't add a mapping. *)letenter_constructor_declarationbuilder{vars;params}cd=letopen(valbuilder:Ast_builder.S)inletvars=matchcd.pcd_reswith|None->vars|Somety->letvars=Map.empty(moduleString)in(matchty.ptyp_descwith|Ptyp_constr(_,args)whenList.lengthparams=List.lengthargs->List.fold2_exnparamsargs~init:vars~f:(funvarsparamarg->matcharg.ptyp_descwith|Ptyp_varname|Ptyp_alias(_,{txt=name;loc=_})->(matchMap.addvars~key:name~data:(Globalize(evarparam))with|`Duplicate->vars|`Okvars->vars)|_->vars)|_->vars)in{vars;params};;letenter_poly_builder{vars;params}names=letvars=List.fold~init:varsnames~f:(funvarsname->Map.setvars~key:name.txt~data:Universal)in{vars;params};;endletglobalize_arrow~locty=[%type:[%tty]->[%tty]](* Generate the type for a copier function for a given list of type
parameters and type name
*)letgenerate_typbuilderparamstype_name=letopen(valbuilder:Ast_builder.S)inletglobalize_arrow=globalize_arrow~locinlettype_lid=Located.lidenttype_nameinlettype_=ptyp_constrtype_lidparamsinList.fold_rightparams~init:(globalize_arrowtype_)~f:(funparamacc->ptyp_arrowNolabel(globalize_arrowparam)acc);;(* Is an object field a polymorphic method? *)letis_polymorphic_methodfield=matchfield.pof_descwith|Otag(_,ct)->(matchct.ptyp_descwith|Ptyp_poly_->true|_->false)|Oinherit_->false;;(* Strip a type to just its head for use in a coercion. This avoids
needing to worry about the scope of type variables. *)letrectype_headbuildertyp=letopen(valbuilder:Ast_builder.S)inmatchPpxlib_jane.Jane_syntax.Core_type.of_asttypwith|Some(Jtyp_tupleargs,_)->letargs=List.map~f:(fun(lbl,_)->lbl,ptyp_any)argsinPpxlib_jane.Jane_syntax.Core_type.core_type_of~loc~attrs:[](Jtyp_tupleargs)|Some(Jtyp_layout_,_)|None->(matchtyp.ptyp_descwith|Ptyp_any|Ptyp_var_|Ptyp_extension_->ptyp_any|Ptyp_tupleargs->letargs=List.map~f:(fun_->ptyp_any)argsinptyp_tupleargs|Ptyp_constr(lid,[])->ptyp_constr(Located.mklid.txt)[]|Ptyp_constr(lid,_::_)->ptyp_constr(Located.mklid.txt)[ptyp_any]|Ptyp_variant(fields,closed,labels)->letfields=List.mapfields~f:(funfield->matchfield.prf_descwith|Rtag(label,const,args)->rtaglabelconst(List.map~f:(fun_->ptyp_any)args)|Rinherittyp->rinherit(type_headbuildertyp))inptyp_variantfieldsclosedlabels|Ptyp_alias(typ,_)->type_headbuildertyp|Ptyp_arrow(lbl,_,_)->ptyp_arrowlblptyp_anyptyp_any|Ptyp_package(mty,constrs)->letconstrs=List.map~f:(fun(lid,_)->lid,ptyp_any)constrsinptyp_package(mty,constrs)|Ptyp_object(fields,closed)->ifList.existsfields~f:is_polymorphic_methodthenptyp_anyelse(letfields=List.mapfields~f:(funfield->matchfield.pof_descwith|Otag(lbl,_)->otaglblptyp_any|Oinherittyp->oinherit(type_headbuildertyp))inptyp_objectfieldsclosed)|Ptyp_class(lid,args)->letargs=List.map~f:(fun_->ptyp_any)argsinptyp_class(Located.mklid.txt)args|Ptyp_poly_|Ptyp_open_->assertfalse);;letmode_crossing_attr_name="globalized"letmode_crossing_attr_core_type=Attribute.declaremode_crossing_attr_nameAttribute.Context.core_typeAst_pattern.(pstrnil)();;letmode_crossing_attr_label_declaration=Attribute.declaremode_crossing_attr_nameAttribute.Context.label_declarationAst_pattern.(pstrnil)();;(* We generate a beta-redex to give a better error message
if the type does not cross modes. *)letglobalized_mode_crossingexptyploc=letloc={locwithloc_ghost=true}inletbuilder=Ast_builder.makelocinletopen(valbuilder:Ast_builder.S)inpexp_apply(pexp_constraint(pexp_funNolabelNone(ppat_var{txt="x";loc})(pexp_ident{txt=Lident"x";loc}))[%type:[%ttyp]->[%tcopy#core_typetyp]])[Nolabel,exp];;(* Generate code to create a globalized copy of the value produced by
the expression [exp] of type [typ]. *)letrecgenerate_globalized_for_typbuilderenvexpname_opttyp=letopen(valbuilder:Ast_builder.S)inlettyp_loc=typ.ptyp_locinmatchAttribute.consumemode_crossing_attr_core_typetypwith|Some(typ,())->globalized_mode_crossingexptyptyp_loc|None->(matchPpxlib_jane.Jane_syntax.Core_type.of_asttypwith|Some(Jtyp_tupleargs,_attrs)->lettpat,texp=generate_globalized_for_tuple_argsbuilderenvargsinpexp_letNonrecursive[value_binding~pat:tpat~expr:exp]texp|Some(Jtyp_layout_,_)|None->(matchtyp.ptyp_descwith|Ptyp_varname->(matchEnv.lookupenvnamewith|Some(Globalizefn)->eapplyfn[exp]|SomeUniversal->error~loc:typ.ptyp_loc"Cannot generate globalize function for universal type variable '%s"name|None->error~loc:typ.ptyp_loc"Cannot generate globalize function for unbound type variable '%s"name)|Ptyp_tupleargs->lettpat,texp=generate_globalized_for_tuple_argsbuilderenv(List.map~f:(funarg->None,arg)args)inpexp_letNonrecursive[value_binding~pat:tpat~expr:exp]texp|Ptyp_constr(lid,args)->letargs=List.map~f:(generate_globalized_for_typ_as_functionbuilderenvNone)argsinletlid=globalize_lidlid.txtineapply(pexp_ident(Located.mklid))(args@[exp])|Ptyp_variant(fields,Closed,None)->letinherits,constants,nonconstants=List.fold_rightfields~init:([],[],[])~f:(funfield(inherits,consts,nonconsts)->matchfield.prf_descwith|Rtag(name,false,[arg])->inherits,consts,(name.txt,arg)::nonconsts|Rtag(name,true,[])->inherits,name.txt::consts,nonconsts|Rtag(_,_,_)->error~loc:typ.ptyp_loc"Cannot generate globalize function for partial variant type"|Rinherittyp->(matchtyp.ptyp_descwith|Ptyp_constr(lid,_)->(lid.txt,typ)::inherits,consts,nonconsts|_->error~loc:typ.ptyp_loc"Cannot generate globalize function for unnamed inherited variant \
constructors"))inletinherit_cases=List.mapinherits~f:(fun(lid,inher)->letv=gen_symbol~prefix:"x"()inletlid=Located.mklidinletlhs=ppat_alias(ppat_typelid)(Located.mkv)inlettyp=matchname_optwith|None->typ|Sometyp->typinletrhs=pexp_coerce(generate_globalized_for_typbuilderenv(evarv)Noneinher)(Some(type_headbuilderinher))(type_headbuildertyp)incase~lhs~rhs~guard:None)inletconstants_case=matchconstantswith|[]->None|first::rest->letv=gen_symbol~prefix:"x"()inletfirst_pat=ppat_variantfirstNoneinletlhs=ppat_alias(List.fold~init:first_patrest~f:(funaccname->ppat_oracc(ppat_variantnameNone)))(Located.mkv)inletrhs=evarvinSome(case~lhs~rhs~guard:None)inletnonconstants_cases=List.mapnonconstants~f:(fun(name,arg)->letv=gen_symbol~prefix:"arg"()inletlhs=ppat_variantname(Some(pvarv))inletarg=generate_globalized_for_typbuilderenv(evarv)Nonearginletrhs=pexp_variantname(Somearg)incase~lhs~rhs~guard:None)inletcases=inherit_cases@Option.to_listconstants_case@nonconstants_casesinpexp_matchexpcases|Ptyp_variant(_,Open,_)->error~loc:typ.ptyp_loc"Cannot generate globalize function for open variant type"|Ptyp_variant(_,Closed,Some_)->error~loc:typ.ptyp_loc"Cannot generate globalize function for partial variant type"|Ptyp_alias(typ,{txt=name;loc=_})->(matchEnv.lookupenvnamewith|Some(Globalizefn)->eapplyfn[exp]|SomeUniversal|None->generate_globalized_for_typbuilderenvexpname_opttyp)|Ptyp_poly(names,typ)->letenv=Env.enter_polybuilderenvnamesingenerate_globalized_for_typbuilderenvexpNonetyp|Ptyp_any->error~loc:typ.ptyp_loc"Cannot generate globalize function for unknown type"|Ptyp_arrow(_,_,_)->error~loc:typ.ptyp_loc"Cannot generate globalize function for function type"|Ptyp_object(_,_)->error~loc:typ.ptyp_loc"Cannot generate globalize function for object type"|Ptyp_class(_,_)->error~loc:typ.ptyp_loc"Cannot generate globalize function for class type"|Ptyp_package_->error~loc:typ.ptyp_loc"Cannot generate globalize function for first-class module type"|Ptyp_extension_->error~loc:typ.ptyp_loc"Cannot generate globalize function for unknown extension"|Ptyp_open_->assertfalse))(* Generate code for a function to globalize values of type [type]. *)andgenerate_globalized_for_typ_as_functionbuilderenvname_opttyp=letopen(valbuilder:Ast_builder.S)inletv=gen_symbol~prefix:"x"()inletlhs=pvarvinletrhs=generate_globalized_for_typbuilderenv(evarv)name_opttypineta_reduce_if_possible(pexp_funNolabelNonelhsrhs)(* Generate code to create a globalized copy of the arguments of a tuple
with types [args]. Returns a pattern to match the tuple, an
expression to produce the copy, and some value bindings for
intermediate values. *)andgenerate_globalized_for_tuple_argsbuilderenvargs=letopen(valbuilder:Ast_builder.S)inletpats,exps=List.fold_right~init:([],[])~f:(fun(lbl,arg)(pats,exps)->letvin=gen_symbol~prefix:"arg"()inletpat=pvarvininletlocal_exp=evarvininletexp=matchPpxlib_jane.Ast_builder.Default.get_tuple_field_modalityargwith|SomeGlobal,_->local_exp|None,_->generate_globalized_for_typbuilderenvlocal_expNoneargin(lbl,pat)::pats,(lbl,exp)::exps)argsinletpat=matchpatswith|[]|[(Some_,_)]->assertfalse|[(None,pat)]->pat|_::_->Ppxlib_jane.Jane_syntax.Pattern.pat_of~loc~attrs:[](Jpat_tuple(pats,Closed))inletexp=matchexpswith|[]|[(Some_,_)]->assertfalse|[(None,exp)]->exp|_::_->Ppxlib_jane.Jane_syntax.Expression.expr_of~loc~attrs:[](Jexp_tupleexps)inpat,exp;;(* Generate code to create a globalized copy of the arguments of a
record with labels [lds]. Returns a pattern to match the record, an
expression to produce the copy, and some value bindings for
intermediate values. *)letgenerate_globalized_for_record_argsbuilderenvlds=letopen(valbuilder:Ast_builder.S)inletpats,exps=List.fold_right~init:([],[])~f:(funld(pats,exps)->letname=ld.pld_name.txtinletlid=Located.mk(Lidentname)inletvin=gen_symbol~prefix:name()inletpat=lid,pvarvininletlocal_exp=evarvininletld_loc=ld.pld_locinletexp=matchAttribute.consumemode_crossing_attr_label_declarationldwith|Some(ld,())->globalized_mode_crossinglocal_expld.pld_typeld_loc|None->ifis_global_fieldldthenlocal_expelsegenerate_globalized_for_typbuilderenvlocal_expNoneld.pld_typeinpat::pats,(lid,exp)::exps)ldsinppat_recordpatsClosed,pexp_recordexpsNone;;(* Generate code to create a globalized copy of the value produced by
the expression [exp] of a type with record labels [lds]. *)letgenerate_globalized_for_recordbuilderenvexplds=letopen(valbuilder:Ast_builder.S)inletrpat,rexp=generate_globalized_for_record_argsbuilderenvldsinpexp_letNonrecursive[value_binding~pat:rpat~expr:exp]rexp;;(* Generate code to create a globalized copy of the value produced by
the expression [exp] of a type with variant constructors [cds]. *)letgenerate_globalized_for_variantbuilderenvexpcds=letopen(valbuilder:Ast_builder.S)inletconstants,nonconstants=List.fold_rightcds~init:([],[])~f:(fun(cd:constructor_declaration)(consts,nonconsts)->(* We differentiate between constant cases for GADTs vs normal variants
because currently, the type checker does not allow the use of as-pattern
to rename an or-pattern of GADTs when it does allow us to do so for normal
variants.
This is fixed in an upstream PR: https://github.com/ocaml/ocaml/pull/11799
When this is merged we can collapse the constants case back into a singular
branch. *)matchcd.pcd_res,cd.pcd_argswith|None,Pcstr_tuple[]->letname=cd.pcd_name.txtinletconsts=name::constsinconsts,nonconsts|None,((Pcstr_tuple_|Pcstr_record_)asargs)|Some_,((Pcstr_tuple_|Pcstr_record_)asargs)->letname=cd.pcd_name.txtinletenv=Env.enter_constructor_declarationbuilderenvcdinletnonconsts=(name,args,env)::nonconstsinconsts,nonconsts)inletconstants_case=matchconstantswith|[]->None|first::rest->letv=gen_symbol~prefix:"x"()inletfirst_lid=Located.mk(Lidentfirst)inletfirst_pat=ppat_constructfirst_lidNoneinletlhs=ppat_alias(List.fold~init:first_patrest~f:(funaccname->letlid=Located.mk(Lidentname)inppat_oracc(ppat_constructlidNone)))(Located.mkv)inletrhs=evarvinSome(case~lhs~rhs~guard:None)inletnonconstants_cases=List.mapnonconstants~f:(fun(name,args,env)->letpat,exp=matchargswith|Pcstr_tuple[]->None,None|Pcstr_tupleargs->letpat,exp=generate_globalized_for_tuple_argsbuilderenv(List.map~f:(funarg->None,arg)args)inSomepat,Someexp|Pcstr_recordlds->letpat,exp=generate_globalized_for_record_argsbuilderenvldsinSomepat,Someexpinletlid=Located.mk(Lidentname)inletlhs=ppat_constructlidpatinletrhs=pexp_constructlidexpincase~lhs~rhs~guard:None)inletcases=Option.to_listconstants_case@nonconstants_casesinpexp_matchexpcases;;(* Generate code to create a globalized copy of the value produced by
the expression [exp] of a type with declaration [decl]. *)letgenerate_globalized_for_declbuilderenvexpnamedecl=letopen(valbuilder:Ast_builder.S)inmatchdecl.ptype_kindwith|Ptype_abstract->(matchdecl.ptype_manifestwith|Sometyp->letname=letargs=matchdecl.ptype_paramswith|[]->[]|_::_->[ptyp_any]inptyp_constr(Located.lidentname)argsingenerate_globalized_for_typbuilderenvexp(Somename)typ|None->error~loc"Cannot generate globalize function for abstract type")|Ptype_recordlds->generate_globalized_for_recordbuilderenvexplds|Ptype_variantcds->generate_globalized_for_variantbuilderenvexpcds|Ptype_open->error~loc"Cannot generate globalize function for extensible variants";;(* Generate code for a function to globalize values of a type with
declaration [decl]. *)letgenerate_globalized_for_decl_as_functionbuilderenvnamedecl=letopen(valbuilder:Ast_builder.S)inletv=gen_symbol~prefix:"x"()inletlhs=pvarvinletrhs=generate_globalized_for_declbuilderenv(evarv)namedeclinpexp_funNolabelNonelhsrhs;;(* Generate a value binding for a function to globalize values of a type with
declaration [decl]. *)letgenerate_vbrec_flagdecl=letloc={decl.ptype_locwithloc_ghost=true}inletbuilder=Ast_builder.makelocinletopen(valbuilder:Ast_builder.S)inlettype_name=decl.ptype_name.txtinletname=globalize_nametype_nameinletpat=pvarnameinletparam_names=List.mapidecl.ptype_params~f:(funi(param,_)->matchparam.ptyp_descwith|Ptyp_varname->name|_->"param"^Int.to_stringi)inletexternal_params=List.mapparam_names~f:(funname->ptyp_varname)inletexternal_param_bindings=List.mapparam_names~f:(funname->Located.mkname)inletexternal_type=ptyp_polyexternal_param_bindings(generate_typbuilderexternal_paramstype_name)inletpat=ppat_constraintpatexternal_typeinletinternal_param_names=List.mapparam_names~f:(funname->gen_symbol~prefix:name())inletinternal_params=List.mapinternal_param_names~f:(funname->ptyp_constr(Located.lidentname)[])inletinternal_type=generate_typbuilderinternal_paramstype_nameinletenv,params=Env.of_type_declbuilderdeclinletfn=generate_globalized_for_decl_as_functionbuilderenvtype_namedeclinletfn=eabstract(List.map~f:pvarparams)fninletfn=eta_reduce_if_possible_and_nonrec~rec_flagfninletexpr=pexp_constraintfninternal_typeinletexpr=List.fold_right~init:expr~f:(funnameacc->pexp_newtype(Located.mkname)acc)internal_param_namesinvalue_binding~pat~expr;;(* Generate a value declaration for a function to globalize values of a type
with declaration [decl]. *)letgenerate_valdecl=letloc={decl.ptype_locwithloc_ghost=true}inletbuilder=Ast_builder.makelocinletopen(valbuilder:Ast_builder.S)inlettype_name=decl.ptype_name.txtinletname=Located.mk(globalize_nametype_name)inletparams=List.mapdecl.ptype_params~f:(fun(param,_)->param)inlettype_=generate_typbuilderparamstype_nameinletvd=value_description~name~type_~prim:[]inpsig_valuevd;;(* The deriver for types in structures *)letgenerate_str~ctxt:_(rec_flag,decls)=letrec_flag=really_recursiverec_flagdeclsinletvbs=List.map~f:(generate_vbrec_flag)declsin[Ast_builder.Default.pstr_value~loc:Location.nonerec_flagvbs];;(* The deriver for types in signatures *)letgenerate_sig~ctxt:_(_rec_flag,decls)=List.map~f:generate_valdecls(* The implementation of `[%globalize: ...]` *)letextension~loc:_~path:_typ=letloc={typ.ptyp_locwithloc_ghost=true}inletbuilder=Ast_builder.makelocingenerate_globalized_for_typ_as_functionbuilderEnv.emptyNonetyp;;letextension_name="globalize"letglobalize=letstr_type_decl=Deriving.Generator.V2.make_noarggenerate_strinletsig_type_decl=Deriving.Generator.V2.make_noarggenerate_siginDeriving.addextension_name~str_type_decl~sig_type_decl~extension;;