Skip to content

Commit 37e7523

Browse files
committed
Forward call with framed pre
1 parent de800c1 commit 37e7523

7 files changed

Lines changed: 238 additions & 0 deletions

File tree

src/ecHiTacticals.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ and process1_phl (_ : ttenv) (t : phltactic located) (tc : tcenv1) =
207207
| Phrex_elim -> EcPhlExists.t_hr_exists_elim
208208
| Phrex_intro (fs, b) -> EcPhlExists.process_exists_intro ~elim:b fs
209209
| Phecall (oside, x) -> EcPhlExists.process_ecall oside x
210+
| Phecallfwd x -> EcPhlExists.process_ecallfwd x
210211
| Pexfalso -> EcPhlAuto.t_exfalso
211212
| Pbydeno (mode, info) -> EcPhlDeno.process_deno mode info
212213
| Pbyupto -> EcPhlUpto.process_uptobad

src/ecMatching.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,10 @@ module EV = struct
372372
| Some (`Set _) -> true
373373
| _ -> false
374374

375+
let map (f : 'a -> 'a) (m : 'a evmap) =
376+
{ ev_map = Mid.map (omap f) m.ev_map
377+
; ev_unset = m.ev_unset }
378+
375379
let doget (x : ident) (m : 'a evmap) =
376380
match get x m with
377381
| Some (`Set a) -> a

