Files
agda-spa/lean/Spa/Lattice.lean
Danila Fedorin b6b30958aa Add proof of reaching definition analysis
This requires a few pieces:

* Make node tags use `Fin n` intead of natural numbers. This makes
  it possible to build a finite lattice over AST nodes, and also
  ensure automatic, total indexing from CFG nodes into the AST that
  created them. For this, use the elaborator to derive the ordering
  statements etc. where possible.
* Adjust the forward framework to enable proofs that don't just state
  correctness on the environment, but also on an arbitrary additional
  state accumulated from traversing the trace.
* State the reaching definition analysis's correctness in terms
  of this new framework.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 18:56:59 -05:00

153 lines
5.8 KiB
Lean4
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.
import Mathlib.Order.Lattice
import Mathlib.Order.RelSeries
/-!
# Lattice Definitions
This file provides some definitions for lattices. It used to be more critical
when this was an Agda project, since it defined (semi)lattices, the ordering
relation, etc. However, these have been lifted into `Mathlib.Order.Lattice`
etc.. What remains are a couple of theorems about folds, as well
as `FiniteHeightLattice`, the core concept of lattice-based static
program analyses. See the documentation on that class for more information. -/
namespace Option
/-- Equality-sensitive eliminator for options in which the `some` case
is sensitive to the base `β`. This makes it mirror a one-element fold
more closely. -/
def elimEq {α : Type*} {β : Sort*} :
(o : Option α) β ((a : α) o = some a β β) β
| none, b, _ => b
| some a, b, f => f a rfl b
end Option
namespace Spa
/-- Predicate for binary functions independently monotone in both their arguments. -/
def Monotone₂ {α β γ : Type*} [Preorder α] [Preorder β] [Preorder γ]
(f : α β γ) : Prop :=
( b, Monotone (f · b)) ( a, Monotone (f a ·))
section Folds
variable {α β : Type*} [Preorder α] [Preorder β]
/-- (right) folds are monotonic in both their arguments if the underlying accumulator function is. -/
lemma foldr_mono {l₁ l₂ : List α} (f : α β β) {b₁ b₂ : β}
(hl : List.Forall₂ (· ·) l₁ l₂) (hb : b₁ b₂)
(hf₁ : b, Monotone (f · b)) (hf₂ : a, Monotone (f a ·)) :
l₁.foldr f b₁ l₂.foldr f b₂ := by
induction hl with
| nil => exact hb
| cons hxy _ ih =>
exact le_trans (hf₁ _ hxy) (hf₂ _ ih)
/-- (left) folds are monotinic in both their arguments if the underlying accumulator function is. -/
lemma foldl_mono {l₁ l₂ : List α} (f : β α β) {b₁ b₂ : β}
(hl : List.Forall₂ (· ·) l₁ l₂) (hb : b₁ b₂)
(hf₁ : a, Monotone (f · a)) (hf₂ : b, Monotone (f b ·)) :
l₁.foldl f b₁ l₂.foldl f b₂ := by
induction hl generalizing b₁ b₂ with
| nil => exact hb
| cons hxy _ ih =>
exact ih (le_trans (hf₁ _ hb) (hf₂ _ hxy))
omit [Preorder α] in
/-- (right) folds on a particular list are monotonic if the underlying accumulator is monotonic in its accumulator argument. -/
lemma foldr_mono' (l : List α) (f : α β β)
(hf : a, Monotone (f a ·)) : Monotone (l.foldr f ·) := by
intro b₁ b₂ hb
induction l with
| nil => exact hb
| cons x xs ih => exact hf x ih
omit [Preorder α] in
/-- (left) folds on a particular list are monotonic if the underlying accumulator is monotonic in its accumulator argument. -/
lemma foldl_mono' (l : List α) (f : β α β)
(hf : a, Monotone (f · a)) : Monotone fun b => l.foldl f b := by
intro b₁ b₂ hb
induction l generalizing b₁ b₂ with
| nil => exact hb
| cons x xs ih => exact ih (hf x hb)
omit [Preorder α] in
/-- The equality-aware eliminator (that also alters its behavior dependent on base case)
for option is monotonic. -/
lemma elimEq_self_mono (o : Option α) (g : (a : α) o = some a β β)
(hg : a h, Monotone (g a h)) :
Monotone (o.elimEq · g) := by
cases o with
| none => exact monotone_id
| some a => exact hg a rfl
end Folds
/-- Predicate on types with `Preorder` that claims all $<$ chains in the type have at most `n` comparisons. -/
def BoundedChains (α : Type*) [Preorder α] (n : ) : Prop :=
c : LTSeries α, c.length n
/-- Since a singleton type's preorder has no nonempty `<` chains,
they are vacuously bounded by any minimum height. -/
lemma boundedChains_of_subsingleton (α : Type*) [Preorder α] [Subsingleton α]
(n : ) : BoundedChains α n := fun c => by
by_contra hc
push_neg at hc
exact (c.step 0, by omega).ne (Subsingleton.elim _ _)
/-- A finite height lattice is a lattice in which all chains $a < \ldots < z$ have a maximum height `height`. -/
class FiniteHeightLattice (α : Type*) extends Lattice α, OrderBot α, OrderTop α where
height :
chains_bounded : BoundedChains α height
-- a < ... < z
-- ----------- length <= height
namespace FiniteHeightLattice
/-- This is something like a lemma about isomorphic types having the same height.
Given a finite-height lattice `α`, lattice `β`, and a `Monotone` bijection
between the two, we can show that lattice `β` also has a finite height.
The proof is fairly trivial: any chain in `β` can be transported to a chain in `α`,
and must be bounded by the same height by `FiniteHeightLattice.chains_bounded`. -/
def transport {α β : Type*} [Lattice β]
[I : FiniteHeightLattice α] (f : α β) (g : β α)
(hf : Monotone f) (hg : Monotone g)
(hfg : Function.LeftInverse f g) :
FiniteHeightLattice β where
toLattice := inferInstance
toOrderBot := {
bot := f ( : α)
bot_le := fun b => by
rw [ hfg b]
exact hf (_root_.bot_le : ( : α) g b) }
toOrderTop := {
top := f ( : α)
le_top := fun b => by
rw [ hfg b]
exact hf (_root_.le_top : g b ( : α)) }
height := I.height
chains_bounded := fun c =>
I.chains_bounded (c.map g (hg.strictMono_of_injective hfg.injective))
/-- A `Unique` lattice trivially has finite height: its only chain is the singleton
`[default]`, and there are no nontrivial `<` chains in a subsingleton. -/
def ofUnique (α : Type*) [Lattice α] [Unique α] :
FiniteHeightLattice α where
toLattice := inferInstance
toOrderBot := {
bot := default
bot_le := fun _ => le_of_eq (Subsingleton.elim _ _) }
toOrderTop := {
top := default
le_top := fun _ => le_of_eq (Subsingleton.elim _ _) }
height := 0
chains_bounded := boundedChains_of_subsingleton α 0
end FiniteHeightLattice
end Spa