Skip to content

Commit 19a5713

Browse files
committed
Implement blocking of propositional conditions
1 parent a553b89 commit 19a5713

5 files changed

Lines changed: 95 additions & 19 deletions

File tree

Lean/Egg/Core/Blocks.lean

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import Egg.Core.Source
22
import Egg.Core.Normalize
3-
import Lean
4-
open Lean
3+
4+
open Lean Meta
55

66
namespace Egg
77

88
abbrev Block := Expr
99

10-
def Block.from (expr : Expr) (cfg : Config.Normalization) : MetaM Block :=
10+
def Block.from? (expr : Expr) (cfg : Config.Normalization) : MetaM (Option Block) := do
11+
unless (← inferType expr).isProp do return none
1112
normalize expr cfg
1213

1314
abbrev Blocks := Array Block

Lean/Egg/Tactic/Basic.lean

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Egg.Tactic.Config.Option
44
import Egg.Tactic.Config.Modifier
55
import Egg.Tactic.Goal
66
import Egg.Tactic.Guides
7+
import Egg.Tactic.Blocks
78
import Egg.Tactic.Rewrite.Rules.Gen.Basic
89
import Egg.Core.Gen.Guides
910
import Egg.Tactic.Trace
@@ -41,8 +42,8 @@ open Config.Modifier (egg_cfg_mod)
4142
syntax egg_baskets := ("+" noWs ident)*
4243

4344
protected partial def eval
44-
(mod : TSyntax ``egg_cfg_mod) (args : TSyntax `egg_args)
45-
(guides : Option (TSyntax `egg_guides)) (baskets : TSyntax `Egg.egg_baskets)
45+
(mod : TSyntax ``egg_cfg_mod) (args : TSyntax `egg_args) (guides : Option (TSyntax `egg_guides))
46+
(blocks : Option (TSyntax `egg_blocks)) (baskets : TSyntax `Egg.egg_baskets)
4647
(calcifyTk? : Option Syntax := none) : TacticM Unit := do
4748
let save ← saveState
4849
try core catch err => restoreState save; throw err
@@ -57,6 +58,7 @@ where
5758
let goal ← Goal.gen goal
5859
goal.id.withContext do
5960
let guides := (← guides.mapM Guides.parseGuides).getD #[]
61+
let blocks ← getBlocks blocks cfg
6062
-- We increase the mvar context depth, so that ambient mvars aren't unified during rewrite
6163
-- generation proof and reconstruction. Note that this also means that we can't assign the
6264
-- `goal` mvar inside of this `do`-block .
@@ -67,7 +69,7 @@ where
6769
let rws := rules.entries.map (·.rw)
6870
guides := guides ++ (← genDerivedGuides goal.toCongr rws)
6971
withTraceNode `egg.guides (fun _ => return "Guides") do guides.trace `egg.guides
70-
runEqSat goal rules guides cfg
72+
runEqSat goal rules guides blocks cfg
7173
match res with
7274
| some (proof, proofTime, result) =>
7375
if cfg.reporting then
@@ -79,9 +81,9 @@ where
7981
if let some tk := calcifyTk? then calcify tk proof goal.intros.unzip.snd
8082
| none => goal.id.admit
8183
runEqSat
82-
(goal : Goal) (rules : Rewrite.Rules) (guides : Guides) (cfg : Config) :
84+
(goal : Goal) (rules : Rewrite.Rules) (guides : Guides) (blocks : Blocks) (cfg : Config) :
8385
TacticM <| Option (AbstractMVarsResult × Nat × Request.Result) := do
84-
let req ← Request.encoding goal.toCongr rules guides #[] cfg
86+
let req ← Request.encoding goal.toCongr rules guides blocks cfg
8587
withTraceNode `egg.encoded (fun _ => return "Encoded") do req.trace `egg.encoded
8688
if let .beforeEqSat := cfg.exitPoint then return none
8789
let result ← req.run cfg.explLengthLimit (onEqSatFailure cfg)
@@ -92,7 +94,7 @@ where
9294
| .proof prf =>
9395
let proofTime := (← IO.monoMsNow) - beforeProof
9496
return some (prf, proofTime, result)
95-
| .retryWithShapes => runEqSat goal rules guides { cfg with shapes := true }
97+
| .retryWithShapes => runEqSat goal rules guides blocks { cfg with shapes := true }
9698
onEqSatFailure (cfg : Config) (report : Request.Result.Report) : Request.Failure → MetaM MessageData
9799
| .backend msg? => do
98100
let mut msg := msg?
@@ -105,17 +107,21 @@ where
105107
let msg := s!"egg found an explanation exceeding the length limit ({len} vs {cfg.explLengthLimit})\nYou can increase this limit using 'set_option egg.explLengthLimit <num>'.\n"
106108
unless cfg.reporting do return msg
107109
return msg ++ formatReport cfg.flattenReports report
110+
getBlocks (stx? : Option <| TSyntax `egg_blocks) (cfg : Config) : TacticM Blocks := do
111+
let some stx := stx? | return #[]
112+
unless cfg.subgoals do logWarningAt stx "conditions are only blocked when `egg.subgoals` is `true`"
113+
Blocks.parseBlocks cfg.toNormalization stx
108114