src/ecMatching.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ module EV : sig
150150
val isset : ident -> 'a evmap -> bool
151151
val set : ident -> 'a -> 'a evmap -> 'a evmap
152152
val get : ident -> 'a evmap -> [`Unset | `Set of 'a] option
153+
val map : ('a -> 'a) -> 'a evmap -> 'a evmap
153154
val doget : ident -> 'a evmap -> 'a
154155
val fold : (ident -> 'a -> 'b -> 'b) -> 'a evmap -> 'b -> 'b
155156
val filled : 'a evmap -> bool

src/ecParser.mly

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3118,6 +3118,9 @@ interleave_info:
31183118
| ECALL s=side? x=paren(p=qident tvi=tvars_app? fs=sform* { (p, tvi, fs) })
31193119
{ Phecall (s, x) }
31203120

3121+
| ECALL RRARROW pterm=paren(p=qident tvi=tvars_app? fs=loc(gpterm_arg)* { (p, tvi, fs) })
3122+
{ Phecallfwd pterm }
3123+
31213124
| EXFALSO
31223125
{ Pexfalso }
31233126

src/ecParsetree.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,9 @@ type matchmode = [
736736
(* -------------------------------------------------------------------- *)
737737
type prrewrite = [`Rw of ppterm | `Simpl]
738738

739+
(* -------------------------------------------------------------------- *)
740+
type pecallfwd = pqsymbol * ptyannot option * ppt_arg located list
741+
739742
(* -------------------------------------------------------------------- *)
740743
type phltactic =
741744
| Pskip
@@ -775,6 +778,7 @@ type phltactic =
775778
| Phrex_elim
776779
| Phrex_intro of (pformula list * bool)
777780
| Phecall of (oside * (pqsymbol * ptyannot option * pformula list))
781+
| Phecallfwd of pecallfwd
778782
| Pexfalso
779783
| Pbydeno of ([`PHoare | `Equiv | `EHoare ] * (deno_ppterm * bool * pformula option))
780784
| PPr of (pformula * pformula) option

src/phl/ecPhlExists.ml

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,3 +239,227 @@ let process_ecall oside (l, tvi, fs) tc =
239239
let tc = FApi.t_focus (EcLowGoal.Apply.t_apply_bwd_hi ~dpe:true pt) tc in
240240

241241
FApi.t_last EcPhlAuto.t_auto (FApi.t_rotate `Right 1 tc)
242+
243+
(* -------------------------------------------------------------------- *)
244+
let process_ecallfwd ((ctt_path, ctt_tvi, ctt_args) : APT.pecallfwd) (tc : tcenv1) =
245+
let hyps = FApi.tc1_hyps tc in
246+
let env = EcEnv.LDecl.toenv hyps in
247+
248+
(* Check that we are in a Hoare statement and that first instruction
249+
* is a call statement. *)
250+
let concl = tc1_as_hoareS tc in
251+
let (lvalue, funname, _), _ = pf_first_call !!tc concl.hs_s in
252+
253+
let lvalue =
254+
match lvalue with
255+
| Some lvalue -> lvalue
256+
| None ->
257+
tc_error !!tc "the call must have a left-value" in
258+
259+
(* Type-check contract (lemma) - apply it fully to find the Hoare contract *)
260+
let ptenv = PT.ptenv_of_penv (LDecl.push_active_ss concl.hs_m hyps) !!tc in
261+
let contract =
262+
let rec apply_all (contract : PT.pt_ev) =
263+
if is_some (TTC.destruct_product ~reduce:true hyps contract.ptev_ax) then
264+
apply_all (PT.apply_pterm_to_hole contract)
265+
else contract
266+
in
267+
let contract = PT.process_pterm ptenv (APT.FPNamed (ctt_path, ctt_tvi)) in
268+
let contract, _ = PT.process_pterm_args_app contract ctt_args in
269+
apply_all contract
270+
in
271+
272+
let rec collect_pvars_from_pt (pt : proofterm) =
273+
match pt with
274+
| PTApply { pt_args = args } -> begin
275+
args
276+
|> List.to_seq
277+
|> Seq.map collect_pvars_from_ptarg
278+
|> Seq.fold_left EcPV.PV.union EcPV.PV.empty
279+
end
280+
| PTQuant (_, pt) ->
281+
collect_pvars_from_pt pt
282+
283+
and collect_pvars_from_ptarg (ptarg : pt_arg) =
284+
match ptarg with
285+
| PAFormula f -> collect_pvars_from_form f
286+
| PAMemory _ -> EcPV.PV.empty
287+
| PAModule _ -> EcPV.PV.empty
288+
| PASub pt -> odfl EcPV.PV.empty (omap collect_pvars_from_pt pt)
289+
290+
and collect_pvars_from_form (f : form) =
291+
let pvs = ref EcPV.PV.empty in
292+
let rec doit (f : form) =
293+
match f.f_node with
294+
| Fpvar (pv, _) ->
295+
pvs := EcPV.PV.add env pv f.f_ty !pvs
296+
| _ -> EcFol.f_iter doit f
297+
in doit f; !pvs
298+
in
299+
300+
begin
301+
let contract =
302+
try
303+
destr_hoareF contract.ptev_ax
304+
with DestrError _ ->
305+
tc_error_lazy ~loc:(L.loc ctt_path) !!tc (fun fmt ->
306+
Format.fprintf fmt
307+
"contract %a should be a Hoare statement"
308+
EcSymbols.pp_qsymbol (L.unloc ctt_path)
309+
)
310+
in
311+
312+
if not (EcReduction.EqTest.for_xp env funname contract.hf_f) then begin
313+
tc_error_lazy ~loc:(L.loc ctt_path) !!tc (fun fmt ->
314+
let ppe = EcPrinting.PPEnv.ofenv env in
315+
Format.fprintf fmt
316+
"contract %a should be for the procedure %a, not %a"
317+
EcSymbols.pp_qsymbol (L.unloc ctt_path)
318+
(EcPrinting.pp_funname ppe) funname
319+
(EcPrinting.pp_funname ppe) contract.hf_f
320+
)
321+
end;
322+
323+
if not (POE.is_empty (hf_po contract).hsi_inv) then
324+
tc_error ~loc:(L.loc ctt_path) !!tc
325+
"contract must have an empty exception post-condition";
326+
end;
327+
328+
let pvs = collect_pvars_from_pt contract.ptev_pt in
329+
let pvs, _ = EcPV.PV.elements pvs in
330+
let ids = List.map (fun (pv, ty) ->
331+
(EcIdent.create (Format.sprintf "%s_" (EcTypes.symbol_of_pv pv)), ty)
332+
) pvs in
333+
let pvs_as_inv = List.map (fun (pv, ty) ->
334+
Inv_ss (f_pvar pv ty (fst concl.hs_m))
335+
) pvs in
336+
337+
let tc = t_hr_exists_intro_r pvs_as_inv tc in
338+
let tc = FApi.t_focus (t_hr_exists_elim_r ~bound:(List.length ids)) tc in
339+
let tc = FApi.t_focus (t_intros_i (List.fst ids)) tc in
340+
341+
let subst = List.fold_left (fun subst ((pv, ty), x) ->
342+
EcPV.PVM.add env pv (fst concl.hs_m) (f_local x ty) subst
343+
) EcPV.PVM.empty (List.combine pvs (List.fst ids)) in
344+
345+
let contract =
346+
let hyps = List.fold_left (fun hyps (x, ty) ->
347+
EcEnv.LDecl.add_local x (LD_var (ty, None)) hyps
348+
) contract.ptev_env.pte_hy ids in
349+
350+
{ contract with
351+
ptev_env = { contract.ptev_env with pte_hy = hyps} } in
352+
353+
let rec subst_pt (pt : proofterm) =
354+
match pt with
355+
| PTApply { pt_head; pt_args } ->
356+
PTApply
357+
{ pt_head = subst_pt_head pt_head
358+
; pt_args = List.map subst_pt_arg pt_args }
359+
| PTQuant _ -> assert false
360+
361+
and subst_pt_head (pth : pt_head) =
362+
match pth with
363+
| PTHandle _
364+
| PTLocal _
365+
| PTGlobal _ -> pth
366+
| PTCut (f, cs) -> PTCut (subst_form f, cs)
367+
| PTTerm pt -> PTTerm (subst_pt pt)
368+
369+
and subst_pt_arg (pta : pt_arg) =
370+
match pta with
371+
| PAFormula f -> PAFormula (subst_form f)
372+
| PAMemory _ -> pta
373+
| PAModule _ -> pta
374+
| PASub pt -> PASub (omap subst_pt pt)
375+
376+
and subst_pt_ev (ptev : PT.pt_ev) =
377+
let ptev_env = subst_pt_env ptev.ptev_env in
378+
let ptev_ax = subst_form ptev.ptev_ax in
379+
let ptev_pt = subst_pt ptev.ptev_pt in
380+
PT.{ ptev_env; ptev_ax; ptev_pt; }
381+
382+
and subst_pt_env (ptenv : PT.pt_env) =
383+
{ pte_pe = ptenv.pte_pe
384+
; pte_hy = ptenv.pte_hy
385+
; pte_ue = EcUnify.UniEnv.copy ptenv.pte_ue
386+
; pte_ev = ref (subst_mevmap !(ptenv.pte_ev)) }
387+
388+
and subst_mevmap (evm : EcMatching.mevmap) =
389+
{ evm with
390+
evm_form = EcMatching.EV.map subst_form evm.evm_form; }
391+
392+
and subst_form (f : form) =
393+
EcPV.PVM.subst env subst f
394+
in
395+
396+
let contract = subst_pt_ev contract in
397+
398+
let seqf =
399+
let inv = destr_hoareF contract.ptev_ax in
400+
let inv = POE.lower (hf_po inv) in
401+
let inv = ss_inv_rebind inv (fst concl.hs_m) in
402+
let lv =
403+
List.map
404+
(fun (pv, ty) -> (f_pvar pv ty inv.m).inv)
405+
(EcModules.lv_to_ty_list lvalue) in
406+
let sargs =
407+
EcPV.PVM.add
408+
env EcTypes.pv_res inv.m
409+
(f_tuple lv) EcPV.PVM.empty in
410+
411+
{ inv = EcPV.PVM.subst env sargs inv.inv; m = inv.m; } in
412+
413+
let seqf_frame =
414+
let wr = EcPV.lp_write env lvalue in
415+
416+
let rec doit (f : form) =
417+
match sform_of_form f with
418+
| SFand (mode, (f1, f2)) -> begin
419+
match doit f1, doit f2 with
420+
| None, None -> None
421+
| Some f, None | None, Some f -> Some f
422+
| Some f1, Some f2 -> begin
423+
match mode with
424+
| `Sym -> Some (f_and f1 f2)
425+
| `Asym -> Some (f_anda f1 f2)
426+
end
427+
end
428+
429+
| _ -> begin
430+
let pvs =
431+
let pvs = ref EcPV.PV.empty in
432+
let rec f_read (f : form) =
433+
match f.f_node with
434+
| Fpvar (pv, _) -> pvs := EcPV.PV.add env pv f.f_ty !pvs
435+
| _ -> f_iter f_read f
436+
in f_read f; !pvs in
437+
438+
if EcPV.PV.indep env pvs wr then
439+
Some f
440+
else None
441+
end
442+
443+
in { inv = odfl f_true (doit (hs_pr concl).inv); m = (hs_pr concl).m; } in
444+
445+
let tc =
446+
FApi.t_first
447+
(EcPhlSeq.t_hoare_seq (Zpr.cpos 1) (map_ss_inv2 f_and seqf seqf_frame))
448+
tc in
449+
450+
let tc = FApi.t_first EcPhlHoare.t_hoaresplit tc in
451+
let tc = FApi.t_first (EcPhlConseq.t_conseqauto ~delta:false ?tsolve:None) tc in
452+
let tc = FApi.t_first EcPhlTAuto.t_hoare_true tc in
453+
454+
let tc = FApi.t_first (EcPhlCall.t_call None contract.ptev_ax) tc in
455+
let tc = FApi.t_sub [
456+
EcLowGoal.Apply.t_apply_bwd_hi ~dpe:true contract.ptev_pt;
457+
EcPhlSkip.t_skip;
458+
t_id
459+
] tc in
460+
461+
let tc =
462+
FApi.t_firsts
463+
(t_generalize_hyps ~clear:`Yes (List.fst ids)) 2 tc in
464+
465+
tc

src/phl/ecPhlExists.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ val t_hr_exists_intro : inv list -> backward
1111
(* -------------------------------------------------------------------- *)
1212
val process_exists_intro : elim:bool -> pformula list -> backward
1313
val process_ecall : oside -> pqsymbol * ptyannot option * pformula list -> backward
14+
val process_ecallfwd : pecallfwd -> backward

0 commit comments

Comments
 (0)