Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: split at h when h has forward dependencies #4211

Merged
merged 1 commit into from
May 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/Lean/Meta/Basic.lean
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,17 @@ context. Fails if the given expression is not a fvar or if no such declaration e
def getFVarLocalDecl (fvar : Expr) : MetaM LocalDecl :=
fvar.fvarId!.getDecl

/--
Returns `true` if another local declaration in the local context depends on `fvarId`.
-/
def _root_.Lean.FVarId.hasForwardDeps (fvarId : FVarId) : MetaM Bool := do
let decl ← fvarId.getDecl
(← getLCtx).foldlM (init := false) (start := decl.index + 1) fun found other =>
if found then
return true
else
localDeclDependsOn other fvarId

/--
Given a user-facing name for a free variable, return its declaration in the current local context.
Throw an exception if free variable is not declared.
Expand Down
13 changes: 10 additions & 3 deletions src/Lean/Meta/Tactic/Split.lean
Original file line number Diff line number Diff line change
Expand Up @@ -320,10 +320,17 @@ def splitLocalDecl? (mvarId : MVarId) (fvarId : FVarId) : MetaM (Option (List MV
if e.isIte || e.isDIte then
return (← splitIfLocalDecl? mvarId fvarId).map fun (mvarId₁, mvarId₂) => [mvarId₁, mvarId₂]
else
let (fvarIds, mvarId) ← mvarId.revert #[fvarId]
let num := fvarIds.size
let mut mvarId := mvarId
let localDecl ← fvarId.getDecl
if (← pure localDecl.isLet <||> exprDependsOn (← mvarId.getType) fvarId <||> fvarId.hasForwardDeps) then
-- If `fvarId` has dependencies or is a let-decl, we create a copy.
mvarId ← mvarId.assert localDecl.userName localDecl.type localDecl.toExpr
else
let (fvarIds, mvarId') ← mvarId.revert #[fvarId]
assert! fvarIds.size == 1 -- fvarId does not have forward dependencies
mvarId := mvarId'
let mvarIds ← splitMatch mvarId e
let mvarIds ← mvarIds.mapM fun mvarId => return (← mvarId.introNP num).2
let mvarIds ← mvarIds.mapM fun mvarId => return (← mvarId.intro1P).2
return some mvarIds
else
return none
Expand Down
76 changes: 76 additions & 0 deletions tests/lean/run/3731.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import Lean.Data.HashMap
open Lean

/--
A circuit node declaration. These are not recursive but instead contain indices into an `Env`.
-/
inductive Decl where
/--
A node with a constant output value.
-/
| const (b : Bool)
/--
An input node to the circuit.
-/
| atom (idx : Nat)
/--
An AIG gate with configurable input nodes and polarity. `l` and `r` are the
input node indices while `linv` and `rinv` say whether there is an inverter on
the left or right input.
-/
| gate (l r : Nat) (linv rinv : Bool)
deriving BEq, Hashable, DecidableEq

/--
A cache that is valid with respect to some `Array Decl`.
-/
def Cache (_decls : Array Decl) := HashMap Decl Nat

/--
Lookup a `decl` in a `cache`.

If this returns `some i`, `Cache.find?_poperty` can be used to demonstrate: `decls[i] = decl`.
-/
@[irreducible]
def Cache.find? (cache : Cache decls) (decl : Decl) : Option Nat :=
match cache.val.find? decl with
| some hit =>
if h1:hit < decls.size then
if decls[hit]'h1 = decl then
some hit
else
none
else
none
| none => none

/--
This states that all indices, found in a `Cache` that is valid with respect to some `decls`,
are within bounds of `decls`.
-/
theorem Cache.find?_bounds {decls : Array Decl} {idx : Nat} (c : Cache decls) (decl : Decl)
(h : c.find? decl = some idx) : idx < decls.size := by
unfold find? at h
split at h
. split at h
. split at h
. injection h
omega
. contradiction
. contradiction
. contradiction

/--
This states that if `Cache.find? decl` returns `some i`, `decls[i] = decl`, holds.
-/
theorem Cache.find?_property {decls : Array Decl} {idx : Nat} (c : Cache decls) (decl : Decl)
(h : c.find? decl = some idx) : decls[idx]'(Cache.find?_bounds c decl h) = decl := by
unfold find? at h
split at h
. split at h
. split at h
. injection h
subst idx; assumption
. contradiction
. contradiction
. contradiction
Loading