open! Base open! Ppxlib open Ast_builder.Default open Helpers open Lifted.Monad_infix (* Generates the signature for type conversion to S-expressions *) module Sig_generate_sexp_of = struct let type_of_sexp_of ~loc t = let loc = { loc with loc_ghost = true } in [%type: [%t t] -> Sexplib0.Sexp.t] ;; let mk_type td = combinator_type_of_type_declaration td ~f:type_of_sexp_of let mk_sig ~loc:_ ~path:_ (_rf, tds) = List.map tds ~f:(fun td -> let loc = td.ptype_loc in psig_value ~loc (value_description ~loc ~name:(Located.map (( ^ ) "sexp_of_") td.ptype_name) ~type_:(mk_type td) ~prim:[])) ;; let mk_sig_exn ~loc:_ ~path:_ _te = [] end module Str_generate_sexp_of = struct module Types_being_defined = struct type t = | Nonrec | Rec of Set.M(String).t let to_rec_flag = function | Nonrec -> Nonrecursive | Rec _ -> Recursive ;; let to_values_being_defined = function | Nonrec -> Set.empty (module String) | Rec types -> Set.map (module String) types ~f:(fun s -> "sexp_of_" ^ s) ;; end let sexp_of_type_constr ~loc id args = type_constr_conv ~loc id ~f:(fun s -> "sexp_of_" ^ s) args ;; (* Conversion of types *) let rec sexp_of_type ~renaming typ : Conversion.t = let loc = { typ.ptyp_loc with loc_ghost = true } in match Ppxlib_jane.Jane_syntax.Core_type.of_ast typ with | Some (Jtyp_tuple alist, (_ : attributes)) -> Conversion.of_lambda [ sexp_of_labeled_tuple ~renaming ~loc alist ] | Some (Jtyp_layout _, _) | None -> (match typ with | _ when Option.is_some (Attribute.get Attrs.opaque typ) -> Conversion.of_reference_exn [%expr Sexplib0.Sexp_conv.sexp_of_opaque] | [%type: _] -> Conversion.of_lambda [ ppat_any ~loc --> [%expr Sexplib0.Sexp.Atom "_"] ] | [%type: [%t? _] sexp_opaque] -> Conversion.of_reference_exn [%expr Sexplib0.Sexp_conv.sexp_of_opaque] | { ptyp_desc = Ptyp_tuple tp; _ } -> Conversion.of_lambda [ sexp_of_tuple ~renaming (loc, tp) ] | { ptyp_desc = Ptyp_var parm; _ } -> (match Renaming.binding_kind renaming parm ~loc with | Universally_bound fresh -> Conversion.of_reference_exn (Fresh_name.expression fresh) | Existentially_bound -> sexp_of_type ~renaming [%type: _]) | { ptyp_desc = Ptyp_constr (id, args); _ } -> Conversion.of_reference_exn (sexp_of_type_constr ~loc id (List.map args ~f:(fun tp -> Conversion.to_expression ~loc (sexp_of_type ~renaming tp)))) | { ptyp_desc = Ptyp_arrow (_, _, _); _ } -> Conversion.of_lambda [ ppat_any ~loc --> [%expr Sexplib0.Sexp_conv.sexp_of_fun Sexplib0.Sexp_conv.ignore] ] | { ptyp_desc = Ptyp_variant (row_fields, Closed, _); _ } -> sexp_of_variant ~renaming (loc, row_fields) | { ptyp_desc = Ptyp_poly (parms, poly_tp); _ } -> sexp_of_poly ~renaming parms poly_tp | { ptyp_desc = Ptyp_variant (_, Open, _); _ } | { ptyp_desc = Ptyp_object (_, _); _ } | { ptyp_desc = Ptyp_class (_, _); _ } | { ptyp_desc = Ptyp_alias (_, _); _ } | { ptyp_desc = Ptyp_package _; _ } | { ptyp_desc = Ptyp_extension _; _ } | { ptyp_desc = Ptyp_open _; _ } -> Location.raise_errorf ~loc "Type unsupported for ppx [sexp_of] conversion") (* Conversion of (unlabeled) tuples *) and sexp_of_tuple ~renaming (loc, tps) = let fps = List.map ~f:(fun tp -> sexp_of_type ~renaming tp) tps in let ({ bindings; arguments; converted } : Conversion.Apply_all.t) = Conversion.apply_all ~loc fps in let in_expr = [%expr Sexplib0.Sexp.List [%e elist ~loc converted]] in let expr = pexp_let ~loc Nonrecursive bindings in_expr in ppat_tuple ~loc arguments --> expr (* Conversion of labeled tuples *) and sexp_of_labeled_tuple ~renaming ~loc alist = assert (Labeled_tuple.is_valid alist); let ({ bindings; arguments; converted } : Conversion.Apply_all.t) = List.map alist ~f:(fun (_, core_type) -> sexp_of_type ~renaming core_type) |> Conversion.apply_all ~loc in let expr = let sexp_exprs = (* Constructor inference allows to to leave off [Sexplib0.Sexp.] here. *) List.map2_exn alist converted ~f:(fun (label_option, _) expr -> [%expr List [ Atom [%e estring ~loc (Labeled_tuple.atom_of_label label_option)] ; [%e expr] ]]) in [%expr Sexplib0.Sexp.List [%e elist ~loc sexp_exprs]] |> pexp_let ~loc Nonrecursive bindings in let pat = ( List.map2_exn alist arguments ~f:(fun (label_option, _) arg -> label_option, arg) , Closed ) |> Ppxlib_jane.Jane_syntax.Labeled_tuples.pat_of ~loc in pat --> expr (* Conversion of variant types *) and sexp_of_variant ~renaming ((loc, row_fields) : Location.t * row_field list) : Conversion.t = let item row = match row.prf_desc with | Rtag ({ txt = cnstr; _ }, true, []) -> ppat_variant ~loc cnstr None --> [%expr Sexplib0.Sexp.Atom [%e estring ~loc cnstr]] | Rtag ({ txt = cnstr; _ }, _, [ tp ]) when Option.is_some (Attribute.get Attrs.list_poly row) -> (match tp with | [%type: [%t? tp] list] -> let cnv_expr = Conversion.to_expression ~loc (sexp_of_type ~renaming tp) in let name = Fresh_name.create "l" ~loc in ppat_variant ~loc cnstr (Some (Fresh_name.pattern name)) --> [%expr Sexplib0.Sexp.List (Sexplib0.Sexp.Atom [%e estring ~loc cnstr] :: Sexplib0.Sexp_conv.list_map [%e cnv_expr] [%e Fresh_name.expression name])] | _ -> Attrs.invalid_attribute ~loc Attrs.list_poly "_ list") | Rtag ({ txt = cnstr; _ }, _, [ [%type: [%t? tp] sexp_list] ]) -> let cnv_expr = Conversion.to_expression ~loc (sexp_of_type ~renaming tp) in let name = Fresh_name.create "l" ~loc in ppat_variant ~loc cnstr (Some (Fresh_name.pattern name)) --> [%expr Sexplib0.Sexp.List (Sexplib0.Sexp.Atom [%e estring ~loc cnstr] :: Sexplib0.Sexp_conv.list_map [%e cnv_expr] [%e Fresh_name.expression name])] | Rtag ({ txt = cnstr; _ }, false, [ tp ]) -> let cnstr_expr = [%expr Sexplib0.Sexp.Atom [%e estring ~loc cnstr]] in let fresh = Fresh_name.create "v" ~loc in let cnstr_arg = Conversion.apply ~loc (sexp_of_type ~renaming tp) (Fresh_name.expression fresh) in let expr = [%expr Sexplib0.Sexp.List [%e elist ~loc [ cnstr_expr; cnstr_arg ]]] in ppat_variant ~loc cnstr (Some (Fresh_name.pattern fresh)) --> expr | Rinherit { ptyp_desc = Ptyp_constr (id, []); _ } -> let name = Fresh_name.create "v" ~loc in ppat_alias ~loc (ppat_type ~loc id) (Fresh_name.to_string_loc name) --> sexp_of_type_constr ~loc id [ Fresh_name.expression name ] | Rtag (_, true, [ _ ]) | Rtag (_, _, _ :: _ :: _) -> Location.raise_errorf ~loc "unsupported: polymorphic variant intersection type" | Rinherit ({ ptyp_desc = Ptyp_constr (id, _ :: _); _ } as typ) -> let call = Conversion.to_expression ~loc (sexp_of_type ~renaming typ) in let name = Fresh_name.create "v" ~loc in ppat_alias ~loc (ppat_type ~loc id) (Fresh_name.to_string_loc name) --> [%expr [%e call] [%e Fresh_name.expression name]] | Rinherit _ -> Location.raise_errorf ~loc "unsupported: polymorphic variant with invalid (non-identifier) inherited type" | Rtag (_, false, []) -> Location.raise_errorf ~loc "unsupported: polymorphic variant empty type" in Conversion.of_lambda (List.map ~f:item row_fields) (* Polymorphic record fields *) and sexp_of_poly ~renaming parms tp = let loc = tp.ptyp_loc in let renaming = List.fold_left parms ~init:renaming ~f:(Renaming.add_universally_bound ~prefix:"_of_") in let bindings = let mk_binding parm = let name = match Renaming.binding_kind renaming parm.txt ~loc:parm.loc with | Universally_bound name -> name | Existentially_bound -> assert false in value_binding ~loc ~pat:(Fresh_name.pattern name) ~expr:[%expr Sexplib0.Sexp_conv.sexp_of_opaque] in List.map ~f:mk_binding parms in Conversion.bind (sexp_of_type ~renaming tp) bindings ;; (* Conversion of record types *) let mk_rec_patt loc patt name fresh = let p = Loc.make (Longident.Lident name) ~loc, Fresh_name.pattern fresh in patt @ [ p ] ;; type is_empty_expr = | Inspect_value of (location -> expression -> expression) | Inspect_sexp of (cnv_expr:expression -> location -> expression -> expression) let sexp_of_record_field ~renaming ~bnds patt expr name tp ?sexp_of is_empty_expr = let loc = tp.ptyp_loc in let fresh = Fresh_name.create name ~loc in let patt = mk_rec_patt loc patt name fresh in let cnv_expr = Conversion.to_expression ~loc (sexp_of_type ~renaming tp) in let cnv_expr = match sexp_of with | None -> cnv_expr | Some sexp_of -> [%expr [%e sexp_of] [%e cnv_expr]] in let bnd = Fresh_name.create "bnd" ~loc in let arg = Fresh_name.create "arg" ~loc in let expr = [%expr let [%p Fresh_name.pattern bnds] = [%e match is_empty_expr with | Inspect_value is_empty_expr -> [%expr if [%e is_empty_expr loc (Fresh_name.expression fresh)] then [%e Fresh_name.expression bnds] else ( let [%p Fresh_name.pattern arg] = [%e cnv_expr] [%e Fresh_name.expression fresh] in let [%p Fresh_name.pattern bnd] = Sexplib0.Sexp.List [ Sexplib0.Sexp.Atom [%e estring ~loc name] ; [%e Fresh_name.expression arg] ] in ([%e Fresh_name.expression bnd] :: [%e Fresh_name.expression bnds] : _ Stdlib.List.t))] | Inspect_sexp is_empty_expr -> [%expr let [%p Fresh_name.pattern arg] = [%e cnv_expr] [%e Fresh_name.expression fresh] in if [%e is_empty_expr ~cnv_expr loc (Fresh_name.expression arg)] then [%e Fresh_name.expression bnds] else ( let [%p Fresh_name.pattern bnd] = Sexplib0.Sexp.List [ Sexplib0.Sexp.Atom [%e estring ~loc name] ; [%e Fresh_name.expression arg] ] in ([%e Fresh_name.expression bnd] :: [%e Fresh_name.expression bnds] : _ Stdlib.List.t))]] in [%e expr]] in patt, expr ;; let disallow_type_variables_and_recursive_occurrences ~types_being_defined ~loc ~attr_name tp = let disallow_variables = let iter = object inherit Ast_traverse.iter as super method! core_type_desc = function | Ptyp_var v -> Location.raise_errorf ~loc "[@%s] was used, but the type of the field contains a type variable: '%s.\n\ Comparison is not avaiable for type variables.\n\ Consider using [@sexp_drop_if _] or [@sexp_drop_default.sexp] instead." attr_name v | t -> super#core_type_desc t end in iter#core_type in let disallow_recursive_occurrences = match (types_being_defined : Types_being_defined.t) with | Nonrec -> fun _ -> () | Rec types_being_defined -> let iter = object inherit Ast_traverse.iter as super method! core_type_desc = function | Ptyp_constr ({ loc = _; txt = Lident s }, _) as t -> if Set.mem types_being_defined s then Location.raise_errorf ~loc "[@%s] was used, but the type of the field contains a type defined \ in the current recursive block: %s.\n\ This is not supported.\n\ Consider using [@sexp_drop_if _] or [@sexp_drop_default.sexp] \ instead." attr_name s; super#core_type_desc t | t -> super#core_type_desc t end in iter#core_type in disallow_variables tp; disallow_recursive_occurrences tp ;; let sexp_of_default_field ~types_being_defined how ~renaming ~bnds patt expr name tp ?sexp_of default = let is_empty = let inspect_value equality_f = Inspect_value (fun loc expr -> [%expr [%e equality_f loc] [%e default] [%e expr]]) in match (how : Record_field_attrs.Sexp_of.Drop.t) with | Sexp -> Inspect_sexp (fun ~cnv_expr loc sexp_expr -> [%expr Sexplib0.Sexp_conv.( = ) ([%e cnv_expr] [%e default]) [%e sexp_expr]]) |> Lifted.return | No_arg -> inspect_value (fun loc -> [%expr Sexplib0.Sexp_conv.( = ) [@ocaml.ppwarning "[@sexp_drop_default] is deprecated: please use \ one of:\n\ - [@sexp_drop_default f] and give an explicit \ equality function ([f = Poly.(=)] corresponds to \ the old behavior)\n\ - [@sexp_drop_default.compare] if the type \ supports [%compare]\n\ - [@sexp_drop_default.equal] if the type \ supports [%equal]\n\ - [@sexp_drop_default.sexp] if you want to \ compare the sexp representations\n"]]) |> Lifted.return | Func lifted -> lifted >>| fun f -> inspect_value (fun _ -> f) | Compare -> inspect_value (fun loc -> disallow_type_variables_and_recursive_occurrences ~types_being_defined ~attr_name:"sexp_drop_default.compare" ~loc tp; [%expr [%compare.equal: [%t tp]]]) |> Lifted.return | Equal -> inspect_value (fun loc -> disallow_type_variables_and_recursive_occurrences ~types_being_defined ~attr_name:"sexp_drop_default.equal" ~loc tp; [%expr [%equal: [%t tp]]]) |> Lifted.return in is_empty >>| sexp_of_record_field ~renaming ~bnds patt expr name tp ?sexp_of ;; let sexp_of_label_declaration_list ~types_being_defined ~renaming loc flds ~wrap_expr = let bnds = Fresh_name.create "bnds" ~loc in let list_empty_expr = Inspect_value (fun loc lst -> [%expr match [%e lst] with | [] -> true | _ -> false]) in let array_empty_expr = Inspect_value (fun loc arr -> [%expr match [%e arr] with | [||] -> true | _ -> false]) in let coll lifted ld = lifted >>= fun ((patt : (Longident.t loc * pattern) list), expr) -> let name = ld.pld_name.txt in let loc = ld.pld_name.loc in let fresh = Fresh_name.create name ~loc in match Record_field_attrs.Sexp_of.create ~loc ld with | Sexp_option tp -> let v = Fresh_name.create "v" ~loc in let bnd = Fresh_name.create "bnd" ~loc in let arg = Fresh_name.create "arg" ~loc in let patt = mk_rec_patt loc patt name fresh in let vname = Fresh_name.expression v in let cnv_expr = Conversion.apply ~loc (sexp_of_type ~renaming tp) vname in let expr = [%expr let [%p Fresh_name.pattern bnds] = match [%e Fresh_name.expression fresh] with | Stdlib.Option.None -> [%e Fresh_name.expression bnds] | Stdlib.Option.Some [%p Fresh_name.pattern v] -> let [%p Fresh_name.pattern arg] = [%e cnv_expr] in let [%p Fresh_name.pattern bnd] = Sexplib0.Sexp.List [ Sexplib0.Sexp.Atom [%e estring ~loc name] ; [%e Fresh_name.expression arg] ] in ([%e Fresh_name.expression bnd] :: [%e Fresh_name.expression bnds] : _ Stdlib.List.t) in [%e expr]] in Lifted.return (patt, expr) | Sexp_bool -> let patt = mk_rec_patt loc patt name fresh in let bnd = Fresh_name.create "bnd" ~loc in let expr = [%expr let [%p Fresh_name.pattern bnds] = if [%e Fresh_name.expression fresh] then ( let [%p Fresh_name.pattern bnd] = Sexplib0.Sexp.List [ Sexplib0.Sexp.Atom [%e estring ~loc name] ] in ([%e Fresh_name.expression bnd] :: [%e Fresh_name.expression bnds] : _ Stdlib.List.t)) else [%e Fresh_name.expression bnds] in [%e expr]] in Lifted.return (patt, expr) | Sexp_list tp -> sexp_of_record_field ~renaming ~bnds patt expr name tp ~sexp_of: (* deliberately using whatever [sexp_of_list] is in scope *) [%expr sexp_of_list] list_empty_expr |> Lifted.return | Sexp_array tp -> sexp_of_record_field ~renaming ~bnds patt expr name tp ~sexp_of: (* deliberately using whatever [sexp_of_array] is in scope *) [%expr sexp_of_array] array_empty_expr |> Lifted.return | Specific (Drop_default how) -> let tp = ld.pld_type in (match Attribute.get Attrs.default ld with | None -> Location.raise_errorf ~loc "no default to drop" | Some { to_lift = default } -> Record_field_attrs.lift_default ~loc ld default >>= sexp_of_default_field ~types_being_defined how ~renaming ~bnds patt expr name tp) | Specific (Drop_if test) -> test >>| fun test -> let tp = ld.pld_type in sexp_of_record_field ~renaming ~bnds patt expr name tp (Inspect_value (fun loc expr -> [%expr [%e test] [%e expr]])) | Omit_nil -> let tp = ld.pld_type in let patt = mk_rec_patt loc patt name fresh in let vname = Fresh_name.expression fresh in let arg = Fresh_name.create "arg" ~loc in let cnv_expr = Conversion.apply ~loc (sexp_of_type ~renaming tp) vname in let bnds_expr = [%expr match [%e cnv_expr] with | Sexplib0.Sexp.List [] -> [%e Fresh_name.expression bnds] | [%p Fresh_name.pattern arg] -> (Sexplib0.Sexp.List [ Sexplib0.Sexp.Atom [%e estring ~loc name] ; [%e Fresh_name.expression arg] ] :: [%e Fresh_name.expression bnds] : _ Stdlib.List.t)] in ( patt , [%expr let [%p Fresh_name.pattern bnds] = [%e bnds_expr] in [%e expr]] ) |> Lifted.return | Specific Keep -> let tp = ld.pld_type in let patt = mk_rec_patt loc patt name fresh in let vname = Fresh_name.expression fresh in let arg = Fresh_name.create "arg" ~loc in let cnv_expr = Conversion.apply ~loc (sexp_of_type ~renaming tp) vname in let bnds_expr = [%expr let [%p Fresh_name.pattern arg] = [%e cnv_expr] in (Sexplib0.Sexp.List [ Sexplib0.Sexp.Atom [%e estring ~loc name] ; [%e Fresh_name.expression arg] ] :: [%e Fresh_name.expression bnds] : _ Stdlib.List.t)] in ( patt , [%expr let [%p Fresh_name.pattern bnds] = [%e bnds_expr] in [%e expr]] ) |> Lifted.return in let init_expr = wrap_expr (Fresh_name.expression bnds) in List.fold_left ~f:coll ~init:(Lifted.return ([], init_expr)) flds >>| fun (patt, expr) -> ( ppat_record ~loc patt Closed , [%expr let [%p Fresh_name.pattern bnds] = ([] : _ Stdlib.List.t) in [%e expr]] ) ;; (* Conversion of sum types *) let branch_sum row inline_attr ~types_being_defined renaming ~loc constr_lid constr_str args = match args with | Pcstr_record lds -> let cnstr_expr = [%expr Sexplib0.Sexp.Atom [%e constr_str]] in sexp_of_label_declaration_list ~types_being_defined ~renaming loc lds ~wrap_expr:(fun expr -> [%expr Sexplib0.Sexp.List ([%e cnstr_expr] :: [%e expr])]) >>| fun (patt, expr) -> ppat_construct ~loc constr_lid (Some patt) --> expr | Pcstr_tuple pcd_args -> (match pcd_args with | [] -> ppat_construct ~loc constr_lid None --> [%expr Sexplib0.Sexp.Atom [%e constr_str]] |> Lifted.return | args -> (match args with | [ tp ] when Option.is_some (Attribute.get inline_attr row) -> (match tp with | [%type: [%t? tp] list] -> let cnv_expr = Conversion.to_expression ~loc (sexp_of_type ~renaming tp) in let name = Fresh_name.create "l" ~loc in ppat_construct ~loc constr_lid (Some (Fresh_name.pattern name)) --> [%expr Sexplib0.Sexp.List (Sexplib0.Sexp.Atom [%e constr_str] :: Sexplib0.Sexp_conv.list_map [%e cnv_expr] [%e Fresh_name.expression name])] | _ -> Attrs.invalid_attribute ~loc inline_attr "_ list") | [ [%type: [%t? tp] sexp_list] ] -> let cnv_expr = Conversion.to_expression ~loc (sexp_of_type ~renaming tp) in let name = Fresh_name.create "l" ~loc in ppat_construct ~loc constr_lid (Some (Fresh_name.pattern name)) --> [%expr Sexplib0.Sexp.List (Sexplib0.Sexp.Atom [%e constr_str] :: Sexplib0.Sexp_conv.list_map [%e cnv_expr] [%e Fresh_name.expression name])] | _ -> let sexp_of_args = List.map ~f:(sexp_of_type ~renaming) args in let cnstr_expr = [%expr Sexplib0.Sexp.Atom [%e constr_str]] in let ({ bindings; arguments; converted } : Conversion.Apply_all.t) = Conversion.apply_all ~loc sexp_of_args in let patt = match arguments with | [ arg ] -> arg | _ -> ppat_tuple ~loc arguments in ppat_construct ~loc constr_lid (Some patt) --> pexp_let ~loc Nonrecursive bindings [%expr Sexplib0.Sexp.List [%e elist ~loc (cnstr_expr :: converted)]]) |> Lifted.return) ;; let sexp_of_sum ~types_being_defined ~renaming tps cds = List.map cds ~f:(fun cd -> let renaming = Renaming.with_constructor_declaration renaming ~type_parameters:tps cd in let constr_lid = Located.map lident cd.pcd_name in let constr_str = estring ~loc:cd.pcd_name.loc cd.pcd_name.txt in branch_sum cd Attrs.list_variant ~types_being_defined renaming ~loc:cd.pcd_loc constr_lid constr_str cd.pcd_args) |> Lifted.all >>| Conversion.of_lambda ;; (* Empty type *) let sexp_of_nil loc = Conversion.of_lambda [ ppat_any ~loc --> [%expr assert false] ] (* Generate code from type definitions *) let sexp_of_td ~types_being_defined td = let td = name_type_params_in_td td in let tps = List.map td.ptype_params ~f:get_type_param_name in let { ptype_name = { txt = type_name; loc = _ }; ptype_loc = loc; _ } = td in let renaming = Renaming.of_type_declaration td ~prefix:"_of_" in let body = let body = match td.ptype_kind with | Ptype_variant cds -> sexp_of_sum ~renaming ~types_being_defined (List.map tps ~f:(fun x -> x.txt)) cds | Ptype_record lds -> sexp_of_label_declaration_list ~renaming loc lds ~types_being_defined ~wrap_expr:(fun expr -> [%expr Sexplib0.Sexp.List [%e expr]]) >>| fun (patt, expr) -> Conversion.of_lambda [ patt --> expr ] | Ptype_open -> Location.raise_errorf ~loc "ppx_sexp_conv: open types not supported" | Ptype_abstract -> (match td.ptype_manifest with | None -> sexp_of_nil loc | Some ty -> sexp_of_type ~renaming ty) |> Lifted.return in body >>| fun body -> let is_private_alias = match td.ptype_kind, td.ptype_manifest, td.ptype_private with | Ptype_abstract, Some _, Private -> true | _ -> false in if is_private_alias then ( (* Replace all type variable by _ to avoid generalization problems *) let ty_src = core_type_of_type_declaration td |> replace_variables_by_underscores in let manifest = match td.ptype_manifest with | Some manifest -> manifest | None -> Location.raise_errorf ~loc "sexp_of_td/no-manifest" in let ty_dst = replace_variables_by_underscores manifest in let v = Fresh_name.create "v" ~loc in let coercion = [%expr ([%e Fresh_name.expression v] : [%t ty_src] :> [%t ty_dst])] in [%expr fun [%p Fresh_name.pattern v] -> [%e Conversion.apply ~loc body coercion]]) else (* Prevent violation of value restriction, problems with recursive types, and top-level effects by eta-expanding function definitions *) Conversion.to_value_expression ~loc ~rec_flag:(Types_being_defined.to_rec_flag types_being_defined) ~values_being_defined: (Types_being_defined.to_values_being_defined types_being_defined) body in let typ = Sig_generate_sexp_of.mk_type td in let func_name = "sexp_of_" ^ type_name in let body = body >>| fun body -> let patts = List.map tps ~f:(fun id -> match Renaming.binding_kind renaming id.txt ~loc:id.loc with | Universally_bound name -> Fresh_name.pattern name | Existentially_bound -> assert false) in let rec_flag = Types_being_defined.to_rec_flag types_being_defined in eta_reduce_if_possible_and_nonrec ~rec_flag (eabstract ~loc patts body) in let body = Lifted.let_bind_user_expressions ~loc body in constrained_function_binding loc td typ ~tps ~func_name body ;; let sexp_of_tds ~loc ~path:_ (rec_flag, tds) = let rec_flag = really_recursive_respecting_opaque rec_flag tds in let (types_being_defined : Types_being_defined.t) = match rec_flag with | Nonrecursive -> Nonrec | Recursive -> Rec (Set.of_list (module String) (List.map tds ~f:(fun td -> td.ptype_name.txt))) in let bindings = List.map tds ~f:(sexp_of_td ~types_being_defined) in pstr_value_list ~loc rec_flag bindings ;; let sexp_of_exn ~loc:_ ~path ec = let renaming = Renaming.without_type () in let get_full_cnstr str = path ^ "." ^ str in let loc = ec.ptyexn_loc in let expr = match ec.ptyexn_constructor with | { pext_name = cnstr ; pext_kind = Pext_decl (_, extension_constructor_kind, None) ; _ } -> let constr_lid = Located.map lident cnstr in branch_sum ec Attrs.list_exception ~types_being_defined:Nonrec renaming ~loc constr_lid (estring ~loc (get_full_cnstr cnstr.txt)) extension_constructor_kind >>| fun converter -> let assert_false = ppat_any ~loc --> [%expr assert false] in [%expr Sexplib0.Sexp_conv.Exn_converter.add [%extension_constructor [%e pexp_construct ~loc constr_lid None]] [%e Conversion.to_expression ~loc (Conversion.of_lambda [ converter; assert_false ])]] | { pext_kind = Pext_decl (_, _, Some _); _ } -> Location.raise_errorf ~loc "sexp_of_exn/:" | { pext_kind = Pext_rebind _; _ } -> Location.raise_errorf ~loc "sexp_of_exn/rebind" in let expr = Lifted.let_bind_user_expressions ~loc expr in [ pstr_value ~loc Nonrecursive [ value_binding ~loc ~pat:[%pat? ()] ~expr ] ] ;; let sexp_of_core_type core_type = let loc = { core_type.ptyp_loc with loc_ghost = true } in sexp_of_type ~renaming:(Renaming.without_type ()) core_type |> Conversion.to_value_expression ~loc ~rec_flag:Nonrecursive ~values_being_defined:(Set.empty (module String)) |> Merlin_helpers.hide_expression ;; end