feat: support partial and over applications at WF/PackDomain.lean

closes #1013
This commit is contained in:
Leonardo de Moura 2022-02-11 09:23:25 -08:00
parent 2390691116
commit 123e0f42e9
3 changed files with 66 additions and 22 deletions

View file

@ -57,7 +57,7 @@ private partial def mkPSigmaCasesOn (y : Expr) (codomain : Expr) (xs : Array Exp
Convert the given pre-definitions into unary functions.
We "pack" the arguments using `PSigma`.
-/
def packDomain (preDefs : Array PreDefinition) : MetaM (Array PreDefinition) := do
partial def packDomain (preDefs : Array PreDefinition) : MetaM (Array PreDefinition) := do
let mut preDefsNew := #[]
let mut arities := #[]
let mut modified := false
@ -86,16 +86,6 @@ def packDomain (preDefs : Array PreDefinition) : MetaM (Array PreDefinition) :=
preDefsNew := preDefsNew.push preDefNew
if !modified then
return preDefs
/- Return `some i` if `e` is a `preDefs[i]` application -/
let isAppOfPreDef? (e : Expr) : OptionM Nat := do
let f := e.getAppFn
guard f.isConst
preDefs.findIdx? (·.declName == f.constName!)
/- Return `some i` if `e` is a `preDefs[i]` application with `arities[i]` arguments. -/
let isTargetApp? (e : Expr) : OptionM Nat := do
let i ← isAppOfPreDef? e
guard (e.getAppNumArgs == arities[i])
return i
-- Update values
for i in [:preDefs.size] do
let preDef := preDefs[i]
@ -105,16 +95,7 @@ def packDomain (preDefs : Array PreDefinition) : MetaM (Array PreDefinition) :=
forallBoundedTelescope preDefNew.type (some 1) fun y codomain => do
let y := y[0]
let newBody ← mkPSigmaCasesOn y codomain xs body
let visit (e : Expr) : MetaM TransformStep := do
if let some idx := isTargetApp? e |>.run then
let f := e.getAppFn
let fNew := mkConst preDefsNew[idx].declName f.constLevels!
let Expr.forallE _ d .. ← inferType fNew | unreachable!
let argNew ← mkUnaryArg d e.getAppArgs
return TransformStep.done <| mkApp fNew argNew
else
return TransformStep.done e
mkLambdaFVars #[y] (← transform newBody (post := visit))
mkLambdaFVars #[y] (← packApplications newBody arities preDefsNew)
let isBad (e : Expr) : Bool :=
match isAppOfPreDef? e with
| none => false
@ -124,5 +105,57 @@ def packDomain (preDefs : Array PreDefinition) : MetaM (Array PreDefinition) :=
throwErrorAt preDef.ref "well-founded recursion cannot be used, function '{preDef.declName}' contains application of function '{preDefs[i].declName}' with #{bad.getAppNumArgs} argument(s), but function has arity {arities[i]}"
preDefsNew := preDefsNew.set! i { preDefNew with value := valueNew }
return preDefsNew
where
/-- Return `some i` if `e` is a `preDefs[i]` application -/
isAppOfPreDef? (e : Expr) : OptionM Nat := do
let f := e.getAppFn
guard f.isConst
preDefs.findIdx? (·.declName == f.constName!)
packApplications (e : Expr) (arities : Array Nat) (preDefsNew : Array PreDefinition) : MetaM Expr := do
let pack (e : Expr) (funIdx : Nat) : MetaM Expr := do
let f := e.getAppFn
let fNew := mkConst preDefsNew[funIdx].declName f.constLevels!
let Expr.forallE _ d .. ← inferType fNew | unreachable!
let argNew ← mkUnaryArg d e.getAppArgs
return mkApp fNew argNew
let rec
visit (e : Expr) : MetaM Expr := do
match e with
| Expr.lam .. => lambdaTelescope e fun xs b => do mkLambdaFVars (usedLetOnly := false) xs (← visit b)
| Expr.letE n t v b _ => withLetDecl n t (← visit v) fun x => do mkLambdaFVars (usedLetOnly := false) #[x] (← visit (b.instantiate1 x))
| Expr.forallE .. => forallTelescope e fun xs b => do mkForallFVars (usedLetOnly := false) xs (← visit b)
| Expr.proj n i s .. => return mkProj n i (← visit s)
| Expr.mdata d b _ => return mkMData d (← visit b)
| Expr.app .. => visitApp e
| Expr.const .. => visitApp e
| e => return e,
visitApp (e : Expr) : MetaM Expr := e.withApp fun f args => do
let args ← args.mapM visit
if let some funIdx := isAppOfPreDef? f then
let numArgs := args.size
let arity := arities[funIdx]
if numArgs < arity then
-- Partial application
let extra := arity - numArgs
withDefault do forallBoundedTelescope (← inferType e) extra fun xs _ => do
if xs.size != extra then
return (mkAppN f args) -- It will fail later
else
mkLambdaFVars xs (← pack (mkAppN (mkAppN f args) xs) funIdx)
else if numArgs > arity then
-- Over application
let r ← pack (mkAppN f args[:arity]) funIdx
let rType ← inferType r
-- Make sure the new auxiliary definition has only one argument.
withLetDecl (← mkFreshUserName `aux) rType r fun aux =>
mkLetFVars #[aux] (mkAppN aux args[arity:])
else
pack (mkAppN f args) funIdx
else if args.isEmpty then
return f
else
return mkAppN (← visit f) args
visit e
end Lean.Elab.WF

View file

@ -1295,6 +1295,17 @@ abbrev isDefEqGuarded (t s : Expr) : MetaM Bool :=
def isDefEqNoConstantApprox (t s : Expr) : MetaM Bool :=
approxDefEq <| isDefEq t s
/--
Eta expand the given expression.
Example:
```
etaExpand (mkConst `Nat.add)
```
produces `fun x y => Nat.add x y`
-/
def etaExpand (e : Expr) : MetaM Expr :=
withDefault do forallTelescopeReducing (← inferType e) fun xs _ => mkLambdaFVars xs (mkAppN e xs)
end Meta
builtin_initialize

View file

@ -11,4 +11,4 @@ argument #2 was not used for structural recursion
structural recursion cannot be used
well-founded recursion cannot be used, function 'g' contains application of function 'g' with #1 argument(s), but function has arity 2
'termination_by' modifier missing