diff --git a/CHANGELOG.md b/CHANGELOG.md index 09cab0b53989626127684da09a02515bd6855f3e..bde91d87d7504c3c76d81607f6e93db8a1c64f97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,8 @@ Coq development, but not every API-breaking change is listed. Changes marked lemmas is through `iInv`, and that does not change.) * Added a construction `bi_rtc` to create reflexive transitive closures of PROP-level binary relations. +* Add new introduction pattern `-# pat` that moves a hypothesis from the + intuitionistic context to the spatial context. ## Iris 3.2.0 (released 2019-08-29) diff --git a/docs/proof_mode.md b/docs/proof_mode.md index 202403f3ef62a159f87ef57e1795f9bae3056858..f87591b57ab330f777954027a7878176a04149d2 100644 --- a/docs/proof_mode.md +++ b/docs/proof_mode.md @@ -227,7 +227,16 @@ _introduction patterns_: - `[]` : false elimination. - `%` : move the hypothesis to the pure Coq context (anonymously). - `->` and `<-` : rewrite using a pure Coq equality -- `# ipat` : move the hypothesis into the intuitionistic context. +- `# ipat` : move the hypothesis into the intuitionistic context. The tactic + will fail if the hypothesis is not intuitionistic. On success, the tactic will + strip any number of intuitionistic and persistence modalities. If the + hypothesis is already in the intuitionistic context, the tactic will still + strip intuitionistic and persistence modalities (it is a no-op if the + hypothesis does not contain such modalities). +- `-# ipat` : move the hypothesis from the intuitionistic context into the + spatial context. If the hypothesis is already in the spatial context, the + tactic is a no-op. If the hypothesis is not affine, an `<affine>` modality is + added to the hypothesis. - `> ipat` : eliminate a modality (if the goal permits). Apart from this, there are the following introduction patterns that can only diff --git a/tests/proofmode.ref b/tests/proofmode.ref index 93b90699c55e66edf9d87d38478d714bf882be8d..be894c20a79851c59c2fd6be4b9de8ebab79c358 100644 --- a/tests/proofmode.ref +++ b/tests/proofmode.ref @@ -54,6 +54,29 @@ --------------------------------------□ <affine> (P ∗ Q) +"test_iDestruct_spatial" + : string +1 subgoal + + PROP : sbi + Q : PROP + ============================ + "HQ" : <affine> Q + --------------------------------------∗ + Q + +"test_iDestruct_spatial_affine" + : string +1 subgoal + + PROP : sbi + Q : PROP + Affine0 : Affine Q + ============================ + "HQ" : Q + --------------------------------------∗ + Q + The command has indeed failed with message: Ltac call to "done" failed. No applicable tactic. diff --git a/tests/proofmode.v b/tests/proofmode.v index b41edc481b33abee85bdff9663bdd11f1b8e4021..fc1e7df41d68ad0bb4bdb3f38cf5f4ca9d082344 100644 --- a/tests/proofmode.v +++ b/tests/proofmode.v @@ -90,6 +90,21 @@ Lemma test_iDestruct_intuitionistic_affine_bi `{!BiAffine PROP} P Q `{!Persisten Q ∗ (Q -∗ P) -∗ P ∗ Q. Proof. iIntros "[HQ HQP]". iDestruct ("HQP" with "HQ") as "#HP". by iFrame. Qed. +Check "test_iDestruct_spatial". +Lemma test_iDestruct_spatial Q : □ Q -∗ Q. +Proof. iIntros "#HQ". iDestruct "HQ" as "-#HQ". Show. done. Qed. + +Check "test_iDestruct_spatial_affine". +Lemma test_iDestruct_spatial_affine Q `{!Affine Q} : □ Q -∗ Q. +Proof. + iIntros "#-#HQ". + (* Since [Q] is affine, it should not add an <affine> modality *) + Show. done. +Qed. + +Lemma test_iDestruct_spatial_noop Q : Q -∗ Q. +Proof. iIntros "-#HQ". done. Qed. + Lemma test_iIntros_pure (ψ φ : Prop) P : ψ → (⌜ φ ⌠→ P → ⌜ φ ∧ ψ ⌠∧ P)%I. Proof. iIntros (??) "H". auto. Qed. diff --git a/theories/proofmode/coq_tactics.v b/theories/proofmode/coq_tactics.v index 50d3c69880b399ca0a05a319564bac9bcd051ef5..ef0c8d8c242a023923fd5e23a7d3fa064234cca8 100644 --- a/theories/proofmode/coq_tactics.v +++ b/theories/proofmode/coq_tactics.v @@ -182,6 +182,22 @@ Proof. absorbingly_sep_l wand_elim_r HQ. Qed. +Lemma tac_spatial Δ i p P P' Q : + envs_lookup i Δ = Some (p, P) → + (if p then FromAffinely P' P else TCEq P' P) → + match envs_replace i p false (Esnoc Enil i P') Δ with + | None => False + | Some Δ' => envs_entails Δ' Q + end → + envs_entails Δ Q. +Proof. + intros ? HP. destruct (envs_replace _ _ _ _ _) as [Δ'|] eqn:Hrep; last done. + rewrite envs_entails_eq=> <-. rewrite envs_replace_singleton_sound //; simpl. + destruct p; simpl; last destruct HP. + - by rewrite intuitionistically_affinely (from_affinely P' P) wand_elim_r. + - by rewrite wand_elim_r. +Qed. + (** * Implication and wand *) Lemma tac_impl_intro Δ i P P' Q R : FromImpl R P Q → diff --git a/theories/proofmode/intro_patterns.v b/theories/proofmode/intro_patterns.v index 1857119891e963c5349d74606f8d332a6b92bbc8..0227092dbdb2e55d7643f39d806b5cd3c9f869d2 100644 --- a/theories/proofmode/intro_patterns.v +++ b/theories/proofmode/intro_patterns.v @@ -10,6 +10,7 @@ Inductive intro_pat := | IList : list (list intro_pat) → intro_pat | IPure : intro_pat | IIntuitionistic : intro_pat → intro_pat + | ISpatial : intro_pat → intro_pat | IModalElim : intro_pat → intro_pat | IRewrite : direction → intro_pat | IPureIntro : intro_pat @@ -29,6 +30,7 @@ Inductive stack_item := | StBar : stack_item | StAmp : stack_item | StIntuitionistic : stack_item + | StSpatial : stack_item | StModalElim : stack_item. Notation stack := (list stack_item). @@ -63,6 +65,7 @@ Fixpoint close_conj_list (k : stack) Some (StPat (big_conj ps) :: k) | StPat pat :: k => guard (cur = None); close_conj_list k (Some pat) ps | StIntuitionistic :: k => p ↠cur; close_conj_list k (Some (IIntuitionistic p)) ps + | StSpatial :: k => p ↠cur; close_conj_list k (Some (ISpatial p)) ps | StModalElim :: k => p ↠cur; close_conj_list k (Some (IModalElim p)) ps | StAmp :: k => p ↠cur; close_conj_list k None (p :: ps) | _ => None @@ -83,6 +86,7 @@ Fixpoint parse_go (ts : list token) (k : stack) : option stack := | TParenR :: ts => close_conj_list k None [] ≫= parse_go ts | TPure :: ts => parse_go ts (StPat IPure :: k) | TIntuitionistic :: ts => parse_go ts (StIntuitionistic :: k) + | TMinus :: TIntuitionistic :: ts => parse_go ts (StSpatial :: k) | TModal :: ts => parse_go ts (StModalElim :: k) | TArrow d :: ts => parse_go ts (StPat (IRewrite d) :: k) | TPureIntro :: ts => parse_go ts (StPat IPureIntro :: k) @@ -113,6 +117,7 @@ Fixpoint close (k : stack) (ps : list intro_pat) : option (list intro_pat) := | [] => Some ps | StPat pat :: k => close k (pat :: ps) | StIntuitionistic :: k => ''(p,ps) ↠maybe2 (::) ps; close k (IIntuitionistic p :: ps) + | StSpatial :: k => ''(p,ps) ↠maybe2 (::) ps; close k (ISpatial p :: ps) | StModalElim :: k => ''(p,ps) ↠maybe2 (::) ps; close k (IModalElim p :: ps) | _ => None end. diff --git a/theories/proofmode/ltac_tactics.v b/theories/proofmode/ltac_tactics.v index bf120ccd7c8c9e5cf1804eb03b4cffd6daf57050..493d2ebbbbda70e1602ff4b408f23912b3cdf4da 100644 --- a/theories/proofmode/ltac_tactics.v +++ b/theories/proofmode/ltac_tactics.v @@ -301,6 +301,14 @@ Local Tactic Notation "iIntuitionistic" constr(H) := fail "iIntuitionistic:" P "not affine and the goal not absorbing" |pm_reduce]. +Local Tactic Notation "iSpatial" constr(H) := + eapply tac_spatial with H _ _ _; + [pm_reflexivity || + let H := pretty_ident H in + fail "iSpatial:" H "not found" + |pm_reduce; iSolveTC + |pm_reduce]. + Tactic Notation "iPure" constr(H) "as" simple_intropattern(pat) := eapply tac_pure with H _ _ _; (* (i:=H1) *) [pm_reflexivity || @@ -1381,6 +1389,7 @@ Local Ltac iDestructHypGo Hz pat := | IRewrite Right => iPure Hz as -> | IRewrite Left => iPure Hz as <- | IIntuitionistic ?pat => iIntuitionistic Hz; iDestructHypGo Hz pat + | ISpatial ?pat => iSpatial Hz; iDestructHypGo Hz pat | IModalElim ?pat => iModCore Hz; iDestructHypGo Hz pat | _ => fail "iDestruct:" pat "invalid" end.