This PR adds the `nondep` field of `Expr.letE` to the C++ data model. Previously this field has been unused, and in followup PRs the elaborator will use it to encode `have` expressions (non-dependent `let`s). The kernel does not verify that `nondep` is correctly applied during typechecking. The `letE` delaborator now prints `have`s when `nondep` is true, though `have` still elaborates as `letFun` for now. Breaking change: `Expr.updateLet!` is renamed to `Expr.updateLetE!`. This PR also fixes a bug in `Expr.letFun?` and `Expr.letFunAppArgs?` when the body is not a lambda. In any case, these functions will be removed once the `Expr.letE (nondep := true)` encoding of `have` expressions is complete.
65 lines
2.7 KiB
Text
65 lines
2.7 KiB
Text
/-
|
|
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
Author: Leonardo de Moura
|
|
-/
|
|
prelude
|
|
import Lean.HeadIndex
|
|
import Lean.Meta.Basic
|
|
|
|
namespace Lean.Meta
|
|
|
|
/--
|
|
Abstract occurrences of `p` in `e`. We detect subterms equivalent to `p` using key-matching.
|
|
That is, only perform `isDefEq` tests when the head symbol of substerm is equivalent to head symbol of `p`.
|
|
|
|
By default, all occurrences are abstracted,
|
|
but this behavior can be controlled using the `occs` parameter.
|
|
|
|
All matches of `p` in `e` are considered for occurrences,
|
|
but for each match that is included by the `occs` parameter,
|
|
metavariables appearing in `p` (or `e`) may become instantiated,
|
|
affecting the possibility of subsequent matches.
|
|
For matches that are not included in the `occs` parameter, the metavariable context is rolled back
|
|
to prevent blocking subsequent matches which require different instantiations.
|
|
-/
|
|
def kabstract (e : Expr) (p : Expr) (occs : Occurrences := .all) : MetaM Expr := do
|
|
let e ← instantiateMVars e
|
|
if p.isFVar && occs == Occurrences.all then
|
|
return e.abstract #[p] -- Easy case
|
|
else
|
|
let pHeadIdx := p.toHeadIndex
|
|
let pNumArgs := p.headNumArgs
|
|
let rec visit (e : Expr) (offset : Nat) : StateRefT Nat MetaM Expr := do
|
|
let visitChildren : Unit → StateRefT Nat MetaM Expr := fun _ => do
|
|
match e with
|
|
| .app f a => return e.updateApp! (← visit f offset) (← visit a offset)
|
|
| .mdata _ b => return e.updateMData! (← visit b offset)
|
|
| .proj _ _ b => return e.updateProj! (← visit b offset)
|
|
| .letE _ t v b _ => return e.updateLetE! (← visit t offset) (← visit v offset) (← visit b (offset+1))
|
|
| .lam _ d b _ => return e.updateLambdaE! (← visit d offset) (← visit b (offset+1))
|
|
| .forallE _ d b _ => return e.updateForallE! (← visit d offset) (← visit b (offset+1))
|
|
| e => return e
|
|
if e.hasLooseBVars then
|
|
visitChildren ()
|
|
else if e.toHeadIndex != pHeadIdx || e.headNumArgs != pNumArgs then
|
|
visitChildren ()
|
|
else
|
|
-- We save the metavariable context here,
|
|
-- so that it can be rolled back unless `occs.contains i`.
|
|
let mctx ← getMCtx
|
|
if (← isDefEq e p) then
|
|
let i ← get
|
|
set (i+1)
|
|
if occs.contains i then
|
|
return mkBVar offset
|
|
else
|
|
-- Revert the metavariable context,
|
|
-- so that other matches are still possible.
|
|
setMCtx mctx
|
|
visitChildren ()
|
|
else
|
|
visitChildren ()
|
|
visit e 0 |>.run' 1
|
|
|
|
end Lean.Meta
|