lean4-htt/library/Init/Lean/AbstractMetavarContext.lean
2019-10-26 07:50:25 -07:00

220 lines
9.8 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Init.Lean.LocalContext
namespace Lean
/--
A delayed assignment for a metavariable `?m`. It represents an assignment of the form
`?m := (fun fvars => val)`. The local context `lctx` provides the declarations for `fvars`.
Note that `fvars` may not be defined in the local context for `?m`. -/
structure DelayedMVarAssignment :=
(lctx : LocalContext)
(fvars : Array Expr)
(val : Expr)
/--
We can `TypeContext` functions with different implementations of
metavariable contexts. For elaboration and tactic framework, we
use `MetavarContext`. During type class resolution and simplifier,
we use temporary metavariables which are cheaper to create and
dispose. Moreover, given a particular task using temporary
metavariables (e.g., matching the left-hand side of an equation),
we assume all metavariables share the same local context.
If `sharedContext == false`, then support for "delayed assignments" is
required. -/
class AbstractMetavarContext (σ : Type) :=
(empty : σ)
(isLevelMVar {} : Level → Bool)
(getLevelAssignment (mctx : σ) (mvarId : Name) : Option Level)
(assignLevel (mctx : σ) (mvarId : Name) (val : Level) : σ)
(isExprMVar {} : Expr → Bool)
(getExprAssignment (mctx : σ) (mvarId : Name) : Option Expr)
(assignExpr (mctx : σ) (mvarId : Name) (val : Expr) : σ)
(getExprMVarLCtx (mctx : σ) (mvarId : Name) : Option LocalContext)
(getExprMVarType (mctx : σ) (mvarId : Name) : Option Expr)
(sharedContext : Bool)
(assignDelayed (mctx : σ) (mvarId : Name) (lctx : LocalContext) (fvars : Array Expr) (val : Expr) : σ)
(getDelayedAssignment (mctx : σ) (mvarId : Name) : Option DelayedMVarAssignment)
(eraseDelayed (mctx : σ) (mvarId : Name) : σ)
namespace AbstractMetavarContext
variables {σ : Type}
@[inline] def isLevelAssigned [AbstractMetavarContext σ] (mctx : σ) (mvarId : Name) : Bool :=
(getLevelAssignment mctx mvarId).isSome
@[inline] def isExprAssigned [AbstractMetavarContext σ] (mctx : σ) (mvarId : Name) : Bool :=
(getExprAssignment mctx mvarId).isSome
def hasAssignedLevelMVar [AbstractMetavarContext σ] (mctx : σ) : Level → Bool
| Level.succ lvl => lvl.hasMVar && hasAssignedLevelMVar lvl
| Level.max lvl₁ lvl₂ => (lvl₁.hasMVar && hasAssignedLevelMVar lvl₁) || (lvl₂.hasMVar && hasAssignedLevelMVar lvl₂)
| Level.imax lvl₁ lvl₂ => (lvl₁.hasMVar && hasAssignedLevelMVar lvl₁) || (lvl₂.hasMVar && hasAssignedLevelMVar lvl₂)
| Level.mvar mvarId => isLevelAssigned mctx mvarId
| Level.zero => false
| Level.param _ => false
/-- Return `true` iff expression contains assigned (level/expr) metavariables -/
def hasAssignedMVar [AbstractMetavarContext σ] (mctx : σ) : Expr → Bool
| Expr.const _ lvls => lvls.any (hasAssignedLevelMVar mctx)
| Expr.sort lvl => hasAssignedLevelMVar mctx lvl
| Expr.app f a => (f.hasMVar && hasAssignedMVar f) || (a.hasMVar && hasAssignedMVar a)
| Expr.letE _ t v b => (t.hasMVar && hasAssignedMVar t) || (v.hasMVar && hasAssignedMVar v) || (b.hasMVar && hasAssignedMVar b)
| Expr.forallE _ _ d b => (d.hasMVar && hasAssignedMVar d) || (b.hasMVar && hasAssignedMVar b)
| Expr.lam _ _ d b => (d.hasMVar && hasAssignedMVar d) || (b.hasMVar && hasAssignedMVar b)
| Expr.fvar _ => false
| Expr.bvar _ => false
| Expr.lit _ => false
| Expr.mdata _ e => e.hasMVar && hasAssignedMVar e
| Expr.proj _ _ e => e.hasMVar && hasAssignedMVar e
| Expr.mvar mvarId => isExprAssigned mctx mvarId
partial def instantiateLevelMVars [AbstractMetavarContext σ] : Level → State σ Level
| lvl@(Level.succ lvl₁) => do lvl₁ ← instantiateLevelMVars lvl₁; pure (Level.updateSucc! lvl lvl₁)
| lvl@(Level.max lvl₁ lvl₂) => do lvl₁ ← instantiateLevelMVars lvl₁; lvl₂ ← instantiateLevelMVars lvl₂; pure (Level.updateMax! lvl lvl₁ lvl₂)
| lvl@(Level.imax lvl₁ lvl₂) => do lvl₁ ← instantiateLevelMVars lvl₁; lvl₂ ← instantiateLevelMVars lvl₂; pure (Level.updateIMax! lvl lvl₁ lvl₂)
| lvl@(Level.mvar mvarId) => do
mctx ← get;
match getLevelAssignment mctx mvarId with
| some newLvl =>
if !newLvl.hasMVar then pure newLvl
else do
newLvl' ← instantiateLevelMVars newLvl;
modify $ fun mctx => assignLevel mctx mvarId newLvl';
pure newLvl'
| none => pure lvl
| lvl => pure lvl
namespace InstantiateExprMVars
/-- Auxiliary structure for instantiating metavariables in `Expr`s. -/
structure InstState (σ : Type) :=
(mctx : σ)
(cache : ExprMap Expr := {})
abbrev M (σ : Type) := State (InstState σ)
@[inline] def instantiateLevelMVars [AbstractMetavarContext σ] (lvl : Level) : M σ Level :=
adaptState
(fun (s : InstState σ) => (s.mctx, { mctx := empty σ, .. s }))
(fun (mctx : σ) (s : InstState σ) => { mctx := mctx, .. s })
(AbstractMetavarContext.instantiateLevelMVars lvl : State σ Level)
@[inline] def checkCache (e : Expr) (f : Expr → M σ Expr) : M σ Expr :=
do s ← get;
match s.cache.find e with
| some r => pure r
| none => do
r ← f e;
modify $ fun s => { cache := s.cache.insert e r, .. s };
pure r
@[inline] def visit (f : Expr → M σ Expr) (e : Expr) : M σ Expr :=
if !e.hasMVar then pure e else checkCache e f
@[inline] def getMCtx : M σ σ :=
do s ← get; pure s.mctx
/--
Auxiliary function for `instantiateDelayed`.
`instantiateDelayed main lctx fvars i body` is used to create `fun fvars[0, i) => body`.
It fails if one of variable declarations in `fvars` still contains unassigned metavariables.
Pre: all expressions in `fvars` are `Expr.fvar`, and `lctx` contains their declarations. -/
@[specialize] def instantiateDelayedAux [AbstractMetavarContext σ] (main : Expr → M σ Expr) (lctx : LocalContext) (fvars : Array Expr)
: Nat → Expr → M σ (Option Expr)
| 0, b => pure b
| i+1, b => do
let fvar := fvars.get! i;
match lctx.findFVar fvar with
| none => panic! "unknown local declaration"
| some (LocalDecl.cdecl _ _ n ty bi) => do
ty ← visit main ty;
if ty.hasMVar then pure none
else instantiateDelayedAux i (Expr.lam n bi (ty.abstractRange i fvars) b)
| some (LocalDecl.ldecl _ _ n ty val) => do
ty ← visit main ty;
if ty.hasMVar then pure none
else do
val ← visit main val;
if val.hasMVar then pure none
else
let ty := ty.abstractRange i fvars;
let val := val.abstractRange i fvars;
instantiateDelayedAux i (Expr.letE n ty val b)
/-- Try to instantiate a delayed assignment. Return `none` (i.e., fail) if assignment still contains variables. -/
@[inline] def instantiateDelayed [AbstractMetavarContext σ] (main : Expr → M σ Expr) (mvarId : Name) : DelayedMVarAssignment → M σ (Option Expr)
| { lctx := lctx, fvars := fvars, val := val } => do
newVal ← visit main val;
let fail : M σ (Option Expr) := do {
/- Join point for updating delayed assignment and failing -/
modify $ fun s => { mctx := assignDelayed s.mctx mvarId lctx fvars newVal, .. s };
pure none
};
if newVal.hasMVar then fail
else do
/- Create `fun fvars => newVal`.
It fails if there is a one of the variable declarations in `fvars` still contain metavariables. -/
newE ← instantiateDelayedAux main lctx fvars fvars.size (newVal.abstract fvars);
match newE with
| none => fail
| some newE => do
/- Succeeded. Thus, replace delayed assignment with a regular assignment. -/
modify $ fun s =>
{ mctx := assignExpr (eraseDelayed s.mctx mvarId) mvarId newE, .. s };
pure (some newE)
/-- instantiateExprMVars main function -/
partial def main [AbstractMetavarContext σ] : Expr → M σ Expr
| e@(Expr.proj _ _ s) => do s ← visit main s; pure (e.updateProj! s)
| e@(Expr.forallE _ _ d b) => do d ← visit main d; b ← visit main b; pure (e.updateForallE! d b)
| e@(Expr.lam _ _ d b) => do d ← visit main d; b ← visit main b; pure (e.updateLambdaE! d b)
| e@(Expr.letE _ t v b) => do t ← visit main t; v ← visit main v; b ← visit main b; pure (e.updateLet! t v b)
| e@(Expr.const _ lvls) => do lvls ← lvls.mmap instantiateLevelMVars; pure (e.updateConst! lvls)
| e@(Expr.sort lvl) => do lvl ← instantiateLevelMVars lvl; pure (e.updateSort! lvl)
| e@(Expr.mdata _ b) => do b ← visit main b; pure (e.updateMData! b)
| e@(Expr.app _ _) => e.withAppRev $ fun f revArgs => do
let wasMVar := f.isMVar;
f ← visit main f;
if wasMVar && f.isLambda then
-- Some of the arguments in revArgs are irrelevant after we beta reduce.
visit main (f.betaRev revArgs)
else do
revArgs ← revArgs.mmap (visit main);
pure (mkAppRev f revArgs)
| e@(Expr.mvar mvarId) => checkCache e $ fun e => do
mctx ← getMCtx;
match getExprAssignment mctx mvarId with
| some newE => do
newE' ← visit main newE;
modify $ fun s => { mctx := assignExpr s.mctx mvarId newE', .. s };
pure newE'
| none =>
/- A delayed assignment can be transformed into a regular assignment
as soon as all metavariables occurring in the assigned value have
been assigned. -/
match getDelayedAssignment mctx mvarId with
| some d => do
newE ← instantiateDelayed main mvarId d;
pure $ newE.getD e
| none => pure e
| e => pure e
end InstantiateExprMVars
def instantiateExprMVars [AbstractMetavarContext σ] (e : Expr) : State σ Expr :=
if !e.hasMVar then pure e
else
adaptState'
(fun mctx => ({ mctx := mctx } : InstantiateExprMVars.InstState σ))
(fun s => s.mctx)
(InstantiateExprMVars.main e : InstantiateExprMVars.M σ Expr)
end AbstractMetavarContext
end Lean