aboutsummaryrefslogtreecommitdiffstats
From mininix Require Export utils nix.operational.
From stdpp Require Import options.

(** Properties of operational semantics *)
Lemma float_to_bounded_Z_ok f : int_ok (float_to_bounded_Z f).
Proof.
  rewrite /float_to_bounded_Z.
  destruct (Float.to_Z f); simplify_option_eq; done.
Qed.

Lemma int_ok_alt i :
  int_ok i ↔ ∀ n, (63 ≤ n)%Z → Z.testbit i n = bool_decide (i < 0)%Z.
Proof.
  rewrite -Z.bounded_iff_bits //.
  rewrite /int_ok bool_decide_spec /int_min /int_max Z.shiftl_1_l. lia.
Qed.

Lemma int_ok_land i1 i2 : int_ok i1 → int_ok i2 → int_ok (Z.land i1 i2).
Proof.
  rewrite !int_ok_alt=> Hi1 Hi2 n ?. rewrite Z.land_spec Hi1 // Hi2 //.
  apply eq_bool_prop_intro. rewrite andb_True !bool_decide_spec Z.land_neg //.
Qed.

Lemma int_ok_lor i1 i2 : int_ok i1 → int_ok i2 → int_ok (Z.lor i1 i2).
Proof.
  rewrite !int_ok_alt=> Hi1 Hi2 n ?. rewrite Z.lor_spec Hi1 // Hi2 //.
  apply eq_bool_prop_intro. rewrite orb_True !bool_decide_spec Z.lor_neg //.
Qed.

Lemma int_ok_lxor i1 i2 : int_ok i1 → int_ok i2 → int_ok (Z.lxor i1 i2).
Proof.
  rewrite !int_ok_alt=> Hi1 Hi2 n ?. rewrite Z.lxor_spec Hi1 // Hi2 //.
  apply eq_bool_prop_intro. rewrite xorb_True !bool_decide_spec.
  rewrite !Z.lt_nge Z.lxor_nonneg. lia.
Qed.

Lemma sem_bin_op_num_ok {op f n1 n2 bl} :
  num_ok n1 → num_ok n2 →
  sem_bin_op_num op n1 = Some f → f n2 = Some bl → base_lit_ok bl.
Proof.
  intros; destruct op, n1, n2; simplify_option_eq;
    try (by apply (bool_decide_pack _));
    auto using int_ok_land, int_ok_lor, int_ok_lxor.
Qed.

Lemma sem_bin_op_string_ok {op f s1 s2} :
  sem_bin_op_string op = Some f → base_lit_ok (f s1 s2).
Proof. intros; destruct op; naive_solver. Qed.

Global Hint Extern 0 (no_recs (_ <$> _)) => by apply map_Forall_fmap : core.

Ltac inv_step := repeat
  match goal with
  | H : ¬no_recs (_ <$> _) |- _ => destruct H; by apply map_Forall_fmap
  | H : ?e -{_}-> _ |- _ => assert_succeeds (is_app_constructor e); inv H
  | H : ctx1 _ _ ?K |- _ => is_var K; inv H
  end.

Global Instance Attr_inj τ : Inj (=) (=) (Attr τ).
Proof. by injection 1. Qed.

Lemma fmap_attr_expr_Attr τ (es : gmap string expr) :
  attr_expr <$> (Attr τ <$> es) = es.
Proof. apply map_eq=> x. rewrite !lookup_fmap. by destruct (_ !! _). Qed.

Lemma no_recs_insert αs x e : no_recs αs → no_recs (<[x:=AttrN e]> αs).
Proof. by apply map_Forall_insert_2. Qed.
Lemma no_recs_insert_inv αs x τ e :
  αs !! x = None → no_recs (<[x:=Attr τ e]> αs) → no_recs αs.
Proof. intros ??%map_Forall_insert; naive_solver. Qed.
Lemma no_recs_lookup αs x τ e : no_recs αs → αs !! x = Some (Attr τ e) → τ = NONREC.
Proof. intros Hall. apply Hall. Qed.

Lemma no_recs_attr_subst αs ds : no_recs αs → no_recs (attr_subst ds <$> αs).
Proof.
  intros. eapply map_Forall_fmap, map_Forall_impl; [done|]. by intros ? [[]] [=].
Qed.

Lemma from_attr_no_recs {A} (f g : expr → A) (αs : gmap string attr) :
  no_recs αs → from_attr f g <$> αs = g ∘ attr_expr <$> αs.
Proof.
  intros Hrecs. apply map_eq=> x. rewrite !lookup_fmap. specialize (Hrecs x).
  destruct (αs !! x) as [[]|] eqn:?; naive_solver.
Qed.

