(**************************************************************************) (* *) (* OCaml Migrate Parsetree *) (* *) (* Frédéric Bour *) (* Jérémie Dimino, Jane Street Europe *) (* *) (* Copyright 2017 Institut National de Recherche en Informatique et *) (* en Automatique (INRIA). *) (* *) (* All rights reserved. This file is distributed under the terms of *) (* the GNU Lesser General Public License version 2.1, with the *) (* special exception on linking described in the file LICENSE. *) (* *) (**************************************************************************) (* BEGIN of BLACK MAGIC *) (*$ open Ast_cinaps_helpers $*) type _ witnesses = .. type _ migration = .. type _ migration += Undefined : _ migration type 'a migration_info = { mutable next_version : 'a migration; mutable previous_version : 'a migration; } (** Abstract view of a version of an OCaml Ast *) module type Ast = sig (*$ foreach_module (fun m types -> printf " module %s : sig\n" m; List.iter types ~f:(printf " type %s\n"); printf " end\n" ) *) module Parsetree : sig type structure type signature type toplevel_phrase type core_type type expression type pattern type case type type_declaration type type_extension type extension_constructor type class_expr type class_field type class_type type class_signature type class_type_field type module_expr type module_type type signature_item type structure_item end (*$*) module Config : sig val ast_impl_magic_number : string val ast_intf_magic_number : string end end (* Shortcuts for talking about ast types outside of the module language *) type 'a _types = 'a constraint 'a = < (*$ foreach_type (fun _ s -> printf " %-21s : _;\n" s) *) structure : _; signature : _; toplevel_phrase : _; core_type : _; expression : _; pattern : _; case : _; type_declaration : _; type_extension : _; extension_constructor : _; class_expr : _; class_field : _; class_type : _; class_signature : _; class_type_field : _; module_expr : _; module_type : _; signature_item : _; structure_item : _; (*$*) > ;; (*$ foreach_type (fun _ s -> printf "type 'a get_%s =\n" s; printf " 'x constraint 'a _types = < %s : 'x; .. >\n" s ) *) type 'a get_structure = 'x constraint 'a _types = < structure : 'x; .. > type 'a get_signature = 'x constraint 'a _types = < signature : 'x; .. > type 'a get_toplevel_phrase = 'x constraint 'a _types = < toplevel_phrase : 'x; .. > type 'a get_core_type = 'x constraint 'a _types = < core_type : 'x; .. > type 'a get_expression = 'x constraint 'a _types = < expression : 'x; .. > type 'a get_pattern = 'x constraint 'a _types = < pattern : 'x; .. > type 'a get_case = 'x constraint 'a _types = < case : 'x; .. > type 'a get_type_declaration = 'x constraint 'a _types = < type_declaration : 'x; .. > type 'a get_type_extension = 'x constraint 'a _types = < type_extension : 'x; .. > type 'a get_extension_constructor = 'x constraint 'a _types = < extension_constructor : 'x; .. > type 'a get_class_expr = 'x constraint 'a _types = < class_expr : 'x; .. > type 'a get_class_field = 'x constraint 'a _types = < class_field : 'x; .. > type 'a get_class_type = 'x constraint 'a _types = < class_type : 'x; .. > type 'a get_class_signature = 'x constraint 'a _types = < class_signature : 'x; .. > type 'a get_class_type_field = 'x constraint 'a _types = < class_type_field : 'x; .. > type 'a get_module_expr = 'x constraint 'a _types = < module_expr : 'x; .. > type 'a get_module_type = 'x constraint 'a _types = < module_type : 'x; .. > type 'a get_signature_item = 'x constraint 'a _types = < signature_item : 'x; .. > type 'a get_structure_item = 'x constraint 'a _types = < structure_item : 'x; .. > (*$*) module type OCaml_version = sig module Ast : Ast val version : int val string_version : string type types = < (*$ foreach_type (fun m s -> printf " %-21s : Ast.%s.%s;\n" s m s)*) structure : Ast.Parsetree.structure; signature : Ast.Parsetree.signature; toplevel_phrase : Ast.Parsetree.toplevel_phrase; core_type : Ast.Parsetree.core_type; expression : Ast.Parsetree.expression; pattern : Ast.Parsetree.pattern; case : Ast.Parsetree.case; type_declaration : Ast.Parsetree.type_declaration; type_extension : Ast.Parsetree.type_extension; extension_constructor : Ast.Parsetree.extension_constructor; class_expr : Ast.Parsetree.class_expr; class_field : Ast.Parsetree.class_field; class_type : Ast.Parsetree.class_type; class_signature : Ast.Parsetree.class_signature; class_type_field : Ast.Parsetree.class_type_field; module_expr : Ast.Parsetree.module_expr; module_type : Ast.Parsetree.module_type; signature_item : Ast.Parsetree.signature_item; structure_item : Ast.Parsetree.structure_item; (*$*) > _types type _ witnesses += Version : types witnesses val migration_info : types migration_info end module Make_witness(Ast : Ast) = struct type types = < (*$ foreach_type (fun m s -> printf " %-21s : Ast.%s.%s;\n" s m s)*) structure : Ast.Parsetree.structure; signature : Ast.Parsetree.signature; toplevel_phrase : Ast.Parsetree.toplevel_phrase; core_type : Ast.Parsetree.core_type; expression : Ast.Parsetree.expression; pattern : Ast.Parsetree.pattern; case : Ast.Parsetree.case; type_declaration : Ast.Parsetree.type_declaration; type_extension : Ast.Parsetree.type_extension; extension_constructor : Ast.Parsetree.extension_constructor; class_expr : Ast.Parsetree.class_expr; class_field : Ast.Parsetree.class_field; class_type : Ast.Parsetree.class_type; class_signature : Ast.Parsetree.class_signature; class_type_field : Ast.Parsetree.class_type_field; module_expr : Ast.Parsetree.module_expr; module_type : Ast.Parsetree.module_type; signature_item : Ast.Parsetree.signature_item; structure_item : Ast.Parsetree.structure_item; (*$*) > _types type _ witnesses += Version : types witnesses let migration_info : types migration_info = { next_version = Undefined; previous_version = Undefined } end type 'types ocaml_version = (module OCaml_version (*$ let sep = with_then_and () in foreach_type (fun m s -> printf " %t type Ast.%s.%s = 'types get_%s\n" sep m s s) *) with type Ast.Parsetree.structure = 'types get_structure and type Ast.Parsetree.signature = 'types get_signature and type Ast.Parsetree.toplevel_phrase = 'types get_toplevel_phrase and type Ast.Parsetree.core_type = 'types get_core_type and type Ast.Parsetree.expression = 'types get_expression and type Ast.Parsetree.pattern = 'types get_pattern and type Ast.Parsetree.case = 'types get_case and type Ast.Parsetree.type_declaration = 'types get_type_declaration and type Ast.Parsetree.type_extension = 'types get_type_extension and type Ast.Parsetree.extension_constructor = 'types get_extension_constructor and type Ast.Parsetree.class_expr = 'types get_class_expr and type Ast.Parsetree.class_field = 'types get_class_field and type Ast.Parsetree.class_type = 'types get_class_type and type Ast.Parsetree.class_signature = 'types get_class_signature and type Ast.Parsetree.class_type_field = 'types get_class_type_field and type Ast.Parsetree.module_expr = 'types get_module_expr and type Ast.Parsetree.module_type = 'types get_module_type and type Ast.Parsetree.signature_item = 'types get_signature_item and type Ast.Parsetree.structure_item = 'types get_structure_item (*$*) ) type ('from, 'to_) migration_functions = { (*$ foreach_type (fun _ s -> printf " copy_%s: 'from get_%s -> 'to_ get_%s;\n" s s s) *) copy_structure: 'from get_structure -> 'to_ get_structure; copy_signature: 'from get_signature -> 'to_ get_signature; copy_toplevel_phrase: 'from get_toplevel_phrase -> 'to_ get_toplevel_phrase; copy_core_type: 'from get_core_type -> 'to_ get_core_type; copy_expression: 'from get_expression -> 'to_ get_expression; copy_pattern: 'from get_pattern -> 'to_ get_pattern; copy_case: 'from get_case -> 'to_ get_case; copy_type_declaration: 'from get_type_declaration -> 'to_ get_type_declaration; copy_type_extension: 'from get_type_extension -> 'to_ get_type_extension; copy_extension_constructor: 'from get_extension_constructor -> 'to_ get_extension_constructor; copy_class_expr: 'from get_class_expr -> 'to_ get_class_expr; copy_class_field: 'from get_class_field -> 'to_ get_class_field; copy_class_type: 'from get_class_type -> 'to_ get_class_type; copy_class_signature: 'from get_class_signature -> 'to_ get_class_signature; copy_class_type_field: 'from get_class_type_field -> 'to_ get_class_type_field; copy_module_expr: 'from get_module_expr -> 'to_ get_module_expr; copy_module_type: 'from get_module_type -> 'to_ get_module_type; copy_signature_item: 'from get_signature_item -> 'to_ get_signature_item; copy_structure_item: 'from get_structure_item -> 'to_ get_structure_item; (*$*) } let id x = x let migration_identity : ('a, 'a) migration_functions = { (*$ foreach_type (fun _ s -> printf " copy_%s = id;\n" s) *) copy_structure = id; copy_signature = id; copy_toplevel_phrase = id; copy_core_type = id; copy_expression = id; copy_pattern = id; copy_case = id; copy_type_declaration = id; copy_type_extension = id; copy_extension_constructor = id; copy_class_expr = id; copy_class_field = id; copy_class_type = id; copy_class_signature = id; copy_class_type_field = id; copy_module_expr = id; copy_module_type = id; copy_signature_item = id; copy_structure_item = id; (*$*) } let compose f g x = f (g x) let migration_compose (ab : ('a, 'b) migration_functions) (bc : ('b, 'c) migration_functions) : ('a, 'c) migration_functions = { (*$ foreach_type (fun _ s -> printf " copy_%-21s = compose bc.copy_%-21s ab.copy_%s;\n" s s s) *) copy_structure = compose bc.copy_structure ab.copy_structure; copy_signature = compose bc.copy_signature ab.copy_signature; copy_toplevel_phrase = compose bc.copy_toplevel_phrase ab.copy_toplevel_phrase; copy_core_type = compose bc.copy_core_type ab.copy_core_type; copy_expression = compose bc.copy_expression ab.copy_expression; copy_pattern = compose bc.copy_pattern ab.copy_pattern; copy_case = compose bc.copy_case ab.copy_case; copy_type_declaration = compose bc.copy_type_declaration ab.copy_type_declaration; copy_type_extension = compose bc.copy_type_extension ab.copy_type_extension; copy_extension_constructor = compose bc.copy_extension_constructor ab.copy_extension_constructor; copy_class_expr = compose bc.copy_class_expr ab.copy_class_expr; copy_class_field = compose bc.copy_class_field ab.copy_class_field; copy_class_type = compose bc.copy_class_type ab.copy_class_type; copy_class_signature = compose bc.copy_class_signature ab.copy_class_signature; copy_class_type_field = compose bc.copy_class_type_field ab.copy_class_type_field; copy_module_expr = compose bc.copy_module_expr ab.copy_module_expr; copy_module_type = compose bc.copy_module_type ab.copy_module_type; copy_signature_item = compose bc.copy_signature_item ab.copy_signature_item; copy_structure_item = compose bc.copy_structure_item ab.copy_structure_item; (*$*) } type _ migration += Migration : 'from ocaml_version * ('from, 'to_) migration_functions * 'to_ ocaml_version -> 'from migration module type Migrate_module = sig module From : Ast module To : Ast (*$ foreach_type (fun m s -> printf " val copy_%-21s: From.%s.%s -> To.%s.%s\n" s m s m s) *) val copy_structure : From.Parsetree.structure -> To.Parsetree.structure val copy_signature : From.Parsetree.signature -> To.Parsetree.signature val copy_toplevel_phrase : From.Parsetree.toplevel_phrase -> To.Parsetree.toplevel_phrase val copy_core_type : From.Parsetree.core_type -> To.Parsetree.core_type val copy_expression : From.Parsetree.expression -> To.Parsetree.expression val copy_pattern : From.Parsetree.pattern -> To.Parsetree.pattern val copy_case : From.Parsetree.case -> To.Parsetree.case val copy_type_declaration : From.Parsetree.type_declaration -> To.Parsetree.type_declaration val copy_type_extension : From.Parsetree.type_extension -> To.Parsetree.type_extension val copy_extension_constructor: From.Parsetree.extension_constructor -> To.Parsetree.extension_constructor val copy_class_expr : From.Parsetree.class_expr -> To.Parsetree.class_expr val copy_class_field : From.Parsetree.class_field -> To.Parsetree.class_field val copy_class_type : From.Parsetree.class_type -> To.Parsetree.class_type val copy_class_signature : From.Parsetree.class_signature -> To.Parsetree.class_signature val copy_class_type_field : From.Parsetree.class_type_field -> To.Parsetree.class_type_field val copy_module_expr : From.Parsetree.module_expr -> To.Parsetree.module_expr val copy_module_type : From.Parsetree.module_type -> To.Parsetree.module_type val copy_signature_item : From.Parsetree.signature_item -> To.Parsetree.signature_item val copy_structure_item : From.Parsetree.structure_item -> To.Parsetree.structure_item (*$*) end module Migration_functions (A : OCaml_version) (B : OCaml_version) (A_to_B : Migrate_module with module From = A.Ast and module To = B.Ast) = struct let migration_functions : (A.types, B.types) migration_functions = let open A_to_B in { (*$ foreach_type (fun _ s -> printf " copy_%s;\n" s) *) copy_structure; copy_signature; copy_toplevel_phrase; copy_core_type; copy_expression; copy_pattern; copy_case; copy_type_declaration; copy_type_extension; copy_extension_constructor; copy_class_expr; copy_class_field; copy_class_type; copy_class_signature; copy_class_type_field; copy_module_expr; copy_module_type; copy_signature_item; copy_structure_item; (*$*) } end module Register_migration (A : OCaml_version) (B : OCaml_version) (A_to_B : Migrate_module with module From = A.Ast and module To = B.Ast) (B_to_A : Migrate_module with module From = B.Ast and module To = A.Ast) = struct let () = ( let is_undefined : type a. a migration -> bool = function | Undefined -> true | _ -> false in assert (A.version < B.version); assert (is_undefined A.migration_info.next_version); assert (is_undefined B.migration_info.previous_version); let module A_to_B_fun = Migration_functions(A)(B)(A_to_B) in let module B_to_A_fun = Migration_functions(B)(A)(B_to_A) in A.migration_info.next_version <- Migration ((module A), A_to_B_fun.migration_functions, (module B)); B.migration_info.previous_version <- Migration ((module B), B_to_A_fun.migration_functions, (module A)); ) end type 'from immediate_migration = | No_migration : 'from immediate_migration | Immediate_migration : ('from, 'to_) migration_functions * 'to_ ocaml_version -> 'from immediate_migration let immediate_migration (*$ foreach_type (fun _ s -> printf " (type %s)\n" s) *) (type structure) (type signature) (type toplevel_phrase) (type core_type) (type expression) (type pattern) (type case) (type type_declaration) (type type_extension) (type extension_constructor) (type class_expr) (type class_field) (type class_type) (type class_signature) (type class_type_field) (type module_expr) (type module_type) (type signature_item) (type structure_item) (*$*) ((module A) : < (*$ foreach_type (fun _ s -> printf " %-21s : %s;\n" s s) *) structure : structure; signature : signature; toplevel_phrase : toplevel_phrase; core_type : core_type; expression : expression; pattern : pattern; case : case; type_declaration : type_declaration; type_extension : type_extension; extension_constructor : extension_constructor; class_expr : class_expr; class_field : class_field; class_type : class_type; class_signature : class_signature; class_type_field : class_type_field; module_expr : module_expr; module_type : module_type; signature_item : signature_item; structure_item : structure_item; (*$*) > ocaml_version) direction = let version = match direction with | `Next -> A.migration_info.next_version | `Previous -> A.migration_info.previous_version in match version with | Undefined -> No_migration | Migration (_, funs, to_) -> Immediate_migration (funs, to_) | _ -> assert false let migrate (*$ foreach_type (fun _ s -> printf " (type %s1) (type %s2)\n" s s) *) (type structure1) (type structure2) (type signature1) (type signature2) (type toplevel_phrase1) (type toplevel_phrase2) (type core_type1) (type core_type2) (type expression1) (type expression2) (type pattern1) (type pattern2) (type case1) (type case2) (type type_declaration1) (type type_declaration2) (type type_extension1) (type type_extension2) (type extension_constructor1) (type extension_constructor2) (type class_expr1) (type class_expr2) (type class_field1) (type class_field2) (type class_type1) (type class_type2) (type class_signature1) (type class_signature2) (type class_type_field1) (type class_type_field2) (type module_expr1) (type module_expr2) (type module_type1) (type module_type2) (type signature_item1) (type signature_item2) (type structure_item1) (type structure_item2) (*$*) ((module A) : < (*$ foreach_type (fun _ s -> printf " %-21s : %s1;\n" s s) *) structure : structure1; signature : signature1; toplevel_phrase : toplevel_phrase1; core_type : core_type1; expression : expression1; pattern : pattern1; case : case1; type_declaration : type_declaration1; type_extension : type_extension1; extension_constructor : extension_constructor1; class_expr : class_expr1; class_field : class_field1; class_type : class_type1; class_signature : class_signature1; class_type_field : class_type_field1; module_expr : module_expr1; module_type : module_type1; signature_item : signature_item1; structure_item : structure_item1; (*$*) > ocaml_version) ((module B) : < (*$ foreach_type (fun _ s -> printf " %-21s : %s2;\n" s s) *) structure : structure2; signature : signature2; toplevel_phrase : toplevel_phrase2; core_type : core_type2; expression : expression2; pattern : pattern2; case : case2; type_declaration : type_declaration2; type_extension : type_extension2; extension_constructor : extension_constructor2; class_expr : class_expr2; class_field : class_field2; class_type : class_type2; class_signature : class_signature2; class_type_field : class_type_field2; module_expr : module_expr2; module_type : module_type2; signature_item : signature_item2; structure_item : structure_item2; (*$*) > ocaml_version) : (A.types, B.types) migration_functions = match A.Version with | B.Version -> migration_identity | _ -> let direction = if A.version < B.version then `Next else `Previous in let rec migrate (m : A.types immediate_migration) : (A.types, B.types) migration_functions = match m with | No_migration -> assert false | Immediate_migration (f, (module To)) -> match To.Version with | B.Version -> f | _ -> match immediate_migration (module To) direction with | No_migration -> assert false | Immediate_migration (g, to2) -> migrate (Immediate_migration (migration_compose f g, to2)) in migrate (immediate_migration (module A) direction) module Convert (A : OCaml_version) (B : OCaml_version) = struct let { (*$ foreach_type (fun _ s -> printf " copy_%s;\n" s) *) copy_structure; copy_signature; copy_toplevel_phrase; copy_core_type; copy_expression; copy_pattern; copy_case; copy_type_declaration; copy_type_extension; copy_extension_constructor; copy_class_expr; copy_class_field; copy_class_type; copy_class_signature; copy_class_type_field; copy_module_expr; copy_module_type; copy_signature_item; copy_structure_item; (*$*) } : (A.types, B.types) migration_functions = migrate (module A) (module B) end (*$ foreach_version (fun n version -> printf "module OCaml_%d = struct\n" n; printf " module Ast = Astlib.Ast_%d\n" n; printf " include Make_witness(Astlib.Ast_%d)\n" n; printf " let version = %d\n" n; printf " let string_version = %S\n" version; printf "end\n"; printf "let ocaml_%d : OCaml_%d.types ocaml_version = (module OCaml_%d)\n" n n n; ) *) module OCaml_408 = struct module Ast = Astlib.Ast_408 include Make_witness(Astlib.Ast_408) let version = 408 let string_version = "4.08" end let ocaml_408 : OCaml_408.types ocaml_version = (module OCaml_408) module OCaml_409 = struct module Ast = Astlib.Ast_409 include Make_witness(Astlib.Ast_409) let version = 409 let string_version = "4.09" end let ocaml_409 : OCaml_409.types ocaml_version = (module OCaml_409) module OCaml_410 = struct module Ast = Astlib.Ast_410 include Make_witness(Astlib.Ast_410) let version = 410 let string_version = "4.10" end let ocaml_410 : OCaml_410.types ocaml_version = (module OCaml_410) module OCaml_411 = struct module Ast = Astlib.Ast_411 include Make_witness(Astlib.Ast_411) let version = 411 let string_version = "4.11" end let ocaml_411 : OCaml_411.types ocaml_version = (module OCaml_411) module OCaml_412 = struct module Ast = Astlib.Ast_412 include Make_witness(Astlib.Ast_412) let version = 412 let string_version = "4.12" end let ocaml_412 : OCaml_412.types ocaml_version = (module OCaml_412) module OCaml_413 = struct module Ast = Astlib.Ast_413 include Make_witness(Astlib.Ast_413) let version = 413 let string_version = "4.13" end let ocaml_413 : OCaml_413.types ocaml_version = (module OCaml_413) module OCaml_414 = struct module Ast = Astlib.Ast_414 include Make_witness(Astlib.Ast_414) let version = 414 let string_version = "4.14" end let ocaml_414 : OCaml_414.types ocaml_version = (module OCaml_414) module OCaml_500 = struct module Ast = Astlib.Ast_500 include Make_witness(Astlib.Ast_500) let version = 500 let string_version = "5.0" end let ocaml_500 : OCaml_500.types ocaml_version = (module OCaml_500) module OCaml_501 = struct module Ast = Astlib.Ast_501 include Make_witness(Astlib.Ast_501) let version = 501 let string_version = "5.1" end let ocaml_501 : OCaml_501.types ocaml_version = (module OCaml_501) module OCaml_502 = struct module Ast = Astlib.Ast_502 include Make_witness(Astlib.Ast_502) let version = 502 let string_version = "5.2" end let ocaml_502 : OCaml_502.types ocaml_version = (module OCaml_502) module OCaml_503 = struct module Ast = Astlib.Ast_503 include Make_witness(Astlib.Ast_503) let version = 503 let string_version = "5.3" end let ocaml_503 : OCaml_503.types ocaml_version = (module OCaml_503) module OCaml_504 = struct module Ast = Astlib.Ast_504 include Make_witness(Astlib.Ast_504) let version = 504 let string_version = "5.4" end let ocaml_504 : OCaml_504.types ocaml_version = (module OCaml_504) (*$*) let all_versions : (module OCaml_version) list = [ (*$foreach_version (fun n _ -> printf "(module OCaml_%d : OCaml_version);\n" n)*) (module OCaml_408 : OCaml_version); (module OCaml_409 : OCaml_version); (module OCaml_410 : OCaml_version); (module OCaml_411 : OCaml_version); (module OCaml_412 : OCaml_version); (module OCaml_413 : OCaml_version); (module OCaml_414 : OCaml_version); (module OCaml_500 : OCaml_version); (module OCaml_501 : OCaml_version); (module OCaml_502 : OCaml_version); (module OCaml_503 : OCaml_version); (module OCaml_504 : OCaml_version); (*$*) ] (*$foreach_version_pair (fun a b -> printf "include Register_migration(OCaml_%d)(OCaml_%d)\n" a b; printf " (Astlib.Migrate_%d_%d)(Astlib.Migrate_%d_%d)\n" a b b a ) *) include Register_migration(OCaml_408)(OCaml_409) (Astlib.Migrate_408_409)(Astlib.Migrate_409_408) include Register_migration(OCaml_409)(OCaml_410) (Astlib.Migrate_409_410)(Astlib.Migrate_410_409) include Register_migration(OCaml_410)(OCaml_411) (Astlib.Migrate_410_411)(Astlib.Migrate_411_410) include Register_migration(OCaml_411)(OCaml_412) (Astlib.Migrate_411_412)(Astlib.Migrate_412_411) include Register_migration(OCaml_412)(OCaml_413) (Astlib.Migrate_412_413)(Astlib.Migrate_413_412) include Register_migration(OCaml_413)(OCaml_414) (Astlib.Migrate_413_414)(Astlib.Migrate_414_413) include Register_migration(OCaml_414)(OCaml_500) (Astlib.Migrate_414_500)(Astlib.Migrate_500_414) include Register_migration(OCaml_500)(OCaml_501) (Astlib.Migrate_500_501)(Astlib.Migrate_501_500) include Register_migration(OCaml_501)(OCaml_502) (Astlib.Migrate_501_502)(Astlib.Migrate_502_501) include Register_migration(OCaml_502)(OCaml_503) (Astlib.Migrate_502_503)(Astlib.Migrate_503_502) include Register_migration(OCaml_503)(OCaml_504) (Astlib.Migrate_503_504)(Astlib.Migrate_504_503) (*$*) module OCaml_current = OCaml_OCAML_VERSION module Find_version = struct type t = Impl of (module OCaml_version) | Intf of (module OCaml_version) | Unknown let from_magic magic = let rec loop = function | [] -> Unknown | (module Version : OCaml_version) :: tail -> if Version.Ast.Config.ast_impl_magic_number = magic then Impl (module Version) else if Version.Ast.Config.ast_intf_magic_number = magic then Intf (module Version) else loop tail in (* Traverse the versions from last to first: if the magic numbers aren't unique among versions, we want the latest version with a magic number match. The situation in mind is trunk support. *) let all_versions_top_down = List.rev all_versions in loop all_versions_top_down end