Skip to content

Commit a6daaa4

Browse files
committed
Integer-indexed types
Type constructors parameterised by both type variables and natural-number indices, with a small index language and decidable index equality: size-carrying types like 'a vec<:n>, with index arithmetic tracked through operators, lemmas, instances, SMT, and cloning. - surface: {n} binders on types/ops/preds/lemmas/notations/abbrevs and clone overrides; type application t<:e> and op/lemma instantiation f[:e], positional or named (partial), composable in either order; `declare index {n}' sections; `_' inference holes; indexed (non-refining) datatypes, records, matchfix operators - indices are polynomials over the naturals with canonical-form equality (hashconsing identifies vec<:8> and vec<:3+5>); solving is restricted to a principal fragment (naked assignment + unit- coefficient single-variable affine with non-negative residual, with deferral); the naturals discipline is enforced at every entry point and exposed as the single axiom Int.ge0_index - stdlib: IArray (length-indexed arrays) and IWord (length-indexed bit-words: bit/bit-set layers, boolean ring, Z/2^n arithmetic with to_uint/of_int, comparisons, shifts/rotates); five generic range facts promoted to IntDiv - ring/field instances are named, index-parametric, and type-polymorphic; each instance records ONE shared instantiation (index expressions and type arguments over its binders), with a uniformity check at declaration; oner_neq0 is no longer a ring- instance obligation (the tactic's certificates are purely equational, so the trivial ring is a sound carrier; fields keep it), making the word arithmetic instance family-wide: `instance ring [warith] with {n} word<:n>' fires at concrete widths, word<:k+1>, bare symbolic word<:m>, and word<:0> - SMT: sort erasure + term-level relativization (leading int arguments on indexed ops, width observers, relativized quantifiers, 0 <= n facts); plain-bodied indexed ops export standard definitions; indexed datatypes are refused (sound incompleteness), with the encoding design documented as follow-up - hint simplify rules over indexed heads (affine index patterns, matched without the unification engine) - dedicated diagnostics: ground index mismatches vs unsolvable unifications, naturals hints (negative literals, subtraction), `>>' token, mixed positional/named argument lists
1 parent ef1b407 commit a6daaa4

104 files changed

Lines changed: 6931 additions & 1203 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎examples/MEE-CBC/FunctionalSpec.ec‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@ instance bring with block
109109
op mul = Self.andb
110110
op opp = Self.oppb
111111

112-
proof oner_neq0 by apply/oneb_neq0
113112
proof addr0 by apply/xorb0
114113
proof addrA by apply/xorbA
115114
proof addrC by apply/xorbC

‎src/ecAlgTactic.ml‎

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@ module Axioms = struct
3030
let cN = "Cn"
3131
let cP = "Cp"
3232

33-
let core_add = ["oner_neq0"; "addr0"; "addrA"; "addrC";]
33+
(* [oner_neq0] is NOT part of the ring obligations: the tactic's
34+
certificates are purely equational (a disequality cannot occur in
35+
a normalization proof), so the trivial ring is a sound carrier --
36+
e.g. an instance over the whole family [word<:n>], including
37+
width 0. Fields keep requiring it (division). *)
38+
let core_add = ["addr0"; "addrA"; "addrC";]
3439
let core_mul = [ "mulr1"; "mulrA"; "mulrC"; "mulrDl"]
3540
let core = core_add @ "addrN" :: core_mul
3641
let core_bool = core_add @ "addrK" :: "mulrK" :: core_mul
@@ -39,7 +44,7 @@ module Axioms = struct
3944
let intpow = ["expr0"; "exprS"]
4045
let ofint = ["ofint0"; "ofint1"; "ofintS"; "ofintN"]
4146
let ofsub = ["subrE"]
42-
let field = ["mulrV"; "exprN"]
47+
let field = ["oner_neq0"; "mulrV"; "exprN"]
4348
let ofdiv = ["divrE"]
4449
let cNax = ["Cn_eq0"]
4550
let cPax = ["Cp_idp"]
@@ -80,7 +85,7 @@ module Axioms = struct
8085
let addctt = fun subst x f -> EcSubst.add_opdef subst (xpath x) ([], f) in
8186

8287
let subst =
83-
EcSubst.add_tydef EcSubst.empty (xpath tname) ([], cr.r_type) in
88+
EcSubst.add_tydef EcSubst.empty (xpath tname) ([], [], cr.r_type) in
8489
let subst =
8590
List.fold_left (fun subst (x, p) -> add subst x p) subst crcore in
8691
let subst = odfl subst (cr.r_opp |> omap (fun p -> add subst opp p)) in
@@ -113,6 +118,33 @@ module Axioms = struct
113118
let subst = odfl subst (cr.f_div |> omap (fun p -> add subst div p)) in
114119
subst
115120

