diff --git a/CHANGES.md b/CHANGES.md index 551d12f499..71b09b3710 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,6 @@ # Unreleased +- Support for OCaml 5.6 (@jonludlam, #1472) - Support for OxCaml unboxed named types (@art-w, #1407) - Support for OxCaml zero alloc definitions (@Leonidas-from-XIV, #1422, #1444) - Remove requirement for ppx_expect in tests (@jonludlam, #1445) diff --git a/odoc.opam b/odoc.opam index 21565bd8b4..6339ffe05c 100644 --- a/odoc.opam +++ b/odoc.opam @@ -45,7 +45,7 @@ depends: [ "cppo" {build & >= "1.1.0"} "dune" {>= "3.21.0"} "fpath" {>= "0.7.3"} - "ocaml" {>= "4.08.0" & < "5.6"} + "ocaml" {>= "4.08.0" & < "5.7"} "tyxml" {>= "4.4.0"} "fmt" "crunch" {>= "1.4.1"} diff --git a/src/loader/cmi.ml b/src/loader/cmi.ml index bc30e26666..3efe11f192 100644 --- a/src/loader/cmi.ml +++ b/src/loader/cmi.ml @@ -46,7 +46,18 @@ module Compat = struct versions *) let repr x = Transient_expr.repr x +#if OCAML_VERSION >= (5,6,0) + (** Since 5.6, abbreviation expansion happens in place in the type graph and + the original abbreviation is remembered in the [Texpand] edge that + replaced it. [Types.get_desc] walks past those edges and would hand us + the expanded type, so use the same accessor the compiler's own printer + uses ([Out_type.printer_get_desc]) to recover the abbreviation. *) + let get_desc ty = Btype.get_folded_desc ~keep_Tvar:true ty + let deep_occur = Btype.deep_occur +#else let get_desc = Types.get_desc + let deep_occur = Ctype.deep_occur +#endif let get_row_name = Types.row_name let row_field_repr = Types.row_field_repr let field_kind_repr = Types.field_kind_repr @@ -69,6 +80,7 @@ module Compat = struct type repr_type_node = Types.type_expr let repr = Btype.repr let get_desc x = (repr x).Types.desc + let deep_occur = Ctype.deep_occur let get_row_name x = x.Types.row_name let row_field_repr = Btype.row_field_repr let field_kind_repr = Btype.field_kind_repr @@ -313,6 +325,10 @@ let mark_type ty = | Tsubst (ty,_) -> loop visited ty #endif | Tlink _ -> assert false +#if OCAML_VERSION >= (5,6,0) + (* [Compat.get_desc] walks past [Texpand] just as it does [Tlink]. *) + | Texpand _ -> assert false +#endif #if defined OXCAML | Tquote typ -> loop visited typ | Tsplice typ -> loop visited typ @@ -459,7 +475,7 @@ let rec mark_class_type params = function let sty = Compat.self_type cty in if is_row_visited (proxy sty) || List.exists aliasable params - || List.exists (Ctype.deep_occur sty) tyl + || List.exists (Compat.deep_occur sty) tyl then mark_class_type params cty else List.iter mark_type tyl | Cty_signature sign -> @@ -663,6 +679,10 @@ let rec read_type_expr env typ = #endif | Tlink _ -> assert false +#if OCAML_VERSION >= (5,6,0) + (* [Compat.get_desc] walks past [Texpand] just as it does [Tlink]. *) + | Texpand _ -> assert false +#endif #if defined OXCAML | Tquote typ -> Quote (read_type_expr env typ) | Tsplice typ -> Splice (read_type_expr env typ) diff --git a/src/loader/cmt.ml b/src/loader/cmt.ml index d00b5d3e97..5ae29ac6bd 100644 --- a/src/loader/cmt.ml +++ b/src/loader/cmt.ml @@ -524,8 +524,13 @@ and read_structure_item env parent item = | Tstr_eval _ -> [] | Tstr_value(_, vbs) -> read_value_bindings env parent vbs +#if OCAML_VERSION >= (5,6,0) + | Tstr_primitive pd -> + [Cmti.read_primitive_description env parent pd] +#else | Tstr_primitive vd -> [Cmti.read_value_description env parent vd] +#endif | Tstr_type (rec_flag, decls) -> let rec_flag = match rec_flag with diff --git a/src/loader/cmti.ml b/src/loader/cmti.ml index 10fda71abc..5025a282a9 100644 --- a/src/loader/cmti.ml +++ b/src/loader/cmti.ml @@ -200,14 +200,64 @@ let read_value_description env parent vd = let doc = Doc_attr.attached_no_tag ~warnings_tag:env.warnings_tag container vd.val_attributes in let type_ = read_core_type env container vd.val_desc in let value = +#if OCAML_VERSION >= (5,6,0) + (* Since 5.6 externals have their own node, so a [val] is never one. *) + Value.Abstract +#else match vd.val_prim with | [] -> Value.Abstract | primitives -> External primitives +#endif in let ext_attrs = Doc_attr.attrs_of_value_description vd.val_val in let modalities = Cmi.read_value_descr_modalities vd.val_val in Value { Value.id; source_loc; doc; type_; value; ext_attrs; modalities } +#if OCAML_VERSION >= (5,6,0) +(* Since 5.6 (RFC 44) [external] declarations have their own Typedtree node, + because they can now also be written as an alias of an existing primitive: + [external x : t = M.y], or [external x = M.y] with the type left implicit. + An alias carries no primitive strings of its own, so recover them from the + resolved [Types.value_description] the way the .cmi reader does. *) +let read_primitive_description env parent pd = + let open Signature in + let id = Env.find_value_identifier env.ident_env pd.prim_id in + let source_loc = None in + let container = + (parent : Identifier.Signature.t :> Identifier.LabelParent.t) + in + let doc = + Doc_attr.attached_no_tag ~warnings_tag:env.warnings_tag container + pd.prim_attributes + in + let type_ = + match pd.prim_kind with + | Tprim_decl (core_type, _) | Tprim_alias (Some core_type, _, _) -> + read_core_type env container core_type + | Tprim_alias (None, _, _) -> + (* No type written at the declaration site: use the inferred one. *) + Cmi.mark_type_expr pd.prim_val.val_type; + Cmi.read_type_expr env pd.prim_val.val_type + in + let value = + match pd.prim_kind with + | Tprim_decl (_, primitives) -> Value.External primitives + | Tprim_alias _ -> ( + match pd.prim_val.val_kind with + | Val_prim desc -> + let open Primitive in + Value.External + (desc.prim_name + :: (match desc.prim_native_name with + | "" -> [] + | name -> [ name ])) + | _ -> Value.Abstract) + in + let ext_attrs = Doc_attr.attrs_of_value_description pd.prim_val in + let modalities = Cmi.read_value_descr_modalities pd.prim_val in + Value { Value.id; source_loc; doc; type_; value; ext_attrs; modalities } +#endif + let read_type_parameter (ctyp, var_and_injectivity) = let open TypeDecl in let desc, kind = @@ -804,6 +854,10 @@ and read_signature_item env parent item = match item.sig_desc with | Tsig_value vd -> [read_value_description env parent vd] +#if OCAML_VERSION >= (5,6,0) + | Tsig_primitive pd -> + [read_primitive_description env parent pd] +#endif | Tsig_type (rec_flag, decls) -> let rec_flag = match rec_flag with diff --git a/src/loader/cmti.mli b/src/loader/cmti.mli index d2e5905a2d..bb46122320 100644 --- a/src/loader/cmti.mli +++ b/src/loader/cmti.mli @@ -49,6 +49,14 @@ val read_value_description : Typedtree.value_description -> Odoc_model.Lang.Signature.item +#if OCAML_VERSION >= (5,6,0) +val read_primitive_description : + Cmi.env -> + Paths.Identifier.Signature.t -> + Typedtree.primitive_description -> + Odoc_model.Lang.Signature.item +#endif + val read_type_declarations : Cmi.env -> Paths.Identifier.Signature.t -> diff --git a/src/loader/ident_env.ml b/src/loader/ident_env.ml index 3dd81576c8..2e85e7b3a2 100644 --- a/src/loader/ident_env.ml +++ b/src/loader/ident_env.ml @@ -253,7 +253,11 @@ let rec extract_signature_tree_items : bool -> Typedtree.signature_item list -> mds @ extract_signature_tree_items hide_item rest #endif | { sig_desc = Tsig_value {val_id; _}; sig_loc; _ } :: rest-> - [`Value (val_id, hide_item, Some sig_loc)] @ extract_signature_tree_items hide_item rest + [`Value (val_id, hide_item, Some sig_loc)] @ extract_signature_tree_items hide_item rest +#if OCAML_VERSION >= (5,6,0) + | { sig_desc = Tsig_primitive {prim_id; _}; sig_loc; _ } :: rest-> + [`Value (prim_id, hide_item, Some sig_loc)] @ extract_signature_tree_items hide_item rest +#endif | { sig_desc = Tsig_modtype mtd; sig_loc; _} :: rest -> [`ModuleType (mtd.mtd_id, hide_item, Some sig_loc)] @ extract_signature_tree_items hide_item rest #if defined OXCAML @@ -450,8 +454,13 @@ let rec extract_structure_tree_items : bool -> Typedtree.structure_item list -> )) cltyps @ extract_structure_tree_items hide_item rest | { str_desc = Tstr_open o; _ } :: rest -> ((extract_extended_open o) :> items list) @ extract_structure_tree_items hide_item rest +#if OCAML_VERSION >= (5,6,0) + | { str_desc = Tstr_primitive {prim_id; _}; str_loc; _ } :: rest -> + [`Value (prim_id, false, Some str_loc)] @ extract_structure_tree_items hide_item rest +#else | { str_desc = Tstr_primitive {val_id; _}; str_loc; _ } :: rest -> [`Value (val_id, false, Some str_loc)] @ extract_structure_tree_items hide_item rest +#endif | { str_desc = Tstr_eval _; _} :: rest -> extract_structure_tree_items hide_item rest #if defined OXCAML | { str_desc = Tstr_jkind _; _ } :: rest -> extract_structure_tree_items hide_item rest diff --git a/src/loader/implementation.ml b/src/loader/implementation.ml index 792f1fb35c..d9c6b3d459 100644 --- a/src/loader/implementation.ml +++ b/src/loader/implementation.ml @@ -38,6 +38,9 @@ module Env = struct | Tsig_exception _ | Tsig_modsubst _ | Tsig_open _ | Tsig_include _ | Tsig_class _ | Tsig_class_type _ | Tsig_attribute _ -> () +#if OCAML_VERSION >= (5,6,0) + | Tsig_primitive _ -> () +#endif #if defined OXCAML | Tsig_jkind _ -> () #endif diff --git a/src/model/compat.cppo.ml b/src/model/compat.cppo.ml index f9aef91fbf..3c60bc4e70 100644 --- a/src/model/compat.cppo.ml +++ b/src/model/compat.cppo.ml @@ -210,6 +210,9 @@ let shape_info_of_cmt_infos : Cmt_format.cmt_infos -> (shape * uid_to_loc) optio let open Typedtree in function | Value v -> v.val_loc +#if OCAML_VERSION >= (5,6,0) + | Primitive p -> p.prim_loc +#endif | Value_binding vb -> vb.vb_pat.pat_loc | Type t -> t.typ_loc | Constructor c -> c.cd_loc diff --git a/test/generators/cases/ocaml_56.mli b/test/generators/cases/ocaml_56.mli new file mode 100644 index 0000000000..b121f5d07a --- /dev/null +++ b/test/generators/cases/ocaml_56.mli @@ -0,0 +1,13 @@ +(** Features introduced in OCaml 5.6. *) + +type ext = external "gmp" +(** An external type declaration. *) + +external classic : int -> int = "caml_classic" "caml_classic_native" +(** A classic external declaration. *) + +external alias_with_type : int -> int = classic +(** A primitive alias declaring its type. *) + +external alias_no_type = classic +(** A primitive alias leaving its type implicit. *) diff --git a/test/generators/gen_rules/gen_rules.ml b/test/generators/gen_rules/gen_rules.ml index a1be5a3e9f..456b527e58 100644 --- a/test/generators/gen_rules/gen_rules.ml +++ b/test/generators/gen_rules/gen_rules.ml @@ -68,6 +68,7 @@ let constraints = ("class_comments.mli", Min "4.08"); ("functor_ml.ml", Min "4.14"); ("ocaml_55.mli", Min "5.5"); + ("ocaml_56.mli", Min "5.6"); ("oxcaml.mli", OxCaml); ("oxcaml_impl.ml", OxCaml); ] diff --git a/test/generators/html/Ocaml_56.html b/test/generators/html/Ocaml_56.html new file mode 100644 index 0000000000..3e7c98d3c7 --- /dev/null +++ b/test/generators/html/Ocaml_56.html @@ -0,0 +1,60 @@ + + + Ocaml_56 (Ocaml_56) + + + + + + + + +
+

