Skip to content
This repository was archived by the owner on Apr 1, 2025. It is now read-only.

Commit 3f77dd6

Browse files
author
Patrick Thomson
authored
Merge pull request #209 from github/generalize-analyses-over-the-term-type
Generalize analyses over the term type
2 parents 5521d7d + 8ad510d commit 3f77dd6

File tree

5 files changed

+170
-109
lines changed

5 files changed

+170
-109
lines changed

semantic-core/src/Analysis/Concrete.hs

Lines changed: 49 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{-# LANGUAGE DerivingVia, FlexibleContexts, FlexibleInstances, LambdaCase, MultiParamTypeClasses, NamedFieldPuns, OverloadedStrings, RecordWildCards, TypeApplications, TypeOperators, UndecidableInstances #-}
1+
{-# LANGUAGE DerivingVia, FlexibleContexts, FlexibleInstances, LambdaCase, MultiParamTypeClasses, NamedFieldPuns, OverloadedStrings, RankNTypes, RecordWildCards, TypeApplications, TypeOperators, UndecidableInstances #-}
22
module Analysis.Concrete
33
( Concrete(..)
44
, concrete
@@ -20,7 +20,6 @@ import Control.Effect.NonDet
2020
import Control.Effect.Reader hiding (Local)
2121
import Control.Effect.State
2222
import Control.Monad ((<=<), guard)
23-
import qualified Data.Core as Core
2423
import Data.File
2524
import Data.Function (fix)
2625
import qualified Data.IntMap as IntMap
@@ -30,7 +29,6 @@ import qualified Data.Map as Map
3029
import Data.Name
3130
import Data.Semigroup (Last (..))
3231
import qualified Data.Set as Set
33-
import Data.Term
3432
import Data.Text (Text, pack)
3533
import Data.Traversable (for)
3634
import Prelude hiding (fail)
@@ -41,17 +39,17 @@ type Env = Map.Map User Precise
4139
newtype FrameId = FrameId { unFrameId :: Precise }
4240
deriving (Eq, Ord, Show)
4341

44-
data Concrete
45-
= Closure Loc User (Term (Core.Ann :+: Core.Core) User) Env
42+
data Concrete term
43+
= Closure Loc User term Env
4644
| Unit
4745
| Bool Bool
4846
| String Text
4947
| Record Env
5048
deriving (Eq, Ord, Show)
5149
-- NB: We derive the 'Semigroup' instance for 'Concrete' to take the second argument. This is equivalent to stating that the return value of an imperative sequence of statements is the value of its final statement.
52-
deriving Semigroup via Last Concrete
50+
deriving Semigroup via Last (Concrete term)
5351

54-
recordFrame :: Concrete -> Maybe Env
52+
recordFrame :: Concrete term -> Maybe Env
5553
recordFrame (Record frame) = Just frame
5654
recordFrame _ = Nothing
5755

@@ -60,44 +58,64 @@ newtype Frame = Frame
6058
}
6159
deriving (Eq, Ord, Show)
6260

63-
type Heap = IntMap.IntMap Concrete
61+
type Heap term = IntMap.IntMap (Concrete term)
6462

6563
data Edge = Lexical | Import
6664
deriving (Eq, Ord, Show)
6765

6866

6967
-- | Concrete evaluation of a term to a value.
7068
--
71-
-- >>> map fileBody (snd (concrete [File (Loc "bool" emptySpan) (Core.bool True)]))
69+
-- >>> map fileBody (snd (concrete eval [File (Loc "bool" emptySpan) (Core.bool True)]))
7270
-- [Right (Bool True)]
73-
concrete :: [File (Term (Core.Ann :+: Core.Core) User)] -> (Heap, [File (Either (Loc, String) Concrete)])
7471
concrete
72+
:: (Foldable term, Show (term User))
73+
=> (forall sig m
74+
. (Carrier sig m, Member (Reader Loc) sig, MonadFail m)
75+
=> Analysis (term User) Precise (Concrete (term User)) m
76+
-> (term User -> m (Concrete (term User)))
77+
-> (term User -> m (Concrete (term User)))
78+
)
79+
-> [File (term User)]
80+
-> (Heap (term User), [File (Either (Loc, String) (Concrete (term User)))])
81+
concrete eval
7582
= run
7683
. runFresh
7784
. runHeap
78-
. traverse runFile
79-
80-
runFile :: ( Carrier sig m
81-
, Effect sig
82-
, Member Fresh sig
83-
, Member (State Heap) sig
84-
)
85-
=> File (Term (Core.Ann :+: Core.Core) User)
86-
-> m (File (Either (Loc, String) Concrete))
87-
runFile file = traverse run file
85+
. traverse (runFile eval)
86+
87+
runFile
88+
:: ( Carrier sig m
89+
, Effect sig
90+
, Foldable term
91+
, Member Fresh sig
92+
, Member (State (Heap (term User))) sig
93+
, Show (term User)
94+
)
95+
=> (forall sig m
96+
. (Carrier sig m, Member (Reader Loc) sig, MonadFail m)
97+
=> Analysis (term User) Precise (Concrete (term User)) m
98+
-> (term User -> m (Concrete (term User)))
99+
-> (term User -> m (Concrete (term User)))
100+
)
101+
-> File (term User)
102+
-> m (File (Either (Loc, String) (Concrete (term User))))
103+
runFile eval file = traverse run file
88104
where run = runReader (fileLoc file)
89105
. runFailWithLoc
90106
. runReader @Env mempty
91107
. fix (eval concreteAnalysis)
92108

93109
concreteAnalysis :: ( Carrier sig m
110+
, Foldable term
94111
, Member Fresh sig
95112
, Member (Reader Env) sig
96113
, Member (Reader Loc) sig
97-
, Member (State Heap) sig
114+
, Member (State (Heap (term User))) sig
98115
, MonadFail m
116+
, Show (term User)
99117
)
100-
=> Analysis Precise Concrete m
118+
=> Analysis (term User) Precise (Concrete (term User)) m
101119
concreteAnalysis = Analysis{..}
102120
where alloc _ = fresh
103121
bind name addr m = local (Map.insert name addr) m
@@ -133,7 +151,7 @@ concreteAnalysis = Analysis{..}
133151
pure (val >>= lookupConcrete heap n)
134152

135153

136-
lookupConcrete :: Heap -> User -> Concrete -> Maybe Precise
154+
lookupConcrete :: Heap term -> User -> Concrete term -> Maybe Precise
137155
lookupConcrete heap name = run . evalState IntSet.empty . runNonDet . inConcrete
138156
where -- look up the name in a concrete value
139157
inConcrete = inFrame <=< maybeA . recordFrame
@@ -150,7 +168,7 @@ lookupConcrete heap name = run . evalState IntSet.empty . runNonDet . inConcrete
150168
maybeA = maybe empty pure
151169

152170

153-
runHeap :: StateC Heap m a -> m (Heap, a)
171+
runHeap :: StateC (Heap term) m a -> m (Heap term, a)
154172
runHeap = runState mempty
155173

156174

@@ -159,7 +177,7 @@ runHeap = runState mempty
159177
-- > λ let (heap, res) = concrete [ruby]
160178
-- > λ writeFile "/Users/rob/Desktop/heap.dot" (export (addressStyle heap) (heapAddressGraph heap))
161179
-- > λ :!dot -Tsvg < ~/Desktop/heap.dot > ~/Desktop/heap.svg
162-
heapGraph :: (Precise -> Concrete -> a) -> (Either Edge User -> Precise -> G.Graph a) -> Heap -> G.Graph a
180+
heapGraph :: (Precise -> Concrete term -> a) -> (Either Edge User -> Precise -> G.Graph a) -> Heap term -> G.Graph a
163181
heapGraph vertex edge h = foldr (uncurry graph) G.empty (IntMap.toList h)
164182
where graph k v rest = (G.vertex (vertex k v) `G.connect` outgoing v) `G.overlay` rest
165183
outgoing = \case
@@ -169,14 +187,14 @@ heapGraph vertex edge h = foldr (uncurry graph) G.empty (IntMap.toList h)
169187
Closure _ _ _ env -> foldr (G.overlay . edge (Left Lexical)) G.empty env
170188
Record frame -> Map.foldrWithKey (\ k -> G.overlay . edge (Right k)) G.empty frame
171189

172-
heapValueGraph :: Heap -> G.Graph Concrete
190+
heapValueGraph :: Heap term -> G.Graph (Concrete term)
173191
heapValueGraph h = heapGraph (const id) (const fromAddr) h
174192
where fromAddr addr = maybe G.empty G.vertex (IntMap.lookup addr h)
175193

176-
heapAddressGraph :: Heap -> G.Graph (EdgeType, Precise)
194+
heapAddressGraph :: Heap term -> G.Graph (EdgeType term, Precise)
177195
heapAddressGraph = heapGraph (\ addr v -> (Value v, addr)) (fmap G.vertex . (,) . either Edge Slot)
178196

179-
addressStyle :: Heap -> G.Style (EdgeType, Precise) Text
197+
addressStyle :: Heap term -> G.Style (EdgeType term, Precise) Text
180198
addressStyle heap = (G.defaultStyle vertex) { G.edgeAttributes }
181199
where vertex (_, addr) = pack (show addr) <> " = " <> maybe "?" fromConcrete (IntMap.lookup addr heap)
182200
edgeAttributes _ (Slot name, _) = ["label" G.:= name]
@@ -191,12 +209,13 @@ addressStyle heap = (G.defaultStyle vertex) { G.edgeAttributes }
191209
Record _ -> "{}"
192210
showPos (Pos l c) = pack (show l) <> ":" <> pack (show c)
193211

194-
data EdgeType
212+
data EdgeType term
195213
= Edge Edge
196214
| Slot User
197-
| Value Concrete
215+
| Value (Concrete term)
198216
deriving (Eq, Ord, Show)
199217

200218

201219
-- $setup
202220
-- >>> :seti -XOverloadedStrings
221+
-- >>> import qualified Data.Core as Core

semantic-core/src/Analysis/Eval.hs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ eval :: ( Carrier sig m
3333
, MonadFail m
3434
, Semigroup value
3535
)
36-
=> Analysis address value m
36+
=> Analysis (Term (Ann :+: Core) User) address value m
3737
-> (Term (Ann :+: Core) User -> m value)
3838
-> (Term (Ann :+: Core) User -> m value)
3939
eval Analysis{..} eval = \case
@@ -214,14 +214,14 @@ ruby = fromBody $ annWith callStack (rec (named' __semantic_global) (do' stateme
214214
__semantic_truthy = "__semantic_truthy"
215215

216216

217-
data Analysis address value m = Analysis
217+
data Analysis term address value m = Analysis
218218
{ alloc :: User -> m address
219219
, bind :: forall a . User -> address -> m a -> m a
220220
, lookupEnv :: User -> m (Maybe address)
221221
, deref :: address -> m (Maybe value)
222222
, assign :: address -> value -> m ()
223-
, abstract :: (Term (Ann :+: Core) User -> m value) -> User -> Term (Ann :+: Core) User -> m value
224-
, apply :: (Term (Ann :+: Core) User -> m value) -> value -> value -> m value
223+
, abstract :: (term -> m value) -> User -> term -> m value
224+
, apply :: (term -> m value) -> value -> value -> m value
225225
, unit :: m value
226226
, bool :: Bool -> m value
227227
, asBool :: value -> m Bool

semantic-core/src/Analysis/FlowInsensitive.hs

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,60 +13,62 @@ import Control.Effect.Fresh
1313
import Control.Effect.NonDet
1414
import Control.Effect.Reader
1515
import Control.Effect.State
16-
import qualified Data.Core as Core
1716
import qualified Data.Map as Map
1817
import Data.Maybe (fromMaybe)
1918
import Data.Monoid (Alt(..))
2019
import qualified Data.Set as Set
21-
import Data.Term (Term)
2220

23-
type Cache name a = Map.Map (Term (Core.Ann :+: Core.Core) name) (Set.Set a)
24-
type Heap name a = Map.Map name (Set.Set a)
21+
newtype Cache term a = Cache { unCache :: Map.Map term (Set.Set a) }
22+
deriving (Eq, Ord, Show)
23+
24+
type Heap address a = Map.Map address (Set.Set a)
2525

2626
newtype FrameId name = FrameId { unFrameId :: name }
2727
deriving (Eq, Ord, Show)
2828

2929

30-
convergeTerm :: forall m sig a name
30+
convergeTerm :: forall m sig a term address proxy
3131
. ( Carrier sig m
3232
, Effect sig
33+
, Eq address
3334
, Member Fresh sig
34-
, Member (State (Heap name a)) sig
35+
, Member (State (Heap address a)) sig
3536
, Ord a
36-
, Ord name
37+
, Ord term
3738
)
38-
=> (Term (Core.Ann :+: Core.Core) name -> NonDetC (ReaderC (Cache name a) (StateC (Cache name a) m)) a)
39-
-> Term (Core.Ann :+: Core.Core) name
39+
=> proxy address
40+
-> (term -> NonDetC (ReaderC (Cache term a) (StateC (Cache term a) m)) a)
41+
-> term
4042
-> m (Set.Set a)
41-
convergeTerm eval body = do
43+
convergeTerm _ eval body = do
4244
heap <- get
43-
(cache, _) <- converge (Map.empty :: Cache name a, heap :: Heap name a) $ \ (prevCache, _) -> runState Map.empty . runReader prevCache $ do
45+
(cache, _) <- converge (Cache Map.empty :: Cache term a, heap :: Heap address a) $ \ (prevCache, _) -> runState (Cache Map.empty) . runReader prevCache $ do
4446
_ <- resetFresh . runNonDetM Set.singleton $ eval body
4547
get
46-
pure (fromMaybe mempty (Map.lookup body cache))
48+
pure (fromMaybe mempty (Map.lookup body (unCache cache)))
4749

48-
cacheTerm :: forall m sig a name
50+
cacheTerm :: forall m sig a term
4951
. ( Alternative m
5052
, Carrier sig m
51-
, Member (Reader (Cache name a)) sig
52-
, Member (State (Cache name a)) sig
53+
, Member (Reader (Cache term a)) sig
54+
, Member (State (Cache term a)) sig
5355
, Ord a
54-
, Ord name
56+
, Ord term
5557
)
56-
=> (Term (Core.Ann :+: Core.Core) name -> m a)
57-
-> (Term (Core.Ann :+: Core.Core) name -> m a)
58+
=> (term -> m a)
59+
-> (term -> m a)
5860
cacheTerm eval term = do
59-
cached <- gets (Map.lookup term)
61+
cached <- gets (Map.lookup term . unCache)
6062
case cached :: Maybe (Set.Set a) of
6163
Just results -> foldMapA pure results
6264
Nothing -> do
63-
results <- asks (fromMaybe mempty . Map.lookup term)
64-
modify (Map.insert term (results :: Set.Set a))
65+
results <- asks (fromMaybe mempty . Map.lookup term . unCache)
66+
modify (Cache . Map.insert term (results :: Set.Set a) . unCache)
6567
result <- eval term
66-
result <$ modify (Map.insertWith (<>) term (Set.singleton (result :: a)))
68+
result <$ modify (Cache . Map.insertWith (<>) term (Set.singleton (result :: a)) . unCache)
6769

68-
runHeap :: name -> ReaderC (FrameId name) (StateC (Heap name a) m) b -> m (Heap name a, b)
69-
runHeap addr m = runState (Map.singleton addr Set.empty) (runReader (FrameId addr) m)
70+
runHeap :: StateC (Heap address a) m b -> m (Heap address a, b)
71+
runHeap m = runState (Map.empty) m
7072

7173
-- | Fold a collection by mapping each element onto an 'Alternative' action.
7274
foldMapA :: (Alternative m, Foldable t) => (b -> m a) -> t b -> m a

0 commit comments

Comments
 (0)