Each graph-composition operator includes its operands via an index
translation preserving node payloads and edges. Capture that once as
GGraph.Embed (a structure, not a class: for g ; g both inclusions share
the type Embed g (g <~> g), so instance resolution could pick the wrong
copy) with five named witnesses, and replace the five structurally
identical trace-lifting inductions in Properties.lean with a single
generic Trace.embed plus one-line corollaries.
The same witnesses' nodes_eq fields will back the upcoming AST-id/CFG
label bijection, so the per-operator content is stated exactly once.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Finish the projection migration for reaching definitions by replacing the
accumulator-style runOfTrace*From definitions and their hand-rolled
re-association lemmas with a single analysis-agnostic projection:
Trace.steps / Traceₗ.steps, the chronological List of executed
(index, statement) pairs. Its four simp lemmas are one-line inductions,
with all re-association falling out of mathlib's List.append_assoc and
List.reverse_append.
Run is now an abbrev for List (State × BasicStmt) (latest-first, so
LastAssign keeps its first-match structure) and runOfTrace is just
steps.reverse.
Also hoist the generic reaches_final_post into Forward.lean, letting
analyze_correct' be stated directly about S.Post (prog.trace hrun).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tag the finite lattice carrier types with `@[aesop safe cases]`
(`AboveBelow`, `Sign`) so aesop performs the dominant proof step in this
framework -- case-splitting a lattice element -- automatically. Combined
with the existing `@[simp]` operation lemmas, this collapses the recurring
"case-split then reduce" proofs to a bare `aesop`:
* AboveBelow's six lattice axioms drop their explicit `rcases`
* Sign/Constant `plus_mono₂`/`minus_mono₂` become `by aesop`
* Constant `plus_valid`/`minus_valid` shrink to a 2-line `rcases <;> simp_all`
* `not_mk_lt_mk` is reexpressed via `le_cases`
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The six lattice axioms (sup/inf comm/assoc, absorption) all close with a
uniform `rcases <;> aesop`, removing the per-lemma simp-lemma lists that had
to be kept in sync with the Max/Min definitions.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduce `tr₁ ++< he >++ tr₂` scoped notation for `Trace.concat`
(precedence 65, right-associative, mirroring `++`) and use it
throughout Properties.lean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
This makes a finite-height proof for any `Fin n -> a` lattice
immediate, and precludes the need for IterProd and Prod altogether.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Convert every theorem to lemma (mathlib's default) except the headline results a
reader of each module seeks out: analyze_correct (Forward/Sign/Constant),
aFix_eq/aFix_le (Fixedpoint), trace (Language), and Stmt.cfg_sufficient
(Language/Properties). lemma and theorem are interchangeable keywords, so no
references change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Spa/Lattice.lean: add the missing space in the PointedLTSeries binder list
((f t : α) (n : ℕ)).
- Spa/Lattice/Unit.lean: use rfl instead of refl _, and split the ~200-column
longestChain record literal across lines, one field per line.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the ad-hoc truncation `evalB`/`evalB_mono` in
Spa/Analysis/Forward/Adapters.lean with `evalBasicStmt`/`evalBasicStmt_mono`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The `inst*` prefix is reserved for compiler-generated instance names; writing it
by hand is non-idiomatic. Rename the recursive instances in Spa/Lattice/IterProd.lean
to descriptive lowerCamelCase matching the file's `def fixedHeight`:
instLattice -> lattice, instDecidableEq -> decidableEq, instFiniteHeight -> finiteHeight.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduce a finite-height lattice instance for Bool, then build the
reaching-definitions analysis on top of the forward framework:
* Spa/Lattice/Bool.lean: FiniteHeightLattice Bool (the two-element
lattice false ≤ true), making FiniteMap A Bool ks a finite-height
"power set" lattice for free.
* Spa/Analysis/Reaching.lean: DefSet prog = FiniteMap prog.State Bool
prog.states as the per-variable lattice of definition sites, with a
StmtEvaluator whose transfer function performs a strong update
(assignment to k at node s sets k's def-set to {s}).
The analysis computes a least fixed point and produces correct
reaching-definitions sets. Soundness (relating def-sets to actual
execution provenance) is deferred; not yet exposed in Spa.lean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>