From 6fc9c27e74bc89239e3191ccca6b1d3f77e2fe44 Mon Sep 17 00:00:00 2001 From: Robbert Krebbers <mail@robbertkrebbers.nl> Date: Sun, 12 Mar 2017 22:56:25 +0100 Subject: [PATCH] Define `fill` in terms of a `foldl` over `fill_item`. This has some advantages: - Evaluation contexts behave like a proper "Huet's zipper", and thus: + We no longer need to reverse the list of evaluation context items in the `reshape_expr` tactic. + The `fill` function becomes tail-recursive. - It gives rise to more definitional equalities in simulation proofs using binary logical relations proofs. In the case of binary logical relations, we simulate an expressions in some ambient context, i.e. `fill K e`. Now, whenever we reshape `e` by turning it into `fill K' e'`, we end up with `fill K (fill K' e')`. In order to use the rules for the expression that is being simulated, we need to turn `fill K (fill K' e')` into `fill K'' e'` for some `K'`. In case of the old `foldr`-based approach, we had to rewrite using the lemma `fill_app` to achieve that. However, in case of the old `foldl`-based `fill`, we have that `fill K (fill K' e')` is definitionally equal to `fill (K' ++ K) e'` provided that `K'` consists of a bunch of `cons`es (which is always the case, since we obtained `K'` by reshaping `e`). Note that this change hardly affected `heap_lang`. Only the proof of `atomic_correct` broke. I fixed this by proving a more general lemma `ectxi_language_atomic` about `ectxi`-languages, which should have been there in the first place. --- theories/heap_lang/tactics.v | 24 +++++------ theories/program_logic/ectxi_language.v | 55 ++++++++++++++++--------- 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/theories/heap_lang/tactics.v b/theories/heap_lang/tactics.v index 92765fd94..c59a73fdf 100644 --- a/theories/heap_lang/tactics.v +++ b/theories/heap_lang/tactics.v @@ -185,19 +185,15 @@ Definition atomic (e : expr) := end. Lemma atomic_correct e : atomic e → language.atomic (to_expr e). Proof. - intros He. apply ectx_language_atomic. - - intros σ e' σ' ef. - intros H; apply language.val_irreducible; revert H. - destruct e; simpl; try done; repeat (case_match; try done); - inversion 1; rewrite ?to_of_val; eauto. subst. - unfold subst'; repeat (case_match || contradiction || simplify_eq/=); eauto. - - intros [|Ki K] e' Hfill Hnotval; [done|exfalso]. - apply (fill_not_val K), eq_None_not_Some in Hnotval. apply Hnotval. simpl. - revert He. destruct e; simpl; try done; repeat (case_match; try done); - rewrite ?bool_decide_spec; - destruct Ki; inversion Hfill; subst; clear Hfill; - try naive_solver eauto using to_val_is_Some. - move=> _ /=; destruct decide as [|Nclosed]; [by eauto | by destruct Nclosed]. + intros He. apply ectxi_language_atomic. + - intros σ e' σ' ef Hstep; simpl in *. + apply language.val_irreducible; revert Hstep. + destruct e=> //=; repeat (simplify_eq/=; case_match=>//); + inversion 1; simplify_eq/=; rewrite ?to_of_val; eauto. + unfold subst'; repeat (simplify_eq/=; case_match=>//); eauto. + - intros Ki e' Hfill []%eq_None_not_Some; simpl in *. + destruct e=> //; destruct Ki; repeat (simplify_eq/=; case_match=>//); + naive_solver eauto using to_val_is_Some. Qed. End W. @@ -264,7 +260,7 @@ Ltac reshape_val e tac := Ltac reshape_expr e tac := let rec go K e := match e with - | _ => tac (reverse K) e + | _ => tac K e | App ?e1 ?e2 => reshape_val e1 ltac:(fun v1 => go (AppRCtx v1 :: K) e2) | App ?e1 ?e2 => go (AppLCtx e2 :: K) e1 | UnOp ?op ?e => go (UnOpCtx op :: K) e diff --git a/theories/program_logic/ectxi_language.v b/theories/program_logic/ectxi_language.v index ee9851867..82c0e4ab0 100644 --- a/theories/program_logic/ectxi_language.v +++ b/theories/program_logic/ectxi_language.v @@ -45,17 +45,18 @@ Section ectxi_language. Implicit Types (e : expr) (Ki : ectx_item). Notation ectx := (list ectx_item). - Definition fill (K : ectx) (e : expr) : expr := fold_right fill_item e K. + Definition fill (K : ectx) (e : expr) : expr := foldl (flip fill_item) e K. - Lemma fill_app (K1 K2 : ectx) e : fill (K1 ++ K2) e = fill K1 (fill K2 e). - Proof. apply fold_right_app. Qed. + Lemma fill_app (K1 K2 : ectx) e : fill (K1 ++ K2) e = fill K2 (fill K1 e). + Proof. apply foldl_app. Qed. Instance fill_inj K : Inj (=) (=) (fill K). - Proof. red; induction K as [|Ki K IH]; naive_solver. Qed. + Proof. induction K as [|Ki K IH]; rewrite /Inj; naive_solver. Qed. Lemma fill_val K e : is_Some (to_val (fill K e)) → is_Some (to_val e). Proof. - induction K; simpl; first done. intros ?%fill_item_val. eauto. + revert e. + induction K as [|Ki K IH]=> e //=. by intros ?%IH%fill_item_val. Qed. Lemma fill_not_val K e : to_val e = None → to_val (fill K e) = None. @@ -66,23 +67,39 @@ Section ectxi_language. other words, [e] also contains the reducible expression *) Lemma step_by_val K K' e1 e1' σ1 e2 σ2 efs : fill K e1 = fill K' e1' → to_val e1 = None → head_step e1' σ1 e2 σ2 efs → - exists K'', K' = K ++ K''. (* K `prefix_of` K' *) + exists K'', K' = K'' ++ K. (* K `prefix_of` K' *) Proof. - intros Hfill Hred Hnval; revert K' Hfill. - induction K as [|Ki K IH]; simpl; intros K' Hfill; first by eauto. - destruct K' as [|Ki' K']; simplify_eq/=. - { exfalso; apply (eq_None_not_Some (to_val (fill K e1))); - eauto using fill_not_val, head_ctx_step_val. } - cut (Ki = Ki'); [naive_solver eauto using prefix_of_cons|]. - eauto using fill_item_no_val_inj, val_stuck, fill_not_val. + intros Hfill Hred Hstep; revert K' Hfill. + induction K as [|Ki K IH] using rev_ind=> /= K' Hfill; eauto using app_nil_r. + destruct K' as [|Ki' K' _] using @rev_ind; simplify_eq/=. + { rewrite fill_app in Hstep. + exfalso; apply (eq_None_not_Some (to_val (fill K e1))); + eauto using fill_not_val, head_ctx_step_val. } + rewrite !fill_app /= in Hfill. + assert (Ki = Ki') as -> + by eauto using fill_item_no_val_inj, val_stuck, fill_not_val. + simplify_eq. destruct (IH K') as [K'' ->]; auto. + exists K''. by rewrite assoc. Qed. - Global Program Instance : EctxLanguage expr val ectx state := - (* For some reason, Coq always rejects the record syntax claiming I - fixed fields of different records, even when I did not. *) - Build_EctxLanguage expr val ectx state of_val to_val [] (++) fill head_step _ _ _ _ _ _ _ _ _. - Solve Obligations with eauto using to_of_val, of_to_val, val_stuck, - fill_not_val, fill_app, step_by_val, fold_right_app, app_eq_nil. + Global Program Instance ectxi_lang_ectx : EctxLanguage expr val ectx state := {| + ectx_language.of_val := of_val; ectx_language.to_val := to_val; + empty_ectx := []; comp_ectx := flip (++); ectx_language.fill := fill; + ectx_language.head_step := head_step |}. + Solve Obligations with simpl; eauto using to_of_val, of_to_val, val_stuck, + fill_not_val, fill_app, step_by_val, foldl_app. + Next Obligation. intros K1 K2 ?%app_eq_nil; tauto. Qed. + + Lemma ectxi_language_atomic e : + (∀ σ e' σ' efs, head_step e σ e' σ' efs → irreducible e' σ') → + (∀ Ki e', e = fill_item Ki e' → to_val e' = None → False) → + atomic e. + Proof. + intros Hastep Hafill. apply ectx_language_atomic=> //= {Hastep} K e'. + destruct K as [|Ki K IH] using @rev_ind=> //=. + rewrite fill_app /= => He Hnval. + destruct (Hafill Ki (fill K e')); auto using fill_not_val. + Qed. Global Instance ectxi_lang_ctx_item Ki : LanguageCtx (ectx_lang expr) (fill_item Ki). -- GitLab