Skip to content

Commit 565921e

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 565921e

File tree

9 files changed

+344
-58
lines changed

9 files changed

+344
-58
lines changed

src/ecHiGoal.ml

Lines changed: 161 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,8 @@ module LowRewrite = struct
249249
| LRW_IdRewriting
250250
| LRW_RPatternNoMatch
251251
| LRW_RPatternNoRuleMatch
252+
| LRW_RPatternNotLinear
253+
| LRW_RPatternNoVariable
252254

253255
exception RewriteError of error
254256

@@ -326,48 +328,133 @@ module LowRewrite = struct
326328

327329
let find_rewrite_patterns = find_rewrite_patterns ~inpred:false
328330

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

331-
let t_rewrite_r ?(mode = `Full) ?target ((s, prw, o) : rwinfos) pt tc =
333+
let selected_subform (p : EcMatching.ptnpos) (f : form) =
334+
let selected = ref None in
335+
336+
let _ =
337+
EcMatching.FPosition.map p
338+
(fun fp ->
339+
if Option.is_none !selected then selected := Some fp;
340+
fp)
341+
f
342+
in
343+
344+
oget !selected
345+
346+
let t_rewrite_r
347+
?(mode : [`Full | `Light] = `Full)
348+
?(target : EcIdent.t option)
349+
((s, prw, o) : rwinfos)
350+
(pt : pt_ev)
351+
(tc : tcenv1)
352+
=
332353
let hyps, tgfp = FApi.tc1_flat ?target tc in
333354

334355
let modes =
335356
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
357+
| `Full -> [{ k_keyed = true; k_conv = false; k_delta = true };
358+
{ k_keyed = true; k_conv = true; k_delta = true };]
359+
| `Light -> [{ k_keyed = true; k_conv = false; k_delta = true }] in
339360

