package rocq-runtime

  1. Overview
  2. Docs
The Rocq Prover -- Core Binaries and Tools

Install

dune-project
 Dependency

Authors

Maintainers

Sources

rocq-9.3.0.tar.gz
sha256=3f0fc283e8644394aa9c7a6e3995b6d9ebbe1e6dda712bf431f9c372dcef95ad

doc/src/rocq-runtime.vernac/vernacgoal.ml.html

Source file vernacgoal.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
(************************************************************************)
(*         *      The Rocq Prover / The Rocq Development Team           *)
(*  v      *         Copyright INRIA, CNRS and contributors             *)
(* <O___,, * (see version control and CREDITS file for authors & dates) *)
(*   \VV/  **************************************************************)
(*    //   *    This file is distributed under the terms of the         *)
(*         *     GNU Lesser General Public License Version 2.1          *)
(*         *     (see LICENSE file for the text of the license)         *)
(************************************************************************)

open Pp
open CErrors
open Util
open Constr
open Environ
open Evd
open Printer

module NamedDecl = Context.Named.Declaration

(* This is set on by proofgeneral proof-tree mode. But may be used for
   other purposes *)
let print_goal_tag_opt_name = ["Printing";"Goal";"Tags"]
let { Goptions.get = should_tag } =
  Goptions.declare_bool_option_and_ref
    ~key:print_goal_tag_opt_name
    ~value:false
    ()

let { Goptions.get = should_unfoc } =
  Goptions.declare_bool_option_and_ref
    ~key:["Printing";"Unfocused"]
    ~value:false
    ()

let { Goptions.get = should_gname } =
  Goptions.declare_bool_option_and_ref
    ~key:["Printing";"Goal";"Names"]
    ~value:false
    ()

let print_goal_name sigma ev =
  should_gname () || Evd.evar_has_unambiguous_name ev sigma

let current_combined = PrintingFlags.current

(* display goal parts (Proof mode) *)

let goal_repr sigma g =
  let EvarInfo evi = Evd.find sigma g in
  let env = Evd.evar_filtered_env (Global.env ()) evi in
  let concl = match Evd.evar_body evi with
  | Evd.Evar_empty -> Evd.evar_concl evi
  | Evd.Evar_defined b -> Retyping.get_type_of env sigma b
  in
  env, concl

(* display complete goal
 og_s has goal+sigma on the previous proof step for diffs
 g_s has goal+sigma on the current proof step
 *)
let pr_goal ?(flags=current_combined()) ?(ogoal=None) sigma g =
  let goal = match ogoal with
  | Some og_s ->
    let g = Proof_diffs.make_goal (Global.env ()) sigma g in
    let (hyps_pp_list, concl_pp) = Proof_diffs.diff_goal ?og_s ~flags g in
    let hyp_list_to_pp hyps =
      match hyps with
      | h :: tl -> List.fold_left (fun x y -> x ++ cut () ++ y) h tl
      | [] -> mt ()
    in
    v 0 (
      (hyp_list_to_pp hyps_pp_list) ++ cut () ++
      str "============================" ++ cut () ++
      concl_pp)
  | None ->
    let env, concl = goal_repr sigma g in
      pr_context_of ~flags env sigma ++ cut () ++
        str "============================" ++ cut ()  ++
        hov 0 (pr_letype_env ~goal_concl_style:true ~flags env sigma concl)
  in
  str "  " ++ v 0 goal

(* display a goal tag *)
let pr_goal_tag g =
  let s = " (ID " ^ Proof.goal_uid g ^ ")" in
  str s

(* display a goal name *)
let pr_goal_name sigma g =
  if print_goal_name sigma g then str " " ++ Pp.surround (pr_existential_key (Global.env ()) sigma g)
  else mt ()

let pr_goal_header nme sigma g =
  str "goal " ++ nme ++ (if should_tag() then pr_goal_tag g else str"")
  ++ (if print_goal_name sigma g then str " " ++ Pp.surround (pr_existential_key (Global.env ()) sigma g) else mt ())

(* display the conclusion of a goal *)
let pr_concl ?(flags=current_combined()) n ?(ogoal=None) sigma g =
  let env, concl = goal_repr sigma g in
  let pc = match ogoal with
  | Some og_s ->
      Proof_diffs.diff_concl ?og_s ~flags (Proof_diffs.make_goal env sigma g)
  | None ->
      pr_letype_env ~goal_concl_style:true ~flags env sigma concl
  in
  let header = pr_goal_header (int n) sigma g in
  header ++ str " is:" ++ cut () ++ str" "  ++ pc

let get_goal_map oldp proof =
  match oldp with
  | _ when not (Proof_diffs.show_diffs ()) -> None
  | Some None -> Some None (* do diffs for first step in proof (ie, no previous proof state) *)
  | Some (Some op) -> (* do diffs *)
    Some (try Some (Proof_diffs.make_goal_map op proof)
    with Pp_diff.Diff_Failure msg ->
      Proof_diffs.notify_proof_diff_failure msg;
      None)
  | None -> None (* don't do diffs *)

let get_ogoal goal_map g =
  let get_ogs map g =
    match map with
    | None -> None
    | Some map -> Proof_diffs.map_goal g map
  in
  Option.map (fun map -> get_ogs map g) goal_map

let pr_selected_subgoal ?(flags=current_combined()) ?(ogoal=None) name sigma g =
  let pg = pr_goal ~flags ~ogoal sigma g in
  let header = pr_goal_header name sigma g in
  v 0 (header ++ str " is:" ++ cut () ++ pg)

let pr_subgoal ~flags oldp proof n sigma =
  let rec prrec p = function
    | [] -> user_err Pp.(str "No such goal.")
    | g::rest ->
        if Int.equal p 1 then
          let goal_map = get_goal_map oldp proof in
          let ogoal = get_ogoal goal_map g in
          pr_selected_subgoal ~flags ~ogoal (int n) sigma g
        else
          prrec (p-1) rest
  in
  prrec n

let pr_internal_existential_key ev = Evar.print ev

let print_evar_constraints ?(flags=current_combined()) gl sigma =
  let pr_env =
    match gl with
    | None -> fun e' -> pr_context_of e' ~flags sigma
    | Some g ->
       let env, _ = goal_repr sigma g in fun e' ->
       begin
         if Context.Named.equal Sorts.relevance_equal Constr.equal (named_context env) (named_context e') then
           if Context.Rel.equal Sorts.relevance_equal Constr.equal (rel_context env) (rel_context e') then mt ()
           else pr_rel_context_of ~flags e' sigma ++ str " |-" ++ spc ()
         else pr_context_of ~flags e' sigma ++ str " |-" ++ spc ()
       end
  in
  let pr_evconstr (pbty,env,t1,t2) =
    let t1 = Evarutil.nf_evar sigma t1
    and t2 = Evarutil.nf_evar sigma t2 in
    let env =
      (* We currently allow evar instances to refer to anonymous de Bruijn
         indices, so we protect the error printing code in this case by giving
         names to every de Bruijn variable in the rel_context of the conversion
         problem. MS: we should rather stop depending on anonymous variables, they
         can be used to indicate independency. Also, this depends on a strategy for
         naming/renaming *)
      Namegen.make_all_name_different env sigma in
    str" " ++
      hov 2 (pr_env env ++ pr_leconstr_env ~flags env sigma t1 ++ spc () ++
             str (match pbty with
                  | Conversion.CONV -> "=="
                  | Conversion.CUMUL -> "<=") ++
             spc () ++ pr_leconstr_env ~flags env sigma t2)
  in
  let pr_candidate ev evi (candidates,acc) =
    if Option.has_some (Evd.evar_candidates evi) then
      (succ candidates, acc ++ pr_evar ~flags sigma (ev,evi) ++ fnl ())
    else (candidates, acc)
  in
  let constraints =
    let _, cstrs = Evd.extract_all_conv_pbs sigma in
    if List.is_empty cstrs then mt ()
    else fnl () ++ str (String.plural (List.length cstrs) "unification constraint")
         ++ str":" ++ fnl () ++ hov 0 (prlist_with_sep fnl pr_evconstr cstrs)
  in
  let candidates, ppcandidates = Evd.fold_undefined pr_candidate sigma (0,mt ()) in
  constraints ++
    if candidates > 0 then
      fnl () ++ str (String.plural candidates "existential") ++
        str" with candidates:" ++ fnl () ++ hov 0 ppcandidates
    else mt ()

let { Goptions.get = should_print_dependent_evars } =
  Goptions.declare_bool_option_and_ref
    ~key:["Printing";"Dependent";"Evars";"Line"]
    ~value:false
    ()

let evar_nodes_of_term c =
  let rec evrec acc c =
    match kind c with
    | Evar (n, l) -> Evar.Set.add n (SList.Skip.fold evrec acc l)
    | _ -> Constr.fold evrec acc c
  in
  evrec Evar.Set.empty (EConstr.Unsafe.to_constr c)

(* spiwack: a few functions to gather evars on which goals depend. *)
let queue_set q is_dependent set =
  Evar.Set.iter (fun a -> Queue.push (is_dependent,a) q) set
let queue_term q is_dependent c =
  queue_set q is_dependent (evar_nodes_of_term c)

let process_dependent_evar q acc evm is_dependent e =
  let EvarInfo evi = Evd.find evm e in
  (* Queues evars appearing in the types of the goal (conclusion, then
     hypotheses), they are all dependent. *)
  let () = match Evd.evar_body evi with
  | Evar_empty ->
    queue_term q true (Evd.evar_concl evi)
  | Evar_defined b ->
    let env = Evd.evar_filtered_env (Global.env ()) evi in
    queue_term q true (Retyping.get_type_of env evm b)
  in
  List.iter begin fun decl ->
    let open NamedDecl in
    queue_term q true (NamedDecl.get_type decl);
    match decl with
    | LocalAssum _ -> ()
    | LocalDef (_,b,_) -> queue_term q true b
  end (EConstr.named_context_of_val (Evd.evar_hyps evi));
  match Evd.evar_body evi with
  | Evar_empty ->
      if is_dependent then Evar.Map.add e None acc else acc
  | Evar_defined b ->
      let subevars = evar_nodes_of_term b in
      (* evars appearing in the definition of an evar [e] are marked
         as dependent when [e] is dependent itself: if [e] is a
         non-dependent goal, then, unless they are reach from another
         path, these evars are just other non-dependent goals. *)
      queue_set q is_dependent subevars;
      if is_dependent then Evar.Map.add e (Some subevars) acc else acc

(** [gather_dependent_evars evm seeds] classifies the evars in [evm]
    as dependent_evars and goals (these may overlap). A goal is an evar
    appearing in the (partial) definition [seeds] (including defined evars). A
    dependent evar is an evar appearing in the type
    (hypotheses and conclusion) of a goal, or in the type or (partial)
    definition of a dependent evar.  The value return is a map
    associating to each dependent evar [None] if it has no (partial)
    definition or [Some s] if [s] is the list of evars appearing in
    its (partial) definition. This completely breaks the EConstr abstraction. *)
let gather_dependent_evars evm l =
  let q = Queue.create () in
  List.iter (queue_term q false) l;
  let acc = ref Evar.Map.empty in
  while not (Queue.is_empty q) do
    let (is_dependent,e) = Queue.pop q in
    (* checks if [e] has already been added to [!acc] *)
    begin if not (Evar.Map.mem e !acc) then
        acc := process_dependent_evar q !acc evm is_dependent e
    end
  done;
  !acc

(* /spiwack *)

let gather_dependent_evars_goal sigma goals =
  let map evk =
    let EvarInfo evi = Evd.find sigma evk in
    EConstr.mkEvar (evk, Evd.evar_identity_subst evi)
  in
  gather_dependent_evars sigma (List.map map goals)

let print_dependent_evars_core gl sigma evars =
  let mt_pp = mt () in
  let evars_pp = Evar.Map.fold (fun e i s ->
      let e' = pr_internal_existential_key e in
      let sep = if s = mt_pp then "" else ", " in
      s ++ str sep ++ e' ++
      (match i with
       | None -> str ":" ++ (Termops.pr_existential_key (Global.env ()) sigma e)
       | Some i ->
         let using = Evar.Set.fold (fun d s ->
             s ++ str " " ++ (pr_internal_existential_key d))
             i mt_pp in
         str " using" ++ using))
      evars mt_pp
  in
  let evars_current_pp = match gl with
    | None -> mt_pp
    | Some gl ->
      let evars_current = gather_dependent_evars_goal sigma [gl] in
      Evar.Map.fold (fun e _ s ->
          s ++ str " " ++ (pr_internal_existential_key e))
        evars_current mt_pp
  in
  cut () ++ cut () ++
  str "(dependent evars: " ++ evars_pp ++
  str "; in current goal:" ++ evars_current_pp ++ str ")"


let print_dependent_evars gl sigma seeds =
  if should_print_dependent_evars () then
    let evars = gather_dependent_evars_goal sigma seeds in
    print_dependent_evars_core gl sigma evars
  else mt ()

let print_dependent_evars_entry gl sigma = function
  | None -> mt ()
  | Some entry ->
    if should_print_dependent_evars () then
      let terms = List.map pi2 (Proofview.initial_goals entry) in
      let evars = gather_dependent_evars sigma terms in
      print_dependent_evars_core gl sigma evars
    else mt ()

(* Print open subgoals. Checks for uninstantiated existential variables *)
(* spiwack: [entry] is for printing dependent evars in emacs mode. *)
(* spiwack: [pr_first] is true when the first goal must be singled out
   and printed in its entirety. *)
(* [os_map] is derived from the previous proof step, used for diffs *)
let pr_subgoals ?(pr_first=true) ?goalmap ?entry
    ~flags sigma ~shelf ~stack ~unfocused ~goals =

  (* Printing functions for the extra informations. *)
  let rec print_stack a = function
    | [] -> Pp.int a
    | b::l -> Pp.int a ++ str"-" ++ print_stack b l
  in
  let print_unfocused_nums l =
    match l with
    | [] -> None
    | a::l -> Some (str"unfocused: " ++ print_stack a l)
  in
  let print_shelf l =
    match l with
    | [] -> None
    | _ -> Some (str"shelved: " ++ Pp.int (List.length l))
  in
  let rec print_comma_separated_list a l =
    match l with
    | [] -> a
    | b::l -> print_comma_separated_list (a++str", "++b) l
  in
  let print_extra_list l =
    match l with
    | [] -> Pp.mt ()
    | a::l -> Pp.spc () ++ str"(" ++ print_comma_separated_list a l ++ str")"
  in
  let extra = Option.List.flatten [ print_unfocused_nums stack ; print_shelf shelf ] in
  let print_extra = print_extra_list extra in
  let focused_if_needed =
    let needed = not (CList.is_empty extra) && pr_first in
    if needed then str" focused "
    else str" " (* non-breakable space *)
  in

  let rec pr_rec n = function
    | [] -> (mt ())
    | g::rest ->
      let ogoal = get_ogoal goalmap g in
      let pc = pr_concl ~flags n ~ogoal sigma g in
        let prest = pr_rec (n+1) rest in
        (cut () ++ pc ++ prest)
  in
  let print_multiple_goals g l =
    if pr_first then
      let ogoal = get_ogoal goalmap g in
      pr_goal ~flags ~ogoal sigma g
      ++ (if l=[] then mt () else cut ())
      ++ pr_rec 2 l
    else
      pr_rec 1 (g::l)
  in
  let pr_evar_info gl =
    let first_goal = if pr_first then gl else None in
    print_evar_constraints ~flags gl sigma ++ print_dependent_evars_entry first_goal sigma entry
  in

  (* Main function *)
  match goals with
  | [] ->
    let exl = Evd.undefined_map sigma in
    if Evar.Map.is_empty exl then
      v 0 (str "No more goals." ++ pr_evar_info None)
    else
      let pei = pr_evars_int ~flags sigma ~shelf ~given_up:[] 1 exl in
      v 0 ((str "No more goals,"
          ++ str " but there are non-instantiated existential variables:"
          ++ cut () ++ (hov 0 pei)
          ++ pr_evar_info None
          ++ cut () ++ str "You can use Unshelve."))
  | g1::rest ->
      let goals = print_multiple_goals g1 rest in
      let ngoals = List.length rest+1 in
      v 0 (
        hov 0 (int ngoals ++ focused_if_needed ++ str(String.plural ngoals "goal")
               ++ print_extra)
        ++ str (if pr_first && (should_gname()) && ngoals > 1 then ", goal 1" else "")
        ++ (if pr_first && should_tag() then pr_goal_tag g1 else str"")
        ++ (if pr_first then pr_goal_name sigma g1 else mt()) ++ cut () ++ goals
        ++ (if unfocused=[] then str ""
           else (cut() ++ cut() ++ str "*** Unfocused goals:" ++ cut()
                 ++ pr_rec (List.length rest + 2) unfocused))
        ++ pr_evar_info (Some g1)
      )

let pr_open_subgoals ?(quiet=false) ?(oldp=None) ?(flags=current_combined()) proof =
  (* spiwack: it shouldn't be the job of the printer to look up stuff
     in the [evar_map], I did stuff that way because it was more
     straightforward, but seriously, [Proof.proof] should return
     [evar_info]-s instead. *)
  let p = proof in
  let Proof.{goals; stack; sigma;entry} = Proof.data p in
  let shelf = Evd.shelf sigma in
  let given_up = Evd.given_up sigma in
  let stack = List.map (fun (l,r) -> List.length l + List.length r) stack in
  begin match goals with
  | [] -> let bgoals = Proof.background_subgoals p in
          begin match bgoals,shelf,given_up with
          | [] , [] , g when Evar.Set.is_empty g -> pr_subgoals ~flags sigma ~entry ~shelf ~stack ~unfocused:[] ~goals
          | [] , [] , _ ->
             Feedback.msg_info (str "No more goals, but there are some goals you gave up:");
             fnl ()
            ++ pr_subgoals ~pr_first:false ~flags sigma ~entry ~shelf:[] ~stack:[] ~unfocused:[] ~goals:(Evar.Set.elements given_up)
            ++ fnl () ++ str "You need to go back and solve them."
          | [] , _ , _ ->
            Feedback.msg_info (str "All the remaining goals are on the shelf.");
            fnl ()
            ++ pr_subgoals ~pr_first:false ~flags sigma ~entry ~shelf:[] ~stack:[] ~unfocused:[] ~goals:shelf
          | _ , _, _ ->
            let () =
              if quiet then ()
              else
              Feedback.msg_info
                (str "This subproof is complete, but there are some unfocused goals." ++
                (let s = Proof_bullet.suggest p in
                 if Pp.ismt s then s else fnl () ++ s) ++
                fnl ())
            in
            pr_subgoals ~pr_first:false ~flags sigma ~entry ~shelf ~stack:[] ~unfocused:[] ~goals:bgoals
          end
  | _ ->
     let bgoals = Proof.background_subgoals p in
     let bgoals_focused, bgoals_unfocused = List.partition (fun x -> List.mem x goals) bgoals in
     let unfocused_if_needed = if should_unfoc() then bgoals_unfocused else [] in
     let goalmap = get_goal_map oldp proof in
     pr_subgoals ~flags ~pr_first:true ?goalmap sigma ~entry ~shelf ~stack:[]
        ~unfocused:unfocused_if_needed ~goals:bgoals_focused
  end

let pr_nth_open_subgoal ?(flags=current_combined()) ?(oldp=None) ~proof n =
  let Proof.{goals;sigma} = Proof.data proof in
  pr_subgoal ~flags oldp proof n sigma goals

let pr_goal_by_id ?(flags=current_combined()) ?(oldp=None) ~proof id =
  try
    let { Proof.sigma } = Proof.data proof in
    let g = Evd.evar_key id sigma in
    let goal_map = get_goal_map oldp proof in
    let ogoal = get_ogoal goal_map g in
    pr_selected_subgoal ~flags ~ogoal (Libnames.pr_qualid id) sigma g
  with Not_found -> user_err Pp.(str "No such goal.")

(** print a goal identified by the goal id as it appears in -emacs mode.
    sid should be the Stm state id corresponding to proof.  Used to support
    the Prooftree tool in Proof General. (https://askra.de/software/prooftree/).
*)
let pr_goal_emacs ?(flags=current_combined()) ~proof gid sid =
  match proof with
  | None -> user_err Pp.(str "No proof for that state.")
  | Some proof ->
    let pr sigma gs =
      v 0 ((str "goal ID " ++ (int gid) ++ str " at state " ++ (int sid)) ++ cut ()
          ++ pr_goal ~flags sigma gs)
    in
    try
      let { Proof.sigma } = Proof.data proof in
      let gl = Evar.unsafe_of_int gid in
      v 0 (pr sigma gl ++ print_dependent_evars (Some gl) sigma [ gl ])
    with Not_found -> user_err Pp.(str "No such goal.")