From c3ee29acea9455bee0713fb8d96b6a6d3eb98d1a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Loi=CC=88c=20Correnson?= <loic.correnson@cea.fr>
Date: Mon, 11 Jul 2022 20:52:12 +0200
Subject: [PATCH] [wp] improved consolidation for smoke tests

---
 src/plugins/wp/ProofEngine.ml      |  58 ++++++++--------
 src/plugins/wp/ProofEngine.mli     |   9 ++-
 src/plugins/wp/ProverScript.ml     |   4 +-
 src/plugins/wp/Stats.ml            | 108 +++++++++++++++++++----------
 src/plugins/wp/Stats.mli           |   6 +-
 src/plugins/wp/VC.ml               |   2 +-
 src/plugins/wp/VC.mli              |   6 +-
 src/plugins/wp/VCS.ml              |  20 +++---
 src/plugins/wp/VCS.mli             |   2 +
 src/plugins/wp/gui/GuiNavigator.ml |   2 +-
 src/plugins/wp/register.ml         |  21 +++---
 src/plugins/wp/wpo.ml              |  16 +++--
 src/plugins/wp/wpo.mli             |   9 ++-
 13 files changed, 161 insertions(+), 102 deletions(-)

diff --git a/src/plugins/wp/ProofEngine.ml b/src/plugins/wp/ProofEngine.ml
index cccb5817a93..093befdcd5f 100644
--- a/src/plugins/wp/ProofEngine.ml
+++ b/src/plugins/wp/ProofEngine.ml
@@ -29,6 +29,7 @@ type node = {
   goal : Wpo.t ; (* only GoalAnnot of a sequent *)
   parent : node option ;
   mutable script : script ;
+  mutable stats : Stats.stats ;
   mutable search_index : int ;
   mutable search_space : Strategy.t array ; (* sorted by priority *)
 }
@@ -128,13 +129,6 @@ let rec walk f node =
     | Tactic (_,children) -> iter_all (walk f) children
     | Opened | Script _ -> f node
 
-let rec witer f node =
-  let proved = Wpo.is_valid node.goal in
-  if proved then f ~proved node else
-    match node.script with
-    | Tactic (_,children) -> iter_all (witer f) children
-    | Opened | Script _ -> f ~proved node
-
 let iteri f tree =
   match tree.root with
   | None -> ()
@@ -152,30 +146,37 @@ let pending n =
   let k = ref 0 in
   walk (fun _ -> incr k) n ; !k
 
-let has_pending n =
-  try walk (fun _ -> raise Exit) n ; false
-  with Exit -> true
-
-let consolidate root =
-  let result = ref VCS.valid in
-  witer
-    (fun ~proved:_ node ->
-       let rs = List.map snd (Wpo.get_results node.goal) in
-       result := VCS.merge !result (VCS.best rs) ;
-    ) root ;
-  !result
-
-let validate ?(incomplete=false) tree =
+let rec consolidate n =
+  let s =
+    if Wpo.is_valid n.goal then
+      Stats.results ~smoke:false (Wpo.get_results n.goal)
+    else
+      match n.script with
+      | Opened | Script _ -> Stats.empty
+      | Tactic(_,children) ->
+        let qed = Wpo.qed_time n.goal in
+        let results = List.map (fun (_,n) -> consolidate n) children in
+        Stats.tactical ~qed results
+  in n.stats <- s ; s
+
+let validate tree =
   match tree.root with
   | None -> ()
   | Some root ->
     if not (Wpo.is_valid tree.main) then
-      if incomplete then
-        let result = consolidate root in
-        Wpo.set_result tree.main VCS.Tactical result
-      else
-      if not (has_pending root) then
-        Wpo.set_result tree.main VCS.Tactical VCS.valid
+      let stats = consolidate root in
+      Wpo.set_result tree.main Tactical (Stats.script stats)
+
+let consolidated wpo =
+  let smoke = Wpo.is_smoke_test wpo in
+  let prs = Wpo.get_results wpo in
+  try
+    if Wpo.is_smoke_test wpo || not (PROOFS.mem wpo) then raise Not_found ;
+    match PROOFS.get wpo with
+    | { root = Some { stats ; script = Tactic _ } } -> stats
+    | _ -> raise Not_found
+  with Not_found ->
+    Stats.results ~smoke prs
 
 (* -------------------------------------------------------------------------- *)
 (* --- Accessors                                                          --- *)