109-
elab "egg " baskets:egg_baskets mod:egg_cfg_mod args:egg_args guides:(egg_guides)? : tactic =>
110-
Egg.eval mod args guides baskets
115+
elab "egg " baskets:egg_baskets mod:egg_cfg_mod args:egg_args guides:(egg_guides)? blocks:(egg_blocks)? : tactic =>
116+
Egg.eval mod args guides blocks baskets
111117

112118
-- WORKAROUND: This fixes `Tests/EndOfInput *`.
113119
macro "egg " baskets:egg_baskets mod:egg_cfg_mod : tactic =>
114120
`(tactic| egg $baskets $mod:egg_cfg_mod)
115121

116122
-- The syntax `egg?` calls calcify after running egg.
117-
elab tk:"egg? " baskets:egg_baskets mod:egg_cfg_mod args:egg_args guides:(egg_guides)? : tactic =>
118-
Egg.eval mod args guides baskets (calcifyTk? := tk)
123+
elab tk:"egg? " baskets:egg_baskets mod:egg_cfg_mod args:egg_args guides:(egg_guides)? blocks:(egg_blocks)? : tactic =>
124+
Egg.eval mod args guides blocks baskets (calcifyTk? := tk)
119125

120126
-- WORKAROUND: This fixes a problem analogous to `Tests/EndOfInput *` for `egg?`.
121127
macro "egg? " baskets:egg_baskets mod:egg_cfg_mod : tactic =>

Lean/Egg/Tactic/Blocks.lean

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import Egg.Core.Blocks
2+
import Lean
3+
open Lean Elab Tactic
4+
5+
namespace Egg.Blocks
6+
7+
declare_syntax_cat egg_blocks
8+
syntax " blocking " (term,*) : egg_blocks
9+
10+
def parseBlocks (cfg : Config.Normalization) : TSyntax `egg_blocks → TacticM Blocks
11+
| `(egg_blocks|blocking $bs,*) => do
12+
let mut blocks : Blocks := #[]
13+
for b in bs.getElems do
14+
let e ← Tactic.elabTerm b none
15+
-- See `parseGuides` for an explanation of this.
16+
if e.hasSorry then continue
17+
let some block ← Block.from? e cfg | throwErrorAt b "blocking terms must be propositions"
18+
blocks := blocks.push block
19+
return blocks
20+
| _ => unreachable!

Lean/Egg/Tactic/Calc.lean

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ syntax egg_calc_step := ppIndent(colGe term (&" with " egg_cfg_mod egg_args)? (e
2323

2424
structure Step where
2525
mod : TSyntax ``egg_cfg_mod
26-
args : TSyntax `egg_args
26+
args : TSyntax `egg_args
2727
guides : Option (TSyntax `egg_guides)
2828
deriving Inhabited
2929

