Skip to content

Commit c4fd44b

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 bb95fdd commit c4fd44b

9 files changed

Lines changed: 342 additions & 58 deletions

src/ecHiGoal.ml

Lines changed: 148 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,120 @@ 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 t_rewrite_r
334+
?(mode : [`Full | `Light] = `Full)
335+
?(target : EcIdent.t option)
336+
((s, prw, o) : rwinfos)
337+
(pt : pt_ev)
338+
(tc : tcenv1)
339+
=
332340
let hyps, tgfp = FApi.tc1_flat ?target tc in
333341

334342
let modes =
335343
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
344+
| `Full -> [{ k_keyed = true; k_conv = false; k_delta = true };
345+
{ k_keyed = true; k_conv = true; k_delta = true };]
346+
| `Light -> [{ k_keyed = true; k_conv = false; k_delta = true }] in
339347

340348
let for1 (pt, mode, (f1, f2)) =
341349
let fp, tp = match s with `LtoR -> f1, f2 | `RtoL -> f2, f1 in
342-
let subf, occmode =
350+
let subf, occmode, cpos =
343351
match prw with
344352
| None -> begin
345353
try
346-
PT.pf_find_occurence_lazy pt.PT.ptev_env ~modes ~ptn:fp tgfp
354+
let subf, occmode =
355+
PT.pf_find_occurence_lazy pt.PT.ptev_env ~modes ~ptn:fp tgfp
356+
in
357+
(subf, occmode, None)
347358
with
348359
| PT.FindOccFailure `MatchFailure ->
349360
raise (RewriteError LRW_NothingToRewrite)
350361
| PT.FindOccFailure `IncompleteMatch ->
351362
raise (RewriteError LRW_CannotInfer)
352363
end
353364

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
365+
| Some (prw, subl) -> begin
366+
let subcpos =
367+
match subl with
368+
| None -> None
361369

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

372446
if not occmode.k_keyed then begin
373447
let tp = PT.concretize_form pt.PT.ptev_env tp in
@@ -377,10 +451,15 @@ module LowRewrite = struct
377451

378452
let pt = fst (PT.concretize pt) in
379453
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))
454+
match cpos with
455+
| Some cpos -> cpos
456+
| None ->
457+
try
458+
FPosition.select_form
459+
~xconv:`AlphaEq ~keyed:occmode.k_keyed
460+
hyps o subf tgfp
461+
with InvalidOccurence ->
462+
raise (RewriteError LRW_InvalidOccurence)
384463
in
385464

386465
EcLowGoal.t_rewrite
@@ -569,7 +648,14 @@ let process_apply_top tc =
569648
| _ -> tc_error !!tc "no top assumption"
570649

571650
(* -------------------------------------------------------------------- *)
572-
let process_rewrite1_core ?mode ?(close = true) ?target (s, p, o) pt tc =
651+
let process_rewrite1_core
652+
?(mode : [`Full | `Light] option)
653+
?(close : bool = true)
654+
?(target : EcIdent.t option)
655+
((s, p, o) : rwside * (form * (EcIdent.t * ty) option) option * rwocc)
656+
(pt : pt_ev)
657+
(tc : tcenv1)
658+
=
573659
let o = norm_rwocc o in
574660

575661
try
@@ -596,9 +682,13 @@ let process_rewrite1_core ?mode ?(close = true) ?target (s, p, o) pt tc =
596682
tc_error !!tc "r-pattern does not match the goal"
597683
| LowRewrite.LRW_RPatternNoRuleMatch ->
598684
tc_error !!tc "r-pattern does not match the rewriting rule"
685+
| LowRewrite.LRW_RPatternNotLinear ->
686+
tc_error !!tc "context variable must appear exactly once in the r-pattern"
687+
| LowRewrite.LRW_RPatternNoVariable ->
688+
tc_error !!tc "context variable does not appear in the r-pattern"
599689

600690
(* -------------------------------------------------------------------- *)
601-
let process_delta ~und_delta ?target (s, o, p) tc =
691+
let process_delta ~und_delta ?target ((s :rwside), o, p) tc =
602692
let env, hyps, concl = FApi.tc1_eflat tc in
603693
let o = norm_rwocc o in
604694

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

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

775-
let do1 tc = process_delta ~und_delta ?target (s, o, p) tc in
865+
let do1 tc =
866+
process_delta ~und_delta ?target (rwopt.side, rwopt.occurrence, p) tc in
776867

777-
match r with
868+
match rwopt.repeat with
778869
| None -> do1 tc
779870
| Some (b, n) -> t_do b n do1 tc
780871
end
781872

782-
| RWRw (((s : rwside), r, o, p), pts) -> begin
873+
| RWRw (rwopt, pts) -> begin
783874
let do1 (mode : [`Full | `Light]) ((subs : rwside), pt) tc =
784875
let hyps = FApi.tc1_hyps tc in
785876
let target = target |> omap (fst -| ((LDecl.hyp_by_name^~ hyps) -| unloc)) in
786877
let hyps = FApi.tc1_hyps ?target tc in
787878

788879
let ptenv, prw =
789-
match p with
880+
match rwopt.match_ with
790881
| None ->
791882
PT.ptenv_of_penv hyps !!tc, None
792883

793-
| Some p ->
884+
| Some (RWM_Plain p) ->
794885
let (ps, ue), p = TTC.tc1_process_pattern tc p in
795886
let ev = MEV.of_idents (Mid.keys ps) `Form in
796-
(PT.ptenv !!tc hyps (ue, ev), Some p) in
887+
(PT.ptenv !!tc hyps (ue, ev), Some (p, None))
888+
889+
| Some (RWM_Context (x, p)) ->
890+
let ps = ref Mid.empty in
891+
let ue = EcProofTyping.unienv_of_hyps hyps in
892+
let x = EcIdent.create (unloc x) in
893+
let xty = EcUnify.UniEnv.fresh ue in
894+
let hyps = FApi.tc1_hyps tc in
895+
let hyps = LDecl.add_local x (LD_var (xty, None)) hyps in
896+
let p = EcTyping.trans_pattern (LDecl.toenv hyps) ps ue p in
897+
let ev = MEV.of_idents (x :: Mid.keys !ps) `Form in
898+
(PT.ptenv !!tc hyps (ue, ev), Some (p, Some (x, xty))) in
797899

798900
let theside =
799-
match s, subs with
800-
| `LtoR, _ -> (subs :> rwside)
801-
| _ , `LtoR -> (s :> rwside)
802-
| `RtoL, `RtoL -> (`LtoR :> rwside) in
901+
match rwopt.side, subs with
902+
| `LtoR, _ -> (subs :> rwside)
903+
| _ , `LtoR -> (rwopt.side :> rwside)
904+
| `RtoL, `RtoL -> (`LtoR :> rwside) in
803905

804906
let is_baserw p =
805907
EcEnv.BaseRw.is_base p.pl_desc (FApi.tc1_env tc) in
@@ -814,7 +916,7 @@ let process_rewrite1_r ttenv ?target ri tc =
814916

815917
let do1 lemma tc =
816918
let pt = PT.pt_of_uglobal_r (PT.copy ptenv) lemma in
817-
process_rewrite1_core ~mode ?target (theside, prw, o) pt tc
919+
process_rewrite1_core ~mode ?target (theside, prw, rwopt.occurrence) pt tc
818920
in t_ors (List.map do1 ls) tc
819921

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

833935
let do1 (lemma, _) tc =
834936
let pt = PT.pt_of_uglobal_r (PT.copy ptenv0) lemma in
835-
process_rewrite1_core ~mode ?target (theside, prw, o) pt tc in
937+
process_rewrite1_core ~mode ?target (theside, prw, rwopt.occurrence) pt tc in
836938
t_ors (List.map do1 ls) tc
837939

838940
| _ ->
839-
process_rewrite1_core ~mode ?target (theside, prw, o) pt tc
941+
process_rewrite1_core ~mode ?target (theside, prw, rwopt.occurrence) pt tc
840942
end
841943

842944
| { fp_head = FPCut (Some f); fp_args = []; }
@@ -856,16 +958,16 @@ let process_rewrite1_r ttenv ?target ri tc =
856958
let pt = PTApply { pt_head = PTCut (f, None); pt_args = []; } in
857959
let pt = { ptev_env = ptenv; ptev_pt = pt; ptev_ax = f; } in
858960

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

861963
| _ ->
862964
let pt = PT.process_full_pterm ~implicits ptenv pt in
863-
process_rewrite1_core ~mode ?target (theside, prw, o) pt tc
965+
process_rewrite1_core ~mode ?target (theside, prw, rwopt.occurrence) pt tc
864966
in
865967

866968
let doall mode tc = t_ors (List.map (do1 mode) pts) tc in
867969

868-
match r with
970+
match rwopt.repeat with
869971
| None ->
870972
doall `Full tc
871973
| 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: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1585,6 +1585,36 @@ module FPosition = struct
15851585
)
15861586
end
15871587

1588+
(* ------------------------------------------------------------------ *)
1589+
let path_of_singleton_occurence (p : ptnpos) =
1590+
let rec aux acc (p : ptnpos) =
1591+
assert (Mint.cardinal p = 1);
1592+
1593+
let i, p = Mint.choose p in
1594+
1595+
match p with
1596+
| `Select _ -> List.rev (i :: acc)
1597+
| `Sub p -> aux (i :: acc) p
1598+
in
1599+
1600+
assert (Mint.cardinal p = 1);
1601+
1602+
let i, p = Mint.choose p in
1603+
assert (i = 0);
1604+
1605+
match p with
1606+
| `Select _ -> []
1607+
| `Sub p -> aux [] p
1608+
1609+
(* ------------------------------------------------------------------ *)
1610+
let first_selected_subform (p : ptnpos) (f : form) =
1611+
let exception Found of form in
1612+
1613+
try
1614+
ignore (map p (fun fp -> raise (Found fp)) f);
1615+
raise InvalidPosition
1616+
with Found fp -> fp
1617+
15881618
(* ------------------------------------------------------------------ *)
15891619
let topattern ?x (p : ptnpos) (f : form) =
15901620
let x = match x with None -> EcIdent.create "_p" | Some x -> x in

src/ecMatching.mli

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,10 @@ module FPosition : sig
413413

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

416+
val path_of_singleton_occurence : ptnpos -> int list
417+
418+
val first_selected_subform : ptnpos -> form -> form
419+
416420
val map : ptnpos -> (form -> form) -> form -> form
417421

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

0 commit comments

Comments
 (0)