340361
let for1 (pt, mode, (f1, f2)) =
341362
let fp, tp = match s with `LtoR -> f1, f2 | `RtoL -> f2, f1 in
342-
let subf, occmode =
363+
let subf, occmode, cpos =
343364
match prw with
344365
| None -> begin
345366
try
346-
PT.pf_find_occurence_lazy pt.PT.ptev_env ~modes ~ptn:fp tgfp
367+
let subf, occmode =
368+
PT.pf_find_occurence_lazy pt.PT.ptev_env ~modes ~ptn:fp tgfp
369+
in
370+
(subf, occmode, None)
347371
with
348372
| PT.FindOccFailure `MatchFailure ->
349373
raise (RewriteError LRW_NothingToRewrite)
350374
| PT.FindOccFailure `IncompleteMatch ->
351375
raise (RewriteError LRW_CannotInfer)
352376
end
353377

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
378+
| Some (prw, subl) -> begin
379+
let subcpos =
380+
match subl with
381+
| None -> None
361382

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 ->
383+
| Some (x, xty) ->
384+
let fx = f_local x xty in
385+
let subcpos =
386+
FPosition.select_form
387+
~xconv:`Eq ~keyed:true hyps None fx prw
388+
in
389+
390+
if FPosition.is_empty subcpos then
391+
raise (RewriteError LRW_RPatternNoVariable);
392+
393+
if FPosition.occurences subcpos <> 1 then
394+
raise (RewriteError LRW_RPatternNotLinear);
395+
396+
let subcpos =
397+
match o with
398+
| None -> subcpos
399+
| Some o ->
400+
if not (FPosition.is_occurences_valid (snd o) subcpos) then
401+
raise (RewriteError LRW_InvalidOccurence);
402+
FPosition.filter o subcpos
403+
in
404+
405+
Some subcpos
406+
in
407+
408+
let ctxt_modes =
409+
match subl with
410+
| None -> modes
411+
| Some _ -> [{ k_keyed = true; k_conv = false; k_delta = false }]
412+
in
413+
414+
let prw, prwmode =
415+
try
416+
PT.pf_find_occurence_lazy
417+
pt.PT.ptev_env ~full:false ~modes:ctxt_modes ~ptn:prw tgfp
418+
with PT.FindOccFailure `MatchFailure ->
419+
raise (RewriteError LRW_RPatternNoMatch) in
420+
421+
let find_in_rpattern ~modes fp prw =
422+
try
423+
PT.pf_find_occurence_lazy
424+
pt.PT.ptev_env ~rooted:true ~modes ~ptn:fp prw
425+
with
426+
| PT.FindOccFailure `MatchFailure ->
367427
raise (RewriteError LRW_RPatternNoRuleMatch)
368-
| PT.FindOccFailure `IncompleteMatch ->
369-
raise (RewriteError LRW_CannotInfer)
370-
end in
428+
| PT.FindOccFailure `IncompleteMatch ->
429+
raise (RewriteError LRW_CannotInfer)
430+
in
431+
432+
match subcpos with
433+
| None ->
434+
let subf, occmode =
435+
find_in_rpattern ~modes:ctxt_modes fp prw
436+
in
437+
(subf, occmode, None)
438+
439+
| Some subcpos ->
440+
let subf = selected_subform subcpos prw in
441+
442+
ignore (find_in_rpattern ~modes:ctxt_modes fp subf);
443+
444+
let cpos =
445+
let prwpos =
446+
FPosition.select_form
447+
~xconv:`AlphaEq ~keyed:prwmode.k_keyed hyps
448+
(Some (`Inclusive, EcMaps.Sint.singleton 1))
449+
prw tgfp
450+
in
451+
let root = FPosition.path_of_singleton_occurence prwpos in
452+
FPosition.reroot root subcpos
453+
in
454+
455+
(subf, { k_keyed = true; k_conv = false; k_delta = false }, Some cpos)
456+
end
457+
in
371458

372459
if not occmode.k_keyed then begin
373460
let tp = PT.concretize_form pt.PT.ptev_env tp in
@@ -377,10 +464,15 @@ module LowRewrite = struct
377464

378465
let pt = fst (PT.concretize pt) in
379466
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))
467+
match cpos with
468+
| Some cpos -> cpos
469+
| None ->
470+
try
471+
FPosition.select_form
472+
~xconv:`AlphaEq ~keyed:occmode.k_keyed
473+
hyps o subf tgfp
474+
with InvalidOccurence ->
475+
raise (RewriteError LRW_InvalidOccurence)
384476
in
385477

386478
EcLowGoal.t_rewrite
@@ -569,7 +661,14 @@ let process_apply_top tc =
569661
| _ -> tc_error !!tc "no top assumption"
570662

571663
(* -------------------------------------------------------------------- *)
572-
let process_rewrite1_core ?mode ?(close = true) ?target (s, p, o) pt tc =
664+
let process_rewrite1_core
665+
?(mode : [`Full | `Light] option)
666+
?(close : bool = true)
667+
?(target : EcIdent.t option)
668+
((s, p, o) : rwside * (form * (EcIdent.t * ty) option) option * rwocc)
669+
(pt : pt_ev)
670+
(tc : tcenv1)
671+
=
573672
let o = norm_rwocc o in
574673

575674
try
@@ -596,9 +695,13 @@ let process_rewrite1_core ?mode ?(close = true) ?target (s, p, o) pt tc =
596695
tc_error !!tc "r-pattern does not match the goal"
597696
| LowRewrite.LRW_RPatternNoRuleMatch ->
598697
tc_error !!tc "r-pattern does not match the rewriting rule"
698+
| LowRewrite.LRW_RPatternNotLinear ->
699+
tc_error !!tc "context variable must appear exactly once in the r-pattern"
700+
| LowRewrite.LRW_RPatternNoVariable ->
701+
tc_error !!tc "context variable does not appear in the r-pattern"
599702

600703
(* -------------------------------------------------------------------- *)
601-
let process_delta ~und_delta ?target (s, o, p) tc =
704+
let process_delta ~und_delta ?target ((s :rwside), o, p) tc =
602705
let env, hyps, concl = FApi.tc1_eflat tc in
603706
let o = norm_rwocc o in
604707

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

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

775-
let do1 tc = process_delta ~und_delta ?target (s, o, p) tc in
878+
let do1 tc =
879+
process_delta ~und_delta ?target (rwopt.side, rwopt.occurrence, p) tc in
776880

777-
match r with
881+
match rwopt.repeat with
778882
| None -> do1 tc
779883
| Some (b, n) -> t_do b n do1 tc
780884
end
781885

782-
| RWRw (((s : rwside), r, o, p), pts) -> begin
886+
| RWRw (rwopt, pts) -> begin
783887
let do1 (mode : [`Full | `Light]) ((subs : rwside), pt) tc =
784888
let hyps = FApi.tc1_hyps tc in
785889
let target = target |> omap (fst -| ((LDecl.hyp_by_name^~ hyps) -| unloc)) in
786890
let hyps = FApi.tc1_hyps ?target tc in
787891

788892
let ptenv, prw =
789-
match p with
893+
match rwopt.match_ with
790894
| None ->
791895
PT.ptenv_of_penv hyps !!tc, None
792896

793-
| Some p ->
897+
| Some (RWM_Plain p) ->
794898
let (ps, ue), p = TTC.tc1_process_pattern tc p in
795899
let ev = MEV.of_idents (Mid.keys ps) `Form in
796-
(PT.ptenv !!tc hyps (ue, ev), Some p) in
900+
(PT.ptenv !!tc hyps (ue, ev), Some (p, None))
901+
902+
| Some (RWM_Context (x, p)) ->
903+
let ps = ref Mid.empty in
904+
let ue = EcProofTyping.unienv_of_hyps hyps in
905+
let x = EcIdent.create (unloc x) in
906+
let xty = EcUnify.UniEnv.fresh ue in
907+
let hyps = FApi.tc1_hyps tc in
908+
let hyps = LDecl.add_local x (LD_var (xty, None)) hyps in
909+
let p = EcTyping.trans_pattern (LDecl.toenv hyps) ps ue p in
910+
let ev = MEV.of_idents (x :: Mid.keys !ps) `Form in
911+
(PT.ptenv !!tc hyps (ue, ev), Some (p, Some (x, xty))) in
797912

798913
let theside =
799-
match s, subs with
800-
| `LtoR, _ -> (subs :> rwside)
801-
| _ , `LtoR -> (s :> rwside)
802-
| `RtoL, `RtoL -> (`LtoR :> rwside) in
914+
match rwopt.side, subs with
915+
| `LtoR, _ -> (subs :> rwside)
916+
| _ , `LtoR -> (rwopt.side :> rwside)
917+
| `RtoL, `RtoL -> (`LtoR :> rwside) in
803918

804919
let is_baserw p =
805920
EcEnv.BaseRw.is_base p.pl_desc (FApi.tc1_env tc) in
@@ -814,7 +929,7 @@ let process_rewrite1_r ttenv ?target ri tc =
814929

815930
let do1 lemma tc =
816931
let pt = PT.pt_of_uglobal_r (PT.copy ptenv) lemma in
817-
process_rewrite1_core ~mode ?target (theside, prw, o) pt tc
932+
process_rewrite1_core ~mode ?target (theside, prw, rwopt.occurrence) pt tc
818933
in t_ors (List.map do1 ls) tc
819934

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

833948
let do1 (lemma, _) tc =
834949
let pt = PT.pt_of_uglobal_r (PT.copy ptenv0) lemma in
835-
process_rewrite1_core ~mode ?target (theside, prw, o) pt tc in
950+
process_rewrite1_core ~mode ?target (theside, prw, rwopt.occurrence) pt tc in
836951
t_ors (List.map do1 ls) tc
837952

838953
| _ ->
839-
process_rewrite1_core ~mode ?target (theside, prw, o) pt tc
954+
process_rewrite1_core ~mode ?target (theside, prw, rwopt.occurrence) pt tc
840955
end
841956

842957
| { fp_head = FPCut (Some f); fp_args = []; }
@@ -856,16 +971,16 @@ let process_rewrite1_r ttenv ?target ri tc =
856971
let pt = PTApply { pt_head = PTCut (f, None); pt_args = []; } in
857972
let pt = { ptev_env = ptenv; ptev_pt = pt; ptev_ax = f; } in
858973

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

861976
| _ ->
862977
let pt = PT.process_full_pterm ~implicits ptenv pt in
863-
process_rewrite1_core ~mode ?target (theside, prw, o) pt tc
978+
process_rewrite1_core ~mode ?target (theside, prw, rwopt.occurrence) pt tc
864979
in
865980

866981
let doall mode tc = t_ors (List.map (do1 mode) pts) tc in
867982

868-
match r with
983+
match rwopt.repeat with
869984
| None ->
870985
doall `Full tc
871986
| 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] ->

src/ecMatching.ml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1581,6 +1581,27 @@ module FPosition = struct
15811581
)
15821582
end
15831583

1584+
(* ------------------------------------------------------------------ *)
1585+
let path_of_singleton_occurence (p : ptnpos) =
1586+
let rec aux acc (p : ptnpos) =
1587+
assert (Mint.cardinal p = 1);
1588+
1589+
let i, p = Mint.choose p in
1590+
1591+
match p with
1592+
| `Select _ -> List.rev (i :: acc)
1593+
| `Sub p -> aux (i :: acc) p
1594+
in
1595+
1596+
assert (Mint.cardinal p = 1);
1597+
1598+
let i, p = Mint.choose p in
1599+
assert (i = 0);
1600+
1601+
match p with
1602+
| `Select _ -> []
1603+
| `Sub p -> aux [] p
1604+
15841605
(* ------------------------------------------------------------------ *)
15851606
let topattern ?x (p : ptnpos) (f : form) =
15861607
let x = match x with None -> EcIdent.create "_p" | Some x -> x in

src/ecMatching.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,8 @@ module FPosition : sig
412412

413413
val filter : occ -> ptnpos -> ptnpos
414414

415+
val path_of_singleton_occurence : ptnpos -> int list
416+
415417
val map : ptnpos -> (form -> form) -> form -> form
416418

417419
val topattern : ?x:EcIdent.t -> ptnpos -> form -> EcIdent.t * form

0 commit comments

Comments
 (0)