Lemma sem_and_attr_empty : sem_and_attr ∅ = ELit (LitBool true).
Proof. done. Qed.
Lemma sem_and_attr_insert es x e :
  es !! x = None → (∀ y, is_Some (es !! y) → attr_le x y) →
  sem_and_attr (<[x:=e]> es) = EIf e (sem_and_attr es) (ELit (LitBool false)).
Proof. intros. by rewrite /sem_and_attr map_fold_sorted_insert. Qed.

Lemma matches_strict es ms ds x e :
  es !! x = None →
  ms !! x = None →
  matches es ms false ds →
  matches (<[x:=e]> es) ms false ds.
Proof.
  remember false as strict.
  induction 3; simplify_eq/=;
    repeat match goal with
    | H : <[ _ := _ ]> _ !! _ = None |- _ => apply lookup_insert_None in H as [??]
    | _ => rewrite (insert_commute _ x) //
    | _ => constructor
    | _ => apply lookup_insert_None
    end; eauto.
Qed.

Lemma subst_empty e : subst ∅ e = e.
Proof.
  induction e; repeat destruct select (option _); do 2 f_equal/=; auto.
  - apply map_eq=> x. rewrite lookup_fmap.
    destruct (_ !! x) as [[e'|]|] eqn:Hx; do 2 f_equal/=. apply (H _ _ Hx).
  - induction H; f_equal/=; auto.
  - apply map_eq; intros i. rewrite lookup_fmap.
    destruct (_ !! i) as [[τ e]|] eqn:?; do 2 f_equal/=.
    by eapply (H _ (Attr _ _)).
Qed.

Lemma subst_union ds1 ds2 e :
  subst (union_kinded ds1 ds2) e = subst ds1 (subst ds2 e).
Proof.
  revert ds1 ds2. induction e; intros ds1 ds2; f_equal/=; auto.
  - rewrite lookup_union_with.
    destruct mkd as [[[]]|],
      (ds1 !! x) as [[[] t1]|], (ds2 !! x) as [[[] t2]|]; naive_solver.
  - apply map_eq=> y. rewrite !lookup_fmap.
    destruct (_ !! y) as [[e'|]|] eqn:Hy; do 2 f_equal/=.
    rewrite -(H _ _ Hy) //.
  - induction H; f_equal/=; auto.
  - apply map_eq=> y. rewrite !lookup_fmap.
    destruct (_ !! y) as [[τ e]|] eqn:Hy; do 2 f_equal/=.
    rewrite -(H _ _ Hy) //.
Qed.

Lemma SAppL μ e1 e1' e2 :
  e1 -{SHALLOW}-> e1' → EApp e1 e2 -{μ}-> EApp e1' e2.
Proof. apply (SCtx (λ e, EApp e _)). constructor. Qed.
Lemma SAppR μ ms strict e1 e2 e2' :
  e2 -{SHALLOW}-> e2' →
  EApp (EAbsMatch ms strict e1) e2 -{μ}-> EApp (EAbsMatch ms strict e1) e2'.
Proof. apply SCtx. constructor. Qed.
Lemma SSeq μ μ' e1 e1' e2 :
  e1 -{μ'}-> e1' → ESeq μ' e1 e2 -{μ}-> ESeq μ' e1' e2.
Proof. apply (SCtx (λ e, ESeq _ e _)). constructor. Qed.
Lemma SList es1 e e' es2 :
  Forall (final DEEP) es1 →
  e -{DEEP}-> e' →
  EList (es1 ++ e :: es2) -{DEEP}-> EList (es1 ++ e' :: es2).
Proof. intros ?. apply (SCtx (λ e, EList (_ ++ e :: _))). by constructor. Qed.
Lemma SAttr αs x e e' :
  no_recs αs →
  αs !! x = None →
  (∀ y α, αs !! y = Some α → final DEEP (attr_expr α) ∨ attr_le x y) →
  e -{DEEP}-> e' →
  EAttr (<[x:=AttrN e]> αs) -{DEEP}-> EAttr (<[x:=AttrN e']> αs).
Proof. intros ???. apply (SCtx (λ e, EAttr (<[x:=AttrN e]> _))). by constructor. Qed.
Lemma SLetAttr μ k e1 e1' e2 :
  e1 -{SHALLOW}-> e1' → ELetAttr k e1 e2 -{μ}-> ELetAttr k e1' e2.
Proof. apply (SCtx (λ e, ELetAttr _ e _)). constructor. Qed.
Lemma SBinOpL μ op e1 e1' e2 :
  e1 -{SHALLOW}-> e1' → EBinOp op e1 e2 -{μ}-> EBinOp op e1' e2.
Proof. apply (SCtx (λ e, EBinOp _ e _)). constructor. Qed.
Lemma SBinOpR μ op e1 Φ e2 e2' :
  final SHALLOW e1 → sem_bin_op op e1 Φ →
  e2 -{SHALLOW}-> e2' → EBinOp op e1 e2 -{μ}-> EBinOp op e1 e2'.
Proof. intros ??. apply SCtx. by econstructor. Qed.
Lemma SIf μ e1 e1' e2 e3 :
  e1 -{SHALLOW}-> e1' → EIf e1 e2 e3 -{μ}-> EIf e1' e2 e3.
Proof. apply (SCtx (λ e, EIf e _ _)). constructor. Qed.

Global Hint Constructors step : step.
Global Hint Resolve SAppL SAppR SSeq SList SAttr SLetAttr SBinOpL SBinOpR SIf : step.

Lemma step_not_final μ e1 e2 : e1 -{μ}-> e2 → ¬final μ e1.
Proof.
  assert (∀ (αs : gmap string attr) x μ e,
    map_Forall (λ _, final DEEP ∘ attr_expr) (<[x:=Attr μ e]> αs) → final DEEP e).
  { intros αs x μ' e Hall. eapply (Hall _ (Attr _ _)), lookup_insert. }
  induction 1; inv 1; inv_step; decompose_Forall; naive_solver.
Qed.
Lemma final_nf μ e : final μ e → nf (step μ) e.
Proof. by intros ? [??%step_not_final]. Qed.

Lemma step_any_shallow μ e1 e2 :
  e1 -{μ}-> e2 → e1 -{SHALLOW}-> e2 ∨ final SHALLOW e1.
Proof.
  induction 1; inv_step;
    naive_solver eauto using final, no_recs_insert with step.
Qed.

Lemma step_shallow_any μ e1 e2 : e1 -{SHALLOW}-> e2 → e1 -{μ}-> e2.
Proof.
  remember SHALLOW as μ'. induction 1; inv_step; simplify_eq/=; eauto with step.
Qed.
Lemma steps_shallow_any μ e1 e2 : e1 -{SHALLOW}->* e2 → e1 -{μ}->* e2.
Proof. induction 1; eauto using rtc, step_shallow_any. Qed.
Lemma final_any_shallow μ e : final μ e → final SHALLOW e.
Proof. destruct μ; [done|]. induction 1; simplify_eq/=; eauto using final. Qed.
Lemma stuck_shallow_any μ e : stuck SHALLOW e → stuck μ e.
Proof.
  intros [Hnf Hfinal]. split; [|naive_solver eauto using final_any_shallow].
  intros [e' Hstep%step_any_shallow]; naive_solver.
Qed.

Lemma step_final_shallow μ e1 e2 :
  final SHALLOW e1 → e1 -{μ}-> e2 → final SHALLOW e2.
Proof.
  induction 1; intros; inv_step; decompose_Forall;
    eauto using step, final, no_recs_insert; try done.
  - by odestruct step_not_final.
  - apply map_Forall_insert in H0 as [??]; simpl in *; last done.
    by odestruct step_not_final.
Qed.

Lemma SAppL_rtc μ e1 e1' e2 :
  e1 -{SHALLOW}->* e1' → EApp e1 e2 -{μ}->* EApp e1' e2.
Proof. induction 1; econstructor; eauto with step. Qed.
Lemma SAppR_rtc μ ms strict e1 e2 e2' :
  e2 -{SHALLOW}->* e2' →
  EApp (EAbsMatch ms strict e1) e2 -{μ}->* EApp (EAbsMatch ms strict e1) e2'.
Proof. induction 1; econstructor; eauto with step. Qed.
Lemma SSeq_rtc μ μ' e1 e1' e2 :
  e1 -{μ'}->* e1' → ESeq μ' e1 e2 -{μ}->* ESeq μ' e1' e2.
Proof. induction 1; econstructor; eauto with step. Qed.
Lemma SList_rtc es1 e e' es2 :
  Forall (final DEEP) es1 →
  e -{DEEP}->* e' →
  EList (es1 ++ e :: es2) -{DEEP}->* EList (es1 ++ e' :: es2).
Proof. induction 2; econstructor; eauto with step. Qed.
Lemma SLetAttr_rtc μ k e1 e1' e2 :
  e1 -{SHALLOW}->* e1' → ELetAttr k e1 e2 -{μ}->* ELetAttr k e1' e2.
Proof. induction 1; econstructor; eauto with step. Qed.
Lemma SBinOpL_rtc μ op e1 e1' e2 :
  e1 -{SHALLOW}->* e1' → EBinOp op e1 e2 -{μ}->* EBinOp op e1' e2.
Proof. induction 1; econstructor; eauto with step. Qed.
Lemma SBinOpR_rtc μ op e1 Φ e2 e2' :
  final SHALLOW e1 → sem_bin_op op e1 Φ →
  e2 -{SHALLOW}->* e2' → EBinOp op e1 e2 -{μ}->* EBinOp op e1 e2'.
Proof. induction 3; econstructor; eauto with step. Qed.
Lemma SIf_rtc μ e1 e1' e2 e3 :
  e1 -{SHALLOW}->* e1' → EIf e1 e2 e3 -{μ}->* EIf e1' e2 e3.
Proof. induction 1; econstructor; eauto with step. Qed.

Lemma SApp_tc μ e1 e1' e2 :
  e1 -{SHALLOW}->+ e1' → EApp e1 e2 -{μ}->+ EApp e1' e2.
Proof. induction 1; by econstructor; eauto with step. Qed.
Lemma SSeq_tc μ μ' e1 e1' e2 :
  e1 -{μ'}->+ e1' → ESeq μ' e1 e2 -{μ}->+ ESeq μ' e1' e2.
Proof. induction 1; by econstructor; eauto with step. Qed.
Lemma SList_tc es1 e e' es2 :
  Forall (final DEEP) es1 →
  e -{DEEP}->+ e' →
  EList (es1 ++ e :: es2) -{DEEP}->+ EList (es1 ++ e' :: es2).
Proof. induction 2; by econstructor; eauto with step. Qed.
Lemma SLetAttr_tc μ k e1 e1' e2 :
  e1 -{SHALLOW}->+ e1' → ELetAttr k e1 e2 -{μ}->+ ELetAttr k e1' e2.
Proof. induction 1; by econstructor; eauto with step. Qed.
Lemma SBinOpL_tc μ op e1 e1' e2 :
  e1 -{SHALLOW}->+ e1' → EBinOp op e1 e2 -{μ}->+ EBinOp op e1' e2.
Proof. induction 1; by econstructor; eauto with step. Qed.
Lemma SBinOpR_tc μ op e1 Φ e2 e2' :
  final SHALLOW e1 → sem_bin_op op e1 Φ →
  e2 -{SHALLOW}->+ e2' → EBinOp op e1 e2 -{μ}->+ EBinOp op e1 e2'.
Proof. induction 3; by econstructor; eauto with step. Qed.
Lemma SIf_tc μ e1 e1' e2 e3 :
  e1 -{SHALLOW}->+ e1' → EIf e1 e2 e3 -{μ}->+ EIf e1' e2 e3.
Proof. induction 1; by econstructor; eauto with step. Qed.

Lemma SList_inv es1 e2 :
  EList es1 -{DEEP}-> e2 ↔ ∃ ds1 ds2 e e',
    es1 = ds1 ++ e :: ds2 ∧ e2 = EList (ds1 ++ e' :: ds2) ∧
    Forall (final DEEP) ds1 ∧
    e -{DEEP}-> e'.
Proof. split; intros; inv_step; naive_solver eauto using SList. Qed.

Lemma List_nf_cons_final es e :
  final DEEP e →
  nf (step DEEP) (EList es) →
  nf (step DEEP) (EList (e :: es)).
Proof.
  intros Hfinal Hnf [e' (ds1 & ds2 & e1 & e2 & ? & -> & Hds1 & Hstep)%SList_inv].
  destruct Hds1; simplify_eq/=.
  - by apply step_not_final in Hstep.
  - naive_solver eauto with step.
Qed.
Lemma List_nf_cons es e :
  ¬final DEEP e →
  nf (step DEEP) e →
  nf (step DEEP) (EList (e :: es)).
Proof.
  intros Hfinal Hnf [e' (ds1 & ds2 & e1 & e2 & ? & -> & Hds1 & Hstep)%SList_inv].
  destruct Hds1; naive_solver.
Qed.

Lemma List_steps_cons es1 es2 e :
  final DEEP e →
  EList es1 -{DEEP}->* EList es2 →
  EList (e :: es1) -{DEEP}->* EList (e :: es2).
Proof.
  intros ? Hstep.
  remember (EList es1) as e1 eqn:He1; remember (EList es2) as e2 eqn:He2.
  revert es1 es2 He1 He2.
  induction Hstep as [|e1 e2 e3 Hstep Hstep' IH];
    intros es1 es3 ??; simplify_eq/=; [done|].
  inv_step. eapply rtc_l; [apply (SList (_ :: _))|]; naive_solver.
Qed.

Lemma SAttr_rec_rtc μ αs :
  EAttr αs -{μ}->*
    EAttr (AttrN ∘ from_attr (subst (indirects αs)) id <$> αs).
Proof.
  destruct (decide (no_recs αs)) as [Hαs|]; [|by eauto using rtc_once, step].
  eapply reflexive_eq. f_equal. apply map_eq=> x. rewrite lookup_fmap.
  destruct (αs !! x) as [[τ e]|] eqn:?; [|done].
  assert (τ = NONREC) as -> by eauto using no_recs_lookup. done.
Qed.

Lemma SAttr_lookup_rtc αs x e e' :
  no_recs αs →
  αs !! x = None →
  (∀ y α, αs !! y = Some α → final DEEP (attr_expr α) ∨ attr_le x y) →
  e -{DEEP}->* e' →
  EAttr (<[x:=AttrN e]> αs) -{DEEP}->* EAttr (<[x:=AttrN e']> αs).
Proof.
  intros Hrecs Hx Hfirst He. revert αs Hrecs Hx Hfirst.
  induction He as [e|e1 e2 e3 He12 He23 IH]; intros eτs Hrec Hx Hfirst; [done|].
  eapply rtc_l; first by eapply SAttr. apply IH; [done..|].
  apply step_not_final in He12. naive_solver.
Qed.

Lemma SAttr_inv αs1 e2 :
  no_recs αs1 →
  EAttr αs1 -{DEEP}-> e2 ↔ ∃ αs x e e',
    αs1 = <[x:=AttrN e]> αs ∧ e2 = EAttr (<[x:=AttrN e']> αs) ∧
    αs !! x = None ∧
    (∀ y α, αs !! y = Some α → final DEEP (attr_expr α) ∨ attr_le x y) ∧
    e -{DEEP}-> e'.
Proof.
  split; [intros; inv_step|];
    naive_solver eauto using SAttr, no_recs_insert_inv.
Qed.

Lemma Attr_nf_insert_final αs x e :
  no_recs αs →
  αs !! x = None →
  final DEEP e →
  (∀ y, is_Some (αs !! y) → attr_le x y) →
  nf (step DEEP) (EAttr αs) →
  nf (step DEEP) (EAttr (<[x:=AttrN e]> αs)).
Proof.
  intros Hrecs Hx Hfinal Hleast Hnf
    [? (αs'&x'&e'&e''&Hαs&->&Hx'&?&Hstep)%SAttr_inv];
    last by eauto using no_recs_insert.
  assert (x ≠ x').
  { intros ->. apply (f_equal (.!! x')) in Hαs. rewrite !lookup_insert in Hαs.
    apply step_not_final in Hstep. naive_solver. }
  destruct Hnf. exists (EAttr (<[x':=AttrN e'']> (delete x αs'))).
  rewrite -(delete_insert αs x (AttrN e)) // Hαs delete_insert_ne //.
  refine (SCtx _ _ _ _ _ (CAttr _ _ _ _ _) _);
    [|by rewrite lookup_delete_ne| |done].
  - apply (no_recs_insert _ x e) in Hrecs. rewrite Hαs in Hrecs.
    apply no_recs_insert_inv in Hrecs; last done. by apply map_Forall_delete.
  - intros ?? ?%lookup_delete_Some; naive_solver.
Qed.
Lemma Attr_nf_insert αs x e :
  no_recs αs →
  αs !! x = None →
  ¬final DEEP e →
  (∀ y, is_Some (αs !! y) → attr_le x y) →
  nf (step DEEP) e →
  nf (step DEEP) (EAttr (<[x:=AttrN e]> αs)).
Proof.
  intros Hrecs Hx ?? Hnf [? (αs'&x'&e'&e''&Hαs&->&Hx'&Hleast'&Hstep)%SAttr_inv];
    last eauto using no_recs_insert.
  assert (x ≠ x') as Hxx'.
  { intros ->. apply (f_equal (.!! x')) in Hαs. rewrite !lookup_insert in Hαs.
    naive_solver. }
  odestruct (Hleast' x (AttrN e)); [|done|].
  - apply (f_equal (.!! x)) in Hαs.
    by rewrite lookup_insert lookup_insert_ne in Hαs.
  - apply (f_equal (.!! x')) in Hαs.
    rewrite lookup_insert lookup_insert_ne // in Hαs.
    destruct Hxx'. apply (anti_symm attr_le); naive_solver.
Qed.

Lemma Attr_step_dom μ αs1 e2 :
  EAttr αs1 -{μ}-> e2 →
  ∃ αs2, e2 = EAttr αs2 ∧ ∀ i, αs1 !! i = None ↔ αs2 !! i = None.
Proof.
  intros; inv_step; (eexists; split; [done|]).
  - intros i. by rewrite lookup_fmap fmap_None.
  - intros i. rewrite !lookup_insert_None; naive_solver.
Qed.
Lemma Attr_steps_dom μ αs1 αs2 :
  EAttr αs1 -{μ}->* EAttr αs2 → ∀ i, αs1 !! i = None ↔ αs2 !! i = None.
Proof.
  intros Hstep.
  remember (EAttr αs1) as e1 eqn:He1; remember (EAttr αs2) as e2 eqn:He2.
  revert αs1 αs2 He1 He2. induction Hstep as [|e1 e2 e3 Hstep Hstep' IH];
    intros αs1 αs3 ??; simplify_eq/=; [done|].
  apply Attr_step_dom in Hstep; naive_solver.
Qed.

Lemma Attr_step_recs αs1 αs2 :
  EAttr αs1 -{DEEP}-> EAttr αs2 → no_recs αs1 → no_recs αs2.
Proof. intros. inv_step; by eauto using no_recs_insert. Qed.
Lemma Attr_steps_recs αs1 αs2 :
  EAttr αs1 -{DEEP}->* EAttr αs2 → no_recs αs1 → no_recs αs2.
Proof.
  intros Hstep.
  remember (EAttr αs1) as e1 eqn:He1; remember (EAttr αs2) as e2 eqn:He2.
  revert αs1 αs2 He1 He2. induction Hstep as [|e1 e2 e3 Hstep Hstep' IH];
    intros αs1 αs3 ???; simplify_eq/=; [done|].
  pose proof (Attr_step_dom _ _ _ Hstep) as (es2 & -> & ?).
  apply Attr_step_recs in Hstep; naive_solver.
Qed.

Lemma Attr_step_insert αs1 αs2 x e :
  no_recs αs1 →
  αs1 !! x = None →
  final DEEP e →
  EAttr αs1 -{DEEP}-> EAttr αs2 →
  EAttr (<[x:=AttrN e]> αs1) -{DEEP}-> EAttr (<[x:=AttrN e]> αs2).
Proof.
  intros Hrecs Hx ?
    (αs' & x' & e1 & e1' & ? & ? & ? & ? & ?)%SAttr_inv; [|done]; simplify_eq.
  apply lookup_insert_None in Hx as [??]. rewrite !(insert_commute _ x) //.
  eapply SAttr; [|by rewrite lookup_insert_ne| |done].
  - by eapply no_recs_insert, no_recs_insert_inv.
  - intros y e' ?%lookup_insert_Some; naive_solver.
Qed.
Lemma Attr_steps_insert αs1 αs2 x e :
  no_recs αs1 →
  αs1 !! x = None →
  final DEEP e →
  EAttr αs1 -{DEEP}->* EAttr αs2 →
  EAttr (<[x:=AttrN e]> αs1) -{DEEP}->* EAttr (<[x:=AttrN e]> αs2).
Proof.
  intros Hrecs Hx ? Hstep.
  remember (EAttr αs1) as e1 eqn:He1; remember (EAttr αs2) as e2 eqn:He2.
  revert αs1 αs2 Hx Hrecs He1 He2.
  induction Hstep as [|e1 e2 e3 Hstep Hstep' IH];
    intros αs1 αs3 ????; simplify_eq/=; [done|].
  pose proof (Attr_step_dom _ _ _ Hstep) as (αs2 & -> & Hdom).
  eapply rtc_l; first by eapply Attr_step_insert.
  eapply IH; naive_solver eauto using Attr_step_recs.
Qed.

Reserved Infix "=D=>" (right associativity, at level 55).

Inductive step_delayed : relation expr :=
  | RDrefl e :
      e =D=> e
  | RDId x e1 e2 :
      e1 =D=> e2 →
      EId x (Some (ABS, e1)) =D=> e2
  | RDBinOp op e1 e1' e2 e2' :
      e1 =D=> e1' → e2 =D=> e2' → EBinOp op e1 e2 =D=> EBinOp op e1' e2'
  | RDIf e1 e1' e2 e2' e3 e3' :
      e1 =D=> e1' → e2 =D=> e2' → e3 =D=> e3' → EIf e1 e2 e3 =D=> EIf e1' e2' e3'
where "e1 =D=> e2" := (step_delayed e1 e2).

Global Instance step_delayed_po : PreOrder step_delayed.
Proof.
  split; [constructor|].
  intros e1 e2 e3 Hstep. revert e3.
  induction Hstep; inv 1; eauto using step_delayed.
Qed.
Hint Extern 0 (_ =D=> _) => reflexivity : core.

Lemma delayed_final_l e1 e2 :
  final SHALLOW e1 →
  e1 =D=> e2 →
  e1 = e2.
Proof. intros Hfinal. induction 1; try by inv Hfinal. Qed.

Lemma delayed_final_r μ e1 e2 :
  final μ e2 →
  e1 =D=> e2 →
  e1 -{μ}->* e2.
Proof.
  intros Hfinal. induction 1; try by inv Hfinal.
  eapply rtc_l; [constructor|]. eauto.
Qed.

Lemma delayed_step_l μ e1 e1' e2 :
  e1 =D=> e1' →
  e1 -{μ}-> e2 →
  ∃ e2', e1' -{μ}->* e2' ∧ e2 =D=> e2'.
Proof.
  intros Hrem. revert μ e2.
  induction Hrem; intros μ ? Hstep.
  - eauto using rtc_once.
  - inv_step. by exists e2.
  - inv_step.
    + eapply delayed_final_l in Hrem1 as ->, Hrem2 as ->; [|by eauto..].
      eexists; split; [|done]. eapply rtc_once. by econstructor.
    + apply IHHrem1 in H2 as (e1'' & ? & ?).
      eexists; split; [by eapply SBinOpL_rtc|]. by constructor.
    + eapply delayed_final_l in Hrem1 as ->; [|by eauto..].
      apply IHHrem2 in H2 as (e2'' & ? & ?).
      eexists (EBinOp _ e1' e2''); split; [|by constructor].
      by eapply SBinOpR_rtc.
  - inv_step.
    + eapply delayed_final_l in Hrem1 as <-; [|by repeat constructor].
      eexists; split; [eapply rtc_once; constructor|]. by destruct b.
    + apply IHHrem1 in H2 as (e1'' & ? & ?).
      eexists; split; [by eapply SIf_rtc|]. by constructor.
Qed.

Lemma delayed_steps_l μ e1 e1' e2 :
  e1 =D=> e1' →
  e1 -{μ}->* e2 →
  ∃ e2', e1' -{μ}->* e2' ∧ e2 =D=> e2'.
Proof.
  intros Hdel Hsteps. revert e1' Hdel.
  induction Hsteps as [e|e1 e2 e3 Hstep Hsteps IH]; intros e1' Hdel.
  { eexists; by split. }
  eapply delayed_step_l in Hstep as (e2' & Hstep2 & Hdel2); [|done].
  apply IH in Hdel2 as (e3' & ? & ?). eexists; by split; [etrans|].
Qed.

Lemma delayed_step_r μ e1 e1' e2 :
  e1' =D=> e1 →
  e1 -{μ}-> e2 →
  ∃ e2', e1' -{μ}->+ e2' ∧ e2' =D=> e2.
Proof.
  intros Hrem. revert μ e2.
  induction Hrem; intros μ ? Hstep.
  - eauto using tc_once.
  - apply IHHrem in Hstep as (e1' & ? & ?).
    eexists. split; [|done]. eapply tc_l; [econstructor|done].
  - inv_step.
    + exists e0; split; [|done].
      eapply tc_rtc_l; [by eapply SBinOpL_rtc, delayed_final_r, Hrem1|].
      eapply tc_rtc_l; [by eapply SBinOpR_rtc, delayed_final_r, Hrem2|].
      eapply tc_once. by econstructor.
    + apply IHHrem1 in H2 as (e1'' & ? & ?).
      eexists; split; [by eapply SBinOpL_tc|]. by constructor.
    + apply IHHrem2 in H2 as (e2'' & ? & ?).
      eexists (EBinOp _ e1' e2''); split; [|by apply RDBinOp].
      eapply tc_rtc_l; [by eapply SBinOpL_rtc, delayed_final_r, Hrem1|].
      by eapply SBinOpR_tc.
  - inv_step.
    + exists (if b then e2 else e3). split; [|by destruct b].
      eapply tc_rtc_l;
        [eapply SIf_rtc, delayed_final_r, Hrem1; by repeat constructor|].
      eapply tc_once; constructor.
    + apply IHHrem1 in H2 as (e1'' & ? & ?).
      eexists; split; [by eapply SIf_tc|]. by constructor.
Qed.

Lemma delayed_steps_r μ e1 e1' e2 :
  e1' =D=> e1 →
  e1 -{μ}->* e2 →
  ∃ e2', e1' -{μ}->* e2' ∧ e2' =D=> e2.
Proof.
  intros Hdel Hsteps. revert e1' Hdel.
  induction Hsteps as [e|e1 e2 e3 Hstep Hsteps IH]; intros e1' Hdel.
  { eexists; by split. }
  eapply delayed_step_r in Hstep as (e2' & Hstep2%tc_rtc & Hdel2); [|done].
  apply IH in Hdel2 as (e3' & ? & ?). eexists; by split; [etrans|].
Qed.

(** Determinism *)

Lemma bin_op_det op e Φ Ψ :
  sem_bin_op op e Φ →
  sem_bin_op op e Ψ →
  Φ = Ψ.
Proof. by destruct 1; inv 1. Qed.

Lemma bin_op_rel_det op e1 Φ e2 d1 d2 :
  sem_bin_op op e1 Φ →
  Φ e2 d1 →
  Φ e2 d2 →
  d1 = d2.
Proof.
  assert (AntiSymm eq attr_le) by apply _.
  unfold AntiSymm in *. inv 1; repeat case_match; naive_solver.
Qed.

Lemma matches_present x e md es ms strict βs :
  es !! x = Some e → ms !! x = Some md →
  matches es ms strict βs →
  βs !! x = Some (AttrN e).
Proof.
  intros Hes Hms. induction 1; try done.
  - by apply lookup_insert_Some in Hes as [[]|[]]; simplify_map_eq.
  - by simplify_map_eq.
Qed.

Lemma matches_default x es ms d strict βs :
  es !! x = None →
  ms !! x = Some (Some d) →
  matches es ms strict βs →
  βs !! x = Some (AttrR d).
Proof.
  intros Hes Hms. induction 1; try done.
  - by apply lookup_insert_None in Hes as []; simplify_map_eq.
  - by apply lookup_insert_Some in Hms as [[]|[]]; simplify_map_eq.
Qed.

Lemma matches_weaken x es ms strict βs :
  matches es ms strict βs →
  matches (delete x es) (delete x ms) strict (delete x βs).
Proof.
  induction 1; [constructor|constructor|..]; rename x0 into y;
    (destruct (decide (x = y)) as [->|Hxy];
      [ rewrite !delete_insert_delete //
      | rewrite !delete_insert_ne //; constructor;
        by simplify_map_eq ]).
Qed.

Lemma matches_det es ms strict βs1 βs2 :
  matches es ms strict βs1 →
  matches es ms strict βs2 →
  βs1 = βs2.
Proof.
  intros Hβs1. revert βs2. induction Hβs1; intros βs2 Hβs2;
    try (inv Hβs2; done || (by exfalso; eapply (insert_non_empty (M:=stringmap)))).
  - eapply (matches_weaken x) in Hβs2 as Hβs2'.
    rewrite !delete_insert // in Hβs2'.
    rewrite (IHHβs1 _ Hβs2') insert_delete //.
    eapply matches_present; eauto; apply lookup_insert.
  - eapply (matches_weaken x) in Hβs2 as Hβs2'.
    rewrite delete_notin // delete_insert // in Hβs2'.
    rewrite (IHHβs1 _ Hβs2') insert_delete //.
    eapply matches_default; eauto. apply lookup_insert.
Qed.

Lemma ctx_det K1 K2 e1 e2 μ μ1' μ2' :
  K1 e1 = K2 e2 →
  ctx1 μ1' μ K1 →
  ctx1 μ2' μ K2 →
  red (step μ1') e1 →
  red (step μ2') e2 →
  K1 = K2 ∧ e1 = e2 ∧ μ1' = μ2'.
Proof.
  intros Hes HK1 HK2 Hred1 Hred2.
  induction HK1; inv HK2; try done.
  - apply not_elem_of_app_cons_inv_l in Hes as [<- [<- <-]]; first done.
    + intros He1. apply (proj1 (Forall_forall _ _) H0) in He1.
      inv Hred1. by apply step_not_final in H1.
    + intros He2. apply (proj1 (Forall_forall _ _) H) in He2.
      inv Hred2. by apply step_not_final in H1.
  - destruct (decide (x = x0)) as [<-|].
    { by apply map_insert_inv_eq in Hes as [[= ->] [= ->]]. }
    apply map_insert_inv_ne in Hes as (Hx0 & Hx & Hαs); try done.
    apply H1 in Hx0 as [contra | Hxlex0].
    + inv Hred2. by apply step_not_final in H5.
    + apply H4 in Hx as [contra | Hx0lex].
      * inv Hred1. by apply step_not_final in H5.
      * assert (Hasym : AntiSymm eq attr_le) by apply _.
        by pose proof (Hasym _ _ Hxlex0 Hx0lex).
  - inv Hred1. inv_step.
  - inv Hred2. inv_step.
  - inv Hred1. by apply step_not_final in H1.
  - inv Hred2. by apply step_not_final in H1.
Qed.

Lemma step_det μ e d1 d2 :
  e -{μ}-> d1 →
  e -{μ}-> d2 →
  d1 = d2.
Proof.
  intros Hred1. revert d2.
  induction Hred1; intros d2 Hred2; inv Hred2; try by inv_step.
  - by apply (matches_det _ _ _ _ _ H0) in H8 as <-.
  - inv_step. by apply step_not_final in H3.
  - inv_step. destruct H. by apply no_recs_insert.
  - assert (Φ = Φ0) as <- by (by eapply bin_op_det).
    by eapply bin_op_rel_det.
  - inv_step; by apply step_not_final in H6.
  - inv_step. by apply step_not_final in Hred1.
  - inv_step. destruct H2. by apply no_recs_insert.
  - inv_step; by apply step_not_final in Hred1.
  - eapply ctx_det in H0 as (?&?&?); [|by eauto..]; naive_solver.
Qed.