@@ -844,6 +844,17 @@ def stripPProdProjs (e : Expr) : Expr :=
844
844
| .proj ``And _ e' => stripPProdProjs e'
845
845
| e => e
846
846
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
+
847
858
/--
848
859
Given a recursive definition `foo` defined via structural recursion, derive `foo.mutual_induct`,
849
860
if needed, and `foo.induct` for all functions in the group.
@@ -977,32 +988,32 @@ def deriveInductionStructural (names : Array Name) (numFixed : Nat) : MetaM Unit
977
988
trace[Meta.FunInd] "processed minors: {minors'}"
978
989
979
990
-- 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 e ← forallBoundedTelescope (← 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 arity ← lambdaTelescope (← 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'
1006
1017
let e' ← mkLambdaFVars motives e'
1007
1018
1008
1019
-- We could pass (usedOnly := true) below, and get nicer induction principles that
0 commit comments