160 lines
5.1 KiB
Text
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
|