121+
(* The op paths of an instance carry their instantiation implicitly
122+
(every op shares the instance's [r_insts]). The template axioms
123+
reference the ops without instantiation, and [subst_of_ring]
124+
swaps paths but cannot re-introduce it; so we patch the
125+
substituted axiom, tagging every instance-op occurrence with the
126+
shared targs. A no-op for uninstantiated instances. *)
127+
let ring_op_paths (cr : ring) : EcPath.Sp.t =
128+
let ps = [cr.r_zero; cr.r_one; cr.r_add; cr.r_mul] in
129+
let ps = ps @ List.filter_map (fun x -> x) [cr.r_opp; cr.r_sub; cr.r_exp] in
130+
let ps = match cr.r_embed with `Embed p -> p :: ps | _ -> ps in
131+
EcPath.Sp.of_list ps
132+
133+
let field_op_paths (cr : field) : EcPath.Sp.t =
134+
let ps = cr.f_inv :: List.filter_map (fun x -> x) [cr.f_div] in
135+
List.fold_left (fun s p -> EcPath.Sp.add p s) (ring_op_paths cr.f_ring) ps
136+
137+
let inject_targs (opset : EcPath.Sp.t) (insts : EcAst.targs) (f : form) =
138+
let open EcAst in
139+
if insts.indices = [] && insts.types = [] then f else
140+
let rec doit f =
141+
match f.f_node with
142+
| Fop (p, ta)
143+
when EcPath.Sp.mem p opset && ta.indices = [] && ta.types = [] ->
144+
f_op_r p insts (f_ty f)
145+
| _ -> f_map (fun ty -> ty) doit f
146+
in doit f
147+
116148
(* FIXME: should use operators inlining when available *)
117149
let get cr env axs =
118150
let subst =
@@ -121,10 +153,16 @@ module Axioms = struct
121153
| `Field cr -> subst_of_field cr
122154
in
123155

156+
let (opset, insts) =
157+
match cr with
158+
| `Ring cr -> ring_op_paths cr, cr.r_insts
159+
| `Field cr -> field_op_paths cr, cr.f_ring.r_insts
160+
in
161+
124162
let for1 axname =
125163
let ax = EcEnv.Ax.by_path (EcPath.pqname tmod axname) env in
126-
assert (ax.ax_tparams = [] && is_axiom ax.ax_kind);
127-
(axname, EcSubst.subst_form subst ax.ax_spec)
164+
assert (ax.ax_tparams.tyvars = [] && ax.ax_tparams.idxvars = [] && is_axiom ax.ax_kind);
165+
(axname, inject_targs opset insts (EcSubst.subst_form subst ax.ax_spec))
128166
in
129167
List.map for1 axs
130168

‎src/ecAlgebra.ml‎

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,12 @@ end
7373
type eq = form * form
7474

7575
(* -------------------------------------------------------------------- *)
76-
let rapp r op args =
76+
let rapp r (op : EcPath.path) args =
7777
let opty = toarrow (List.map f_ty args) r.r_type in
78-
f_app (f_op op [] opty) args r.r_type
78+
let ta = r.r_insts in
79+
let indices = if ta.EcAst.indices = [] then None else Some ta.EcAst.indices in
80+
let tyargs = if ta.EcAst.types = [] then None else Some ta.EcAst.types in
81+
f_app (f_op op ?indices ?tyargs opty) args r.r_type
7982

8083
let rzero r = rapp r r.r_zero []
8184
let rone r = rapp r r.r_one []
@@ -157,6 +160,9 @@ type cfieldop = [cringop | `Inv | `Div]
157160
type cfield = field * (cfieldop Mp.t)
158161

159162
(* -------------------------------------------------------------------- *)
163+
(* Recognition is keyed by op path, then checked against the ring's
164+
shared instantiation: an occurrence of the same path at OTHER
165+
indices/types is not this ring's operator. *)
160166
let cring_of_ring (r : ring) : cring =
161167
let cr = [(r.r_zero, `Zero);
162168
(r.r_one , `One );
@@ -192,9 +198,11 @@ let toring hyps ((r, cr) : cring) (rmap : RState.rstate) (form : form) =
192198
let rec doit form =
193199
let o, args = destr_app form in
194200
match o.f_node with
195-
| Fop (op, _) -> begin
201+
| Fop (op, ta) -> begin
196202
match Mp.find_opt op cr with
197203
| None -> abstract form
204+
| Some _ when not (EcDecl.targs_equal ta r.r_insts) ->
205+
abstract form
198206
| Some op -> begin
199207
match op,args with
200208
| `Zero, [] -> PEc c0
@@ -255,9 +263,11 @@ let tofield hyps ((r, cr) : cfield) (rmap : RState.rstate) (form : form) =
255263
let rec doit form =
256264
let o, args = destr_app form in
257265
match o.f_node with
258-
| Fop(op, _) -> begin
266+
| Fop(op, ta) -> begin
259267
match Mp.find_opt op cr with
260268
| None -> abstract form
269+
| Some _ when not (EcDecl.targs_equal ta r.EcDecl.f_ring.EcDecl.r_insts) ->
270+
abstract form
261271
| Some op -> begin
262272
match op,args with
263273
| `Zero, [] -> FEc c0

‎src/ecAlphaInvHashtbl.ml‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
The hash is invariant under the renaming of bound variables: a bound
66
occurrence is hashed by the de-Bruijn *level* of its binder (an
77
integer, intrinsically stable) rather than by its name, so
8-
alpha-equivalent formulas hash equal. Free variables, operators and
9-
types are stable under alpha-renaming and are hashed as-is.
8+
alpha-equivalent formulas hash equal. Free variables, operators
9+
(with their type and index instantiations) and types are stable
10+
under alpha-renaming and are hashed as-is.
1011
1112
The hash traverses the whole formula, but is memoized on the hash-cons
1213
tag ([f_tag]) of every subformula reached with no binder in scope: each
@@ -78,7 +79,7 @@ let hash_memo (memo : (int, int) Hashtbl.t) (f0 : form) : int =
7879
combine 3 (pv_hash pv)
7980
| Fglob (mp, _m) -> combine 4 (id_hash mp)
8081
| Fop (p, tys) ->
81-
combine 5 (combine_list (EcPath.p_hash p) (List.map ty_hash tys))
82+
combine 5 (targ_hash (EcPath.p_hash p) tys)
8283
| Fif (c, t, f) -> combine 6 (combine_list 0 [hash e c; hash e t; hash e f])
8384
| Fmatch (c, bs, ty) ->
8485
combine 7 (combine_list (ty_hash ty) (hash e c :: List.map (hash e) bs))

0 commit comments

Comments
 (0)