feat: use nondep flag in Expr.letE and LocalContext.ldecl (#8804)
This PR implements first-class support for nondependent let expressions in the elaborator; recall that a let expression `let x : t := v; b` is called *nondependent* if `fun x : t => b` typechecks, and the notation for a nondependent let expression is `have x := v; b`. Previously we encoded `have` using the `letFun` function, but now we make use of the `nondep` flag in the `Expr.letE` constructor for the encoding. This has been given full support throughout the metaprogramming interface and the elaborator. Key changes to the metaprogramming interface: - Local context `ldecl`s with `nondep := true` are generally treated as `cdecl`s. This is because in the body of a `have` expression the variable is opaque. Functions like `LocalDecl.isLet` by default return `false` for nondependent `ldecl`s. In the rare case where it is needed, they take an additional optional `allowNondep : Bool` flag (defaults to `false`) if the variable is being processed in a context where the value is relevant. - Functions such as `mkLetFVars` by default generalize nondependent let variables and create lambda expressions for them. The `generalizeNondepLet` flag (default true) can be set to false if `have` expressions should be produced instead. **Breaking change:** Uses of `letLambdaTelescope`/`mkLetFVars` need to use `generalizeNondepLet := false`. See the next item. - There are now some mapping functions to make telescoping operations more convenient. See `mapLetTelescope` and `mapLambdaLetTelescope`. There is also `mapLetDecl` as a counterpart to `withLetDecl` for creating `let`/`have` expressions. - Important note about the `generalizeNondepLet` flag: it should only be used for variables in a local context that the metaprogram "owns". Since nondependent let variables are treated as constants in most cases, the `value` field might refer to variables that do not exist, if for example those variables were cleared or reverted. Using `mapLetDecl` is always fine. - The simplifier will cache its let dependence calculations in the nondep field of let expressions. - The `intro` tactic still produces *dependent* local variables. Given that the simplifier will transform lets into haves, it would be surprising if that would prevent `intro` from creating a local variable whose value cannot be used. Note that nondependence of lets is not checked by the kernel. To external checker authors: If the elaborator gets the nondep flag wrong, we consider this to be an elaborator error. Feel free to typecheck `letE n t v b true` as if it were `app (lam n t b default) v` and please report issues. This PR follows up from #8751, which made sure the nondep flag was preserved in the C++ interface.
This commit is contained in:
parent
2ebc001dd1
commit
02c8c2f9e1
40 changed files with 647 additions and 293 deletions
|
|
@ -785,54 +785,33 @@ def elabLetDeclAux (id : Syntax) (binders : Array Syntax) (typeStx : Syntax) (va
|
|||
pure (type, val, binders)
|
||||
let kind := kindOfBinderName id.getId
|
||||
trace[Elab.let.decl] "{id.getId} : {type} := {val}"
|
||||
let elabBody : TermElabM Expr := do
|
||||
let body ← elabTermEnsuringType body expectedType?
|
||||
instantiateMVars body
|
||||
let result ←
|
||||
if config.zeta then
|
||||
let elabZetaCore (x : Expr) : TermElabM Expr := do
|
||||
addLocalVarInfo id x
|
||||
if let some h := config.eq? then
|
||||
let hTy ← mkEq x val
|
||||
withLocalDeclD h.getId hTy fun h' => do
|
||||
addLocalVarInfo h h'
|
||||
let body ← elabBody
|
||||
pure <| (← body.abstractM #[x, h']).instantiateRev #[val, ← mkEqRefl val]
|
||||
else
|
||||
let body ← elabBody
|
||||
withLetDecl id.getId (kind := kind) type val (nondep := config.nondep) fun x => do
|
||||
let elabBody : TermElabM Expr :=
|
||||
elabTermEnsuringType body expectedType? >>= instantiateMVars
|
||||
addLocalVarInfo id x
|
||||
match config.eq? with
|
||||
| none =>
|
||||
let body ← elabBody
|
||||
if config.zeta then
|
||||
pure <| (← body.abstractM #[x]).instantiate1 val
|
||||
if !config.nondep then
|
||||
withLetDecl id.getId (kind := kind) type val elabZetaCore
|
||||
else
|
||||
withLocalDecl id.getId (kind := kind) .default type elabZetaCore
|
||||
else
|
||||
if !config.nondep then
|
||||
withLetDecl id.getId (kind := kind) type val fun x => do
|
||||
addLocalVarInfo id x
|
||||
if let some h := config.eq? then
|
||||
let hTy ← mkEq x val
|
||||
withLocalDeclD h.getId hTy fun h' => do
|
||||
addLocalVarInfo h h'
|
||||
let body ← elabBody
|
||||
let body := (← body.abstractM #[h']).instantiate1 (← mkEqRefl x)
|
||||
mkLetFVars #[x] body (usedLetOnly := config.usedOnly)
|
||||
else
|
||||
mkLetFVars #[x] body (usedLetOnly := config.usedOnly) (generalizeNondepLet := false)
|
||||
| some h =>
|
||||
let hTy ← mkEq x val
|
||||
withLetDecl h.getId hTy (← mkEqRefl x) (nondep := true) fun h' => do
|
||||
addLocalVarInfo h h'
|
||||
let body ← elabBody
|
||||
if config.zeta then
|
||||
pure <| (← body.abstractM #[x, h']).instantiateRev #[val, ← mkEqRefl val]
|
||||
else if config.nondep then
|
||||
-- TODO(kmill): Think more about how to encode this case.
|
||||
-- Currently we produce `(fun (x : α) (h : x = val) => b) val rfl`.
|
||||
-- N.B. the nondep lets become lambdas here.
|
||||
let f ← mkLambdaFVars #[x, h'] body
|
||||
return mkApp2 f val (← mkEqRefl val)
|
||||
else
|
||||
let body ← elabBody
|
||||
mkLetFVars #[x] body (usedLetOnly := config.usedOnly)
|
||||
else
|
||||
withLocalDecl id.getId (kind := kind) .default type fun x => do
|
||||
addLocalVarInfo id x
|
||||
if let some h := config.eq? then
|
||||
-- TODO(kmill): Think about how to encode this case.
|
||||
let hTy ← mkEq x val
|
||||
withLocalDeclD h.getId hTy fun h' => do
|
||||
addLocalVarInfo h h'
|
||||
let body ← elabBody
|
||||
let f ← mkLambdaFVars #[x, h'] body
|
||||
return mkApp2 f val (← mkEqRefl val)
|
||||
else
|
||||
let body ← elabBody
|
||||
mkLetFun x val body
|
||||
mkLetFVars #[x, h'] body (usedLetOnly := config.usedOnly) (generalizeNondepLet := false)
|
||||
if config.postponeValue then
|
||||
forallBoundedTelescope type binders.size fun xs type => do
|
||||
-- the original `fvars` from above are gone, so add back info manually
|
||||
|
|
|
|||
|
|
@ -670,7 +670,7 @@ where
|
|||
match p with
|
||||
| .forallE n d b bi => withLocalDecl n bi (← go d) fun x => do mkForallFVars #[x] (← go (b.instantiate1 x))
|
||||
| .lam n d b bi => withLocalDecl n bi (← go d) fun x => do mkLambdaFVars #[x] (← go (b.instantiate1 x))
|
||||
| .letE n t v b .. => withLetDecl n (← go t) (← go v) fun x => do mkLetFVars #[x] (← go (b.instantiate1 x))
|
||||
| .letE n t v b nondep => mapLetDecl n (← go t) (← go v) (nondep := nondep) fun x => go (b.instantiate1 x)
|
||||
| .app f a => return mkApp (← go f) (← go a)
|
||||
| .proj _ _ b => return p.updateProj! (← go b)
|
||||
| .mdata k b =>
|
||||
|
|
|
|||
|
|
@ -869,10 +869,11 @@ private partial def mkClosureForAux (toProcess : Array FVarId) : StateRefT Closu
|
|||
| .cdecl _ _ userName type bi k =>
|
||||
let toProcess ← pushLocalDecl toProcess fvarId userName type bi k
|
||||
mkClosureForAux toProcess
|
||||
| .ldecl _ _ userName type val _ k =>
|
||||
| .ldecl _ _ userName type val nondep k =>
|
||||
let zetaDeltaFVarIds ← getZetaDeltaFVarIds
|
||||
if !zetaDeltaFVarIds.contains fvarId then
|
||||
/- Non-dependent let-decl. See comment at src/Lean/Meta/Closure.lean -/
|
||||
-- Note: If `nondep` is true then `zetaDeltaFVarIds.contains fvarId` must be false.
|
||||
if nondep || !zetaDeltaFVarIds.contains fvarId then
|
||||
/- Nondependent let-decl. See comment at src/Lean/Meta/Closure.lean -/
|
||||
let toProcess ← pushLocalDecl toProcess fvarId userName type .default k
|
||||
mkClosureForAux toProcess
|
||||
else
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ private partial def ensureNoUnassignedLevelMVarsAtPreDef (preDef : PreDefinition
|
|||
checkCache { val := e : ExprStructEq } fun _ => do
|
||||
match e with
|
||||
| .forallE n d b c | .lam n d b c => withExpr e do visit d; withLocalDecl n c d fun x => visit (b.instantiate1 x)
|
||||
| .letE n t v b _ => withExpr e do visit t; visit v; withLetDecl n t v fun x => visit (b.instantiate1 x)
|
||||
| .letE n t v b nondep => withExpr e do visit t; visit v; withLetDecl n t v (nondep := nondep) fun x => visit (b.instantiate1 x)
|
||||
| .mdata _ b => withExpr e do visit b
|
||||
| .proj _ _ b => withExpr e do visit b
|
||||
| .sort u => visitLevel u (← read)
|
||||
|
|
|
|||
|
|
@ -133,9 +133,9 @@ private partial def replaceRecApps (recArgInfos : Array RecArgInfo) (positions :
|
|||
| Expr.forallE n d b c =>
|
||||
withLocalDecl n c (← loop below d) fun x => do
|
||||
mkForallFVars #[x] (← loop below (b.instantiate1 x))
|
||||
| Expr.letE n type val body _ =>
|
||||
withLetDecl n (← loop below type) (← loop below val) fun x => do
|
||||
mkLetFVars #[x] (← loop below (body.instantiate1 x)) (usedLetOnly := false)
|
||||
| Expr.letE n type val body nondep =>
|
||||
mapLetDecl n (← loop below type) (← loop below val) (nondep := nondep) (usedLetOnly := false) fun x => do
|
||||
loop below (body.instantiate1 x)
|
||||
| Expr.mdata d b =>
|
||||
if let some stx := getRecAppSyntax? e then
|
||||
withRef stx <| loop below b
|
||||
|
|
|
|||
|
|
@ -50,9 +50,9 @@ private partial def replaceIndPredRecApps (recArgInfo : RecArgInfo) (funType : E
|
|||
| Expr.forallE n d b c =>
|
||||
withLocalDecl n c (← loop d) fun x => do
|
||||
mkForallFVars #[x] (← loop (b.instantiate1 x))
|
||||
| Expr.letE n type val body _ =>
|
||||
withLetDecl n (← loop type) (← loop val) fun x => do
|
||||
mkLetFVars #[x] (← loop (body.instantiate1 x))
|
||||
| Expr.letE n type val body nondep =>
|
||||
mapLetDecl n (← loop type) (← loop val) (nondep := nondep) fun x => do
|
||||
loop (body.instantiate1 x)
|
||||
| Expr.mdata d b => do
|
||||
if let some stx := getRecAppSyntax? e then
|
||||
withRef stx <| loop b
|
||||
|
|
|
|||
|
|
@ -32,8 +32,9 @@ where
|
|||
match e with
|
||||
| Expr.lam .. => lambdaTelescope e fun xs b => do mkLambdaFVars xs (← visit b)
|
||||
| Expr.forallE .. => forallTelescope e fun xs b => do mkForallFVars xs (← visit b)
|
||||
| Expr.letE n type val body _ =>
|
||||
withLetDecl n type (← visit val) fun x => do mkLetFVars #[x] (← visit (body.instantiate1 x))
|
||||
| Expr.letE n type val body nondep =>
|
||||
mapLetDecl n type (← visit val) (nondep := nondep) fun x => do
|
||||
visit (body.instantiate1 x)
|
||||
| Expr.mdata d b => return mkMData d (← visit b)
|
||||
| Expr.proj n i s => return mkProj n i (← visit s)
|
||||
| Expr.app .. =>
|
||||
|
|
|
|||
|
|
@ -84,9 +84,9 @@ where
|
|||
| Expr.forallE n d b c =>
|
||||
withLocalDecl n c (← loop F d) fun x => do
|
||||
mkForallFVars #[x] (← loop F (b.instantiate1 x))
|
||||
| Expr.letE n type val body _ =>
|
||||
withLetDecl n (← loop F type) (← loop F val) fun x => do
|
||||
mkLetFVars #[x] (← loop F (body.instantiate1 x)) (usedLetOnly := false)
|
||||
| Expr.letE n type val body nondep =>
|
||||
mapLetDecl n (← loop F type) (← loop F val) (nondep := nondep) (usedLetOnly := false) fun x => do
|
||||
loop F (body.instantiate1 x)
|
||||
| Expr.mdata d b =>
|
||||
if let some stx := getRecAppSyntax? e then
|
||||
withRef stx <| loop F b
|
||||
|
|
|
|||
|
|
@ -241,10 +241,10 @@ where
|
|||
loop param d
|
||||
withLocalDecl n c d fun x => do
|
||||
loop param (b.instantiate1 x)
|
||||
| Expr.letE n type val body _ =>
|
||||
| Expr.letE n type val body nondep =>
|
||||
loop param type
|
||||
loop param val
|
||||
withLetDecl n type val fun x => do
|
||||
withLetDecl n type val (nondep := nondep) fun x => do
|
||||
loop param (body.instantiate1 x)
|
||||
| Expr.mdata _d b =>
|
||||
if let some stx := getRecAppSyntax? e then
|
||||
|
|
|
|||
|
|
@ -110,14 +110,68 @@ builtin_dsimproc paramMatcher (_) := fun e => do
|
|||
let matcherApp' := { matcherApp with discrs := discrs', alts := alts' }
|
||||
return .continue <| matcherApp'.toExpr
|
||||
|
||||
/-- `let x := (wfParam e); body[x] ==> let x := e; body[wfParam y] -/
|
||||
private def anyLetValueIsWfParam (e : Expr) : Bool :=
|
||||
match e with
|
||||
| .letE _ _ v b _ => (isWfParam? v).isSome || anyLetValueIsWfParam b
|
||||
| _ => false
|
||||
|
||||
private def numLetsWithValueNotIsWfParam (e : Expr) (acc := 0) : Nat :=
|
||||
match e with
|
||||
| .letE _ _ v b _ => if (isWfParam? v).isSome then acc else numLetsWithValueNotIsWfParam b (acc + 1)
|
||||
| _ => acc
|
||||
|
||||
private partial def processParamLet (e : Expr) : MetaM Expr := do
|
||||
if let .letE _ t v b _ := e then
|
||||
if let some v' := isWfParam? v then
|
||||
if ← Meta.isProp t then
|
||||
processParamLet <| e.updateLetE! t v' b
|
||||
else
|
||||
let u ← getLevel t
|
||||
let b' := b.instantiate1 <| mkApp2 (.const ``wfParam [u]) t (.bvar 0)
|
||||
processParamLet <| e.updateLetE! t v' b'
|
||||
else
|
||||
let num := numLetsWithValueNotIsWfParam e
|
||||
assert! num > 0
|
||||
letBoundedTelescope e num fun xs b => do
|
||||
let b' ← processParamLet b
|
||||
mkLetFVars (usedLetOnly := false) (generalizeNondepLet := false) xs b'
|
||||
else
|
||||
return e
|
||||
|
||||
/--
|
||||
`let x : T := (wfParam e); body[x] ==> let x : T := e; body[wfParam y]` if `T` is not a proposition,
|
||||
otherwise `... ==> let x : T := e; body[x]`. (Applies to `have`s too.)
|
||||
|
||||
Note: simprocs are provided the head of a let telescope, but not intermediate lets.
|
||||
-/
|
||||
builtin_dsimproc paramLet (_) := fun e => do
|
||||
unless e.isLet do return .continue
|
||||
let some v := isWfParam? e.letValue! | return .continue
|
||||
let u ← getLevel e.letType!
|
||||
let body' := e.letBody!.instantiate1 <|
|
||||
mkApp2 (.const ``wfParam [u]) e.letType! (.bvar 0)
|
||||
return .continue <| e.updateLetE! e.letType! v body'
|
||||
unless e.isLet || anyLetValueIsWfParam e do return .continue
|
||||
return .continue (← processParamLet e)
|
||||
|
||||
/--
|
||||
Transforms non-Prop `have`s to `let`s, so that the values can be used in GuessLex and decreasing-by proofs.
|
||||
These `have`s may have been introdued by `simp`, which converts `let`s to `have`s.
|
||||
-/
|
||||
private def nonPropHaveToLet (e : Expr) : MetaM Expr := do
|
||||
Meta.transform e (pre := fun e => do
|
||||
if (← Meta.isProof e) then
|
||||
return .done e
|
||||
else if e.isLet then
|
||||
-- Recall that `Meta.transform` processes entire let telescopes,
|
||||
-- so we need to handle the telescope all at once.
|
||||
let lctx ← getLCtx
|
||||
let e' ← letTelescope e fun xs b => do
|
||||
let lctx' ← xs.foldlM (init := lctx) fun lctx' x => do
|
||||
let decl ← x.fvarId!.getDecl
|
||||
-- Clear the flag if it's not a prop.
|
||||
let decl' := decl.setNondep <| ← pure decl.isNondep <&&> Meta.isProp decl.type
|
||||
pure <| lctx'.addDecl decl'
|
||||
withLCtx' lctx' do
|
||||
mkLetFVars (usedLetOnly := false) (generalizeNondepLet := false) xs b
|
||||
return .continue e'
|
||||
else
|
||||
return .continue
|
||||
)
|
||||
|
||||
def preprocess (e : Expr) : MetaM Simp.Result := do
|
||||
unless wf.preprocess.get (← getOptions) do
|
||||
|
|
@ -141,9 +195,13 @@ def preprocess (e : Expr) : MetaM Simp.Result := do
|
|||
if h : as.size ≥ 2 then
|
||||
return .continue (mkAppN as[1] as[2:])
|
||||
return .continue
|
||||
|
||||
-- Transform `have`s to `let`s for non-propositions.
|
||||
let e'' ← nonPropHaveToLet e''
|
||||
|
||||
let result := { result with expr := e'' }
|
||||
|
||||
trace[Elab.definition.wf] "Attach-introduction:{indentExpr e'}\nto{indentExpr result.expr}\ncleaned up as{indentExpr e''}"
|
||||
trace[Elab.definition.wf] "Attach-introduction:{indentExpr e'}\nto{indentExpr result.expr}"
|
||||
result.addLambdas xs
|
||||
|
||||
end Lean.Elab.WF
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ def solveMonoStep (failK : ∀ {α}, Expr → Array Name → MetaM α := @defaul
|
|||
return [goal'.mvarId!]
|
||||
|
||||
-- Handle let
|
||||
if let .letE n t v b _nonDep := e then
|
||||
if let .letE n t v b nondep := e then
|
||||
if t.hasLooseBVars || v.hasLooseBVars then
|
||||
-- We cannot float the let to the context, so just zeta-reduce.
|
||||
let b' := f.updateLambdaE! f.bindingDomain! (b.instantiate1 v)
|
||||
|
|
@ -143,10 +143,10 @@ def solveMonoStep (failK : ∀ {α}, Expr → Array Name → MetaM α := @defaul
|
|||
return [goal'.mvarId!]
|
||||
else
|
||||
-- No recursive call in t or v, so float out
|
||||
let goal' ← withLetDecl n t v fun x => do
|
||||
let goal' ← withLetDecl n t v (nondep := nondep) fun x => do
|
||||
let b' := f.updateLambdaE! f.bindingDomain! (b.instantiate1 x)
|
||||
let goal' ← mkFreshExprSyntheticOpaqueMVar (mkApp type.appFn! b')
|
||||
goal.assign (← mkLetFVars #[x] goal')
|
||||
goal.assign (← mkLetFVars (generalizeNondepLet := false) #[x] goal')
|
||||
pure goal'
|
||||
return [goal'.mvarId!]
|
||||
|
||||
|
|
|
|||
|
|
@ -48,10 +48,36 @@ See `LocalDecl.index`, `LocalDecl.fvarId`, `LocalDecl.userName`, `LocalDecl.type
|
|||
arguments common to both constructors.
|
||||
-/
|
||||
inductive LocalDecl where
|
||||
/-- A local variable. -/
|
||||
/-- A local variable without any value.
|
||||
`Lean.LocalContext.mkBinding` creates lambdas or foralls from `cdecl`s. -/
|
||||
| cdecl (index : Nat) (fvarId : FVarId) (userName : Name) (type : Expr) (bi : BinderInfo) (kind : LocalDeclKind)
|
||||
/-- A let-bound free variable, with a `value : Expr`. -/
|
||||
| ldecl (index : Nat) (fvarId : FVarId) (userName : Name) (type : Expr) (value : Expr) (nonDep : Bool) (kind : LocalDeclKind)
|
||||
/-- A let-bound free variable, with a value `value : Expr`.
|
||||
If `nondep := false`, then the variable is definitionally equal to its value.
|
||||
If `nondep := true`, then the variable has an opaque value; we call these "have-bound free variables."
|
||||
`Lean.LocalContext.mkBinding` creates let/have expressions from `ldecl`s.
|
||||
|
||||
**Important:** The `nondep := true` case is subtle; it is not merely an opaque `ldecl`!
|
||||
- In most contexts, nondependent `ldecl`s should be treated like `cdecl`s.
|
||||
For example, suppose we have a tactic goal `x : α := v (nondep) ⊢ b`.
|
||||
It would be incorrect for `revert x` to produce the goal `⊢ have x : α := v; b`,
|
||||
since this would be saying "to prove `b` without knowledge of the value of `x`, it suffices to
|
||||
prove `have x : α := v; b` for this particular value of `x`."
|
||||
Instead, `revert x` *must* produce the goal `⊢ ∀ x : α, b`.
|
||||
Furthermore, given a goal `⊢ have x : α := v; b`, the `intro x` tactic should yield a *dependent* `ldecl`,
|
||||
since users expect to be able to make use of the value of `x`,
|
||||
plus, as discussed, if `intro` yielded a nondep `ldecl` then `intro x; revert x` would convert the goal into a forall, not a `have`.
|
||||
- Also: `value` might not be type correct. Metaprograms may decide to pretend that all `nondep := true`
|
||||
`ldecl`s are `cdecl`s (for example, when reverting variables). As a consequence, nondep `ldecl`s may
|
||||
have type-incorrect values. This design decision allows metaprograms to not have to think about nondep `ldecl`s,
|
||||
so long as `LocalDecl` values are consumed through `LocalDecl.isLet` and `LocalDecl.value?` with `(allowNondep := false)`.
|
||||
**Rule:** never use `(generalizeNondepLet := false)` in `mkBinding`-family functions within a local context you do not own.
|
||||
See `LocalDecl.setNondep` for some additional discussion.
|
||||
- Where then do nondep ldecls come from? Common functions are `Meta.mapLetDecl`, `Meta.withLetDecl`, and `Meta.letTelescope`.
|
||||
The `have` term syntax makes use of a nondep ldecl as well.
|
||||
|
||||
Therefore, `nondep := true` should be used with consideration.
|
||||
Its primary use is in metaprograms that enter `let`/`have` telescopes and wish to reconstruct them. -/
|
||||
| ldecl (index : Nat) (fvarId : FVarId) (userName : Name) (type : Expr) (value : Expr) (nondep : Bool) (kind : LocalDeclKind)
|
||||
deriving Inhabited
|
||||
|
||||
@[export lean_mk_local_decl]
|
||||
|
|
@ -66,9 +92,15 @@ def LocalDecl.binderInfoEx : LocalDecl → BinderInfo
|
|||
| _ => BinderInfo.default
|
||||
namespace LocalDecl
|
||||
|
||||
def isLet : LocalDecl → Bool
|
||||
| cdecl .. => false
|
||||
| ldecl .. => true
|
||||
/--
|
||||
Returns true if this is an `ldecl` with a visible value.
|
||||
|
||||
If `allowNondep` is true then includes `ldecl`s with `nondep := true`, whose values are normally hidden.
|
||||
-/
|
||||
def isLet : LocalDecl → (allowNondep : Bool := false) → Bool
|
||||
| cdecl .., _ => false
|
||||
| ldecl (nondep := false) .., _ => true
|
||||
| ldecl (nondep := true) .., allowNondep => allowNondep
|
||||
|
||||
/-- The position of the decl in the local context. -/
|
||||
def index : LocalDecl → Nat
|
||||
|
|
@ -115,22 +147,81 @@ Is the local declaration an implementation-detail hypothesis
|
|||
def isImplementationDetail (d : LocalDecl) : Bool :=
|
||||
d.kind != .default
|
||||
|
||||
def value? : LocalDecl → Option Expr
|
||||
| cdecl .. => none
|
||||
| ldecl (value := v) .. => some v
|
||||
/--
|
||||
Returns the value of the `ldecl` if it has a visible value.
|
||||
|
||||
def value : LocalDecl → Expr
|
||||
| cdecl .. => panic! "let declaration expected"
|
||||
| ldecl (value := v) .. => v
|
||||
If `allowNondep` is true, then allows nondependent `ldecl`s, whose values are normally hidden.
|
||||
-/
|
||||
def value? : LocalDecl → (allowNondep : Bool := false) → Option Expr
|
||||
| ldecl (nondep := false) (value := v) .., _ => some v
|
||||
| ldecl (nondep := true) (value := v) .., true => some v
|
||||
| _, _ => none
|
||||
|
||||
def hasValue : LocalDecl → Bool
|
||||
| cdecl .. => false
|
||||
| ldecl .. => true
|
||||
/--
|
||||
Returns the value of the `ldecl` if it has a visible value.
|
||||
|
||||
If `allowNondep` is true, then allows nondependent `ldecl`s, whose values are normally hidden.
|
||||
-/
|
||||
def value : LocalDecl → (allowNondep : Bool := false) → Expr
|
||||
| cdecl .., _ => panic! "let declaration expected"
|
||||
| ldecl (nondep := false) (value := v) .., _ => v
|
||||
| ldecl (nondep := true) (value := v) .., true => v
|
||||
| ldecl (nondep := true) .., false => panic! "dependent let declaration expected"
|
||||
|
||||
/--
|
||||
Returns `true` if `LocalDecl.value?` is not `none`.
|
||||
-/
|
||||
def hasValue : LocalDecl → (allowNondep : Bool := false) → Bool
|
||||
| cdecl .., _ => false
|
||||
| ldecl (nondep := nondep) .., allowNondep => !nondep || allowNondep
|
||||
|
||||
/-- Sets the value of an `ldecl`, otherwise returns `cdecl`s unchanged. -/
|
||||
def setValue : LocalDecl → Expr → LocalDecl
|
||||
| ldecl idx id n t _ nd k, v => ldecl idx id n t v nd k
|
||||
| d, _ => d
|
||||
|
||||
/--
|
||||
Sets the `nondep` flag of an `ldecl`, otherwise returns `cdecl`s unchanged.
|
||||
|
||||
This is a low-level function, and it is the responsibility of the caller to ensure that
|
||||
transitions of `nondep` are valid.
|
||||
|
||||
Rules:
|
||||
- If the declaration is not under the caller's control, then setting `nondep := false` must not be done.
|
||||
General nondependent `ldecl`s should be treated like `cdecl`s.
|
||||
See also the docstring for `LocalDecl.ldecl` about the `value` not necessarily being type correct.
|
||||
- Setting `nondep := true` is usually fine.
|
||||
- Caution: be sure any relevant caches are cleared so that the value associated to this `FVarId` does not leak.
|
||||
- Caution: be sure that metavariables dependent on this declaration created before and after the transition are not mixed,
|
||||
since unification does not check "`nondep`-compatibility" of local contexts when assigning metavariables.
|
||||
|
||||
For example, setting `nondep := false` is fine from within a telescope combinator, to update the local context
|
||||
right before calling `mkLetFVars`:
|
||||
```lean
|
||||
let lctx ← getLCtx
|
||||
letTelescope e fun xs b => do
|
||||
let lctx' ← xs.foldlM (init := lctx) fun lctx' x => do
|
||||
let decl ← x.fvarId!.getDecl
|
||||
-- Clear the flag if it's not a prop.
|
||||
let decl' := decl.setNondep <| ← pure decl.isNondep <&&> Meta.isProp decl.type
|
||||
pure <| lctx'.addDecl decl'
|
||||
withLCtx' lctx' do
|
||||
mkLetFVars (usedLetOnly := false) (generalizeNondepLet := false) xs b
|
||||
```
|
||||
1. The declarations for `xs` are in the control of this metaprogram.
|
||||
2. `mkLetFVars` does make use of `MetaM` caches.
|
||||
3. Even if `e` has metavariables, these do not include `xs` in their contexts,
|
||||
so the change of the `nondep` flag does not cause any issues in the `abstractM` system used by `mkLetFVars`.
|
||||
-/
|
||||
def setNondep : LocalDecl → Bool → LocalDecl
|
||||
| ldecl idx id n t v _ k, nd => ldecl idx id n t v nd k
|
||||
| d, _ => d
|
||||
|
||||
/-- Returns `true` if this is an `ldecl` with `nondep := true`. -/
|
||||
def isNondep : LocalDecl → Bool
|
||||
| ldecl (nondep := nondep) .. => nondep
|
||||
| _ => false
|
||||
|
||||
def setUserName : LocalDecl → Name → LocalDecl
|
||||
| cdecl index id _ type bi k, userName => cdecl index id userName type bi k
|
||||
| ldecl index id _ type val nd k, userName => ldecl index id userName type val nd k
|
||||
|
|
@ -152,8 +243,8 @@ Set the kind of a `LocalDecl`.
|
|||
def setKind : LocalDecl → LocalDeclKind → LocalDecl
|
||||
| cdecl index fvarId userName type bi _, kind =>
|
||||
cdecl index fvarId userName type bi kind
|
||||
| ldecl index fvarId userName type value nonDep _, kind =>
|
||||
ldecl index fvarId userName type value nonDep kind
|
||||
| ldecl index fvarId userName type value nondep _, kind =>
|
||||
ldecl index fvarId userName type value nondep kind
|
||||
|
||||
end LocalDecl
|
||||
|
||||
|
|
@ -182,7 +273,7 @@ def empty : LocalContext := {}
|
|||
def isEmpty (lctx : LocalContext) : Bool :=
|
||||
lctx.fvarIdToDecl.isEmpty
|
||||
|
||||
/-- Low level API for creating local declarations.
|
||||
/-- Low level API for creating local declarations (`LocalDecl.cdecl`).
|
||||
It is used to implement actions in the monads `Elab` and `Tactic`.
|
||||
It should not be used directly since the argument `(fvarId : FVarId)` is
|
||||
assumed to be unique. You can create a unique fvarId with `mkFreshFVarId`. -/
|
||||
|
|
@ -199,16 +290,16 @@ private def mkLocalDeclExported (lctx : LocalContext) (fvarId : FVarId) (userNam
|
|||
mkLocalDecl lctx fvarId userName type bi
|
||||
|
||||
/-- Low level API for let declarations. Do not use directly.-/
|
||||
def mkLetDecl (lctx : LocalContext) (fvarId : FVarId) (userName : Name) (type : Expr) (value : Expr) (nonDep := false) (kind : LocalDeclKind := default) : LocalContext :=
|
||||
def mkLetDecl (lctx : LocalContext) (fvarId : FVarId) (userName : Name) (type : Expr) (value : Expr) (nondep := false) (kind : LocalDeclKind := default) : LocalContext :=
|
||||
match lctx with
|
||||
| { fvarIdToDecl := map, decls := decls, auxDeclToFullName } =>
|
||||
let idx := decls.size
|
||||
let decl := LocalDecl.ldecl idx fvarId userName type value nonDep kind
|
||||
let decl := LocalDecl.ldecl idx fvarId userName type value nondep kind
|
||||
{ fvarIdToDecl := map.insert fvarId decl, decls := decls.push decl, auxDeclToFullName }
|
||||
|
||||
@[export lean_local_ctx_mk_let_decl]
|
||||
private def mkLetDeclExported (lctx : LocalContext) (fvarId : FVarId) (userName : Name) (type : Expr) (value : Expr) (nonDep : Bool) : LocalContext :=
|
||||
mkLetDecl lctx fvarId userName type value nonDep
|
||||
private def mkLetDeclExported (lctx : LocalContext) (fvarId : FVarId) (userName : Name) (type : Expr) (value : Expr) (nondep : Bool) : LocalContext :=
|
||||
mkLetDecl lctx fvarId userName type value nondep
|
||||
|
||||
/-- Low level API for auxiliary declarations. Do not use directly. -/
|
||||
def mkAuxDecl (lctx : LocalContext) (fvarId : FVarId) (userName : Name) (type : Expr) (fullName : Name) : LocalContext :=
|
||||
|
|
@ -431,35 +522,39 @@ partial def isSubPrefixOfAux (a₁ a₂ : PArray (Option LocalDecl)) (exceptFVar
|
|||
def isSubPrefixOf (lctx₁ lctx₂ : LocalContext) (exceptFVars : Array Expr := #[]) : Bool :=
|
||||
isSubPrefixOfAux lctx₁.decls lctx₂.decls exceptFVars 0 0
|
||||
|
||||
@[inline] def mkBinding (isLambda : Bool) (lctx : LocalContext) (xs : Array Expr) (b : Expr) : Expr :=
|
||||
@[inline] def mkBinding (isLambda : Bool) (lctx : LocalContext) (xs : Array Expr) (b : Expr) (generalizeNondepLet := false) : Expr :=
|
||||
let b := b.abstract xs
|
||||
xs.size.foldRev (init := b) fun i _ b =>
|
||||
let x := xs[i]
|
||||
match lctx.findFVar? x with
|
||||
| some (.cdecl _ _ n ty bi _) =>
|
||||
let handleCDecl (n : Name) (ty : Expr) (bi : BinderInfo) : Expr :=
|
||||
let ty := ty.abstractRange i xs;
|
||||
if isLambda then
|
||||
Lean.mkLambda n bi ty b
|
||||
else
|
||||
Lean.mkForall n bi ty b
|
||||
| some (.ldecl _ _ n ty val nonDep _) =>
|
||||
if b.hasLooseBVar 0 then
|
||||
match lctx.findFVar? x with
|
||||
| some (.cdecl _ _ n ty bi _) =>
|
||||
handleCDecl n ty bi
|
||||
| some (.ldecl _ _ n ty val nondep _) =>
|
||||
if nondep && generalizeNondepLet then
|
||||
handleCDecl n ty .default
|
||||
else if b.hasLooseBVar 0 then
|
||||
let ty := ty.abstractRange i xs
|
||||
let val := val.abstractRange i xs
|
||||
mkLet n ty val b nonDep
|
||||
mkLet n ty val b nondep
|
||||
else
|
||||
b.lowerLooseBVars 1 1
|
||||
| none => panic! "unknown free variable"
|
||||
|
||||
/-- Creates the expression `fun x₁ .. xₙ => b` for free variables `xs = #[x₁, .., xₙ]`,
|
||||
suitably abstracting `b` and the types for each of the `xᵢ`. -/
|
||||
def mkLambda (lctx : LocalContext) (xs : Array Expr) (b : Expr) : Expr :=
|
||||
mkBinding true lctx xs b
|
||||
def mkLambda (lctx : LocalContext) (xs : Array Expr) (b : Expr) (generalizeNondepLet := false) : Expr :=
|
||||
mkBinding true lctx xs b generalizeNondepLet
|
||||
|
||||
/-- Creates the expression `(x₁:α₁) → .. → (xₙ:αₙ) → b` for free variables `xs = #[x₁, .., xₙ]`,
|
||||
suitably abstracting `b` and the types for each of the `xᵢ`, `αᵢ`. -/
|
||||
def mkForall (lctx : LocalContext) (xs : Array Expr) (b : Expr) : Expr :=
|
||||
mkBinding false lctx xs b
|
||||
def mkForall (lctx : LocalContext) (xs : Array Expr) (b : Expr) (generalizeNondepLet := false) : Expr :=
|
||||
mkBinding false lctx xs b generalizeNondepLet
|
||||
|
||||
@[inline] def anyM [Monad m] (lctx : LocalContext) (p : LocalDecl → m Bool) : m Bool :=
|
||||
lctx.decls.anyM fun d => match d with
|
||||
|
|
@ -539,7 +634,7 @@ def LocalDecl.replaceFVarId (fvarId : FVarId) (e : Expr) (d : LocalDecl) : Local
|
|||
if d.fvarId == fvarId then d
|
||||
else match d with
|
||||
| .cdecl idx id n type bi k => .cdecl idx id n (type.replaceFVarId fvarId e) bi k
|
||||
| .ldecl idx id n type val nonDep k => .ldecl idx id n (type.replaceFVarId fvarId e) (val.replaceFVarId fvarId e) nonDep k
|
||||
| .ldecl idx id n type val nondep k => .ldecl idx id n (type.replaceFVarId fvarId e) (val.replaceFVarId fvarId e) nondep k
|
||||
|
||||
def LocalContext.replaceFVarId (fvarId : FVarId) (e : Expr) (lctx : LocalContext) : LocalContext :=
|
||||
let lctx := lctx.erase fvarId
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ partial def visit (e : Expr) : M Expr := do
|
|||
let localDecl ← xFVarId.getDecl
|
||||
let type ← visit localDecl.type
|
||||
let localDecl := localDecl.setType type
|
||||
let localDecl ← match localDecl.value? with
|
||||
let localDecl ← match localDecl.value? (allowNondep := true) with
|
||||
| some value => let value ← visit value; pure <| localDecl.setValue value
|
||||
| none => pure localDecl
|
||||
lctx := lctx.modifyLocalDecl xFVarId fun _ => localDecl
|
||||
|
|
@ -70,8 +70,8 @@ partial def visit (e : Expr) : M Expr := do
|
|||
/- Ensure proofs nested in type are also abstracted -/
|
||||
abstractProof e (← read).cache visit
|
||||
else match e with
|
||||
| .lam .. => lambdaLetTelescope e fun xs b => visitBinders xs do mkLambdaFVars xs (← visit b) (usedLetOnly := false)
|
||||
| .letE .. => lambdaLetTelescope e fun xs b => visitBinders xs do mkLambdaFVars xs (← visit b) (usedLetOnly := false)
|
||||
| .lam ..
|
||||
| .letE .. => lambdaLetTelescope e fun xs b => visitBinders xs do mkLambdaFVars xs (← visit b) (usedLetOnly := false) (generalizeNondepLet := false)
|
||||
| .forallE .. => forallTelescope e fun xs b => visitBinders xs do mkForallFVars xs (← visit b)
|
||||
| .mdata _ b => return e.updateMData! (← visit b)
|
||||
| .proj _ _ b => return e.updateProj! (← visit b)
|
||||
|
|
|
|||
|
|
@ -445,8 +445,8 @@ structure Context where
|
|||
When `trackZetaDelta = true`, we track all free variables that have been zetaDelta-expanded.
|
||||
That is, suppose the local context contains
|
||||
the declaration `x : t := v`, and we reduce `x` to `v`, then we insert `x` into `State.zetaDeltaFVarIds`.
|
||||
We use `trackZetaDelta` to discover which let-declarations `let x := v; e` can be represented as `(fun x => e) v`.
|
||||
When we find these declarations we set their `nonDep` flag with `true`.
|
||||
We use `trackZetaDelta` to discover which let-declarations `let x := v; e` can be represented as `have x := v; e`.
|
||||
When we find these declarations we set their `nondep` flag with `true`.
|
||||
To find these let-declarations in a given term `s`, we
|
||||
1- Reset `State.zetaDeltaFVarIds`
|
||||
2- Set `trackZetaDelta := true`
|
||||
|
|
@ -978,17 +978,27 @@ def _root_.Lean.FVarId.getType (fvarId : FVarId) : MetaM Expr :=
|
|||
def _root_.Lean.FVarId.getBinderInfo (fvarId : FVarId) : MetaM BinderInfo :=
|
||||
return (← fvarId.getDecl).binderInfo
|
||||
|
||||
/-- Return `some value` if the given free variable is a let-declaration, and `none` otherwise. -/
|
||||
def _root_.Lean.FVarId.getValue? (fvarId : FVarId) : MetaM (Option Expr) :=
|
||||
return (← fvarId.getDecl).value?
|
||||
/--
|
||||
Returns `some value` if the given free let-variable has a visible local definition in the current local context
|
||||
(using `Lean.LocalDecl.value?`), and `none` otherwise.
|
||||
|
||||
Setting `allowNondep := true` allows access of the normally hidden value of a nondependent let declaration.
|
||||
-/
|
||||
def _root_.Lean.FVarId.getValue? (fvarId : FVarId) (allowNondep : Bool := false) : MetaM (Option Expr) :=
|
||||
return (← fvarId.getDecl).value? allowNondep
|
||||
|
||||
/-- Return the user-facing name for the given free variable. -/
|
||||
def _root_.Lean.FVarId.getUserName (fvarId : FVarId) : MetaM Name :=
|
||||
return (← fvarId.getDecl).userName
|
||||
|
||||
/-- Return `true` is the free variable is a let-variable. -/
|
||||
def _root_.Lean.FVarId.isLetVar (fvarId : FVarId) : MetaM Bool :=
|
||||
return (← fvarId.getDecl).isLet
|
||||
/--
|
||||
Returns `true` if the free variable is a let-variable with a visible local definition in the current local context
|
||||
(using `Lean.LocalDecl.isLet`).
|
||||
|
||||
Setting `allowNondep := true` includes nondependent let declarations, whose values are normally hidden.
|
||||
-/
|
||||
def _root_.Lean.FVarId.isLetVar (fvarId : FVarId) (allowNondep : Bool := false) : MetaM Bool :=
|
||||
return (← fvarId.getDecl).isLet allowNondep
|
||||
|
||||
/-- Get the local declaration associated to the given `Expr` in the current local
|
||||
context. Fails if the given expression is not a fvar or if no such declaration exists. -/
|
||||
|
|
@ -1054,26 +1064,30 @@ def _root_.Lean.Expr.abstractM (e : Expr) (xs : Array Expr) : MetaM Expr :=
|
|||
/--
|
||||
Collect forward dependencies for the free variables in `toRevert`.
|
||||
Recall that when reverting free variables `xs`, we must also revert their forward dependencies.
|
||||
|
||||
When `generalizeNondepLet := true` (the default), then the values of nondependent lets are not considered
|
||||
when computing forward dependencies.
|
||||
-/
|
||||
def collectForwardDeps (toRevert : Array Expr) (preserveOrder : Bool) : MetaM (Array Expr) := do
|
||||
liftMkBindingM <| MetavarContext.collectForwardDeps toRevert preserveOrder
|
||||
def collectForwardDeps (toRevert : Array Expr) (preserveOrder : Bool) (generalizeNondepLet := true) : MetaM (Array Expr) := do
|
||||
liftMkBindingM <| MetavarContext.collectForwardDeps toRevert preserveOrder generalizeNondepLet
|
||||
|
||||
/-- Takes an array `xs` of free variables or metavariables and a term `e` that may contain those variables, and abstracts and binds them as universal quantifiers.
|
||||
|
||||
- if `usedOnly = true` then only variables that the expression body depends on will appear.
|
||||
- if `usedLetOnly = true` same as `usedOnly` except for let-bound variables. (That is, local constants which have been assigned a value.)
|
||||
- if `generalizeNondepLet = true` then nondependent `ldecl`s become foralls too.
|
||||
-/
|
||||
def mkForallFVars (xs : Array Expr) (e : Expr) (usedOnly : Bool := false) (usedLetOnly : Bool := true) (binderInfoForMVars := BinderInfo.implicit) : MetaM Expr :=
|
||||
if xs.isEmpty then return e else liftMkBindingM <| MetavarContext.mkForall xs e usedOnly usedLetOnly binderInfoForMVars
|
||||
def mkForallFVars (xs : Array Expr) (e : Expr) (usedOnly : Bool := false) (usedLetOnly : Bool := true) (generalizeNondepLet := true) (binderInfoForMVars := BinderInfo.implicit) : MetaM Expr :=
|
||||
if xs.isEmpty then return e else liftMkBindingM <| MetavarContext.mkForall xs e usedOnly usedLetOnly generalizeNondepLet binderInfoForMVars
|
||||
|
||||
/-- Takes an array `xs` of free variables and metavariables and a
|
||||
body term `e` and creates `fun ..xs => e`, suitably
|
||||
abstracting `e` and the types in `xs`. -/
|
||||
def mkLambdaFVars (xs : Array Expr) (e : Expr) (usedOnly : Bool := false) (usedLetOnly : Bool := true) (etaReduce : Bool := false) (binderInfoForMVars := BinderInfo.implicit) : MetaM Expr :=
|
||||
if xs.isEmpty then return e else liftMkBindingM <| MetavarContext.mkLambda xs e usedOnly usedLetOnly etaReduce binderInfoForMVars
|
||||
def mkLambdaFVars (xs : Array Expr) (e : Expr) (usedOnly : Bool := false) (usedLetOnly : Bool := true) (etaReduce : Bool := false) (generalizeNondepLet := true) (binderInfoForMVars := BinderInfo.implicit) : MetaM Expr :=
|
||||
if xs.isEmpty then return e else liftMkBindingM <| MetavarContext.mkLambda xs e usedOnly usedLetOnly etaReduce generalizeNondepLet binderInfoForMVars
|
||||
|
||||
def mkLetFVars (xs : Array Expr) (e : Expr) (usedLetOnly := true) (binderInfoForMVars := BinderInfo.implicit) : MetaM Expr :=
|
||||
mkLambdaFVars xs e (usedLetOnly := usedLetOnly) (binderInfoForMVars := binderInfoForMVars)
|
||||
def mkLetFVars (xs : Array Expr) (e : Expr) (usedLetOnly := true) (generalizeNondepLet := true) (binderInfoForMVars := BinderInfo.implicit) : MetaM Expr :=
|
||||
mkLambdaFVars xs e (usedLetOnly := usedLetOnly) (generalizeNondepLet := generalizeNondepLet) (binderInfoForMVars := binderInfoForMVars)
|
||||
|
||||
/-- `fun _ : Unit => a` -/
|
||||
def mkFunUnit (a : Expr) : MetaM Expr :=
|
||||
|
|
@ -1519,40 +1533,48 @@ private def forallBoundedTelescopeImp (type : Expr) (maxFVars? : Option Nat) (k
|
|||
def forallBoundedTelescope (type : Expr) (maxFVars? : Option Nat) (k : Array Expr → Expr → n α) (cleanupAnnotations := false) (whnfType := false) : n α :=
|
||||
map2MetaM (fun k => forallBoundedTelescopeImp type maxFVars? k cleanupAnnotations (whnfType := whnfType)) k
|
||||
|
||||
private partial def lambdaTelescopeImp (e : Expr) (consumeLet : Bool) (maxFVars? : Option Nat)
|
||||
private partial def lambdaTelescopeImp (e : Expr) (consumeLambda : Bool) (consumeLet : Bool) (preserveNondepLet : Bool) (nondepLetOnly : Bool) (maxFVars? : Option Nat)
|
||||
(k : Array Expr → Expr → MetaM α) (cleanupAnnotations := false) : MetaM α := do
|
||||
process consumeLet (← getLCtx) #[] e
|
||||
process consumeLambda consumeLet (← getLCtx) #[] e
|
||||
where
|
||||
process (consumeLet : Bool) (lctx : LocalContext) (fvars : Array Expr) (e : Expr) : MetaM α := do
|
||||
match fvarsSizeLtMaxFVars fvars maxFVars?, consumeLet, e with
|
||||
| true, _, .lam n d b bi =>
|
||||
process (consumeLambda : Bool) (consumeLet : Bool) (lctx : LocalContext) (fvars : Array Expr) (e : Expr) : MetaM α := do
|
||||
let finish (e : Expr) : MetaM α :=
|
||||
let e := e.instantiateRevRange 0 fvars.size fvars
|
||||
withReader (fun ctx => { ctx with lctx := lctx }) do
|
||||
withNewLocalInstancesImp fvars 0 do
|
||||
k fvars e
|
||||
match fvarsSizeLtMaxFVars fvars maxFVars?, consumeLambda, consumeLet, e with
|
||||
| true, true, _, .lam n d b bi =>
|
||||
let d := d.instantiateRevRange 0 fvars.size fvars
|
||||
let d := if cleanupAnnotations then d.cleanupAnnotations else d
|
||||
let fvarId ← mkFreshFVarId
|
||||
let lctx := lctx.mkLocalDecl fvarId n d bi
|
||||
let fvar := mkFVar fvarId
|
||||
process consumeLet lctx (fvars.push fvar) b
|
||||
| true, true, .letE n t v b _ => do
|
||||
let t := t.instantiateRevRange 0 fvars.size fvars
|
||||
let t := if cleanupAnnotations then t.cleanupAnnotations else t
|
||||
let v := v.instantiateRevRange 0 fvars.size fvars
|
||||
let fvarId ← mkFreshFVarId
|
||||
let lctx := lctx.mkLetDecl fvarId n t v
|
||||
let fvar := mkFVar fvarId
|
||||
process true lctx (fvars.push fvar) b
|
||||
| _, _, e =>
|
||||
let e := e.instantiateRevRange 0 fvars.size fvars
|
||||
withReader (fun ctx => { ctx with lctx := lctx }) do
|
||||
withNewLocalInstancesImp fvars 0 do
|
||||
k fvars e
|
||||
process true consumeLet lctx (fvars.push fvar) b
|
||||
| true, _, true, .letE n t v b nondep => do
|
||||
if !nondep && nondepLetOnly then
|
||||
finish e
|
||||
else
|
||||
let t := t.instantiateRevRange 0 fvars.size fvars
|
||||
let t := if cleanupAnnotations then t.cleanupAnnotations else t
|
||||
let v := v.instantiateRevRange 0 fvars.size fvars
|
||||
let fvarId ← mkFreshFVarId
|
||||
let lctx := lctx.mkLetDecl fvarId n t v (nondep && preserveNondepLet)
|
||||
let fvar := mkFVar fvarId
|
||||
process consumeLambda true lctx (fvars.push fvar) b
|
||||
| _, _, _, e =>
|
||||
finish e
|
||||
|
||||
/--
|
||||
Similar to `lambdaTelescope` but for lambda and let expressions.
|
||||
|
||||
If `cleanupAnnotations` is `true`, we apply `Expr.cleanupAnnotations` to each type in the telescope.
|
||||
- If `cleanupAnnotations` is `true`, we apply `Expr.cleanupAnnotations` to each type in the telescope.
|
||||
- If `preserveNondep` is `false`, all `have`s are converted to `let`s.
|
||||
|
||||
See also `mapLambdaLetTelescope` for entering and rebuilding the telescope.
|
||||
-/
|
||||
def lambdaLetTelescope (e : Expr) (k : Array Expr → Expr → n α) (cleanupAnnotations := false) : n α :=
|
||||
map2MetaM (fun k => lambdaTelescopeImp e true .none k (cleanupAnnotations := cleanupAnnotations)) k
|
||||
def lambdaLetTelescope (e : Expr) (k : Array Expr → Expr → n α) (cleanupAnnotations := false) (preserveNondepLet := true) : n α :=
|
||||
map2MetaM (fun k => lambdaTelescopeImp e true true preserveNondepLet false .none k (cleanupAnnotations := cleanupAnnotations)) k
|
||||
|
||||
/--
|
||||
Given `e` of the form `fun ..xs => A`, execute `k xs A`.
|
||||
|
|
@ -1562,7 +1584,7 @@ def lambdaLetTelescope (e : Expr) (k : Array Expr → Expr → n α) (cleanupAnn
|
|||
If `cleanupAnnotations` is `true`, we apply `Expr.cleanupAnnotations` to each type in the telescope.
|
||||
-/
|
||||
def lambdaTelescope (e : Expr) (k : Array Expr → Expr → n α) (cleanupAnnotations := false) : n α :=
|
||||
map2MetaM (fun k => lambdaTelescopeImp e false none k (cleanupAnnotations := cleanupAnnotations)) k
|
||||
map2MetaM (fun k => lambdaTelescopeImp e true false true false none k (cleanupAnnotations := cleanupAnnotations)) k
|
||||
|
||||
/--
|
||||
Given `e` of the form `fun ..xs ..ys => A`, execute `k xs (fun ..ys => A)` where
|
||||
|
|
@ -1573,7 +1595,42 @@ def lambdaTelescope (e : Expr) (k : Array Expr → Expr → n α) (cleanupAnnota
|
|||
If `cleanupAnnotations` is `true`, we apply `Expr.cleanupAnnotations` to each type in the telescope.
|
||||
-/
|
||||
def lambdaBoundedTelescope (e : Expr) (maxFVars : Nat) (k : Array Expr → Expr → n α) (cleanupAnnotations := false) : n α :=
|
||||
map2MetaM (fun k => lambdaTelescopeImp e false (.some maxFVars) k (cleanupAnnotations := cleanupAnnotations)) k
|
||||
map2MetaM (fun k => lambdaTelescopeImp e true false true false (.some maxFVars) k (cleanupAnnotations := cleanupAnnotations)) k
|
||||
|
||||
/--
|
||||
Given `e` of the form `let x₁ := v₁; ...; let xₙ := vₙ; A`, executes `k xs A`,
|
||||
where `xs` is an array of free variables for the binders.
|
||||
The `let`s can also be `have`s.
|
||||
|
||||
- If `cleanupAnnotations` is `true`, applies `Expr.cleanupAnnotations` to each type in the telescope.
|
||||
- If `preserveNondep` is `false`, all `have`s are converted to `let`s.
|
||||
- If `nondepLetOnly` is `true`, then only `have`s are consumed (it stops at the first dependent `let`).
|
||||
|
||||
See also `mapLetTelescope` for entering and rebuilding the telescope.
|
||||
-/
|
||||
def letTelescope (e : Expr) (k : Array Expr → Expr → n α) (cleanupAnnotations := false) (preserveNondepLet := true) (nondepLetOnly := false) : n α :=
|
||||
map2MetaM (fun k => lambdaTelescopeImp e false true preserveNondepLet nondepLetOnly none k (cleanupAnnotations := cleanupAnnotations)) k
|
||||
|
||||
/--
|
||||
Like `letTelescope`, but limits the number of `let`/`have`s consumed to `maxFVars?`.
|
||||
If `maxFVars?` is none, then this is the same as `letTelescope`.
|
||||
-/
|
||||
def letBoundedTelescope (e : Expr) (maxFVars? : Option Nat) (k : Array Expr → Expr → n α) (cleanupAnnotations := false) (preserveNondepLet := true) (nondepLetOnly := false) : n α :=
|
||||
map2MetaM (fun k => lambdaTelescopeImp e false true preserveNondepLet nondepLetOnly maxFVars? k (cleanupAnnotations := cleanupAnnotations)) k
|
||||
|
||||
/--
|
||||
Evaluates `k` from within a `lambdaLetTelescope`, then uses `mkLetFVars` to rebuild the telescope.
|
||||
-/
|
||||
def mapLambdaLetTelescope [MonadLiftT MetaM n] (e : Expr) (k : Array Expr → Expr → n Expr) (cleanupAnnotations := false) (preserveNondepLet := true) (usedLetOnly := true) : n Expr :=
|
||||
lambdaLetTelescope e (cleanupAnnotations := cleanupAnnotations) (preserveNondepLet := preserveNondepLet) fun xs b => do
|
||||
mkLambdaFVars (usedLetOnly := usedLetOnly) (generalizeNondepLet := false) xs (← k xs b)
|
||||
|
||||
/--
|
||||
Evaluates `k` from within a `letTelescope`, then uses `mkLetFVars` to rebuild the telescope.
|
||||
-/
|
||||
def mapLetTelescope [MonadLiftT MetaM n] (e : Expr) (k : Array Expr → Expr → n Expr) (cleanupAnnotations := false) (preserveNondepLet := true) (nondepLetOnly := false) (usedLetOnly := true) : n Expr :=
|
||||
letTelescope e (cleanupAnnotations := cleanupAnnotations) (preserveNondepLet := preserveNondepLet) (nondepLetOnly := nondepLetOnly) fun xs b => do
|
||||
mkLetFVars (usedLetOnly := usedLetOnly) (generalizeNondepLet := false) xs (← k xs b)
|
||||
|
||||
/-- Return the parameter names for the given global declaration. -/
|
||||
def getParamNames (declName : Name) : MetaM (Array Name) := do
|
||||
|
|
@ -1754,10 +1811,10 @@ def withInstImplicitAsImplict (xs : Array Expr) (k : MetaM α) : MetaM α := do
|
|||
return none
|
||||
withNewBinderInfos newBinderInfos k
|
||||
|
||||
private def withLetDeclImp (n : Name) (type : Expr) (val : Expr) (k : Expr → MetaM α) (kind : LocalDeclKind) : MetaM α := do
|
||||
private def withLetDeclImp (n : Name) (type : Expr) (val : Expr) (k : Expr → MetaM α) (nondep : Bool) (kind : LocalDeclKind) : MetaM α := do
|
||||
let fvarId ← mkFreshFVarId
|
||||
let ctx ← read
|
||||
let lctx := ctx.lctx.mkLetDecl fvarId n type val (nonDep := false) kind
|
||||
let lctx := ctx.lctx.mkLetDecl fvarId n type val nondep kind
|
||||
let fvar := mkFVar fvarId
|
||||
withReader (fun ctx => { ctx with lctx := lctx }) do
|
||||
withNewFVar fvar type k
|
||||
|
|
@ -1766,8 +1823,17 @@ private def withLetDeclImp (n : Name) (type : Expr) (val : Expr) (k : Expr → M
|
|||
Add the local declaration `<name> : <type> := <val>` to the local context and execute `k x`, where `x` is a new
|
||||
free variable corresponding to the `let`-declaration. After executing `k x`, the local context is restored.
|
||||
-/
|
||||
def withLetDecl (name : Name) (type : Expr) (val : Expr) (k : Expr → n α) (kind : LocalDeclKind := .default) : n α :=
|
||||
map1MetaM (fun k => withLetDeclImp name type val k kind) k
|
||||
def withLetDecl (name : Name) (type : Expr) (val : Expr) (k : Expr → n α) (nondep : Bool := false) (kind : LocalDeclKind := .default) : n α :=
|
||||
map1MetaM (fun k => withLetDeclImp name type val k nondep kind) k
|
||||
|
||||
/--
|
||||
Runs `k x` with the local declaration `<name> : <type> := <val>` added to the local context, where `x` is the new free variable.
|
||||
Afterwards, the result is wrapped in the given `let`/`have` expression (according to the value of `nondep`).
|
||||
- If `usedLetOnly := true` (the default) then the the `let`/`have` is not created if the variable is unused.
|
||||
-/
|
||||
def mapLetDecl [MonadLiftT MetaM n] (name : Name) (type : Expr) (val : Expr) (k : Expr → n Expr) (nondep : Bool := false) (kind : LocalDeclKind := .default) (usedLetOnly : Bool := true) : n Expr :=
|
||||
withLetDecl name type val (nondep := nondep) (kind := kind) fun x => do
|
||||
mkLetFVars (usedLetOnly := usedLetOnly) (generalizeNondepLet := false) #[x] (← k x)
|
||||
|
||||
def withLocalInstancesImp (decls : List LocalDecl) (k : MetaM α) : MetaM α := do
|
||||
let mut localInsts := (← read).localInstances
|
||||
|
|
|
|||
|
|
@ -183,10 +183,11 @@ where
|
|||
def throwLetTypeMismatchMessage {α} (fvarId : FVarId) : MetaM α := do
|
||||
let lctx ← getLCtx
|
||||
match lctx.find? fvarId with
|
||||
| some (LocalDecl.ldecl _ _ _ t v _ _) => do
|
||||
| some (LocalDecl.ldecl _ _ _ t v nondep _) => do
|
||||
let vType ← inferType v
|
||||
let (vType, t) ← addPPExplicitToExposeDiff vType t
|
||||
throwError "invalid let declaration, term{indentExpr v}\nhas type{indentExpr vType}\nbut is expected to have type{indentExpr t}"
|
||||
let declKind := if nondep then "have" else "let"
|
||||
throwError "invalid {declKind} declaration, term{indentExpr v}\nhas type{indentExpr vType}\nbut is expected to have type{indentExpr t}"
|
||||
| _ => unreachable!
|
||||
|
||||
/--
|
||||
|
|
|
|||
|
|
@ -286,9 +286,10 @@ partial def process : ClosureM Unit := do
|
|||
pushLocalDecl newFVarId userName type bi
|
||||
pushFVarArg (mkFVar fvarId)
|
||||
process
|
||||
| .ldecl _ _ userName type val _ _ =>
|
||||
| .ldecl _ _ userName type val nondep _ =>
|
||||
let zetaDeltaFVarIds ← getZetaDeltaFVarIds
|
||||
if !zetaDeltaFVarIds.contains fvarId then
|
||||
-- Note: If `nondep` is true then `zetaDeltaFVarIds.contains fvarId` must be false.
|
||||
if nondep || !zetaDeltaFVarIds.contains fvarId then
|
||||
/- Non-dependent let-decl
|
||||
|
||||
Recall that if `fvarId` is in `zetaDeltaFVarIds`, then we zetaDelta-expanded it
|
||||
|
|
@ -321,11 +322,11 @@ partial def process : ClosureM Unit := do
|
|||
Lean.mkLambda n bi ty b
|
||||
else
|
||||
Lean.mkForall n bi ty b
|
||||
| .ldecl _ _ n ty val nonDep _ =>
|
||||
| .ldecl _ _ n ty val nondep _ =>
|
||||
if b.hasLooseBVar 0 then
|
||||
let ty := ty.abstractRange i xs
|
||||
let val := val.abstractRange i xs
|
||||
mkLet n ty val b nonDep
|
||||
mkLet n ty val b nondep
|
||||
else
|
||||
b.lowerLooseBVars 1 1
|
||||
|
||||
|
|
|
|||
|
|
@ -426,7 +426,7 @@ private partial def mkLambdaFVarsWithLetDeps (xs : Array Expr) (v : Expr) : Meta
|
|||
mkLambdaFVars ys v (etaReduce := true)
|
||||
|
||||
where
|
||||
/-- Return true if there are let-declarions between `xs[0]` and `xs[xs.size-1]`.
|
||||
/-- Return true if there are let-declarations between `xs[0]` and `xs[xs.size-1]`.
|
||||
We use it a quick-check to avoid the more expensive collection procedure. -/
|
||||
hasLetDeclsInBetween : MetaM Bool := do
|
||||
let check (lctx : LocalContext) : Bool := Id.run do
|
||||
|
|
@ -728,7 +728,21 @@ mutual
|
|||
else
|
||||
let lctx := ctxMeta.lctx
|
||||
match lctx.findFVar? fvar with
|
||||
| some (.ldecl (value := v) ..) => check v
|
||||
/-
|
||||
Recall: if `nondep := true`, then the ldecl is locally a cdecl, so the `value` field is not relevant.
|
||||
In the following example, switching the indicated `have` for a `let` causes the unification to fail,
|
||||
since then `v` depends on a variable not in `?mvar`'s local context.
|
||||
```
|
||||
example : Nat → Nat :=
|
||||
let f : Nat → Nat := ?mvar
|
||||
let x : Nat := 2
|
||||
-- if this is a `let`, then `refine rfl` fails.
|
||||
have v := x
|
||||
have : ?mvar v = v := by refine rfl
|
||||
f
|
||||
```
|
||||
-/
|
||||
| some (.ldecl (nondep := false) (value := v) ..) => check v
|
||||
| _ =>
|
||||
if ctx.fvars.contains fvar then pure fvar
|
||||
else
|
||||
|
|
@ -917,7 +931,10 @@ unsafe def checkImpl
|
|||
| .fvar fvarId .. =>
|
||||
if mvarDecl.lctx.contains fvarId then
|
||||
return true
|
||||
if let some (LocalDecl.ldecl ..) := lctx.find? fvarId then
|
||||
/-
|
||||
Recall: if `nondep := true` then the ldecl is locally a cdecl. See comment in `CheckAssignment.checkFVar`.
|
||||
-/
|
||||
if let some (LocalDecl.ldecl (nondep := false) ..) := lctx.find? fvarId then
|
||||
return false -- need expensive CheckAssignment.check
|
||||
if fvars.any fun x => x.fvarId! == fvarId then
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ private def lensCoord (g : Expr → M Expr) (n : Nat) (e : Expr) : M Expr := do
|
|||
| 1, .forallE n y b c => withLocalDecl n c y fun x => do mkForallFVars #[x] <|← g <| b.instantiateRev #[x]
|
||||
| 0, .letE _ y a b _ => return e.updateLetE! (← g y) a b
|
||||
| 1, .letE _ y a b _ => return e.updateLetE! y (← g a) b
|
||||
| 2, .letE n y a b _ => withLetDecl n y a fun x => do mkLetFVars #[x] <|← g <| b.instantiateRev #[x]
|
||||
| 2, .letE n y a b nondep => mapLetDecl n y a (nondep := nondep) (usedLetOnly := false) fun x => g <| b.instantiate1 x
|
||||
| 0, .proj _ _ b => e.updateProj! <$> g b
|
||||
| n, .mdata _ a => e.updateMData! <$> lensCoord g n a
|
||||
| 3, _ => throwError "Lensing on types is not supported"
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ private def inferProjType (structName : Name) (idx : Nat) (e : Expr) : MetaM Exp
|
|||
| .forallE _ d _ _ => return d.consumeTypeAnnotations
|
||||
| _ => failed ()
|
||||
|
||||
def throwTypeExcepted {α} (type : Expr) : MetaM α :=
|
||||
def throwTypeExpected {α} (type : Expr) : MetaM α :=
|
||||
throwError "type expected{indentExpr type}"
|
||||
|
||||
def getLevel (type : Expr) : MetaM Level := do
|
||||
|
|
@ -131,12 +131,12 @@ def getLevel (type : Expr) : MetaM Level := do
|
|||
| Expr.sort lvl => return lvl
|
||||
| Expr.mvar mvarId =>
|
||||
if (← mvarId.isReadOnlyOrSyntheticOpaque) then
|
||||
throwTypeExcepted type
|
||||
throwTypeExpected type
|
||||
else
|
||||
let lvl ← mkFreshLevelMVar
|
||||
mvarId.assign (mkSort lvl)
|
||||
return lvl
|
||||
| _ => throwTypeExcepted type
|
||||
| _ => throwTypeExpected type
|
||||
|
||||
private def inferForallType (e : Expr) : MetaM Expr :=
|
||||
forallTelescope e fun xs e => do
|
||||
|
|
@ -151,7 +151,7 @@ private def inferForallType (e : Expr) : MetaM Expr :=
|
|||
private def inferLambdaType (e : Expr) : MetaM Expr :=
|
||||
lambdaLetTelescope e fun xs e => do
|
||||
let type ← inferType e
|
||||
mkForallFVars xs type
|
||||
mkForallFVars (generalizeNondepLet := false) xs type
|
||||
|
||||
def throwUnknownMVar {α} (mvarId : MVarId) : MetaM α :=
|
||||
throwError "unknown metavariable '?{mvarId.name}'"
|
||||
|
|
|
|||
|
|
@ -102,7 +102,8 @@ def ppGoal (mvarId : MVarId) : MetaM Format := do
|
|||
return fmt ++ (Format.joinSep ids.reverse (format " ") ++ " :" ++ Format.nest indent (Format.line ++ typeFmt)).group
|
||||
let rec ppVars (varNames : List Name) (prevType? : Option Expr) (fmt : Format) (localDecl : LocalDecl) : MetaM (List Name × Option Expr × Format) := do
|
||||
match localDecl with
|
||||
| .cdecl _ _ varName type _ _ =>
|
||||
| .cdecl _ _ varName type ..
|
||||
| .ldecl _ _ varName type (nondep := true) .. =>
|
||||
let varName := varName.simpMacroScopes
|
||||
let type ← instantiateMVars type
|
||||
if prevType? == none || prevType? == some type then
|
||||
|
|
@ -110,7 +111,7 @@ def ppGoal (mvarId : MVarId) : MetaM Format := do
|
|||
else do
|
||||
let fmt ← pushPending varNames prevType? fmt
|
||||
return ([varName], some type, fmt)
|
||||
| .ldecl _ _ varName type val _ _ => do
|
||||
| .ldecl _ _ varName type val (nondep := false) .. => do
|
||||
let varName := varName.simpMacroScopes
|
||||
let fmt ← pushPending varNames prevType? fmt
|
||||
let fmt := addLine fmt
|
||||
|
|
|
|||
|
|
@ -379,13 +379,13 @@ partial def foldAndCollect (oldIH newIH : FVarId) (isRecCall : Expr → Option E
|
|||
let body' ← foldAndCollect oldIH newIH isRecCall (body.instantiate1 x)
|
||||
mkForallFVars #[x] body'
|
||||
|
||||
| .letE n t v b _ =>
|
||||
| .letE n t v b nondep =>
|
||||
let t' ← foldAndCollect oldIH newIH isRecCall t
|
||||
let v' ← foldAndCollect oldIH newIH isRecCall v
|
||||
withLetDecl n t' v' fun x => do
|
||||
M.localMapM (mkLetFVars (usedLetOnly := true) #[x] ·) do
|
||||
withLetDecl n t' v' (nondep := nondep) fun x => do
|
||||
M.localMapM (mkLetFVars (usedLetOnly := true) (generalizeNondepLet := false) #[x] ·) do
|
||||
let b' ← foldAndCollect oldIH newIH isRecCall (b.instantiate1 x)
|
||||
mkLetFVars #[x] b'
|
||||
mkLetFVars (generalizeNondepLet := false) #[x] b'
|
||||
|
||||
| .mdata m b =>
|
||||
pure <| .mdata m (← foldAndCollect oldIH newIH isRecCall b)
|
||||
|
|
@ -474,6 +474,11 @@ where
|
|||
for localDecl in (← getLCtx) do
|
||||
if localDecl.index > index && (!firstPass || localDecl.userName.hasMacroScopes) then
|
||||
if localDecl.isLet then
|
||||
if ← Meta.isProp localDecl.type then
|
||||
if let some mvarId' ← observing? <| mvarId.clearValue localDecl.fvarId then
|
||||
return some mvarId'
|
||||
else
|
||||
continue
|
||||
if let some mvarId' ← observing? <| mvarId.clear localDecl.fvarId then
|
||||
return some mvarId'
|
||||
if let some mvarId' ← substVar? mvarId localDecl.fvarId then
|
||||
|
|
@ -908,10 +913,10 @@ partial def buildInductionBody (toErase toClear : Array FVarId) (goal : Expr)
|
|||
if let some (n, t, v, b) := e.letFun? then
|
||||
let t' ← foldAndCollect oldIH newIH isRecCall t
|
||||
let v' ← foldAndCollect oldIH newIH isRecCall v
|
||||
return ← withLocalDeclD n t' fun x => M2.branch do
|
||||
return ← withLetDecl n t' v' fun x => M2.branch do
|
||||
let b' ← withRewrittenMotiveArg goal (rwHaveWith x) fun goal' =>
|
||||
buildInductionBody toErase toClear goal' oldIH newIH isRecCall (b.instantiate1 x)
|
||||
mkLetFun x v' b'
|
||||
mkLetFVars #[x] b' (usedLetOnly := false)
|
||||
|
||||
-- Special case for traversing the PProd’ed bodies in our encoding of structural mutual recursion
|
||||
if let .lam n t b bi := e then
|
||||
|
|
|
|||
|
|
@ -31,6 +31,11 @@ namespace Lean.Meta
|
|||
let val := val.instantiateRevRange j fvars.size fvars
|
||||
let fvarId ← mkFreshFVarId
|
||||
let (n, s) ← mkName lctx n true s
|
||||
/-
|
||||
We have both dependent and non-dependent `let` expressions result in dependent `ldecl`s.
|
||||
It is counterintuitive if `have` expressions are introduced with opaque values,
|
||||
especially when we run transformations to aggressively turn `let`s into `have`s.
|
||||
-/
|
||||
let lctx := lctx.mkLetDecl fvarId n type val
|
||||
let fvar := mkFVar fvarId
|
||||
let fvars := fvars.push fvar
|
||||
|
|
|
|||
|
|
@ -17,17 +17,18 @@ namespace Lean.Meta
|
|||
/-!
|
||||
### `let` extraction
|
||||
|
||||
Extracting `let`s means to locate `let`/`letFun`s in a term and to extract them
|
||||
Extracting `let`s means to locate `let`/`have`s in a term and to extract them
|
||||
from the term, extending the local context with new declarations in the process.
|
||||
A related process is lifting `lets`, which means to move `let`/`letFun`s toward the root of a term.
|
||||
A related process is lifting `lets`, which means to move `let`/`have`s toward the root of a term.
|
||||
-/
|
||||
|
||||
namespace ExtractLets
|
||||
|
||||
structure LocalDecl' where
|
||||
/-- An `ldecl` with `nondep := false`. -/
|
||||
decl : LocalDecl
|
||||
/--
|
||||
If true, is a `let`, if false, is a `letFun`.
|
||||
If true, is a `let`, if false, is a `have`.
|
||||
Used in `lift` mode.
|
||||
-/
|
||||
isLet : Bool
|
||||
|
|
@ -90,13 +91,13 @@ def isExtractableLet (fvars : List Expr) (n : Name) (t v : Expr) : M (Bool × Na
|
|||
if let some n ← nextNameForBinderName? n then
|
||||
return (true, n)
|
||||
-- In lift mode, we temporarily extract non-extractable lets, but we do not make use of `givenNames` for them.
|
||||
-- These will be flushed as let/letFun expressions, and we wish to preserve the original binder name.
|
||||
-- These will be flushed as let/have expressions, and we wish to preserve the original binder name.
|
||||
if (← read).lift then
|
||||
return (true, n)
|
||||
return (false, n)
|
||||
|
||||
/--
|
||||
Adds the `decl` to the `decls` list. Assumes that `decl` is an ldecl.
|
||||
Adds the `decl` to the `decls` list. Assumes that `decl` is an ldecl with `nondep := false`.
|
||||
-/
|
||||
def addDecl (decl : LocalDecl) (isLet : Bool) : M Unit := do
|
||||
let cfg ← read
|
||||
|
|
@ -140,13 +141,9 @@ This should *not* be used when closing lets for new goal metavariables, since
|
|||
1. The goal contains the decls in its local context, violating the assumption.
|
||||
2. We need to use true `let`s in that case, since tactics may zeta-delta reduce these declarations.
|
||||
-/
|
||||
def mkLetDecls (decls : Array LocalDecl') (e : Expr) : MetaM Expr := do
|
||||
withEnsuringDeclsInContext decls do
|
||||
decls.foldrM (init := e) fun { decl, isLet } e => do
|
||||
if isLet then
|
||||
return .letE decl.userName decl.type decl.value (e.abstract #[decl.toExpr]) false
|
||||
else
|
||||
mkLetFun decl.toExpr decl.value e
|
||||
def mkLetDecls (decls : Array LocalDecl') (e : Expr) : Expr :=
|
||||
decls.foldr (init := e) fun { decl, isLet } e =>
|
||||
Expr.letE decl.userName decl.type decl.value (e.abstract #[decl.toExpr]) (nondep := !isLet)
|
||||
|
||||
/--
|
||||
Makes sure the declaration for `fvarId` is marked with `isLet := true`.
|
||||
|
|
@ -227,7 +224,7 @@ partial def extractCore (fvars : List Expr) (e : Expr) (topLevel : Bool := false
|
|||
match e with
|
||||
| .bvar .. | .fvar .. | .mvar .. | .sort .. | .const .. | .lit .. => unreachable!
|
||||
| .mdata _ e' => return e.updateMData! (← extractCore fvars e' (topLevel := topLevel))
|
||||
| .letE n t v b _ => extractLetLike true n t v b (fun t v b => pure <| e.updateLetE! t v b) (topLevel := topLevel)
|
||||
| .letE n t v b nondep => extractLetLike (!nondep) n t v b (fun t v b => pure <| e.updateLetE! t v b) (topLevel := topLevel)
|
||||
| .app .. =>
|
||||
if e.isLetFun then
|
||||
extractLetFun e (topLevel := topLevel)
|
||||
|
|
@ -244,7 +241,7 @@ where
|
|||
let b ← extractCore (x :: fvars) (b.instantiate1 x)
|
||||
if (← read).lift then
|
||||
let toFlush ← flushDecls x.fvarId!
|
||||
let b ← mkLetDecls toFlush b
|
||||
let b := mkLetDecls toFlush b
|
||||
return mk t (b.abstract #[x])
|
||||
else
|
||||
return mk t (b.abstract #[x])
|
||||
|
|
@ -326,7 +323,7 @@ private def extractLetsImp (es : Array Expr) (givenNames : List Name)
|
|||
withExistingLocalDecls decls.toList <| k (decls.map (·.fvarId)) es givenNames'
|
||||
|
||||
/--
|
||||
Extracts `let` and `letFun` expressions into local definitions,
|
||||
Extracts `let` and `have` expressions into local definitions,
|
||||
evaluating `k` at the post-extracted expressions and the extracted fvarids, within a context containing those local declarations.
|
||||
- The `givenNames` is a list of explicit names to use for extracted local declarations.
|
||||
If a name is `_` (or if there is no provided given name and `config.onlyGivenNames` is true) then uses a hygienic name
|
||||
|
|
@ -337,11 +334,11 @@ def extractLets [Monad m] [MonadControlT MetaM m] (es : Array Expr) (givenNames
|
|||
map3MetaM (fun k => extractLetsImp es givenNames k config) k
|
||||
|
||||
/--
|
||||
Lifts `let` and `letFun` expressions in the given expression as far out as possible.
|
||||
Lifts `let` and `have` expressions in the given expression as far out as possible.
|
||||
-/
|
||||
def liftLets (e : Expr) (config : LiftLetsConfig := {}) : MetaM Expr := do
|
||||
let (es, st) ← ExtractLets.extract #[e] |>.run { config with onlyGivenNames := true } |>.run' {} |>.run { givenNames := [] }
|
||||
ExtractLets.mkLetDecls st.decls es[0]!
|
||||
return ExtractLets.mkLetDecls st.decls es[0]!
|
||||
|
||||
end Lean.Meta
|
||||
|
||||
|
|
@ -349,7 +346,7 @@ private def throwMadeNoProgress (tactic : Name) (mvarId : MVarId) : MetaM α :=
|
|||
throwTacticEx tactic mvarId m!"made no progress"
|
||||
|
||||
/--
|
||||
Extracts `let` and `letFun` expressions from the target,
|
||||
Extracts `let` and `have` expressions from the target,
|
||||
returning `FVarId`s for the extracted let declarations along with the new goal.
|
||||
- The `givenNames` is a list of explicit names to use for extracted local declarations.
|
||||
If a name is `_` (or if there is no provided given name and `config.onlyGivenNames` is true) then uses a hygienic name
|
||||
|
|
@ -397,7 +394,7 @@ def Lean.MVarId.extractLetsLocalDecl (mvarId : MVarId) (fvarId : FVarId) (givenN
|
|||
| _ => throwTacticEx `extract_lets mvarId "unexpected auxiliary target"
|
||||
|
||||
/--
|
||||
Lifts `let` and `letFun` expressions in target as far out as possible.
|
||||
Lifts `let` and `have` expressions in target as far out as possible.
|
||||
Throws an exception if nothing is lifted.
|
||||
|
||||
Like `Lean.MVarId.extractLets`, but top-level lets are not added to the local context.
|
||||
|
|
|
|||
|
|
@ -235,9 +235,23 @@ inductive SimpLetCase where
|
|||
| nondep -- `let x := v; b` is equivalent to `(fun x => b) v`, and result type does not depend on `x`
|
||||
deriving Repr
|
||||
|
||||
def getSimpLetCase (n : Name) (t : Expr) (b : Expr) : MetaM SimpLetCase := do
|
||||
def getSimpLetCase (n : Name) (t : Expr) (b : Expr) (nondep : Bool) : MetaM SimpLetCase := do
|
||||
withLocalDeclD n t fun x => do
|
||||
let bx := b.instantiate1 x
|
||||
/-
|
||||
If the let has `nondep := true`, then we know the body does not depend on the value already.
|
||||
Then there are two cases, depending on whether or not the type of the body refers to the variable.
|
||||
-/
|
||||
if nondep then
|
||||
let bxType ← whnf (← inferType bx)
|
||||
if (← dependsOn bxType x.fvarId!) then
|
||||
return .nondepDepVar
|
||||
else
|
||||
return .nondep
|
||||
/-
|
||||
Otherwise, we first detect whether `nondep := true` *should* have been set by checking type correctness of the body.
|
||||
If that fails, the let is dependent.
|
||||
-/
|
||||
/- The following step is potentially very expensive when we have many nested let-decls.
|
||||
TODO: handle a block of nested let decls in a single pass if this becomes a performance problem. -/
|
||||
if (← isTypeCorrect bx) then
|
||||
|
|
@ -389,11 +403,13 @@ def simpForall (e : Expr) : SimpM Result := withParent e do
|
|||
return { expr := (← dsimp e) }
|
||||
|
||||
def simpLet (e : Expr) : SimpM Result := do
|
||||
let .letE n t v b _ := e | unreachable!
|
||||
let .letE n t v b nondep := e | unreachable!
|
||||
if (← getConfig).zeta then
|
||||
return { expr := b.instantiate1 v }
|
||||
else if !b.hasLooseBVars && (← getConfig).zetaUnused then
|
||||
return { expr := b.lowerLooseBVars 1 1 }
|
||||
else
|
||||
let simpLetCase ← getSimpLetCase n t b
|
||||
let simpLetCase ← getSimpLetCase n t b nondep
|
||||
trace[Debug.Meta.Tactic.simp] "getSimpLetCase is {repr simpLetCase}:{indentExpr e}"
|
||||
match simpLetCase with
|
||||
| SimpLetCase.dep => return { expr := (← dsimp e) }
|
||||
|
|
@ -405,7 +421,7 @@ def simpLet (e : Expr) : SimpM Result := do
|
|||
let hb? ← match rbx.proof? with
|
||||
| none => pure none
|
||||
| some h => pure (some (← mkLambdaFVars #[x] h))
|
||||
let e' := mkLet n t rv.expr (← rbx.expr.abstractM #[x])
|
||||
let e' := mkLet n t rv.expr (← rbx.expr.abstractM #[x]) (nondep := true)
|
||||
match rv.proof?, hb? with
|
||||
| none, none => return { expr := e' }
|
||||
| some h, none => return { expr := e', proof? := some (← mkLetValCongr (← mkLambdaFVars #[x] rbx.expr) h) }
|
||||
|
|
@ -415,7 +431,7 @@ def simpLet (e : Expr) : SimpM Result := do
|
|||
withLocalDeclD n t fun x => withNewLemmas #[x] do
|
||||
let bx := b.instantiate1 x
|
||||
let rbx ← simp bx
|
||||
let e' := mkLet n t v' (← rbx.expr.abstractM #[x])
|
||||
let e' := mkLet n t v' (← rbx.expr.abstractM #[x]) (nondep := true)
|
||||
match rbx.proof? with
|
||||
| none => return { expr := e' }
|
||||
| some h =>
|
||||
|
|
@ -721,8 +737,16 @@ def simpApp (e : Expr) : SimpM Result := do
|
|||
if isOfNatNatLit e || isOfScientificLit e || isCharLit e then
|
||||
-- Recall that we fold "orphan" kernel Nat literals `n` into `OfNat.ofNat n`
|
||||
return { expr := e }
|
||||
else if isNonDepLetFun e then
|
||||
simpNonDepLetFun e
|
||||
else if let some (args, n, t, v, b) := e.letFunAppArgs? then
|
||||
/-
|
||||
Note: we will be removing `letFun`, and we want everything to be in terms of `nondep := true` lets.
|
||||
This used to call `simpNonDepLetFun`, which is optimized for letFun telescopes.
|
||||
Considerations:
|
||||
- we will soon replace `simpNonDepLetFun` with a `let` version
|
||||
- simp is now using the `nondep` flag to cache which `let`s are nondependent.
|
||||
- even without the optimized version we still manage to pass the test suite for now without timing out.
|
||||
-/
|
||||
return { expr := mkAppN (Expr.letE n t v b true) args }
|
||||
else
|
||||
congr e
|
||||
|
||||
|
|
|
|||
|
|
@ -113,10 +113,10 @@ partial def transformWithCache {m} [Monad m] [MonadLiftT MetaM m] [MonadControlT
|
|||
| e => visitPost (← mkForallFVars (usedLetOnly := usedLetOnly) fvars (← visit (e.instantiateRev fvars)))
|
||||
let rec visitLet (fvars : Array Expr) (e : Expr) : MonadCacheT ExprStructEq Expr m Expr := do
|
||||
match e with
|
||||
| .letE n t v b _ =>
|
||||
withLetDecl n (← visit (t.instantiateRev fvars)) (← visit (v.instantiateRev fvars)) fun x =>
|
||||
| .letE n t v b nondep =>
|
||||
withLetDecl n (← visit (t.instantiateRev fvars)) (← visit (v.instantiateRev fvars)) (nondep := nondep) fun x =>
|
||||
visitLet (fvars.push x) b
|
||||
| e => visitPost (← mkLetFVars (usedLetOnly := usedLetOnly) fvars (← visit (e.instantiateRev fvars)))
|
||||
| e => visitPost (← mkLetFVars (usedLetOnly := usedLetOnly) (generalizeNondepLet := false) fvars (← visit (e.instantiateRev fvars)))
|
||||
let visitApp (e : Expr) : MonadCacheT ExprStructEq Expr m Expr :=
|
||||
e.withApp fun f args => do
|
||||
if skipConstInApp && f.isConst then
|
||||
|
|
|
|||
|
|
@ -372,8 +372,7 @@ end
|
|||
| .fvar fvarId =>
|
||||
let decl ← fvarId.getDecl
|
||||
match decl with
|
||||
| .cdecl .. => return e
|
||||
| .ldecl (value := v) .. =>
|
||||
| .ldecl (value := v) (nondep := false) .. =>
|
||||
-- Let-declarations marked as implementation detail should always be unfolded
|
||||
-- We initially added this feature for `simp`, and added it here for consistency.
|
||||
let cfg ← getConfig
|
||||
|
|
@ -383,6 +382,7 @@ end
|
|||
if (← read).trackZetaDelta then
|
||||
modify fun s => { s with zetaDeltaFVarIds := s.zetaDeltaFVarIds.insert fvarId }
|
||||
whnfEasyCases v k
|
||||
| _ => return e
|
||||
| .mvar mvarId =>
|
||||
match (← getExprMVarAssignment? mvarId) with
|
||||
| some v => whnfEasyCases v k
|
||||
|
|
@ -697,7 +697,7 @@ partial def smartUnfoldingReduce? (e : Expr) : MetaM (Option Expr) :=
|
|||
where
|
||||
go (e : Expr) : OptionT MetaM Expr := do
|
||||
match e with
|
||||
| .letE n t v b _ => withLetDecl n t (← go v) fun x => do mkLetFVars #[x] (← go (b.instantiate1 x))
|
||||
| .letE n t v b nondep => mapLetDecl n t (← go v) (nondep := nondep) fun x => go (b.instantiate1 x)
|
||||
| .lam .. => lambdaTelescope e fun xs b => do mkLambdaFVars xs (← go b)
|
||||
| .app f a .. => return mkApp (← go f) (← go a)
|
||||
| .proj _ _ s => return e.updateProj! (← go s)
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ the requirements imposed by these modules.
|
|||
process above is recursive. We claim it terminates because we keep
|
||||
creating new metavariables with smaller local contexts.
|
||||
|
||||
- Suppose, we have `t[?m]` and we want to create a let-expression by
|
||||
- Suppose we have `t[?m]` and we want to create a `let`-expression by
|
||||
abstracting a let-decl free variable `x`, and the local context of
|
||||
`?m` contains `x`. Similarly to the previous case
|
||||
```
|
||||
|
|
@ -153,13 +153,14 @@ the requirements imposed by these modules.
|
|||
will be ill-formed if we later assign a term `s` to `?m`, and
|
||||
`s` contains free variable `x`. Again, assume the type of `?m` is `A[x]`.
|
||||
|
||||
1. If `?m` is natural or synthetic, then we create `?n : (let x : T := v; A[x])` with
|
||||
and local context := local context of `?m` - `x`, we assign `?m := ?n`,
|
||||
and produce the term `let x : T := v; t[?n]`. That is, we are just making
|
||||
1. If `?m` is natural or synthetic, then we create `?n : (let x : T := v; A[x])` whose
|
||||
local context is the local context of `?m` minus `x`, we assign `?m := ?n`
|
||||
(which is correct since the types of `?m` and `?n` both reduce to `A[v]`),
|
||||
and then produce the term `let x : T := v; t[?n]`. That is, we are just making
|
||||
sure `?n` must never be assigned to a term containing `x`.
|
||||
|
||||
2. If `?m` is syntheticOpaque, we create a fresh syntheticOpaque `?n`
|
||||
with type `?n : T -> (let x : T := v; A[x])` and local context := local context of `?m` - `x`,
|
||||
with type `?n : T -> (let x : T := v; A[x])` whose local context is the local context of `?m` minus `x`,
|
||||
create the delayed assignment `?n #[x] := ?m`, and produce the term `let x : T := v; t[?n x]`.
|
||||
|
||||
Now suppose we assign `s` to `?m`. We do not assign the term `fun (x : T) => s` to `?n`, since
|
||||
|
|
@ -170,6 +171,18 @@ the requirements imposed by these modules.
|
|||
We are essentially using the pair "delayed assignment + application" to implement a delayed
|
||||
substitution.
|
||||
|
||||
- Suppose we have `t[?m]` and we want to create a `have`-expression
|
||||
by abstracting a *nondependent* let-decl free variable `x`.
|
||||
This needs a different procedure since `A[x]` does not reduce to `A[v]`.
|
||||
It is the same as abstracting for lambda expressions, but it produces `have` instead of lambda terms:
|
||||
|
||||
1. If `?m` is natural or synthetic, then we create `?n : ∀ (x : T), A[x]` whose
|
||||
local context is the local context of `?m` minus `x`, and then we assign `?m := ?n x`,
|
||||
and we produce the term `have x : T := v; t[?n x]`.
|
||||
|
||||
2. If `?m` is syntheticOpaque, we create the same `?n` but as syntheticOpaque,
|
||||
create the delayed assignment `?n #[x] := ?m`, and produce the term `have x : T := v; t[?n x]`.
|
||||
|
||||
- We use TC for implementing coercions. Both Joe Hendrix and Reid Barton
|
||||
reported a nasty limitation. In Lean3, TC will not be used if there are
|
||||
metavariables in the TC problem. For example, the elaborator will not try
|
||||
|
|
@ -600,10 +613,10 @@ def instantiateLCtxMVars [Monad m] [MonadMCtx m] (lctx : LocalContext) : m Local
|
|||
| .cdecl _ fvarId userName type bi k =>
|
||||
let type ← instantiateMVars type
|
||||
return lctx.mkLocalDecl fvarId userName type bi k
|
||||
| .ldecl _ fvarId userName type value nonDep k =>
|
||||
| .ldecl _ fvarId userName type value nondep k =>
|
||||
let type ← instantiateMVars type
|
||||
let value ← instantiateMVars value
|
||||
return lctx.mkLetDecl fvarId userName type value nonDep k
|
||||
return lctx.mkLetDecl fvarId userName type value nondep k
|
||||
|
||||
def instantiateMVarDeclMVars [Monad m] [MonadMCtx m] (mvarId : MVarId) : m Unit := do
|
||||
let mvarDecl := (← getMCtx).getDecl mvarId
|
||||
|
|
@ -616,8 +629,8 @@ def instantiateLocalDeclMVars [Monad m] [MonadMCtx m] (localDecl : LocalDecl) :
|
|||
match localDecl with
|
||||
| .cdecl idx id n type bi k =>
|
||||
return .cdecl idx id n (← instantiateMVars type) bi k
|
||||
| .ldecl idx id n type val nonDep k =>
|
||||
return .ldecl idx id n (← instantiateMVars type) (← instantiateMVars val) nonDep k
|
||||
| .ldecl idx id n type val nondep k =>
|
||||
return .ldecl idx id n (← instantiateMVars type) (← instantiateMVars val) nondep k
|
||||
|
||||
namespace DependsOn
|
||||
|
||||
|
|
@ -698,15 +711,21 @@ end DependsOn
|
|||
return result
|
||||
|
||||
/--
|
||||
Similar to `findExprDependsOn`, but checks the expressions in the given local declaration
|
||||
depends on a free variable `x` s.t. `pf x` is `true` or an unassigned metavariable `?m` s.t. `pm ?m` is true. -/
|
||||
@[inline] def findLocalDeclDependsOn [Monad m] [MonadMCtx m] (localDecl : LocalDecl) (pf : FVarId → Bool := fun _ => false) (pm : MVarId → Bool := fun _ => false) : m Bool := do
|
||||
Similar to `findExprDependsOn`, but checks the expressions in the given local declaration
|
||||
depends on a free variable `x` s.t. `pf x` is `true` or an unassigned metavariable `?m` s.t. `pm ?m` is true.
|
||||
- When `generalizeNondepLet := true` (the default), then values of nondependent lets are ignored,
|
||||
for computing dependencies from "within" a telescope.
|
||||
-/
|
||||
@[inline] def findLocalDeclDependsOn [Monad m] [MonadMCtx m] (localDecl : LocalDecl) (pf : FVarId → Bool := fun _ => false) (pm : MVarId → Bool := fun _ => false) (generalizeNondepLet := true) : m Bool := do
|
||||
match localDecl with
|
||||
| .cdecl (type := t) .. => findExprDependsOn t pf pm
|
||||
| .ldecl (type := t) (value := v) .. =>
|
||||
let (result, { mctx, .. }) := (DependsOn.main pf pm t <||> DependsOn.main pf pm v).run { mctx := (← getMCtx) }
|
||||
setMCtx mctx
|
||||
return result
|
||||
| .ldecl (type := t) (value := v) (nondep := nondep) .. =>
|
||||
if generalizeNondepLet && nondep then
|
||||
findExprDependsOn t pf pm
|
||||
else
|
||||
let (result, { mctx, .. }) := (DependsOn.main pf pm t <||> DependsOn.main pf pm v).run { mctx := (← getMCtx) }
|
||||
setMCtx mctx
|
||||
return result
|
||||
|
||||
def exprDependsOn [Monad m] [MonadMCtx m] (e : Expr) (fvarId : FVarId) : m Bool :=
|
||||
findExprDependsOn e (fvarId == ·)
|
||||
|
|
@ -715,9 +734,13 @@ def exprDependsOn [Monad m] [MonadMCtx m] (e : Expr) (fvarId : FVarId) : m Bool
|
|||
def dependsOn [Monad m] [MonadMCtx m] (e : Expr) (fvarId : FVarId) : m Bool :=
|
||||
exprDependsOn e fvarId
|
||||
|
||||
/-- Return true iff `localDecl` depends on the free variable `fvarId` -/
|
||||
def localDeclDependsOn [Monad m] [MonadMCtx m] (localDecl : LocalDecl) (fvarId : FVarId) : m Bool :=
|
||||
findLocalDeclDependsOn localDecl (fvarId == ·)
|
||||
/--
|
||||
Returns true iff `localDecl` depends on the free variable `fvarId`
|
||||
- When `generalizeNondepLet := true` (the default), then values of nondependent lets are ignored,
|
||||
for computing dependencies from "within" a telescope.
|
||||
-/
|
||||
def localDeclDependsOn [Monad m] [MonadMCtx m] (localDecl : LocalDecl) (fvarId : FVarId) (generalizeNondepLet := true) : m Bool :=
|
||||
findLocalDeclDependsOn localDecl (fvarId == ·) (generalizeNondepLet := generalizeNondepLet)
|
||||
|
||||
/-- Similar to `exprDependsOn`, but `x` can be a free variable or an unassigned metavariable. -/
|
||||
def exprDependsOn' [Monad m] [MonadMCtx m] (e : Expr) (x : Expr) : m Bool :=
|
||||
|
|
@ -729,11 +752,11 @@ def exprDependsOn' [Monad m] [MonadMCtx m] (e : Expr) (x : Expr) : m Bool :=
|
|||
return false
|
||||
|
||||
/-- Similar to `localDeclDependsOn`, but `x` can be a free variable or an unassigned metavariable. -/
|
||||
def localDeclDependsOn' [Monad m] [MonadMCtx m] (localDecl : LocalDecl) (x : Expr) : m Bool :=
|
||||
def localDeclDependsOn' [Monad m] [MonadMCtx m] (localDecl : LocalDecl) (x : Expr) (generalizeNondepLet := true) : m Bool :=
|
||||
if x.isFVar then
|
||||
findLocalDeclDependsOn localDecl (x.fvarId! == ·)
|
||||
findLocalDeclDependsOn localDecl (x.fvarId! == ·) (generalizeNondepLet := generalizeNondepLet)
|
||||
else if x.isMVar then
|
||||
findLocalDeclDependsOn localDecl (pm := (x.mvarId! == ·))
|
||||
findLocalDeclDependsOn localDecl (pm := (x.mvarId! == ·)) (generalizeNondepLet := generalizeNondepLet)
|
||||
else
|
||||
return false
|
||||
|
||||
|
|
@ -742,8 +765,8 @@ def dependsOnPred [Monad m] [MonadMCtx m] (e : Expr) (pf : FVarId → Bool := fu
|
|||
findExprDependsOn e pf pm
|
||||
|
||||
/-- Return true iff the local declaration `localDecl` depends on a free variable `x` s.t. `pf x`, an unassigned metavariable `?m` s.t. `pm ?m` is true. -/
|
||||
def localDeclDependsOnPred [Monad m] [MonadMCtx m] (localDecl : LocalDecl) (pf : FVarId → Bool := fun _ => false) (pm : MVarId → Bool := fun _ => false) : m Bool := do
|
||||
findLocalDeclDependsOn localDecl pf pm
|
||||
def localDeclDependsOnPred [Monad m] [MonadMCtx m] (localDecl : LocalDecl) (pf : FVarId → Bool := fun _ => false) (pm : MVarId → Bool := fun _ => false) (generalizeNondepLet := true) : m Bool := do
|
||||
findLocalDeclDependsOn localDecl pf pm (generalizeNondepLet := generalizeNondepLet)
|
||||
|
||||
|
||||
namespace MetavarContext
|
||||
|
|
@ -949,6 +972,9 @@ private def getLocalDeclWithSmallestIdx (lctx : LocalContext) (xs : Array Expr)
|
|||
return a new array of free variables that contains `toRevert` and all variables
|
||||
in `lctx` that may depend on `toRevert`.
|
||||
|
||||
When `generalizeNondepLet := true` (the default), then the values of nondependent lets are not considered
|
||||
when computing forward dependencies.
|
||||
|
||||
Remark: the result is sorted by `LocalDecl` indices.
|
||||
|
||||
Remark: We used to throw an `Exception.revertFailure` exception when an auxiliary declaration
|
||||
|
|
@ -969,7 +995,7 @@ private def getLocalDeclWithSmallestIdx (lctx : LocalContext) (xs : Array Expr)
|
|||
When we try to create the lambda `fun {α : Type} (a : α) => ?m`, we first need to create
|
||||
an auxiliary `?n` which does not contain `α` and `a` in its context. That is,
|
||||
we create the metavariable `?n : {α : Type} → (a : α) → (f : α → List α) → List α`,
|
||||
add the delayed assignment `?n #[α, a, f] := ?m`, and create the lambda
|
||||
add the delayed assignment `?n #[l, a, f] := ?m`, and create the lambda
|
||||
`fun {α : Type} (a : α) => ?n α a f`.
|
||||
|
||||
See `elimMVarDeps` for more information.
|
||||
|
|
@ -980,7 +1006,7 @@ private def getLocalDeclWithSmallestIdx (lctx : LocalContext) (xs : Array Expr)
|
|||
Note that <https://github.com/leanprover/lean/issues/1258> is not an issue in Lean4 because
|
||||
we have changed how we compile recursive definitions.
|
||||
-/
|
||||
def collectForwardDeps (lctx : LocalContext) (toRevert : Array Expr) : M (Array Expr) := do
|
||||
def collectForwardDeps (lctx : LocalContext) (toRevert : Array Expr) (generalizeNondepLet := true) : M (Array Expr) := do
|
||||
if toRevert.size == 0 then
|
||||
pure toRevert
|
||||
else
|
||||
|
|
@ -991,7 +1017,7 @@ def collectForwardDeps (lctx : LocalContext) (toRevert : Array Expr) : M (Array
|
|||
i.forM fun j _ => do
|
||||
let prevFVar := toRevert[j]
|
||||
let prevDecl := lctx.getFVar! prevFVar
|
||||
if (← localDeclDependsOn prevDecl fvar.fvarId!) then
|
||||
if (← localDeclDependsOn prevDecl fvar.fvarId! (generalizeNondepLet := generalizeNondepLet)) then
|
||||
throw (Exception.revertFailure (← getMCtx) lctx toRevert prevDecl.userName.toString)
|
||||
let newToRevert := if (← preserveOrder) then toRevert else Array.mkEmpty toRevert.size
|
||||
let firstDeclToVisit := getLocalDeclWithSmallestIdx lctx toRevert
|
||||
|
|
@ -1001,7 +1027,7 @@ def collectForwardDeps (lctx : LocalContext) (toRevert : Array Expr) : M (Array
|
|||
return newToRevert
|
||||
else if toRevert.any fun x => decl.fvarId == x.fvarId! then
|
||||
return newToRevert.push decl.toExpr
|
||||
else if (← findLocalDeclDependsOn decl (newToRevert.any fun x => x.fvarId! == ·)) then
|
||||
else if (← findLocalDeclDependsOn decl (newToRevert.any fun x => x.fvarId! == ·) (generalizeNondepLet := generalizeNondepLet)) then
|
||||
return newToRevert.push decl.toExpr
|
||||
else
|
||||
return newToRevert
|
||||
|
|
@ -1076,12 +1102,16 @@ mutual
|
|||
let type := type.headBeta
|
||||
let type ← abstractRangeAux xs i type
|
||||
return Lean.mkForall n bi type e
|
||||
| LocalDecl.ldecl _ _ n type value nonDep _ =>
|
||||
| LocalDecl.ldecl (nondep := true) _ _ n type _ _ =>
|
||||
let type := type.headBeta
|
||||
let type ← abstractRangeAux xs i type
|
||||
return Lean.mkForall n .default type e
|
||||
| LocalDecl.ldecl (nondep := false) _ _ n type value _ =>
|
||||
if !usedLetOnly || e.hasLooseBVar 0 then
|
||||
let type := type.headBeta
|
||||
let type ← abstractRangeAux xs i type
|
||||
let value ← abstractRangeAux xs i value
|
||||
let e := mkLet n type value e nonDep
|
||||
let e := mkLet n type value e false
|
||||
match kind with
|
||||
| MetavarKind.syntheticOpaque =>
|
||||
-- See "Gruesome details" section in the beginning of the file
|
||||
|
|
@ -1233,16 +1263,25 @@ private def mkLambda' (x : Name) (bi : BinderInfo) (t : Expr) (b : Expr) (etaRed
|
|||
mkLambda x bi t b
|
||||
|
||||
/--
|
||||
Similar to `LocalContext.mkBinding`, but handles metavariables correctly.
|
||||
If `usedOnly == true` then `forall` and `lambda` expressions are created only for used variables.
|
||||
If `usedLetOnly == true` then `let` expressions are created only for used (let-) variables. -/
|
||||
def mkBinding (isLambda : Bool) (lctx : LocalContext) (xs : Array Expr) (e : Expr) (usedOnly : Bool) (usedLetOnly : Bool) (etaReduce : Bool) : M Expr := do
|
||||
Similar to `LocalContext.mkBinding`, but handles metavariables correctly.
|
||||
This function trusts that `xs` has all forward dependencies that appear in `e` and that the variables are in order.
|
||||
|
||||
- If `usedOnly := true` then `forall` and `lambda` expressions are created only for used variables.
|
||||
- If `usedLetOnly := true` then `let` expressions are created only for used (let-) variables.
|
||||
- If `generalizeNondepLet := true` then nondependent let variables become `forall` or `lambda` expressions
|
||||
according to the value of `usedOnly`.
|
||||
Generally, `generalizeNondepLet` should be `true` *unless* `mkBinding` is being used when leaving a telescope combinator (like `Meta.lambdaLetTelescope`).
|
||||
This needs to be `true` when making terms that should remain type correct with respect to the same `lctx`;
|
||||
for example, if `e' ← mkBinding true lctx xs e (generalizeNondepLet := true)` and `xs' ← xs.filterM (FVarId.isLetVar · false)`,
|
||||
then one has that `mkAppN e' xs'` is definitionally equal to `e` with respect to `lctx`.
|
||||
**Note:** `generalizeNondepLet := true` is the common case, so `mkBinding` API uses it as the default.
|
||||
-/
|
||||
def mkBinding (isLambda : Bool) (lctx : LocalContext) (xs : Array Expr) (e : Expr) (usedOnly : Bool) (usedLetOnly : Bool) (etaReduce : Bool) (generalizeNondepLet : Bool) : M Expr := do
|
||||
let e ← abstractRange xs xs.size e
|
||||
xs.size.foldRevM (init := e) fun i _ e => do
|
||||
let x := xs[i]
|
||||
if x.isFVar then
|
||||
match lctx.getFVar! x with
|
||||
| LocalDecl.cdecl _ _ n type bi _ =>
|
||||
let handleCDecl (n : Name) (type : Expr) (bi : BinderInfo) : M Expr := do
|
||||
if !usedOnly || e.hasLooseBVar 0 then
|
||||
let type := type.headBeta;
|
||||
let type ← abstractRange xs i type
|
||||
|
|
@ -1252,11 +1291,16 @@ def mkBinding (isLambda : Bool) (lctx : LocalContext) (xs : Array Expr) (e : Exp
|
|||
return Lean.mkForall n bi type e
|
||||
else
|
||||
return e.lowerLooseBVars 1 1
|
||||
| LocalDecl.ldecl _ _ n type value nonDep _ =>
|
||||
if !usedLetOnly || e.hasLooseBVar 0 then
|
||||
match lctx.getFVar! x with
|
||||
| LocalDecl.cdecl _ _ n type bi _ =>
|
||||
handleCDecl n type bi
|
||||
| LocalDecl.ldecl _ _ n type value nondep _ =>
|
||||
if generalizeNondepLet && nondep then
|
||||
handleCDecl n type .default
|
||||
else if !usedLetOnly || e.hasLooseBVar 0 then
|
||||
let type ← abstractRange xs i type
|
||||
let value ← abstractRange xs i value
|
||||
return mkLet n type value e nonDep
|
||||
return mkLet n type value e nondep
|
||||
else
|
||||
return e.lowerLooseBVars 1 1
|
||||
else
|
||||
|
|
@ -1283,21 +1327,21 @@ def elimMVarDeps (xs : Array Expr) (e : Expr) (preserveOrder : Bool) : MkBinding
|
|||
def revert (xs : Array Expr) (mvarId : MVarId) (preserveOrder : Bool) : MkBindingM (Expr × Array Expr) := fun ctx =>
|
||||
MkBinding.revert xs mvarId { preserveOrder, mainModule := ctx.mainModule }
|
||||
|
||||
def mkBinding (isLambda : Bool) (xs : Array Expr) (e : Expr) (usedOnly : Bool := false) (usedLetOnly : Bool := true) (etaReduce := false) (binderInfoForMVars := BinderInfo.implicit) : MkBindingM Expr := fun ctx =>
|
||||
def mkBinding (isLambda : Bool) (xs : Array Expr) (e : Expr) (usedOnly : Bool := false) (usedLetOnly : Bool := true) (etaReduce := false) (generalizeNondepLet := true) (binderInfoForMVars := BinderInfo.implicit) : MkBindingM Expr := fun ctx =>
|
||||
let mvarIdsToAbstract := xs.foldl (init := {}) fun s x => if x.isMVar then s.insert x.mvarId! else s
|
||||
MkBinding.mkBinding isLambda ctx.lctx xs e usedOnly usedLetOnly etaReduce { preserveOrder := false, binderInfoForMVars, mvarIdsToAbstract, mainModule := ctx.mainModule }
|
||||
MkBinding.mkBinding isLambda ctx.lctx xs e usedOnly usedLetOnly etaReduce generalizeNondepLet { preserveOrder := false, binderInfoForMVars, mvarIdsToAbstract, mainModule := ctx.mainModule }
|
||||
|
||||
@[inline] def mkLambda (xs : Array Expr) (e : Expr) (usedOnly : Bool := false) (usedLetOnly : Bool := true) (etaReduce := false) (binderInfoForMVars := BinderInfo.implicit) : MkBindingM Expr :=
|
||||
mkBinding (isLambda := true) xs e usedOnly usedLetOnly etaReduce binderInfoForMVars
|
||||
@[inline] def mkLambda (xs : Array Expr) (e : Expr) (usedOnly : Bool := false) (usedLetOnly : Bool := true) (etaReduce := false) (generalizeNondepLet := true) (binderInfoForMVars := BinderInfo.implicit) : MkBindingM Expr :=
|
||||
mkBinding (isLambda := true) xs e usedOnly usedLetOnly etaReduce generalizeNondepLet binderInfoForMVars
|
||||
|
||||
@[inline] def mkForall (xs : Array Expr) (e : Expr) (usedOnly : Bool := false) (usedLetOnly : Bool := true) (binderInfoForMVars := BinderInfo.implicit) : MkBindingM Expr :=
|
||||
mkBinding (isLambda := false) xs e usedOnly usedLetOnly false binderInfoForMVars
|
||||
@[inline] def mkForall (xs : Array Expr) (e : Expr) (usedOnly : Bool := false) (usedLetOnly : Bool := true) (generalizeNondepLet := true) (binderInfoForMVars := BinderInfo.implicit) : MkBindingM Expr :=
|
||||
mkBinding (isLambda := false) xs e usedOnly usedLetOnly false generalizeNondepLet binderInfoForMVars
|
||||
|
||||
@[inline] def abstractRange (e : Expr) (n : Nat) (xs : Array Expr) : MkBindingM Expr := fun ctx =>
|
||||
MkBinding.abstractRange xs n e { preserveOrder := false, mainModule := ctx.mainModule }
|
||||
|
||||
@[inline] def collectForwardDeps (toRevert : Array Expr) (preserveOrder : Bool) : MkBindingM (Array Expr) := fun ctx =>
|
||||
MkBinding.collectForwardDeps ctx.lctx toRevert { preserveOrder, mainModule := ctx.mainModule }
|
||||
@[inline] def collectForwardDeps (toRevert : Array Expr) (preserveOrder : Bool) (generalizeNondepLet := true) : MkBindingM (Array Expr) := fun ctx =>
|
||||
MkBinding.collectForwardDeps ctx.lctx toRevert generalizeNondepLet { preserveOrder, mainModule := ctx.mainModule }
|
||||
|
||||
/--
|
||||
`isWellFormed lctx e` returns true iff
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ variable {α} (ctx : Context α) (builtin : Bool) (force : Bool) in
|
|||
partial def compileParserExpr (e : Expr) : MetaM Expr := do
|
||||
let e ← whnfCore e
|
||||
match e with
|
||||
| .lam .. => lambdaLetTelescope e fun xs b => compileParserExpr b >>= mkLambdaFVars xs
|
||||
| .lam .. => mapLambdaLetTelescope e fun _ b => compileParserExpr b
|
||||
| .fvar .. => return e
|
||||
| _ => do
|
||||
let fn := e.getAppFn
|
||||
|
|
|
|||
|
|
@ -1031,7 +1031,7 @@ def delabLetE : Delab := do
|
|||
let Expr.letE n t v b nondep ← getExpr | unreachable!
|
||||
let n ← getUnusedName n b
|
||||
let stxV ← descend v 1 delab
|
||||
let (stxN, stxB) ← withLetDecl n t v fun fvar => do
|
||||
let (stxN, stxB) ← withLetDecl n t v (nondep := nondep) fun fvar => do
|
||||
let b := b.instantiate1 fvar
|
||||
return (← mkAnnotatedIdent n fvar, ← descend b 2 delab)
|
||||
if ← getPPOption getPPLetVarTypes <||> getPPOption getPPAnalysisLetVarType then
|
||||
|
|
@ -1305,7 +1305,7 @@ partial def delabDoElems : DelabM (List Syntax) := do
|
|||
let n ← getUnusedName n b
|
||||
let stxT ← descend t 0 delab
|
||||
let stxV ← descend v 1 delab
|
||||
withLetDecl n t v fun fvar =>
|
||||
withLetDecl n t v (nondep := nondep) fun fvar =>
|
||||
let b := b.instantiate1 fvar
|
||||
descend b 2 $
|
||||
if nondep then
|
||||
|
|
|
|||
|
|
@ -121,8 +121,8 @@ def withLetValue (x : m α) : m α := do
|
|||
descend v 1 x
|
||||
|
||||
def withLetBody (x : m α) : m α := do
|
||||
let Expr.letE n t v b _ ← getExpr | unreachable!
|
||||
Meta.withLetDecl n t v fun fvar =>
|
||||
let Expr.letE n t v b nondep ← getExpr | unreachable!
|
||||
Meta.withLetDecl n t v (nondep := nondep) fun fvar =>
|
||||
let b := b.instantiate1 fvar
|
||||
descend b 2 x
|
||||
|
||||
|
|
|
|||
|
|
@ -176,7 +176,8 @@ def goalToInteractive (mvarId : MVarId) : MetaM InteractiveGoal := do
|
|||
continue
|
||||
else
|
||||
match localDecl with
|
||||
| LocalDecl.cdecl _index fvarId varName type _ _ =>
|
||||
| LocalDecl.cdecl _index fvarId varName type ..
|
||||
| LocalDecl.ldecl _index fvarId varName type (nondep := true) .. =>
|
||||
-- We rely on the fact that `withGoalCtx` runs `LocalContext.sanitizeNames`,
|
||||
-- so the `userName`s of local hypotheses are already pretty-printed
|
||||
-- and it suffices to simply `toString` them.
|
||||
|
|
@ -188,7 +189,7 @@ def goalToInteractive (mvarId : MVarId) : MetaM InteractiveGoal := do
|
|||
hyps ← pushPending varNames prevType? hyps
|
||||
varNames := #[(varName, fvarId)]
|
||||
prevType? := some type
|
||||
| LocalDecl.ldecl _index fvarId varName type val _ _ => do
|
||||
| LocalDecl.ldecl _index fvarId varName type val (nondep := false) .. => do
|
||||
let varName := toString varName
|
||||
hyps ← pushPending varNames prevType? hyps
|
||||
let type ← instantiateMVars type
|
||||
|
|
|
|||
|
|
@ -1,34 +1,34 @@
|
|||
import Lean.Elab.Command
|
||||
|
||||
/-!
|
||||
# Tests for `let_fun x := v; b` notation
|
||||
# Tests for `have x := v; b` notation
|
||||
-/
|
||||
|
||||
/-!
|
||||
Checks that types can be inferred and that default instances work with `let_fun`.
|
||||
Checks that types can be inferred and that default instances work with `have`.
|
||||
-/
|
||||
#check
|
||||
let_fun f x := x * 2
|
||||
let_fun x := 1
|
||||
let_fun y := x + 1
|
||||
have f x := x * 2
|
||||
have x := 1
|
||||
have y := x + 1
|
||||
f (y + x)
|
||||
|
||||
/-!
|
||||
Checks that `simp` can do zeta reduction of `let_fun`s
|
||||
Checks that `simp` can do zeta reduction of `have`s
|
||||
-/
|
||||
example (a b : Nat) (h1 : a = 0) (h2 : b = 0) : (let_fun x := a + 1; x + x) > b := by
|
||||
example (a b : Nat) (h1 : a = 0) (h2 : b = 0) : (have x := a + 1; x + x) > b := by
|
||||
simp (config := { zeta := false }) [h1]
|
||||
trace_state
|
||||
simp (config := { decide := true }) [h2]
|
||||
|
||||
/-!
|
||||
Checks that the underlying encoding for `let_fun` can be overapplied.
|
||||
This still pretty prints with `let_fun` notation.
|
||||
Checks that the underlying encoding for `have` can be overapplied.
|
||||
This still pretty prints with `have` notation.
|
||||
-/
|
||||
#check (show Nat → Nat from id) 1
|
||||
|
||||
/-!
|
||||
Checks that zeta reduction still occurs even if the `let_fun` is applied to an argument.
|
||||
Checks that zeta reduction still occurs even if the `have` is applied to an argument.
|
||||
-/
|
||||
example (a b : Nat) (h : a > b) : (show Nat → Nat from id) a > b := by
|
||||
simp
|
||||
|
|
@ -36,23 +36,23 @@ example (a b : Nat) (h : a > b) : (show Nat → Nat from id) a > b := by
|
|||
exact h
|
||||
|
||||
/-!
|
||||
Checks that the type of a `let_fun` can depend on the value.
|
||||
Checks that the type of a `have` can depend on the value.
|
||||
-/
|
||||
#check let_fun n := 5
|
||||
#check have n := 5
|
||||
(⟨[], Nat.zero_le n⟩ : { as : List Bool // as.length ≤ n })
|
||||
|
||||
/-!
|
||||
Check that `let_fun` is reducible.
|
||||
Check that `have` is reducible.
|
||||
-/
|
||||
#check (rfl : (let_fun n := 5; n) = 5)
|
||||
#check (rfl : (have n := 5; n) = 5)
|
||||
|
||||
/-!
|
||||
Exercise `isDefEqQuick` for `let_fun`.
|
||||
Exercise `isDefEqQuick` for `have`.
|
||||
-/
|
||||
#check (rfl : (let_fun _n := 5; 2) = 2)
|
||||
#check (rfl : (have _n := 5; 2) = 2)
|
||||
|
||||
/-!
|
||||
Check that `let_fun` responds to WHNF's `zeta` option.
|
||||
Check that `have` responds to WHNF's `zeta` option.
|
||||
-/
|
||||
|
||||
open Lean Meta Elab Term in
|
||||
|
|
@ -61,5 +61,5 @@ elab "#whnfCore " z?:(&"noZeta")? t:term : command => Command.runTermElabM fun _
|
|||
let e ← withConfig (fun c => { c with zeta := z?.isNone }) <| Meta.whnfCore e
|
||||
logInfo m!"{e}"
|
||||
|
||||
#whnfCore let_fun n := 5; n
|
||||
#whnfCore noZeta let_fun n := 5; n
|
||||
#whnfCore have n := 5; n
|
||||
#whnfCore noZeta have n := 5; n
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ a b : Nat
|
|||
h : a > b
|
||||
⊢ b < a
|
||||
have n := 5;
|
||||
⟨[], ⋯⟩ : { as // as.length ≤ 5 }
|
||||
⟨[], ⋯⟩ : have n := 5;
|
||||
{ as // as.length ≤ n }
|
||||
rfl : (have n := 5;
|
||||
n) =
|
||||
have n := 5;
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ example (f : Nat → Nat) : f x = 0 → f x + 1 = y := by
|
|||
sorry
|
||||
|
||||
example (f : Nat → Nat) : let _ : f x = 0 := sorryAx _ false; f x + 1 = y := by
|
||||
simp (config := { contextual := true, zeta := false })
|
||||
guard_target =ₛ let _ : f x = 0 := sorryAx _ false; 1 = y
|
||||
simp (config := { contextual := true, zeta := false, zetaUnused := false })
|
||||
guard_target =ₛ have _ : f x = 0 := sorryAx _ false; 1 = y
|
||||
sorry
|
||||
|
||||
def overlap : Nat → Nat
|
||||
|
|
@ -46,6 +46,6 @@ example : (if p x then g x else g x + 1) + g x = y := by
|
|||
sorry
|
||||
|
||||
example : (let _ : p x := sorryAx _ false; g x + 1 = y) ↔ g x = y := by
|
||||
simp (config := { zeta := false }) (discharger := assumption)
|
||||
guard_target =ₛ (let _ : p x := sorryAx _ false; x + 1 = y) ↔ g x = y
|
||||
simp (config := { zeta := false, zetaUnused := false }) (discharger := assumption)
|
||||
guard_target =ₛ (have _ : p x := sorryAx _ false; x + 1 = y) ↔ g x = y
|
||||
sorry
|
||||
|
|
|
|||
|
|
@ -16,6 +16,21 @@ example : let x := 22; 0 ≤ x := by
|
|||
trace_state
|
||||
exact Nat.zero_le _
|
||||
|
||||
/-!
|
||||
Check that `clear_value` preserves the order of the local context.
|
||||
-/
|
||||
/--
|
||||
trace: x : Nat
|
||||
y : Nat := 23
|
||||
⊢ True
|
||||
-/
|
||||
#guard_msgs in
|
||||
example : let _x := 22; let _y := 23; True := by
|
||||
intro x y
|
||||
clear_value x
|
||||
trace_state
|
||||
trivial
|
||||
|
||||
/-!
|
||||
Test `*` mode.
|
||||
-/
|
||||
|
|
|
|||
|
|
@ -109,8 +109,8 @@ def let_tailrec : Nat → Nat
|
|||
termination_by n => n
|
||||
|
||||
/--
|
||||
info: let_tailrec.induct (motive : Nat → Prop) (case1 : motive 0) (case2 : ∀ (n : Nat), motive n → motive n.succ) (a✝ : Nat) :
|
||||
motive a✝
|
||||
info: let_tailrec.induct (motive : Nat → Prop) (case1 : motive 0) (case2 : ∀ (n : Nat), n < n + 1 → motive n → motive n.succ)
|
||||
(a✝ : Nat) : motive a✝
|
||||
-/
|
||||
#guard_msgs in
|
||||
#check let_tailrec.induct
|
||||
|
|
@ -545,7 +545,7 @@ termination_by xs => xs
|
|||
|
||||
/--
|
||||
info: LetFun.bar.induct.{u_1} {α : Type u_1} (x : α) (motive : List α → Prop) (case1 : motive [])
|
||||
(case2 : ∀ (_y : α) (ys : List α) (this : Nat), motive ys → motive (_y :: ys)) (a✝ : List α) : motive a✝
|
||||
(case2 : ∀ (_y : α) (ys : List α), motive ys → motive (_y :: ys)) (a✝ : List α) : motive a✝
|
||||
-/
|
||||
#guard_msgs in
|
||||
#check bar.induct
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import Lean
|
|||
|
||||
This file exercises the Lean/C++ interface to make sure that the `nondep` field
|
||||
is successfully part of the data model.
|
||||
|
||||
It also tests the metaprogramming API.
|
||||
-/
|
||||
|
||||
open Lean
|
||||
|
|
@ -83,3 +85,22 @@ info: Lean.Expr.letE `n (Lean.Expr.const `Nat []) (Lean.Expr.const `Nat.zero [])
|
|||
let m ← Meta.mkFreshExprMVar none
|
||||
m.mvarId!.assign (mkConst ``Unit)
|
||||
Lean.instantiateMVars (Lean.mkLet `n (mkConst ``Nat) (mkConst ``Nat.zero) m true)
|
||||
|
||||
namespace TestLambdaLetTelescope
|
||||
/-!
|
||||
Check that `lambdaLetTelescope` consumes `haves`. Also checks that `preserveNondepLet := false` turns `have`s into `let`s.
|
||||
-/
|
||||
def c : Nat → Nat → Bool := fun x => have y := 1; fun z => x == y + z
|
||||
/--
|
||||
info: #[false, true, false]
|
||||
#[false, false, false]
|
||||
-/
|
||||
#guard_msgs in
|
||||
open Lean Meta in
|
||||
run_meta do
|
||||
let decl ← getConstInfo ``c
|
||||
lambdaLetTelescope decl.value! fun xs _ => do
|
||||
IO.println <| ← xs.mapM fun x => return (← x.fvarId!.getDecl).isNondep
|
||||
lambdaLetTelescope decl.value! (preserveNondepLet := false) fun xs _ => do
|
||||
IO.println <| ← xs.mapM fun x => return (← x.fvarId!.getDecl).isNondep
|
||||
end TestLambdaLetTelescope
|
||||
|
|
|
|||
|
|
@ -19,6 +19,27 @@ def Tree.map (f : α → β) (t : Tree α) : Tree β :=
|
|||
termination_by t
|
||||
decreasing_by trace_state; cases t; decreasing_tactic
|
||||
|
||||
/-!
|
||||
Checking that the attaches make their way through `let`s.
|
||||
-/
|
||||
/--
|
||||
trace: α : Type u_1
|
||||
t : Tree α
|
||||
cs : List (Tree α) := t.cs
|
||||
t' : Tree α
|
||||
h✝ : t' ∈ cs
|
||||
⊢ sizeOf t' < sizeOf t
|
||||
-/
|
||||
#guard_msgs(trace) in
|
||||
def Tree.map' (f : α → β) (t : Tree α) : Tree β :=
|
||||
have n := 22
|
||||
let v := t.val
|
||||
let cs := t.cs
|
||||
have : n = n := rfl
|
||||
⟨f v, cs.map (fun t' => t'.map' f)⟩
|
||||
termination_by t
|
||||
decreasing_by trace_state; cases t; decreasing_tactic
|
||||
|
||||
/--
|
||||
info: equations:
|
||||
theorem Tree.map.eq_1.{u_1, u_2} : ∀ {α : Type u_1} {β : Type u_2} (f : α → β) (t : Tree α),
|
||||
|
|
|
|||
|
|
@ -19,5 +19,5 @@ example (h : z = 9) : let x := 5; let y := 4; x + y = z := by
|
|||
example (h : z = 9) : let x := 5; let y := 4; x + y = z := by
|
||||
intro x
|
||||
simp (config := { zetaDelta := true, zeta := false })
|
||||
guard_target =ₛlet y := 4; 5 + y = z
|
||||
guard_target =ₛ have y := 4; 5 + y = z
|
||||
rw [h]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
x : Nat
|
||||
h : f (f x) = x
|
||||
⊢ (let y := x * x;
|
||||
⊢ (have y := x * x;
|
||||
if True then 1 else y + 1) =
|
||||
1
|
||||
theorem ex1 : ∀ (x : Nat),
|
||||
|
|
@ -19,7 +19,7 @@ fun x h =>
|
|||
x z : Nat
|
||||
h : f (f x) = x
|
||||
h' : z = x
|
||||
⊢ (let y := x;
|
||||
⊢ (have y := x;
|
||||
y) =
|
||||
z
|
||||
theorem ex2 : ∀ (x z : Nat),
|
||||
|
|
@ -37,7 +37,7 @@ x z : Nat
|
|||
id
|
||||
p : Prop
|
||||
h : p
|
||||
⊢ (let n := 10;
|
||||
⊢ (have n := 10;
|
||||
fun x => True) =
|
||||
fun z => p
|
||||
theorem ex4 : ∀ (p : Prop),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue