Skip to content

Commit 58cc113

Browse files
committed
Add contextual rewrite-pattern selection
Allow rewrite patterns to designate a subterm inside a larger context with the [x in pattern] syntax. This lets rewrite target exactly the occurrence named by the surrounding context, and adds regression coverage for that form. The context variable must appear exactly once in the pattern (linearity check). Delta expansion and conversion are disabled during contextual pattern matching to ensure position computation remains sound.
1 parent 0d1602f commit 58cc113

7 files changed

Lines changed: 361 additions & 59 deletions

File tree

src/ecHiGoal.ml

Lines changed: 201 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ open EcLowGoal
1818

1919
module Sid = EcIdent.Sid
2020
module Mid = EcIdent.Mid
21+
module Mint = EcMaps.Mint
2122
module Sp = EcPath.Sp
2223

2324
module ER = EcReduction
@@ -249,6 +250,8 @@ module LowRewrite = struct
249250
| LRW_IdRewriting
250251
| LRW_RPatternNoMatch
251252
| LRW_RPatternNoRuleMatch
253+
| LRW_RPatternNotLinear
254+
| LRW_RPatternNoVariable
252255

253256
exception RewriteError of error
254257

@@ -326,48 +329,171 @@ module LowRewrite = struct
326329

327330
let find_rewrite_patterns = find_rewrite_patterns ~inpred:false
328331

329-
type rwinfos = rwside * EcFol.form option * EcMatching.occ option
332+
type rwinfos = rwside * (form * (EcIdent.t * ty) option) option * EcMatching.occ option
330333