Module Ocaml_56

+

Features introduced in OCaml 5.6.

+
+
+
+
+ + type ext +

An external type declaration.

+
+
+
+ + + val classic : + int -> int + + +

A classic external declaration.

+
+
+
+ + + val alias_with_type : + int -> int + + +
+

A primitive alias declaring its type.

+
+
+
+ + + val alias_no_type : + int -> int + + +
+

A primitive alias leaving its type implicit.

+
+
+
+ + diff --git a/test/generators/html/ocaml_56.targets b/test/generators/html/ocaml_56.targets new file mode 100644 index 0000000000..cb13c18ad1 --- /dev/null +++ b/test/generators/html/ocaml_56.targets @@ -0,0 +1 @@ +Ocaml_56.html diff --git a/test/generators/latex/Ocaml_56.tex b/test/generators/latex/Ocaml_56.tex new file mode 100644 index 0000000000..84c2f7b629 --- /dev/null +++ b/test/generators/latex/Ocaml_56.tex @@ -0,0 +1,13 @@ +\section{Module \ocamlinlinecode{Ocaml\_\allowbreak{}56}}\label{Ocaml_56}% +Features introduced in OCaml 5.6. + +\label{Ocaml_56--type-ext}\ocamlcodefragment{\ocamltag{keyword}{type} ext}\begin{ocamlindent}An external type declaration.\end{ocamlindent}% +\medbreak +\label{Ocaml_56--val-classic}\ocamlcodefragment{\ocamltag{keyword}{val} classic : int \ocamltag{arrow}{$\rightarrow$} int}\begin{ocamlindent}A classic external declaration.\end{ocamlindent}% +\medbreak +\label{Ocaml_56--val-alias_with_type}\ocamlcodefragment{\ocamltag{keyword}{val} alias\_\allowbreak{}with\_\allowbreak{}type : int \ocamltag{arrow}{$\rightarrow$} int}\begin{ocamlindent}A primitive alias declaring its type.\end{ocamlindent}% +\medbreak +\label{Ocaml_56--val-alias_no_type}\ocamlcodefragment{\ocamltag{keyword}{val} alias\_\allowbreak{}no\_\allowbreak{}type : int \ocamltag{arrow}{$\rightarrow$} int}\begin{ocamlindent}A primitive alias leaving its type implicit.\end{ocamlindent}% +\medbreak + + diff --git a/test/generators/latex/ocaml_56.targets b/test/generators/latex/ocaml_56.targets new file mode 100644 index 0000000000..56d4e55fec --- /dev/null +++ b/test/generators/latex/ocaml_56.targets @@ -0,0 +1 @@ +Ocaml_56.tex diff --git a/test/generators/link.dune.inc b/test/generators/link.dune.inc index 5efe5a1677..73a21c326a 100644 --- a/test/generators/link.dune.inc +++ b/test/generators/link.dune.inc @@ -566,6 +566,30 @@ (enabled_if (>= %{ocaml_version} 5.5))) +(rule + (target ocaml_56.cmti) + (package odoc) + (action + (run ocamlc -c -bin-annot -o %{target} %{dep:cases/ocaml_56.mli})) + (enabled_if + (>= %{ocaml_version} 5.6))) + +(rule + (target ocaml_56.odoc) + (package odoc) + (action + (run odoc compile -o %{target} %{dep:ocaml_56.cmti})) + (enabled_if + (>= %{ocaml_version} 5.6))) + +(rule + (target ocaml_56.odocl) + (package odoc) + (action + (run odoc link -o %{target} %{dep:ocaml_56.odoc})) + (enabled_if + (>= %{ocaml_version} 5.6))) + (rule (target ocamlary.cmti) (package odoc) @@ -7625,6 +7649,166 @@ (enabled_if (>= %{ocaml_version} 5.5)))) +(subdir + html + (rule + (targets Ocaml_56.html.gen) + (package odoc) + (action + (run + odoc + html-generate + --indent + --flat + --extra-suffix + gen + -o + . + %{dep:../ocaml_56.odocl})) + (enabled_if + (>= %{ocaml_version} 5.6))) + (rule + (alias runtest) + (package odoc) + (action + (diff Ocaml_56.html Ocaml_56.html.gen)) + (enabled_if + (>= %{ocaml_version} 5.6)))) + +(subdir + html + (rule + (target ocaml_56.targets.gen) + (package odoc) + (action + (with-outputs-to + ocaml_56.targets.gen + (run odoc html-targets -o . %{dep:../ocaml_56.odocl} --flat))) + (enabled_if + (>= %{ocaml_version} 5.6))) + (rule + (alias runtest) + (package odoc) + (action + (diff ocaml_56.targets ocaml_56.targets.gen)) + (enabled_if + (>= %{ocaml_version} 5.6)))) + +(subdir + latex + (rule + (targets Ocaml_56.tex.gen) + (package odoc) + (action + (run odoc latex-generate -o . --extra-suffix gen %{dep:../ocaml_56.odocl})) + (enabled_if + (>= %{ocaml_version} 5.6))) + (rule + (alias runtest) + (package odoc) + (action + (diff Ocaml_56.tex Ocaml_56.tex.gen)) + (enabled_if + (>= %{ocaml_version} 5.6)))) + +(subdir + latex + (rule + (target ocaml_56.targets.gen) + (package odoc) + (action + (with-outputs-to + ocaml_56.targets.gen + (run odoc latex-targets -o . %{dep:../ocaml_56.odocl}))) + (enabled_if + (>= %{ocaml_version} 5.6))) + (rule + (alias runtest) + (package odoc) + (action + (diff ocaml_56.targets ocaml_56.targets.gen)) + (enabled_if + (>= %{ocaml_version} 5.6)))) + +(subdir + man + (rule + (targets Ocaml_56.3o.gen) + (package odoc) + (action + (run odoc man-generate -o . --extra-suffix gen %{dep:../ocaml_56.odocl})) + (enabled_if + (>= %{ocaml_version} 5.6))) + (rule + (alias runtest) + (package odoc) + (action + (diff Ocaml_56.3o Ocaml_56.3o.gen)) + (enabled_if + (>= %{ocaml_version} 5.6)))) + +(subdir + man + (rule + (target ocaml_56.targets.gen) + (package odoc) + (action + (with-outputs-to + ocaml_56.targets.gen + (run odoc man-targets -o . %{dep:../ocaml_56.odocl}))) + (enabled_if + (>= %{ocaml_version} 5.6))) + (rule + (alias runtest) + (package odoc) + (action + (diff ocaml_56.targets ocaml_56.targets.gen)) + (enabled_if + (>= %{ocaml_version} 5.6)))) + +(subdir + markdown + (rule + (targets Ocaml_56.md.gen) + (package odoc) + (action + (run + odoc + markdown-generate + -o + . + --extra-suffix + gen + %{dep:../ocaml_56.odocl})) + (enabled_if + (>= %{ocaml_version} 5.6))) + (rule + (alias runtest) + (package odoc) + (action + (diff Ocaml_56.md Ocaml_56.md.gen)) + (enabled_if + (>= %{ocaml_version} 5.6)))) + +(subdir + markdown + (rule + (target ocaml_56.targets.gen) + (package odoc) + (action + (with-outputs-to + ocaml_56.targets.gen + (run odoc markdown-targets -o . %{dep:../ocaml_56.odocl}))) + (enabled_if + (>= %{ocaml_version} 5.6))) + (rule + (alias runtest) + (package odoc) + (action + (diff ocaml_56.targets ocaml_56.targets.gen)) + (enabled_if + (>= %{ocaml_version} 5.6)))) + (subdir html (rule diff --git a/test/generators/man/Ocaml_56.3o b/test/generators/man/Ocaml_56.3o new file mode 100644 index 0000000000..97bbee8c93 --- /dev/null +++ b/test/generators/man/Ocaml_56.3o @@ -0,0 +1,44 @@ + +.TH Ocaml_56 3 "" "Odoc" "OCaml Library" +.SH Name +Ocaml_56 +.SH Synopsis +.sp +.in 2 +\fBModule Ocaml_56\fR +.in +.sp +.fi +Features introduced in OCaml 5\.6\. +.nf +.SH Documentation +.sp +.nf +\f[CB]type\fR ext +.fi +.br +.ti +2 +An external type declaration\. +.nf +.sp +\f[CB]val\fR classic : int \f[CB]\->\fR int +.fi +.br +.ti +2 +A classic external declaration\. +.nf +.sp +\f[CB]val\fR alias_with_type : int \f[CB]\->\fR int +.fi +.br +.ti +2 +A primitive alias declaring its type\. +.nf +.sp +\f[CB]val\fR alias_no_type : int \f[CB]\->\fR int +.fi +.br +.ti +2 +A primitive alias leaving its type implicit\. +.nf + diff --git a/test/generators/man/ocaml_56.targets b/test/generators/man/ocaml_56.targets new file mode 100644 index 0000000000..54238541b7 --- /dev/null +++ b/test/generators/man/ocaml_56.targets @@ -0,0 +1 @@ +Ocaml_56.3o diff --git a/test/generators/markdown/Ocaml_56.md b/test/generators/markdown/Ocaml_56.md new file mode 100644 index 0000000000..dfa31a8162 --- /dev/null +++ b/test/generators/markdown/Ocaml_56.md @@ -0,0 +1,24 @@ + +# Module `Ocaml_56` + +Features introduced in OCaml 5\.6. + +```ocaml +type ext +``` +An external type declaration. + +```ocaml +val classic : int -> int +``` +A classic external declaration. + +```ocaml +val alias_with_type : int -> int +``` +A primitive alias declaring its type. + +```ocaml +val alias_no_type : int -> int +``` +A primitive alias leaving its type implicit. diff --git a/test/generators/markdown/ocaml_56.targets b/test/generators/markdown/ocaml_56.targets new file mode 100644 index 0000000000..ba6a8e30f4 --- /dev/null +++ b/test/generators/markdown/ocaml_56.targets @@ -0,0 +1 @@ +Ocaml_56.md diff --git a/test/xref2/lib/common.cppo.ml b/test/xref2/lib/common.cppo.ml index bc41ab9f0d..4bc88ca4db 100644 --- a/test/xref2/lib/common.cppo.ml +++ b/test/xref2/lib/common.cppo.ml @@ -34,6 +34,8 @@ let cmti_of_string s = #elif defined OXCAML ~sourcefile:"" dummy_compilation_unit +#elif OCAML_VERSION >= (5,6,0) + Unit_info.(make ~source_file:"" Intf "") #endif env p;;