StableConstructorK.hs (8265B)
1 {-# LANGUAGE BangPatterns #-} 2 -- Independent finite oracle for constructor-only K pruning. The model stores 3 -- full free-variable sets (not the portable minimum-index cache). It retains 4 -- a no-shortcut baseline and checks complete results, not DAG sizes. 5 module Main where 6 import Runtime 7 import Wire 8 import TreeObservation 9 import qualified Data.ByteString as BS 10 import qualified Data.Vector as V 11 import qualified Data.Text as Text 12 import qualified Data.Map.Strict as M 13 import qualified Data.IntSet as S 14 import Control.Monad 15 import System.Environment 16 load path = do 17 bytes<-BS.readFile path 18 b<-either error pure(decodeBundle bytes) 19 either error pure(verifyBundle b) 20 let ts=reconstructBundleTerms(bundleNodes b) 21 pure $ M.fromList[(Text.unpack(exportName e),ts V.! fromIntegral(exportRoot e))|e<-manifestExports(bundleManifest b)] 22 p1(Fork a _)=a 23 p1 _=error "pair" 24 p2(Fork _ b)=b 25 p2 _=error "pair" 26 result(Fork (Stem Leaf) (Fork v _))=v 27 result _=error "portable failure" 28 list=either error id . toList 29 number t=fromInteger(either error id(toNumber t))::Int 30 payload=p1.p2 31 -- Shape -1: unproved computation; -2: opaque value (free variable). 32 -- 0/1/2: proved Leaf/Stem/Fork. Applications require safe operands and a 33 -- known Leaf/Stem function; a Fork application is NEVER certified. 34 data Term = Term !Int !S.IntSet !Int Residual 35 data Residual = Lit T | Var Int | App Term Term 36 shape(Term s _ _ _)=s 37 vars(Term _ v _ _)=v 38 size(Term _ _ n _)=n 39 lit t=Term (case t of Leaf->0;Stem _->1;Fork _ _->2) S.empty 1 (Lit t) 40 var n=Term (-2) (S.singleton n) 1 (Var n) 41 app a b=Term sh (S.union(vars a)(vars b)) (1+size a+size b) (App a b) 42 where sh=if shape b/=(-1) && shape a `elem` [0,1] then shape a+1 else -1 43 foldApp a@(Term _ _ _ (Lit f)) b@(Term _ _ _ (Lit x))=case f of 44 Leaf->lit(Stem x) 45 Stem y->lit(Fork y x) 46 _->app a b 47 foldApp a b=app a b 48 k=lit(Stem Leaf) 49 i=lit(Fork(Stem(Stem Leaf))Leaf) 50 application l r=foldApp (foldApp (lit Leaf)(foldApp(lit Leaf)l)) r 51 -- Baseline abstraction deliberately has no K/eta shortcuts. 52 baseline term@(Term _ _ _ node)=case node of 53 Lit _->foldApp k term 54 Var 0->i 55 Var n->foldApp k(var(n-1)) 56 App a b->application(baseline a)(baseline b) 57 run(Term _ _ _ node)=case node of 58 Lit t->t 59 Var _->error "open output" 60 App f x->apply(run f)(run x) 61 lower term@(Term _ _ _ n)=case n of 62 Lit _->term 63 Var v->if v>0 then var(v-1) else error "lowered used binder" 64 App a b->app(lower a)(lower b) 65 eligible term@(Term sh vs _ node)=case node of 66 App _ _->sh/=(-1) && not(S.member 0 vs) 67 _->False 68 safeBracket term@(Term _ _ _ node) 69 | eligible term=foldApp k(lower term) 70 | otherwise=case node of 71 Lit _->foldApp k term 72 Var 0->i 73 Var n->foldApp k(var(n-1)) 74 App a b->application(safeBracket a)(safeBracket b) 75 encodeTerm (Term _ _ _ n)=case n of 76 Lit t->node 1 t 77 Var v->node 2(ofNumber(fromIntegral v)) 78 App a b->node 3(Fork(encodeTerm a)(encodeTerm b)) 79 where node k p=Fork(ofNumber k)(Fork p Leaf) 80 checkMetadata term annotated=do 81 let wantedShape=case shape term of -1->0; -2->4; n->n+1 82 wantedBound=if S.null(vars term) then 0 else S.findMin(vars term)+1 83 unless(number(p1(p1 annotated))==wantedShape && number(p2(p1 annotated))==wantedBound)(error "cached summary mismatch") 84 case term of 85 Term _ _ _ (App a b)->do 86 let children=payload(p2 annotated) 87 checkMetadata a(p1 children) 88 checkMetadata b(p2 children) 89 _->pure () 90 main=do 91 [bundle,referenceBundle]<-getArgs 92 portable<-load bundle 93 let abstract=apply(portable M.! "stableBracket") 94 forceTreeShared(portable M.! "stableBracket") 95 let leaf=lit Leaf 96 unknown=app(var 1)(var 2) 97 checks=[("open stem",app leaf(var 1),True), 98 ("open fork",app(app leaf(var 1))(var 2),True), 99 ("third argument",app(app(app leaf(var 1))(var 2))(var 3),False), 100 ("unknown function",unknown,False), 101 ("unsafe child",app leaf unknown,False), 102 ("used binder",app leaf(var 0),False), 103 ("known stem",app(lit(Stem Leaf))(var 1),True)] 104 forM_ checks $ \(name,t,expected)->unless(eligible t==expected)(error name) 105 let atoms=[var 0,var 1,var 2,leaf,lit(Stem Leaf),lit(Fork Leaf Leaf),i] 106 level1=atoms++[foldApp a b|a<-atoms,b<-atoms] 107 terms=filter ((/=(-1)).shape)(atoms++[foldApp a b|a<-level1,b<-level1]) 108 values=[Leaf,Stem Leaf,Fork Leaf Leaf] 109 forM_ terms $ \body->do 110 let step expected actual n=do 111 checkMetadata expected actual 112 unless(apply(portable M.! "stableCachedToPlain")actual==encodeTerm expected)(error "cached residual mismatch") 113 when(n>0)(step(safeBracket expected)(apply(portable M.! "stableCachedBracket")actual)(n-1)) 114 step body (apply(portable M.! "stableCachedFromPlain")(encodeTerm body)) (3::Int) 115 let old=iterate baseline body!!3 116 forM_ (take 3(iterate safeBracket body)) $ \term -> 117 unless(abstract(encodeTerm term)==encodeTerm(safeBracket term))(error "portable/model abstraction differs") 118 let new=iterate safeBracket body!!3 119 oldTree=run old;newTree=run new 120 forceTreeShared oldTree 121 forceTreeShared newTree 122 forM_ values $ \a->forM_ values $ \b->forM_ values $ \c-> 123 unless(foldl apply oldTree[a,b,c]==foldl apply newTree[a,b,c])(error "nested abstraction changed result") 124 -- Sparse/high indices and unsafe applications exercise summary maintenance 125 -- without assuming application termination. Compare cached annotations after 126 -- each abstraction, not after re-importing ordinary residuals. 127 let mixed=[app(lit Leaf)(var v)|v<-[0,1,7,63,256]]++ 128 [app(var 7)(var 63),app(lit(Fork Leaf Leaf))(var 7), 129 app(lit Leaf)(app(var 7)(var 63)), 130 app(app(lit Leaf)(var 63))(var 0)] 131 forM_ mixed $ \body->do 132 let step expected actual n=do 133 checkMetadata expected actual 134 unless(apply(portable M.! "stableCachedToPlain")actual==encodeTerm expected)(error "sparse/unsafe residual mismatch") 135 when(n>0)(step(safeBracket expected)(apply(portable M.! "stableCachedBracket")actual)(n-1)) 136 step body (apply(portable M.! "stableCachedFromPlain")(encodeTerm body)) (4::Int) 137 -- An opaque variable is only stored, never invoked or forced by a constructor. 138 let poison=error "opaque argument forced"::T 139 saved=run(safeBracket(safeBracket(app leaf(var 1)))) 140 case apply(apply saved poison)Leaf of Stem _->pure();_->error "opaque constructor" 141 -- omega has a Fork function: it must not be evaluated during construction. 142 let ident=Fork(Stem(Stem Leaf))Leaf 143 self=Fork(Stem ident)ident 144 omega=app(lit self)(lit self) 145 unless(not(eligible omega) && not(eligible(app leaf omega)))(error "omega certified") 146 forceTreeShared(run(safeBracket omega)) 147 -- Full portable bracket construction, without executing omega. 148 forceTreeShared(abstract(encodeTerm omega)) 149 -- Demand-sensitive checks run actual portable-produced function trees too. 150 let node kind value=Fork(ofNumber kind)(Fork value(Fork Leaf Leaf)) 151 lambda body=node 4 body 152 applyCore a b=node 3(Fork a b) 153 portableSaved=result(apply(portable M.! "stableCompileCore") 154 (lambda(lambda(applyCore(node 1 Leaf)(node 2(ofNumber 1)))))) 155 forceTreeShared portableSaved 156 case apply(apply portableSaved poison)Leaf of Stem _->pure();_->error "portable opaque constructor" 157 let portableOmega=result(apply(portable M.! "stableCompileCore") 158 (lambda(applyCore(node 5 self)(node 5 self)))) 159 forceTreeShared portableOmega 160 let portableEta=result(apply(portable M.! "stableCompileCore") 161 (lambda(applyCore (applyCore(node 5 self)(node 5 self)) (node 2 Leaf)))) 162 forceTreeShared portableEta 163 reference<-load referenceBundle 164 let core kind value=Fork(ofNumber kind)(Fork value(Fork(ofNumber 23)(ofNumber 24))) 165 badVariable=core 2(ofNumber 3) 166 invalid=core 99 Leaf 167 application a b=core 3(Fork a b) 168 forM_ [badVariable,invalid,application badVariable invalid,application(core 1 Leaf)invalid] $ \input-> 169 unless(apply(portable M.! "stableCompileCore")input==apply(reference M.! "source_stable.stableCompileCore")input)(error "core diagnostic/remainder mismatch") 170 putStrLn("PASS: "++show(length terms)++" constructor bodies under three nested binders; "++show(length terms*27)++" complete application results; classification, sparse indices, opaque arguments, K/eta construction hazards and diagnostic controls")