feat: add Sym.simp theorem set attributes (#13018)
This PR adds named theorem sets for `Sym.simp` with associated attributes, following the same pattern as `Meta.simp`'s `register_simp_attr`. - `register_sym_simp_attr my_set` creates a named set with its own `PersistentEnvExtension` and attribute - `@[my_set] theorem ...` adds a rewrite theorem - `@[my_set] def ...` adds equation theorems from the definition - `builtin_initialize symSimpExtension` registers a default `@[sym_simp]` set - `getSymSimpTheorems` / `getSymSimpExtension?` retrieve theorem sets at tactic time New files: - `Sym/Simp/Attr.lean`: attribute logic (`mkSymSimpAttr`, `registerSymSimpAttr`) - `Sym/Simp/RegisterCommand.lean`: `register_sym_simp_attr` macro Tests: - `tests/pkg/sym_simp_attr/`: package test with user-defined set (`my_sym_simp`) - `tests/elab/sym_simp_set.lean`: tests for the builtin `@[sym_simp]` set
This commit is contained in:
parent
4a62d4a79b
commit
973062e4e1
10 changed files with 225 additions and 0 deletions
|
|
@ -23,3 +23,5 @@ public import Lean.Meta.Sym.Simp.Discharger
|
|||
public import Lean.Meta.Sym.Simp.ControlFlow
|
||||
public import Lean.Meta.Sym.Simp.Goal
|
||||
public import Lean.Meta.Sym.Simp.Telescope
|
||||
public import Lean.Meta.Sym.Simp.Attr
|
||||
public import Lean.Meta.Sym.Simp.RegisterCommand
|
||||
|
|
|
|||
74
src/Lean/Meta/Sym/Simp/Attr.lean
Normal file
74
src/Lean/Meta/Sym/Simp/Attr.lean
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/-
|
||||
Copyright (c) 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
module
|
||||
prelude
|
||||
public import Lean.Meta.Sym.Simp.Theorems
|
||||
import Lean.Meta.Tactic.Simp.SimpTheorems -- for ignoreEquations
|
||||
import Lean.Meta.Eqns -- for getEqnsFor?
|
||||
public section
|
||||
namespace Lean.Meta.Sym.Simp
|
||||
|
||||
/--
|
||||
Adds a `Sym.Simp` theorem (an equality) to the given extension.
|
||||
-/
|
||||
def addSymSimpTheorem (ext : SymSimpExtension) (declName : Name) (attrKind : AttributeKind) : MetaM Unit := do
|
||||
let thm ← mkTheoremFromDecl declName
|
||||
ext.add thm attrKind
|
||||
|
||||
/--
|
||||
Creates a `Sym.Simp` attribute for a named theorem set.
|
||||
When a proposition is tagged, it is added as a rewrite theorem.
|
||||
When a definition is tagged, its equation theorems are added.
|
||||
-/
|
||||
def mkSymSimpAttr (attrName : Name) (attrDescr : String) (ext : SymSimpExtension)
|
||||
(ref : Name := by exact decl_name%) : IO Unit :=
|
||||
registerBuiltinAttribute {
|
||||
ref := ref
|
||||
name := attrName
|
||||
descr := attrDescr
|
||||
applicationTime := AttributeApplicationTime.afterCompilation
|
||||
add := fun declName _ attrKind => do
|
||||
let go : MetaM Unit := do
|
||||
let info ← getAsyncConstInfo declName
|
||||
if (← isProp info.sig.get.type) then
|
||||
addSymSimpTheorem ext declName attrKind
|
||||
else if info.kind matches .defn then
|
||||
if (← Simp.ignoreEquations declName) then
|
||||
throwError "Cannot add `{attrName}` attribute to `{.ofConstName declName}`: \
|
||||
It is a reducible definition or projection. `Sym.simp` does not support unfolding."
|
||||
else if let some eqns ← getEqnsFor? declName then
|
||||
for eqn in eqns do
|
||||
addSymSimpTheorem ext eqn attrKind
|
||||
else
|
||||
throwError "Cannot add `{attrName}` attribute to `{.ofConstName declName}`: \
|
||||
No equation theorems found."
|
||||
else
|
||||
throwError "Cannot add `{attrName}` attribute to `{.ofConstName declName}`: \
|
||||
It is not a proposition nor a definition with equation theorems."
|
||||
discard <| go.run {} {}
|
||||
erase := fun _declName => do
|
||||
throwError "Erasing `Sym.simp` attributes is not supported yet."
|
||||
}
|
||||
|
||||
/--
|
||||
Registers a named `Sym.Simp` theorem set. Each set gets its own attribute
|
||||
and its own `SymSimpExtension` (persistent environment extension).
|
||||
|
||||
Must be called during initialization.
|
||||
-/
|
||||
def registerSymSimpAttr (attrName : Name) (attrDescr : String)
|
||||
(ref : Name := by exact decl_name%) : IO SymSimpExtension := do
|
||||
let ext ← mkSymSimpExt ref
|
||||
mkSymSimpAttr attrName attrDescr ext ref
|
||||
symSimpExtensionMapRef.modify fun map => map.insert attrName ext
|
||||
return ext
|
||||
|
||||
builtin_initialize symSimpExtension : SymSimpExtension ← registerSymSimpAttr `sym_simp "Sym.simp theorem"
|
||||
|
||||
def getSymSimpTheorems : CoreM Theorems :=
|
||||
symSimpExtension.getTheorems
|
||||
|
||||
end Lean.Meta.Sym.Simp
|
||||
22
src/Lean/Meta/Sym/Simp/RegisterCommand.lean
Normal file
22
src/Lean/Meta/Sym/Simp/RegisterCommand.lean
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/-
|
||||
Copyright (c) 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
module
|
||||
prelude
|
||||
public import Lean.Meta.Sym.Simp.Attr
|
||||
public meta import Init.Data.ToString.Name
|
||||
public meta import Init.Data.String.Extra
|
||||
public section
|
||||
namespace Lean.Meta.Sym.Simp
|
||||
|
||||
macro (name := _root_.Lean.Parser.Command.registerSymSimpAttr) doc:(docComment)?
|
||||
"register_sym_simp_attr" id:ident : command => do
|
||||
let str := id.getId.toString
|
||||
let idParser := mkIdentFrom id (`Parser.Attr ++ id.getId)
|
||||
let descr := quote ((doc.map (·.getDocString) |>.getD s!"Sym.simp set for {id.getId.toString}").removeLeadingSpaces)
|
||||
`($[$doc:docComment]? public meta initialize ext : SymSimpExtension ← registerSymSimpAttr $(quote id.getId) $descr
|
||||
$[$doc:docComment]? syntax (name := $idParser:ident) $(quote str):str : attr)
|
||||
|
||||
end Lean.Meta.Sym.Simp
|
||||
|
|
@ -8,6 +8,7 @@ prelude
|
|||
public import Lean.Meta.Sym.Pattern
|
||||
public import Lean.Meta.DiscrTree
|
||||
import Lean.Meta.Sym.Simp.DiscrTree
|
||||
import Lean.ExtraModUses
|
||||
public section
|
||||
namespace Lean.Meta.Sym.Simp
|
||||
|
||||
|
|
@ -32,6 +33,7 @@ instance : BEq Theorem where
|
|||
/-- Collection of simplification theorems available to the simplifier. -/
|
||||
structure Theorems where
|
||||
thms : DiscrTree Theorem := {}
|
||||
deriving Inhabited
|
||||
|
||||
def Theorems.insert (thms : Theorems) (thm : Theorem) : Theorems :=
|
||||
{ thms with thms := insertPattern thms.thms thm.pattern thm }
|
||||
|
|
@ -46,4 +48,34 @@ def mkTheoremFromDecl (declName : Name) : MetaM Theorem := do
|
|||
let (pattern, rhs) ← mkEqPatternFromDecl declName
|
||||
return { expr := mkConst declName, pattern, rhs }
|
||||
|
||||
/--
|
||||
Environment extension storing a set of `Sym.Simp` theorems.
|
||||
Each named set gets its own extension, created by `registerSymSimpAttr`.
|
||||
-/
|
||||
abbrev SymSimpExtension := SimpleScopedEnvExtension Theorem Theorems
|
||||
|
||||
def SymSimpExtension.getTheorems (ext : SymSimpExtension) : CoreM Theorems :=
|
||||
return ext.getState (← getEnv)
|
||||
|
||||
def mkSymSimpExt (name : Name := by exact decl_name%) : IO SymSimpExtension :=
|
||||
registerSimpleScopedEnvExtension {
|
||||
name := name
|
||||
initial := {}
|
||||
addEntry := fun thms thm => thms.insert thm
|
||||
exportEntry? := fun lvl thm => do
|
||||
let .const declName _ := thm.expr | return thm
|
||||
guard (lvl == .private || !isPrivateName declName)
|
||||
return thm
|
||||
}
|
||||
|
||||
abbrev SymSimpExtensionMap := Std.HashMap Name SymSimpExtension
|
||||
|
||||
builtin_initialize symSimpExtensionMapRef : IO.Ref SymSimpExtensionMap ← IO.mkRef {}
|
||||
|
||||
def getSymSimpExtension? (attrName : Name) : CoreM (Option SymSimpExtension) := do
|
||||
let ext? := (← symSimpExtensionMapRef.get)[attrName]?
|
||||
if let some ext := ext? then
|
||||
recordExtraModUseFromDecl (isMeta := true) ext.ext.name
|
||||
return ext?
|
||||
|
||||
end Lean.Meta.Sym.Simp
|
||||
|
|
|
|||
38
tests/elab/sym_simp_set.lean
Normal file
38
tests/elab/sym_simp_set.lean
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/-
|
||||
Tests for the builtin `@[sym_simp]` attribute.
|
||||
-/
|
||||
import Lean
|
||||
|
||||
open Lean Elab Tactic Meta
|
||||
|
||||
-- Add rewrite theorems to the default set
|
||||
@[sym_simp] theorem add_zero_nat (a : Nat) : a + 0 = a := Nat.add_zero a
|
||||
@[sym_simp] theorem zero_add_nat (a : Nat) : 0 + a = a := Nat.zero_add a
|
||||
|
||||
-- Add a definition (equation theorems)
|
||||
@[sym_simp] def myMul : Nat → Nat → Nat
|
||||
| 0, _ => 0
|
||||
| a + 1, b => b + myMul a b
|
||||
|
||||
-- Tactic that uses the default sym_simp set
|
||||
elab "sym_simp_default" : tactic => do
|
||||
let thms ← Sym.Simp.getSymSimpTheorems
|
||||
let rewrite := thms.rewrite
|
||||
let methods : Sym.Simp.Methods := {
|
||||
post := Sym.Simp.evalGround.andThen rewrite
|
||||
}
|
||||
liftMetaTactic1 fun mvarId => Sym.SymM.run do
|
||||
let mvarId ← Sym.preprocessMVar mvarId
|
||||
(← Sym.simpGoal mvarId methods).toOption
|
||||
|
||||
-- Test: ground evaluation
|
||||
example : 2 + 3 = 5 := by sym_simp_default
|
||||
|
||||
-- Test: rewrite theorem
|
||||
example (n : Nat) : n + 0 = n := by sym_simp_default
|
||||
|
||||
-- Test: both rewrite theorems
|
||||
example (n : Nat) : 0 + n + 0 = n := by sym_simp_default
|
||||
|
||||
-- Test: equation theorems from definition
|
||||
example : myMul 3 2 = 6 := by sym_simp_default
|
||||
40
tests/pkg/sym_simp_attr/SymSimpAttr.lean
Normal file
40
tests/pkg/sym_simp_attr/SymSimpAttr.lean
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/-
|
||||
Tests for `Sym.simp` theorem set attributes.
|
||||
-/
|
||||
module
|
||||
|
||||
import SymSimpAttr.Decl
|
||||
public meta import Lean.Elab.Tactic.Basic
|
||||
public meta import Lean.Meta.Sym
|
||||
|
||||
open Lean Elab Tactic Meta
|
||||
|
||||
-- Add a proposition as a rewrite theorem
|
||||
@[my_sym_simp] theorem add_zero_nat (a : Nat) : a + 0 = a := Nat.add_zero a
|
||||
|
||||
-- Add a definition (equation theorems)
|
||||
@[my_sym_simp] def myAdd : Nat → Nat → Nat
|
||||
| 0, b => b
|
||||
| a + 1, b => (myAdd a b) + 1
|
||||
|
||||
-- Tactic that uses the theorem set
|
||||
elab "sym_simp_set" "[" id:ident "]" : tactic => do
|
||||
let some ext ← Sym.Simp.getSymSimpExtension? id.getId
|
||||
| throwError "Unknown Sym.simp set: {id.getId}"
|
||||
let thms ← ext.getTheorems
|
||||
let rewrite := thms.rewrite
|
||||
let methods : Sym.Simp.Methods := {
|
||||
post := Sym.Simp.evalGround.andThen rewrite
|
||||
}
|
||||
liftMetaTactic1 fun mvarId => Sym.SymM.run do
|
||||
let mvarId ← Sym.preprocessMVar mvarId
|
||||
(← Sym.simpGoal mvarId methods).toOption
|
||||
|
||||
-- Test: ground evaluation
|
||||
example : 2 + 3 = 5 := by sym_simp_set [my_sym_simp]
|
||||
|
||||
-- Test: rewrite theorem from the set
|
||||
example (n : Nat) : n + 0 = n := by sym_simp_set [my_sym_simp]
|
||||
|
||||
-- Test: equation theorems from definition
|
||||
example : myAdd 3 2 = 5 := by sym_simp_set [my_sym_simp]
|
||||
9
tests/pkg/sym_simp_attr/SymSimpAttr/Decl.lean
Normal file
9
tests/pkg/sym_simp_attr/SymSimpAttr/Decl.lean
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/-
|
||||
Declare a `Sym.simp` theorem set.
|
||||
-/
|
||||
module
|
||||
|
||||
public import Lean
|
||||
public meta import Lean.Meta.Sym.Simp.Attr
|
||||
|
||||
register_sym_simp_attr my_sym_simp
|
||||
5
tests/pkg/sym_simp_attr/lakefile.lean
Normal file
5
tests/pkg/sym_simp_attr/lakefile.lean
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import Lake
|
||||
open System Lake DSL
|
||||
|
||||
package sym_simp_attr
|
||||
@[default_target] lean_lib SymSimpAttr
|
||||
1
tests/pkg/sym_simp_attr/lean-toolchain
Normal file
1
tests/pkg/sym_simp_attr/lean-toolchain
Normal file
|
|
@ -0,0 +1 @@
|
|||
../../../build/release/stage1
|
||||
2
tests/pkg/sym_simp_attr/run_test.sh
Executable file
2
tests/pkg/sym_simp_attr/run_test.sh
Executable file
|
|
@ -0,0 +1,2 @@
|
|||
rm -rf .lake/build
|
||||
lake build
|
||||
Loading…
Add table
Reference in a new issue