331-
let t_rewrite_r ?(mode = `Full) ?target ((s, prw, o) : rwinfos) pt tc =
334+
let first_occurrence (p : EcMatching.ptnpos) =
335+
EcMatching.FPosition.filter (`Inclusive, EcMaps.Sint.singleton 1) p
336+
337+
let ptnpos_as_map (p : EcMatching.ptnpos) =
338+
(p :> [`Select of int | `Sub of EcMatching.ptnpos] Mint.t)
339+
340+
let path_of_occurrence (p : EcMatching.ptnpos) =
341+
let rec aux acc (p : EcMatching.ptnpos) =
342+
let p = ptnpos_as_map p in
343+
assert (Mint.cardinal p = 1);
344+
345+
let i, p = Mint.choose p in
346+
347+
match p with
348+
| `Select _ -> List.rev (i :: acc)
349+
| `Sub p -> aux (i :: acc) p
350+
in
351+
352+
let p = first_occurrence p in
353+
let p = ptnpos_as_map p in
354+
355+
assert (Mint.cardinal p = 1);
356+
357+
let i, p = Mint.choose p in
358+
assert (i = 0);
359+
360+
match p with
361+
| `Select _ -> []
362+
| `Sub p -> aux [] p
363+
364+
let first_selected_subform (p : EcMatching.ptnpos) (f : form) =
365+
let selected = ref None in
366+
367+
let _ =
368+
EcMatching.FPosition.map p
369+
(fun fp ->
370+
if Option.is_none !selected then selected := Some fp;
371+
fp)
372+
f
373+
in
374+
375+
oget !selected
376+
377+
let t_rewrite_r
378+
?(mode : [`Full | `Light] = `Full)
379+
?(target : EcIdent.t option)
380+
((s, prw, o) : rwinfos)
381+
(pt : pt_ev)
382+
(tc : tcenv1)
383+
=
332384
let hyps, tgfp = FApi.tc1_flat ?target tc in
333385

334386
let modes =
335387
match mode with
336-
| `Full -> [{ k_keyed = true; k_conv = false };
337-
{ k_keyed = true; k_conv = true };]
338-
| `Light -> [{ k_keyed = true; k_conv = false }] in
388+
| `Full -> [{ k_keyed = true; k_conv = false; k_delta = true };
389+
{ k_keyed = true; k_conv = true; k_delta = true };]
390+
| `Light -> [{ k_keyed = true; k_conv = false; k_delta = true }] in
339391

340392
let for1 (pt, mode, (f1, f2)) =
341393
let fp, tp = match s with `LtoR -> f1, f2 | `RtoL -> f2, f1 in
342-
let subf, occmode =
394+
let subf, occmode, cpos =
343395
match prw with
344396
| None -> begin
345397
try
346-
PT.pf_find_occurence_lazy pt.PT.ptev_env ~modes ~ptn:fp tgfp
398+
let subf, occmode =
399+
PT.pf_find_occurence_lazy pt.PT.ptev_env ~modes ~ptn:fp tgfp
400+
in
401+
(subf, occmode, None)
347402
with
348403
| PT.FindOccFailure `MatchFailure ->
349404
raise (RewriteError LRW_NothingToRewrite)
350405
| PT.FindOccFailure `IncompleteMatch ->
351406
raise (RewriteError LRW_CannotInfer)
352407
end
353408

354-
| Some prw -> begin
355-
let prw, _ =
356-
try
357-
PT.pf_find_occurence_lazy
358-
pt.PT.ptev_env ~full:false ~modes ~ptn:prw tgfp
359-
with PT.FindOccFailure `MatchFailure ->
360-
raise (RewriteError LRW_RPatternNoMatch) in
409+
| Some (prw, subl) -> begin
410+
let subcpos =
411+
match subl with
412+
| None -> None
361413

362-
try
363-
PT.pf_find_occurence_lazy
364-
pt.PT.ptev_env ~rooted:true ~modes ~ptn:fp prw
365-
with
366-
| PT.FindOccFailure `MatchFailure ->
367-
raise (RewriteError LRW_RPatternNoRuleMatch)
368-
| PT.FindOccFailure `IncompleteMatch ->
369-
raise (RewriteError LRW_CannotInfer)
370-
end in
414+
| Some (x, xty) ->
415+
let fx = f_local x xty in
416+
let subcpos =
417+
FPosition.select_form
418+
~xconv:`Eq ~keyed:true hyps None fx prw
419+
in
420+
421+
if FPosition.is_empty subcpos then
422+
raise (RewriteError LRW_RPatternNoVariable);
423+
424+
if FPosition.occurences subcpos <> 1 then
425+
raise (RewriteError LRW_RPatternNotLinear);
426+
427+
let subcpos =
428+
match o with
429+
| None -> subcpos
430+
| Some o ->
431+
if not (FPosition.is_occurences_valid (snd o) subcpos) then
432+
raise (RewriteError LRW_InvalidOccurence);
433+
FPosition.filter o subcpos
434+
in
435+
436+
Some subcpos
437+
in
438+
439+
let ctxt_modes =
440+
match subl with
441+
| None -> modes
442+
| Some _ -> [{ k_keyed = true; k_conv = false; k_delta = false }]
443+
in
444+
445+
let prw, prwmode =
446+
try
447+
PT.pf_find_occurence_lazy
448+
pt.PT.ptev_env ~full:false ~modes:ctxt_modes ~ptn:prw tgfp
449+
with PT.FindOccFailure `MatchFailure ->
450+
raise (RewriteError LRW_RPatternNoMatch) in
451+
452+
match subcpos with
453+
| None ->
454+
begin
455+
try
456+
let subf, occmode =
457+
PT.pf_find_occurence_lazy
458+
pt.PT.ptev_env ~rooted:true ~modes:ctxt_modes ~ptn:fp prw
459+
in
460+
(subf, occmode, None)
461+
with
462+
| PT.FindOccFailure `MatchFailure ->
463+
raise (RewriteError LRW_RPatternNoRuleMatch)
464+
| PT.FindOccFailure `IncompleteMatch ->
465+
raise (RewriteError LRW_CannotInfer)
466+
end
467+
468+
| Some subcpos ->
469+
let subf = first_selected_subform subcpos prw in
470+
471+
begin
472+
try
473+
ignore
474+
(PT.pf_find_occurence_lazy
475+
pt.PT.ptev_env ~rooted:true ~modes:ctxt_modes ~ptn:fp subf)
476+
with
477+
| PT.FindOccFailure `MatchFailure ->
478+
raise (RewriteError LRW_RPatternNoRuleMatch)
479+
| PT.FindOccFailure `IncompleteMatch ->
480+
raise (RewriteError LRW_CannotInfer)
481+
end;
482+
483+
let cpos =
484+
let prwpos =
485+
FPosition.select_form
486+
~xconv:`AlphaEq ~keyed:prwmode.k_keyed hyps
487+
(Some (`Inclusive, EcMaps.Sint.singleton 1))
488+
prw tgfp
489+
in
490+
let root = path_of_occurrence prwpos in
491+
FPosition.reroot root subcpos
492+
in
493+
494+
(subf, { k_keyed = true; k_conv = false; k_delta = false }, Some cpos)
495+
end
496+
in
371497

372498
if not occmode.k_keyed then begin
373499
let tp = PT.concretize_form pt.PT.ptev_env tp in
@@ -377,10 +503,15 @@ module LowRewrite = struct
377503

