Preuves Coq
Sommaire
- TL;DR
- Pourquoi des preuves formelles
- Vue d'ensemble
- A. Preuves syntaxiques
- B. Preuves sémantiques
- C. Modèle IR
- D. Preuves runtime
- E. Preuves d'optimisation
- F. Preuves d'analyse et CFG
- G. Preuves runtime avancées
- A. Preuves syntaxiques
- GrammarProof.v
- CatnipAddMulProof.v
- CatnipExprProof.v
- CatnipExprMonoProof.v
- B. Preuves sémantiques
- CatnipDimensional.v
- CatnipDimensionalProps.v
- CatnipNDRecursion.v
- C. Modèle IR
- CatnipIR.v
- D. Preuves runtime
- CatnipScopeProof.v
- CatnipPatternProof.v
- CatnipFunctionProof.v
- E. Preuves d'optimisation
- CatnipOptimProof.v (facade)
- CatnipStrengthRedProof.v
- CatnipBluntCodeProof.v
- CatnipDCEFlattenProof.v
- CatnipVMOpCode.v
- CatnipVMState.v
- CatnipVMStackSafety.v
- CatnipVMInvariants.v
- CatnipVMExamples.v
- CatnipVMFrame.v
- CatnipArithProof.v
- CatnipPureFrameProof.v
- CatnipVMProof.v
- CatnipMRO*.v (6 modules)
- CatnipOpDesugar*.v (4 modules)
- F. Preuves d'analyse
- CatnipLivenessProof.v (facade)
- CatnipVarSet.v
- CatnipLivenessLinear.v
- CatnipLivenessCFG.v
- Technique : récursion à carburant
- Build
- Références
Vérification mécanisée de fragments de Catnip : grammaire, précédence, modèle dimensionnel, IR, scopes, pattern matching, fonctions/TCO, et passes d'optimisation.
TL;DR
56 fichiers Coq dans proof/ (~18500 lignes, 0 Admitted) prouvent des invariants structurels et sémantiques couvrant le
parsing, le broadcasting, la résolution de scopes, le pattern matching, le trampoline TCO, les 10/10 passes
d'optimisation IR, l'analyse de liveness, la ND-récursion, le pipeline CFG/SSA (Braun et al. 2013), la dominance, le
NaN-boxing VM, la sécurité de pile VM, les frames/IP/jumps, la linéarisation C3 (MRO), les structs/traits, le desugaring
opérateurs (sémantique, pureté, broadcast), et le cache. Si make proof passe, les théorèmes sont validés
mécaniquement. Ces preuves portent sur des modèles formels alignés avec le code Rust, pas sur l'exécution du runtime en
production. L'alignement est maintenu explicitement dans les commentaires en tête de chaque fichier .v. Tree-sitter et
Cranelift ne sont pas formellement prouvés dans ce repo.
Un parseur sans preuve est un parseur qui ne sait pas encore qu'il a tort.
Pourquoi des preuves formelles
Catnip utilise tree-sitter pour parser, et tree-sitter fait son travail correctement. Mais la grammaire déclarée dans
grammar.js encode des invariants implicites : la précédence de * sur +, l'associativité gauche de -, le fait que
not lie plus fort que and. Ces propriétés ne sont vérifiées par aucun test unitaire classique - un test vérifie
qu'un cas marche, pas que tous les cas marchent.
Les fichiers dans proof/ couvrent six axes :
- Syntaxe - invariants de parsing de la grammaire (
grammar.js) via un parseur à descente récursive formalisé. - Sémantique - propriétés structurelles du modèle dimensionnel (broadcast, ND-récursion).
- Runtime - IR opcodes, scopes (shadowing, isolation), pattern matching (6 types, déterminisme), fonctions (binding, trampoline TCO, tail detection), NaN-boxing VM (7 tags), VM opcodes et stack safety, frames/IP/jumps, C3 linearization (MRO), structs/traits (field access, method resolution, inheritance), desugaring opérateurs (injectivité, totalité, cohérence IR).
- Optimisations - 10/10 passes IR prouvées : strength reduction, blunt code, DCE, block flattening, constant folding, constant/copy propagation, CSE, DSE, tail recursion to loop.
- Analyses - liveness analysis (linéaire + CFG), dead store elimination, fixpoint, dominance CFG (idom, frontières).
- Infrastructure - CFG/SSA (single assignment, phi-nodes, GVN, LICM, CSE inter-blocs, DSE globale), cache (FIFO, LRU+TTL, memoization, atomic writes).
Coq vérifie chaque étape de raisonnement : si make proof passe, les propriétés sont vraies.
Ce ne sont pas des preuves du runtime lui-même. L'alignement entre les modèles Coq et le code Rust est maintenu
manuellement - les commentaires en tête de chaque fichier .v citent les définitions correspondantes.
Vue d'ensemble
A. Preuves syntaxiques
Prouvent précédence, associativité, non-ambiguïté et chaînage pour le modèle de parsing.
| Fichier | Couverture | Théorèmes clés |
|---|---|---|
GrammarProof.v |
CFG formelle (S -> AB), arbres, unicité, non-ambiguïté via yield | tree_sound, grammar_unambiguous_S, yield_injective, grammar_unambiguous |
CatnipAddMulProof.v |
+/*, précédence, associativité gauche, monotonie fuel, soundness |
fuel_mono, precedence_general, parser_sound, parse_full_sound |
CatnipExprProof.v |
Tour complet (or > and > not > cmp > add > mul), chaînage, desugaring | not_and_or_precedence, extract_chain, chain_desugar_correct_single, chain_desugar_correct_two |
CatnipExprMonoProof.v |
Monotonie fuel pour le parseur d'expressions complet (12 fonctions mutuelles) | fuel_mono, parse_bool_or_mono, parse_full_mono |
B. Preuves sémantiques
Prouvent cohérence, confluence, terminaison partielle et universalité du modèle dimensionnel.
| Fichier | Couverture | Théorèmes clés |
|---|---|---|
CatnipDimensional.v |
Broadcast, cohérence (lois de foncteur), confluence, filtrage, masque booléen | coherence_composition, eval_deterministic, eval_fusion, mask_map_commute |
CatnipDimensionalProps.v |
Universalité, lois structurelles, homomorphisme, algèbre de pipelines | universality, broadcast_unique, broadcast_concat, pipeline_normalization |
CatnipNDRecursion.v |
ND-récursion fuel-bounded, monotonie, déterminisme, terminaison partielle, mémoisation | nd_eval_mono, nd_eval_deterministic, nd_partial_termination, memo_coherence |
CatnipBroadcastOverload.v |
Broadcast/ND sous surcharge opérateurs : invariants de shape, composition, déterminisme | overloaded_broadcast_preserves_length, overloaded_broadcast_composition, nd_overloaded_deterministic |
C. Modèle IR
Formalise la structure de l'IR et ses invariants structurels.
| Fichier | Couverture | Théorèmes clés |
|---|---|---|
CatnipIR.v |
IROpCode, IR, bijection, classification, arity | opcode_to_nat_injective, opcode_roundtrip, control_flow_not_arithmetic |
D. Preuves runtime
Prouvent les invariants des composants d'exécution : scopes, pattern matching, fonctions.
| Fichier | Couverture | Théorèmes clés |
|---|---|---|
CatnipScopeProof.v |
Lookup/set O(1), shadowing, push/pop, frames isolées | scope_set_lookup_same, scope_push_pop, scope_shadowing, scope_pop_restores |
CatnipPatternProof.v |
6 types de patterns, guards, dispatch, déterminisme | wildcard_always_matches, or_first_match_wins, match_pattern_deterministic |
CatnipFunctionProof.v |
Binding params (positional, defaults), trampoline TCO, scope depth, tail detection | bind_params_exact_length, trampoline_normal_terminates, trampoline_preserves_scope_depth, tail_position_produces_tailcall |
E. Preuves d'optimisation
Prouvent la correction des 10/10 passes IR du pipeline.
| Fichier | Couverture | Théorèmes clés |
|---|---|---|
CatnipStrengthRedProof.v |
Strength reduction (20 identités algébriques + correction sémantique) | sr_mul_one_r, sr_pow_two, strength_reduce_bool_sound |
CatnipBluntCodeProof.v |
Blunt code (boolean algebra, inversion cmp, idempotence, complément) | blunt_double_neg, blunt_and_complement, simplify_blunt_bool_sound |
CatnipDCEFlattenProof.v |
DCE, block flattening, composition de passes, lowering IR | flatten_stmts_idempotent, flatten_block_sound, compose_preserves_eval |
CatnipOptimProof.v |
Façade (Require Export des 3 fichiers ci-dessus) |
- |
CatnipConstFoldProof.v |
Constant folding (arith+cmp+bool+bitwise), guards div/0 et b\<0 | cf_add_fold, cf_truediv_fold, cf_pow_fold, cf_band_fold, cf_add_fold_sem |
CatnipStorePropProof.v |
Store model, constant propagation, copy propagation, CSE (structural equality) | const_prop_correct, copy_chain_terminates, cse_replace_correct |
CatnipTailRecLoopProof.v |
Tail recursion → loop, fuel monotonie, two-phase rebinding | tail_rec_loop_equiv, rebind_two_phase, fuel_monotone |
CatnipPurityProof.v |
Pureté sous surcharge opérateurs : struct ops hors pure_ops, non CSE-eligible | overloaded_op_never_eligible, desugared_builtins_are_pure, call_not_pure |
F. Preuves d'analyse et CFG
Prouvent la correction de l'analyse de liveness, de la dominance, et du pipeline CFG/SSA.
| Fichier | Couverture | Théorèmes clés |
|---|---|---|
CatnipVarSet.v |
Bibliothèque VarSet réutilisable : add, union, remove, NoDup, subset | add_preserves_nodup, union_preserves_nodup, remove_list_subset |
CatnipLivenessLinear.v |
Liveness linéaire : USE/DEF, transfer, backward analysis, DSE linéaire | live_in_sound, dse_linear_correct, transfer_monotone |
CatnipLivenessCFG.v |
Liveness CFG : LiveMap, fixpoint itératif, DSE CFG, path soundness | dse_cfg_sound_head, exec_path_sound, iterate_cfg_stable |
CatnipLivenessProof.v |
Façade (Require Export des 3 fichiers ci-dessus) |
- |
CatnipDominanceProof.v |
Dominance CFG : réflexivité, transitivité, antisymétrie, idom unicité, frontières | dom_refl, entry_dom_all, dom_trans, dom_antisym, idom_unique, entry_frontier_empty |
CatnipCFGSSABase.v |
SSA base : modèle SSA, utilitaires, modèles opérationnels (construction SSA, use-count, DSE) | ssaval_eqb_eq, unique_def_from_def_block, no_dup_phi_from_lookup, seal_block_sealed, dse_iterate_mono |
CatnipCFGSSACorrectness.v |
SSA correctness (49 lemmes/théorèmes, 0 Admitted) : single assignment, phi-nodes, CSE, GVN, LICM, DSE, destruction | single_assignment, def_before_use, phi_at_frontier, cse_same_key_same_value, licm_hoist_sound, dse_fixpoint_terminates |
CatnipCFGSSAProof.v |
Façade de compatibilité (Require Export de CatnipCFGSSABase + CatnipCFGSSACorrectness) |
- |
G. Preuves runtime avancées
Prouvent les invariants des composants runtime avancés.
| Fichier | Couverture | Théorèmes clés |
|---|---|---|
CatnipNanBoxProof.v |
NaN-boxing VM : 8 tags (SmallInt/Bool/Nil/Symbol/PyObj/Struct/BigInt/VMFunc), encoding 47-bit | tag_injective, encode_decode_roundtrip, smallint_range, promote_demote_id |
CatnipVMOpCode.v |
VM opcodes (83), bijection VMOpCode <-> nat, injectivité, range [1..83] |
vm_opcode_to_nat_injective, vm_opcode_roundtrip, nat_to_vm_opcode_roundtrip |
CatnipVMState.v |
Stack effect model (Fixed/ArgDependent), VMState record, classification |
fixed_effect_count |
CatnipVMStackSafety.v |
Stack safety, net effects par catégorie, arg-dependent effects, instruction sequences | stack_safety_fixed, exec_seq_app, call_stack_safety, exit_stack_safety, membership_net_effect |
CatnipVMInvariants.v |
Compilation invariants : expression net +1, statement net 0, DupTop/RotTwo | load_net_plus_one, binop_pattern_depth, assignment_pattern_depth |
CatnipVMExamples.v |
10 exemples concrets, classification completeness (effect_total, arg_dependent_opcodes) | effect_total, arg_dependent_opcodes |
CatnipVMFrame.v |
VM frames (locals, IP, jumps, block stack, ForRange encoding roundtrips) | get_set_same, ip_advance_in_bounds, jump_preserves_bounds, push_pop_restores, for_range_full_roundtrip |
CatnipArithProof.v |
Floor div/mod (Python semantics), equality, overflow promotion | floor_div_mod_identity, floor_mod_sign, exact_div_mod_zero, neg_overflow_only_min |
CatnipPureFrameProof.v |
PureFrame bind_args, copy_args, fill_defaults, pool alloc/free | copy_args_slot_bound, bind_args_length, bind_args_no_defaults, pool_round_trip |
CatnipVMProof.v |
Façade (Require Export des 5 modules VM + CatnipVMFrame) |
- |
CatnipMROC3Core.v |
C3 merge algorithm, self-first property | c3_self_first, c3_self_is_head, c3_no_parents |
CatnipMROC3Properties.v |
C3 local precedence and monotonicity | c3_preserves_local_precedence, c3_monotonicity, c3_merge_preserves_order |
CatnipMROFields.v |
MRO field merge, diamond dedup, redefinition detection | dedup_at_most_once, no_redefinition_correct, redefinition_detected |
CatnipMROMethods.v |
MRO method resolution, left-priority | left_priority, merge_methods_subset |
CatnipMROSuper.v |
Super resolution, cooperative termination | super_at_self, super_at_end, super_max_steps, super_from_last_is_empty |
CatnipMROExamples.v |
Exemples concrets (diamond, linear, inconsistent, init chain) | diamond_c3, inconsistent_c3, diamond_method_resolution, super_from_B_in_diamond |
CatnipMROProof.v |
Facade (Require Export des 6 modules MRO ci-dessus) |
- |
CatnipOpDesugar.v |
Desugaring opérateurs : symbol x arity -> method name, injectivité, totalité | desugar_injective, desugar_total |
CatnipOpDesugarProps.v |
Disambiguation +/-, distinctness, résolvabilité méthode, cohérence opcode, préfixe op_ | arity_disambiguation_minus, desugar_names_distinct, desugar_method_resolvable, desugar_opcode_consistent |
CatnipOpDesugarExamples.v |
Exemples concrets (Vec2, disambiguation unaire/binaire, cas négatifs) | vec2_find_add, minus_as_binary, minus_as_unary, eq_not_unary |
CatnipOpDesugarSemantics.v |
Préservation sémantique : dispatch VM = appel méthode, reverse dispatch, roundtrip opcode, déterminisme | operator_dispatch_is_method_call, dispatch_finds_same_method, reverse_dispatch_finds_method, forward_priority_over_reverse, rev_dispatch_subsumes_old, no_dispatch_ambiguity |
CatnipStructProof.v |
Structs/traits : field access O(1), method resolution, inheritance, super chain | field_access_sound, method_resolution_order, super_chain_terminates, trait_linearization |
CatnipCacheKey.v |
Cache keys : CacheType, encoding Z, injectivité, disjointness | cache_key_injective, cache_type_disjoint, cache_key_bijection |
CatnipCacheMemory.v |
MemoryCache FIFO : find/remove/set, key uniqueness, round-trip, eviction | mc_set_get_same, mc_fifo_evicts_oldest, mc_set_size_bounded |
CatnipCacheDisk.v |
DiskCache LRU+TTL : expiration, prune, eviction, atomic writes | dc_get_ttl_enforcement, dc_lru_evict_size, atomic_write_no_partial |
CatnipCacheAdapter.v |
CatnipCache adapter, Memoization, invalidation (16 keys) | memo_set_get_same, invalidation_covers_all, all_invalidation_keys_nodup |
CatnipCacheProof.v |
Façade (Require Export des 4 fichiers ci-dessus) |
- |
A. Preuves syntaxiques
GrammarProof.v
Modèle minimal d'une CFG (S -> A B, A -> "a", B -> "b") avec trois résultats :
Soundness des arbres : tout arbre de dérivation produit une séquence de terminaux dérivable depuis le non-terminal
racine (tree_sound). La preuve construit la chaîne de réécritures explicitement.
Génération : la grammaire engendre bien [a; b] (generates_example_ab), par application successive des trois
productions.
Non-ambiguïté structurelle : pour chaque non-terminal, il n'existe qu'une seule forme d'arbre
(grammar_unambiguous_S). La preuve procède par destruction dépendante - Coq élimine structurellement toute
alternative.
Non-ambiguïté via yield : formulation standard en théorie des langages - si deux arbres pour le même non-terminal
produisent la même chaîne de terminaux, les arbres sont identiques (grammar_unambiguous). La preuve passe par
yield_injective (injectivité de la fonction yield), puis congruence. La complétude est aussi prouvée : tout arbre
pour S engendre [ta; tb] et dérive via la relation (yield_S_unique, tree_complete_S).
Ce fichier ne modélise pas directement le parseur Catnip. Il pose le vocabulaire (dérivation, arbre, ambiguïté) utilisé implicitement par les deux fichiers suivants.
CatnipAddMulProof.v
Formalise le fragment arithmétique de grammar.js :
_additive -> additive | _multiplicative
additive -> _additive ("+" | "-") _multiplicative (left)
_multiplicative -> multiplicative | _exponent
multiplicative -> _multiplicative ("*" | "/" | "//" | "%") _exponent (left)
Le modèle Coq garde + et * avec atomes parenthésés. Cinq fonctions mutuellement récursives (parse_expr,
parse_expr_tail, parse_term, parse_term_tail, parse_factor) implémentent un RDP classique.
Précédence et associativité (exemples concrets) :
- Précédence (
precedence_example) :n + n * nparse commen + (n * n), pas(n + n) * n - Associativité gauche (
left_assoc_add_example) :n + n + nparse comme(n + n) + n - Parenthèses (
paren_example) :(n + n) * nparse comme(n + n) * n- les parenthèses forcent la structure
Le déterminisme (parse_expr_deterministic) est trivial : deux appels sur la même entrée produisent le même résultat,
par réécriture et inversion.
Monotonie du fuel (fuel_mono) : si le parseur réussit avec un fuel f, il réussit avec le même résultat pour tout
f' >= f. Le théorème est un 5-conjonction prouvé par induction sur le fuel, avec lemmes de dépliage
(parse_expr_unfold, etc.) pour exposer la structure récursive. Les corollaires parse_expr_mono et parse_full_mono
fournissent les projections utiles.
Résultats indépendants du fuel : avec la monotonie, les propriétés de précédence et d'associativité sont
généralisées à tout fuel suffisant (precedence_general : forall fuel, fuel >= 5 -> ...). La tactique
solve_fuel_general automatise la preuve : fournir un témoin concret, puis lia.
Soundness : une relation de parsing inductive (parses_expr, parses_expr_tail, etc.) avec 8 constructeurs miroir
la structure du parseur. Le théorème parser_sound (5-conjonction par induction sur le fuel) prouve que si le parseur
retourne Some, le résultat satisfait la relation. Le corollaire parse_full_sound combine soundness et consommation
complète de l'entrée.
CatnipExprProof.v
Étend le modèle au tour de précédence complet de Catnip :
bool_or > bool_and > bool_not > comparison > additive > multiplicative > atom
Douze fonctions mutuellement récursives (6 niveaux de précédence, chacun avec sa tail function, plus parse_bool_not et
parse_atom). Les tokens couvrent les 4 opérateurs arithmétiques (+, -, *, /), les 6 opérateurs de comparaison
(<, <=, >, >=, ==, !=), les 3 opérateurs booléens (and, or, not), les parenthèses et les littéraux.
Douze fonctions mutuellement récursives. Coq n'a pas bronché. Le reviewer, si.
Précédence arithmétique : * lie plus fort que + (mul_over_add), - est associatif gauche (sub_left_assoc).
Précédence booléenne : not > and > or vérifié sur not n < n or false qui parse comme (not (n < n)) or false
(not_and_or_precedence). Les comparaisons lient plus fort que and (cmp_over_and).
Parenthèses : forcent la structure à travers tous les niveaux (paren_override_bool).
Opérateurs de comparaison : les 6 opérateurs sont testés individuellement (comparison_ops_examples) - chacun
produit le bon noeud AST.
Chaînage : n < n <= n parse comme (n < n) <= n syntaxiquement (comparison_chain_example). Le fichier inclut
aussi un modèle sémantique : eval_comp_chain évalue une chaîne de comparaisons comme une conjonction. Le théorème
chain_two_ops_desugars_to_and prouve que a op1 b op2 c est équivalent à (a op1 b) and (b op2 c).
Desugaring des chaînes : extract_chain extrait récursivement la base et les opérateurs d'un AST de comparaisons
imbriquées à gauche. Trois propriétés structurelles prouvées : la chaîne n'est jamais vide (extract_chain_nonempty),
la base n'est jamais elle-même une comparaison (extract_chain_base_non_cmp), et les non-comparaisons donnent None
(extract_chain_non_cmp). La correction sémantique est prouvée pour les chaînes à 1 et 2 opérateurs
(chain_desugar_correct_single, chain_desugar_correct_two).
CatnipExprMonoProof.v
Monotonie du fuel pour le parseur d'expressions complet, split de CatnipExprProof.v pour réduire la mémoire de
compilation.
Monotonie du fuel (fuel_mono) : même structure que CatnipAddMulProof.v, mais avec 12 conjonctions (une par
fonction mutuellement récursive). Les 6 opérateurs de comparaison sont traités uniformément dans parse_comparison_tail
par semi-colon tactique. Corollaires : parse_bool_or_mono, parse_full_mono.
Les lemmes de dépliage (parse_*_unfold) exposent la structure récursive des 12 fonctions pour que les hypothèses
d'induction soient applicables.
B. Preuves sémantiques
CatnipDimensional.v
Formalise le modèle dimensionnel de Catnip : le domaine de valeurs (Scalar nat | Coll (list Val)), le broadcast
(.[op]), et le filtrage (.[if p]).
Trois groupes de résultats :
Cohérence (lois de foncteur) : broadcast_map satisfait identité (v.[id] = v) et composition
(xs.[f].[g] = xs.[g . f]). Le broadcast préserve la longueur des collections et fixe le topos vide.
Confluence : l'évaluation des expressions broadcast est totale et déterministe (eval_deterministic). Si une
expression évalue vers v1 et v2, alors v1 = v2. La fusion de broadcasts chaînés est prouvée (eval_fusion).
Propriétés de base : masque booléen (mask_select), sémantique un-niveau (broadcast_shallow), absorption de
filtres (filter_filter).
CatnipDimensionalProps.v
Propriétés structurelles et algébriques avancées, split de CatnipDimensional.v pour réduire la mémoire de compilation.
Universalité : toute opération elementwise (qui préserve la longueur et commute avec l'extraction par index) sur
une collection est nécessairement Coll (map op xs) - c'est-à-dire broadcast_map op (universality). Il n'existe pas
d'autre façon de lifter une opération aux collections avec ces propriétés (broadcast_unique). Pour les collections
plates, le broadcast est entièrement déterminé par son noyau scalaire (broadcast_minimal_flat). Le broadcast Catnip
est caractérisé comme le foncteur libre sur les collections plates sous les axiomes d'elementwise-ness.
# Le broadcast est un foncteur libre (loi de composition)
# Pour des fonctions pures f et g :
# F(id) = id
data.[x => x] == data
# F(f . g) = F(f) . F(g)
# viva la composition
data.[g].[f] == data.[x => f(g(x))]
Propriétés non triviales : lois structurelles prouvées par induction :
- Filter-map pullback (
filter_map_pullback) : filtrer après mapper = mapper après filtrer avec le prédicat tiré en arrièrep . f. Généralisefilter_map_commutesans hypothèse d'invariance. - Absorption de filtres (
filter_filter) : deux filtres successifs = un filtre avec conjonction des prédicats. - Pullback broadcast-filter-map (
broadcast_filter_map) : corollaire au niveauVal. - Masque booléen (
mask_select) : all-true = identité (mask_all_true), all-false = vide (mask_all_false), commutation avec map (mask_map_commute), borne de longueur (mask_length_le). - Homomorphisme de liste (
broadcast_concat) : le broadcast distribue sur la concaténation - fondation algébrique de la parallélisation. - Sémantique un-niveau (
broadcast_shallow,broadcast_two_levels) : le broadcast opère à exactement un niveau de profondeur ; le broadcast récursif nécessite un emboîtement explicite. - Homomorphisme de monoide (
fold_broadcast_exchange) : quandfdistribue suropet fixez,fcommute avec le fold. Corollaire : fold sur un broadcast = fold avec accumulateur composé (fold_broadcast_map).
Algèbre de pipelines : formalise les pipelines comme séquences d'opérations PMap / PFilt et prouve trois
résultats :
- Fusion complète (
map_chain_fusion) : toute chaîne de n broadcasts maps se réduit à un seul broadcast avec la fonction composée. Sûre (préserve la sémantique) et complète (n quelconque). - Equivalence transformationnelle : trois règles de réécriture prouvées correctes - map; map fusionne
(
equiv_map_map), map;filter se réordonne en filter;map (equiv_filter_map_swap), filter;filter s'absorbe (equiv_filter_filter). - Normalisation (
pipeline_normalization) : tout pipeline mixte de maps et filters sur une collection se réduit à une forme canoniquefilter;map. Deux pipelines qui ont la même forme normale sont équivalents (normalization_sound).
CatnipNDRecursion.v
Modèle parametrique de la ND-récursion (~~(seed, lambda)), split de CatnipDimensional.v. Indépendant du domaine de
valeurs.
Monotonie (nd_eval_mono) : si l'évaluation réussit avec un fuel f, elle réussit avec le même résultat pour tout
f' >= f.
Déterminisme (nd_eval_deterministic) : deux évaluations avec un fuel suffisant produisent le même résultat.
Terminaison partielle (nd_partial_termination) : quand step_seed garantit une mesure strictement décroissante,
la récursion termine pour un fuel suffisant.
Mémoisation (memo_coherence) : si une valeur est dans le cache pour un seed, elle correspond au résultat de
l'évaluation.
C. Modèle IR
CatnipIR.v
Formalise les opcodes de catnip_rs/src/ir/opcode.rs comme un inductif Coq IROpCode. Chaque opcode reçoit une
numérotation via opcode_to_nat : IROpCode -> nat (bijection) et nat_to_opcode inverse.
Injectivité et roundtrip : opcode_to_nat_injective (numérotation injective), opcode_roundtrip (aller-retour nat
-> opcode -> nat = identité). Prouvés par énumération exhaustive.
Classification : prédicats is_literal, is_op, is_pattern, is_collection, is_comparison_op,
is_control_flow_op, is_arithmetic_op avec propriétés de disjointness (literal_not_op,
control_flow_not_arithmetic).
IR : inductif représentant les noeuds IR (IRInt, IRFloat, IRBool, IRStr, IRNone, IRDecimal, IRImaginary, IROp), avec
ir_size (taille structurelle), ir_op (constructeur par opcode + args), ir_binop (spécialisation binaire).
Ce fichier sert de fondation importée par CatnipStrengthRedProof.v, CatnipBluntCodeProof.v et
CatnipDCEFlattenProof.v.
D. Preuves runtime
CatnipScopeProof.v
Modélise le système de scopes de catnip_rs/src/core/scope.rs. Un environnement (env) est un string -> option Z, un
scope est une pile de frames avec opérations push_scope / pop_scope.
Lookup/set : env_lookup_set_same (lire ce qu'on vient d'écrire), env_lookup_set_other (écrire ne touche pas les
autres variables). Même résultats liftés au scope : scope_set_lookup_same, scope_set_lookup_other.
Push/pop : scope_push_pop (push puis pop = identité pour les variables pré-existantes),
scope_push_preserves_lookup (push ne touche pas les variables existantes).
Shadowing : scope_shadowing (une variable dans un frame enfant masque le parent), scope_pop_restores (pop
restaure la valeur d'avant le push), scope_shadow_restore (composition des deux).
Frames isolées : modèle étendu (scope_ex) avec flag d'isolation par frame. scopeex_isolated_shadow (les frames
isolées ne voient pas les variables du parent), scopeex_isolated_restore (le pop restaure correctement même avec
isolation).
CatnipPatternProof.v
Modélise les 6 types de patterns de catnip_rs/src/core/pattern.rs. Chaque pattern est un inductif (PatWild,
PatLit, PatVar, PatOr, PatTuple, PatStruct) avec match_pattern : Pattern -> Value -> option Bindings.
Wildcard : matche toujours, ne capture rien (wildcard_always_matches, wildcard_no_bindings).
Variable : matche toujours, capture la valeur (var_always_matches, var_captures_value, var_single_binding).
Literal : matche si et seulement si les valeurs sont égales (literal_matches_equal, literal_rejects_different).
OR : premier match gagne (or_first_match_wins), singleton = pattern nu (or_singleton), wildcard dans un OR
attrape tout (or_with_wildcard).
Tuple/Struct : mismatch de longueur = échec (tuple_length_mismatch), type mismatch = échec
(struct_type_mismatch).
Dispatch : match_cases_first_wins (premier case qui matche est sélectionné), match_cases_guard_fail (si le guard
échoue, on passe au case suivant), match_cases_wildcard_catches_all (un wildcard en dernier case attrape tout).
Déterminisme : match_pattern_deterministic et match_cases_deterministic - le résultat du match est unique.
CatnipFunctionProof.v
Modélise les fonctions et le trampoline TCO de catnip_rs/src/core/registry/functions.rs et
catnip_rs/src/core/nodes.rs.
Binding de paramètres : bind_params prend une liste de specs (name, default) et une liste d'arguments, produit
un environnement. bind_params_exact_length (si autant d'args que de params, chaque param reçoit l'arg correspondant),
bind_params_all_defaults (0 args = tous les defaults), bind_params_missing_required (param sans default et sans arg
= erreur).
Trampoline TCO : modèle fuel-bounded trampoline fuel scope body. Le body produit soit un Normal v (terminaison),
soit un Tail args (tail call = rebind + continuer). trampoline_normal_terminates (un Normal termine immédiatement),
trampoline_tail_continues (un Tail rebind les params et relance), trampoline_fuel_monotone, trampoline_two_steps,
trampoline_three_steps (exemples multi-itérations).
Scope : trampoline_preserves_scope_depth - le trampoline ne modifie pas la profondeur du scope.
Tail detection : tail_position_produces_tailcall (si le TCO est actif et la position est tail, un appel récursif
produit un TailCall), non_tail_produces_normal (en position non-tail, l'appel est normal),
tco_disabled_produces_normal (si TCO désactivé, pas de TailCall).
E. Preuves d'optimisation
CatnipOptimProof.v (facade)
Façade qui re-exporte les 3 modules suivants. Source de vérité : catnip_rs/src/semantic/strength_reduction.rs,
blunt_code.rs, dead_code_elimination.rs, block_flattening.rs.
Le modèle d'expressions Expr (Const, BConst, Var, BinOp, UnOp, IfExpr, WhileExpr, Block, MatchExpr) avec évaluateur
partiel eval_expr est défini dans CatnipExprModel.v.
CatnipStrengthRedProof.v
Strength reduction (strength_reduce : Expr -> Expr) : 20 théorèmes individuels couvrant les identités
multiplicatives (x * 1 = x, x * 0 = 0), exponentielles (x^2 -> x*x, x^1 = x, x^0 = 1), additives (x + 0 = x,
x - 0 = x), division (x / 1 = x), et booléennes (x && True = x, x || False = x, x && False = False,
x || True = True). Correction sémantique prouvée pour les cas arithmétiques. Théorème principal :
strength_reduce_bool_sound (la passe préserve eval_bool).
CatnipBluntCodeProof.v
Blunt code (simplify_blunt : Expr -> Expr) : double négation (not not x = x), inversion de comparaisons
(not (a < b) = a >= b) avec preuve d'involution de invert_cmp, simplification booléenne (x == True = x),
idempotence (x && x = x, x || x = x), complément (x && not x = False, x || not x = True). Les preuves de
complément utilisent des lemmes de taille structurelle (expr_eqb_not_self : x n'est jamais structurellement égal à
UnOp Not x). Inclut expr_eqb_eq (réflexion de l'égalité structurelle). Théorème principal :
simplify_blunt_bool_sound.
CatnipDCEFlattenProof.v
Dead code elimination (eliminate_dead : Expr -> option Expr) : if True { t } else { f } -> t, if False -> f,
while False { body } -> eliminated, Block [] -> eliminated, Block [e] -> e. Correction sémantique via eval_expr.
Block flattening (flatten_block : Expr -> Expr) : aplatit les blocs imbriqués
(Block [s1, Block [s2, s3], s4] -> Block [s1, s2, s3, s4]). Distributivité sur append (flatten_stmts_app).
Idempotence (flatten_stmts_idempotent, flatten_block_idempotent) prouvée via un lemme intermédiaire : la sortie de
flatten_stmts ne contient jamais de Block au top-level (flatten_stmts_output_no_blocks).
Composition : compose_passes (fold_left), preserves_eval (une passe préserve la sémantique),
compose_preserves_eval (la composition de passes qui préservent la sémantique préserve la sémantique),
compose_two_idempotent (conditions d'idempotence pour la composition de deux passes).
Les 10 passes du pipeline IR sont toutes prouvées. Les 6 passes store-based (constant folding, constant/copy propagation, CSE, DSE, tail rec to loop) ont été ajoutées dans
CatnipConstFoldProof.v,CatnipStorePropProof.v,CatnipLivenessProof.vetCatnipTailRecLoopProof.v.
CatnipVMOpCode.v
Modélise les 83 opcodes VM de catnip_core/src/vm/opcode.rs. Bijection VMOpCode <-> nat avec
vm_opcode_to_nat_injective et vm_opcode_roundtrip. 83 constructeurs (repr(u8) 1..83). Même technique que
CatnipIR.v pour les IR opcodes.
CatnipVMState.v
Définit le modèle d'état VM et la classification des effets de pile. Chaque opcode a un effet (pops, pushes). 68
opcodes ont un effet fixe (connu statiquement), 15 sont arg-dépendants (Call, BuildList, BuildDict, Exit, etc.).
Prédicats is_fixed_effect, get_pops, get_pushes.
CatnipVMStackSafety.v
Prouve la sécurité de pile et les propriétés d'exécution.
Stack safety : théorème central stack_safety_fixed - pour tout opcode à effet fixe, si la pile a au moins pops
éléments, l'exécution produit une pile de profondeur depth - pops + pushes, sans underflow. Pour les opcodes
arg-dépendants, safety prouvée paramétriquement (call_stack_safety, build_seq_stack_safety,
build_dict_stack_safety, unpack_seq_stack_safety, exit_stack_safety).
Propriétés par catégorie : net effects uniformes - arithmétique = -1, comparaison = -1, membership/identity = -1,
unaire = 0 (inclut ToBool), load = +1, store = -1, noop = 0 (inclut MatchFail), conditional jumps = Fixed 1 0
(inclut JumpIfNotNoneOrPop), match transforms = Fixed 1 1 (inclut MatchAssignPatternVM).
Exit : arg-dépendant (arg=0 : pops 0, arg=1 : pops 1). exit_zero_noop, exit_one_requires_one.
Instruction sequences : exec_seq exécute une liste d'instructions, exec_seq_app prouve la composition.
CatnipVMInvariants.v
Invariants du compilateur. Expression = net +1 (binop_pattern_depth, membership_pattern_depth), statement = net 0
(assignment_pattern_depth, discard_pattern_depth). Propriétés DupTop (net +1) et RotTwo (net 0).
CatnipVMExamples.v
10 exemples concrets d'exécution (addition, assignation, négation, expressions imbriquées, membership, etc.) prouvés par
réflexivité. Classification exhaustive : effect_total (tout opcode est Fixed ou ArgDependent), arg_dependent_opcodes
(énumération des 15).
CatnipVMFrame.v
Modélise les frames VM de catnip_rs/src/vm/frame.rs et prouve les invariants de gestion mémoire et de contrôle.
Locals : modèle de vecteur à taille fixe initialisé à nil. get_set_same (roundtrip lecture/écriture),
get_set_other (écriture ne touche pas les autres slots), set_local_preserves_wf (la taille du vecteur est
préservée).
IP safety : ip_initial_valid (IP initial en bounds), ip_advance_in_bounds (avancer reste en bounds si pas au
dernier), ip_fetch_some (fetch réussit si en bounds), ip_exit (IP = len(code) signifie terminaison).
Jump safety : is_jump_op_enumerated (classification exhaustive des opcodes de saut), jump_preserves_bounds (un
saut vers une cible valide reste en bounds), non_jump_advances (un opcode non-saut avance l'IP de 1),
jump_ops_fixed_effect (les jumps ont un effet de pile fixe de 0).
Block stack : push_pop_restores (push puis pop = identité), push_block_depth / pop_block_depth (profondeur
incrémente/décrémente), push_pop_saved_region (la région sauvegardée est restaurée).
ForRange encoding : roundtrips pour le bitpacking ForRangeInt (slot_i, slot_stop, step_sign, offset) et
ForRangeStep (slot_i, step, target). 7 théorèmes de roundtrip individuels couvrant chaque champ.
CatnipArithProof.v
Prouve les propriétés des opérations arithmétiques pures de catnip_vm/src/ops/arith.rs. Couvre floor division et floor
modulo (sémantique Python, distincte de la division tronquée C), propriétés d'égalité native, et correction de la
promotion overflow SmallInt vers BigInt.
Théorèmes clés : floor_div_mod_identity (a = q*b + r pour tout b != 0), floor_mod_sign (le reste a le signe du
diviseur), floor_mod_bound_pos/neg (bornes du reste), exact_div_mod_zero (division exacte implique reste nul),
neg_overflow_only_min (seul -SMALLINT_MIN déborde en négation). 10 exemples concrets validés par réflexivité contre
les résultats Python.
CatnipPureFrameProof.v
Prouve les propriétés spécifiques au PureFrame de catnip_vm/src/vm/frame.rs : liaison d'arguments positionnels
(bind_args), copie dans les slots locaux, remplissage des valeurs par défaut, et invariants du pool de frames.
Théorèmes clés : copy_args_slot_bound (les arguments atterrissent aux bons slots), copy_args_unbound_nil (slots non
liés restent Nil), bind_args_length (préserve la taille des locals), bind_args_no_defaults (sans défauts, les slots
correspondent aux args), pool_alloc_fresh / pool_alloc_all_nil (alloc produit des locals propres),
pool_free_bounded (taille du pool bornée), pool_round_trip (free puis alloc = frame propre).
CatnipVMProof.v
Façade (Require Export des 5 modules VM + CatnipVMFrame).
CatnipMRO*.v (6 modules)
Modélisent la linéarisation C3 et la résolution MRO de catnip_rs/src/vm/mro.rs. Standalone, sans dépendances sur les
autres preuves Catnip. CatnipMROProof.v est une facade (Require Export des 6 modules).
CatnipMROC3Core.v : Algorithme C3 merge. c3_self_first (la classe courante est toujours en tête du MRO),
c3_no_parents (classe sans parents = MRO singleton).
CatnipMROC3Properties.v : Propriétés C3 (dépend de C3Core). c3_preserves_local_precedence (l'ordre de déclaration
des parents est respecté dans le MRO), c3_monotonicity (le MRO d'un parent est un sous-ordre du MRO de l'enfant),
c3_merge_preserves_order (l'ordre relatif des séquences d'entrée est préservé dans le résultat).
CatnipMROFields.v : Field merge (indépendant du C3). dedup_at_most_once (chaque champ apparaît au plus une fois
après fusion), no_redefinition_correct / redefinition_detected (détection de redéfinition de champs entre classe
enfant et héritage).
CatnipMROMethods.v : Method resolution (indépendant du C3). left_priority (premier parent dans le MRO gagne pour
la résolution de méthode), merge_methods_deterministic (la fusion est déterministe).
CatnipMROSuper.v : Super resolution (dépend de Methods). super_at_self (super depuis la classe courante commence
au parent suivant dans le MRO), super_tail_bounded / super_max_steps (la chaîne super termine en au plus |MRO|
pas), super_from_last_is_empty (super depuis la dernière classe du MRO = vide).
CatnipMROExamples.v : Exemples concrets (dépend de tous les modules). Diamond, linear chain, inconsistency, init chaining, field dedup.
CatnipOpDesugar*.v (4 modules)
Modélisent le desugaring des opérateurs surchargés (op <symbol>) de catnip_core/src/parser/pure_transforms.rs. Le
mapping (symbol, arity) → method_name est prouvé injectif et total.
CatnipOpDesugar.v : Modèle du mapping (19 symboles, 21 combinaisons valides sur 38). desugar_injective (deux
couples distincts ne produisent jamais le même nom), desugar_total (toute combinaison valide produit un nom).
CatnipOpDesugarProps.v : Propriétés dérivées. arity_disambiguation_minus/plus (- et + unaire vs binaire
donnent des noms différents), desugar_names_distinct (corollaire d'injectivité), invalid_combinations_fail (17 cas
invalides), desugar_method_resolvable (connexion avec find_method de CatnipStructProof), desugar_opcode_consistent
(cohérence avec les IROpCode), op_prefix_preserved (tous les noms commencent par op_).
CatnipOpDesugarExamples.v : Exemples concrets. Vec2 (struct avec méthodes opérateur), disambiguation - unaire vs
binaire sur un même struct, cas négatifs (SymEq Unary = None, etc.).
CatnipOpDesugarSemantics.v : Préservation sémantique du dispatch VM. Modélise le dispatch two-phase de vm/core.rs
(native op → struct method) et le dispatch reverse (prim OP struct → méthode du struct).
operator_dispatch_is_method_call (pour les structs, le dispatch VM = appel méthode direct),
reverse_dispatch_finds_method (prim OP struct dispatche vers la méthode du struct), forward_priority_over_reverse
(le dispatch forward a priorité), rev_dispatch_subsumes_old (rétro-compatibilité : l'ancien dispatch est un cas
particulier du nouveau), opcode_roundtrip (symbol → method name → opcode → même opcode), no_dispatch_ambiguity (un
symbole résout vers exactement une méthode).
F. Preuves d'analyse
CatnipLivenessProof.v (facade)
Façade qui re-exporte les 3 modules suivants. Modélise l'analyse de liveness et la dead store elimination (DSE), d'abord sur des blocs linéaires puis sur un CFG. 48 lemmes/théorèmes.
CatnipVarSet.v
Bibliothèque réutilisable pour les ensembles de variables (VarSet = list nat). Opérations : vs_add, vs_union,
vs_remove_var, vs_remove_list. Propriétés : préservation de NoDup, inclusion (vs_subset), membership. Utilisée
par les deux fichiers suivants.
CatnipLivenessLinear.v
Modèle : variables nat, instructions avec ensembles USE/DEF, états Var -> nat.
Liveness linéaire : la fonction de transfert transfer calcule live_in = (live_out \ defs) ∪ uses. Monotonie de
la fonction de transfert (transfer_monotone). live_in_sound : si deux états coïncident sur les variables vivantes en
entrée, ils coïncident sur les variables vivantes en sortie après exécution du bloc.
DSE linéaire (dse_linear_correct) : les instructions mortes (assignation à une variable non vivante en sortie)
peuvent être supprimées sans changer l'état observable (variables vivantes en sortie).
CatnipLivenessCFG.v
CFG : extension au cas multi-blocs avec table de successeurs. La liveness inter-blocs utilise un fixpoint itératif
(iterate_cfg) avec monotonie (step_in_monotone) et convergence (iterate_cfg_stable).
DSE CFG (exec_path_sound) : pour tout chemin d'exécution dans le CFG, la DSE guidée par la liveness préserve les
variables vivantes. La preuve procède par induction sur le chemin.
Technique : récursion à carburant
Coq exige que toute récursion termine. Un RDP ne termine pas structurellement sur sa liste de tokens - la tail function
consomme des tokens mais Coq ne le voit pas. Les parseurs dans CatnipAddMulProof.v et CatnipExprProof.v utilisent le
pattern fuel-based recursion :
Fixpoint parse_expr (fuel : nat) (ts : list token) : option (expr * list token)
Le paramètre fuel décroît strictement à chaque appel récursif (destruct fuel as [|fuel'], appel sur fuel'). Quand
le fuel est épuisé, le parseur retourne None. Coq accepte la définition parce que fuel est un nat qui décroît
structurellement.
En pratique, les théorèmes par réflexivité (Proof. vm_compute; reflexivity. Qed.) calculent le résultat exact du
parseur sur une entrée concrète - Coq exécute le parseur et vérifie que le résultat correspond.
Monotonie : le théorème fuel_mono (prouvé dans les deux fichiers de parsing) établit que si le parseur réussit
avec un fuel f, il réussit avec le même résultat pour tout f' >= f. Cela élimine la dépendance aux constantes de
fuel spécifiques (32, 64) : les résultats quantifiés universellement (forall fuel, fuel >= k -> ...) sont dérivés en
fournissant un témoin concret puis en appliquant la monotonie via lia. La technique utilise des lemmes de dépliage
(parse_*_unfold : parse_*(S f) ts = ...) prouvés par reflexivity, qui exposent la structure récursive pour que les
hypothèses d'induction soient applicables.
Le parseur réel (tree-sitter) ne fonctionne pas par fuel mais par la structure de la grammaire PEG. Le fuel est un artefact de la preuve, pas du runtime.
Build
make proof # Compile les fichiers .v, vérifie toutes les preuves
make proof-clean # Supprime les artefacts Coq (.vo, .glob, Makefile.coq)
make proof-scan # Vérifie l'absence d'Admitted, Abort, axiomes, imports classiques
Prérequis : Coq installé (coqc, coq_makefile). Le fichier proof/_CoqProject liste les sources.
Références
- The Coq Proof Assistant - assistant de preuve utilisé
- Braun et al. 2013 - Simple and Efficient Construction of Static Single Assignment Form (utilisé dans le pipeline SSA de Catnip, ARCHITECTURE)
grammar.js- grammaire tree-sitter source de vérité, citée en tête de chaque fichier.v