diff --git a/CHANGES.md b/CHANGES.md index 7203c4eeac..a1e881c861 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,7 @@ - Remove requirement for ppx_expect in tests (@jonludlam, #1445) - Support for OxCaml modalities (@art-w, #1420) - Fix resolving functor through `module type of` (@Leonidas-from-XIV, #1471) +- Support for OxCaml modes (@art-w, #1454) # 3.2.1 diff --git a/sherlodoc/index/load_doc.ml b/sherlodoc/index/load_doc.ml index cf417947c7..af2a483889 100644 --- a/sherlodoc/index/load_doc.ml +++ b/sherlodoc/index/load_doc.ml @@ -82,8 +82,9 @@ let searchable_type_of_constructor args res = | TypeDecl.Constructor.Tuple args -> begin match args with | _ :: _ :: _ -> - TypeExpr.Arrow (None, Tuple (List.map (fun (x, _mods) -> None, x) args), res) - | [ (arg, _) ] -> TypeExpr.Arrow (None, arg, res) + TypeExpr.Arrow + (None, (Tuple (List.map (fun (x, _mods) -> None, x) args), []), (res, [])) + | [ (arg, _) ] -> TypeExpr.Arrow (None, (arg, []), (res, [])) | _ -> res end | TypeDecl.Constructor.Record fields -> @@ -91,12 +92,12 @@ let searchable_type_of_constructor args res = (fun res field -> let open TypeDecl.Field in let field_name = Odoc_model.Paths.Identifier.name field.id in - TypeExpr.Arrow (Some (Label field_name), field.type_, res)) + TypeExpr.Arrow (Some (Label field_name), (field.type_, []), (res, []))) res fields let searchable_type_of_record parent_type type_ = - Odoc_model.Lang.TypeExpr.Arrow (None, parent_type, type_) + Odoc_model.Lang.TypeExpr.Arrow (None, (parent_type, []), (type_, [])) let convert_kind ~db (Odoc_index.Entry.{ kind; _ } as entry) = match kind with diff --git a/sherlodoc/index/type_cache.ml b/sherlodoc/index/type_cache.ml index 42d9ed6c94..5a8843bb2c 100644 --- a/sherlodoc/index/type_cache.ml +++ b/sherlodoc/index/type_cache.ml @@ -16,7 +16,8 @@ let rec of_odoc ~cache otyp = match otyp with | Odoc_model.Lang.TypeExpr.Var _str -> Any | Any -> Any - | Arrow (_lbl, left, right) -> cache (Arrow (of_odoc ~cache left, of_odoc ~cache right)) + | Arrow (_lbl, (left, _), (right, _)) -> + cache (Arrow (of_odoc ~cache left, of_odoc ~cache right)) | Constr (name, args) -> cache (Constr (Typename.to_string name, List.map (of_odoc ~cache) args)) | Tuple li -> cache (Tuple (List.map (fun (_, ty) -> of_odoc ~cache ty) li)) diff --git a/src/document/generator.ml b/src/document/generator.ml index 0133714b0f..f8efd42ef8 100644 --- a/src/document/generator.ml +++ b/src/document/generator.ml @@ -325,6 +325,11 @@ module Make (Syntax : SYNTAX) = struct val with_kind_annotation : Odoc_model.Lang.Kind.t -> text -> text end = struct + let format_modes (modes : Odoc_model.Lang.Modes.t) = + match modes with + | [] -> O.noop + | ms -> O.txt " @ " ++ O.txt (String.concat ~sep:" " ms) + let rec te_variant (t : Odoc_model.Lang.TypeExpr.Polymorphic_variant.t) = let style_arguments ~constant arguments = (* Multiple arguments in a polymorphic variant constructor correspond @@ -494,35 +499,26 @@ module Make (Syntax : SYNTAX) = struct enclose_parens_if_needed (type_expr ~needs_parentheses:true te ++ O.txt " " ++ O.keyword "as" ++ O.txt " '" ++ O.txt alias) - | Arrow (None, src, dst) -> - let res = - O.span - ((O.box_hv @@ type_expr ~needs_parentheses:true src) - ++ O.txt " " ++ Syntax.Type.arrow) - ++ O.sp ++ type_expr dst - (* ++ O.end_hv *) - in - enclose_parens_if_needed res - | Arrow (Some (RawOptional _ as lbl), _src, dst) -> - let res = - O.span - (O.box_hv - @@ label lbl ++ O.txt ":" - ++ tag "error" (O.txt "???") - ++ O.txt " " ++ Syntax.Type.arrow) - ++ O.sp ++ type_expr dst + | Arrow (lbl, (src, src_modes), (dst, dst_modes)) -> + let src = + (O.box_hv @@ type_expr ~needs_parentheses:true src) + ++ format_modes src_modes in - enclose_parens_if_needed res - | Arrow (Some lbl, src, dst) -> - let res = - O.span - ((O.box_hv - @@ label lbl ++ O.txt ":" ++ O.cut - ++ (O.box_hv @@ type_expr ~needs_parentheses:true src)) - ++ O.txt " " ++ Syntax.Type.arrow) - ++ O.sp ++ type_expr dst + let labelized_src = + match lbl with + | None -> src + | Some (RawOptional _ as lbl) -> + O.box_hv + @@ label lbl ++ O.txt ":" + ++ tag "error" (O.txt "???") + ++ format_modes src_modes + | Some lbl -> O.box_hv @@ (label lbl ++ O.txt ":" ++ O.cut ++ src) in - enclose_parens_if_needed res + enclose_parens_if_needed + (O.span (labelized_src ++ O.txt " " ++ Syntax.Type.arrow) + ++ O.sp + ++ type_expr ~needs_parentheses:(dst_modes <> []) dst + ++ format_modes dst_modes) | Tuple lst -> tuple ~needs_parentheses ~boxed:true lst | Unboxed_tuple lst -> tuple ~needs_parentheses ~boxed:false lst | Constr (path, args) -> @@ -1053,7 +1049,7 @@ module Make (Syntax : SYNTAX) = struct val value : Lang.Value.t -> Item.t end = struct let rec arity_of_type_expr = function - | Lang.TypeExpr.Arrow (_lbl, _arg, curried) -> + | Lang.TypeExpr.Arrow (_lbl, _arg, (curried, _modes)) -> 1 + arity_of_type_expr curried | _ -> 0 diff --git a/src/loader/cmi.ml b/src/loader/cmi.ml index bc30e26666..2cb0bcb22b 100644 --- a/src/loader/cmi.ml +++ b/src/loader/cmi.ml @@ -548,6 +548,94 @@ let read_label_modalities ld = let read_constructor_argument arg = arg.ca_type, read_modalities Immutable arg.ca_modalities +let tree_of_modes (modes : Mode.Alloc.Const.t) : string list = + (* Same as the OxCaml's [tree_of_modes]: axes whose value is legacy + or is implied by another axis are suppressed. *) + let forkable = + match modes.areality, modes.forkable with + | Local, Unforkable | Global, Forkable -> None + | _, _ -> Some modes.forkable + in + let yielding = + match modes.areality, modes.yielding with + | Local, Yielding | Global, Unyielding -> None + | _, _ -> Some modes.yielding + in + let contention = + match modes.visibility, modes.contention with + | Immutable, Contended + | Read, Shared + | Read_write, Uncontended -> None + | _, _ -> Some modes.contention + in + let portability = + match modes.statefulness, modes.portability with + | Stateless, Portable + | Observing, Shareable + | Stateful, Nonportable -> None + | _, _ -> Some modes.portability + in + let { Mode.Alloc.areality; + linearity; + uniqueness; + portability = _; + contention = _; + forkable = _; + yielding = _; + statefulness; + visibility; + staticity + } = Mode.Alloc.Const.diff modes Mode.Alloc.Const.legacy + in + let print_opt print a = Option.map (Format_doc.asprintf "%a" print) a in + List.filter_map (fun x -> x) + [ print_opt Mode.Locality.Const.print areality + ; print_opt Mode.Uniqueness.Const.print uniqueness + ; print_opt Mode.Linearity.Const.print linearity + ; print_opt Mode.Portability.Const.print portability + ; print_opt Mode.Contention.Const.print contention + ; print_opt Mode.Forkable.Const.print forkable + ; print_opt Mode.Yielding.Const.print yielding + ; print_opt Mode.Statefulness.Const.print statefulness + ; print_opt Mode.Visibility.Const.print visibility + ; print_opt Mode.Staticity.Const.print staticity ] + +let read_alloc_modes m = tree_of_modes (Mode.Alloc.zap_to_legacy m) + +type modes = Mode.Alloc.Const.t + +let legacy_modes = Mode.Alloc.Const.legacy + +let curried_acc modes arg_mode = + Ctype.curry_mode modes (Mode.Alloc.zap_to_legacy arg_mode) + +let mode_is_implied implied_modes res_mode = + let snap = Btype.snapshot () in + let implied = + match Mode.Alloc.equate (Mode.Alloc.of_const implied_modes) res_mode with + | Ok () -> true + | Error _ -> false + in + Btype.backtrack snap; + implied + +let read_arrow_modes implied_modes typ = + match Compat.get_desc typ with + | Tarrow ((_, arg_mode, res_mode), _, res, _) -> + let arg_modes = read_alloc_modes arg_mode in + let implied_modes = curried_acc implied_modes arg_mode in + let res_is_arrow = + match Compat.get_desc res with + | Tarrow _ -> not (is_aliased (proxy res)) + | _ -> false + in + let res_modes = + if res_is_arrow && mode_is_implied implied_modes res_mode then [] + else read_alloc_modes res_mode + in + (arg_modes, res_modes, implied_modes) + | _ -> ([], [], implied_modes) + #else let jkind_of_type_desc _te = Kind.Default @@ -555,9 +643,17 @@ let read_value_descr_modalities _vd = [] let read_label_modalities _ld = [] let read_constructor_argument arg = arg, [] +type modes = unit + +let legacy_modes : modes = () + +let read_arrow_modes (implied_modes : modes) _typ = ([], [], implied_modes) + #endif -let rec read_type_expr env typ = +let rec read_type_expr env typ = read_type_expr_modal env legacy_modes typ + +and read_type_expr_modal env implied_modes typ = let open TypeExpr in let px = proxy typ in if used_alias px then Var (name_of_type typ) @@ -592,8 +688,9 @@ let rec read_type_expr env typ = | _ -> lbl, read_type_expr env arg in - let res = read_type_expr env res in - Arrow(lbl, arg, res) + let arg_modes, res_modes, modes = read_arrow_modes implied_modes typ in + let res = read_type_expr_modal env implied_modes res in + Arrow(lbl, (arg, arg_modes), (res, res_modes)) | Ttuple typs -> #if OCAML_VERSION >= (5,4,0) || defined OXCAML let typs = List.map (fun (lbl,x) -> lbl, read_type_expr env x) typs in diff --git a/src/loader/cmi.mli b/src/loader/cmi.mli index 76b5b5671b..a19f595fc1 100644 --- a/src/loader/cmi.mli +++ b/src/loader/cmi.mli @@ -108,8 +108,19 @@ val read_modalities : val read_value_modalities : Mode.Modality.t -> Odoc_model.Lang.Modalities.t + +val tree_of_modes : Mode.Alloc.Const.t -> Odoc_model.Lang.Modes.t #endif +type modes + +val legacy_modes : modes + +val read_arrow_modes : + modes -> + Types.type_expr -> + Odoc_model.Lang.Modes.t * Odoc_model.Lang.Modes.t * modes + val read_value_descr_modalities : Types.value_description -> Odoc_model.Lang.Modalities.t diff --git a/src/loader/cmt.ml b/src/loader/cmt.ml index d00b5d3e97..ae5ebdd82f 100644 --- a/src/loader/cmt.ml +++ b/src/loader/cmt.ml @@ -288,10 +288,13 @@ let rec read_class_field env parent cf = | Tcfk_concrete(_, expr) -> (* Types of concrete methods in class implementation begin with the object as first (implicit) argument, so we - must keep only the type after the first arrow. *) + must keep only the type after the first arrow. That + arrow's modes are always legacy, since a method type + cannot carry a mode annotation. *) let type_ = match Cmi.read_type_expr env expr.exp_type with - | Arrow (_, _, t) -> t + | Arrow (_, _, (t, [])) -> t + | Arrow (_, _, (_, _ :: _)) -> invalid_arg "unexpected modes on method" | t -> t in false, type_ diff --git a/src/loader/cmti.ml b/src/loader/cmti.ml index 10fda71abc..11a58ec03f 100644 --- a/src/loader/cmti.ml +++ b/src/loader/cmti.ml @@ -41,6 +41,9 @@ let opt_map f = function let read_label = Cmi.read_label let rec read_core_type env container ctyp = + read_core_type_modal env Cmi.legacy_modes container ctyp + +and read_core_type_modal env modes container ctyp = let open TypeExpr in match ctyp.ctyp_desc with #if defined OXCAML @@ -59,8 +62,11 @@ let rec read_core_type env container ctyp = #endif let lbl = read_label lbl in let arg = read_core_type env container arg in - let res = read_core_type env container res in - Arrow(lbl, arg, res) + let arg_modes, res_modes, modes = + Cmi.read_arrow_modes modes ctyp.ctyp_type + in + let res = read_core_type_modal env modes container res in + Arrow(lbl, (arg, arg_modes), (res, res_modes)) | Ttyp_tuple typs -> #if OCAML_VERSION >= (5,4,0) || defined OXCAML let typs = List.map (fun (lbl,x) -> lbl, read_core_type env container x) typs in diff --git a/src/model/lang.ml b/src/model/lang.ml index 4fe5b890ae..9dabb70404 100644 --- a/src/model/lang.ml +++ b/src/model/lang.ml @@ -217,6 +217,11 @@ and Modalities : sig end = Modalities +and Modes : sig + type t = string list +end = + Modes + and TypeDecl : sig module Field : sig type t = { @@ -496,7 +501,7 @@ and TypeExpr : sig | Var of string | Any | Alias of t * string - | Arrow of label option * t * t + | Arrow of label option * with_modes * with_modes | Tuple of (string option * t) list | Unboxed_tuple of (string option * t) list | Constr of Path.Type.t * t list @@ -508,6 +513,8 @@ and TypeExpr : sig | Splice of t | Package of TypeExpr.Package.t | Arrow_functor of label option * Module.t * t + + and with_modes = t * Modes.t end = TypeExpr diff --git a/src/model_desc/lang_desc.ml b/src/model_desc/lang_desc.ml index 75e9bcf7e3..f09fad15bb 100644 --- a/src/model_desc/lang_desc.ml +++ b/src/model_desc/lang_desc.ml @@ -683,7 +683,10 @@ and typeexpr_t = C ( "Arrow", (x1, x2, x3), - Triple (Option typeexpr_label, typeexpr_t, typeexpr_t) ) + Triple + ( Option typeexpr_label, + Pair (typeexpr_t, List string), + Pair (typeexpr_t, List string) ) ) | Tuple x -> C ("Tuple", x, List (Pair (Option string, typeexpr_t))) | Unboxed_tuple x -> C ("Unboxed_tuple", x, List (Pair (Option string, typeexpr_t))) diff --git a/src/xref2/compile.ml b/src/xref2/compile.ml index a6dd9a101d..3c64d74b2b 100644 --- a/src/xref2/compile.ml +++ b/src/xref2/compile.ml @@ -887,14 +887,14 @@ and handle_arrow : Env.t -> Id.Id.label_parent -> TypeExpr.label option -> - TypeExpr.t -> - TypeExpr.t -> + TypeExpr.with_modes -> + TypeExpr.with_modes -> TypeExpr.t = - fun env parent lbl t1 t2 -> - let t2' = type_expression env parent t2 in + fun env parent lbl (t1, m1) (t2, m2) -> + let t2' = (type_expression env parent t2, m2) in match lbl with | Some (Optional _ | Label _) | None -> - Arrow (lbl, type_expression env parent t1, t2') + Arrow (lbl, (type_expression env parent t1, m1), t2') | Some (RawOptional s) -> ( (* s is definitely an option type, but not _obviously_ so. *) match Component.Of_Lang.(type_expression (empty ()) t1) with @@ -916,10 +916,14 @@ and handle_arrow : in match find_option p with | Some t1 -> - Arrow (Some (Optional s), type_expression env parent t1, t2') + Arrow (Some (Optional s), (type_expression env parent t1, m1), t2') | None -> - Arrow (Some (RawOptional s), type_expression env parent t1, t2')) - | _ -> Arrow (Some (RawOptional s), type_expression env parent t1, t2')) + Arrow + (Some (RawOptional s), (type_expression env parent t1, m1), t2') + ) + | _ -> + Arrow (Some (RawOptional s), (type_expression env parent t1, m1), t2') + ) and type_expression : Env.t -> Id.LabelParent.t -> _ -> _ = fun env parent texpr -> diff --git a/src/xref2/component.ml b/src/xref2/component.ml index be23a7b3ea..f132b56729 100644 --- a/src/xref2/component.ml +++ b/src/xref2/component.ml @@ -125,7 +125,10 @@ and TypeExpr : sig | Var of string | Any | Alias of t * string - | Arrow of label option * t * t + | Arrow of + label option + * (t * Odoc_model.Lang.Modes.t) + * (t * Odoc_model.Lang.Modes.t) | Tuple of (string option * t) list | Unboxed_tuple of (string option * t) list | Constr of Cpath.type_ * t list @@ -1201,7 +1204,7 @@ module Fmt = struct | Var x -> Format.fprintf ppf "%s" x | Any -> Format.fprintf ppf "_" | Alias (x, y) -> Format.fprintf ppf "(alias %a %s)" (type_expr c) x y - | Arrow (l, t1, t2) -> + | Arrow (l, (t1, _), (t2, _)) -> Format.fprintf ppf "%a(%a) -> %a" type_expr_label l (type_expr c) t1 (type_expr c) t2 | Tuple ts -> Format.fprintf ppf "(%a)" (type_labeled_tuple c) ts @@ -2362,8 +2365,11 @@ module Of_Lang = struct | Any -> Any | Constr (p, xs) -> Constr (type_path ident_map p, List.map (type_expression ident_map) xs) - | Arrow (lbl, t1, t2) -> - Arrow (lbl, type_expression ident_map t1, type_expression ident_map t2) + | Arrow (lbl, (t1, m1), (t2, m2)) -> + Arrow + ( lbl, + (type_expression ident_map t1, m1), + (type_expression ident_map t2, m2) ) | Tuple ts -> Tuple (List.map (fun (lbl, ty) -> (lbl, type_expression ident_map ty)) ts) diff --git a/src/xref2/component.mli b/src/xref2/component.mli index abda54e140..d9e25394ff 100644 --- a/src/xref2/component.mli +++ b/src/xref2/component.mli @@ -120,7 +120,10 @@ and TypeExpr : sig | Var of string | Any | Alias of t * string - | Arrow of label option * t * t + | Arrow of + label option + * (t * Odoc_model.Lang.Modes.t) + * (t * Odoc_model.Lang.Modes.t) | Tuple of (string option * t) list | Unboxed_tuple of (string option * t) list | Constr of Cpath.type_ * t list diff --git a/src/xref2/expand_tools.ml b/src/xref2/expand_tools.ml index 6ec6930ad4..0688fb5045 100644 --- a/src/xref2/expand_tools.ml +++ b/src/xref2/expand_tools.ml @@ -57,7 +57,8 @@ let rec type_expr map t = | Any -> Any | Alias (t, s) -> if List.mem_assoc s map then raise Clash else Alias (type_expr map t, s) - | Arrow (l, t1, t2) -> Arrow (l, type_expr map t1, type_expr map t2) + | Arrow (l, (t1, m1), (t2, m2)) -> + Arrow (l, (type_expr map t1, m1), (type_expr map t2, m2)) | Tuple ts -> Tuple (List.map (fun (l, ty) -> (l, type_expr map ty)) ts) | Unboxed_tuple ts -> Unboxed_tuple (List.map (fun (l, t) -> (l, type_expr map t)) ts) diff --git a/src/xref2/lang_of.ml b/src/xref2/lang_of.ml index 5c49cb96c9..796bad42e3 100644 --- a/src/xref2/lang_of.ml +++ b/src/xref2/lang_of.ml @@ -1039,8 +1039,8 @@ and type_expr map (parent : Identifier.LabelParent.t) (t : Component.TypeExpr.t) | Var s -> Var s | Any -> Any | Alias (t, str) -> Alias (type_expr map parent t, str) - | Arrow (lbl, t1, t2) -> - Arrow (lbl, type_expr map parent t1, type_expr map parent t2) + | Arrow (lbl, (t1, m1), (t2, m2)) -> + Arrow (lbl, (type_expr map parent t1, m1), (type_expr map parent t2, m2)) | Tuple ts -> Tuple (List.map (fun (lbl, ty) -> (lbl, type_expr map parent ty)) ts) | Unboxed_tuple ts -> diff --git a/src/xref2/link.ml b/src/xref2/link.ml index bd332ee691..2620a49065 100644 --- a/src/xref2/link.ml +++ b/src/xref2/link.ml @@ -442,7 +442,7 @@ let warn_on_hidden_representation (id : Id.Type.t) is_hidden (p :> Paths.Path.t) || List.exists (fun t -> internal_typ_exp t) ts | Poly (_, t) | Alias (t, _) -> internal_typ_exp t - | Arrow (_, t, t2) -> internal_typ_exp t || internal_typ_exp t2 + | Arrow (_, (t, _), (t2, _)) -> internal_typ_exp t || internal_typ_exp t2 | Tuple ts -> List.exists (fun (_, t) -> internal_typ_exp t) ts | Class (_, ts) -> List.exists (fun t -> internal_typ_exp t) ts | _ -> false @@ -1141,11 +1141,11 @@ and type_expression : Env.t -> Id.Signature.t -> _ -> _ = match texpr with | Var _ | Any -> texpr | Alias (t, str) -> Alias (type_expression env parent visited t, str) - | Arrow (lbl, t1, t2) -> + | Arrow (lbl, (t1, m1), (t2, m2)) -> Arrow ( lbl, - type_expression env parent visited t1, - type_expression env parent visited t2 ) + (type_expression env parent visited t1, m1), + (type_expression env parent visited t2, m2) ) | Tuple ts -> Tuple (List.map diff --git a/src/xref2/subst.ml b/src/xref2/subst.ml index 49bbf0205e..5a90ef98cb 100644 --- a/src/xref2/subst.ml +++ b/src/xref2/subst.ml @@ -164,8 +164,8 @@ let rec substitute_vars vars t = | Var s -> ( try List.assoc s vars with Not_found -> t) | Any -> Any | Alias (t, str) -> Alias (substitute_vars vars t, str) - | Arrow (lbl, t1, t2) -> - Arrow (lbl, substitute_vars vars t1, substitute_vars vars t2) + | Arrow (lbl, (t1, m1), (t2, m2)) -> + Arrow (lbl, (substitute_vars vars t1, m1), (substitute_vars vars t2, m2)) | Tuple ts -> Tuple (List.map (fun (lbl, ty) -> (lbl, substitute_vars vars ty)) ts) | Unboxed_tuple ts -> @@ -609,7 +609,8 @@ and type_expr s t = | Var s -> Var s | Any -> Any | Alias (t, str) -> Alias (type_expr s t, str) - | Arrow (lbl, t1, t2) -> Arrow (lbl, type_expr s t1, type_expr s t2) + | Arrow (lbl, (t1, m1), (t2, m2)) -> + Arrow (lbl, (type_expr s t1, m1), (type_expr s t2, m2)) | Tuple ts -> Tuple (List.map (fun (lbl, ty) -> (lbl, type_expr s ty)) ts) | Unboxed_tuple ts -> Unboxed_tuple (List.map (fun (l, t) -> (l, type_expr s t)) ts) diff --git a/test/generators/cases/oxcaml.mli b/test/generators/cases/oxcaml.mli index 11db3f653a..61b39cfb6e 100644 --- a/test/generators/cases/oxcaml.mli +++ b/test/generators/cases/oxcaml.mli @@ -280,3 +280,192 @@ module M3 : sig @@ contended end (** [contended] modality applied to all definitions in the module, except the ones which have already specified this axis. *) + +(** {1 Modes} *) + +val mode_arg : int @ local -> int +(** Mode on a function argument. *) + +val mode_ret : int -> int @ local +(** Mode on a function return. *) + +val mode_both : int @ local -> int @ local +(** Modes on both argument and return. *) + +val mode_multi : string @ local once -> string @ local unique +(** Multiple modes on argument and return. *) + +val mode_multi_flipped : string @ once local -> string @ unique local +(** Same as [mode_multi], to show that modes order is normalized. *) + +val mode_labeled : x:int @ local -> int +(** Mode on a labeled argument. *) + +val mode_optional : ?x:int @ local -> unit -> int +(** Mode on an optional argument. *) + +val mode_higher_order : ('a -> 'b) @ local -> 'a -> 'b +(** Mode on a higher-order function argument. *) + +val mode_arrow_result : int -> (int -> int) @ local +(** Mode on a result that is itself an arrow. The arrow must be parenthesized so + the mode does not appear to bind to the inner return type. *) + +(** {2 Curry-implied result modes} + + Closing over an argument constrains the partial-application closure across + several axes, not just locality. When the result mode is the one currying + implies from the argument, it is suppressed (as the compiler does). *) + +val curry_once : (int -> int) @ once -> int -> int +(** [once] argument: the implied [once] result mode is suppressed. *) + +val curry_portable : (int -> int) @ portable -> int -> int +(** [portable] argument: the implied result mode is suppressed. *) + +val curry_contended : (int -> int) @ contended -> int -> int +(** [contended] argument: the implied result mode is suppressed. *) + +(** {2 Result modes that are kept} + + A result mode is only suppressed when it is exactly the one currying + implies. An explicit mode on a different axis is kept (and the arrow result + is parenthesized). *) + +val keep_portable : int @ local -> (int -> int) @ portable +(** [portable] on the result is not implied by a [local] argument, so it is kept. *) + +val keep_once : int @ local -> (int -> int) @ once +(** [once] on the result is not implied by a [local] argument, so it is kept. *) + +val keep_over_once : (int -> int) @ once -> (int -> int) @ portable +(** The curry-implied [once] is suppressed, but the explicit [portable] is kept. *) + +val keep_over_local : (int -> int) @ local -> (int -> int) @ portable +(** The curry-implied [local] is suppressed, but the explicit [portable] is kept. *) + +val keep_portable_over_nonportable : (int -> int) @ nonportable -> (int -> int) @ portable +(** The [nonportable] argument mode is the default and dropped, while the + explicit [portable] result, not implied by currying, is kept. *) + +(** {2 All mode axes} *) + +val mode_global : int @ global -> unit +(** Locality mode (legacy, not rendered). *) + +val mode_local : int @ local -> unit +(** Locality mode. *) + +val mode_aliased : int @ aliased -> unit +(** Uniqueness mode (legacy, not rendered). *) + +val mode_unique : int @ unique -> unit +(** Uniqueness mode. *) + +val mode_many : int @ many -> unit +(** Linearity mode (legacy, not rendered). *) + +val mode_once : int @ once -> unit +(** Linearity mode. *) + +val mode_portable : int @ portable -> unit +(** Portability mode. *) + +val mode_shareable : int @ shareable -> unit +(** Portability mode (intermediate value). *) + +val mode_nonportable : int @ nonportable -> unit +(** Portability mode (legacy, not rendered). *) + +val mode_uncontended : int @ uncontended -> unit +(** Contention mode (legacy, not rendered). *) + +val mode_shared : int @ shared -> unit +(** Contention mode. *) + +val mode_contended : int @ contended -> unit +(** Contention mode. *) + +val mode_yielding : int @ yielding -> unit +(** Yield mode. *) + +val mode_unyielding : int @ unyielding -> unit +(** Yield mode (legacy, not rendered). *) + +val mode_forkable : int @ forkable -> unit +(** Fork mode (identity on a non-[local] argument, not rendered). *) + +val mode_local_forkable : int @ local forkable -> unit +(** Fork mode, rendered because the argument is also [local]. *) + +val mode_unforkable : int @ unforkable -> unit +(** Fork mode. *) + +val mode_local_unforkable : int @ local unforkable -> unit +(** Fork mode (identity for a [local] argument, not rendered). *) + +val mode_stateless : int @ stateless -> unit +(** Statefulness mode. *) + +val mode_observing : int @ observing -> unit +(** Statefulness mode. *) + +val mode_stateful : int @ stateful -> unit +(** Statefulness mode (identity when [portability] is at its default, not rendered). *) + +val mode_immutable : int @ immutable -> unit +(** Visibility mode. *) + +val mode_read : int @ read -> unit +(** Visibility mode. *) + +val mode_read_write : int @ read_write -> unit +(** Visibility mode (legacy, not rendered). *) + +val mode_static : int @ static -> unit +(** Staticity mode. *) + +val mode_dynamic : int @ dynamic -> unit +(** Staticity mode (legacy, not rendered). *) + +(** {2 Cross-axis suppression} + + Some axes have a default value that is implied by another axis; the implied + value is suppressed when rendering. *) + +val mode_local_yielding : int @ local yielding -> unit +(** [yielding] is the default for [local], so it is not rendered. *) + +val mode_local_unyielding : int @ local unyielding -> unit +(** [unyielding] is non-default for [local], so it is rendered. *) + +val mode_immutable_contended : int @ immutable contended -> unit +(** [contended] is the default for [immutable], so it is not rendered. *) + +val mode_immutable_uncontended : int @ immutable uncontended -> unit +(** [uncontended] is non-default for [immutable], so it is rendered. *) + +val mode_stateless_portable : int @ stateless portable -> unit +(** [portable] is the default for [stateless], so it is not rendered. *) + +val mode_stateful_portable : int @ stateful portable -> unit +(** [portable] is non-default for [stateful], so it is rendered. *) + +(** {2 Modes in type definitions} *) + +type mode_alias = int @ local -> int +(** Type alias for an arrow with a mode on its argument. *) + +type mode_record = { + fn : int @ local -> int; (** Record field whose type is an arrow with a mode. *) + fn_both : int @ local -> int @ local; (** Arrow field with modes on both sides. *) + mutable mfn : int @ local -> int; (** Mutable arrow field with a mode. *) +} + +type mode_cstr = + | Mc_arrow of (int @ local -> int) + (** Constructor argument is a parenthesized arrow with a mode. *) + | Mc_nested of ((int @ local -> int) -> unit) + (** Nested arrow: higher-order with a mode on the inner argument. *) + | Mc_gadt : ('a @ once -> 'a) -> mode_cstr + (** GADT constructor *) diff --git a/test/generators/cases/oxcaml_impl.ml b/test/generators/cases/oxcaml_impl.ml index 351121dbe1..3d605794d5 100644 --- a/test/generators/cases/oxcaml_impl.ml +++ b/test/generators/cases/oxcaml_impl.ml @@ -8,3 +8,54 @@ end module Including = struct include To_be_included end + +(* The declarations below exercise reading modes and modalities from an + implementation's [.cmt] file, as opposed to an interface's [.cmti]. *) + +type opaque + +(** {1 Modalities} *) + +(** {2 Modalities on record fields} *) + +type modalities_record = { + f_global : opaque @@ global; (** Locality modality. *) + f_portable : opaque @@ portable; (** Portability modality. *) + f_multi : opaque @@ global portable; (** Multiple modalities. *) + f_plain : opaque; (** No modality, for reference. *) +} + +(** {2 Modalities on constructor arguments} *) + +type modalities_variant = + | A of string @@ global (** Constructor argument with [global] modality. *) + | B of (int -> int) @@ portable + (** Function constructor argument with modality. *) + | C of int @@ portable * string @@ global + (** Per-element modalities in a constructor tuple. *) + +(** {1 Modes} *) + +(** {2 Modes in type definitions} *) + +type mode_alias = int @ local -> int +(** Type alias for an arrow with a mode on its argument. *) + +type mode_record = { + fn : int @ local -> int; (** Field whose type is an arrow with a mode. *) + fn_both : int @ local -> int @ local; (** Modes on both sides. *) +} + +type mode_cstr = + | Mc_arrow of (int @ local -> int) + (** Constructor argument is a parenthesized arrow with a mode. *) + | Mc_nested of ((int @ local -> int) -> unit) + (** Nested arrow: higher-order with a mode on the inner argument. *) + +(** {2 Modes on values} *) + +let mode_arg : int @ local -> int = fun x -> x +(** Mode on a function argument, via a type annotation. *) + +let mode_multi : string @ local once -> string @ local once = fun x -> x +(** Multiple modes on argument and return. *) diff --git a/test/generators/html/Oxcaml.html b/test/generators/html/Oxcaml.html index 88dfb52689..6a01015c55 100644 --- a/test/generators/html/Oxcaml.html +++ b/test/generators/html/Oxcaml.html @@ -72,6 +72,19 @@

Module Oxcaml

+
  • Modes + +
  • @@ -1181,6 +1194,837 @@

    axis.

    +

    Modes

    +
    +
    + + + val mode_arg : + int @ local -> + int + + +

    Mode on a function argument.

    +
    +
    +
    + + + val mode_ret : + int -> int @ local + + +

    Mode on a function return.

    +
    +
    +
    + + + val mode_both : + int @ local -> + int @ local + + +
    +

    Modes on both argument and return.

    +
    +
    +
    + + + val mode_multi : + string @ local once -> + string @ local unique + + +
    +

    Multiple modes on argument and return.

    +
    +
    +
    + + + val mode_multi_flipped : + string @ local once -> + string @ local unique + + +
    +
    +

    Same as mode_multi, to show that modes order is + normalized. +

    +
    +
    +
    +
    + + + val mode_labeled : + x:int @ local + -> + int + + +

    Mode on a labeled argument.

    +
    +
    +
    + + + val mode_optional : + ?x:int @ local + -> + unit -> + int + + +

    Mode on an optional argument.

    +
    +
    +
    + + + val mode_higher_order : + + ( + 'a + -> + 'b) + @ local -> + + 'a + -> + 'b + + +
    +

    Mode on a higher-order function argument.

    +
    +
    +
    +
    + + + val mode_arrow_result : + int -> + (int -> int) + @ local + + +
    +
    +

    Mode on a result that is itself an arrow. The arrow must be + parenthesized so the mode does not appear to bind to the inner + return type. +

    +
    +
    +

    + Curry-implied + result modes +

    +

    Closing over an argument constrains the partial-application closure + across several axes, not just locality. When the result mode is + the one currying implies from the argument, it is suppressed (as + the compiler does). +

    +
    +
    + + + val curry_once : + + (int -> int) + @ once -> + int -> + int + + +
    +
    +

    once argument: the implied once result + mode is suppressed. +

    +
    +
    +
    +
    + + + val curry_portable : + + (int -> int) + @ portable -> + int -> + int + + +
    +
    +

    portable argument: the implied result mode is + suppressed. +

    +
    +
    +
    +
    + + + val curry_contended : + + (int -> int) + @ contended -> + int -> + int + + +
    +
    +

    contended argument: the implied result mode is + suppressed. +

    +
    +
    +

    + Result modes + that are kept +

    +

    A result mode is only suppressed when it is exactly the one currying + implies. An explicit mode on a different axis is kept (and the arrow + result is parenthesized). +

    +
    +
    + + + val keep_portable : + int @ local -> + (int -> int) + @ portable + + +
    +
    +

    portable on the result is not implied by a + local argument, so it is kept. +

    +
    +
    +
    +
    + + + val keep_once : + int @ local -> + (int -> int) + @ once + + +
    +
    +

    once on the result is not implied by a + local argument, so it is kept. +

    +
    +
    +
    +
    + + + val keep_over_once : + + (int -> int) + @ once -> + + (int -> int) + @ portable + + +
    +
    +

    The curry-implied once is suppressed, but the explicit + portable is kept. +

    +
    +
    +
    +
    + + + val keep_over_local : + + (int -> int) + @ local -> + + (int -> int) + @ portable + + +
    +
    +

    The curry-implied local is suppressed, but the explicit + portable is kept. +

    +
    +
    +
    +
    + + + val keep_portable_over_nonportable + : + + (int -> int) + -> + + (int -> int) + @ portable + + +
    +
    +

    The nonportable argument mode is the default and + dropped, while the explicit portable result, not + implied by currying, is kept. +

    +
    +
    +

    + All mode axes +

    +
    +
    + + + val mode_global : + int -> unit + + +
    +

    Locality mode (legacy, not rendered).

    +
    +
    +
    + + + val mode_local : + int @ local -> + unit + + +

    Locality mode.

    +
    +
    +
    + + + val mode_aliased : + int -> unit + + +
    +

    Uniqueness mode (legacy, not rendered).

    +
    +
    +
    +
    + + + val mode_unique : + int @ unique -> + unit + + +

    Uniqueness mode.

    +
    +
    +
    + + + val mode_many : + int -> unit + + +
    +

    Linearity mode (legacy, not rendered).

    +
    +
    +
    + + + val mode_once : + int @ once -> unit + + +

    Linearity mode.

    +
    +
    +
    + + + val mode_portable : + int @ portable -> + unit + + +

    Portability mode.

    +
    +
    +
    + + + val mode_shareable : + int @ shareable -> + unit + + +
    +

    Portability mode (intermediate value).

    +
    +
    +
    + + + val mode_nonportable : + int -> unit + + +
    +

    Portability mode (legacy, not rendered).

    +
    +
    +
    +
    + + + val mode_uncontended : + int -> unit + + +
    +

    Contention mode (legacy, not rendered).

    +
    +
    +
    +
    + + + val mode_shared : + int @ shared -> + unit + + +

    Contention mode.

    +
    +
    +
    + + + val mode_contended : + int @ contended -> + unit + + +

    Contention mode.

    +
    +
    +
    + + + val mode_yielding : + int @ yielding -> + unit + + +

    Yield mode.

    +
    +
    +
    + + + val mode_unyielding : + int -> unit + + +
    +

    Yield mode (legacy, not rendered).

    +
    +
    +
    + + + val mode_forkable : + int -> unit + + +
    +
    +

    Fork mode (identity on a non-local argument, not + rendered). +

    +
    +
    +
    +
    + + + val mode_local_forkable : + int @ local forkable -> + unit + + +
    +
    +

    Fork mode, rendered because the argument is also local. +

    +
    +
    +
    +
    + + + val mode_unforkable : + int @ unforkable -> + unit + + +

    Fork mode.

    +
    +
    +
    + + + val mode_local_unforkable : + int @ local -> + unit + + +
    +
    +

    Fork mode (identity for a local argument, not rendered). +

    +
    +
    +
    +
    + + + val mode_stateless : + int @ stateless -> + unit + + +

    Statefulness mode.

    +
    +
    +
    + + + val mode_observing : + int @ observing -> + unit + + +

    Statefulness mode.

    +
    +
    +
    + + + val mode_stateful : + int -> unit + + +
    +
    +

    Statefulness mode (identity when portability is + at its default, not rendered). +

    +
    +
    +
    +
    + + + val mode_immutable : + int @ immutable -> + unit + + +

    Visibility mode.

    +
    +
    +
    + + + val mode_read : + int @ read -> unit + + +

    Visibility mode.

    +
    +
    +
    + + + val mode_read_write : + int -> unit + + +
    +

    Visibility mode (legacy, not rendered).

    +
    +
    +
    +
    + + + val mode_static : + int @ static -> + unit + + +

    Staticity mode.

    +
    +
    +
    + + + val mode_dynamic : + int -> unit + + +
    +

    Staticity mode (legacy, not rendered).

    +
    +

    + Cross-axis + suppression +

    +

    Some axes have a default value that is implied by another axis; + the implied value is suppressed when rendering. +

    +
    +
    + + + val mode_local_yielding : + int @ local -> + unit + + +
    +
    +

    yielding is the default for local, + so it is not rendered. +

    +
    +
    +
    +
    + + + val mode_local_unyielding : + int @ local unyielding -> + unit + + +
    +
    +

    unyielding is non-default for local + , so it is rendered. +

    +
    +
    +
    +
    + + + val mode_immutable_contended + : int @ immutable -> + unit + + +
    +
    +

    contended is the default for immutable + , so it is not rendered. +

    +
    +
    +
    +
    + + + val mode_immutable_uncontended + : + int @ uncontended immutable -> + unit + + +
    +
    +

    uncontended is non-default for immutable + , so it is rendered. +

    +
    +
    +
    +
    + + + val mode_stateless_portable : + int @ stateless -> + unit + + +
    +
    +

    portable is the default for stateless + , so it is not rendered. +

    +
    +
    +
    +
    + + + val mode_stateful_portable : + int @ portable -> + unit + + +
    +
    +

    portable is non-default for stateful + , so it is rendered. +

    +
    +
    +

    + Modes in + type definitions +

    +
    +
    + + type mode_alias + = int @ local -> + int + + +
    +
    +

    Type alias for an arrow with a mode on its argument.

    +
    +
    +
    +
    + + type mode_record + = { + +
      +
    1. + + + fn : + int @ local -> + int; + + +
      (* +

      Record field whose type is an arrow with a mode.

      + *) +
      +
    2. +
    3. + + + fn_both : + int @ local -> + int @ local; + + +
      (* +

      Arrow field with modes on both sides.

      + *) +
      +
    4. +
    5. + + + mutable mfn : + int @ local -> + int; + + +
      (* +

      Mutable arrow field with a mode.

      + *) +
      +
    6. +
    } +
    +
    +
    +
    + + type mode_cstr + = + +
      +
    1. + | + Mc_arrow + of + int @ local -> + int + + +
      (* +

      Constructor argument is a parenthesized arrow with a mode.

      + *) +
      +
    2. +
    3. + | + Mc_nested + of + + ( + int @ local -> + int) + -> + unit + + +
      (* +

      Nested arrow: higher-order with a mode on the inner argument.

      + *) +
      +
    4. +
    5. + | + Mc_gadt : + ( + 'a @ once + -> + 'a) + -> + mode_cstr + + +
      (* +

      GADT constructor

      *) +
      +
    6. +
    +
    diff --git a/test/generators/html/Oxcaml_impl.html b/test/generators/html/Oxcaml_impl.html index 285dc626a8..9fde4ca6d5 100644 --- a/test/generators/html/Oxcaml_impl.html +++ b/test/generators/html/Oxcaml_impl.html @@ -14,6 +14,31 @@

    Module Oxcaml_impl

    +
    + +
    @@ -54,6 +79,245 @@

    Module Oxcaml_impl

    +
    +
    + + type opaque +
    +
    +

    Modalities +

    +

    + Modalities + on record fields +

    +
    +
    + + type modalities_record + = { + +
      +
    1. + + + f_global : opaque @@ global; + +
      (* +

      Locality modality.

      *) +
      +
    2. +
    3. + + + f_portable : opaque @@ portable; + + +
      (* +

      Portability modality.

      *) +
      +
    4. +
    5. + + + f_multi : opaque @@ global portable; + + +
      (* +

      Multiple modalities.

      *) +
      +
    6. +
    7. + + f_plain : opaque; +
      (* +

      No modality, for reference.

      + *) +
      +
    8. +
    } +
    +
    +

    + + Modalities on constructor arguments +

    +
    +
    + + type modalities_variant + = + +
      +
    1. + | + A + of string @@ global + + +
      (* +

      Constructor argument with global modality.

      + *) +
      +
    2. +
    3. + | + B + of + int -> int @@ + portable + + +
      (* +

      Function constructor argument with modality.

      + *) +
      +
    4. +
    5. + | + C + of int @@ portable * string @@ + global + + +
      (* +

      Per-element modalities in a constructor tuple.

      + *) +
      +
    6. +
    +
    +

    Modes

    +

    + Modes in + type definitions +

    +
    +
    + + type mode_alias + = int @ local -> + int + + +
    +
    +

    Type alias for an arrow with a mode on its argument.

    +
    +
    +
    +
    + + type mode_record + = { + +
      +
    1. + + + fn : + int @ local -> + int; + + +
      (* +

      Field whose type is an arrow with a mode.

      + *) +
      +
    2. +
    3. + + + fn_both : + int @ local -> + int @ local; + + +
      (* +

      Modes on both sides.

      *) +
      +
    4. +
    } +
    +
    +
    +
    + + type mode_cstr + = + +
      +
    1. + | + Mc_arrow + of + int @ local -> + int + + +
      (* +

      Constructor argument is a parenthesized arrow with a mode.

      + *) +
      +
    2. +
    3. + | + Mc_nested + of + + ( + int @ local -> + int) + -> + unit + + +
      (* +

      Nested arrow: higher-order with a mode on the inner argument.

      + *) +
      +
    4. +
    +
    +
    +

    + Modes on values +

    +
    +
    + + + val mode_arg : + int @ local -> + int + + +
    +
    +

    Mode on a function argument, via a type annotation.

    +
    +
    +
    +
    + + + val mode_multi : + string @ local once -> + string @ local once + + +
    +

    Multiple modes on argument and return.

    +
    diff --git a/test/generators/latex/Oxcaml.tex b/test/generators/latex/Oxcaml.tex index 86830b7210..8ae5c07ffc 100644 --- a/test/generators/latex/Oxcaml.tex +++ b/test/generators/latex/Oxcaml.tex @@ -240,5 +240,130 @@ \subsubsection{Modalities on module declarations\label{Oxcaml--modalities-on-mod \end{ocamlindent}% \ocamlcodefragment{\ocamltag{keyword}{end}}\begin{ocamlindent}\ocamlinlinecode{contended} modality applied to all definitions in the module, except the ones which have already specified this axis.\end{ocamlindent}% \medbreak +\subsection{Modes\label{Oxcaml--modes}}% +\label{Oxcaml--val-mode_arg}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}arg : int @ local \ocamltag{arrow}{$\rightarrow$} int}\begin{ocamlindent}Mode on a function argument.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_ret}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}ret : int \ocamltag{arrow}{$\rightarrow$} int @ local}\begin{ocamlindent}Mode on a function return.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_both}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}both : int @ local \ocamltag{arrow}{$\rightarrow$} int @ local}\begin{ocamlindent}Modes on both argument and return.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_multi}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}multi : string @ local once \ocamltag{arrow}{$\rightarrow$} string @ local unique}\begin{ocamlindent}Multiple modes on argument and return.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_multi_flipped}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}multi\_\allowbreak{}flipped : string @ local once \ocamltag{arrow}{$\rightarrow$} string @ local unique}\begin{ocamlindent}Same as \ocamlinlinecode{mode\_\allowbreak{}multi}, to show that modes order is normalized.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_labeled}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}labeled : \ocamltag{label}{x}:int @ local \ocamltag{arrow}{$\rightarrow$} int}\begin{ocamlindent}Mode on a labeled argument.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_optional}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}optional : \ocamltag{optlabel}{?x}:int @ local \ocamltag{arrow}{$\rightarrow$} unit \ocamltag{arrow}{$\rightarrow$} int}\begin{ocamlindent}Mode on an optional argument.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_higher_order}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}higher\_\allowbreak{}order : (\ocamltag{type-var}{'a} \ocamltag{arrow}{$\rightarrow$} \ocamltag{type-var}{'b}) @ local \ocamltag{arrow}{$\rightarrow$} \ocamltag{type-var}{'a} \ocamltag{arrow}{$\rightarrow$} \ocamltag{type-var}{'b}}\begin{ocamlindent}Mode on a higher-order function argument.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_arrow_result}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}arrow\_\allowbreak{}result : int \ocamltag{arrow}{$\rightarrow$} (int \ocamltag{arrow}{$\rightarrow$} int) @ local}\begin{ocamlindent}Mode on a result that is itself an arrow. The arrow must be parenthesized so the mode does not appear to bind to the inner return type.\end{ocamlindent}% +\medbreak +\subsubsection{Curry-implied result modes\label{Oxcaml--curry-implied-result-modes}}% +Closing over an argument constrains the partial-application closure across several axes, not just locality. When the result mode is the one currying implies from the argument, it is suppressed (as the compiler does). + +\label{Oxcaml--val-curry_once}\ocamlcodefragment{\ocamltag{keyword}{val} curry\_\allowbreak{}once : (int \ocamltag{arrow}{$\rightarrow$} int) @ once \ocamltag{arrow}{$\rightarrow$} int \ocamltag{arrow}{$\rightarrow$} int}\begin{ocamlindent}\ocamlinlinecode{once} argument: the implied \ocamlinlinecode{once} result mode is suppressed.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-curry_portable}\ocamlcodefragment{\ocamltag{keyword}{val} curry\_\allowbreak{}portable : (int \ocamltag{arrow}{$\rightarrow$} int) @ portable \ocamltag{arrow}{$\rightarrow$} int \ocamltag{arrow}{$\rightarrow$} int}\begin{ocamlindent}\ocamlinlinecode{portable} argument: the implied result mode is suppressed.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-curry_contended}\ocamlcodefragment{\ocamltag{keyword}{val} curry\_\allowbreak{}contended : (int \ocamltag{arrow}{$\rightarrow$} int) @ contended \ocamltag{arrow}{$\rightarrow$} int \ocamltag{arrow}{$\rightarrow$} int}\begin{ocamlindent}\ocamlinlinecode{contended} argument: the implied result mode is suppressed.\end{ocamlindent}% +\medbreak +\subsubsection{Result modes that are kept\label{Oxcaml--result-modes-that-are-kept}}% +A result mode is only suppressed when it is exactly the one currying implies. An explicit mode on a different axis is kept (and the arrow result is parenthesized). + +\label{Oxcaml--val-keep_portable}\ocamlcodefragment{\ocamltag{keyword}{val} keep\_\allowbreak{}portable : int @ local \ocamltag{arrow}{$\rightarrow$} (int \ocamltag{arrow}{$\rightarrow$} int) @ portable}\begin{ocamlindent}\ocamlinlinecode{portable} on the result is not implied by a \ocamlinlinecode{local} argument, so it is kept.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-keep_once}\ocamlcodefragment{\ocamltag{keyword}{val} keep\_\allowbreak{}once : int @ local \ocamltag{arrow}{$\rightarrow$} (int \ocamltag{arrow}{$\rightarrow$} int) @ once}\begin{ocamlindent}\ocamlinlinecode{once} on the result is not implied by a \ocamlinlinecode{local} argument, so it is kept.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-keep_over_once}\ocamlcodefragment{\ocamltag{keyword}{val} keep\_\allowbreak{}over\_\allowbreak{}once : (int \ocamltag{arrow}{$\rightarrow$} int) @ once \ocamltag{arrow}{$\rightarrow$} (int \ocamltag{arrow}{$\rightarrow$} int) @ portable}\begin{ocamlindent}The curry-implied \ocamlinlinecode{once} is suppressed, but the explicit \ocamlinlinecode{portable} is kept.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-keep_over_local}\ocamlcodefragment{\ocamltag{keyword}{val} keep\_\allowbreak{}over\_\allowbreak{}local : (int \ocamltag{arrow}{$\rightarrow$} int) @ local \ocamltag{arrow}{$\rightarrow$} (int \ocamltag{arrow}{$\rightarrow$} int) @ portable}\begin{ocamlindent}The curry-implied \ocamlinlinecode{local} is suppressed, but the explicit \ocamlinlinecode{portable} is kept.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-keep_portable_over_nonportable}\ocamlcodefragment{\ocamltag{keyword}{val} keep\_\allowbreak{}portable\_\allowbreak{}over\_\allowbreak{}nonportable : (int \ocamltag{arrow}{$\rightarrow$} int) \ocamltag{arrow}{$\rightarrow$} (int \ocamltag{arrow}{$\rightarrow$} int) @ portable}\begin{ocamlindent}The \ocamlinlinecode{nonportable} argument mode is the default and dropped, while the explicit \ocamlinlinecode{portable} result, not implied by currying, is kept.\end{ocamlindent}% +\medbreak +\subsubsection{All mode axes\label{Oxcaml--all-mode-axes}}% +\label{Oxcaml--val-mode_global}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}global : int \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Locality mode (legacy, not rendered).\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_local}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}local : int @ local \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Locality mode.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_aliased}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}aliased : int \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Uniqueness mode (legacy, not rendered).\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_unique}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}unique : int @ unique \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Uniqueness mode.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_many}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}many : int \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Linearity mode (legacy, not rendered).\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_once}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}once : int @ once \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Linearity mode.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_portable}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}portable : int @ portable \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Portability mode.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_shareable}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}shareable : int @ shareable \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Portability mode (intermediate value).\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_nonportable}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}nonportable : int \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Portability mode (legacy, not rendered).\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_uncontended}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}uncontended : int \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Contention mode (legacy, not rendered).\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_shared}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}shared : int @ shared \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Contention mode.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_contended}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}contended : int @ contended \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Contention mode.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_yielding}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}yielding : int @ yielding \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Yield mode.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_unyielding}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}unyielding : int \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Yield mode (legacy, not rendered).\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_forkable}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}forkable : int \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Fork mode (identity on a non-\ocamlinlinecode{local} argument, not rendered).\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_local_forkable}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}local\_\allowbreak{}forkable : int @ local forkable \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Fork mode, rendered because the argument is also \ocamlinlinecode{local}.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_unforkable}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}unforkable : int @ unforkable \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Fork mode.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_local_unforkable}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}local\_\allowbreak{}unforkable : int @ local \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Fork mode (identity for a \ocamlinlinecode{local} argument, not rendered).\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_stateless}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}stateless : int @ stateless \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Statefulness mode.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_observing}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}observing : int @ observing \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Statefulness mode.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_stateful}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}stateful : int \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Statefulness mode (identity when \ocamlinlinecode{portability} is at its default, not rendered).\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_immutable}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}immutable : int @ immutable \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Visibility mode.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_read}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}read : int @ read \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Visibility mode.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_read_write}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}read\_\allowbreak{}write : int \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Visibility mode (legacy, not rendered).\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_static}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}static : int @ static \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Staticity mode.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_dynamic}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}dynamic : int \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}Staticity mode (legacy, not rendered).\end{ocamlindent}% +\medbreak +\subsubsection{Cross-axis suppression\label{Oxcaml--cross-axis-suppression}}% +Some axes have a default value that is implied by another axis; the implied value is suppressed when rendering. + +\label{Oxcaml--val-mode_local_yielding}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}local\_\allowbreak{}yielding : int @ local \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}\ocamlinlinecode{yielding} is the default for \ocamlinlinecode{local}, so it is not rendered.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_local_unyielding}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}local\_\allowbreak{}unyielding : int @ local unyielding \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}\ocamlinlinecode{unyielding} is non-default for \ocamlinlinecode{local}, so it is rendered.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_immutable_contended}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}immutable\_\allowbreak{}contended : int @ immutable \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}\ocamlinlinecode{contended} is the default for \ocamlinlinecode{immutable}, so it is not rendered.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_immutable_uncontended}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}immutable\_\allowbreak{}uncontended : int @ uncontended immutable \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}\ocamlinlinecode{uncontended} is non-default for \ocamlinlinecode{immutable}, so it is rendered.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_stateless_portable}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}stateless\_\allowbreak{}portable : int @ stateless \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}\ocamlinlinecode{portable} is the default for \ocamlinlinecode{stateless}, so it is not rendered.\end{ocamlindent}% +\medbreak +\label{Oxcaml--val-mode_stateful_portable}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}stateful\_\allowbreak{}portable : int @ portable \ocamltag{arrow}{$\rightarrow$} unit}\begin{ocamlindent}\ocamlinlinecode{portable} is non-default for \ocamlinlinecode{stateful}, so it is rendered.\end{ocamlindent}% +\medbreak +\subsubsection{Modes in type definitions\label{Oxcaml--modes-in-type-definitions}}% +\label{Oxcaml--type-mode_alias}\ocamlcodefragment{\ocamltag{keyword}{type} mode\_\allowbreak{}alias = int @ local \ocamltag{arrow}{$\rightarrow$} int}\begin{ocamlindent}Type alias for an arrow with a mode on its argument.\end{ocamlindent}% +\medbreak +\label{Oxcaml--type-mode_record}\ocamlcodefragment{\ocamltag{keyword}{type} mode\_\allowbreak{}record = \{}\\ +\begin{ocamltabular}{p{0.500\textwidth}p{0.500\textwidth}}\ocamlinlinecode{fn : int @ local \ocamltag{arrow}{$\rightarrow$} int;\allowbreak{}}\label{Oxcaml--type-mode_record.fn}& Record field whose type is an arrow with a mode.\\ +\ocamlinlinecode{fn\_\allowbreak{}both : int @ local \ocamltag{arrow}{$\rightarrow$} int @ local;\allowbreak{}}\label{Oxcaml--type-mode_record.fn_both}& Arrow field with modes on both sides.\\ +\ocamlinlinecode{\ocamltag{keyword}{mutable} mfn : int @ local \ocamltag{arrow}{$\rightarrow$} int;\allowbreak{}}\label{Oxcaml--type-mode_record.mfn}& Mutable arrow field with a mode.\\ +\end{ocamltabular}% +\\ +\ocamlcodefragment{\}}\\ +\label{Oxcaml--type-mode_cstr}\ocamlcodefragment{\ocamltag{keyword}{type} mode\_\allowbreak{}cstr = }\\ +\begin{ocamltabular}{p{0.500\textwidth}p{0.500\textwidth}}\ocamlcodefragment{| \ocamltag{constructor}{Mc\_\allowbreak{}arrow} \ocamltag{keyword}{of} int @ local \ocamltag{arrow}{$\rightarrow$} int}\label{Oxcaml--type-mode_cstr.Mc_arrow}& Constructor argument is a parenthesized arrow with a mode.\\ +\ocamlcodefragment{| \ocamltag{constructor}{Mc\_\allowbreak{}nested} \ocamltag{keyword}{of} (int @ local \ocamltag{arrow}{$\rightarrow$} int) \ocamltag{arrow}{$\rightarrow$} unit}\label{Oxcaml--type-mode_cstr.Mc_nested}& Nested arrow: higher-order with a mode on the inner argument.\\ +\ocamlcodefragment{| \ocamltag{constructor}{Mc\_\allowbreak{}gadt} : (\ocamltag{type-var}{'a} @ once \ocamltag{arrow}{$\rightarrow$} \ocamltag{type-var}{'a}) \ocamltag{arrow}{$\rightarrow$} \hyperref[Oxcaml--type-mode_cstr]{\ocamlinlinecode{mode\_\allowbreak{}cstr}}}\label{Oxcaml--type-mode_cstr.Mc_gadt}& GADT constructor\\ +\end{ocamltabular}% +\\ \input{Oxcaml.M1.tex} diff --git a/test/generators/latex/Oxcaml_impl.tex b/test/generators/latex/Oxcaml_impl.tex index 149a39e20c..ef632140c6 100644 --- a/test/generators/latex/Oxcaml_impl.tex +++ b/test/generators/latex/Oxcaml_impl.tex @@ -6,5 +6,43 @@ \section{Module \ocamlinlinecode{Oxcaml\_\allowbreak{}impl}}\label{Oxcaml_impl}% \label{Oxcaml_impl--module-Including}\ocamlcodefragment{\ocamltag{keyword}{module} \hyperref[Oxcaml_impl-Including]{\ocamlinlinecode{Including}}}\label{Oxcaml_impl-Including}\ocamlcodefragment{ : \ocamltag{keyword}{sig}}\begin{ocamlindent}\ocamltag{keyword}{include} \ocamltag{keyword}{module} \ocamltag{keyword}{type} \ocamltag{keyword}{of} \ocamltag{keyword}{struct} \ocamltag{keyword}{include} \hyperref[Oxcaml_impl-To_be_included]{\ocamlinlinecode{To\_\allowbreak{}be\_\allowbreak{}included}} \ocamltag{keyword}{end}\label{Oxcaml_impl-Including--val-add}\ocamlcodefragment{\ocamltag{keyword}{val} add : bool \ocamltag{arrow}{$\rightarrow$} int \ocamltag{arrow}{$\rightarrow$} int \ocamltag{arrow}{$\rightarrow$} int [@@zero\_\allowbreak{}alloc]}\\ \end{ocamlindent}% \ocamlcodefragment{\ocamltag{keyword}{end}}\\ +\label{Oxcaml_impl--type-opaque}\ocamlcodefragment{\ocamltag{keyword}{type} opaque}\\ +\subsection{Modalities\label{Oxcaml_impl--modalities}}% +\subsubsection{Modalities on record fields\label{Oxcaml_impl--modalities-on-record-fields}}% +\label{Oxcaml_impl--type-modalities_record}\ocamlcodefragment{\ocamltag{keyword}{type} modalities\_\allowbreak{}record = \{}\\ +\begin{ocamltabular}{p{0.500\textwidth}p{0.500\textwidth}}\ocamlinlinecode{f\_\allowbreak{}global : \hyperref[Oxcaml_impl--type-opaque]{\ocamlinlinecode{opaque}} @@ global;\allowbreak{}}\label{Oxcaml_impl--type-modalities_record.f_global}& Locality modality.\\ +\ocamlinlinecode{f\_\allowbreak{}portable : \hyperref[Oxcaml_impl--type-opaque]{\ocamlinlinecode{opaque}} @@ portable;\allowbreak{}}\label{Oxcaml_impl--type-modalities_record.f_portable}& Portability modality.\\ +\ocamlinlinecode{f\_\allowbreak{}multi : \hyperref[Oxcaml_impl--type-opaque]{\ocamlinlinecode{opaque}} @@ global portable;\allowbreak{}}\label{Oxcaml_impl--type-modalities_record.f_multi}& Multiple modalities.\\ +\ocamlinlinecode{f\_\allowbreak{}plain : \hyperref[Oxcaml_impl--type-opaque]{\ocamlinlinecode{opaque}};\allowbreak{}}\label{Oxcaml_impl--type-modalities_record.f_plain}& No modality, for reference.\\ +\end{ocamltabular}% +\\ +\ocamlcodefragment{\}}\\ +\subsubsection{Modalities on constructor arguments\label{Oxcaml_impl--modalities-on-constructor-arguments}}% +\label{Oxcaml_impl--type-modalities_variant}\ocamlcodefragment{\ocamltag{keyword}{type} modalities\_\allowbreak{}variant = }\\ +\begin{ocamltabular}{p{0.500\textwidth}p{0.500\textwidth}}\ocamlcodefragment{| \ocamltag{constructor}{A} \ocamltag{keyword}{of} string @@ global}\label{Oxcaml_impl--type-modalities_variant.A}& Constructor argument with \ocamlinlinecode{global} modality.\\ +\ocamlcodefragment{| \ocamltag{constructor}{B} \ocamltag{keyword}{of} int \ocamltag{arrow}{$\rightarrow$} int @@ portable}\label{Oxcaml_impl--type-modalities_variant.B}& Function constructor argument with modality.\\ +\ocamlcodefragment{| \ocamltag{constructor}{C} \ocamltag{keyword}{of} int @@ portable * string @@ global}\label{Oxcaml_impl--type-modalities_variant.C}& Per-element modalities in a constructor tuple.\\ +\end{ocamltabular}% +\\ +\subsection{Modes\label{Oxcaml_impl--modes}}% +\subsubsection{Modes in type definitions\label{Oxcaml_impl--modes-in-type-definitions}}% +\label{Oxcaml_impl--type-mode_alias}\ocamlcodefragment{\ocamltag{keyword}{type} mode\_\allowbreak{}alias = int @ local \ocamltag{arrow}{$\rightarrow$} int}\begin{ocamlindent}Type alias for an arrow with a mode on its argument.\end{ocamlindent}% +\medbreak +\label{Oxcaml_impl--type-mode_record}\ocamlcodefragment{\ocamltag{keyword}{type} mode\_\allowbreak{}record = \{}\\ +\begin{ocamltabular}{p{0.500\textwidth}p{0.500\textwidth}}\ocamlinlinecode{fn : int @ local \ocamltag{arrow}{$\rightarrow$} int;\allowbreak{}}\label{Oxcaml_impl--type-mode_record.fn}& Field whose type is an arrow with a mode.\\ +\ocamlinlinecode{fn\_\allowbreak{}both : int @ local \ocamltag{arrow}{$\rightarrow$} int @ local;\allowbreak{}}\label{Oxcaml_impl--type-mode_record.fn_both}& Modes on both sides.\\ +\end{ocamltabular}% +\\ +\ocamlcodefragment{\}}\\ +\label{Oxcaml_impl--type-mode_cstr}\ocamlcodefragment{\ocamltag{keyword}{type} mode\_\allowbreak{}cstr = }\\ +\begin{ocamltabular}{p{0.500\textwidth}p{0.500\textwidth}}\ocamlcodefragment{| \ocamltag{constructor}{Mc\_\allowbreak{}arrow} \ocamltag{keyword}{of} int @ local \ocamltag{arrow}{$\rightarrow$} int}\label{Oxcaml_impl--type-mode_cstr.Mc_arrow}& Constructor argument is a parenthesized arrow with a mode.\\ +\ocamlcodefragment{| \ocamltag{constructor}{Mc\_\allowbreak{}nested} \ocamltag{keyword}{of} (int @ local \ocamltag{arrow}{$\rightarrow$} int) \ocamltag{arrow}{$\rightarrow$} unit}\label{Oxcaml_impl--type-mode_cstr.Mc_nested}& Nested arrow: higher-order with a mode on the inner argument.\\ +\end{ocamltabular}% +\\ +\subsubsection{Modes on values\label{Oxcaml_impl--modes-on-values}}% +\label{Oxcaml_impl--val-mode_arg}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}arg : int @ local \ocamltag{arrow}{$\rightarrow$} int}\begin{ocamlindent}Mode on a function argument, via a type annotation.\end{ocamlindent}% +\medbreak +\label{Oxcaml_impl--val-mode_multi}\ocamlcodefragment{\ocamltag{keyword}{val} mode\_\allowbreak{}multi : string @ local once \ocamltag{arrow}{$\rightarrow$} string @ local once}\begin{ocamlindent}Multiple modes on argument and return.\end{ocamlindent}% +\medbreak diff --git a/test/generators/man/Oxcaml.3o b/test/generators/man/Oxcaml.3o index ada6bbb18b..23cf7db859 100644 --- a/test/generators/man/Oxcaml.3o +++ b/test/generators/man/Oxcaml.3o @@ -681,4 +681,433 @@ Module with portable modality\. The modality is applied to all value members of .ti +2 contended modality applied to all definitions in the module, except the ones which have already specified this axis\. .nf +.sp +.in 3 +\fB15 Modes\fR +.in +.sp +\f[CB]val\fR mode_arg : int @ local \f[CB]\->\fR int +.fi +.br +.ti +2 +Mode on a function argument\. +.nf +.sp +\f[CB]val\fR mode_ret : int \f[CB]\->\fR int @ local +.fi +.br +.ti +2 +Mode on a function return\. +.nf +.sp +\f[CB]val\fR mode_both : int @ local \f[CB]\->\fR int @ local +.fi +.br +.ti +2 +Modes on both argument and return\. +.nf +.sp +\f[CB]val\fR mode_multi : string @ local once \f[CB]\->\fR string @ local unique +.fi +.br +.ti +2 +Multiple modes on argument and return\. +.nf +.sp +\f[CB]val\fR mode_multi_flipped : string @ local once \f[CB]\->\fR string @ local unique +.fi +.br +.ti +2 +Same as mode_multi, to show that modes order is normalized\. +.nf +.sp +\f[CB]val\fR mode_labeled : \f[CB]x\fR:int @ local \f[CB]\->\fR int +.fi +.br +.ti +2 +Mode on a labeled argument\. +.nf +.sp +\f[CB]val\fR mode_optional : \f[CB]?x\fR:int @ local \f[CB]\->\fR unit \f[CB]\->\fR int +.fi +.br +.ti +2 +Mode on an optional argument\. +.nf +.sp +\f[CB]val\fR mode_higher_order : (\f[CB]'a\fR \f[CB]\->\fR \f[CB]'b\fR) @ local \f[CB]\->\fR \f[CB]'a\fR \f[CB]\->\fR \f[CB]'b\fR +.fi +.br +.ti +2 +Mode on a higher-order function argument\. +.nf +.sp +\f[CB]val\fR mode_arrow_result : int \f[CB]\->\fR (int \f[CB]\->\fR int) @ local +.fi +.br +.ti +2 +Mode on a result that is itself an arrow\. The arrow must be parenthesized so the mode does not appear to bind to the inner return type\. +.nf +.sp +.in 4 +\fB15\.1 Curry-implied result modes\fR +.in +.sp +.fi +Closing over an argument constrains the partial-application closure across several axes, not just locality\. When the result mode is the one currying implies from the argument, it is suppressed (as the compiler does)\. +.nf +.sp +\f[CB]val\fR curry_once : (int \f[CB]\->\fR int) @ once \f[CB]\->\fR int \f[CB]\->\fR int +.fi +.br +.ti +2 +once argument: the implied once result mode is suppressed\. +.nf +.sp +\f[CB]val\fR curry_portable : (int \f[CB]\->\fR int) @ portable \f[CB]\->\fR int \f[CB]\->\fR int +.fi +.br +.ti +2 +portable argument: the implied result mode is suppressed\. +.nf +.sp +\f[CB]val\fR curry_contended : (int \f[CB]\->\fR int) @ contended \f[CB]\->\fR int \f[CB]\->\fR int +.fi +.br +.ti +2 +contended argument: the implied result mode is suppressed\. +.nf +.sp +.in 4 +\fB15\.2 Result modes that are kept\fR +.in +.sp +.fi +A result mode is only suppressed when it is exactly the one currying implies\. An explicit mode on a different axis is kept (and the arrow result is parenthesized)\. +.nf +.sp +\f[CB]val\fR keep_portable : int @ local \f[CB]\->\fR (int \f[CB]\->\fR int) @ portable +.fi +.br +.ti +2 +portable on the result is not implied by a local argument, so it is kept\. +.nf +.sp +\f[CB]val\fR keep_once : int @ local \f[CB]\->\fR (int \f[CB]\->\fR int) @ once +.fi +.br +.ti +2 +once on the result is not implied by a local argument, so it is kept\. +.nf +.sp +\f[CB]val\fR keep_over_once : (int \f[CB]\->\fR int) @ once \f[CB]\->\fR (int \f[CB]\->\fR int) @ portable +.fi +.br +.ti +2 +The curry-implied once is suppressed, but the explicit portable is kept\. +.nf +.sp +\f[CB]val\fR keep_over_local : (int \f[CB]\->\fR int) @ local \f[CB]\->\fR (int \f[CB]\->\fR int) @ portable +.fi +.br +.ti +2 +The curry-implied local is suppressed, but the explicit portable is kept\. +.nf +.sp +\f[CB]val\fR keep_portable_over_nonportable : (int \f[CB]\->\fR int) \f[CB]\->\fR (int \f[CB]\->\fR int) @ portable +.fi +.br +.ti +2 +The nonportable argument mode is the default and dropped, while the explicit portable result, not implied by currying, is kept\. +.nf +.sp +.in 4 +\fB15\.3 All mode axes\fR +.in +.sp +\f[CB]val\fR mode_global : int \f[CB]\->\fR unit +.fi +.br +.ti +2 +Locality mode (legacy, not rendered)\. +.nf +.sp +\f[CB]val\fR mode_local : int @ local \f[CB]\->\fR unit +.fi +.br +.ti +2 +Locality mode\. +.nf +.sp +\f[CB]val\fR mode_aliased : int \f[CB]\->\fR unit +.fi +.br +.ti +2 +Uniqueness mode (legacy, not rendered)\. +.nf +.sp +\f[CB]val\fR mode_unique : int @ unique \f[CB]\->\fR unit +.fi +.br +.ti +2 +Uniqueness mode\. +.nf +.sp +\f[CB]val\fR mode_many : int \f[CB]\->\fR unit +.fi +.br +.ti +2 +Linearity mode (legacy, not rendered)\. +.nf +.sp +\f[CB]val\fR mode_once : int @ once \f[CB]\->\fR unit +.fi +.br +.ti +2 +Linearity mode\. +.nf +.sp +\f[CB]val\fR mode_portable : int @ portable \f[CB]\->\fR unit +.fi +.br +.ti +2 +Portability mode\. +.nf +.sp +\f[CB]val\fR mode_shareable : int @ shareable \f[CB]\->\fR unit +.fi +.br +.ti +2 +Portability mode (intermediate value)\. +.nf +.sp +\f[CB]val\fR mode_nonportable : int \f[CB]\->\fR unit +.fi +.br +.ti +2 +Portability mode (legacy, not rendered)\. +.nf +.sp +\f[CB]val\fR mode_uncontended : int \f[CB]\->\fR unit +.fi +.br +.ti +2 +Contention mode (legacy, not rendered)\. +.nf +.sp +\f[CB]val\fR mode_shared : int @ shared \f[CB]\->\fR unit +.fi +.br +.ti +2 +Contention mode\. +.nf +.sp +\f[CB]val\fR mode_contended : int @ contended \f[CB]\->\fR unit +.fi +.br +.ti +2 +Contention mode\. +.nf +.sp +\f[CB]val\fR mode_yielding : int @ yielding \f[CB]\->\fR unit +.fi +.br +.ti +2 +Yield mode\. +.nf +.sp +\f[CB]val\fR mode_unyielding : int \f[CB]\->\fR unit +.fi +.br +.ti +2 +Yield mode (legacy, not rendered)\. +.nf +.sp +\f[CB]val\fR mode_forkable : int \f[CB]\->\fR unit +.fi +.br +.ti +2 +Fork mode (identity on a non-local argument, not rendered)\. +.nf +.sp +\f[CB]val\fR mode_local_forkable : int @ local forkable \f[CB]\->\fR unit +.fi +.br +.ti +2 +Fork mode, rendered because the argument is also local\. +.nf +.sp +\f[CB]val\fR mode_unforkable : int @ unforkable \f[CB]\->\fR unit +.fi +.br +.ti +2 +Fork mode\. +.nf +.sp +\f[CB]val\fR mode_local_unforkable : int @ local \f[CB]\->\fR unit +.fi +.br +.ti +2 +Fork mode (identity for a local argument, not rendered)\. +.nf +.sp +\f[CB]val\fR mode_stateless : int @ stateless \f[CB]\->\fR unit +.fi +.br +.ti +2 +Statefulness mode\. +.nf +.sp +\f[CB]val\fR mode_observing : int @ observing \f[CB]\->\fR unit +.fi +.br +.ti +2 +Statefulness mode\. +.nf +.sp +\f[CB]val\fR mode_stateful : int \f[CB]\->\fR unit +.fi +.br +.ti +2 +Statefulness mode (identity when portability is at its default, not rendered)\. +.nf +.sp +\f[CB]val\fR mode_immutable : int @ immutable \f[CB]\->\fR unit +.fi +.br +.ti +2 +Visibility mode\. +.nf +.sp +\f[CB]val\fR mode_read : int @ read \f[CB]\->\fR unit +.fi +.br +.ti +2 +Visibility mode\. +.nf +.sp +\f[CB]val\fR mode_read_write : int \f[CB]\->\fR unit +.fi +.br +.ti +2 +Visibility mode (legacy, not rendered)\. +.nf +.sp +\f[CB]val\fR mode_static : int @ static \f[CB]\->\fR unit +.fi +.br +.ti +2 +Staticity mode\. +.nf +.sp +\f[CB]val\fR mode_dynamic : int \f[CB]\->\fR unit +.fi +.br +.ti +2 +Staticity mode (legacy, not rendered)\. +.nf +.sp +.in 4 +\fB15\.4 Cross-axis suppression\fR +.in +.sp +.fi +Some axes have a default value that is implied by another axis; the implied value is suppressed when rendering\. +.nf +.sp +\f[CB]val\fR mode_local_yielding : int @ local \f[CB]\->\fR unit +.fi +.br +.ti +2 +yielding is the default for local, so it is not rendered\. +.nf +.sp +\f[CB]val\fR mode_local_unyielding : int @ local unyielding \f[CB]\->\fR unit +.fi +.br +.ti +2 +unyielding is non-default for local, so it is rendered\. +.nf +.sp +\f[CB]val\fR mode_immutable_contended : int @ immutable \f[CB]\->\fR unit +.fi +.br +.ti +2 +contended is the default for immutable, so it is not rendered\. +.nf +.sp +\f[CB]val\fR mode_immutable_uncontended : int @ uncontended immutable \f[CB]\->\fR unit +.fi +.br +.ti +2 +uncontended is non-default for immutable, so it is rendered\. +.nf +.sp +\f[CB]val\fR mode_stateless_portable : int @ stateless \f[CB]\->\fR unit +.fi +.br +.ti +2 +portable is the default for stateless, so it is not rendered\. +.nf +.sp +\f[CB]val\fR mode_stateful_portable : int @ portable \f[CB]\->\fR unit +.fi +.br +.ti +2 +portable is non-default for stateful, so it is rendered\. +.nf +.sp +.in 4 +\fB15\.5 Modes in type definitions\fR +.in +.sp +\f[CB]type\fR mode_alias = int @ local \f[CB]\->\fR int +.fi +.br +.ti +2 +Type alias for an arrow with a mode on its argument\. +.nf +.sp +\f[CB]type\fR mode_record = { +.br +.ti +2 +fn : int @ local \f[CB]\->\fR int; +.br +.ti +4 +(* Record field whose type is an arrow with a mode\. *) +.br +.ti +2 +fn_both : int @ local \f[CB]\->\fR int @ local; +.br +.ti +4 +(* Arrow field with modes on both sides\. *) +.br +.ti +2 +\f[CB]mutable\fR mfn : int @ local \f[CB]\->\fR int; +.br +.ti +4 +(* Mutable arrow field with a mode\. *) +.br +} +.sp +\f[CB]type\fR mode_cstr = +.br +.ti +2 +| \f[CB]Mc_arrow\fR \f[CB]of\fR int @ local \f[CB]\->\fR int +.br +.ti +4 +(* Constructor argument is a parenthesized arrow with a mode\. *) +.br +.ti +2 +| \f[CB]Mc_nested\fR \f[CB]of\fR (int @ local \f[CB]\->\fR int) \f[CB]\->\fR unit +.br +.ti +4 +(* Nested arrow: higher-order with a mode on the inner argument\. *) +.br +.ti +2 +| \f[CB]Mc_gadt\fR : (\f[CB]'a\fR @ once \f[CB]\->\fR \f[CB]'a\fR) \f[CB]\->\fR mode_cstr +.br +.ti +4 +(* GADT constructor *) +.br diff --git a/test/generators/man/Oxcaml_impl.3o b/test/generators/man/Oxcaml_impl.3o index c73fcd111f..2db71c4dbb 100644 --- a/test/generators/man/Oxcaml_impl.3o +++ b/test/generators/man/Oxcaml_impl.3o @@ -16,3 +16,131 @@ Oxcaml_impl \f[CB]module\fR To_be_included : \f[CB]sig\fR \.\.\. \f[CB]end\fR .sp \f[CB]module\fR Including : \f[CB]sig\fR \.\.\. \f[CB]end\fR +.sp +\f[CB]type\fR opaque +.sp +.in 3 +\fB1 Modalities\fR +.in +.sp +.in 4 +\fB1\.1 Modalities on record fields\fR +.in +.sp +\f[CB]type\fR modalities_record = { +.br +.ti +2 +f_global : opaque @@ global; +.br +.ti +4 +(* Locality modality\. *) +.br +.ti +2 +f_portable : opaque @@ portable; +.br +.ti +4 +(* Portability modality\. *) +.br +.ti +2 +f_multi : opaque @@ global portable; +.br +.ti +4 +(* Multiple modalities\. *) +.br +.ti +2 +f_plain : opaque; +.br +.ti +4 +(* No modality, for reference\. *) +.br +} +.sp +.in 4 +\fB1\.2 Modalities on constructor arguments\fR +.in +.sp +\f[CB]type\fR modalities_variant = +.br +.ti +2 +| \f[CB]A\fR \f[CB]of\fR string @@ global +.br +.ti +4 +(* Constructor argument with global modality\. *) +.br +.ti +2 +| \f[CB]B\fR \f[CB]of\fR int \f[CB]\->\fR int @@ portable +.br +.ti +4 +(* Function constructor argument with modality\. *) +.br +.ti +2 +| \f[CB]C\fR \f[CB]of\fR int @@ portable * string @@ global +.br +.ti +4 +(* Per-element modalities in a constructor tuple\. *) +.br +.sp +.in 3 +\fB2 Modes\fR +.in +.sp +.in 4 +\fB2\.1 Modes in type definitions\fR +.in +.sp +\f[CB]type\fR mode_alias = int @ local \f[CB]\->\fR int +.fi +.br +.ti +2 +Type alias for an arrow with a mode on its argument\. +.nf +.sp +\f[CB]type\fR mode_record = { +.br +.ti +2 +fn : int @ local \f[CB]\->\fR int; +.br +.ti +4 +(* Field whose type is an arrow with a mode\. *) +.br +.ti +2 +fn_both : int @ local \f[CB]\->\fR int @ local; +.br +.ti +4 +(* Modes on both sides\. *) +.br +} +.sp +\f[CB]type\fR mode_cstr = +.br +.ti +2 +| \f[CB]Mc_arrow\fR \f[CB]of\fR int @ local \f[CB]\->\fR int +.br +.ti +4 +(* Constructor argument is a parenthesized arrow with a mode\. *) +.br +.ti +2 +| \f[CB]Mc_nested\fR \f[CB]of\fR (int @ local \f[CB]\->\fR int) \f[CB]\->\fR unit +.br +.ti +4 +(* Nested arrow: higher-order with a mode on the inner argument\. *) +.br +.sp +.in 4 +\fB2\.2 Modes on values\fR +.in +.sp +\f[CB]val\fR mode_arg : int @ local \f[CB]\->\fR int +.fi +.br +.ti +2 +Mode on a function argument, via a type annotation\. +.nf +.sp +\f[CB]val\fR mode_multi : string @ local once \f[CB]\->\fR string @ local once +.fi +.br +.ti +2 +Multiple modes on argument and return\. +.nf + diff --git a/test/generators/markdown/Oxcaml.md b/test/generators/markdown/Oxcaml.md index 20ba08da3f..d1f6dad452 100644 --- a/test/generators/markdown/Oxcaml.md +++ b/test/generators/markdown/Oxcaml.md @@ -345,3 +345,291 @@ Module with `portable` modality. The modality is applied to all value members of module M3 : sig ... end ``` `contended` modality applied to all definitions in the module, except the ones which have already specified this axis. + + +## Modes + +```ocaml +val mode_arg : int @ local -> int +``` +Mode on a function argument. + +```ocaml +val mode_ret : int -> int @ local +``` +Mode on a function return. + +```ocaml +val mode_both : int @ local -> int @ local +``` +Modes on both argument and return. + +```ocaml +val mode_multi : string @ local once -> string @ local unique +``` +Multiple modes on argument and return. + +```ocaml +val mode_multi_flipped : string @ local once -> string @ local unique +``` +Same as `mode_multi`, to show that modes order is normalized. + +```ocaml +val mode_labeled : x:int @ local -> int +``` +Mode on a labeled argument. + +```ocaml +val mode_optional : ?x:int @ local -> unit -> int +``` +Mode on an optional argument. + +```ocaml +val mode_higher_order : ('a -> 'b) @ local -> 'a -> 'b +``` +Mode on a higher-order function argument. + +```ocaml +val mode_arrow_result : int -> (int -> int) @ local +``` +Mode on a result that is itself an arrow. The arrow must be parenthesized so the mode does not appear to bind to the inner return type. + + +### Curry-implied result modes + +Closing over an argument constrains the partial-application closure across several axes, not just locality. When the result mode is the one currying implies from the argument, it is suppressed (as the compiler does). + +```ocaml +val curry_once : (int -> int) @ once -> int -> int +``` +`once` argument: the implied `once` result mode is suppressed. + +```ocaml +val curry_portable : (int -> int) @ portable -> int -> int +``` +`portable` argument: the implied result mode is suppressed. + +```ocaml +val curry_contended : (int -> int) @ contended -> int -> int +``` +`contended` argument: the implied result mode is suppressed. + + +### Result modes that are kept + +A result mode is only suppressed when it is exactly the one currying implies. An explicit mode on a different axis is kept (and the arrow result is parenthesized). + +```ocaml +val keep_portable : int @ local -> (int -> int) @ portable +``` +`portable` on the result is not implied by a `local` argument, so it is kept. + +```ocaml +val keep_once : int @ local -> (int -> int) @ once +``` +`once` on the result is not implied by a `local` argument, so it is kept. + +```ocaml +val keep_over_once : (int -> int) @ once -> (int -> int) @ portable +``` +The curry-implied `once` is suppressed, but the explicit `portable` is kept. + +```ocaml +val keep_over_local : (int -> int) @ local -> (int -> int) @ portable +``` +The curry-implied `local` is suppressed, but the explicit `portable` is kept. + +```ocaml +val keep_portable_over_nonportable : (int -> int) -> (int -> int) @ portable +``` +The `nonportable` argument mode is the default and dropped, while the explicit `portable` result, not implied by currying, is kept. + + +### All mode axes + +```ocaml +val mode_global : int -> unit +``` +Locality mode (legacy, not rendered). + +```ocaml +val mode_local : int @ local -> unit +``` +Locality mode. + +```ocaml +val mode_aliased : int -> unit +``` +Uniqueness mode (legacy, not rendered). + +```ocaml +val mode_unique : int @ unique -> unit +``` +Uniqueness mode. + +```ocaml +val mode_many : int -> unit +``` +Linearity mode (legacy, not rendered). + +```ocaml +val mode_once : int @ once -> unit +``` +Linearity mode. + +```ocaml +val mode_portable : int @ portable -> unit +``` +Portability mode. + +```ocaml +val mode_shareable : int @ shareable -> unit +``` +Portability mode (intermediate value). + +```ocaml +val mode_nonportable : int -> unit +``` +Portability mode (legacy, not rendered). + +```ocaml +val mode_uncontended : int -> unit +``` +Contention mode (legacy, not rendered). + +```ocaml +val mode_shared : int @ shared -> unit +``` +Contention mode. + +```ocaml +val mode_contended : int @ contended -> unit +``` +Contention mode. + +```ocaml +val mode_yielding : int @ yielding -> unit +``` +Yield mode. + +```ocaml +val mode_unyielding : int -> unit +``` +Yield mode (legacy, not rendered). + +```ocaml +val mode_forkable : int -> unit +``` +Fork mode (identity on a non-`local` argument, not rendered). + +```ocaml +val mode_local_forkable : int @ local forkable -> unit +``` +Fork mode, rendered because the argument is also `local`. + +```ocaml +val mode_unforkable : int @ unforkable -> unit +``` +Fork mode. + +```ocaml +val mode_local_unforkable : int @ local -> unit +``` +Fork mode (identity for a `local` argument, not rendered). + +```ocaml +val mode_stateless : int @ stateless -> unit +``` +Statefulness mode. + +```ocaml +val mode_observing : int @ observing -> unit +``` +Statefulness mode. + +```ocaml +val mode_stateful : int -> unit +``` +Statefulness mode (identity when `portability` is at its default, not rendered). + +```ocaml +val mode_immutable : int @ immutable -> unit +``` +Visibility mode. + +```ocaml +val mode_read : int @ read -> unit +``` +Visibility mode. + +```ocaml +val mode_read_write : int -> unit +``` +Visibility mode (legacy, not rendered). + +```ocaml +val mode_static : int @ static -> unit +``` +Staticity mode. + +```ocaml +val mode_dynamic : int -> unit +``` +Staticity mode (legacy, not rendered). + + +### Cross-axis suppression + +Some axes have a default value that is implied by another axis; the implied value is suppressed when rendering. + +```ocaml +val mode_local_yielding : int @ local -> unit +``` +`yielding` is the default for `local`, so it is not rendered. + +```ocaml +val mode_local_unyielding : int @ local unyielding -> unit +``` +`unyielding` is non-default for `local`, so it is rendered. + +```ocaml +val mode_immutable_contended : int @ immutable -> unit +``` +`contended` is the default for `immutable`, so it is not rendered. + +```ocaml +val mode_immutable_uncontended : int @ uncontended immutable -> unit +``` +`uncontended` is non-default for `immutable`, so it is rendered. + +```ocaml +val mode_stateless_portable : int @ stateless -> unit +``` +`portable` is the default for `stateless`, so it is not rendered. + +```ocaml +val mode_stateful_portable : int @ portable -> unit +``` +`portable` is non-default for `stateful`, so it is rendered. + + +### Modes in type definitions + +```ocaml +type mode_alias = int @ local -> int +``` +Type alias for an arrow with a mode on its argument. + +```ocaml +type mode_record = { + fn : int @ local -> int; (* Record field whose type is an arrow with a mode. *) + fn_both : int @ local -> int @ local; (* Arrow field with modes on both sides. *) + mutable mfn : int @ local -> int; (* Mutable arrow field with a mode. *) +} +``` +```ocaml +type mode_cstr = + | Mc_arrow of int @ local -> int (* Constructor argument is a parenthesized arrow with a mode. *) + | Mc_nested of (int @ local -> int) -> unit (* Nested arrow: higher-order with a mode on the inner argument. *) + | Mc_gadt : ('a @ once -> 'a) -> mode_cstr (* GADT constructor *) +``` \ No newline at end of file diff --git a/test/generators/markdown/Oxcaml_impl.md b/test/generators/markdown/Oxcaml_impl.md index f903a30efd..6504572984 100644 --- a/test/generators/markdown/Oxcaml_impl.md +++ b/test/generators/markdown/Oxcaml_impl.md @@ -9,4 +9,64 @@ module To_be_included : sig ... end ``` ```ocaml module Including : sig ... end -``` \ No newline at end of file +``` +```ocaml +type opaque +``` + +## Modalities + + +### Modalities on record fields + +```ocaml +type modalities_record = { + f_global : opaque @@ global; (* Locality modality. *) + f_portable : opaque @@ portable; (* Portability modality. *) + f_multi : opaque @@ global portable; (* Multiple modalities. *) + f_plain : opaque; (* No modality, for reference. *) +} +``` + +### Modalities on constructor arguments + +```ocaml +type modalities_variant = + | A of string @@ global (* Constructor argument with global modality. *) + | B of int -> int @@ portable (* Function constructor argument with modality. *) + | C of int @@ portable * string @@ global (* Per-element modalities in a constructor tuple. *) +``` + +## Modes + + +### Modes in type definitions + +```ocaml +type mode_alias = int @ local -> int +``` +Type alias for an arrow with a mode on its argument. + +```ocaml +type mode_record = { + fn : int @ local -> int; (* Field whose type is an arrow with a mode. *) + fn_both : int @ local -> int @ local; (* Modes on both sides. *) +} +``` +```ocaml +type mode_cstr = + | Mc_arrow of int @ local -> int (* Constructor argument is a parenthesized arrow with a mode. *) + | Mc_nested of (int @ local -> int) -> unit (* Nested arrow: higher-order with a mode on the inner argument. *) +``` + +### Modes on values + +```ocaml +val mode_arg : int @ local -> int +``` +Mode on a function argument, via a type annotation. + +```ocaml +val mode_multi : string @ local once -> string @ local once +``` +Multiple modes on argument and return. diff --git a/test/xref2/classes.t/run.t b/test/xref2/classes.t/run.t index c241e1bb6e..ca0f54b0c8 100644 --- a/test/xref2/classes.t/run.t +++ b/test/xref2/classes.t/run.t @@ -121,36 +121,42 @@ resolve correctly. All of the 'Class' json objects should contain { "Arrow": [ "None", - { - "Class": [ - { - "`Resolved": { - "`ClassType": [ - { - "`Identifier": { - "`Root": [ - "None", - "B" - ] - } - }, - "u" - ] - } - }, - [] - ] - }, - { - "Constr": [ - { - "`Resolved": { - "`CoreType": "unit" - } - }, - [] - ] - } + [ + { + "Class": [ + { + "`Resolved": { + "`ClassType": [ + { + "`Identifier": { + "`Root": [ + "None", + "B" + ] + } + }, + "u" + ] + } + }, + [] + ] + }, + [] + ], + [ + { + "Constr": [ + { + "`Resolved": { + "`CoreType": "unit" + } + }, + [] + ] + }, + [] + ] ] } diff --git a/test/xref2/github_issue_1001.t/run.t b/test/xref2/github_issue_1001.t/run.t index b29f054c0c..880260e22d 100644 --- a/test/xref2/github_issue_1001.t/run.t +++ b/test/xref2/github_issue_1001.t/run.t @@ -14,14 +14,20 @@ We should have an 'Optional' argument (as opposed to a 'RawOptional' one) "type_": { "Arrow": [ { "Some": { "Optional": "optional" } }, - { "Constr": [ { "`Resolved": { "`CoreType": "int" } }, [] ] }, - { - "Arrow": [ - "None", - { "Constr": [ { "`Resolved": { "`CoreType": "unit" } }, [] ] }, - { "Var": "a" } - ] - } + [ { "Constr": [ { "`Resolved": { "`CoreType": "int" } }, [] ] }, [] ], + [ + { + "Arrow": [ + "None", + [ + { "Constr": [ { "`Resolved": { "`CoreType": "unit" } }, [] ] }, + [] + ], + [ { "Var": "a" }, [] ] + ] + }, + [] + ] ] }, "value": "Abstract", @@ -38,25 +44,34 @@ Harder case contains a "RawOptional": "type_": { "Arrow": [ { "Some": { "RawOptional": "optional" } }, - { - "Constr": [ - { - "`Resolved": { - "`Identifier": { - "`Type": [ { "`Root": [ "None", "Test" ] }, "hard" ] + [ + { + "Constr": [ + { + "`Resolved": { + "`Identifier": { + "`Type": [ { "`Root": [ "None", "Test" ] }, "hard" ] + } } - } - }, - [] - ] - }, - { - "Arrow": [ - "None", - { "Constr": [ { "`Resolved": { "`CoreType": "unit" } }, [] ] }, - { "Var": "a" } - ] - } + }, + [] + ] + }, + [] + ], + [ + { + "Arrow": [ + "None", + [ + { "Constr": [ { "`Resolved": { "`CoreType": "unit" } }, [] ] }, + [] + ], + [ { "Var": "a" }, [] ] + ] + }, + [] + ] ] }, "value": "Abstract", diff --git a/test/xref2/github_issue_930.t/run.t b/test/xref2/github_issue_930.t/run.t index 22a97fea5a..dbc59f9502 100644 --- a/test/xref2/github_issue_930.t/run.t +++ b/test/xref2/github_issue_930.t/run.t @@ -30,10 +30,10 @@ S1 is included in S2 and S2 is included in S3: Check that S0_with_creators_base compiles without crashing and has the concat function with simplified types (t -> t): - $ odoc_print test.odocl -r S0_with_creators_base.concat | jq -c '.type_.Arrow[1].Constr[0]' + $ odoc_print test.odocl -r S0_with_creators_base.concat | jq -c '.type_.Arrow[1][0].Constr[0]' {"`Resolved":{"`Identifier":{"`Type":[{"`ModuleType":[{"`Root":["None","Test"]},"S0_with_creators_base"]},"t"]}}} - $ odoc_print test.odocl -r S0_with_creators_base.concat | jq -c '.type_.Arrow[2].Constr[0]' + $ odoc_print test.odocl -r S0_with_creators_base.concat | jq -c '.type_.Arrow[2][0].Constr[0]' {"`Resolved":{"`Identifier":{"`Type":[{"`ModuleType":[{"`Root":["None","Test"]},"S0_with_creators_base"]},"t"]}}} === Test 3: Deeply nested includes ===