378504
let pt = fst (PT.concretize pt) in
379505
let cpos =
380-
try FPosition.select_form
381-
~xconv:`AlphaEq ~keyed:occmode.k_keyed
382-
hyps o subf tgfp
383-
with InvalidOccurence -> raise (RewriteError (LRW_InvalidOccurence))
506+
match cpos with
507+
| Some cpos -> cpos
508+
| None ->
509+
try
510+
FPosition.select_form
511+
~xconv:`AlphaEq ~keyed:occmode.k_keyed
512+
hyps o subf tgfp
513+
with InvalidOccurence ->
514+
raise (RewriteError LRW_InvalidOccurence)
384515
in
385516

386517
EcLowGoal.t_rewrite
@@ -569,7 +700,14 @@ let process_apply_top tc =
569700
| _ -> tc_error !!tc "no top assumption"
570701

571702
(* -------------------------------------------------------------------- *)
572-
let process_rewrite1_core ?mode ?(close = true) ?target (s, p, o) pt tc =
703+
let process_rewrite1_core
704+
?(mode : [`Full | `Light] option)
705+
?(close : bool = true)
706+
?(target : EcIdent.t option)
707+
((s, p, o) : rwside * (form * (EcIdent.t * ty) option) option * rwocc)
708+
(pt : pt_ev)
709+
(tc : tcenv1)
710+
=
573711
let o = norm_rwocc o in
574712

575713
try
@@ -596,9 +734,13 @@ let process_rewrite1_core ?mode ?(close = true) ?target (s, p, o) pt tc =
596734
tc_error !!tc "r-pattern does not match the goal"
597735
| LowRewrite.LRW_RPatternNoRuleMatch ->
598736
tc_error !!tc "r-pattern does not match the rewriting rule"
737+
| LowRewrite.LRW_RPatternNotLinear ->
738+
tc_error !!tc "context variable must appear exactly once in the r-pattern"
739+
| LowRewrite.LRW_RPatternNoVariable ->
740+
tc_error !!tc "context variable does not appear in the r-pattern"
599741

600742
(* -------------------------------------------------------------------- *)
601-
let process_delta ~und_delta ?target (s, o, p) tc =
743+
let process_delta ~und_delta ?target ((s :rwside), o, p) tc =
602744
let env, hyps, concl = FApi.tc1_eflat tc in
603745
let o = norm_rwocc o in
604746

