lean4-htt/src/Lean/Meta/ACLt.lean
2022-07-02 15:12:05 -07:00

160 lines
5.1 KiB
Text

/-
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
import Lean.Meta.Basic
import Lean.Meta.FunInfo
namespace Lean
def Expr.ctorWeight : Expr → UInt8
| bvar .. => 0
| fvar .. => 1
| mvar .. => 2
| sort .. => 3
| const .. => 4
| lit .. => 5
| mdata .. => 6
| proj .. => 7
| app .. => 8
| lam .. => 9
| forallE .. => 10
| letE .. => 11
namespace Meta
namespace ACLt
mutual
/--
An AC-compatible ordering.
Recall that an AC-compatible ordering if it is monotonic, well-founded, and total.
Both KBO and LPO are AC-compatible. KBO is faster, but we do not cache the weight of
each expression in Lean 4. Even if we did, we would need to have a weight where implicit instace arguments are ignored.
So, we use a LPO-like term ordering.
Remark: this method is used to implement ordered rewriting. We ignore implicit instance
arguments to address an issue reported at issue #972.
Remark: the order is not really total on terms since
- We instance implicit arguments.
- We ignore metadata.
- We ignore universe parameterst at constants.
-/
unsafe def lt (a b : Expr) : MetaM Bool :=
if ptrAddrUnsafe a == ptrAddrUnsafe b then
return false
-- We ignore metadata
else if a.isMData then
lt a.mdataExpr! b
else if b.isMData then
lt a b.mdataExpr!
else
lpo a b
where
ltPair (a₁ a₂ b₁ b₂ : Expr) : MetaM Bool := do
if (← lt a₁ b₁) then
return true
else if (← lt b₁ a₁) then
return false
else
lt a₂ b₂
ltApp (a b : Expr) : MetaM Bool := do
let aFn := a.getAppFn
let bFn := b.getAppFn
if (← lt aFn bFn) then
return true
else if (← lt bFn aFn) then
return false
else
let aArgs := a.getAppArgs
let bArgs := b.getAppArgs
if aArgs.size < bArgs.size then
return true
else if aArgs.size > bArgs.size then
return false
else
let infos := (← getFunInfoNArgs aFn aArgs.size).paramInfo
for i in [:infos.size] do
-- We ignore instance implicit arguments during comparison
if !infos[i]!.isInstImplicit then
if (← lt aArgs[i]! bArgs[i]!) then
return true
else if (← lt bArgs[i]! aArgs[i]!) then
return false
for i in [infos.size:aArgs.size] do
if (← lt aArgs[i]! bArgs[i]!) then
return true
else if (← lt bArgs[i]! aArgs[i]!) then
return false
return false
lexSameCtor (a b : Expr) : MetaM Bool :=
match a with
-- Atomic
| Expr.bvar i .. => return i < b.bvarIdx!
| Expr.fvar id .. => return Name.lt id.name b.fvarId!.name
| Expr.mvar id .. => return Name.lt id.name b.mvarId!.name
| Expr.sort u .. => return Level.normLt u b.sortLevel!
| Expr.const n .. => return Name.lt n b.constName! -- We igore the levels
| Expr.lit v .. => return Literal.lt v b.litValue!
-- Composite
| Expr.proj _ i e .. => if i != b.projIdx! then return i < b.projIdx! else lt e b.projExpr!
| Expr.app .. => ltApp a b
| Expr.lam _ d e .. => ltPair d e b.bindingDomain! b.bindingBody!
| Expr.forallE _ d e .. => ltPair d e b.bindingDomain! b.bindingBody!
| Expr.letE _ _ v e .. => ltPair v e b.letValue! b.letBody!
-- See main function
| Expr.mdata .. => unreachable!
allChildrenLt (a b : Expr) : MetaM Bool :=
match a with
| Expr.proj _ _ e .. => lt e b
| Expr.app .. =>
a.withApp fun f args => do
let infos := (← getFunInfoNArgs f args.size).paramInfo
for i in [:infos.size] do
-- We ignore instance implicit arguments during comparison
if !infos[i]!.isInstImplicit then
if !(← lt args[i]! b) then
return false
for i in [infos.size:args.size] do
if !(← lt args[i]! b) then
return false
return true
| Expr.lam _ d e .. => lt d b <&&> lt e b
| Expr.forallE _ d e .. => lt d b <&&> lt e b
| Expr.letE _ _ v e .. => lt v b <&&> lt e b
| _ => return true
someChildGe (a b : Expr) : MetaM Bool :=
return !(← allChildrenLt a b)
lpo (a b : Expr) : MetaM Bool := do
-- Case 1: `a < b` if for some child `b_i` of `b`, we have `b_i >= a`
if (← someChildGe b a) then
return true
else if a.ctorWeight > b.ctorWeight then
return false
else
-- Case 2: `a < b` if `a.ctorWeight < b.ctorWeight` and for all children `a_i` of `a`, `a_i < b`
-- Case 3: `a < b` if `a` & `b` have the same ctor, and `a` is lexicographically smaller, and for all children `a_i` of a, `a_i < b`
if !(← allChildrenLt a b) then
return false -- Cases 2 and 3 can't be true
else if a.ctorWeight < b.ctorWeight then
return true -- Case 2
else
-- `a.ctorWeight == b.ctorWeight`
lexSameCtor a b -- Case 3
end
end ACLt
@[implementedBy ACLt.lt]
opaque Expr.acLt : Expr → Expr → MetaM Bool
end Lean.Meta