Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
b15e335
start adding indexed types
strub Jan 30, 2026
c413f37
WIP
strub Feb 4, 2026
a23243c
indexed-types: phase 0 — restore the build
strub Apr 19, 2026
8057186
indexed-types: phase 1 — polynomial normal form for tindex
strub Apr 20, 2026
e780cfe
indexed-types: phase 2 — substitution and free variables
strub Apr 20, 2026
97ee906
indexed-types: phase 3 — surface syntax for indexed types
strub Apr 20, 2026
02b7e17
indexed-types: phase 3.5 — index inference at op-application sites
strub Apr 20, 2026
3cc7953
indexed-types: phase 4 — cloning of indexed types
strub Apr 20, 2026
0e63822
indexed-types: phase 5 — clean SMT gating for indexed types
strub Apr 20, 2026
1c06b46
indexed-types: phase 6 — pretty-printer + cleanup
strub Apr 20, 2026
53ffe56
indexed-types: gap E + D-investigation fix
strub Apr 20, 2026
8f40f53
indexed-types: gap A — explicit index instantiation at op call sites
strub Apr 21, 2026
5b37d24
indexed-types: schedule remaining gaps B / C / F in memory.md
strub Apr 21, 2026
af4ea84
indexed-types: gap B — solve index equations with one TIUnivar
strub Apr 21, 2026
c17422a
indexed-types: gap C — non-refining indexed datatypes / records
strub Apr 21, 2026
a8708e6
indexed-types: document GADT/refinement limitation in memory.md
strub Apr 21, 2026
693888b
indexed-types: gap F — SMT translation via per-concrete-index monomor…
strub Apr 21, 2026
1361235
indexed-types: regression tests for lemmas with index binders
strub Apr 21, 2026
a115f7e
indexed-types: pretty-print [n 'a] binders on type/op/pred/axiom decls
strub Apr 21, 2026
4e9999c
indexed-types: reinterpret op-leading bracket as binder when not a tag
strub Apr 21, 2026
224a724
indexed-types: switch index binders from [n 'a] to {n} ['a]
strub Apr 21, 2026
f75a301
indexed-types: bound idxvars are also int-typed formula locals
strub Apr 21, 2026
525119d
indexed-types: rewrite/apply on indexed lemmas — substitute idx-univars
strub Apr 21, 2026
ce74882
indexed-types: rewrite under op-unfold — propagate idxvars through Fo…
strub Apr 21, 2026
142d028
indexed-types: substitute idxvars in formula-locals on op-unfold and …
strub Apr 21, 2026
4fda8d0
indexed-types: process_named_pterm also bridges idxvar formula-locals
strub Apr 21, 2026
54b80ce
indexed-types: best-effort idx unification on Fop heads in matcher
strub Apr 21, 2026
7fd04a6
indexed-types: skip form-side idxvar binding when tindex still has un…
strub Apr 21, 2026
af6f983
indexed-types: show active index variables in proof state
strub Apr 21, 2026
6b66b8c
indexed-types: expose [0 <= n] as a proof hypothesis for each idxvar
strub Apr 21, 2026
18863b4
indexed-types: record multi-univar Diophantine as a deliberate non-goal
strub Apr 21, 2026
303b0b3
indexed-types: defer IxUni problems, retry after each assignment
strub Apr 21, 2026
2c92dff
indexed-types: opt-in [0 <= n] hypothesis via `+` marker on idxvars
strub Apr 21, 2026
fe9757f
indexed-types: reject `+` marker on non-lemma binders
strub Apr 21, 2026
50c82aa
indexed-types: subst tactic skips idxvars in formula FV iteration
strub Apr 22, 2026
9adc523
indexed-types: document the idxvar-namespace fragility in memory.md
strub Apr 22, 2026
8bda965
indexed-types: register idxvars as int locals in the proof env
strub Apr 22, 2026
3e986b2
indexed-types: register idxvars in env at LDecl.init, not in h_local
strub Apr 22, 2026
5ac1f7f
indexed-types: `_` placeholder for inferred indices in [op[:...]]
strub Apr 22, 2026
4e93167
indexed-types: bridge form-evar / tindex-univar via fresh evars
strub Apr 22, 2026
3f832b4
Merge origin/main into indexed-types
strub Jul 2, 2026
9111094
Merge origin/main into indexed-types
strub Jul 2, 2026
41496a0
indexed-types: section-declared indices (`declare index {n}`)
strub Jul 3, 2026
f41f4e7
indexed-types: IArray / IWord theories (indexed array + bit-word)
strub Jul 3, 2026
f1d8fe2
indexed-types: index-parametric ring/field instances + predicate inde…
strub Jul 4, 2026
0aa7cce
indexed-types: SMT translates symbolic indices as first-class int sym…
strub Jul 6, 2026
a52b88b
indexed-types: word numeric layer — arithmetic, comparisons, shifts
strub Jul 7, 2026
bd3c9b3
Merge remote-tracking branch 'origin/main' into indexed-types
strub Aug 17, 2026
37f36b7
fix build after origin/main merge
strub Aug 17, 2026
9460bc0
indexed-types: named (possibly partial) index instantiation
strub Aug 17, 2026
4908019
indexed-types: print explicit indices on operator references
strub Aug 17, 2026
f2074ba
indexed-types: precise diagnostics for bad index/type instantiations
strub Aug 17, 2026
f7c574e
indexed-types: move application-error assertions to the dedicated file
strub Aug 17, 2026
61323d1
indexed-types: indexed type/op/pred overrides in cloning
strub Aug 17, 2026
25f518e
indexed-types: prover-robust proof for msbE
strub Aug 17, 2026
2a5a2c4
indexed-types: SMT soundness — relativize indexed types on the erased…
strub Aug 18, 2026
4466e4e
indexed-types: single closing API — a combined substitution or nothing
strub Aug 18, 2026
d213631
indexed-types: drop unused open after closing-API change (ci profile)
strub Aug 18, 2026
de53691
indexed-types: iota/delta must instantiate indices in both namespaces
strub Aug 18, 2026
7c58adf
indexed-types: index-carrying nodes at desugar sites; chain-chasing l…
strub Aug 18, 2026
2fc22d6
indexed-types: named index-parametric ring instances; honest index di…
strub Aug 18, 2026
9977dd2
indexed-types: enforce the naturals discipline at the proof-term bridge
strub Aug 18, 2026
97ff8ba
indexed-types: conversion must compare operator instantiations, not p…
strub Aug 18, 2026
8f1f785
indexed-types: idxvar-complete declaration builders; kernel index arity
strub Aug 18, 2026
be23c4c
indexed-types: drop the ill-shaped optional ring-instance slots
strub Aug 18, 2026
ca4b791
indexed-types: reject shape-mismatched instance operators at declaration
strub Aug 18, 2026
6887ece
indexed-types: drop the WIP phase log
strub Aug 18, 2026
8e1c043
indexed-types: indexed elimination schemes; ground-strict index matching
strub Aug 18, 2026
0b9e9a4
indexed-types: transactional unification; consistent idx links; env a…
strub Aug 18, 2026
15c46cc
indexed-types: index diagnostics (mismatch wording, naturals hints)
strub Aug 18, 2026
9cfbd56
indexed-types: explicit ge0_index; duplicate declare-index and `>>' e…
strub Aug 18, 2026
36b2aaf
indexed-types: promote IWord's integer range facts to IntDiv
strub Aug 18, 2026
abfaaf1
indexed-types: named index instantiation at type applications
strub Aug 18, 2026
a9107ef
indexed-types: single-head opaque IArray accessors + API parity lemmas
strub Aug 18, 2026
7b9c586
indexed-types: drop the `{n+}' marker; ge0_index becomes an axiom
strub Aug 18, 2026
705dc02
indexed-types: hint simplify rules over indexed operator heads
strub Aug 18, 2026
5e3e3c0
indexed-types: export indexed-op definitions to SMT
strub Aug 18, 2026
9d24063
indexed-types: symmetric annotation orders; intentional mixed-list er…
strub Aug 18, 2026
606b930
indexed-types: defensive targs hygiene in the simplifiers
strub Aug 18, 2026
5def985
indexed-types: alpha-invariant hash covers op indices
strub Aug 18, 2026
df993bb
indexed-types: IWord structural lemmas (phase A)
strub Aug 18, 2026
569b2de
indexed-types: IWord bit-set layer (phase B)
strub Aug 18, 2026
0a612db
indexed-types: IWord subtraction (phase C)
strub Aug 18, 2026
d4ebd09
indexed-types: IWord unsigned-order kit (phase D)
strub Aug 18, 2026
fe71fd7
indexed-types: IWord shift/rotate laws (phase E)
strub Aug 18, 2026
22de623
indexed-types: IWord scoping note (phase F)
strub Aug 18, 2026
332180b
indexed-types: per-slot instantiations in ring/field instances
strub Aug 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 56 additions & 13 deletions src/ecAlgTactic.ml
Original file line number Diff line number Diff line change
Expand Up @@ -70,22 +70,22 @@ module Axioms = struct
(div, (false, ty2 ty))]

