Commit 2447cee6 authored by Amin Timany's avatar Amin Timany

Add erasure

parent e27e771f
......@@ -109,3 +109,4 @@ theories/logatom/conditional_increment/spec.v
theories/logatom/conditional_increment/cinc.v
theories/logatom/rdcss/rdcss.v
theories/logatom/rdcss/spec.v
theories/logatom/proph_erasure.v
From iris.heap_lang Require Export lang notation tactics.
From iris.program_logic Require Export adequacy.
(** This file contains the proof that prophecies can be safely erased
from programs. We erase a program by replacing prophecy identifiers
with the unit values and respectively adapt the NewProph and Resolve
expressions. We prove that if a program e is safe then so is the
erasure of e. *)
Fixpoint erase_expr (e : expr) : expr :=
match e with
| Val v => Val (erase_val v)
| Var x => Var x
| Rec f x e => Rec f x (erase_expr e)
| App e1 e2 => App (erase_expr e1) (erase_expr e2)
| UnOp op e => UnOp op (erase_expr e)
| BinOp op e1 e2 => BinOp op (erase_expr e1) (erase_expr e2)
| If e0 e1 e2 => If (erase_expr e0) (erase_expr e1) (erase_expr e2)
| Pair e1 e2 => Pair (erase_expr e1) (erase_expr e2)
| Fst e => Fst (erase_expr e)
| Snd e => Snd (erase_expr e)
| InjL e => InjL (erase_expr e)
| InjR e => InjR (erase_expr e)
| Case e0 e1 e2 => Case (erase_expr e0) (erase_expr e1) (erase_expr e2)
| Fork e => Fork (erase_expr e)
| AllocN e1 e2 => AllocN (erase_expr e1) (erase_expr e2)
| Load e => Load (erase_expr e)
| Store e1 e2 => Store (erase_expr e1) (erase_expr e2)
| CmpXchg e0 e1 e2 => CmpXchg (erase_expr e0) (erase_expr e1) (erase_expr e2)
| FAA e1 e2 => FAA (erase_expr e1) (erase_expr e2)
| NewProph => Skip
| Resolve e0 e1 e2 => Fst (Fst (erase_expr e0, erase_expr e1, erase_expr e2))
end
with
erase_val (v : val) : val :=
match v with
| LitV l =>
LitV
match l with
| LitProphecy p => LitUnit
| _ => l
end
| RecV f x e => RecV f x (erase_expr e)
| PairV v1 v2 => PairV (erase_val v1) (erase_val v2)
| InjLV v => InjLV (erase_val v)
| InjRV v => InjRV (erase_val v)
end.
Lemma erase_expr_subst x v e :
erase_expr (subst x v e) = subst x (erase_val v) (erase_expr e)
with
erase_val_subst x v (w : val) :
erase_expr (subst x v w) = subst x (erase_val v) (erase_val w).
Proof.
- destruct e; simpl; try destruct decide;
rewrite ?erase_expr_subst ?erase_val_subst; auto.
- by destruct v; simpl.
Qed.
Lemma erase_expr_subst' x v e :
erase_expr (subst' x v e) = subst' x (erase_val v) (erase_expr e).
Proof. destruct x; first done; apply erase_expr_subst. Qed.
Lemma erase_val_subst' x v (w : val) :
erase_expr (subst x v w) = subst x (erase_val v) (erase_val w).
Proof. destruct x; first done; apply erase_val_subst. Qed.
Fixpoint erase_ectx_item (Ki : ectx_item) : list ectx_item :=
match Ki with
| AppLCtx v2 => [AppLCtx (erase_val v2)]
| AppRCtx e1 => [AppRCtx (erase_expr e1)]
| UnOpCtx op => [UnOpCtx op]
| BinOpLCtx op v2 => [BinOpLCtx op (erase_val v2)]
| BinOpRCtx op e1 => [BinOpRCtx op (erase_expr e1)]
| IfCtx e1 e2 => [IfCtx (erase_expr e1) (erase_expr e2)]
| PairLCtx v2 => [PairLCtx (erase_val v2)]
| PairRCtx e1 => [PairRCtx (erase_expr e1)]
| FstCtx => [FstCtx]
| SndCtx => [SndCtx]
| InjLCtx => [InjLCtx]
| InjRCtx => [InjRCtx]
| CaseCtx e1 e2 => [CaseCtx (erase_expr e1) (erase_expr e2)]
| AllocNLCtx v2 => [AllocNLCtx (erase_val v2)]
| AllocNRCtx e1 => [AllocNRCtx (erase_expr e1)]
| LoadCtx => [LoadCtx]
| StoreLCtx v2 => [StoreLCtx (erase_val v2)]
| StoreRCtx e1 => [StoreRCtx (erase_expr e1)]
| CmpXchgLCtx v1 v2 => [CmpXchgLCtx (erase_val v1) (erase_val v2)]
| CmpXchgMCtx e0 v2 => [CmpXchgMCtx (erase_expr e0) (erase_val v2)]
| CmpXchgRCtx e0 e1 => [CmpXchgRCtx (erase_expr e0) (erase_expr e1)]
| FaaLCtx v2 => [FaaLCtx (erase_val v2)]
| FaaRCtx e1 => [FaaRCtx (erase_expr e1)]
| ResolveLCtx ctx v1 v2 =>
erase_ectx_item ctx ++
[PairLCtx (erase_val v1); PairLCtx (erase_val v2); FstCtx; FstCtx]
| ResolveMCtx e0 v2 =>
[PairRCtx (erase_expr e0); PairLCtx (erase_val v2); FstCtx; FstCtx]
| ResolveRCtx e0 e1 =>
[PairRCtx (erase_expr e0, erase_expr e1); FstCtx; FstCtx]
end.
Definition erase_ectx (K : ectx heap_ectx_lang) : ectx heap_ectx_lang :=
foldr (λ Ki K, erase_ectx_item Ki ++ K) [] K.
Definition erase_tp (tp : list expr) : list expr := erase_expr <$> tp.
Definition erase_heap (h : gmap loc val) : gmap loc val := erase_val <$> h.
Definition erase_state (σ : state) : state :=
{| heap := erase_heap (heap σ); used_proph_id := |}.
Definition erase_cfg (ρ : cfg heap_lang) : cfg heap_lang :=
(erase_tp ρ.1, erase_state ρ.2).
Lemma erase_to_val e v :
to_val (erase_expr e) = Some v v', to_val e = Some v' erase_val v' = v.
Proof. destruct e; intros ?; simplify_eq/=; eauto. Qed.
Lemma erase_not_val e : to_val e = None to_val (erase_expr e) = None.
Proof. by destruct e. Qed.
Lemma erase_ectx_app K K' : erase_ectx (K ++ K') = erase_ectx K ++ erase_ectx K'.
Proof.
revert K'; induction K as [|Ki K IHK]; intros K'; simpl in *; first done.
by rewrite IHK assoc.
Qed.
Lemma erase_ectx_expr K e :
erase_expr (fill K e) = fill (erase_ectx K) (erase_expr e).
Proof.
revert e; induction K as [|Ki K IHK] using rev_ind; intros e;
simpl in *; rewrite ?erase_ectx_app ?fill_app /=; first done.
induction Ki;
rewrite /= ?IHK ?IHKi ?fill_app //=.
Qed.
(* helper lemma consider moving? *)
Lemma ectx_prim_step (K : list ectx_item) e1 σ1 κ e2 σ2 ef :
prim_step e1 σ1 κ e2 σ2 ef prim_step (fill K e1) σ1 κ (fill K e2) σ2 ef.
Proof.
inversion 1; simpl in *; simplify_eq.
by rewrite -!fill_app; econstructor.
Qed.
(* helper lemma consider moving? *)
Lemma rtc_congruence {A B} (f : A B) (R : A A Prop) (R' : B B Prop) x y :
( x y, R x y R' (f x) (f y)) rtc R x y rtc R' (f x) (f y).
Proof.
intros Hcng.
induction 1 as [|e1 em e2 ?]; simpl in *; first done.
etrans; last done.
by apply rtc_once, Hcng.
Qed.
(* helper lemma consider moving? *)
Lemma rtc_Ectx_pure_step (K : list ectx_item) e1 e2 :
rtc pure_step e1 e2 rtc pure_step (fill K e1) (fill K e2).
Proof.
apply rtc_congruence.
by intros; eapply pure_step_ctx; first typeclasses eauto.
Qed.
(* helper lemma consider moving? *)
Definition is_safe e σ :=
is_Some (to_val e) reducible e σ.
(* helper lemma consider moving? *)
Lemma is_safe_under_ectx K e σ : is_safe (fill K e) σ is_safe e σ.
Proof.
rewrite /is_safe /reducible /=.
intros [?|(?&?&?&?&Hstp)]; simpl in *;
first by left; eapply fill_val.
destruct (to_val e) eqn:Hval; first by left; eauto.
inversion Hstp as [K' e1' ? He]; simpl in *; simplify_eq.
edestruct (step_by_val K K' e e1'); eauto; simplify_eq.
rewrite -fill_comp in He; apply fill_inj in He; simplify_eq.
right; eauto 10 using Ectx_step.
Qed.
(* helper lemma consider moving? *)
Lemma head_step_safe e σ κ e' σ' efs : head_step e σ κ e' σ' efs is_safe e σ.
Proof. rewrite /is_safe /reducible /=. eauto 10 using head_prim_step. Qed.
(* helper lemma consider moving? *)
Lemma prim_step_safe e σ κ e' σ' efs : prim_step e σ κ e' σ' efs is_safe e σ.
Proof. rewrite /is_safe /reducible /=. eauto 10. Qed.
Lemma val_for_compare_erase v1 v2 :
val_for_compare v1 = val_for_compare v2
val_for_compare (erase_val v1) = val_for_compare (erase_val v2).
Proof.
revert v2; induction v1; induction v2; split; intros Ho; simpl in *;
repeat case_match; simpl in *; simplify_eq; eauto; firstorder.
Qed.
Lemma val_for_compare_erase_bdec v1 v2 :
bool_decide (val_for_compare v1 = val_for_compare v2) =
bool_decide (val_for_compare (erase_val v1) = val_for_compare (erase_val v2)).
Proof. by apply bool_decide_iff; rewrite val_for_compare_erase. Qed.
Lemma vals_cmpxchg_compare_safe_erase v1 v2 :
vals_cmpxchg_compare_safe (erase_val v1) (erase_val v2)
vals_cmpxchg_compare_safe v1 v2.
Proof.
by destruct v1; destruct v2; repeat (done || simpl; case_match).
Qed.
(** if un_op_eval succeeds on erased value,
the so should it on the original value. *)
Lemma un_op_eval_erase op v v' :
un_op_eval op (erase_val v) = Some v'
w, un_op_eval op v = Some w erase_val w = v'.
Proof.
destruct op; simpl; repeat case_match;
(split; [intros ?|intros [? [? ?]]]);
simplify_eq; simpl in *; simplify_eq; eauto.
Qed.
(** if unbin_op_eval succeeds on erased value,
then so should it on the original value. *)
Lemma bin_op_eval_erase op v1 v2 v' :
bin_op_eval op (erase_val v1) (erase_val v2) = Some v'
w, bin_op_eval op v1 v2 = Some w erase_val w = v'.
Proof.
rewrite /bin_op_eval /bin_op_eval_int /bin_op_eval_bool;
destruct decide; simpl; repeat case_match;
(split; [intros ?|intros [? [? ?]]]);
simplify_eq; simpl in *; simplify_eq; eauto.
- eexists _; split; eauto; simpl.
erewrite bool_decide_iff; first by eauto.
by rewrite val_for_compare_erase.
- repeat f_equal.
erewrite bool_decide_iff; first by eauto.
by rewrite -val_for_compare_erase.
Qed.
Lemma erase_heap_lookup h l : (erase_heap h) !! l = None h !! l = None.
Proof. rewrite lookup_fmap; by destruct (h !! l). Qed.
Lemma erase_heap_lookup' h l : (erase_heap h) !! l = erase_val <$> h !! l.
Proof. by rewrite lookup_fmap. Qed.
Lemma erase_heap_insert h l v :
erase_heap (<[l := v]> h) = <[l := erase_val v]> (erase_heap h).
Proof.
by rewrite /erase_heap fmap_insert.
Qed.
Lemma fmap_heap_array f l vs :
f <$> heap_array l vs = heap_array l (f <$> vs).
Proof.
revert l; induction vs as [|v vs IHvs]; intros l;
first by rewrite /= fmap_empty.
by rewrite /= -!insert_union_singleton_l !fmap_insert IHvs.
Qed.
Lemma erase_heap_array l n v σ :
erase_heap (heap_array l (replicate (Z.to_nat n) v) heap σ) =
heap_array l (replicate (Z.to_nat n) (erase_val v)) (erase_heap (heap σ)).
Proof.
apply map_eq => i.
rewrite /erase_heap lookup_fmap !lookup_union -fmap_replicate
-fmap_heap_array !lookup_fmap.
by destruct (heap_array l (replicate (Z.to_nat n) v) !! i);
destruct (heap σ !! i).
Qed.
Lemma erase_state_init l n v σ:
erase_state (state_init_heap l n v σ) =
state_init_heap l n (erase_val v) (erase_state σ).
Proof. by rewrite /erase_state /state_init_heap /= erase_heap_array. Qed.
Ltac simplify_erasure :=
let is_constructor A :=
let HF := fresh in
pose (match A with
| _ => A
end) as HF;
(simpl in HF); unify HF A; clear HF
in
lazymatch goal with
| H : erase_expr ?A = Val ?B |- _ => destruct A
| H : Val ?B = erase_expr ?A |- _ => destruct A
| H : _ = erase_val ?A |- _ => destruct A
| H : erase_val ?A = _ |- _ => destruct A
| H : erase_expr ?A = ?B |- _ =>
lazymatch B with
| fill _ _ => fail
| context [fill _ _] => destruct A
| _ => is_constructor B; destruct A
end
| H : ?B = erase_expr ?A |- _ =>
lazymatch B with
| fill _ _ => fail
| context [fill _ _] => destruct A
| _ => is_constructor B; destruct A
end
| H : Val _ = fill ?K ?e |- _ =>
edestruct (to_val_fill_some K e);
first (by rewrite -H; eauto)
| H : head_step (Val _) _ _ _ _ _ |- _ => apply val_head_stuck in H
| H : Rec _ _ _ = fill ?K _ |- _ =>
destruct K as [|[] ? _] using rev_ind; simpl in *;
rewrite ?fill_app in H
| H : un_op_eval _ (erase_val _) = Some _ |- _ =>
apply un_op_eval_erase in H as [? [? ?]]
| H : bin_op_eval _ (erase_val _) (erase_val _) = Some _ |- _ =>
apply bin_op_eval_erase in H as [? [? ?]]
| H : context [erase_heap (heap _) !! _] |- _ =>
rewrite /erase_heap lookup_fmap in H
| H : _ <$> (?B !! ?C) = Some _ |- _ =>
let Hf := fresh in destruct (B !! C) eqn:Hf; simpl in H; try done
| H : is_Some (_ <$> (?B !! ?C)) |- _ => destruct H as [? ?]
end; simplify_eq/=.
Definition head_steps_to_erasure_of (e1 : expr) (σ1 : state) (e2 : expr)
(σ2 : state) (efs : list expr) :=
κ' e2' σ2' efs',
head_step e1 σ1 κ' e2' σ2' efs'
erase_expr e2' = e2 erase_state σ2' = σ2 erase_tp efs' = efs.
(** When the erased program makes a head step, so does the original program. *)
Lemma erased_head_step_head_step e1 σ1 κ e2 σ2 efs:
head_step (erase_expr e1) (erase_state σ1) κ e2 σ2 efs
head_steps_to_erasure_of e1 σ1 e2 σ2 efs.
Proof.
intros Hstp; rewrite /head_steps_to_erasure_of.
inversion Hstp;
(repeat ((repeat simplify_erasure); try case_match; simplify_eq));
try
(by (eexists _, _, _, _; simpl; split;
first match goal with
| |- head_step NewProph _ _ _ _ _ => by apply new_proph_id_fresh
| _ => by econstructor;
eauto using erase_heap_lookup, val_for_compare_erase
end;
try rewrite -val_for_compare_erase;
rewrite ?erase_expr_subst' /erase_state ?erase_heap_insert /=;
eauto using erase_state_init)); [| |].
- (* case of allocN *)
eexists _, _, _, _; simpl; split;
first econstructor; try rewrite -val_for_compare_erase;
try setoid_rewrite <- erase_heap_lookup;
rewrite ?erase_heap_insert /=; eauto using erase_state_init.
- (* case of CmpXchg succeeding *)
match goal with
| H : bool_decide (val_for_compare (erase_val _)
= val_for_compare (erase_val _)) = _ |- _ =>
rename H into Hvfc; rewrite -val_for_compare_erase_bdec in Hvfc
end.
eexists _, _, _, _; simpl; split.
{ econstructor; first rewrite -vals_cmpxchg_compare_safe_erase; eauto. }
rewrite Hvfc /erase_state ?erase_heap_insert /=; eauto.
- (* case of CmpXchg failing *)
match goal with
| H : bool_decide (val_for_compare (erase_val _)
= val_for_compare (erase_val _)) = _ |- _ =>
rename H into Hvfc; rewrite -val_for_compare_erase_bdec in Hvfc
end.
eexists _, _, _, _; simpl; split.
{ econstructor; first rewrite -vals_cmpxchg_compare_safe_erase; eauto. }
rewrite Hvfc; eauto.
Qed.
Lemma fill_to_resolve e0 (v1 v2 : val) K e' :
to_val e' = None
Resolve e0 v1 v2 = fill K e'
K = [] K' Ki, K = K' ++ [ResolveLCtx Ki v1 v2].
Proof.
intros Hnv Hrs; simpl in *.
destruct K as [|Ki K _] using rev_ind; first by left.
rewrite fill_app in Hrs.
destruct Ki; simplify_eq/=; eauto; simplify_erasure.
Qed.
(* helper lemma, move? *)
Lemma head_step_to_val e σ κ (v : val) σ' efs :
head_step e σ κ v σ' efs
σ' κ' e' σ'' efs', head_step e σ' κ' e' σ'' efs' is_Some (to_val e').
Proof.
inversion 1; simplify_eq/= => ?????; inversion 1; simplify_eq; eauto; [].
match goal with | H : _ = ?A |- is_Some (to_val ?A) => rewrite -H end; eauto.
Qed.
Ltac solve_for_trivial_pure_head_step :=
let helper :=
apply rtc_once;
apply pure_head_step_pure_step;
split; rewrite /head_reducible_no_obs /=;
first (by eauto using head_step);
intros ?????; inversion 1; simplify_eq; eauto
in
let go K e :=
((etrans;
first (apply (rtc_Ectx_pure_step K); helper)); simpl)
in
repeat match goal with
| |- rtc pure_step ?A _ => reshape_expr A go
| |- rtc pure_step ?A ?A => done
end.
Lemma projs_pure_steps (v0 v1 v2 : val) :
rtc pure_step (Fst (Fst (v0, v1, v2))) v0.
Proof. solve_for_trivial_pure_head_step. Qed.
Lemma prim_step_strip_ctx K K' e1 σ1 κ e2 σ2 efs κ' e2' σ2' efs' :
head_step e1 σ1 κ' e2' σ2' efs'
prim_step (fill K (fill K' e1)) σ1 κ e2 σ2 efs
e2'', e2 = fill K e2'' prim_step (fill K' e1) σ1 κ e2'' σ2 efs.
Proof.
rewrite -fill_app => Hhstp Hpstp.
inversion Hpstp as [K'' ?? HK'' ? hhstp']; simplify_eq/=.
edestruct (step_by_val (K' ++ K)) as [K3 HK3];
eauto using val_head_stuck; simplify_eq/=.
rewrite !fill_app in HK''; repeat apply fill_inj in HK''; simplify_eq.
rewrite !fill_app.
eexists; split; first done.
rewrite -!fill_app.
by eapply Ectx_step'.
Qed.
Lemma Resolve_3_vals_unsafe (v0 v1 v2 : val) σ :
is_safe (Resolve v0 v1 v2) σ False.
Proof.
intros [[? ?]|(?&?&?&?&Hstp)]; simpl in *; first done.
inversion Hstp as [??? Hfill ? Hhstp']; simpl in *; simplify_eq.
destruct K as [|Ki K _] using rev_ind; simpl in *; simplify_eq.
- inversion Hhstp'; simplify_eq.
match goal with
| H : head_step (Val _) _ _ _ _ _ |- _ =>
by apply val_head_stuck in H
end.
- clear Hstp; rewrite fill_app /= in Hfill.
apply val_head_stuck in Hhstp'.
destruct Ki; simpl in *; simplify_eq;
match goal with
| H : Val _ = fill_item ?Ki ?e |- _ =>
by destruct (fill_item_val Ki e) as [? [-> ->]%to_val_fill_some];
first by rewrite -H; eauto
| H : Val _ = fill ?K ?e |- _ =>
edestruct (to_val_fill_some K e);
first (by rewrite -H; eauto); simplify_eq
end.
Qed.
(** (e1, σ1) takes a [prim_step] to (e2', σ2') forking threads efs'
such that σ2 is the erasure of σ2' and efs is the erasure of
efs'. Furthermore, e2 takes [pure_steps] to match up with e2. It is
crucial for us that e2 takes [pure_step]s because we need to know
that e2 does not get stuck and that the steps are deterministic.
Essentially, the main part of the erasure proof's argument is that if
the erased program takes steps, then the original program also takes
matching steps. This however, does not entirely hold. In cases where
the erasure of [Resovle] takes a step, the original program
immediately produces the value while the erased program has to still
perform projections [Fst] to get the result (see the [Resolve] case
of [erase_expr]). For this purpose, we prove that in those cases (and
also in general) the erased program also takes a number of (possibly
zero) steps so that the original and the erased programs are matched
up again. *)
Definition prim_step_matched_by_erased_steps e1 σ1 e2 σ2 efs :=
e2' σ2' κ' efs' e2'',
prim_step e1 σ1 κ' e2' σ2' efs' rtc pure_step e2 e2''
erase_expr e2' = e2'' erase_state σ2' = σ2 erase_tp efs' = efs.
Lemma prim_step_matched_by_erased_steps_ectx K e1 σ1 e2 σ2 efs :
prim_step_matched_by_erased_steps e1 σ1 e2 σ2 efs
prim_step_matched_by_erased_steps (fill K e1) σ1 (fill (erase_ectx K) e2) σ2 efs.
Proof.
intros (?&?&?&?&?&?&?&?&?&?); simplify_eq/=.
eexists _, _, _, _, _; repeat split.
- by eapply ectx_prim_step.
- by rewrite erase_ectx_expr; apply rtc_Ectx_pure_step.
Qed.
Definition is_Resolve (e : expr) :=
match e with | Resolve _ _ _ => True | _ => False end.
Global Instance is_Resolve_dec e : Decision (is_Resolve e).
Proof. destruct e; solve_decision. Qed.
Ltac solve_is_safe :=
match goal with
| H : is_safe ?C ?B |- is_safe ?A ?B =>
let tac K e := by (apply (is_safe_under_ectx K)) in
reshape_expr C tac
| H : head_step ?A ?B _ _ _ _ |- is_safe ?A ?B => by eapply head_step_safe
| H : prim_step ?A ?B _ _ _ _ |- is_safe ?A ?B => by eapply prim_step_safe
end.
Ltac solve_prim_step_ectx :=
match goal with
| |- prim_step ?A _ _ _ _ _ =>
let tac K e :=
lazymatch K with [] => fail | _ => (apply (ectx_prim_step K)) end
in
reshape_expr A tac; eauto
end.
Ltac solve_rtc_pure_step_ectx :=
match goal with
| |- rtc pure_step ?A _ =>
let tac K e :=
lazymatch K with [] => fail | _ => (apply (rtc_Ectx_pure_step K)) end
in
reshape_expr A tac; eauto
end.
Lemma non_resolve_prim_step_matched_by_erased_steps_ectx_item
Ki e1 e1' σ1 e2 σ2 efs :
to_val e1' = None
¬ is_Resolve e1
is_safe e1 σ1
erase_expr e1 = fill_item Ki e1'
( e1, erase_expr e1 = e1' is_safe e1 σ1
prim_step_matched_by_erased_steps e1 σ1 e2 σ2 efs)
prim_step_matched_by_erased_steps e1 σ1 (fill_item Ki e2) σ2 efs.
Proof.
intros Hnv Hnr Hsf He1 IH.
destruct Ki; repeat simplify_erasure; try done;
match goal with
| |- prim_step_matched_by_erased_steps ?A _ _ _ _ =>
let tac K e :=
lazymatch K with
| [] => fail
| _ => apply (prim_step_matched_by_erased_steps_ectx K)
end
in
reshape_expr A tac; apply IH; [done|solve_is_safe]
end.
Qed.
Lemma prim_step_matched_by_erased_steps_ectx_item Ki K e1 e1' σ1 e2 σ2 efs κ :
head_step e1' (erase_state σ1) κ e2 σ2 efs
is_safe e1 σ1
erase_expr e1 = fill_item Ki (fill K e1')
( K' e1, length K' length K
erase_expr e1 = (fill K' e1') is_safe e1 σ1
prim_step_matched_by_erased_steps e1 σ1 (fill K' e2) σ2 efs)
prim_step_matched_by_erased_steps e1 σ1 (fill_item Ki (fill K e2)) σ2 efs.
Proof.
intros Hhstp Hsf He1 IH; simpl in *.
(** Case split on whether e1 is a [Resolve] expression. *)
destruct (decide (is_Resolve e1)); last first.
{ (** e1 is not a [Resolve] expression. *)
eapply non_resolve_prim_step_matched_by_erased_steps_ectx_item; eauto; [|].
- by eapply fill_not_val, val_head_stuck.
- intros; eapply (IH K); simpl; eauto with lia. }
(** e1 is a [Resolve] expression. *)
destruct Ki; repeat simplify_erasure; try done; [].
destruct K as [|Ki K _] using rev_ind; simplify_eq/=; [|].
{ (* case where (Fst (erase_expr e1_1, erase_expr e1_2, erase_expr e1_3)) *)
(* takes a head_step; it is impossible! *)
inversion Hhstp. }
rewrite fill_app /= in He1.
destruct Ki; simplify_eq/=; rewrite fill_app /=.
destruct K as [|Ki K _] using rev_ind; simplify_eq/=; [|].
{ (* case where (erase_expr e1_1, erase_expr e1_2, erase_expr e1_3) *)
(* takes a head_step; it is impossible! *)
inversion Hhstp. }
rewrite fill_app /= in He1.
destruct Ki; simplify_eq/=; rewrite fill_app /=.
- destruct K as [|Ki K _] using rev_ind; simplify_eq/=; [|].
{ (** [Resolve v0 v1 v2] is not safe! *)
inversion Hhstp; repeat simplify_erasure.
exfalso; eauto using Resolve_3_vals_unsafe. }
rewrite fill_app /= in He1.
destruct Ki; simplify_eq/=; rewrite fill_app /=.
+ (** e1 is of the form ([Resolve] e10 e11 v0) and e11 takes a prim_step. *)
destruct Hsf as [[? ?]| (?&?&?&?&Hrpstp)]; first done; simpl in *.
inversion Hrpstp as [??? Hrs ? Hhstp']; simpl in *; simplify_eq.
repeat simplify_erasure.
edestruct fill_to_resolve as [?|[K' [Ki HK]]]; eauto;
[by eapply val_head_stuck| |]; simplify_eq/=.
* (** e1 is of the form ([Resolve] e10 e11 v0) and e11 takes a head_step. *)
inversion Hhstp'; simplify_eq.
edestruct (IH K) as (?&?&?&?&?&Hpstp&?&?&?&?);
[rewrite !app_length /=; lia|done|by eapply head_step_safe|];
simplify_eq/=.
apply head_reducible_prim_step in Hpstp; simpl in *;
last rewrite /head_reducible /=; eauto 10.
edestruct head_step_to_val as [? ?%of_to_val]; eauto; simplify_eq.
eexists _, _, _, _, _; repeat split;
first (by apply head_prim_step; econstructor; eauto); auto.
etrans.
{ by apply (rtc_Ectx_pure_step
[PairLCtx _; PairLCtx _; FstCtx; FstCtx]). }
apply projs_pure_steps.
* (** e1 is of the form ([Resolve] e10 v v0) and e10 takes a
(non-head) prim_step. *)
rewrite fill_app in Hrs; simplify_eq/=.
edestruct (IH K) as (?&?&?&?&?&Hpstp&?&?&?&?);
[rewrite !app_length; lia|done| |].
{ change (fill_item Ki) with (fill [Ki]).
by rewrite -fill_app; eapply prim_step_safe, Ectx_step. }
simplify_eq/=.
eapply (prim_step_strip_ctx [_]) in Hpstp as [e2'' [He2'' Hpstp]];
last done; simplify_eq/=.
eexists _, _, _, _, _; repeat split; eauto;
first by apply (ectx_prim_step [ResolveLCtx _ _ _]).
by apply (rtc_Ectx_pure_step [PairLCtx _; PairLCtx _; FstCtx; FstCtx]).
+ (** e1 is of the form ([Resolve] e1_ e1_2 v) and e1_2 takes a prim_step. *)
repeat simplify_erasure.
apply (prim_step_matched_by_erased_steps_ectx [ResolveMCtx _ _]).
apply IH; [rewrite !app_length /=; lia|done|solve_is_safe].
- (** e1 is of the form ([Resolve] e1_ e1_2 e13) and e1_3 takes a prim_step. *)
apply (prim_step_matched_by_erased_steps_ectx [ResolveRCtx _ _]).
apply IH; [rewrite !app_length /=; lia|done|solve_is_safe].
Qed.
Lemma erased_prim_step_prim_step e1 σ1 κ e2 σ2 efs:
prim_step (erase_expr e1) (erase_state σ1) κ e2 σ2 efs is_safe e1 σ1
prim_step_matched_by_erased_steps e1 σ1 e2 σ2 efs.
Proof.
intros Hstp He1sf.
inversion Hstp as [K e1' e2' He1 ? Hhstp]; clear Hstp;
simpl in *; simplify_eq.
set (len := length K); assert (length K = len) as Hlen by done; clearbody len.
revert K Hlen e1 He1 He1sf.
induction len as [m IHm]using lt_wf_ind; intros K Hlen e1 He1 He1sf;
simplify_eq.
destruct K as [|Ki K _] using rev_ind; simpl in *; simplify_eq.
{ apply erased_head_step_head_step in Hhstp as (?&?&?&?&?&<-&?&<-).
eexists _, _, _, _, _; repeat split;
first (by apply head_prim_step); auto using rtc_refl. }
rewrite app_length <