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 @@
OxcamlMultiple modes on argument and return.
Same as mode_multi, to show that modes order is
+ normalized.
+
Mode on an optional argument.
Mode on a higher-order function argument.
+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. +
+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). +
+once argument: the implied once result
+ mode is suppressed.
+
portable argument: the implied result mode is
+ suppressed.
+
contended argument: the implied result mode is
+ suppressed.
+
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). +
+portable on the result is not implied by a
+ local argument, so it is kept.
+
once on the result is not implied by a
+ local argument, so it is kept.
+
The curry-implied once is suppressed, but the explicit
+ portable is kept.
+
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.
+
Portability mode (intermediate value).
Contention mode.
Fork mode (identity on a non-local argument, not
+ rendered).
+
Fork mode, rendered because the argument is also local.
+
Fork mode (identity for a local argument, not rendered).
+
Statefulness mode (identity when portability is
+ at its default, not rendered).
+
Some axes have a default value that is implied by another axis; + the implied value is suppressed when rendering. +
+yielding is the default for local,
+ so it is not rendered.
+
unyielding is non-default for local
+ , so it is rendered.
+
contended is the default for immutable
+ , so it is not rendered.
+
uncontended is non-default for immutable
+ , so it is rendered.
+
portable is the default for stateless
+ , so it is not rendered.
+
portable is non-default for stateful
+ , so it is rendered.
+
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
*) +