let subst_of_ring (cr : ring) =
let crcore = [(zero, cr.r_zero);
(one , cr.r_one );
(add , cr.r_add );
(mul , cr.r_mul ); ] in
let crcore = [(zero, cr.r_zero.ro_op);
(one , cr.r_one .ro_op);
(add , cr.r_add .ro_op);
(mul , cr.r_mul .ro_op); ] in

let xpath = fun x -> EcPath.pqname tmod x in
let add = fun subst x p -> EcSubst.add_path subst ~src:(xpath x) ~dst:p in
let addctt = fun subst x f -> EcSubst.add_opdef subst (xpath x) ([], f) in

let subst =
EcSubst.add_tydef EcSubst.empty (xpath tname) ([], cr.r_type) in
EcSubst.add_tydef EcSubst.empty (xpath tname) ([], [], cr.r_type) in
let subst =
List.fold_left (fun subst (x, p) -> add subst x p) subst crcore in
let subst = odfl subst (cr.r_opp |> omap (fun p -> add subst opp p)) in
let subst = odfl subst (cr.r_sub |> omap (fun p -> add subst sub p)) in
let subst = odfl subst (cr.r_exp |> omap (fun p -> add subst expr p)) in
let subst = odfl subst (cr.r_opp |> omap (fun o -> add subst opp o.ro_op)) in
let subst = odfl subst (cr.r_sub |> omap (fun o -> add subst sub o.ro_op)) in
let subst = odfl subst (cr.r_exp |> omap (fun o -> add subst expr o.ro_op)) in

