Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file ppx_stable_witness.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282open!BaseopenPpxlibopenAst_builder.Defaultletghostloc={locwithloc_ghost=true}letstrip_locs=(* Replace locations with a dummy so that syntax can be compared for equality. *)objectinheritAst_traverse.mapmethod!location_=Location.noneend;;letcopy=(* Strip off attributes and mark all locations as ghost so that a copy of syntax
(usually a type) can be included in generated code. *)objectinheritAst_traverse.mapmethod!attributes_=[]method!location=ghostend;;letcustom_attr=Attribute.declare"stable_witness.custom"Core_typeAst_pattern.(pstr(pstr_eval__nil^::nil))Fn.id;;letstable_witness_name=function|"t"->"stable_witness"|type_name->"stable_witness_"^type_name;;letstable_witness_type~loccore_type=[%type:[%tcopy#core_typecore_type]Ppx_stable_witness_runtime.Stable_witness.t];;letstable_witness_variablevar="__'"^var^"_stable_witness"moduleValue_binding=structletcompareab=Comparable.liftPoly.compare~f:strip_locs#value_bindingabendmoduleSignature=structletexpand_type_declarationtd=lettd=name_type_params_in_tdtdinletloc=ghosttd.ptype_locinvalue_description~loc~name:(Loc.map~f:stable_witness_nametd.ptype_name)~type_:(combinator_type_of_type_declarationtd~f:stable_witness_type)~prim:[]|>psig_value~loc;;letexpand~loc:_~path:_(_,tds):signature=List.maptds~f:expand_type_declarationendmoduleStructure=struct(* We generate [let] clauses to check types of stable witnesses that the current one
depends on. *)letcheck~locwitness_exprwitness_type=value_binding~loc~pat:[%pat?(_:[%twitness_type])]~expr:witness_expr;;letunsupported~locdescription=(* Rather than raise at expansion time, we generate a [%error] node that can complain
during compile time. This is more merlin-friendly, among other benefits. *)letmessage=Printf.sprintf"ppx_stable_witness: %s not supported"descriptionincheck~loc[%expr[%ocaml.error[%eestring~locmessage]]][%type:_];;letcheck_type_constructor~locidparams=letwitness_expr=unapplied_type_constr_conv~locid~f:stable_witness_nameinletwitness_type=List.fold_rightparams~init:(stable_witness_type~loc(ptyp_constr~locidparams))~f:(funparamcore_type->letloc=ghostparam.ptyp_locinptyp_arrow~locNolabel(stable_witness_type~locparam)core_type)incheck~locwitness_exprwitness_type;;letcheck_type_variable~locvar=letwitness_expr=evar~loc(stable_witness_variablevar)inletwitness_type=stable_witness_type~loc(ptyp_var~locvar)incheck~locwitness_exprwitness_type;;letreccheck_core_typecore_type=letloc=ghostcore_type.ptyp_locinmatchAttribute.getcustom_attrcore_typewith|Someexpr->[check~locexpr(stable_witness_type~loccore_type)]|None->(matchcore_type.ptyp_descwith|Ptyp_any->[unsupported~loc"wildcard type"]|Ptyp_varvar->[check_type_variable~locvar]|Ptyp_arrow_->[unsupported~loc"arrow type"]|Ptyp_tupletuple->List.concat_maptuple~f:check_core_type|Ptyp_constr(id,params)->check_type_constructor~locidparams::List.concat_mapparams~f:check_core_type|Ptyp_object_->[unsupported~loc"object type"]|Ptyp_class_->[unsupported~loc"class type"]|Ptyp_alias(core_type,_)->check_core_typecore_type|Ptyp_variant(rows,_,_)->List.concat_maprows~f:check_row_field|Ptyp_poly(_,_)->[unsupported~loc"polymorphic type"]|Ptyp_package_->[unsupported~loc"first-class module type"]|Ptyp_extension_->[unsupported~loc"ppx extension"])andcheck_row_fieldrow=matchrow.prf_descwith|Rtag(_,_,core_types)->List.concat_map~f:check_core_typecore_types|Rinheritcore_type->check_core_typecore_type;;letcheck_label_declarationld=check_core_typeld.pld_typeletcheck_constructor_declarationcd=matchcd.pcd_reswith|Some_->[unsupported~loc:cd.pcd_loc"GADT"]|None->(matchcd.pcd_argswith|Pcstr_tupletuple->List.concat_map~f:check_core_typetuple|Pcstr_recordrecord->List.concat_map~f:check_label_declarationrecord);;letparam_patternstd=List.maptd.ptype_params~f:(funparam->letcore_type=fstparaminletloc=ghostcore_type.ptyp_locinppat_constraint~loc(pvar~loc(stable_witness_variable(get_type_param_nameparam).txt))(stable_witness_type~loccore_type));;(* Generate all the witness type checks for a type declaration. *)letcheck_type_declarationtd=letloc=ghosttd.ptype_locinletpat=pvar~loc("__stable_witness_checks_for_"^td.ptype_name.txt^"__")inletchecks=matchtd.ptype_kindwith|Ptype_open->[unsupported~loc"open type"]|Ptype_recordfields->List.concat_mapfields~f:check_label_declaration|Ptype_variantclauses->List.concat_mapclauses~f:check_constructor_declaration|Ptype_abstract->(matchtd.ptype_manifestwith|None->[]|Somecore_type->check_core_typecore_type)inletchecks=(* Don't bother generating obviously redundant checks. *)List.stable_dedup~compare:Value_binding.comparechecksinmatchList.is_emptycheckswith|true->[]|false->(* If there are any witnesses to check, we generate a function parameterized by any
arguments to the current witness and a unit argument. Since this is always a
lambda, it is safe inside [let rec]. *)letexpr=eunit~loc|>pexp_let~locNonrecursivechecks|>eabstract~loc(param_patternstd@[punit~loc])in[value_binding~loc~pat~expr];;(* Create a stable witness for a type we trust to be stable. Evalutes to a variable
reference so that it is safe inside [let rec]. *)letassert_witness_forcore_type=letloc=ghostcore_type.ptyp_locinpexp_constraint~loc[%exprPpx_stable_witness_runtime.Stable_witness.assert_stable](stable_witness_type~loccore_type);;(* Generate the actual stable witness definition for a type declaration. *)letexpand_type_declarationtd=letloc=ghosttd.ptype_locinletexpr=List.maptd.ptype_params~f:fst|>ptyp_constr~loc(Located.map_lidenttd.ptype_name)|>assert_witness_for|>eabstract~loc(param_patternstd)inletpat=pvar~loc:td.ptype_name.loc(stable_witness_nametd.ptype_name.txt)invalue_binding~loc~pat~expr;;letshadows_self_without_redefiningtd=matchtd.ptype_manifestwith|Some{ptyp_desc=Ptyp_constr({txt=Lidentname;_},params);_}whenString.equalnametd.ptype_name.txt->(matchList.for_all2paramstd.ptype_params~f:(funactual(formal,_)->matchactual.ptyp_desc,formal.ptyp_descwith|Ptyp_vara,Ptyp_varb->String.equalab|_->false)with|Okbool->bool|Unequal_lengths->false)|_->false;;classrefers_to_redefined_typetds=object(* We pass [Recursive] here, even though this is only actually called on
non-recursive definitions. What we really want to check for is whether a type
refers to its own name, not whether it's recursive. This is equivalent to
[type_is_recursive Recursive], so we use that even though it reads wrong. *)inherittype_is_recursiveRecursivetdsval!type_names=List.filter_maptds~f:(funtd->matchshadows_self_without_redefiningtdwith|true->(* No need to check for references to types that redefine a name to precisely
what it was before. *)None|false->(* Anything else, we need to look for. *)Sometd.ptype_name.txt)endletrefers_to_redefined_typetds=letobj=newrefers_to_redefined_typetdsinmatchobj#go()with|Nonrecursive->false|Recursive->true;;(* Define both the stable witness, and all the checks, for a type declaration. Define
them both in the same [let] with a shared [rec_flag] so that the checks have the same
scope as the stable witness itself. *)letexpand~loc~path:_(rec_flag,tds)=lettds=List.maptds~f:name_type_params_in_tdinmatchrec_flagwith|Nonrecursivewhenrefers_to_redefined_typetds->letmessage=Printf.sprintf"ppx_stable_witness: This definition shadows a type that it also refers to. \
Expanded code needs to refer to the shadowed name.\n\n\
Perhaps you can define an alias for the shadowed type, then use the alias in \
this definition."in[%str[%%ocaml.error[%eestring~locmessage]]]|_->letrec_flag=really_recursiverec_flagtdsinList.concat[List.maptds~f:expand_type_declaration;List.concat_maptds~f:check_type_declaration]|>pstr_value_list~locrec_flag;;(* Expand a single type to an expression containing its checks and a stable witness. *)letextension~loc~path:_core_type=letchecks=check_core_typecore_typeinletbody=assert_witness_forcore_typeinmatchList.is_emptycheckswith|true->body|false->pexp_let~locNonrecursivechecksbody;;endletextension=Structure.extensionletsig_type_decl=Deriving.Generator.make_noargSignature.expandletstr_type_decl=Deriving.Generator.make_noargStructure.expandlet()=Deriving.add"stable_witness"~sig_type_decl~str_type_decl~extension|>Deriving.ignore;;