@@ -186,6 +187,7 @@ let head t = match t.head with
   | None -> t.main
   | Some n -> n.goal
 let goal n = n.goal
+let stats n = n.stats
 let tree_context t = Wpo.get_context t.main
 let node_context n = Wpo.get_context n.goal
 let parent n = n.parent
@@ -320,6 +322,7 @@ let mk_tree_node ~tree ~anchor goal = {
   tree = tree.main ; goal ;
   parent = Some anchor ;
   script = Opened ;
+  stats = Stats.empty ;
   search_index = 0 ;
   search_space = [| |] ;
 }
@@ -328,6 +331,7 @@ let mk_root_node goal = {
   tree = goal ; goal ;
   parent = None ;
   script = Opened ;
+  stats = Stats.empty ;
   search_index = 0 ;
   search_space = [| |] ;
 }
diff --git a/src/plugins/wp/ProofEngine.mli b/src/plugins/wp/ProofEngine.mli
index 55dde001ca1..03c14eb81ee 100644
--- a/src/plugins/wp/ProofEngine.mli
+++ b/src/plugins/wp/ProofEngine.mli
@@ -34,10 +34,14 @@ val get : Wpo.t -> [ `Script | `Proof | `Saved | `None ]
 val proof : main:Wpo.t -> tree
 val reset : tree -> unit
 val remove : Wpo.t -> unit
-val validate : ?incomplete:bool -> tree -> unit
 
-(** Leaves are numbered from 0 to n-1 *)
+(** Re-compute stats & set status of the entire script *)
+val validate : tree -> unit
+
+(** Consolidate statistics wrt current script or prover results *)
+val consolidated : Wpo.t -> Stats.stats
 