@@ -768,38 +910,50 @@ let process_rewrite1_r ttenv ?target ri tc =
768910
let target = target |> omap (fst -| ((LDecl.hyp_by_name^~ hyps) -| unloc)) in
769911
t_simplify_lg ?target ~delta:`IfApplied (ttenv, logic) tc
770912

771-
| RWDelta ((s, r, o, px), p) -> begin
772-
if Option.is_some px then
913+
| RWDelta (rwopt, p) -> begin
914+
if Option.is_some rwopt.match_ then
773915
tc_error !!tc "cannot use pattern selection in delta-rewrite rules";
774916

775-
let do1 tc = process_delta ~und_delta ?target (s, o, p) tc in
917+
let do1 tc =
918+
process_delta ~und_delta ?target (rwopt.side, rwopt.occurrence, p) tc in
776919

777-
match r with
920+
match rwopt.repeat with
778921
| None -> do1 tc
779922
| Some (b, n) -> t_do b n do1 tc
780923
end
781924

782-
| RWRw (((s : rwside), r, o, p), pts) -> begin
925+
| RWRw (rwopt, pts) -> begin
783926
let do1 (mode : [`Full | `Light]) ((subs : rwside), pt) tc =
784927
let hyps = FApi.tc1_hyps tc in
785928
let target = target |> omap (fst -| ((LDecl.hyp_by_name^~ hyps) -| unloc)) in
786929
let hyps = FApi.tc1_hyps ?target tc in
787930

788931
let ptenv, prw =
789-
match p with
932+
match rwopt.match_ with
790933
| None ->
791934
PT.ptenv_of_penv hyps !!tc, None
792935

793-
| Some p ->
936+
| Some (RWM_Plain p) ->
794937
let (ps, ue), p = TTC.tc1_process_pattern tc p in
795938
let ev = MEV.of_idents (Mid.keys ps) `Form in
796-
(PT.ptenv !!tc hyps (ue, ev), Some p) in
939+
(PT.ptenv !!tc hyps (ue, ev), Some (p, None))
940+
941+
| Some (RWM_Context (x, p)) ->
942+
let ps = ref Mid.empty in
943+
let ue = EcProofTyping.unienv_of_hyps hyps in
944+
let x = EcIdent.create (unloc x) in
945+
let xty = EcUnify.UniEnv.fresh ue in
946+
let hyps = FApi.tc1_hyps tc in
947+
let hyps = LDecl.add_local x (LD_var (xty, None)) hyps in
948+
let p = EcTyping.trans_pattern (LDecl.toenv hyps) ps ue p in
949+
let ev = MEV.of_idents (x :: Mid.keys !ps) `Form in
950+
(PT.ptenv !!tc hyps (ue, ev), Some (p, Some (x, xty))) in
797951

798952
let theside =
799-
match s, subs with
800-
| `LtoR, _ -> (subs :> rwside)
801-
| _ , `LtoR -> (s :> rwside)
802-
| `RtoL, `RtoL -> (`LtoR :> rwside) in
953+
match rwopt.side, subs with
954+
| `LtoR, _ -> (subs :> rwside)
955+
| _ , `LtoR -> (rwopt.side :> rwside)
956+
| `RtoL, `RtoL -> (`LtoR :> rwside) in
803957

804958
let is_baserw p =
805959
EcEnv.BaseRw.is_base p.pl_desc (FApi.tc1_env tc) in
@@ -814,7 +968,7 @@ let process_rewrite1_r ttenv ?target ri tc =
814968

815969
let do1 lemma tc =
816970
let pt = PT.pt_of_uglobal_r (PT.copy ptenv) lemma in
817-
process_rewrite1_core ~mode ?target (theside, prw, o) pt tc
971+
process_rewrite1_core ~mode ?target (theside, prw, rwopt.occurrence) pt tc
818972
in t_ors (List.map do1 ls) tc
819973

820974
| { fp_head = FPNamed (p, None); fp_args = []; }
@@ -832,11 +986,11 @@ let process_rewrite1_r ttenv ?target ri tc =
832986

833987
let do1 (lemma, _) tc =
834988
let pt = PT.pt_of_uglobal_r (PT.copy ptenv0) lemma in
835-
process_rewrite1_core ~mode ?target (theside, prw, o) pt tc in
989+
process_rewrite1_core ~mode ?target (theside, prw, rwopt.occurrence) pt tc in
836990
t_ors (List.map do1 ls) tc
837991

838992
| _ ->
839-
process_rewrite1_core ~mode ?target (theside, prw, o) pt tc
993+
process_rewrite1_core ~mode ?target (theside, prw, rwopt.occurrence) pt tc
840994
end
841995

842996
| { fp_head = FPCut (Some f); fp_args = []; }
@@ -856,16 +1010,16 @@ let process_rewrite1_r ttenv ?target ri tc =
8561010
let pt = PTApply { pt_head = PTCut (f, None); pt_args = []; } in
8571011
let pt = { ptev_env = ptenv; ptev_pt = pt; ptev_ax = f; } in
8581012

859-
process_rewrite1_core ~mode ?target (theside, prw, o) pt tc
1013+
process_rewrite1_core ~mode ?target (theside, prw, rwopt.occurrence) pt tc
8601014

8611015
| _ ->
8621016
let pt = PT.process_full_pterm ~implicits ptenv pt in
863-
process_rewrite1_core ~mode ?target (theside, prw, o) pt tc
1017+
process_rewrite1_core ~mode ?target (theside, prw, rwopt.occurrence) pt tc
8641018
in
8651019

8661020
let doall mode tc = t_ors (List.map (do1 mode) pts) tc in
8671021

868-
match r with
1022+
match rwopt.repeat with
8691023
| None ->
8701024
doall `Full tc
8711025
| Some (`Maybe, None) ->

src/ecHiGoal.mli

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,18 @@ module LowRewrite : sig
4141
| LRW_IdRewriting
4242
| LRW_RPatternNoMatch
4343
| LRW_RPatternNoRuleMatch
44+
| LRW_RPatternNotLinear
45+
| LRW_RPatternNoVariable
4446

4547
exception RewriteError of error
4648

4749
val find_rewrite_patterns:
4850
rwside -> pt_ev -> (pt_ev * rwmode * (form * form)) list
4951

50-
type rwinfos = rwside * EcFol.form option * EcMatching.occ option
52+
type rwinfos =
53+
rwside
54+
* (form * (EcIdent.t * EcTypes.ty) option) option
55+
* EcMatching.occ option
5156

5257
val t_rewrite_r:
5358
?mode:[ `Full | `Light] ->

0 commit comments

Comments
 (0)