{"action":"created","comment":{"url":"https://api.github.com/repos/ocaml/ocaml/pulls/comments/214552415","pull_request_review_id":151632399,"id":214552415,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIxNDU1MjQxNQ==","diff_hunk":"@@ -306,338 +322,671 @@ let classify_expression : Typedtree.expression -> sd =\n         Dynamic\n   in classify_expression Ident.empty\n \n-let rec expression : Env.env -> Typedtree.expression -> Use.t =\n-  fun env exp -> match exp.exp_desc with\n+let remove_pat pat env =\n+  Env.remove_list (pat_bound_idents pat) env\n+\n+let remove_patlist pats env =\n+  List.fold_right remove_pat pats env\n+\n+(* Usage mode judgments.\n+\n+   There are two main groups of judgment functions:\n+\n+   - Judgments of the form \"Gamma |- ... : m\"\n+     compute the environment of a subterm from its mode,\n+     so the corresponding function has type [... -> Mode.t -> Env.t].\n+\n+     We write [... -> term_judg] in this case.\n+\n+   - Judgments of the form \"Gamma |- ... : m -| Gamma'\"\n+     correspond to binding constructs that have both an\n+     exterior environment Gamma (the environment of the whole term)\n+     and an interior environment Gamma' (the environment after the binding\n+     construct has introduced new names in scope).\n+\n+     For example, a toplevel let-binding could be given\n+     the following rule:\n+\n+       G |- e : m + m'\n+       -----------------------------------\n+       G+G' |- (let x = e : m) -| x:m', G'\n+\n+     In this case, the corresponding function has type\n+     [... -> Mode.t -> Env.t -> Env.t]: it takes the mode\n+     and interior environment as inputs, and returns\n+     the exterior environment.\n+\n+     We write [... -> bind_judg] in this case.\n+*)\n+type term_judg = Mode.t -> Env.t\n+type bind_judg = Mode.t -> Env.t -> Env.t\n+\n+let option : 'a. ('a -> term_judg) -> 'a option -> term_judg =\n+  fun f o m -> match o with\n+    | None -> Env.empty\n+    | Some v -> f v m\n+let list : 'a. ('a -> term_judg) -> 'a list -> term_judg =\n+  fun f li m ->\n+    List.fold_left (fun env item -> Env.join env (f item m)) Env.empty li\n+let array : 'a. ('a -> term_judg) -> 'a array -> term_judg =\n+  fun f ar m ->\n+    Array.fold_left (fun env item -> Env.join env (f item m)) Env.empty ar\n+\n+let single : Ident.t -> term_judg = Env.single\n+let remove_id : Ident.t -> term_judg -> term_judg =\n+  fun id f m -> Env.remove id (f m)\n+let remove_ids : Ident.t list -> term_judg -> term_judg =\n+  fun ids f m -> Env.remove_list ids (f m)\n+\n+let join : term_judg list -> term_judg =\n+  fun li m -> Env.join_list (List.map (fun f -> f m) li)\n+\n+let empty = fun _ -> Env.empty\n+\n+(* A judgment [judg] takes a mode from the context as input, and\n+   returns an environment. The judgment [judg << m], given a mode [m']\n+   from the context, evaluates [judg] in the composed mode [m'[m]]. *)\n+let (<<) : term_judg -> Mode.t -> term_judg =\n+  fun f inner_mode -> fun outer_mode -> f (Mode.compos outer_mode inner_mode)\n+\n+(* A binding judgment [binder] expects a mode and an inner environment,\n+   and returns an outer environment. [binder >> judg] computes\n+   the inner environment as the environment returned by [judg]\n+   in the ambient mode. *)\n+let (>>) : bind_judg -> term_judg -> term_judg =\n+  fun binder term mode -> binder mode (term mode)\n+\n+(* Expression judgment:\n+     G |- e : m\n+   where (m) is an output of the code and (G) is an output;\n+   in the Prolog mode notation, this is (+G |- -e : -m).\n+*)\n+let rec expression : Typedtree.expression -> term_judg =\n+  fun exp -> match exp.exp_desc with\n     | Texp_ident (pth, _, _) ->\n-        (path env pth)\n+      path pth\n     | Texp_let (rec_flag, bindings, body) ->\n-      let env', ty = value_bindings rec_flag env bindings in\n-      (* Here and in other binding constructs 'discard' is used in a\n-          similar way to the way it's used in sequence: uses are\n-          propagated, but unguarded access are not. *)\n-      Use.join (Use.discard ty) (expression (Env.join env env') body)\n-    | Texp_letmodule (x, _, m, e) ->\n-      let ty = modexp env m in\n-      Use.join (Use.discard ty) (expression (Ident.add x ty env) e)\n+      value_bindings rec_flag bindings >> expression body\n+    | Texp_letmodule (x, _, mexp, e) ->\n+      module_binding (x, mexp) >> expression e\n     | Texp_match (e, cases, _) ->\n-      let t = expression env e in\n-      list (case ~scrutinee:t) env cases\n-    | Texp_for (_, _, e1, e2, _, e3) ->\n-      Use.(join\n-              (join\n-                  (inspect (expression env e1))\n-                  (inspect (expression env e2)))\n-              (* The body is evaluated, but not used, and not available\n-                  for inclusion in another value *)\n-              (discard (expression env e3)))\n-\n+      (*\n+         (Gi; mi |- pi -> ei : m)^i\n+         G |- e : sum(mi)^i\n+         ----------------------------------------------\n+         G + sum(Gi)^i |- match e with (pi -> ei)^i : m\n+       *)\n+      (fun mode ->\n+        let pat_envs, pat_modes =\n+          List.split (List.map (fun c -> case c mode) cases) in\n+        let env_e = expression e (List.fold_left Mode.join Unused pat_modes) in\n+        Env.join_list (env_e :: pat_envs))\n+    | Texp_for (_, _, low, high, _, body) ->\n+      (*\n+        G1 |- low: m[Dereference]\n+        G2 |- high: m[Dereference]\n+        G3 |- body: m[Guard]\n+        ---\n+        G1 + G2 + G3 |- for _ = low to high do body done: m\n+      *)\n+      join [\n+        expression low << Dereference;\n+        expression high << Dereference;\n+        expression body << Guard;\n+      ]\n     | Texp_constant _ ->\n-      Use.empty\n+      empty\n     | Texp_new (pth, _, _) ->\n-        Use.inspect (path env pth)\n-    | Texp_instvar _ ->\n-      Use.empty\n+      (*\n+        G |- c: m[Dereference]\n+        -----------------------\n+        G |- new c: m\n+      *)\n+      path pth << Dereference\n+    | Texp_instvar (self_path, pth, _inst_var) ->\n+        join [path self_path << Dereference; path pth]\n     | Texp_apply ({exp_desc = Texp_ident (_, _, vd)}, [_, Some arg])\n       when is_ref vd ->\n-        Use.guard (expression env arg)\n+      (*\n+        G |- e: m[Guard]\n+        ------------------\n+        G |- ref e: m\n+      *)\n+      expression arg << Guard\n     | Texp_apply (e, args)  ->\n-        let arg env (_, eo) = option expression env eo in\n-        let ty = Use.join (list arg env args) (expression env e) in\n-        if List.exists is_abstracted_arg args\n-        then (* evaluate expressions, abstract over the results\n-                let g = f and x = e in fun z -> g ~x z *)\n-          Use.discard ty\n-        else Use.inspect ty\n+        let arg (_, eo) = option expression eo in\n+        let app_mode = if List.exists is_abstracted_arg args\n+          then (* see the comment on Texp_apply in typedtree.mli;\n+                  the non-abstracted arguments are bound to local\n+                  variables, which corresponds to a Guard mode. *)\n+            Guard\n+          else Dereference\n+        in\n+        join [expression e; list arg args] << app_mode\n     | Texp_tuple exprs ->\n-      Use.guard (list expression env exprs)\n-    | Texp_array args ->\n-      let ty = list expression env args in\n-      begin match Typeopt.array_kind exp with\n-      | Lambda.Pfloatarray ->\n-          (* (flat) float arrays unbox their elements *)\n-          Use.inspect ty\n-      | Lambda.Pgenarray ->\n-          (* This is counted as a use, because constructing a generic array\n-             involves inspecting to decide whether to unbox (PR#6939). *)\n-          Use.inspect ty\n-      | Lambda.Paddrarray | Lambda.Pintarray ->\n-          (* non-generic, non-float arrays act as constructors *)\n-          Use.guard ty\n-      end\n+      list expression exprs << Guard\n+    | Texp_array exprs ->\n+      let array_mode = match Typeopt.array_kind exp with\n+        | Lambda.Pfloatarray ->\n+            (* (flat) float arrays unbox their elements *)\n+            Dereference\n+        | Lambda.Pgenarray ->\n+            (* This is counted as a use, because constructing a generic array\n+               involves inspecting to decide whether to unbox (PR#6939). *)\n+            Dereference\n+        | Lambda.Paddrarray | Lambda.Pintarray ->\n+            (* non-generic, non-float arrays act as constructors *)\n+            Guard\n+      in\n+      list expression exprs << array_mode\n     | Texp_construct (_, desc, exprs) ->\n       let access_constructor =\n         match desc.cstr_tag with\n-        | Cstr_extension (pth, _) -> Use.inspect (path env pth)\n-        | _ -> Use.empty\n+        | Cstr_extension (pth, _) ->\n+          path pth << Dereference\n+        | _ -> empty\n       in\n-      let use = match desc.cstr_tag with\n-        | Cstr_unboxed -> (fun x -> x)\n-        | Cstr_constant _ | Cstr_block _ | Cstr_extension _ -> Use.guard\n+      let m' = match desc.cstr_tag with\n+        | Cstr_unboxed ->\n+          Return\n+        | Cstr_constant _ | Cstr_block _ | Cstr_extension _ ->\n+          Guard\n       in\n-      Use.join access_constructor (use (list expression env exprs))\n+      join [\n+        access_constructor;\n+        list expression exprs << m'\n+      ]\n     | Texp_variant (_, eo) ->\n-      Use.guard (option expression env eo)\n+      (*\n+        G |- e: m[Guard]\n+        ------------------   -----------\n+        G |- `A e: m         [] |- `A: m\n+      *)\n+      option expression eo << Guard\n     | Texp_record { fields = es; extended_expression = eo;\n                     representation = rep } ->\n-        let use = match rep with\n-          | Record_float -> Use.inspect\n-          | Record_unboxed _ -> (fun x -> x)\n+        let field_mode = match rep with\n+          | Record_float -> Dereference\n+          | Record_unboxed _ -> Return\n           | Record_regular | Record_inlined _\n-          | Record_extension -> Use.guard\n+          | Record_extension -> Guard\n         in\n-        let field env = function\n-            _, Kept _ -> Use.empty\n-          | _, Overridden (_, e) -> expression env e\n+        let field (_label, field_def) = match field_def with\n+            Kept _ -> empty\n+          | Overridden (_, e) -> expression e\n         in\n-        Use.join\n-          (use (array field env es))\n-          (option expression env eo)\n+        join [\n+          array field es << field_mode;\n+          option expression eo << Dereference\n+        ]\n     | Texp_ifthenelse (cond, ifso, ifnot) ->\n-        Use.(join (inspect (expression env cond))\n-                (join\n-                    (expression env ifso)\n-                    (option expression env ifnot)))\n+      (*\n+        Gc |- c: m[Dereference]\n+        G1 |- e1: m\n+        G2 |- e2: m\n+        ---\n+        Gc + G1 + G2 |- if c then e1 else e2: m\n+\n+      Note: `if c then e1 else e2` is treated in the same way as\n+      `match c with true -> e1 | false -> e2`\n+      *)\n+      join [\n+        expression cond << Dereference;\n+        expression ifso;\n+        option expression ifnot;\n+      ]\n     | Texp_setfield (e1, _, _, e2) ->\n-        Use.(join (inspect (expression env e1))\n-              (inspect (expression env e2)))\n+      (*\n+        G1 |- e1: m[Dereference]\n+        G2 |- e2: m[Dereference] (*TODO: why is this a dereference?*)\n+        ---\n+        G1 + G2 |- e1.x <- e2: m\n+      *)\n+      join [\n+        expression e1 << Dereference;\n+        expression e2 << Dereference;\n+      ]\n     | Texp_sequence (e1, e2) ->\n-      Use.(join (discard (expression env e1))\n-              (expression env e2))\n-    | Texp_while (e1, e2) ->\n-      Use.(join (inspect (expression env e1))\n-              (discard (expression env e2)))\n+      (*\n+        G1 |- e1: m[Guard]\n+        G2 |- e2: m\n+        --------------------\n+        G1 + G2 |- e1; e2: m\n+\n+        Note: `e1; e2` is treated in the same way as `let _ = e1 in e2`\n+      *)\n+      join [\n+        expression e1 << Guard;\n+        expression e2;\n+      ]\n+    | Texp_while (cond, body) ->\n+      (*\n+        G1 |- cond: m[Dereference]\n+        G2 |- body: m[Guard]\n+        ---------------------------------\n+        G1 + G2 |- while cond do body done: m\n+      *)\n+      join [\n+        expression cond << Dereference;\n+        expression body << Guard;\n+      ]\n     | Texp_send (e1, _, eo) ->\n-      Use.(join (inspect (expression env e1))\n-              (inspect (option expression env eo)))\n+      (*\n+        G |- e: m[Dereference]\n+        ---------------------- (plus weird 'eo' option)\n+        G |- e#x: m\n+      *)\n+      join [\n+        expression e1 << Dereference;\n+        option expression eo << Dereference;\n+      ]\n     | Texp_field (e, _, _) ->\n-      Use.(inspect (expression env e))\n-    | Texp_setinstvar (_,_,_,e) ->\n-        Use.(inspect (expression env e))\n-    | Texp_letexception (_, e) ->\n-        expression env e\n+      (*\n+        G |- e: m[Dereference]\n+        -----------------------\n+        G |- e.x: m\n+      *)\n+      expression e << Dereference\n+    | Texp_setinstvar (pth,_,_,e) ->\n+      (*\n+        G |- e: m[Dereference]  (*TODO: why is this a dereference?*)\n+        -----------------------\n+        G |- x <- e: m\n+      *)\n+      join [\n+        path pth << Dereference;\n+        expression e << Dereference;\n+      ]\n+    | Texp_letexception ({ext_id}, e) ->\n+      (* G |- e: m\n+         ----------------------------\n+         G |- let exception A in e: m\n+      *)\n+      remove_id ext_id (expression e)\n     | Texp_assert e ->\n-        Use.inspect (expression env e)\n-    | Texp_pack m ->\n-        modexp env m\n+      (*\n+        G |- e: m[Dereference]\n+        -----------------------\n+        G |- assert e: m\n+\n+        Note: `assert e` is treated just as if `assert` was a function.\n+      *)\n+      expression e << Dereference\n+    | Texp_pack mexp ->\n+      (*\n+        G |- M: m\n+        ----------------\n+        G |- module M: m\n+      *)\n+      modexp mexp\n     | Texp_object (clsstrct, _) ->\n-        class_structure env clsstrct\n+      class_structure clsstrct\n     | Texp_try (e, cases) ->\n-      (* This is more permissive than the old check. *)\n-      let case env {Typedtree.c_rhs} = expression env c_rhs in\n-      Use.join (expression env e)\n-        (list case env cases)\n-    | Texp_override (_, fields) ->\n-      let field env (_, _, e) = expression env e in\n-      Use.inspect (list field env fields)\n+      (*\n+        G |- e: m      (Gi; _ |- pi -> ei : m)^i\n+        --------------------------------------------\n+        G + sum(Gi)^i |- try e with (pi -> ei)^i : m\n+\n+        Contrarily to match, the patterns p do not inspect\n+        the value of e, so their mode does not influence the\n+        mode of e.\n+      *)\n+      let case_env c m = fst (case c m) in\n+      join [\n+        expression e;\n+        list case_env cases;\n+      ]\n+    | Texp_override (pth, fields) ->\n+      (*\n+         G |- pth : m   (Gi |- ei : m[Derefence])^i\n+         ----------------------------------------------------\n+         G + sum(Gi)^i |- {< (xi = ei)^i >} (at path pth) : m\n+\n+         Note: {< .. >} is desugared to a function application, but\n+         the function implementation might still use its arguments in\n+         a guarded way only -- intuitively it should behave as a constructor.\n+         We could possibly refine the arguments' Dereference into Guard here.\n+      *)\n+      let field (_, _, arg) = expression arg in\n+      join [\n+        path pth << Dereference;\n+        list field fields << Dereference;\n+      ]\n     | Texp_function { cases } ->\n-      Use.delay (list (case ~scrutinee:Use.empty) env cases)\n+      (*\n+         (Gi; _ |- pi -> ei : m[Delay])^i\n+         --------------------------------------\n+         sum(Gi)^i |- function (pi -> ei)^i : m\n+\n+         Contrarily to match, the value that is pattern-matched\n+         is bound locally, so the pattern modes do not influence\n+         the final environment.\n+      *)\n+      let case_env c m = fst (case c m) in\n+      list case_env cases << Delay\n     | Texp_lazy e ->\n-        begin match Typeopt.classify_lazy_argument e with\n+      (*\n+        G |- e: m[Delay]\n+        ----------------  (modulo some subtle compiler optimizations)","path":"typing/rec_check.ml","position":744,"original_position":744,"commit_id":"06fef59824ad46f714d502fe8cf35dd26982bc0a","original_commit_id":"06fef59824ad46f714d502fe8cf35dd26982bc0a","user":{"login":"gasche","id":426238,"node_id":"MDQ6VXNlcjQyNjIzOA==","avatar_url":"https://avatars2.githubusercontent.com/u/426238?v=4","gravatar_id":"","url":"https://api.github.com/users/gasche","html_url":"https://github.com/gasche","followers_url":"https://api.github.com/users/gasche/followers","following_url":"https://api.github.com/users/gasche/following{/other_user}","gists_url":"https://api.github.com/users/gasche/gists{/gist_id}","starred_url":"https://api.github.com/users/gasche/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gasche/subscriptions","organizations_url":"https://api.github.com/users/gasche/orgs","repos_url":"https://api.github.com/users/gasche/repos","events_url":"https://api.github.com/users/gasche/events{/privacy}","received_events_url":"https://api.github.com/users/gasche/received_events","type":"User","site_admin":false},"body":"Yes, I'm expecting such future changes to return one of the known-to-be-shortcut variants (for unboxed constructors, typically the classifier of the underlying argument), or to add a new variants to be handled properly here.","created_at":"2018-09-02T20:49:50Z","updated_at":"2018-09-02T20:49:50Z","html_url":"https://github.com/ocaml/ocaml/pull/1942#discussion_r214552415","pull_request_url":"https://api.github.com/repos/ocaml/ocaml/pulls/1942","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/ocaml/ocaml/pulls/comments/214552415"},"html":{"href":"https://github.com/ocaml/ocaml/pull/1942#discussion_r214552415"},"pull_request":{"href":"https://api.github.com/repos/ocaml/ocaml/pulls/1942"}},"in_reply_to_id":214457034},"pull_request":{"url":"https://api.github.com/repos/ocaml/ocaml/pulls/1942","id":204784343,"node_id":"MDExOlB1bGxSZXF1ZXN0MjA0Nzg0MzQz","html_url":"https://github.com/ocaml/ocaml/pull/1942","diff_url":"https://github.com/ocaml/ocaml/pull/1942.diff","patch_url":"https://github.com/ocaml/ocaml/pull/1942.patch","issue_url":"https://api.github.com/repos/ocaml/ocaml/issues/1942","number":1942,"state":"open","locked":false,"title":"New implementations of the static check for recursive definitions","user":{"login":"gasche","id":426238,"node_id":"MDQ6VXNlcjQyNjIzOA==","avatar_url":"https://avatars2.githubusercontent.com/u/426238?v=4","gravatar_id":"","url":"https://api.github.com/users/gasche","html_url":"https://github.com/gasche","followers_url":"https://api.github.com/users/gasche/followers","following_url":"https://api.github.com/users/gasche/following{/other_user}","gists_url":"https://api.github.com/users/gasche/gists{/gist_id}","starred_url":"https://api.github.com/users/gasche/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gasche/subscriptions","organizations_url":"https://api.github.com/users/gasche/orgs","repos_url":"https://api.github.com/users/gasche/repos","events_url":"https://api.github.com/users/gasche/events{/privacy}","received_events_url":"https://api.github.com/users/gasche/received_events","type":"User","site_admin":false},"body":"This GPR is on top of #1937.\r\n\r\nThis work was conducted by Alban Reynaud @Cemoixerestre during an internship at INRIA\r\nSaclay, with the help and under the supervision of Gabriel Scherer in\r\nthe Parsifal team.\r\n\r\nA summary is included below. For more details, see\r\n\r\n  https://github.com/Cemoixerestre/Internship-2018-ocaml-recursive-value\r\n\r\nor the comments in the code itself, which has evolved a bit from\r\nAlban's internship report above.\r\n\r\nSummary:\r\n--------\r\n\r\nRec_check is based on the idea of assigning \"access modes\" to variables\r\n\r\n    m ::= Dereference (* full access and inspection of the value *)\r\n        | Unguarded (* the value result is directly returned *)\r\n        | Guard (* the value is stored under a data constructor *)\r\n        | Delayed (* the value is not needed at definition time *)\r\n        | Unused (* the value is not used at all *)\r\n\r\nBefore this patchset, the implementation (contributed by Jeremy Yallop)\r\nwas formulated as a type-checker manipulating \"uses\", which are mappings\r\nfrom free variables to modes\r\n\r\n    u ::= (x : m)*\r\n\r\nand contexts Γ mapping variables to uses\r\n\r\n    Γ ::= (x : u)*\r\n\r\nThe check relied on a judgment of the form\r\n\r\n    Γ ⊢ e : u\r\n\r\nwhere, in the typing algorithm, `Γ` and `e` are the inputs, and `u` is\r\nthe output — this could be written `-Γ ⊢ e : +u`.\r\n\r\nAfter this patchset, the implementation uses a simpler notion of context\r\nmapping variables to mods\r\n\r\n    Γ ::= (x : m)*\r\n\r\nand the judgment has the simpler structure\r\n\r\n     Γ ⊢ e : m\r\n\r\nbut now `m` is an input to the judgment, indicating in which usage\r\ncontext `e` is checked, and `Γ` is the output: `+Γ ⊢ e : -m`.\r\n\r\nOther fixes:\r\n------------\r\n\r\nThis patchset also fixes a soundness bug around `{ foo with ... }`\r\nwhich is already present in 4.06 and 4.07.\r\n\r\nAnother soundness bug found during this work was submitted and fixed\r\nindependently as GPR #1915.\r\n\r\nThis work also led to the refactoring of GPR #1922, and the refactoring\r\nof the testsuite in GPR #1937.\r\n","created_at":"2018-07-30T12:13:37Z","updated_at":"2018-09-02T20:49:50Z","closed_at":null,"merged_at":null,"merge_commit_sha":"e3a5fb0703750643aa7bb3c6f223144198a9c9d0","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/ocaml/ocaml/pulls/1942/commits","review_comments_url":"https://api.github.com/repos/ocaml/ocaml/pulls/1942/comments","review_comment_url":"https://api.github.com/repos/ocaml/ocaml/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ocaml/ocaml/issues/1942/comments","statuses_url":"https://api.github.com/repos/ocaml/ocaml/statuses/06fef59824ad46f714d502fe8cf35dd26982bc0a","head":{"label":"gasche:alban-rec-check","ref":"alban-rec-check","sha":"06fef59824ad46f714d502fe8cf35dd26982bc0a","user":{"login":"gasche","id":426238,"node_id":"MDQ6VXNlcjQyNjIzOA==","avatar_url":"https://avatars2.githubusercontent.com/u/426238?v=4","gravatar_id":"","url":"https://api.github.com/users/gasche","html_url":"https://github.com/gasche","followers_url":"https://api.github.com/users/gasche/followers","following_url":"https://api.github.com/users/gasche/following{/other_user}","gists_url":"https://api.github.com/users/gasche/gists{/gist_id}","starred_url":"https://api.github.com/users/gasche/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gasche/subscriptions","organizations_url":"https://api.github.com/users/gasche/orgs","repos_url":"https://api.github.com/users/gasche/repos","events_url":"https://api.github.com/users/gasche/events{/privacy}","received_events_url":"https://api.github.com/users/gasche/received_events","type":"User","site_admin":false},"repo":{"id":18431480,"node_id":"MDEwOlJlcG9zaXRvcnkxODQzMTQ4MA==","name":"ocaml","full_name":"gasche/ocaml","owner":{"login":"gasche","id":426238,"node_id":"MDQ6VXNlcjQyNjIzOA==","avatar_url":"https://avatars2.githubusercontent.com/u/426238?v=4","gravatar_id":"","url":"https://api.github.com/users/gasche","html_url":"https://github.com/gasche","followers_url":"https://api.github.com/users/gasche/followers","following_url":"https://api.github.com/users/gasche/following{/other_user}","gists_url":"https://api.github.com/users/gasche/gists{/gist_id}","starred_url":"https://api.github.com/users/gasche/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gasche/subscriptions","organizations_url":"https://api.github.com/users/gasche/orgs","repos_url":"https://api.github.com/users/gasche/repos","events_url":"https://api.github.com/users/gasche/events{/privacy}","received_events_url":"https://api.github.com/users/gasche/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/gasche/ocaml","description":" The core OCaml system: compilers, runtime system, base libraries ","fork":true,"url":"https://api.github.com/repos/gasche/ocaml","forks_url":"https://api.github.com/repos/gasche/ocaml/forks","keys_url":"https://api.github.com/repos/gasche/ocaml/keys{/key_id}","collaborators_url":"https://api.github.com/repos/gasche/ocaml/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/gasche/ocaml/teams","hooks_url":"https://api.github.com/repos/gasche/ocaml/hooks","issue_events_url":"https://api.github.com/repos/gasche/ocaml/issues/events{/number}","events_url":"https://api.github.com/repos/gasche/ocaml/events","assignees_url":"https://api.github.com/repos/gasche/ocaml/assignees{/user}","branches_url":"https://api.github.com/repos/gasche/ocaml/branches{/branch}","tags_url":"https://api.github.com/repos/gasche/ocaml/tags","blobs_url":"https://api.github.com/repos/gasche/ocaml/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/gasche/ocaml/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/gasche/ocaml/git/refs{/sha}","trees_url":"https://api.github.com/repos/gasche/ocaml/git/trees{/sha}","statuses_url":"https://api.github.com/repos/gasche/ocaml/statuses/{sha}","languages_url":"https://api.github.com/repos/gasche/ocaml/languages","stargazers_url":"https://api.github.com/repos/gasche/ocaml/stargazers","contributors_url":"https://api.github.com/repos/gasche/ocaml/contributors","subscribers_url":"https://api.github.com/repos/gasche/ocaml/subscribers","subscription_url":"https://api.github.com/repos/gasche/ocaml/subscription","commits_url":"https://api.github.com/repos/gasche/ocaml/commits{/sha}","git_commits_url":"https://api.github.com/repos/gasche/ocaml/git/commits{/sha}","comments_url":"https://api.github.com/repos/gasche/ocaml/comments{/number}","issue_comment_url":"https://api.github.com/repos/gasche/ocaml/issues/comments{/number}","contents_url":"https://api.github.com/repos/gasche/ocaml/contents/{+path}","compare_url":"https://api.github.com/repos/gasche/ocaml/compare/{base}...{head}","merges_url":"https://api.github.com/repos/gasche/ocaml/merges","archive_url":"https://api.github.com/repos/gasche/ocaml/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/gasche/ocaml/downloads","issues_url":"https://api.github.com/repos/gasche/ocaml/issues{/number}","pulls_url":"https://api.github.com/repos/gasche/ocaml/pulls{/number}","milestones_url":"https://api.github.com/repos/gasche/ocaml/milestones{/number}","notifications_url":"https://api.github.com/repos/gasche/ocaml/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/gasche/ocaml/labels{/name}","releases_url":"https://api.github.com/repos/gasche/ocaml/releases{/id}","deployments_url":"https://api.github.com/repos/gasche/ocaml/deployments","created_at":"2014-04-04T07:49:39Z","updated_at":"2018-07-01T15:08:19Z","pushed_at":"2018-09-01T21:08:34Z","git_url":"git://github.com/gasche/ocaml.git","ssh_url":"git@github.com:gasche/ocaml.git","clone_url":"https://github.com/gasche/ocaml.git","svn_url":"https://github.com/gasche/ocaml","homepage":"","size":194834,"stargazers_count":0,"watchers_count":0,"language":"OCaml","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"other","name":"Other","spdx_id":null,"url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":0,"open_issues":0,"watchers":0,"default_branch":"trunk"}},"base":{"label":"ocaml:trunk","ref":"trunk","sha":"40033ad7fbc5abb218b48274bce45995e02f5c7a","user":{"login":"ocaml","id":1841483,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE4NDE0ODM=","avatar_url":"https://avatars3.githubusercontent.com/u/1841483?v=4","gravatar_id":"","url":"https://api.github.com/users/ocaml","html_url":"https://github.com/ocaml","followers_url":"https://api.github.com/users/ocaml/followers","following_url":"https://api.github.com/users/ocaml/following{/other_user}","gists_url":"https://api.github.com/users/ocaml/gists{/gist_id}","starred_url":"https://api.github.com/users/ocaml/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ocaml/subscriptions","organizations_url":"https://api.github.com/users/ocaml/orgs","repos_url":"https://api.github.com/users/ocaml/repos","events_url":"https://api.github.com/users/ocaml/events{/privacy}","received_events_url":"https://api.github.com/users/ocaml/received_events","type":"Organization","site_admin":false},"repo":{"id":6786166,"node_id":"MDEwOlJlcG9zaXRvcnk2Nzg2MTY2","name":"ocaml","full_name":"ocaml/ocaml","owner":{"login":"ocaml","id":1841483,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE4NDE0ODM=","avatar_url":"https://avatars3.githubusercontent.com/u/1841483?v=4","gravatar_id":"","url":"https://api.github.com/users/ocaml","html_url":"https://github.com/ocaml","followers_url":"https://api.github.com/users/ocaml/followers","following_url":"https://api.github.com/users/ocaml/following{/other_user}","gists_url":"https://api.github.com/users/ocaml/gists{/gist_id}","starred_url":"https://api.github.com/users/ocaml/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ocaml/subscriptions","organizations_url":"https://api.github.com/users/ocaml/orgs","repos_url":"https://api.github.com/users/ocaml/repos","events_url":"https://api.github.com/users/ocaml/events{/privacy}","received_events_url":"https://api.github.com/users/ocaml/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ocaml/ocaml","description":"The core OCaml system: compilers, runtime system, base libraries","fork":false,"url":"https://api.github.com/repos/ocaml/ocaml","forks_url":"https://api.github.com/repos/ocaml/ocaml/forks","keys_url":"https://api.github.com/repos/ocaml/ocaml/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ocaml/ocaml/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ocaml/ocaml/teams","hooks_url":"https://api.github.com/repos/ocaml/ocaml/hooks","issue_events_url":"https://api.github.com/repos/ocaml/ocaml/issues/events{/number}","events_url":"https://api.github.com/repos/ocaml/ocaml/events","assignees_url":"https://api.github.com/repos/ocaml/ocaml/assignees{/user}","branches_url":"https://api.github.com/repos/ocaml/ocaml/branches{/branch}","tags_url":"https://api.github.com/repos/ocaml/ocaml/tags","blobs_url":"https://api.github.com/repos/ocaml/ocaml/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ocaml/ocaml/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ocaml/ocaml/git/refs{/sha}","trees_url":"https://api.github.com/repos/ocaml/ocaml/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ocaml/ocaml/statuses/{sha}","languages_url":"https://api.github.com/repos/ocaml/ocaml/languages","stargazers_url":"https://api.github.com/repos/ocaml/ocaml/stargazers","contributors_url":"https://api.github.com/repos/ocaml/ocaml/contributors","subscribers_url":"https://api.github.com/repos/ocaml/ocaml/subscribers","subscription_url":"https://api.github.com/repos/ocaml/ocaml/subscription","commits_url":"https://api.github.com/repos/ocaml/ocaml/commits{/sha}","git_commits_url":"https://api.github.com/repos/ocaml/ocaml/git/commits{/sha}","comments_url":"https://api.github.com/repos/ocaml/ocaml/comments{/number}","issue_comment_url":"https://api.github.com/repos/ocaml/ocaml/issues/comments{/number}","contents_url":"https://api.github.com/repos/ocaml/ocaml/contents/{+path}","compare_url":"https://api.github.com/repos/ocaml/ocaml/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ocaml/ocaml/merges","archive_url":"https://api.github.com/repos/ocaml/ocaml/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ocaml/ocaml/downloads","issues_url":"https://api.github.com/repos/ocaml/ocaml/issues{/number}","pulls_url":"https://api.github.com/repos/ocaml/ocaml/pulls{/number}","milestones_url":"https://api.github.com/repos/ocaml/ocaml/milestones{/number}","notifications_url":"https://api.github.com/repos/ocaml/ocaml/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ocaml/ocaml/labels{/name}","releases_url":"https://api.github.com/repos/ocaml/ocaml/releases{/id}","deployments_url":"https://api.github.com/repos/ocaml/ocaml/deployments","created_at":"2012-11-20T22:18:22Z","updated_at":"2018-09-02T15:28:51Z","pushed_at":"2018-09-02T17:49:20Z","git_url":"git://github.com/ocaml/ocaml.git","ssh_url":"git@github.com:ocaml/ocaml.git","clone_url":"https://github.com/ocaml/ocaml.git","svn_url":"https://github.com/ocaml/ocaml","homepage":"","size":177246,"stargazers_count":1956,"watchers_count":1956,"language":"OCaml","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":477,"mirror_url":null,"archived":false,"open_issues_count":171,"license":{"key":"other","name":"Other","spdx_id":null,"url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":477,"open_issues":171,"watchers":1956,"default_branch":"trunk"}},"_links":{"self":{"href":"https://api.github.com/repos/ocaml/ocaml/pulls/1942"},"html":{"href":"https://github.com/ocaml/ocaml/pull/1942"},"issue":{"href":"https://api.github.com/repos/ocaml/ocaml/issues/1942"},"comments":{"href":"https://api.github.com/repos/ocaml/ocaml/issues/1942/comments"},"review_comments":{"href":"https://api.github.com/repos/ocaml/ocaml/pulls/1942/comments"},"review_comment":{"href":"https://api.github.com/repos/ocaml/ocaml/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ocaml/ocaml/pulls/1942/commits"},"statuses":{"href":"https://api.github.com/repos/ocaml/ocaml/statuses/06fef59824ad46f714d502fe8cf35dd26982bc0a"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"gasche"}}