@@ -65,11 +65,11 @@ def parseRawSteps : (TSyntax ``egg_calc_steps) → TacticM RawSteps
6565
$tail]*) => return { head := ← parseRawStep head, tail := ← tail.mapM parseRawStep }
6666
| _ => throwUnsupportedSyntax
6767

68-
syntax &"egg " egg_baskets &" calc " egg_args egg_calc_steps : tactic
68+
syntax &"egg " egg_baskets &" calc " egg_args egg_calc_steps (egg_blocks)? : tactic
6969

7070
def eval
7171
(baskets : TSyntax `Egg.egg_baskets) (args : TSyntax `egg_args)
72-
(steps : TSyntax ``egg_calc_steps) : TacticM Unit := do
72+
(steps : TSyntax ``egg_calc_steps) (blocks : Option <| TSyntax `egg_blocks) : TacticM Unit := do
7373
withMainContext do
7474
let rawSteps ← parseRawSteps steps
7575
let goalType ← getMainTarget
@@ -112,7 +112,7 @@ where
112112
throwError "'egg calc' failed to infer goal type"
113113
stepToEgg (step : Step) : TacticM (TSyntax `tactic) := do
114114
let allPrems ← appendArgs step.args args
115-
`(tactic| egg $baskets $step.mod:egg_cfg_mod $allPrems $[$step.guides]?)
115+
`(tactic| egg $baskets $step.mod:egg_cfg_mod $allPrems $[$step.guides]? $[$blocks]?)
116116
dedupSubgoals (subgoals : List MVarId) : MetaM (List MVarId) := do
117117
let mut result := []
118118
for subgoal in subgoals do
@@ -164,5 +164,5 @@ where
164164
lastRhs := rhs
165165
return result
166166

167-
elab "egg " baskets:egg_baskets " calc " args:egg_args steps:egg_calc_steps : tactic =>
168-
eval baskets args steps
167+
elab "egg " baskets:egg_baskets " calc " args:egg_args steps:egg_calc_steps blocks:(egg_blocks)?: tactic =>
168+
eval baskets args steps blocks

Lean/Egg/Tests/Blocking.lean

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import Egg
2+
3+
/-- warning: conditions are only blocked when `egg.subgoals` is `true` -/
4+
#guard_msgs in
5+
example : True := by
6+
egg blocking True
7+
8+
/-- error: blocking terms must be propositions -/
9+
#guard_msgs in
10+
example : True := by
11+
egg blocking 0
12+
13+
set_option egg.subgoals true
14+
15+
example : True := by
16+
egg blocking True
17+
18+
variable {P : Prop}
19+
20+
/--
21+
error: unsolved goals
22+
P : Prop
23+
h : P → False
24+
⊢ P
25+
-/
26+
#guard_msgs in
27+
example (h : P → False) : False := by
28+
egg [h]
29+
30+
/-- error: egg failed to prove the goal (saturated) -/
31+
#guard_msgs in
32+
example (h : P → False) : False := by
33+
egg [h] blocking P
34+
35+
/--
36+
error: unsolved goals
37+
P Q : Prop
38+
hq : Q
39+
h₁ : P → False
40+
h₂ : Q → False
41+
⊢ P
42+
-/
43+
#guard_msgs in
44+
example {Q : Prop} (hq : Q) (h₁ : P → False) (h₂ : Q → False) : False := by
45+
egg [h₁, h₂]
46+
47+
example {Q : Prop} (hq : Q) (_h₁ : P → False) (h₂ : Q → False) : False := by
48+
egg [_h₁, h₂] blocking P
49+
exact hq

0 commit comments

Comments
 (0)