The idea is to make clear that the field `posponed` is transient state. It is only used during `isDefEq`. The refactoring was motivated by a bug I found where the `posponed` constraints were not being handled correctly. For example, the `check (e : Expr)` method was returning `true`, but leaving pending universe constraints at `postponed`. cc @Kha
123 lines
4.1 KiB
Text
123 lines
4.1 KiB
Text
/-
|
||
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
||
Released under Apache 2.0 license as described in the file LICENSE.
|
||
Authors: Leonardo de Moura
|
||
-/
|
||
import Lean.Data.LBool
|
||
import Lean.Meta.InferType
|
||
|
||
namespace Lean
|
||
namespace Meta
|
||
|
||
partial def evalNat : Expr → Option Nat
|
||
| Expr.lit (Literal.natVal n) _ => pure n
|
||
| Expr.mdata _ e _ => evalNat e
|
||
| Expr.const `Nat.zero _ _ => pure 0
|
||
| e@(Expr.app _ a _) =>
|
||
let fn := e.getAppFn;
|
||
match fn with
|
||
| Expr.const c _ _ =>
|
||
let nargs := e.getAppNumArgs;
|
||
if c == `Nat.succ && nargs == 1 then do
|
||
v ← evalNat a; pure $ v+1
|
||
else if c == `Nat.add && nargs == 2 then do
|
||
v₁ ← evalNat (e.getArg! 0);
|
||
v₂ ← evalNat (e.getArg! 1);
|
||
pure $ v₁ + v₂
|
||
else if c == `Nat.sub && nargs == 2 then do
|
||
v₁ ← evalNat (e.getArg! 0);
|
||
v₂ ← evalNat (e.getArg! 1);
|
||
pure $ v₁ - v₂
|
||
else if c == `Nat.mul && nargs == 2 then do
|
||
v₁ ← evalNat (e.getArg! 0);
|
||
v₂ ← evalNat (e.getArg! 1);
|
||
pure $ v₁ * v₂
|
||
else if c == `HasAdd.add && nargs == 4 then do
|
||
v₁ ← evalNat (e.getArg! 2);
|
||
v₂ ← evalNat (e.getArg! 3);
|
||
pure $ v₁ + v₂
|
||
else if c == `HasAdd.sub && nargs == 4 then do
|
||
v₁ ← evalNat (e.getArg! 2);
|
||
v₂ ← evalNat (e.getArg! 3);
|
||
pure $ v₁ - v₂
|
||
else if c == `HasAdd.mul && nargs == 4 then do
|
||
v₁ ← evalNat (e.getArg! 2);
|
||
v₂ ← evalNat (e.getArg! 3);
|
||
pure $ v₁ * v₂
|
||
else if c == `HasOfNat.ofNat && nargs == 3 then
|
||
evalNat (e.getArg! 2)
|
||
else
|
||
none
|
||
| _ => none
|
||
| _ => none
|
||
|
||
/- Quick function for converting `e` into `s + k` s.t. `e` is definitionally equal to `Nat.add s k`. -/
|
||
private partial def getOffsetAux : Expr → Bool → Option (Expr × Nat)
|
||
| e@(Expr.app _ a _), top =>
|
||
let fn := e.getAppFn;
|
||
match fn with
|
||
| Expr.const c _ _ =>
|
||
let nargs := e.getAppNumArgs;
|
||
if c == `Nat.succ && nargs == 1 then do
|
||
(s, k) ← getOffsetAux a false;
|
||
pure (s, k+1)
|
||
else if c == `Nat.add && nargs == 2 then do
|
||
v ← evalNat (e.getArg! 1);
|
||
(s, k) ← getOffsetAux (e.getArg! 0) false;
|
||
pure (s, k+v)
|
||
else if c == `HasAdd.add && nargs == 4 then do
|
||
v ← evalNat (e.getArg! 3);
|
||
(s, k) ← getOffsetAux (e.getArg! 2) false;
|
||
pure (s, k+v)
|
||
else if top then none else pure (e, 0)
|
||
| _ => if top then none else pure (e, 0)
|
||
| e, top => if top then none else pure (e, 0)
|
||
|
||
private def getOffset (e : Expr) : Option (Expr × Nat) :=
|
||
getOffsetAux e true
|
||
|
||
private partial def isOffset : Expr → Option (Expr × Nat)
|
||
| e@(Expr.app _ a _) =>
|
||
let fn := e.getAppFn;
|
||
match fn with
|
||
| Expr.const c _ _ =>
|
||
let nargs := e.getAppNumArgs;
|
||
if (c == `Nat.succ && nargs == 1) || (c == `Nat.add && nargs == 2) || (c == `HasAdd.add && nargs == 4) then
|
||
getOffset e
|
||
else none
|
||
| _ => none
|
||
| _ => none
|
||
|
||
private def isNatZero (e : Expr) : Bool :=
|
||
match evalNat e with
|
||
| some v => v == 0
|
||
| _ => false
|
||
|
||
private def mkOffset (e : Expr) (offset : Nat) : Expr :=
|
||
if offset == 0 then e
|
||
else if isNatZero e then mkNatLit offset
|
||
else mkAppB (mkConst `Nat.add) e (mkNatLit offset)
|
||
|
||
def isDefEqOffset (s t : Expr) : DefEqM LBool :=
|
||
let isDefEq (s t) : DefEqM LBool := toLBoolM $ Meta.isExprDefEqAux s t;
|
||
match isOffset s with
|
||
| some (s, k₁) => match isOffset t with
|
||
| some (t, k₂) => -- s+k₁ =?= t+k₂
|
||
if k₁ == k₂ then isDefEq s t
|
||
else if k₁ < k₂ then isDefEq s (mkOffset t (k₂ - k₁))
|
||
else isDefEq (mkOffset s (k₁ - k₂)) t
|
||
| none => match evalNat t with
|
||
| some v₂ => -- s+k₁ =?= v₂
|
||
if v₂ ≥ k₁ then isDefEq s (mkNatLit $ v₂ - k₁) else pure LBool.false
|
||
| none => pure LBool.undef
|
||
| none => match evalNat s with
|
||
| some v₁ => match isOffset t with
|
||
| some (t, k₂) => -- v₁ =?= t+k₂
|
||
if v₁ ≥ k₂ then isDefEq (mkNatLit $ v₁ - k₂) t else pure LBool.false
|
||
| none => match evalNat t with
|
||
| some v₂ => pure (v₁ == v₂).toLBool -- v₁ =?= v₂
|
||
| none => pure LBool.undef
|
||
| none => pure LBool.undef
|
||
|
||
end Meta
|
||
end Lean
|