Tactical: extens
extens is a tactical that performs an extensionality-style
case-split: it enumerates the concrete values of a finite quantity,
generates one subgoal per case, and runs a user-supplied tactic on
each generated subgoal. The original goal closes only when every
subgoal closes.
Two goal shapes are recognised, distinguished by the presence or absence of the bracketed binder:
A first-order proposition of the form
all P (iota_ start len)(overint) — no binder. The split produces one subgoalP iper integeriin the range[start, start+len).A Hoare triple
hoare[M.f : pre ==> post]together with a binder[v]naming a program variable. The variable’s type must bebind bitstring-bound (see Command: bind). The split produces2^nHoare triples (withnthe bound bitstring size), in each of which the program variablevhas been substituted by the correspondingof_int ieverywhere — in the program, in the precondition, and in the postcondition.
In both cases the inner tactic is then run on each generated subgoal. If a subgoal fails to close, the residual goal is reported as an error.
Syntax
extens [v]? : tactic
The bracketed binder [v] is required for the Hoare-triple variant
(it picks the program variable to enumerate) and forbidden for the
iota_ variant.
The most common use is extens [v] : circuit (or, with
simplification first, extens [v] : (circuit simplify; ...)).
The benefit over a bare circuit is that the per-case translation
sees a program in which one input has been replaced by a concrete
constant, which lets circuit translation succeed on programs whose
whole-input translation would fail or blow up.
Variant: List enumeration over iota_
The goal all (fun i => a.[i] = a.[i]) (iota_ 0 8) is split into
eight independent subgoals (one per i in [0, 8)), each of
which is then discharged by circuit.
Both the start and len arguments of iota_ must be ground
integer literals; extens rejects a non-constant range with
Iota start should be constant or Iota length should be
constant.
Variant: Hoare-triple enumeration over a bitstring variable
The binder [a] picks the program variable to enumerate. Since
a : W8 is bound to an eight-bit bitstring, the tactic produces
2^8 = 256 Hoare triples in which a has been replaced by each
concrete value of_int i; the supplied tactic
wp; skip; smt() then closes each.
This pattern is most useful when the inner tactic is circuit
itself: replacing one program input by a concrete constant often lets
the per-case circuit translation succeed even when the
whole-program circuit translation would not. The same example with
circuit as the inner tactic:
The 2^n blow-up makes this variant practical only for small bit
widths: n = 8 already produces 256 subgoals, and the cost grows
exponentially.
Failure modes
Wrong goal shapeThe goal is neither
all _ (iota_ _ _)nor a Hoare triple, or the binder presence does not match the goal shape (binder given on aniota_goal, or omitted on a Hoare triple).Failed to find var <name> in memory <m>The bracketed binder names a variable that does not exist in the Hoare triple’s memory.
Failed to get size for type <τ>The bracketed binder names a variable whose type is not
bind bitstring-bound (or is bound only abstractly, without a concrete size). Arrays are not currently supported for the binder.Iota start should be constant/Iota length should be constantThe
iota_arguments are not ground integer literals.Unsupported List patternThe list inside
allis not of the formiota_ start len.Failed to close goal: <residual>The inner tactic ran on every subgoal but left at least one unclosed. The residual goal is reported as part of the error.