Skip to content

Commit 3678d64

Browse files
hyperpolymathclaude
andcommitted
proof: non-empty module witness + discrimination proofs for items 7+8
Extends the structural agreement work (parent commit 1d09ef6) past the empty-list case with a concrete alloc/free module and a discrimination proof showing the L10 single-consumption rule has teeth. The minimal interesting case: a module exporting Produces 0 followed by Consumes 0. Builds witnesses through every ILA* / TF* constructor: * allocFreeModule — the ModuleSummary value. * allocFreeSpecAccepts — SpecAccepts witness built from raw ILAProduces / ILAConsumes / TFNil constructors. * allocFreeVerifierAccepts / allocFreeSourceAccepts — derived from the spec witness via the structural agreement directions. Closes the structural triangle for a real module. Discrimination — predicate has teeth. A predicate is only useful if it can REJECT. Without a Not (SpecAccepts m) proof the predicate could be vacuously true. This commit lands the first such proof: * badDoubleConsumeModule — one function with [Consumes 0, Consumes 0]. * notSpecAcceptsBadDoubleConsume : Not (SpecAccepts badDoubleConsumeModule) — by case-matching on the assumed witness, extracts the Not (0 = 0) embedded in TFConsumesOther, applies Refl, derives Void. Idris2's totality checker confirms the impossibility pattern is exhaustive. * notVerifierStructuralAcceptsBadDoubleConsume — symmetric on the structural verifier side. (VADifferential remains the differential-trust escape hatch by design.) Idris2 0.8.0 build green: 22/22 modules. Zero new believe_me, assert_total, postulate, sorry, assert_smaller. %default total preserved. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 1d09ef6 commit 3678d64

1 file changed

Lines changed: 96 additions & 0 deletions

File tree

src/abi/TypedWasm/ABI/VerifierSpec.idr

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,3 +465,99 @@ public export
465465
emptyModuleSourceAccepts :
466466
(n : String) -> SourceAccepts (MkModuleSummary n [])
467467
emptyModuleSourceAccepts n = SAStructural FANil
468+
469+
-- ============================================================================
470+
-- Concrete instance proofs — non-empty module (alloc / free pair)
471+
-- ============================================================================
472+
--
473+
-- The minimal interesting case: a module exporting an allocator
474+
-- (`Produces 0`) and a deallocator (`Consumes 0`). This is the
475+
-- smallest non-vacuous L10 single-consumption witness:
476+
--
477+
-- * `Produces 0` introduces handle token 0.
478+
-- * `Consumes 0` consumes it exactly once.
479+
-- * `TokenFresh 0 []` for each per-function obligation is trivial.
480+
--
481+
-- Demonstrates that the structural witness machinery scales past the
482+
-- empty list, and exercises every `ILA*` / `TF*` constructor needed to
483+
-- build a real witness.
484+
485+
||| Demo module: one allocator + one deallocator, sharing token 0.
486+
||| The canonical "valid pair" example. Used by the discrimination
487+
||| section below to contrast with `badDoubleConsumeModule`.
488+
public export
489+
allocFreeModule : ModuleSummary
490+
allocFreeModule = MkModuleSummary "allocFree"
491+
[ MkFunctionSummary "alloc" [Produces 0]
492+
, MkFunctionSummary "free" [Consumes 0]
493+
]
494+
495+
||| Spec acceptance witness for `allocFreeModule`. Built from raw
496+
||| structural constructors: `ILAProduces` for the allocator,
497+
||| `ILAConsumes` for the deallocator, `TFNil` for the trivial
498+
||| per-function freshness obligations.
499+
public export
500+
allocFreeSpecAccepts : SpecAccepts VerifierSpec.allocFreeModule
501+
allocFreeSpecAccepts = MkSpecAccepts
502+
(FACons (ILAProduces TFNil ILANil)
503+
(FACons (ILAConsumes TFNil ILANil)
504+
FANil))
505+
506+
||| Verifier acceptance for `allocFreeModule`, derived via the
507+
||| structural spec→verifier direction. Concrete demonstration that
508+
||| the agreement value works on a real, non-empty module.
509+
public export
510+
allocFreeVerifierAccepts : VerifierAccepts VerifierSpec.allocFreeModule
511+
allocFreeVerifierAccepts =
512+
specImpliesVerifierStructural allocFreeSpecAccepts
513+
514+
||| Source-checker acceptance for `allocFreeModule`, derived via the
515+
||| structural spec→source direction. Closes the structural triangle
516+
||| for a real module.
517+
public export
518+
allocFreeSourceAccepts : SourceAccepts VerifierSpec.allocFreeModule
519+
allocFreeSourceAccepts =
520+
specImpliesSourceStructural allocFreeSpecAccepts
521+
522+
-- ============================================================================
523+
-- Discrimination — predicate rejects bad modules
524+
-- ============================================================================
525+
--
526+
-- A predicate is only useful if it discriminates: there must be some
527+
-- module the spec REJECTS. Without a `Not (SpecAccepts badModule)`
528+
-- proof, the predicate could be vacuously true on everything and pass
529+
-- every regression test. The proof below exhibits a concrete bad
530+
-- module and shows the L10 single-consumption rule has teeth.
531+
532+
||| Bad module: one function double-consumes token 0. Violates L10
533+
||| (a linear handle is consumed twice). The structural witness
534+
||| machinery should make `SpecAccepts` of this module impossible.
535+
public export
536+
badDoubleConsumeModule : ModuleSummary
537+
badDoubleConsumeModule = MkModuleSummary "badDoubleConsume"
538+
[ MkFunctionSummary "doubleFree" [Consumes 0, Consumes 0]
539+
]
540+
541+
||| The spec does not accept `badDoubleConsumeModule`. This is the
542+
||| discrimination proof: assuming an acceptance witness, we extract
543+
||| the `TokenFresh 0 [Consumes 0]` obligation that `ILAConsumes`
544+
||| requires. The only constructor matching that shape is
545+
||| `TFConsumesOther (Not (0 = 0)) _`, and applying `Refl` to the
546+
||| `Not (0 = 0)` produces `Void`. Demonstrates L10 has teeth.
547+
public export
548+
notSpecAcceptsBadDoubleConsume :
549+
Not (SpecAccepts VerifierSpec.badDoubleConsumeModule)
550+
notSpecAcceptsBadDoubleConsume
551+
(MkSpecAccepts (FACons (ILAConsumes (TFConsumesOther noteq _) _) _)) =
552+
noteq Refl
553+
554+
||| Symmetric: the structural verifier witness is also impossible for
555+
||| the bad module. (The `VADifferential` case is not ruled out by
556+
||| this lemma — that escape hatch is by design and remains the
557+
||| differential-trust obligation.)
558+
public export
559+
notVerifierStructuralAcceptsBadDoubleConsume :
560+
Not (FunctionsAccepted VerifierSpec.badDoubleConsumeModule.functions)
561+
notVerifierStructuralAcceptsBadDoubleConsume
562+
(FACons (ILAConsumes (TFConsumesOther noteq _) _) _) =
563+
noteq Refl

0 commit comments

Comments
 (0)