let subst =
match cr.r_kind with
Expand All @@ -99,7 +99,7 @@ module Axioms = struct
let subst =
match cr.r_embed with
| `Direct | `Default -> subst
| `Embed p -> add subst embed p
| `Embed o -> add subst embed o.ro_op
in

subst
Expand All @@ -109,10 +109,47 @@ module Axioms = struct
let add = fun subst x p -> EcSubst.add_path subst ~src:(xpath x) ~dst:p in

let subst = subst_of_ring cr.f_ring in
let subst = add subst inv cr.f_inv in
let subst = odfl subst (cr.f_div |> omap (fun p -> add subst div p)) in
let subst = add subst inv cr.f_inv.ro_op in
let subst = odfl subst (cr.f_div |> omap (fun o -> add subst div o.ro_op)) in
subst

(* The op paths of an instance carry their instantiation implicitly
(each op records the indices/types at which it sits at the
carrier). The template axioms reference the ops without
instantiation, and [subst_of_ring] swaps paths but cannot
re-introduce it; so we patch the substituted axiom, tagging every
instance-op occurrence with its recorded targs. (If two slots
share one path they must also share their instantiation; the
first registered wins.) *)
let ring_op_targs (cr : ring) : EcDecl.ring_op EcPath.Mp.t =
let os = [cr.r_zero; cr.r_one; cr.r_add; cr.r_mul] in
let os = os @ List.filter_map (fun x -> x) [cr.r_opp; cr.r_sub; cr.r_exp] in
let os = match cr.r_embed with `Embed o -> o :: os | _ -> os in
List.fold_left
(fun m (o : EcDecl.ring_op) ->
if EcPath.Mp.mem o.ro_op m then m else EcPath.Mp.add o.ro_op o m)
EcPath.Mp.empty os

let field_op_targs (cr : field) : EcDecl.ring_op EcPath.Mp.t =
let os = cr.f_inv :: List.filter_map (fun x -> x) [cr.f_div] in
List.fold_left
(fun m (o : EcDecl.ring_op) ->
if EcPath.Mp.mem o.ro_op m then m else EcPath.Mp.add o.ro_op o m)
(ring_op_targs cr.f_ring) os

let inject_targs (opmap : EcDecl.ring_op EcPath.Mp.t) (f : form) =
let open EcAst in
let rec doit f =
match f.f_node with
| Fop (p, ta) when ta.indices = [] && ta.types = [] -> begin
match EcPath.Mp.find_opt p opmap with
| Some { ro_idxs = []; ro_tys = []; _ } | None -> f
| Some o ->
f_op_r p { indices = o.ro_idxs; types = o.ro_tys } (f_ty f)
end
| _ -> f_map (fun ty -> ty) doit f
in doit f

(* FIXME: should use operators inlining when available *)
let get cr env axs =
let subst =
Expand All @@ -121,10 +158,16 @@ module Axioms = struct
| `Field cr -> subst_of_field cr
in