+(** Leaves are numbered from 0 to n-1 *)
 
 type status = [
   | `Unproved (** proof obligation not proved *)
@@ -67,6 +71,7 @@ val node_context : node -> WpContext.t
 val title : node -> string
 val proved : node -> bool
 val pending : node -> int
+val stats : node -> Stats.stats
 val parent : node -> node option
 val children : node -> (string * node) list
 val tactical : node -> ProofScript.jtactic option
diff --git a/src/plugins/wp/ProverScript.ml b/src/plugins/wp/ProverScript.ml
index 652b0220a88..ea335f1aecb 100644
--- a/src/plugins/wp/ProverScript.ml
+++ b/src/plugins/wp/ProverScript.ml
@@ -141,13 +141,13 @@ struct
   let stuck env =
     if not env.signaled then
       begin
-        ProofEngine.validate ~incomplete:true env.tree ;
+        ProofEngine.validate env.tree ;
         env.success (ProofEngine.main env.tree) None ;
         env.signaled <- true ;
       end
 
   let validate ?(finalize=false) env =
-    ProofEngine.validate ~incomplete:true env.tree ;
+    ProofEngine.validate env.tree ;
     if not env.signaled then
       let wpo = ProofEngine.main env.tree in
       let proved = Wpo.is_valid wpo in
diff --git a/src/plugins/wp/Stats.ml b/src/plugins/wp/Stats.ml
index ebd9a175550..cd724a5c7eb 100644
--- a/src/plugins/wp/Stats.ml
+++ b/src/plugins/wp/Stats.ml
@@ -36,6 +36,7 @@ type pstats = {
 }
 
 type stats = {
+  verdict : VCS.verdict ;
   provers : (VCS.prover * pstats) list ;
   tactics : int ;
   proved : int ;
@@ -84,11 +85,6 @@ let ptime t valid =
   { tmin = t ; tval = t ; tmax = t ; time = t ; tnbr = 1.0 ;
     success = if valid then 1.0 else 0.0 }
 
-let psmoke r =
-  { pzero with
-    time = r.prover_time ;
-    success = if VCS.is_valid valid then 1.0 else 0.0 }
-
 let pqed r = ptime r.solver_time (VCS.is_valid r)
 let presult r = ptime r.prover_time (VCS.is_valid r)
 let psolver r = ptime r.solver_time false
@@ -97,7 +93,8 @@ let psolver r = ptime r.solver_time false
 (* --- Global Stats                                                       --- *)
 (* -------------------------------------------------------------------------- *)
 
-let zero = {
+let empty = {
+  verdict = NoResult;
   provers = [];
   tactics = 0;
   proved = 0;
@@ -108,46 +105,75 @@ let zero = {
   cached = 0 ;
 }
 
-let choose (p,rp) (q,rq) =
-  if VCS.leq rp rq then (p,rp) else (q,rq)
+let choose_best a b =
+  if VCS.leq (snd a) (snd b) then a else b
+
+let choose_worst a b =
+  if VCS.leq (snd b) (snd a) then a else b
 
-let consolidated ~smoke = function
+let consolidated_valid = function
   | [] -> NoResult, 0, []
   | u::w as results ->
-    let p,r = List.fold_left choose u w in
+    let p,r = List.fold_left choose_best u w in
     let cached =
       p = Qed ||
       if VCS.is_valid r then r.cached else
         List.for_all
           (fun (_,r) -> r.VCS.cached || not (VCS.is_verdict r))
           results in
-    r.verdict,
+    r.VCS.verdict,
     (if cached then 1 else 0),
     if p = Qed then [Qed,pqed r]
-    else pmerge [Qed,psolver r] [p,if smoke then psmoke r else presult r]
-
-let smoked_result (p,r) = p, { r with verdict = VCS.smoked r.verdict }
+    else pmerge [Qed,psolver r] [p,presult r]
 
-let results ~smoke prs =
-  let prs = if smoke then List.map smoked_result prs else prs in
-  let verdict, cached, provers = consolidated ~smoke prs in
-  verdict,
+let valid_stats prs =
+  let verdict, cached, provers = consolidated_valid prs in
   match verdict with
   | Valid ->
-    { zero with provers ; cached ; proved = 1 }
+    { empty with verdict ; provers ; cached ; proved = 1 }
   | Timeout | Stepout ->
-    { zero with provers ; cached ; timeout = 1 }
+    { empty with verdict ; provers ; cached ; timeout = 1 }
   | Unknown ->
-    { zero with provers ; cached ; unknown = 1 }
+    { empty with verdict ; provers ; cached ; unknown = 1 }
   | NoResult | Computing _ ->
-    { zero with provers ; cached ; noresult = 1 }
+    { empty with verdict ; provers ; cached ; noresult = 1 }
   | Failed | Invalid ->
-    { zero with provers ; cached ; failed = 1 }
+    { empty with verdict ; provers ; cached ; failed = 1 }
+
+let results ~smoke prs =
+  if not smoke then valid_stats prs
+  else
+    let verdict, missing =
+      List.partition (fun (_,r) -> VCS.is_verdict r) prs in
+    let doomed, passed =
+      List.partition (fun (_,r) -> VCS.is_valid r) verdict in
+    if doomed <> [] then
+      valid_stats doomed
+    else
+      let cached = List.fold_left
+          (fun c (_,r) -> if r.VCS.cached then succ c else c)
+          0 doomed in
+      let stucked = List.map
+          (fun (p,r) -> p, ptime r.prover_time true)
+          doomed in
+      let solver = List.fold_left
+          (fun t (_,r) -> t +. r.solver_time)
+          0.0 doomed in
+      let provers = pmerge [Qed,ptime solver false] stucked in
+      let verdict =
+        if missing <> [] then NoResult else
+          match passed with
+          | [] -> NoResult
+          | u::w -> (snd @@ List.fold_left choose_worst u w).verdict in
+      let proved = List.length passed in
+      let failed = List.length missing + List.length doomed in
+      { empty with verdict ; provers ; cached ; proved ; failed }
 
 let add a b =
-  if a == zero then b else
-  if b == zero then a else
+  if a == empty then b else
+  if b == empty then a else
     {
+      verdict = VCS.combine a.verdict b.verdict ;
       provers = pmerge a.provers b.provers ;
       tactics = a.tactics + b.tactics ;
       proved = a.proved + b.proved ;
@@ -159,9 +185,19 @@ let add a b =
     }
 
 let tactical ~qed children =
-  let valid = children = [] in
-  let provers = [Qed,ptime qed valid] in
-  List.fold_left add { zero with provers ; tactics = 1 } children
+  let valid = List.for_all (fun c -> c.verdict = Valid) children in
+  let qed_only = children = [] in
+  let verdict = if valid then Valid else Unknown in
+  let provers = [Qed,ptime qed qed_only] in
+  List.fold_left add { empty with verdict ; provers ; tactics = 1 } children
+
+let script stats =
+  let cached = stats.cached = stats.proved in
+  let solver = List.fold_left
+      (fun t (p,s) -> if p = Qed then t +. s.time else t) 0.0 stats.provers in
+  let time = List.fold_left
+      (fun t (p,s) -> if p <> Qed then t +. s.time else t) 0.0 stats.provers in
+  VCS.result ~cached ~solver ~time stats.verdict
 
 (* -------------------------------------------------------------------------- *)
 (* --- Utils                                                              --- *)
@@ -195,20 +231,20 @@ let pp_pstats fmt p =
 let pp_stats ~shell ~updating fmt s =
   let vp = s.proved in
   let np = proofs s in
-  if vp < np && np > 1 then
-    Format.fprintf fmt " (Proofs %d/%d)" vp np ;
   if s.tactics > 1 then
-    Format.fprintf fmt " (Tactics %d)" s.tactics
-  else if np <= 1 && s.tactics = 1 then
-    Format.fprintf fmt " (Tactic)" ;
+    Format.fprintf fmt " (Script %d)" s.tactics
+  else if s.tactics = 1 then
+    Format.fprintf fmt " (Script)" ;
   let perfo = not shell || (not updating && s.cached < vp) in
-  let only_qed = match s.provers with [Qed,_] -> true | _ -> false in
+  let qed_only =
+    match s.provers with [Qed,_] -> vp = np | _ -> false in
   List.iter
     (fun (p,pr) ->
        let success = truncate pr.success in
        let print_perfo = perfo && pr.time > Rformat.epsilon in
        let print_proofs = success > 0 && np > 1 in
-       if p != Qed || only_qed || print_perfo || print_proofs then
+       let print_qed = qed_only && s.verdict = Valid && vp = np in
+       if p != Qed || print_qed || print_perfo || print_proofs then
          begin
            let title = VCS.title_of_prover ~version:false p in
            Format.fprintf fmt " (%s" title ;
@@ -229,6 +265,8 @@ let pp_stats ~shell ~updating fmt s =
     else
       Format.fprintf fmt " (Cached %d/%d)" s.cached np
 
+let pretty = pp_stats ~shell:false ~updating:false
+
 (* -------------------------------------------------------------------------- *)
 (* --- Yojson                                                             --- *)
 (* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/wp/Stats.mli b/src/plugins/wp/Stats.mli
index c1ced421667..cf83426f8f3 100644
--- a/src/plugins/wp/Stats.mli
+++ b/src/plugins/wp/Stats.mli
@@ -36,6 +36,7 @@ type pstats = {
 
 (** Cumulated Stats *)
 type stats = {
+  verdict : VCS.verdict ;
   provers : (VCS.prover * pstats) list ;
   tactics : int ;
   proved : int ;
@@ -48,9 +49,12 @@ type stats = {
 
 val pp_pstats : Format.formatter -> pstats -> unit
 val pp_stats : shell:bool -> updating:bool -> Format.formatter -> stats -> unit
+val pretty : Format.formatter -> stats -> unit
 
-val results : smoke:bool -> (VCS.prover * VCS.result) list -> VCS.verdict * stats
+val empty : stats
+val results : smoke:bool -> (VCS.prover * VCS.result) list -> stats
 val tactical : qed:float -> stats list -> stats
+val script : stats -> VCS.result
 
 val proofs : stats -> int
 val complete : stats -> bool
diff --git a/src/plugins/wp/VC.ml b/src/plugins/wp/VC.ml
index cf8ebc3518c..8a033bbc2ce 100644
--- a/src/plugins/wp/VC.ml
+++ b/src/plugins/wp/VC.ml
@@ -41,8 +41,8 @@ let get_logout = Wpo.get_file_logout
 let get_logerr = Wpo.get_file_logerr
 let is_trivial = Wpo.is_trivial
 let is_valid = Wpo.is_valid
-let is_unknown = Wpo.is_unknown
 let is_passed = Wpo.is_passed
+let has_unknown = Wpo.has_unknown
 
 let get_formula po =
   match po.po_formula with
diff --git a/src/plugins/wp/VC.mli b/src/plugins/wp/VC.mli
index ac4abf38e2a..fc3724a2b36 100644
--- a/src/plugins/wp/VC.mli
+++ b/src/plugins/wp/VC.mli
@@ -48,11 +48,11 @@ val get_sequent : t -> Conditions.sequent
 val get_formula: t -> Lang.F.pred
 val is_trivial : t -> bool
 
-(** One prover at least returns Valid verdict. *)
+(** One prover at least returns a valid verdict. *)
 val is_valid : t -> bool
 
-(** No prover with Valid verdict, and at least one non-Valid verdict. *)
-val is_unknown : t -> bool
+(** At least one non-valid verdict. *)
+val has_unknown : t -> bool
 
 (** Same as [is_valid] for non-smoke tests. For smoke-tests,
     same as [is_unknown]. *)
diff --git a/src/plugins/wp/VCS.ml b/src/plugins/wp/VCS.ml
index 7e56f3caa29..b4d955ed251 100644
--- a/src/plugins/wp/VCS.ml
+++ b/src/plugins/wp/VCS.ml
@@ -209,6 +209,7 @@ let is_result = function
 let is_verdict r = is_result r.verdict
 
 let is_valid = function { verdict = Valid } -> true | _ -> false
+let is_not_valid r = is_verdict r && not (is_valid r)
 let is_computing = function { verdict=Computing _ } -> true | _ -> false
 
 let smoked = function
@@ -349,16 +350,16 @@ let pp_result_qualif ?(updating=true) prover result fmt =
   else
     pp_result fmt result
 
+let vrank = function
+  | NoResult | Computing _ -> 0
+  | Failed -> 1
+  | Unknown -> 2
+  | Timeout | Stepout -> 3
+  | Valid -> 4
+  | Invalid -> 5
+
 let compare p q =
-  let rank = function
-    | NoResult | Computing _ -> 0
-    | Failed -> 1
-    | Unknown -> 2
-    | Timeout | Stepout -> 3
-    | Valid -> 4
-    | Invalid -> 5
-  in
-  let r = rank q.verdict - rank p.verdict in
+  let r = vrank q.verdict - vrank p.verdict in
   if r <> 0 then r else
     let s = Stdlib.compare p.prover_steps q.prover_steps in
     if s <> 0 then s else
@@ -394,5 +395,4 @@ let leq r1 r2 =
   | _ -> compare r1 r2 <= 0
 
 let choose r1 r2 = if leq r1 r2 then r1 else r2
-
 let best = List.fold_left choose no_result
diff --git a/src/plugins/wp/VCS.mli b/src/plugins/wp/VCS.mli
index 1f384006307..818fb7667f5 100644
--- a/src/plugins/wp/VCS.mli
+++ b/src/plugins/wp/VCS.mli
@@ -114,6 +114,7 @@ val is_auto : prover -> bool
 val is_result : verdict -> bool
 val is_verdict : result -> bool
 val is_valid: result -> bool
+val is_not_valid: result -> bool
 val is_computing: result -> bool
 val is_proved: smoke:bool -> result -> bool
 
@@ -129,6 +130,7 @@ val pp_result_qualif : ?updating:bool -> prover -> result ->
 
 val compare : result -> result -> int (* best is minimal *)
 
+val combine : verdict -> verdict -> verdict
 val merge : result -> result -> result
 val leq : result -> result -> bool
 val choose : result -> result -> result
diff --git a/src/plugins/wp/gui/GuiNavigator.ml b/src/plugins/wp/gui/GuiNavigator.ml
index c99c230f647..8928431f449 100644
--- a/src/plugins/wp/gui/GuiNavigator.ml
+++ b/src/plugins/wp/gui/GuiNavigator.ml
@@ -139,7 +139,7 @@ class behavior
             | `All -> true
             | `Smoke -> Wpo.is_smoke_test g
             | `Scripts -> has_proof g
-            | `ToProve -> to_prove g && (Wpo.is_unknown g || has_proof g)
+            | `ToProve -> to_prove g && (Wpo.has_unknown g || has_proof g)
           in if ok then list#add g
         in
         begin
diff --git a/src/plugins/wp/register.ml b/src/plugins/wp/register.ml
index aa8d04a8b6a..bdd6e296559 100644
--- a/src/plugins/wp/register.ml
+++ b/src/plugins/wp/register.ml
@@ -295,20 +295,20 @@ let do_wpo_result goal prover res =
       do_wpo_stat goal prover res ;
     end
 
-let do_report_stats ~shell ~updating ~smoke goal (verdict,stats) =
+let do_report_stats ~shell ~updating ~smoke goal (stats : Stats.stats) =
   let status =
     if smoke then
-      match verdict with
-      | VCS.NoResult | Computing _ -> ""
+      match stats.verdict with
       | Valid -> "Failed (Doomed)"
       | Failed ->  "Unknown (Failure)"
-      | Invalid -> "Passed (Invalid)"
+      | NoResult | Computing _ -> "Unknown (Incomplete)"
       | Unknown -> "Passed (Unknown)"
       | Timeout -> "Passed (Timeout)"
       | Stepout -> "Passed (Stepout)"
+      | Invalid -> "Passed (Invalid)"
     else
-      match verdict with
-      | VCS.NoResult | Computing _ -> ""
+      match stats.verdict with
+      | NoResult | Computing _ -> ""
       | Valid -> "Valid"
       | Invalid -> "Invalid"
       | Failed ->  "Failure"
@@ -329,12 +329,11 @@ let do_wpo_success ~shell ~updating goal success =
         "[Generated] Goal %s (%a)" (Wpo.get_gid goal) VCS.pp_prover prover
   else
     let smoke = Wpo.is_smoke_test goal in
-    let prs = Wpo.get_results goal in
-    let (verdict,_) as vstats = Stats.results ~smoke prs in
+    let stats = ProofEngine.consolidated goal in
     begin
-      if shell || verdict <> Valid then
-        do_report_stats ~shell ~updating goal ~smoke vstats ;
-      if smoke && verdict <> Valid then
+      if shell || stats.verdict <> Valid then
+        do_report_stats ~shell ~updating goal ~smoke stats ;
+      if smoke && stats.verdict <> Valid then
         begin
           let target = Wpo.get_target goal in
           let source = fst (Property.location target) in
diff --git a/src/plugins/wp/wpo.ml b/src/plugins/wp/wpo.ml
index 852c10d1bdd..361ef48ee20 100644
--- a/src/plugins/wp/wpo.ml
+++ b/src/plugins/wp/wpo.ml
@@ -830,18 +830,22 @@ let compute g =
 let is_valid g =
   is_trivial g || List.exists (fun (_,r) -> VCS.is_valid r) (get_results g)
 
-let is_unknown g =
-  not (is_valid g) &&
-  List.exists
-    (fun (_,r) -> VCS.is_verdict r && not (VCS.is_valid r))
-    (get_results g)
+let all_not_valid g =
+  not (is_trivial g) &&
+  List.for_all (fun (_,r) -> VCS.is_not_valid r) (get_results g)
 
 let is_passed g =
   if is_smoke_test g then
-    is_unknown g
+    all_not_valid g
   else
     is_valid g
 
+let has_unknown g =
+  not (is_valid g) &&
+  List.exists
+    (fun (_,r) -> VCS.is_verdict r && not (VCS.is_valid r))
+    (get_results g)
+
 (* -------------------------------------------------------------------------- *)
 (* --- Proof Obligations : Pretty-printing                                --- *)
 (* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/wp/wpo.mli b/src/plugins/wp/wpo.mli
index 4023e885a48..e5f8c3313b8 100644
--- a/src/plugins/wp/wpo.mli
+++ b/src/plugins/wp/wpo.mli
@@ -199,11 +199,14 @@ val is_trivial : t -> bool
 val is_valid : t -> bool
 (** Checks for some prover with valid verdict (no forced simplification) *)
 
-val is_unknown : t -> bool
-(** Checks that all provers has a non-valid verdict *)
+val all_not_valid : t -> bool
+(** Checks for all provers to give a non-valid, computed verdict *)
 
 val is_passed : t -> bool
-(** valid, or unknown for smoke tests *)
+(** valid, or all-not-valid for smoke tests *)
+
+val has_unknown : t -> bool
+(** Checks there is some provers with a non-valid verdict *)
 
 val warnings : t -> Warning.t list
 
-- 
GitLab