Repository navigation
Conversation
Adds SPARK (the provable subset of Ada) as a fourth language track
alongside Dafny, Verus and Lean, with gnatprove as the checker and the
same spec -> verified-code task shape.
benchmarks/spark/ 26 seed tasks + reference solutions
scripts/spark_verify.py the checker
scripts/spark_negative_control.py
proof that the checker rejects things
src/spark/prompts-spark.yaml SPARK generation and repair prompts
config/language_config.toml [spark] section
cheat_checker.py SPARK bypass patterns
The seed set is 26 of the 58 numpy_simple Dafny tasks, translated with
the Dafny spec as the authority. All 26 reference solutions discharge at
--level=2: 387 proof obligations, 0 unproved, 0 justified. The other 32
tasks were skipped rather than weakened, in groups, each with its reason
recorded in benchmarks/spark/README.md.
The harness requires a non-zero check count to pass. GNATprove exits 0
with an empty summary for code it never analysed, so a harness that
checks only the exit status and the unproved count scores that as a
pass; the negative control includes that case as one of four mutants.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add a SPARK / GNATprove language track
Hello — this adds SPARK (the provable subset of Ada) as a fourth language
track alongside Dafny, Verus and Lean, with
gnatproveas the checker.There is no
CONTRIBUTING.mdand no documented process for adding alanguage, so this is written by reverse-engineering the existing
benchmarks/dafnyandbenchmarks/veruslayouts. If any of it cutsagainst how you would rather it were done, say so and I will redo it — the
shape is not precious, and I would rather match your conventions than mine.
What is here
benchmarks/spark/scripts/spark_verify.pyscripts/spark_negative_control.pysrc/spark/prompts-spark.yamlconfig/language_config.toml[spark]sectionsrc/vericoding/processing/cheat_checker.pyThe task shape is the one you define — spec → verified code, with the
specification fixed and the model writing the body and its proof
annotations.
The seed set
26 of the 58
numpy_simpleDafny tasks, translated one for one with yourDafny spec as the authority. All 26 reference solutions discharge at
--level=2: 387 proof obligations, 0 unproved, 0 justified, about 45 sfor the whole set.
The other 32 were skipped rather than weakened, grouped by what blocks
them, each group with its reason written down in
benchmarks/spark/README.md— Dafny
real(6), recursive spec functions (6), multiset/permutationpostconditions (3), seq-of-seq matrices (8), strings (2), number theory (2),
higher-order specs (1), Dafny datatypes (1), result-dependent shape (1), one
with no non-degenerate bounded domain, one near-duplicate. No postcondition
was weakened to make a translation land. The skip list is written to be a
to-do list for whoever grows this next, including us.
Two adaptations are uniform, are properties of the language rather than of
any task, and are declared in the README rather than buried:
intis mathematical; Ada'sIntegerisnot, and GNATprove makes you prove every operation in range. Each task
fixes a finite domain. This makes the SPARK tasks strictly harder than
their Dafny originals in one respect — an implementation must also prove
absence of overflow.
/and%onintareEuclidean; Ada's
/truncates and Ada'smodis floor-mod. They agreeonly for a positive divisor, so the two division tasks restrict the
divisor and say so, rather than quietly proving a different operation.
One thing worth a look even if you take nothing else
The harness fails any submission where GNATprove emitted zero checks.
GNATprove exits 0 with an empty summary for code it never analysed — an
empty
SPARK_Moderegion, a body excluded from analysis. A harness thatchecks only the exit status and the unproved count scores that as a pass: a
green light for a proof that never happened. We hit this in our own work and
only caught it because something else was watching, so it is a rule here and
one of the four cases in the negative control:
unproved— "postcondition might fail"pragma AssumebypasscheatSPARK_Mode => Off— the empty-summary casecheatcompile_errorBaseline unmutated reference:
pass. All four rejected.python scripts/spark_negative_control.pyruns it.The equivalent hazard may exist for the other tracks — a Lean file that
elaborates nothing, a Verus body behind
#[verifier::external]— and if soit is worth the same guard there. That is offered as a question, not a
claim about your harness.
Notes on the file format
Ada wants the package specification and body in separate files, so a task is
a single GNAT multi-unit
.adasource that the harness runsgnatchopoverbefore proving. Two consequences, both documented in the track README:
vc-speccomes beforevc-helpers(helpers live in the body, which cannotprecede the spec), and
vc-speccarries the non-editable package frame sothat every byte of the file stays inside a section and the JSONL form
round-trips exactly.
The unfilled body carries
pragma Assume (False), playing the part yourDafny templates give
assume {:axiom} false.Reference solutions are shipped in
numpy_simple/solutions/, which theother tracks do not do. They are there so nobody has to take the seed set's
dischargeability on trust, and so a harness change can be regression-tested.
The harness never reads them. Happy to drop them if you would rather the
tracks stayed symmetrical.
Running it
Needs
gnatproveandgnatchoponPATH; via Alire,alr toolchain --selectthenalr install gnatprove. Verified on FSF GNAT15.0 / SPARK with Why3 1.7.1, Alt-Ergo 2.6.0 and cvc5 1.2.1.
Where this comes from, and what we are offering
We run a small production shop that forges SPARK cores from prose
specifications with local models and admits nothing that a second machine
has not independently re-proved under GNATprove — so the seed set and the
harness are a slice of something already in daily use, rather than a
one-off written for this PR.
We would like to grow the track — the matrix group and the recursive-spec
group are the obvious next slices, and we are happy to do that work whether
or not this lands as-is. If you would rather a smaller PR, or the tasks
without the reference solutions, or a different file format, tell us which
and we will send that instead.
— The Dark Factory