Skip to content

Commit 5ddc835

Browse files
committed
Let-bind minors to reduce produced code size
1 parent da4883c commit 5ddc835

File tree

1 file changed

+37
-26
lines changed

1 file changed

+37
-26
lines changed

src/Lean/Meta/Tactic/FunInd.lean

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,17 @@ def stripPProdProjs (e : Expr) : Expr :=
844844
| .proj ``And _ e' => stripPProdProjs e'
845845
| e => e
846846

847+
def withLetDecls {α} (name : Name) (ts : Array Expr) (es : Array Expr) (k : Array Expr → MetaM α) : MetaM α := do
848+
assert! es.size = ts.size
849+
go 0 #[]
850+
where
851+
go (i : Nat) (acc : Array Expr) := do
852+
if h : i < es.size then
853+
let n := if es.size = 1 then name else name.appendIndexAfter (i + 1)
854+
withLetDecl n ts[i]! es[i] fun x => go (i+1) (acc.push x)
855+
else
856+
k acc
857+
847858
/--
848859
Given a recursive definition `foo` defined via structural recursion, derive `foo.mutual_induct`,
849860
if needed, and `foo.induct` for all functions in the group.
@@ -977,32 +988,32 @@ def deriveInductionStructural (names : Array Name) (numFixed : Nat) : MetaM Unit
977988
trace[Meta.FunInd] "processed minors: {minors'}"
978989

979990
-- Now assemble the mutual_induct theorem
980-
-- Plenty of code duplication here (packed Motive, minors', brecOn applications)!
981-
982-
let mut brecOnApps := #[]
983-
for info in infos, recArgInfo in recArgInfos, idx in [:infos.size] do
984-
-- Take care to pick the `ys` from the type, to get the variable names expected
985-
-- by the user, but use the value arity
986-
let arity ← lambdaTelescope (← instantiateLambda info.value xs) fun ys _ => pure ys.size
987-
let eforallBoundedTelescope (← instantiateForall info.type xs) arity fun ys _ => do
988-
let (indicesMajor, rest) := recArgInfo.pickIndicesMajor ys
989-
-- Find where in the function packing we are (TODO: abstract out)
990-
let some indIdx := positions.findIdx? (·.contains idx) | panic! "invalid positions"
991-
let some pos := positions.find? (·.contains idx) | panic! "invalid positions"
992-
let some packIdx := pos.findIdx? (· == idx) | panic! "invalid positions"
993-
-- TODO: Always use binduction?
994-
let e := group.brecOn true lvl indIdx
995-
let e := mkAppN e packedMotives
996-
let e := mkAppN e indicesMajor
997-
let e := mkAppN e minors'
998-
let e ← if pos.size = 1 then pure e else Structural.mkPProdProjN packIdx e
999-
let e := mkAppN e rest
1000-
let e ← mkLambdaFVars ys e
1001-
trace[Meta.FunInd] "assembled call for {info.name}: {e}"
1002-
pure e
1003-
brecOnApps := brecOnApps.push e
1004-
let e' ← mkAndIntroN brecOnApps
1005-
let e' ← abstractIndependentMVars mvars (← motives.back.fvarId!.getDecl).index e'
991+
-- Let-bind the transformed minors to avoid code duplication of possibly very large
992+
-- terms when we have mutual induction.
993+
let e' ← withLetDecls `minor minorTypes minors' fun minors' => do
994+
let mut brecOnApps := #[]
995+
for info in infos, recArgInfo in recArgInfos, idx in [:infos.size] do
996+
-- Take care to pick the `ys` from the type, to get the variable names expected
997+
-- by the user, but use the value arity
998+
let aritylambdaTelescope (← instantiateLambda info.value xs) fun ys _ => pure ys.size
999+
let e ← forallBoundedTelescope (← instantiateForall info.type xs) arity fun ys _ => do
1000+
let (indicesMajor, rest) := recArgInfo.pickIndicesMajor ys
1001+
-- Find where in the function packing we are (TODO: abstract out)
1002+
let some indIdx := positions.findIdx? (·.contains idx) | panic! "invalid positions"
1003+
let some pos := positions.find? (·.contains idx) | panic! "invalid positions"
1004+
let some packIdx := pos.findIdx? (· == idx) | panic! "invalid positions"
1005+
let e := group.brecOn true lvl indIdx -- unconditionally using binduction here
1006+
let e := mkAppN e packedMotives
1007+
let e := mkAppN e indicesMajor
1008+
let e := mkAppN e minors'
1009+
let e ← if pos.size = 1 then pure e else Structural.mkPProdProjN packIdx e
1010+
let e := mkAppN e rest
1011+
let e ← mkLambdaFVars ys e
1012+
trace[Meta.FunInd] "assembled call for {info.name}: {e}"
1013+
pure e
1014+
brecOnApps := brecOnApps.push e
1015+
mkLetFVars minors' (← mkAndIntroN brecOnApps)
1016+
let e' ← abstractIndependentMVars mvars (← motives.back.fvarId!.getDecl).index e'
10061017
let e' ← mkLambdaFVars motives e'
10071018

10081019
-- We could pass (usedOnly := true) below, and get nicer induction principles that

0 commit comments

Comments
 (0)