[Pkg-ocaml-maint-commits] [atdgen] 03/09: Imported Upstream version 1.9.1
Stéphane Glondu
glondu at moszumanska.debian.org
Thu Aug 4 09:37:58 UTC 2016
This is an automated email from the git hooks/post-receive script.
glondu pushed a commit to branch master
in repository atdgen.
commit f74d05850780d750110bb44099b6151f62d16f47
Author: Stephane Glondu <steph at glondu.net>
Date: Thu Aug 4 11:14:57 2016 +0200
Imported Upstream version 1.9.1
---
src/Makefile | 2 +-
src/ag_main.ml | 8 +--
src/ag_ob_emit.ml | 73 ++++++++++++++++-----------
src/ag_oj_emit.ml | 17 +++++--
src/ag_ov_emit.ml | 1 +
src/ag_ox_emit.mli | 32 ++++++++++++
test/test_atdgen_main.ml | 127 ++++++++++++++++++++++++++++++++++++++++++++++-
7 files changed, 223 insertions(+), 37 deletions(-)
diff --git a/src/Makefile b/src/Makefile
index c25441a..328539e 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,4 +1,4 @@
-VERSION = 1.9.0
+VERSION = 1.9.1
ifeq "$(shell ocamlc -config |grep os_type)" "os_type: Win32"
EXE=.exe
else
diff --git a/src/ag_main.ml b/src/ag_main.ml
index 3a87e44..5455c92 100644
--- a/src/ag_main.ml
+++ b/src/ag_main.ml
@@ -54,8 +54,8 @@ let parse_ocaml_version () =
else
None
-let get_default_name_overlap () =
- match parse_ocaml_version () with
+let get_default_name_overlap ocaml_version =
+ match ocaml_version with
| Some (major, minor) when major < 4 -> false
| Some (4, 0) -> false
| _ -> true
@@ -77,7 +77,8 @@ let main () =
let unknown_field_handler = ref None in
let constr_mismatch_handler = ref None in
let type_aliases = ref None in
- let name_overlap = ref (get_default_name_overlap ()) in
+ let ocaml_version = parse_ocaml_version () in
+ let name_overlap = ref (get_default_name_overlap ocaml_version) in
let set_opens s =
let l = Str.split (Str.regexp " *, *\\| +") s in
opens := List.rev_append l !opens
@@ -435,6 +436,7 @@ Recommended usage: %s (-t|-b|-j|-v|-dep|-list) example.atd" Sys.argv.(0) in
~pos_lnum: !pos_lnum
~type_aliases
~force_defaults
+ ~ocaml_version
~name_overlap: !name_overlap
atd_file ocaml_prefix
diff --git a/src/ag_ob_emit.ml b/src/ag_ob_emit.ml
index 71a11f0..ead7476 100644
--- a/src/ag_ob_emit.ml
+++ b/src/ag_ob_emit.ml
@@ -730,7 +730,7 @@ and make_table_writer deref tagged list_kind x =
main
-let study_record deref fields =
+let study_record ~ocaml_version deref fields =
let field_assignments =
List.fold_right (
fun (x, name, default, opt, unwrap) field_assignments ->
@@ -738,7 +738,11 @@ let study_record deref fields =
match default with
None ->
assert (not opt);
- "Obj.magic 0.0"
+ begin match ocaml_version with
+ | Some (maj, min) when (maj > 4 || maj = 4 && min >= 3) ->
+ "Obj.magic (Sys.opaque_identity 0.0)"
+ | _ -> "Obj.magic 0.0"
+ end
| Some s ->
s
in
@@ -890,7 +894,7 @@ let wrap_bodies ~tagged l =
let rec make_reader
- deref ~tagged ?type_annot (x : ob_mapping)
+ deref ~tagged ~ocaml_version ?type_annot (x : ob_mapping)
: Ag_indent.t list =
match x with
`Unit _
@@ -918,7 +922,8 @@ let rec make_reader
Array.to_list (
Array.map
(fun x ->
- `Inline (make_variant_reader deref type_annot tick x)
+ `Inline (make_variant_reader ~ocaml_version
+ deref type_annot tick x)
)
a
)
@@ -937,11 +942,11 @@ let rec make_reader
| `Object ->
error loc "Sorry, OCaml objects are not supported"
);
- let body = make_record_reader deref ~tagged type_annot a o in
+ let body = make_record_reader deref ~ocaml_version ~tagged type_annot a o in
wrap_body ~tagged Bi_io.record_tag body
| `Tuple (loc, a, `Tuple, `Tuple) ->
- let body = make_tuple_reader deref ~tagged a in
+ let body = make_tuple_reader deref ~ocaml_version ~tagged a in
wrap_body ~tagged Bi_io.tuple_tag body
| `List (loc, x, `List o, `List b) ->
@@ -953,7 +958,7 @@ let rec make_reader
in
[
`Line (f ^ " (");
- `Block (make_reader deref ~tagged:false x);
+ `Block (make_reader deref ~ocaml_version ~tagged:false x);
`Line ")";
]
| `Array, `Array ->
@@ -963,12 +968,13 @@ let rec make_reader
in
[
`Line (f ^ " (");
- `Block (make_reader deref ~tagged:false x);
+ `Block (make_reader deref ~ocaml_version ~tagged:false x);
`Line ")";
]
| list_kind, `Table ->
(* Support table format and regular array format *)
- let body1 = make_table_reader deref loc list_kind x in
+ let body1 =
+ make_table_reader ~ocaml_version deref loc list_kind x in
let body2 =
let f =
match list_kind with
@@ -977,7 +983,7 @@ let rec make_reader
in
[
`Line (f ^ " (");
- `Block (make_reader deref ~tagged:false x);
+ `Block (make_reader deref ~tagged:false ~ocaml_version x);
`Line ") ib";
]
in
@@ -996,7 +1002,7 @@ let rec make_reader
`Line "Some (";
`Block [
`Line "(";
- `Block (make_reader deref ~tagged:true x);
+ `Block (make_reader deref ~tagged:true ~ocaml_version x);
`Line ")";
`Block [ `Line "ib"];
];
@@ -1009,7 +1015,7 @@ let rec make_reader
wrap_body ~tagged Bi_io.num_variant_tag body
| `Wrap (loc, x, `Wrap o, `Wrap) ->
- let simple_reader = make_reader deref ~tagged x in
+ let simple_reader = make_reader deref ~tagged ~ocaml_version x in
(match o with
None -> simple_reader
| Some { Ag_ocaml.ocaml_wrap } ->
@@ -1035,7 +1041,7 @@ let rec make_reader
| _ -> assert false
-and make_variant_reader deref type_annot tick x : Ag_indent.t list =
+and make_variant_reader ~ocaml_version deref type_annot tick x : Ag_indent.t list =
let o =
match x.var_arepr, x.var_brepr with
`Variant o, `Variant -> o
@@ -1054,7 +1060,7 @@ and make_variant_reader deref type_annot tick x : Ag_indent.t list =
`Block [
`Block [
`Line "(";
- `Block (make_reader deref ~tagged:true v);
+ `Block (make_reader deref ~tagged:true ~ocaml_version v);
`Line ") ib";
];
`Line (sprintf ")%s)" (Ag_ox_emit.insert_annot type_annot));
@@ -1062,11 +1068,11 @@ and make_variant_reader deref type_annot tick x : Ag_indent.t list =
]
and make_record_reader
- deref ~tagged type_annot
+ deref ~ocaml_version ~tagged type_annot
a record_kind =
let fields = get_fields deref a in
let init_fields, init_bits, set_bit, check_bits, create_record =
- study_record deref fields
+ study_record ~ocaml_version deref fields
in
let body =
@@ -1090,7 +1096,7 @@ and make_record_reader
let read_value =
[
`Line "(";
- `Block (make_reader deref ~tagged:true f_value);
+ `Block (make_reader deref ~tagged:true ~ocaml_version f_value);
`Line ") ib"
]
in
@@ -1128,7 +1134,7 @@ and make_record_reader
]
-and make_tuple_reader deref ~tagged a =
+and make_tuple_reader deref ~tagged ~ocaml_version a =
let cells =
Array.map (
fun x ->
@@ -1157,7 +1163,9 @@ and make_tuple_reader deref ~tagged a =
Array.to_list (
Array.mapi (
fun i (x, default) ->
- let read_value = make_reader deref ~tagged:true x.cel_value in
+ let read_value =
+ make_reader deref ~ocaml_version ~tagged:true
+ x.cel_value in
let get_value =
if i < min_length then
[
@@ -1212,7 +1220,7 @@ and make_tuple_reader deref ~tagged a =
]
-and make_table_reader deref loc list_kind x =
+and make_table_reader deref ~ocaml_version loc list_kind x =
let empty_list, to_list =
match list_kind with
`List -> "[ ]", (fun s -> "Array.to_list " ^ s)
@@ -1231,7 +1239,7 @@ and make_table_reader deref loc list_kind x =
error loc "Not a list or array of records"
in
let init_fields, init_bits, set_bit, check_bits, create_record =
- study_record deref fields
+ study_record ~ocaml_version deref fields
in
let cases =
Array.to_list (
@@ -1244,7 +1252,7 @@ and make_table_reader deref loc list_kind x =
`Line "let read =";
`Block [
`Line "(";
- `Block (make_reader deref ~tagged:false x.f_value);
+ `Block (make_reader deref ~tagged:false ~ocaml_version x.f_value);
`Line ")";
`Block [ `Line "tag" ]
];
@@ -1339,7 +1347,8 @@ let make_ocaml_biniou_writer ~original_types deref is_rec let1 let2 def =
]
]
-let make_ocaml_biniou_reader ~original_types deref is_rec let1 let2 def =
+let make_ocaml_biniou_reader ~original_types ~ocaml_version
+ deref is_rec let1 let2 def =
let x = match def.def_value with None -> assert false | Some x -> x in
let name = def.def_name in
let type_constraint = Ag_ox_emit.get_type_constraint ~original_types def in
@@ -1352,8 +1361,9 @@ let make_ocaml_biniou_reader ~original_types deref is_rec let1 let2 def =
| true -> Some type_constraint
| false -> None
in
- let get_reader_expr = make_reader deref ~tagged:false ?type_annot x in
- let read_expr = make_reader deref ~tagged:true ?type_annot x in
+ let get_reader_expr =
+ make_reader deref ~tagged:false ~ocaml_version ?type_annot x in
+ let read_expr = make_reader deref ~tagged:true ~ocaml_version ?type_annot x in
let eta_expand1 = is_rec && not (Ag_ox_emit.is_function get_reader_expr) in
let eta_expand2 = is_rec && not (Ag_ox_emit.is_function read_expr) in
let extra_param1, extra_args1 =
@@ -1389,7 +1399,8 @@ let get_let ~is_rec ~is_first =
else "let", "let"
else "and", "and"
-let make_ocaml_biniou_impl ~with_create ~original_types buf deref defs =
+let make_ocaml_biniou_impl ~with_create ~original_types ~ocaml_version
+ buf deref defs =
let ll =
List.map (
@@ -1407,7 +1418,7 @@ let make_ocaml_biniou_impl ~with_create ~original_types buf deref defs =
map (
fun is_first def ->
let let1, let2 = get_let ~is_rec ~is_first in
- make_ocaml_biniou_reader
+ make_ocaml_biniou_reader ~ocaml_version
~original_types deref is_rec let1 let2 def
) l
in
@@ -1456,6 +1467,7 @@ let make_mli
let make_ml
~header ~opens ~with_typedefs ~with_create ~with_fundefs ~original_types
+ ~ocaml_version
ocaml_typedefs deref defs =
let buf = Buffer.create 1000 in
bprintf buf "%s\n" header;
@@ -1465,7 +1477,9 @@ let make_ml
if with_typedefs && with_fundefs then
bprintf buf "\n";
if with_fundefs then
- make_ocaml_biniou_impl ~with_create ~original_types buf deref defs;
+ make_ocaml_biniou_impl
+ ~with_create ~original_types ~ocaml_version
+ buf deref defs;
Buffer.contents buf
let make_ocaml_files
@@ -1479,6 +1493,7 @@ let make_ocaml_files
~type_aliases
~force_defaults
~name_overlap
+ ~ocaml_version
~pp_convs
atd_file out =
let ((head, m0), _) =
@@ -1528,7 +1543,7 @@ let make_ocaml_files
in
let ml =
make_ml ~header ~opens ~with_typedefs ~with_create ~with_fundefs
- ~original_types ocaml_typedefs
+ ~original_types ~ocaml_version ocaml_typedefs
(Ag_mapping.make_deref defs) defs
in
Ag_ox_emit.write_ocaml out mli ml
diff --git a/src/ag_oj_emit.ml b/src/ag_oj_emit.ml
index 6463fec..244a78c 100644
--- a/src/ag_oj_emit.ml
+++ b/src/ag_oj_emit.ml
@@ -34,6 +34,8 @@ type param = {
preprocess_input : string option;
(* intended for UTF-8 validation *)
+ ocaml_version: (int * int) option;
+
}
@@ -739,8 +741,11 @@ and make_record_writer p a record_kind =
`Line "Bi_outbuf.add_char ob '}';";
]
-let study_record deref fields =
- let unset_field_value = "Obj.magic 0.0" in
+let study_record p fields =
+ let unset_field_value = match p.ocaml_version with
+ | Some (maj, min) when (maj > 4 || maj = 4 && min >= 3) ->
+ "Obj.magic (Sys.opaque_identity 0.0)"
+ | _ -> "Obj.magic 0.0" in
let _, field_assignments =
Array.fold_right (fun field (i, field_assignments) ->
@@ -1233,7 +1238,7 @@ and make_deconstructed_reader p loc fields set_bit =
and make_record_reader p type_annot loc a record_kind =
let fields = get_fields p a in
let init_fields, init_bits, set_bit, check_bits, create_record =
- study_record p.deref fields
+ study_record p fields
in
let read_field =
@@ -1617,6 +1622,7 @@ let get_let ~is_rec ~is_first =
let make_ocaml_json_impl
~std ~unknown_field_handler ~constr_mismatch_handler
~with_create ~force_defaults ~preprocess_input ~original_types
+ ~ocaml_version
buf deref defs =
let p = {
deref = deref;
@@ -1625,6 +1631,7 @@ let make_ocaml_json_impl
constr_mismatch_handler = constr_mismatch_handler;
force_defaults = force_defaults;
preprocess_input;
+ ocaml_version;
} in
let ll =
List.map (
@@ -1692,6 +1699,7 @@ let make_ml
~header ~opens ~with_typedefs ~with_create ~with_fundefs
~std ~unknown_field_handler ~constr_mismatch_handler
~force_defaults ~preprocess_input ~original_types
+ ~ocaml_version
ocaml_typedefs deref defs =
let buf = Buffer.create 1000 in
bprintf buf "%s\n" header;
@@ -1704,6 +1712,7 @@ let make_ml
make_ocaml_json_impl
~std ~unknown_field_handler ~constr_mismatch_handler
~with_create ~force_defaults ~preprocess_input ~original_types
+ ~ocaml_version
buf deref defs;
Buffer.contents buf
@@ -1722,6 +1731,7 @@ let make_ocaml_files
~force_defaults
~preprocess_input
~name_overlap
+ ~ocaml_version
~pp_convs
atd_file out =
let ((head, m0), _) =
@@ -1772,6 +1782,7 @@ let make_ocaml_files
make_ml ~header ~opens ~with_typedefs ~with_create ~with_fundefs
~std ~unknown_field_handler ~constr_mismatch_handler
~force_defaults ~preprocess_input ~original_types
+ ~ocaml_version
ocaml_typedefs (Ag_mapping.make_deref defs) defs
in
Ag_ox_emit.write_ocaml out mli ml
diff --git a/src/ag_ov_emit.ml b/src/ag_ov_emit.ml
index de9d803..154ae04 100644
--- a/src/ag_ov_emit.ml
+++ b/src/ag_ov_emit.ml
@@ -473,6 +473,7 @@ let make_ocaml_files
~type_aliases
~force_defaults
~name_overlap
+ ~ocaml_version
~pp_convs
atd_file out =
let ((head, m0), _) =
diff --git a/src/ag_ox_emit.mli b/src/ag_ox_emit.mli
new file mode 100644
index 0000000..ea3d1d3
--- /dev/null
+++ b/src/ag_ox_emit.mli
@@ -0,0 +1,32 @@
+type 'a expr = (Ag_ocaml.atd_ocaml_repr, 'a) Ag_mapping.mapping
+type 'a def = (Ag_ocaml.atd_ocaml_repr, 'a) Ag_mapping.def
+type 'a grouped_defs = (bool * 'a def list) list
+
+val get_full_type_name : (_, _) Ag_mapping.def -> string
+
+val is_exportable : (_, _) Ag_mapping.def -> bool
+
+val make_record_creator
+ : ((Ag_ocaml.atd_ocaml_repr, 'a) Ag_mapping.mapping
+ -> (Ag_ocaml.atd_ocaml_repr, 'b) Ag_mapping.mapping)
+ -> (Ag_ocaml.atd_ocaml_repr, 'a) Ag_mapping.def
+ -> string * string
+
+val opt_annot : string option -> string -> string
+
+val opt_annot_def : string option -> string -> string
+
+val insert_annot : string option -> string
+
+val get_type_constraint
+ : original_types:(string, string * int) Hashtbl.t
+ -> ('a, 'b) Ag_mapping.def
+ -> string
+
+val is_function : Ag_indent.t list -> bool
+
+val needs_type_annot : _ expr -> bool
+
+val check : _ grouped_defs -> unit
+
+val write_ocaml : [< `Files of string | `Stdout ] -> string -> string -> unit
diff --git a/test/test_atdgen_main.ml b/test/test_atdgen_main.ml
index 1a475a5..732f32b 100644
--- a/test/test_atdgen_main.ml
+++ b/test/test_atdgen_main.ml
@@ -63,10 +63,135 @@ let test_missing_tuple = (123, 4.56)
type internals1 = { int : int }
type internals2 = { float : float }
+ (* Obj.magic 0.0, opaque_identity, and record fields
+
+ Instead of using options (which may allocate), atdgen uses
+ a default value for references that denote record fields that may
+ not yet have been deserialized.
+
+ For example, consider the following example in the test.ml
+ generated code:
+
+type extended = {
+ b0x: int;
+ b1x: bool;
+ b2x: string;
+ b3x: string option;
+ b4x: string option;
+ b5x: float
+}
+
+let get_extended_reader = (
+ fun tag ->
+ if tag <> 21 then Ag_ob_run.read_error () else
+ fun ib ->
+ let field_b0x = ref (Obj.magic (Sys.opaque_identity 0.0)) in
+ let field_b1x = ref (Obj.magic (Sys.opaque_identity 0.0)) in
+ let field_b2x = ref (Obj.magic (Sys.opaque_identity 0.0)) in
+ let field_b3x = ref (None) in
+ let field_b4x = ref (Obj.magic (Sys.opaque_identity 0.0)) in
+ let field_b5x = ref (0.5) in
+ let bits0 = ref 0 in
+ let len = Bi_vint.read_uvint ib in
+ for i = 1 to len do
+ match Bi_io.read_field_hashtag ib with
+ | 21902 ->
+ field_b0x := (
+ (
+ Ag_ob_run.read_int
+ ) ib
+ );
+ bits0 := !bits0 lor 0x1;
+ | 21903 ->
+ field_b1x := (
+ (
+ Ag_ob_run.read_bool
+ ) ib
+ );
+ bits0 := !bits0 lor 0x2;
+ (* ... CODE ELIDED HERE ... *)
+ | 21907 ->
+ field_b5x := (
+ (
+ Ag_ob_run.read_float64
+ ) ib
+ );
+ | _ -> Bi_io.skip ib
+ done;
+ if !bits0 <> 0xf then Ag_ob_run.missing_fields
+ [| !bits0 |] [| "b0"; "b1"; "b2"; "b4" |];
+ (
+ {
+ b0x = !field_b0x;
+ b1x = !field_b1x;
+ b2x = !field_b2x;
+ b3x = !field_b3x;
+ b4x = !field_b4x;
+ b5x = !field_b5x;
+ }
+ : extended)
+
+ # Why Obj.magic?
+
+ At code generation time we do not have a default
+ value for the type of this field (we don't know what the type
+ is), so we create one out of thin air with Obj.magic
+
+ # Why 0.0?
+
+ Atdgen does not run the type-checker, so it does not a-priori
+ know if the field type is float (it may be a type alias of
+ "float" or even depend on a functor parameter).
+
+ If the type *is* float and the type-checker notices it
+ statically, then it may allocate an unboxed float reference, and
+ in particular unbox the default value passed at reference create
+ time. If this default value was *not* a float, then the code could
+ segfault. So in this case we must use a float value.
+
+ If the type is *not* float, then passing a float value is still
+ correct: the compiler will not try to unbox it, so a (word-sized)
+ pointer will be stored in the reference.
+
+ # Why Sys.opaque_identity?
+
+ Starting from 4.03, the compiler is more clever at assuming
+ things from values. When it sees the constant 0.0, it will infer
+ in particular that the reference contains a float (so it may
+ decide to unbox it!), etc. Notice that the compiler makes just
+ the same assumptions about (Obj.magic 0.0) than about 0.0, the
+ magic changes the type but not the value.
+
+ Also in 4.03, the Sys.opaque_identity function was added in the
+ Sys module; it is a compiler primitive of type ('a -> 'a) that
+ prevents the compiler from assuming anything about its return value.
+
+ In practice, using Sys.opaque_identity here avoids the segfault
+ that happened without it on 4.03. Note that this may not be
+ enough; in particular, (Sys.opaque_identity 0.0) is still
+ recognizeably a value of "float" type to the compiler (only the
+ value is unknown), so it would be legal for the compiler to still
+ decide to unbox in the future!
+
+ The long-term solution would be to stop using these unsafe
+ Obj.magic and use an option type to store the reference fields in
+ this case. This would be a more invasive change to the
+ implementation.
+ *)
+
let test_ocaml_internals () =
section "ocaml internals";
- let int = ref (Obj.magic 0.0) in
+ let opaque_identity =
+ (* neat trick to fallback to just the identity if we are using
+ a <4.03 version and Sys.opaque_identity is not available; found
+ in
+ https://github.com/LaurentMazare/tensorflow-ocaml/commit/111b4727cec992bab8bc67c22ccc8c31942ffbb2 *)
+ let opaque_identity x = x in
+ ignore opaque_identity;
+ let open Sys in opaque_identity in
+
+ let int = ref (Obj.magic (opaque_identity 0.0)) in
Gc.compact ();
int := 123;
Gc.compact ();
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ocaml-maint/packages/atdgen.git
More information about the Pkg-ocaml-maint-commits
mailing list