let opmap =
match cr with
| `Ring cr -> ring_op_targs cr
| `Field cr -> field_op_targs cr
in

let for1 axname =
let ax = EcEnv.Ax.by_path (EcPath.pqname tmod axname) env in
assert (ax.ax_tparams = [] && is_axiom ax.ax_kind);
(axname, EcSubst.subst_form subst ax.ax_spec)
assert (ax.ax_tparams.tyvars = [] && ax.ax_tparams.idxvars = [] && is_axiom ax.ax_kind);
(axname, inject_targs opmap (EcSubst.subst_form subst ax.ax_spec))
in
List.map for1 axs

Expand Down
48 changes: 32 additions & 16 deletions src/ecAlgebra.ml
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,11 @@ end
type eq = form * form

(* -------------------------------------------------------------------- *)
let rapp r op args =
let rapp r (op : EcDecl.ring_op) args =
let opty = toarrow (List.map f_ty args) r.r_type in
f_app (f_op op [] opty) args r.r_type
let indices = if op.ro_idxs = [] then None else Some op.ro_idxs in
let tyargs = if op.ro_tys = [] then None else Some op.ro_tys in
f_app (f_op op.ro_op ?indices ?tyargs opty) args r.r_type

let rzero r = rapp r r.r_zero []
let rone r = rapp r r.r_one []
Expand Down Expand Up @@ -150,35 +152,41 @@ let emb_fone r = emb_rone r.f_ring

(* -------------------------------------------------------------------- *)
type cringop = [`Zero | `One | `Add | `Opp | `Sub | `Mul | `Exp | `OfInt]
type cring = ring * (cringop Mp.t)
type cring = ring * ((cringop * EcDecl.ring_op) Mp.t)

(* -------------------------------------------------------------------- *)
type cfieldop = [cringop | `Inv | `Div]
type cfield = field * (cfieldop Mp.t)
type cfield = field * ((cfieldop * EcDecl.ring_op) Mp.t)

(* -------------------------------------------------------------------- *)
(* Recognition is keyed by op path, then checked against the slot's
recorded instantiation: an occurrence of the same path at OTHER
indices/types is not this ring's operator. *)
let cring_of_ring (r : ring) : cring =
let cr = [(r.r_zero, `Zero);
(r.r_one , `One );
(r.r_add , `Add );
(r.r_mul , `Mul );]
in

let cr = List.fold_left (fun m (p, op) -> Mp.add p op m) Mp.empty cr in
let cr = odfl cr (r.r_opp |> omap (fun p -> Mp.add p `Opp cr)) in
let cr = odfl cr (r.r_sub |> omap (fun p -> Mp.add p `Sub cr)) in
let cr = odfl cr (r.r_exp |> omap (fun p -> Mp.add p `Exp cr)) in
let radd (o : EcDecl.ring_op) tag m = Mp.add o.ro_op (tag, o) m in
let cr = List.fold_left (fun m (o, tag) -> radd o tag m) Mp.empty cr in
let cr = odfl cr (r.r_opp |> omap (fun o -> radd o `Opp cr)) in
let cr = odfl cr (r.r_sub |> omap (fun o -> radd o `Sub cr)) in
let cr = odfl cr (r.r_exp |> omap (fun o -> radd o `Exp cr)) in
let cr = r.r_embed |>
(function (`Direct | `Default) -> cr | `Embed p -> Mp.add p `OfInt cr) in
(function (`Direct | `Default) -> cr | `Embed o -> radd o `OfInt cr) in
(r, cr)

let ring_of_cring (cr:cring) = fst cr

(* -------------------------------------------------------------------- *)
let cfield_of_field (r : field) : cfield =
let cr = (snd (cring_of_ring r.f_ring) :> cfieldop Mp.t) in
let cr = Mp.add r.f_inv `Inv cr in
let cr = odfl cr (r.f_div |> omap (fun p -> Mp.add p `Div cr)) in
let cr =
(snd (cring_of_ring r.f_ring) :> (cfieldop * EcDecl.ring_op) Mp.t) in
let cr = Mp.add r.f_inv.ro_op (`Inv, r.f_inv) cr in
let cr =
odfl cr (r.f_div |> omap (fun o -> Mp.add o.ro_op (`Div, o) cr)) in
(r, cr)

let field_of_cfield (cr:cfield) : field = fst cr
Expand All @@ -192,10 +200,14 @@ let toring hyps ((r, cr) : cring) (rmap : RState.rstate) (form : form) =
let rec doit form =
let o, args = destr_app form in
match o.f_node with
| Fop (op, _) -> begin
| Fop (op, ta) -> begin
match Mp.find_opt op cr with
| None -> abstract form
| Some op -> begin
| Some (_, ro)
when not (List.all2 EcAst.tindex_equal ta.indices ro.ro_idxs
&& List.all2 ty_equal ta.types ro.ro_tys) ->
abstract form
| Some (op, _) -> begin
match op,args with
| `Zero, [] -> PEc c0
| `One , [] -> PEc c1
Expand Down Expand Up @@ -255,10 +267,14 @@ let tofield hyps ((r, cr) : cfield) (rmap : RState.rstate) (form : form) =
let rec doit form =
let o, args = destr_app form in
match o.f_node with
| Fop(op, _) -> begin
| Fop(op, ta) -> begin
match Mp.find_opt op cr with
| None -> abstract form
| Some op -> begin
| Some (_, ro)
when not (List.all2 EcAst.tindex_equal ta.indices ro.ro_idxs
&& List.all2 ty_equal ta.types ro.ro_tys) ->
abstract form
| Some (op, _) -> begin
match op,args with
| `Zero, [] -> FEc c0
| `One , [] -> FEc c1
Expand Down
2 changes: 1 addition & 1 deletion src/ecAlgebra.mli
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ module RState : sig
end

(* -------------------------------------------------------------------- *)
val rapp : ring -> EcPath.path -> form list -> form
val rapp : ring -> EcDecl.ring_op -> form list -> form
val rzero : ring -> form
val rone : ring -> form
val radd : ring -> form -> form -> form
Expand Down
7 changes: 4 additions & 3 deletions src/ecAlphaInvHashtbl.ml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
The hash is invariant under the renaming of bound variables: a bound
occurrence is hashed by the de-Bruijn *level* of its binder (an
integer, intrinsically stable) rather than by its name, so
alpha-equivalent formulas hash equal. Free variables, operators and
types are stable under alpha-renaming and are hashed as-is.
alpha-equivalent formulas hash equal. Free variables, operators
(with their type and index instantiations) and types are stable
under alpha-renaming and are hashed as-is.

The hash traverses the whole formula, but is memoized on the hash-cons
tag ([f_tag]) of every subformula reached with no binder in scope: each
Expand Down Expand Up @@ -78,7 +79,7 @@ let hash_memo (memo : (int, int) Hashtbl.t) (f0 : form) : int =
combine 3 (pv_hash pv)
| Fglob (mp, _m) -> combine 4 (id_hash mp)
| Fop (p, tys) ->
combine 5 (combine_list (EcPath.p_hash p) (List.map ty_hash tys))
combine 5 (targ_hash (EcPath.p_hash p) tys)
| Fif (c, t, f) -> combine 6 (combine_list 0 [hash e c; hash e t; hash e f])
| Fmatch (c, bs, ty) ->
combine 7 (combine_list (ty_hash ty) (hash e c :: List.map (hash e) bs))
Expand Down
Loading
Loading