packages feed

AsyncRattus (empty) → 0.1

raw patch · 25 files changed

+4767/−0 lines, 25 filesdep +AsyncRattusdep +basedep +containersbuild-type:Customsetup-changedbinary-added

Dependencies added: AsyncRattus, base, containers, ghc, hashtables, simple-affine-space, transformers

Files

+ AsyncRattus.cabal view
@@ -0,0 +1,187 @@+cabal-version:       1.18+name:                AsyncRattus+version:             0.1+category:            FRP+synopsis:            An asynchronous modal FRP language+description:++            This library implements the Async Rattus programming+            language as an embedded DSL. To this end the library+            provides a GHC plugin that checks the stricter typing+            rules of Async Rattus.+            +            +            What follows is a brief introduction to the language and+            its usage. A more detailed introduction can be found in+            this <src/docs/paper.pdf paper>.+            +            .+            +            Async Rattus is a functional reactive programming (FRP)+            language that uses modal types to express temporal+            dependencies. In return the language will guarantee that+            programs are productive (in each computation step, the+            program makes progress), causal (output depends only on+            current and earlier input), and have no space leaks+            (programs do not implicitly retain memory over time).+            +            .+            +            The modal type constructor @O@ (pronounced "later") is+            used to express the passage of time at the type+            level. Intuitively speaking, a value of type @O a@+            represents a computation that will produce a value of type+            @a@ in the next time step. Additionally, the language also+            features the @Box@ modal type constructor. A value of type+            @Box a@ is a time-independent computation that can be+            executed at any time to produce a value of type @a@.++            .++            For example, the type of signals is defined as++            .++            > data Sig a = a ::: (O (Sig a))++            .++            So the current value of the signal is available now, but+            its future state is only available in the next time+            step. Writing a @map@ function for this type of streams,+            requires us to use the @Box@ modality:++            .++            > map :: Box (a -> b) -> Sig a -> Sig b+            > map f (x ::: xs) = unbox f x ::: delay (map f (adv xs))++            .++            This makes sure that the function @f@ that we give to+            @map@ is available at any time in the future.++            .++            The core of the language is defined in the module+            "AsyncRattus.Primitives". Note that the operations on @O@+            and @Box@ have non-standard typing rules. Therefore, this+            library provides a compiler plugin that checks these+            non-standard typing rules. To write Async Rattus programs,+            you must enable this plugin via the GHC option+            @-fplugin=AsyncRattus.Plugin@, e.g. by including the following+            line in the source file:+            +            .+            +            > {-# OPTIONS -fplugin=AsyncRattus.Plugin #-}+            +            .++            In addition, you have to annotate functions that are+            written in Async Rattus:++            .+            +            > {-# ANN myFunction AsyncRattus #-}++            .++            You can also annotate the whole module as an Async Rattus module:+            +            .++            > {-# ANN module AsyncRattus #-}++            .++            Below is a minimal Async Rattus program using the+            "AsyncRattus.Signal" module for programming with signals:++            .++            > {-# OPTIONS -fplugin=AsyncRattus.Plugin #-}+            >     +            > import AsyncRattus+            > import AsyncRattus.Signal+            > +            > {-# ANN sums AsyncRattus #-}+            > sums :: Sig Int -> Sig Int+            > sums = scan (box (+)) 0++            .++            The <docs/src/AsyncRattus.Signal.html source code of the AsyncRattus.Signal module>+            provides more examples on how to program in Async Rattus.+            An example project using Async Rattus can be found+            <https://github.com/pa-ba/AsyncRattus/tree/master/examples/console here>.++homepage:            https://github.com/pa-ba/AsyncRattus/+bug-reports:         https://github.com/pa-ba/AsyncRattus/issues+License:             BSD3+License-file:        LICENSE+copyright:           Copyright (C) 2023 Emil Houlborg, Gregers Rørdam, Patrick Bahr+Author:              Emil Houlborg, Gregers Rørdam, Patrick Bahr+maintainer:          Patrick Bahr <paba@itu.dk>+stability:           experimental++build-type:          Custom++extra-source-files:  CHANGELOG.md++extra-doc-files:     docs/paper.pdf+                     +custom-setup+  setup-depends:+    base  >= 4.5 && < 5,+    Cabal >= 1.18  && < 4+++library+  exposed-modules:     AsyncRattus+                       AsyncRattus.Signal+                       AsyncRattus.Future+                       AsyncRattus.Strict+                       AsyncRattus.Plugin+                       AsyncRattus.Primitives+                       AsyncRattus.InternalPrimitives+                       AsyncRattus.Channels+                       AsyncRattus.Plugin.Annotation+                                              +  other-modules:       AsyncRattus.Plugin.ScopeCheck+                       AsyncRattus.Plugin.SingleTick+                       AsyncRattus.Plugin.CheckClockCompatibility+                       AsyncRattus.Plugin.Strictify+                       AsyncRattus.Plugin.Utils+                       AsyncRattus.Plugin.Dependency+                       AsyncRattus.Plugin.StableSolver+                       AsyncRattus.Plugin.Transform+                       AsyncRattus.Plugin.PrimExpr+  build-depends:       base >=4.16 && <5,+                       containers >= 0.6.5 && < 0.8,+                       ghc >= 9.2 && < 9.7,+                       hashtables >= 1.3.1 && < 1.4,+                       simple-affine-space >= 0.2.1 && < 0.3,+                       transformers >= 0.5.6 && < 0.7+  hs-source-dirs:      src+  default-language:    Haskell2010+  ghc-options:         -W++++Test-Suite ill-typed+  type:                exitcode-stdio-1.0+  main-is:             test/IllTyped.hs+  default-language:    Haskell2010+  build-depends:       AsyncRattus, base+  ghc-options:         -fplugin=AsyncRattus.Plugin+++Test-Suite well-typed+  type:                exitcode-stdio-1.0+  main-is:             WellTyped.hs+  hs-source-dirs:      test+  default-language:    Haskell2010+  build-depends:       AsyncRattus, base, containers+  ghc-options:         -fplugin=AsyncRattus.Plugin+
+ CHANGELOG.md view
@@ -0,0 +1,3 @@+# 0.1++First release.
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2023 Emil Houlborg, Gregers Rørdam, Patrick Bahr++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:++1. Redistributions of source code must retain the above copyright+   notice, this list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright+   notice, this list of conditions and the following disclaimer in the+   documentation and/or other materials provided with the distribution.++3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,29 @@+{-+Disable some errors and warnings during the haddock pass+  (caused by compiler plugins and hs-boot)+ -}+{-# OPTIONS_GHC -Wall #-}+module Main (main) where++import Distribution.Simple+import Distribution.Simple.Setup++main :: IO ()+main = defaultMainWithHooks simpleUserHooks+  { confHook = \a -> confHook simpleUserHooks a . tweakFlags }++tweakFlags :: ConfigFlags -> ConfigFlags+tweakFlags flags = flags+  { configProgramArgs = addHaddockArgs (configProgramArgs flags) }++addHaddockArgs :: [(String, [String])] -> [(String, [String])]+addHaddockArgs []+  = [("haddock", newHaddockGhcArgs)]+addHaddockArgs (("haddock", args):otherProgsArgs)+  = ("haddock", args ++ newHaddockGhcArgs) : otherProgsArgs+addHaddockArgs (progArgs:otherProgsArgs)+  = progArgs : addHaddockArgs otherProgsArgs++newHaddockGhcArgs :: [String]+newHaddockGhcArgs =+  [ "--optghc=-fobject-code" ]
+ docs/paper.pdf view

binary file changed (absent → 385715 bytes)

+ src/AsyncRattus.hs view
@@ -0,0 +1,26 @@+{-# OPTIONS -fplugin=AsyncRattus.Plugin #-}+++-- | The bare-bones Asynchronous Rattus language. To program with streams,+-- you can use "AsyncRattus.Stream".++module AsyncRattus (+  -- * Asynchronous Rattus language primitives+  module AsyncRattus.Primitives,+  -- * Strict data types+  module AsyncRattus.Strict,+  -- * Annotation+  AsyncRattus(..),+  -- * other+  mapO+  )+  where++import AsyncRattus.Plugin+import AsyncRattus.Strict+import AsyncRattus.Primitives++{-# ANN module AsyncRattus #-}++mapO :: Box (a -> b) -> O a -> O b+mapO f later = delay (unbox f (adv later))
+ src/AsyncRattus/Channels.hs view
@@ -0,0 +1,169 @@+{-# OPTIONS -fplugin=AsyncRattus.Plugin #-}++{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE GADTs #-}++-- | This module is meant for library authors that want to build APIs+-- for interacting with asynchronous resources, e.g. a GUI framework. ++module AsyncRattus.Channels (+  getInput,+  setOutput,+  mkInput,+  startEventLoop,+  timer,+  Producer (..),+) where++import AsyncRattus.InternalPrimitives++import AsyncRattus.Plugin.Annotation+import AsyncRattus.Strict+import Control.Concurrent.MVar+import Control.Monad+import System.IO.Unsafe+import Data.IORef+import Unsafe.Coerce+import qualified Data.HashTable.IO as H+import Data.HashTable.IO (BasicHashTable)+import qualified Data.IntSet as IntSet+import Control.Concurrent++-- | A type @p@ satisfying @Producer p a@ is essentially a signal that+-- produces values of type @a@ but it might not produce such values at+-- each tick.+class Producer p a | p -> a where+  -- | Get the current value of the producer if any.+  getCurrent :: p -> Maybe' a+  -- | Get the next state of the producer. Morally, the type of this+  -- method should be+  --+  -- > getNext :: p -> (exists q. Producer q a => O q)+  --+  -- We encode the existential type using continuation-passing style.+  getNext :: p -> (forall q. Producer q a => O q -> b) -> b++{-# ANN module AsyncRattus #-}+{-# ANN module AllowLazyData #-}++instance Producer p a => Producer (O p) a where+  getCurrent _ = Nothing'+  getNext p cb = cb p++instance Producer p a => Producer (Box p) a where+  getCurrent p = getCurrent (unbox p)+  getNext p cb = getNext (unbox p) cb+++{-# NOINLINE nextFreshChannel #-}+nextFreshChannel :: IORef InputChannelIdentifier+nextFreshChannel = unsafePerformIO (newIORef (-1))+++{-# NOINLINE input #-}+input :: MVar InputValue+input = unsafePerformIO newEmptyMVar++data OutputChannel where+  OutputChannel :: Producer p a => !(O p) -> !(a -> IO ()) -> OutputChannel+++{-# NOINLINE output #-}+output :: BasicHashTable InputChannelIdentifier (List (IORef (Maybe' OutputChannel)))+output = unsafePerformIO (H.new)++{-# NOINLINE eventLoopStarted #-}+eventLoopStarted :: IORef Bool+eventLoopStarted = unsafePerformIO (newIORef False)+++-- | This function can be used to implement input signals. It returns+-- a boxed delayed computation @s@ and a callback function @cb@. The+-- signal @mkSig s@ will produce a new value @v@ whenever the callback+-- function @cb@ is called with argument @v@.+getInput :: IO (Box (O a) :* (a -> IO ()))+getInput = do ch <- atomicModifyIORef nextFreshChannel (\ x -> (x - 1, x))+              return ((box (Delay (singletonClock ch) (\ (InputValue _ v) -> unsafeCoerce v)))+                       :* \ x -> putMVar input (InputValue ch x))++setOutput' :: Producer p a => (a -> IO ()) -> O p -> IO ()+setOutput' cb !sig = do+  ref <- newIORef (Just' (OutputChannel sig cb))+  let upd Nothing = (Just (ref :! Nil),())+      upd (Just ls) = (Just (ref :! ls),())+  let upd' ch Nothing = do+        forkIO (threadDelay ch >> putMVar input (InputValue ch ()))+        return (Just (ref :! Nil),())+      upd' _ (Just ls) = return (Just (ref :! ls),())+  let run pre ch =+        if ch > 0 then+          pre >> H.mutateIO output ch (upd' ch)+        else +          pre >> H.mutate output ch upd+  IntSet.foldl' run (return ()) (extractClock sig)+++-- | This function can be used to produces outputs. Given a signal @s@+-- and function @f@, the call @setOutput s f@ registers @f@ as a+-- callback function that is called with argument @v@ whenever the+-- signal produces a new value @v@. For this function to work,+-- 'startEventLoop' must be called.+setOutput :: Producer p a => p -> (a -> IO ()) -> IO ()+setOutput !sig cb = do+  case getCurrent sig of+    Just' cur' -> cb cur'+    Nothing' -> return ()+  getNext sig (setOutput' cb)++-- | This function is essentially the composition of 'getInput' and+-- 'setOutput'. It turns any producer into a signal.+mkInput :: Producer p a => p -> IO (Box (O a))+mkInput p = do (out :* cb) <- getInput+               setOutput p cb+               return out++-- | @timer n@ produces a delayed computation that ticks every @n@+-- milliseconds. In particular @mkSig (timer n)@ is a signal that+-- produces a new value every #n# milliseconds.+timer :: Int -> Box (O ())+timer d = Box (Delay (singletonClock (d `max` 10)) (\ _ -> ()))+++update :: InputValue -> IORef (Maybe' OutputChannel) -> IO ()+update inp ref = do+  mout <- readIORef ref+  case mout of+    Nothing' -> return ()+    Just' (OutputChannel (Delay _ sigf) cb) -> do+      writeIORef ref Nothing'+      let new = sigf inp+      case getCurrent new of+        Just' w' -> cb w'+        Nothing' -> return ()+      getNext new (setOutput' cb)+++{-# ANN eventLoop NotAsyncRattus #-}++eventLoop :: IO ()+eventLoop = do inp@(InputValue ch _) <- takeMVar input+               res <- H.lookup output ch+               case res of+                 Nothing -> return ()+                 Just ls -> do+                   H.delete output ch+                   mapM_ (update inp) ls+               eventLoop++-- | In order for 'setOutput' to work, this IO action must be invoked.++startEventLoop :: IO ()+startEventLoop = do+  started <- atomicModifyIORef eventLoopStarted (\b -> (True,b))+  when (not started) eventLoop
+ src/AsyncRattus/Future.hs view
@@ -0,0 +1,221 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TypeOperators #-}+{-# OPTIONS -fplugin=AsyncRattus.Plugin #-}+++-- | Programming with futures.++module AsyncRattus.Future+  ( F(..)+  , SigF(..)+  , mkSigF+  , mkSigF'+  , current+  , future+  , bindF+  , mapF+  , sync+  , syncF+  , switchAwait+  , switch+  , switchS+  , filterMap+  , filterMapAwait+  , filterAwait+  , filter+  , trigger+  , triggerAwait+  , map+  , mapAwait+  , zipWith+  , zipWithAwait+  , fromSig+  , scan+  , scanAwait+  )++where++import AsyncRattus+import AsyncRattus.Signal (Sig(..))+import Prelude hiding (map, filter, zipWith)+import AsyncRattus.Channels++{-# ANN module AsyncRattus #-}++newtype OneShot a = OneShot (F a)++instance Producer (OneShot a) a where+  getCurrent (OneShot (Now x)) = Just' x+  getCurrent (OneShot (Wait _)) = Nothing'++  getNext (OneShot (Now _)) cb = cb (never :: O (OneShot a))+  getNext (OneShot (Wait x)) cb = cb (delay (OneShot (adv x)))++instance Producer p a => Producer (F p) a where+  getCurrent (Now x) = getCurrent x+  getCurrent (Wait _) = Nothing'+  +  getNext (Now x) cb = getNext x cb+  getNext (Wait x) cb = cb x++instance Producer (SigF a) a where+  getCurrent (x :>: _) = Just' x+  getNext (_ :>: xs) cb = cb xs++++-- | @F a@ will produces a value of type @a@ after zero or more ticks+-- of some clocks+data F a = Now !a | Wait !(O (F a))++++bindF :: F a -> Box (a -> F b) -> F b+bindF (Now x) f = unbox f x+bindF (Wait x) f = Wait (delay (bindF (adv x) f))++mapF :: Box (a -> b) -> F a -> F b+mapF f d = d `bindF` (box (\ x -> Now (unbox f x)))+++sync :: O (F a) -> O (F b) -> O (F a :* F b)+sync x y = delay (case select x y of+                     Fst x' y' -> (x' :* Wait y')+                     Snd x' y' -> (Wait x' :* y')+                     Both x' y' -> (x' :* y'))++syncF :: (Stable a, Stable b) => F a -> F b -> F (a :* b)+syncF (Now x) (Now y) = Now (x :* y)+syncF (Wait x) (Now y) = Wait (delay (syncA (adv x) y))+syncF (Now x) (Wait y) = Wait (delay (syncB x (adv y)))+syncF (Wait x) (Wait y) = Wait (delay (case select x y of+                                         Fst x' y' -> syncF x' (Wait y')+                                         Snd x' y' -> syncF (Wait x') y'+                                         Both x' y' -> syncF x' y'+                                      )) ++syncA :: (Stable b) => F a -> b -> F (a :* b)+syncA (Now x) y = Now (x :* y)+syncA (Wait x) y = Wait (delay (syncA (adv x) y))+++syncB :: (Stable a) => a -> F b -> F (a :* b)+syncB x (Now y) = Now (x :* y)+syncB x (Wait y) = Wait (delay (syncB x (adv y)))+++-- | @SigF a@ is a signal of values of type @a@. In contrast to 'Sig',+-- 'SigF' supports the 'filter' and 'filterMap' functions.+data SigF a = !a :>: !(O (F (SigF a)))+++-- | Get the current value of a signal.+current :: SigF a -> a+current (x :>: _) = x+++-- | Get the future the signal.+future :: SigF a -> O (F (SigF a))+future (_ :>: xs) = xs+++mkSigF :: Box (O a) -> F (SigF a)+mkSigF b = Wait (mkSigF' b) where++mkSigF' :: Box (O a) -> O (F (SigF a))+mkSigF' b = delay (Now (adv (unbox b) :>: mkSigF' b))+++fromSig :: Sig a -> SigF a+fromSig (x ::: xs) = x :>: delay (Now (fromSig (adv xs)))++  +switchAwait :: F (SigF a) -> F (SigF a) -> F(SigF a)+switchAwait _ (Now ys) = Now ys+switchAwait (Now (x :>: xs)) (Wait ys) = Now (x :>: delay (uncurry' switchAwait (adv (sync xs ys)) ))+switchAwait (Wait xs) (Wait ys) = Wait (delay (uncurry' switchAwait (adv (sync xs ys)) ))++switch :: SigF a -> F (SigF a) -> SigF a+switch _ (Now ys) = ys+switch (x :>: xs) (Wait ys) = x :>: delay (uncurry' switchAwait (adv (sync xs ys)))++switchS :: Stable a => SigF a -> F (a -> SigF a) -> SigF a+switchS (x :>: _) (Now f) = f x+switchS (x :>: xs) (Wait ys) = x :>: delay (uncurry' (switchAwaitS x) (adv (sync xs ys)))++switchAwaitS :: Stable a => a -> F (SigF a) -> F (a -> SigF a) -> F (SigF a)+switchAwaitS _ (Now (x :>: _)) (Now f) = Now (f x)+switchAwaitS _ (Now (x :>: xs)) (Wait ys) =+  Now (x :>: delay (uncurry' (switchAwaitS x) (adv (sync xs ys))))+switchAwaitS x (Wait _) (Now f) = Now (f x)+switchAwaitS x (Wait xs) (Wait ys) = Wait (delay (uncurry' (switchAwaitS x) (adv (sync xs ys))))++++filterMapAwait :: Box (a -> Maybe' b) -> F(SigF a) -> F (SigF b)+filterMapAwait f (Wait xs) = Wait (delay (filterMapAwait f (adv xs)))+filterMapAwait f (Now (x :>: xs)) = case unbox f x of+                                     Just' y  -> Now (y :>: delay (filterMapAwait f (adv xs)))+                                     Nothing' -> Wait (delay (filterMapAwait f (adv xs)))++filterMap :: Box (a -> Maybe' b) -> SigF a -> F (SigF b)+filterMap f xs = filterMapAwait f (Now xs)+++filterAwait :: Box (a -> Bool) -> F( SigF a) -> F (SigF a)+filterAwait p = filterMapAwait (box (\ x -> if unbox p x then Just' x else Nothing'))++filter :: Box (a -> Bool) -> SigF a -> F (SigF a)+filter p = filterMap (box (\ x -> if unbox p x then Just' x else Nothing'))++trigger :: Stable b => Box (a -> b -> c) -> SigF a -> SigF b -> SigF c+trigger f (a :>: as) (b :>: bs) =+  unbox f a b :>:+  delay (uncurry' (trigger' b f) (adv (sync as bs)))++triggerAwait :: Stable b => Box (a -> b -> c) -> F (SigF a) -> SigF b -> F (SigF c)+triggerAwait f (Now (a :>: as)) (b :>: bs)+  = Now (unbox f a b :>: delay (uncurry' (trigger' b f) (adv (sync as bs))))+triggerAwait f (Wait as) (b :>: bs)+  = Wait (delay (uncurry' (trigger' b f) (adv (sync as bs))))++trigger' :: Stable b => b -> Box (a -> b -> c) -> F (SigF a) -> F (SigF b) -> F (SigF c)+trigger' b f (Now (a :>: as)) (Wait bs) =+  Now (unbox f a b :>: delay (uncurry' (trigger' b f) (adv (sync as bs))))+trigger' _ f (Now (a :>: as)) (Now (b :>: bs)) =+  Now (unbox f a b :>: delay (uncurry' (trigger' b f) (adv (sync as bs))))+trigger' b f (Wait as) (Wait bs) =+  Wait (delay (uncurry' (trigger' b f) (adv (sync as bs))))+trigger' _ f (Wait as) (Now (b :>: bs)) =+  Wait (delay (uncurry' (trigger' b f) (adv (sync as bs))))+++mapAwait :: Box (a -> b) -> F (SigF a) -> F (SigF b)+mapAwait f (Now (x :>: xs)) = Now (unbox f x :>: delay (mapAwait f (adv xs)))+mapAwait f (Wait xs) = Wait (delay (mapAwait f (adv xs)))++map :: Box (a -> b) -> SigF a -> SigF b+map f (x :>: xs) = unbox f x :>: delay (mapAwait f (adv xs))++++zipWith :: (Stable a, Stable b) => Box(a -> b -> c) -> SigF a -> SigF b -> SigF c+zipWith f (a :>: as) (b :>: bs) = unbox f a b :>: delay (uncurry' (zipWithAwait f a b) (adv (sync as bs)))++zipWithAwait :: (Stable a, Stable b) => Box(a -> b -> c) -> a -> b -> F (SigF a) -> F (SigF b) -> F (SigF c)+zipWithAwait f _ _ (Now (a :>: as)) (Now (b :>: bs)) = Now (unbox f a b :>: delay (uncurry' (zipWithAwait f a b) (adv (sync as bs))))+zipWithAwait f _ b (Now (a :>: as)) (Wait bs) = Now (unbox f a b :>: delay (uncurry' (zipWithAwait f a b) (adv (sync as bs))))+zipWithAwait f a _ (Wait as) (Now (b :>: bs)) = Now (unbox f a b :>: delay (uncurry' (zipWithAwait f a b) (adv (sync as bs))))+zipWithAwait f a b (Wait as) (Wait bs) = Wait (delay (uncurry' (zipWithAwait f a b) (adv (sync as bs))))++scan :: (Stable b) => Box(b -> a -> b) -> b -> SigF a -> SigF b+scan f acc (a :>: as) = acc' :>: delay (scanAwait f acc' (adv as))+  where acc' = unbox f acc a++scanAwait :: (Stable b) => Box (b -> a -> b) -> b -> F (SigF a) -> F (SigF b)+scanAwait f acc (Now (a :>: as)) = Now (acc' :>: delay (scanAwait f acc' (adv as)))+  where acc' = unbox f acc a+scanAwait f acc (Wait as) = Wait (delay (scanAwait f acc (adv as)))
+ src/AsyncRattus/InternalPrimitives.hs view
@@ -0,0 +1,172 @@+{-# LANGUAGE GADTs #-}++module AsyncRattus.InternalPrimitives where++import Prelude hiding (Left, Right)+import Data.IntSet (IntSet)+import qualified Data.IntSet as IntSet++-- An input channel is identified by an integer. The programmer should not know about it.+type InputChannelIdentifier = Int++type Clock = IntSet++singletonClock :: InputChannelIdentifier -> Clock+singletonClock = IntSet.singleton++clockUnion :: Clock -> Clock -> Clock+clockUnion = IntSet.union++channelMember :: InputChannelIdentifier -> Clock -> Bool+channelMember = IntSet.member++data InputValue where+  InputValue :: !InputChannelIdentifier -> !a -> InputValue+++-- | The "later" type modality. A value @v@ of type @O 𝜏@ consists of+-- two components: Its clock, denoted @cl(v)@, and a delayed+-- computation that will produce a value of type @𝜏@ as soon as the+-- clock @cl(v)@ ticks. The clock @cl(v)@ is only used for type+-- checking and is not directly accessible, whereas the delayed+-- computation is accessible via 'adv' and 'select'.++data O a = Delay !Clock (InputValue -> a)++-- | The return type of the 'select' primitive.+data Select a b = Fst !a !(O b) | Snd !(O a) !b | Both !a !b++asyncRattusError pr = error (pr ++ ": Did you forget to mark this as Async Rattus code?")++-- | This is the constructor for the "later" modality 'O':+--+-- >     Γ ✓θ ⊢ t :: 𝜏+-- > --------------------+-- >  Γ ⊢ delay t :: O 𝜏+--+-- The typing rule requires that its argument @t@ typecheck with an+-- additional tick @✓θ@ of some clock @θ@.+{-# INLINE [1] delay #-}+delay :: a -> O a+delay _ = asyncRattusError "delay"++extractClock :: O a -> Clock+extractClock (Delay cl _) = cl++{-# INLINE [1] adv' #-}+adv' :: O a -> InputValue -> a+adv' (Delay _ f) inp = f inp+++-- | This is the eliminator for the "later" modality 'O':+--+-- >   Γ ⊢ t :: O 𝜏     Γ' tick-free+-- > ---------------------------------+-- >     Γ ✓cl(t) Γ' ⊢ adv t :: 𝜏+--+-- It requires that a tick @✓θ@ is in the context whose clock matches+-- exactly the clock of @t@, i.e. @θ = cl(t)@.++{-# INLINE [1] adv #-}+adv :: O a -> a+adv _ = asyncRattusError "adv"++-- | If we want to eliminate more than one delayed computation, i.e.\+-- two @s :: O σ@ and @t :: O 𝜏@, we need to use 'select' instead of+-- just 'adv'.+--+-- >   Γ ⊢ s :: O σ     Γ ⊢ t :: O 𝜏     Γ' tick-free+-- > --------------------------------------------------+-- >    Γ ✓cl(s)⊔cl(t) Γ' ⊢ select s t :: Select σ 𝜏+--+-- It requires that we have a tick @✓θ@ in the context whose clock+-- matches the union of the clocks of @s@ and @t@, i.e. @θ =+-- cl(s)⊔cl(t)@. The union of two clocks ticks whenever either of the+-- two clocks ticks, i.e. @cl(s)⊔cl(t)@, whenever @cl(s)@ or @cl(t)@+-- ticks.+--+-- That means there are three possible outcomes, which are reflected+-- in the result type of @select s t@. A value of @Select σ 𝜏@ is+-- either+--+--   * a value of type @σ@ and a delayed computation of type @O 𝜏@, if+--     @cl(s)@ ticks before @cl(t)@,+--+--   * a value of type @𝜏@ and a delayed computation of type @O σ@, if+--     @cl(t)@ ticks before @cl(s)@, or+--+--   * a value of type @σ@ and a value of type @𝜏@, if @cl(s)@ and+--   * @cl(s)@ tick simultaneously.+++{-# INLINE [1] select #-}+select :: O a -> O b -> Select a b+select _ _ = asyncRattusError "select"++select' :: O a -> O b -> InputValue -> Select a b+select' a@(Delay clA inpFA) b@(Delay clB inpFB) inputValue@(InputValue chId _)+  = if chId `channelMember` clA then+      if chId `channelMember` clB then Both (inpFA inputValue) (inpFB inputValue)+      else Fst (inpFA inputValue) b+    else Snd a (inpFB inputValue)+++-- | The clock of @never :: O 𝜏@ will never tick, i.e. it will never+-- produce a value of type @𝜏@. With 'never' we can for example+-- implement the constant signal @x ::: never@ of type @Sig a@ for any @x ::+-- a@.+never :: O a+never = Delay IntSet.empty (error "Trying to adv on the 'never' delayed computation")++-- | A type is @Stable@ if it is a strict type and the later modality+-- @O@ and function types only occur under @Box@.+--+-- For example, these types are stable: @Int@, @Box (a -> b)@, @Box (O+-- Int)@, @Box (Sig a -> Sig b)@.+--+-- But these types are not stable: @[Int]@ (because the list type is+-- not strict), @Int -> Int@, (function type is not stable), @O+-- Int@, @Sig Int@.++class  Stable a  where++++-- | The "stable" type modality. A value of type @Box a@ is a+-- time-independent computation that produces a value of type @a@.+-- Use 'box' and 'unbox' to construct and consume 'Box'-types.+data Box a = Box a+++-- | This is the constructor for the "stable" modality 'Box':+--+-- >     Γ☐ ⊢ t :: 𝜏+-- > --------------------+-- >  Γ ⊢ box t :: Box 𝜏+--+-- where Γ☐ is obtained from Γ by removing all ticks and all variables+-- @x :: 𝜏@, where 𝜏 is not a stable type.++{-# INLINE [1] box #-}+box :: a -> Box a+box x = Box x+++-- | This is the eliminator for the "stable" modality  'Box':+--+-- >   Γ ⊢ t :: Box 𝜏+-- > ------------------+-- >  Γ ⊢ unbox t :: 𝜏+{-# INLINE [1] unbox #-}+unbox :: Box a -> a+unbox (Box d) = d+++{-# RULES+  "unbox/box"    forall x. unbox (box x) = x+    #-}+++{-# RULES+  "box/unbox"    forall x. box (unbox x) = x+    #-}
+ src/AsyncRattus/Plugin.hs view
@@ -0,0 +1,166 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE CPP #-}+++-- | The plugin to make it all work.++module AsyncRattus.Plugin (plugin, AsyncRattus(..)) where+import AsyncRattus.Plugin.StableSolver+import AsyncRattus.Plugin.ScopeCheck+import AsyncRattus.Plugin.Strictify+import AsyncRattus.Plugin.SingleTick+import AsyncRattus.Plugin.CheckClockCompatibility+import AsyncRattus.Plugin.Utils+import AsyncRattus.Plugin.Annotation+import AsyncRattus.Plugin.Transform++import Prelude hiding ((<>))++import Control.Monad+import Data.Maybe+import Data.List+import Data.Data hiding (tyConName)+import qualified Data.Set as Set+import Data.Set (Set)++#if __GLASGOW_HASKELL__ >= 900+import GHC.Plugins+import GHC.Tc.Types+#else+import GhcPlugins+import TcRnTypes+#endif++-- | Use this to enable Asynchronous Rattus' plugin, either by supplying the option+-- @-fplugin=AsyncRattus.Plugin@ directly to GHC, or by including the+-- following pragma in each source file:+-- +-- > {-# OPTIONS -fplugin=AsyncRattus.Plugin #-}+plugin :: Plugin+plugin = defaultPlugin {+  installCoreToDos = install,+  pluginRecompile = purePlugin,+  typeCheckResultAction = typechecked,+  tcPlugin = tcStable+  }+++data Options = Options {debugMode :: Bool}++typechecked :: [CommandLineOption] -> ModSummary -> TcGblEnv -> TcM TcGblEnv+typechecked _ _ env = checkAll env >> return env++install :: [CommandLineOption] -> [CoreToDo] -> CoreM [CoreToDo]+install opts todo = case find findSamePass todo of       -- check that we don't run the transformation twice+                      Nothing -> return (strPass : todo) -- (e.g. if the "-fplugin" option is used twice)+                      _ -> return todo+    where name = "Async Rattus strictify"+          strPass = CoreDoPluginPass name (strictifyProgram Options{debugMode = dmode})+          dmode = "debug" `elem` opts+          findSamePass (CoreDoPluginPass s _) = s == name+          findSamePass _ = False+          ++-- | Apply the following operations to all Asynchronous Rattus definitions in the+-- program:+--+-- * Transform into single tick form (see SingleTick module)+-- * Check whether lazy data types are used (see Strictify module)+-- * Transform into call-by-value form (see Strictify module)++strictifyProgram :: Options -> ModGuts -> CoreM ModGuts+strictifyProgram opts guts = do+  newBinds <- mapM (strictify opts guts) (mg_binds guts)+  return guts { mg_binds = newBinds }++strictify :: Options -> ModGuts -> CoreBind -> CoreM CoreBind+strictify opts guts b@(Rec bs) = do+  let debug = debugMode opts+  tr <- liftM or (mapM (shouldProcessCore guts . fst) bs)+  if tr then do+    let vs = map fst bs+    es' <- mapM (\ (v,e) -> do+      processCore <- shouldProcessCore guts v+      if not processCore+      then do+        when debug $ putMsg $ text "Skipping binding: " <> ppr v+        return e+      else checkAndTransform guts (Set.fromList vs) debug v e+      ) bs+    when debug $ putMsg $ "Plugin | result of transformation: " <> ppr es'+    return (Rec (zip vs es'))+  else return b+strictify opts guts b@(NonRec v e) = do+    let debug = debugMode opts+    when debug $ putMsg $ text "Processing binding: " <> ppr v <> text " | Non-recursive binding"+    when debug $ putMsg $ text "Expr: " <> ppr e+    processCore <- shouldProcessCore guts v+    if not processCore then do+      when debug $ putMsg $ text "Skipping binding: " <> ppr v+      return b+    else do+      transformed <- checkAndTransform guts Set.empty debug v e+      when debug $ putMsg $ "Plugin | result of transformation: " <> ppr transformed+      return $ NonRec v transformed++checkAndTransform :: ModGuts -> Set Var -> Bool -> Var -> CoreExpr -> CoreM CoreExpr+checkAndTransform guts recursiveSet debug v e = do+  when debug $ putMsg $ text "Processing binding: " <> ppr v+  when debug $ putMsg $ text "Expr: " <> ppr e+  allowRec <- allowRecursion guts v+  singleTick <- toSingleTick e+  when debug $ putMsg $ text "Single-tick: " <> ppr singleTick+  lazy <- allowLazyData guts v+  strict <- strictifyExpr (SCxt (nameSrcSpan $ getName v) (not lazy)) singleTick+  when debug $ putMsg $ text "Strict single-tick: " <> ppr strict+  checkExpr CheckExpr{ recursiveSet = recursiveSet, oldExpr = e,+                        verbose = debug,+                        allowRecExp = allowRec} strict+  transform strict++getModuleAnnotations :: Data a => ModGuts -> [a]+getModuleAnnotations guts = anns'+  where anns = filter (\a-> case ann_target a of+                         ModuleTarget m -> m == (mg_module guts)+                         _ -> False) (mg_anns guts)+        anns' = mapMaybe (fromSerialized deserializeWithData . ann_value) anns+++++allowLazyData :: ModGuts -> CoreBndr -> CoreM Bool+allowLazyData guts bndr = do+  l <- annotationsOn guts bndr :: CoreM [AsyncRattus]+  return (AllowLazyData `elem` l)++allowRecursion :: ModGuts -> CoreBndr -> CoreM Bool+allowRecursion guts bndr = do+  l <- annotationsOn guts bndr :: CoreM [AsyncRattus]+  return (AllowRecursion `elem` l)++expectError :: ModGuts -> CoreBndr -> CoreM Bool+expectError guts bndr = do+  l <- annotationsOn guts bndr :: CoreM [InternalAnn]+  return $ ExpectError `elem` l+++shouldProcessCore :: ModGuts -> CoreBndr -> CoreM Bool+shouldProcessCore guts bndr = do+  l <- annotationsOn guts bndr :: CoreM [AsyncRattus]+  expectScopeError <- expectError guts bndr+  return (AsyncRattus `elem` l && notElem NotAsyncRattus l && userFunction bndr && not expectScopeError)++annotationsOn :: (Data a) => ModGuts -> CoreBndr -> CoreM [a]+annotationsOn guts bndr = do+#if __GLASGOW_HASKELL__ >= 900+  (_,anns)  <- getAnnotations deserializeWithData guts+  return $+    lookupWithDefaultUFM anns [] (varName bndr) +++    getModuleAnnotations guts+#else    +  anns <- getAnnotations deserializeWithData guts+  return $+    lookupWithDefaultUFM anns [] (varUnique bndr) +++    getModuleAnnotations guts+#endif
+ src/AsyncRattus/Plugin/Annotation.hs view
@@ -0,0 +1,40 @@+{-# LANGUAGE DeriveDataTypeable #-}+module AsyncRattus.Plugin.Annotation (AsyncRattus(..), InternalAnn (..)) where++import Data.Data++-- | Use this type to mark a Haskell function definition as an+-- Asynchronous Rattus function:+--+-- > {-# ANN myFunction AsyncRattus #-}+-- +-- Or mark a whole module as consisting of Asynchronous Rattus functions only:+--+-- > {-# ANN module AsyncRattus #-}+--+-- If you use the latter option, you can mark exceptions+-- (i.e. functions that should be treated as ordinary Haskell function+-- definitions) as follows:+--+-- > {-# ANN myFunction NotAsyncRattus #-}+--+-- By default all Asynchronous Rattus functions are checked for use of lazy data+-- types, since these may cause memory leaks. If any lazy data types+-- are used, a warning is issued. These warnings can be disabled by+-- annotating the module or the function with 'AllowLazyData'+--+-- > {-# ANN myFunction AllowLazyData #-}+-- >+-- > {-# ANN module AllowLazyData #-}+--+-- Asynchronous Rattus only allows guarded recursion, i.e. recursive calls must+-- occur in the scope of a tick. Structural recursion over strict data+-- types is safe as well, but is currently not checked. To disable the+-- guarded recursion check, annotate the module or function with+-- 'AllowRecursion'.++data AsyncRattus = AsyncRattus | NotAsyncRattus | AllowLazyData | AllowRecursion deriving (Typeable, Data, Show, Ord, Eq)+++-- | This annotation type is for internal use only.+data InternalAnn = ExpectError | ExpectWarning deriving (Typeable, Data, Show, Eq, Ord)
+ src/AsyncRattus/Plugin/CheckClockCompatibility.hs view
@@ -0,0 +1,268 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE CPP #-}++-- | This module implements the check that the transformed code is+-- typable in the single tick calculus.++module AsyncRattus.Plugin.CheckClockCompatibility+  (checkExpr, CheckExpr (..)) where++#if __GLASGOW_HASKELL__ >= 902+import GHC.Types.Tickish+#endif++#if __GLASGOW_HASKELL__ >= 900+import GHC.Plugins+#else+import GhcPlugins+#endif++import AsyncRattus.Plugin.Utils+import qualified AsyncRattus.Plugin.PrimExpr as Prim+import Prelude hiding ((<>))+import Data.Set (Set)+import qualified Data.Set as Set+import Data.Map (Map)+import qualified Data.Map as Map+import Data.Maybe (isJust)+import Control.Monad (foldM, when)+import Control.Applicative ((<|>))+import System.Exit (exitFailure)++type LCtx = Set Var+data HiddenReason = BoxApp | AdvApp | NestedRec Var | FunDef | DelayApp+type Hidden = Map Var HiddenReason++data TypeError = TypeError SrcSpan SDoc+++data Ctx = Ctx+  { current :: LCtx,+    hidden :: Hidden,+    earlier :: Maybe LCtx,+    srcLoc :: SrcSpan,+    recDef :: Set Var, -- ^ recursively defined variables +    stableTypes :: Set Var,+    allowRecursion :: Bool,+    allowGuardedRec :: Bool+    }++hasTick :: Ctx -> Bool+hasTick = isJust . earlier++stabilize :: HiddenReason -> Ctx -> Ctx+stabilize hr c = c+  {current = Set.empty,+   earlier = Nothing,+   hidden = hidden c `Map.union` Map.fromSet (const hr) ctxHid,+   allowGuardedRec = False+  }+  where ctxHid = maybe (current c) (Set.union (current c)) (earlier c)++data Scope = Hidden SDoc | Visible++getScope  :: Ctx -> Var -> Scope+getScope c v =+    if v `Set.member` recDef c then+      if allowGuardedRec c || allowRecursion c || typeClassFunction v then Visible+      else Hidden ("(Mutually) recursive call to " <> ppr v <> " must occur under delay")+    else case Map.lookup v (hidden c) of+      Just reason ->+        if (isStable (stableTypes c) (varType v)) then Visible+        else case reason of+          NestedRec rv ->+            if allowRecursion c then Visible+            else Hidden ("Variable " <> ppr v <> " is no longer in scope:"+                         $$ "It appears in a local recursive definition (namely of " <> ppr rv <> ")"+                         $$ "and is of type " <> ppr (varType v) <> ", which is not stable.")+          BoxApp -> Hidden ("Variable " <> ppr v <> " is no longer in scope:" $$+                       "It occurs under " <> keyword "box" $$ "and is of type " <> ppr (varType v) <> ", which is not stable.")+          AdvApp -> Hidden ("Variable " <> ppr v <> " is no longer in scope: It occurs under adv.")++          FunDef -> Hidden ("Variable " <> ppr v <> " is no longer in scope: It occurs in a function that is defined under a delay, is a of a non-stable type " <> ppr (varType v) <> ", and is bound outside delay")+          DelayApp -> Hidden ("Variable " <> ppr v <> " is no longer in scope: It occurs under two occurrences of delay and is a of a non-stable type " <> ppr (varType v))+      Nothing+          | maybe False (Set.member v) (earlier c) ->+            if isStable (stableTypes c) (varType v) then Visible+            else Hidden ("Variable " <> ppr v <> " is no longer in scope:" $$+                         "It occurs under delay" $$ "and is of type " <> ppr (varType v) <> ", which is not stable.")+          | Set.member v (current c) -> Visible+          | otherwise -> Visible++++pickFirst :: SrcSpan -> SrcSpan -> SrcSpan+pickFirst s@RealSrcSpan{} _ = s+pickFirst _ s = s++typeError :: Ctx -> Var -> SDoc -> TypeError+typeError ctx var = TypeError (pickFirst (srcLoc ctx) (nameSrcSpan (varName var)))++instance Outputable TypeError where+  ppr (TypeError srcLoc sdoc) = text "TypeError at " <> ppr srcLoc <> text ": " <> ppr sdoc++emptyCtx :: CheckExpr -> Ctx+emptyCtx c =+  Ctx { current =  Set.empty,+        earlier = Nothing,+        hidden = Map.empty,+        srcLoc = noLocationInfo,+        recDef = recursiveSet c,+        stableTypes = Set.empty,+        allowRecursion = allowRecExp c,+        allowGuardedRec = False+        }++stabilizeLater :: Ctx -> Ctx+stabilizeLater c =+  case earlier c of+    Just earl -> c {earlier = Nothing,+                    hidden = hidden c `Map.union` Map.fromSet (const FunDef) earl}+    Nothing -> c++isStableConstr :: Type -> CoreM (Maybe Var)+isStableConstr t =+  case splitTyConApp_maybe t of+    Just (con,[args]) ->+      case getNameModule con of+        Just (name, mod) ->+          if isRattModule mod && name == "Stable"+          then return (getTyVar_maybe args)+          else return Nothing+        _ -> return Nothing+    _ ->  return Nothing++-- should be equatable+type SymbolicClock = Set Var++mkClock1 :: Var -> SymbolicClock+mkClock1 = Set.singleton++mkClock2 :: Var -> Var -> SymbolicClock+mkClock2 v1 v2 = Set.fromList [v1, v2]++newtype CheckResult = CheckResult{+  -- if present, contains the variable of the primitive applied so we can report its position+  -- in case of an error, and the clock for the primitive+  prim :: Maybe (Var, SymbolicClock)+}++instance Outputable CheckResult where+  ppr (CheckResult prim) = text "CheckResult {prim = " <> ppr prim <> text "}"++emptyCheckResult :: CheckResult+emptyCheckResult = CheckResult {prim = Nothing}++data CheckExpr = CheckExpr{+  recursiveSet :: Set Var,+  oldExpr :: Expr Var,+  verbose :: Bool,+  allowRecExp :: Bool+  }++checkExpr :: CheckExpr -> Expr Var -> CoreM ()+checkExpr c e = do+  when (verbose c) $ putMsg $ text "checkExpr: " <> ppr e+  res <- checkExpr' (emptyCtx c) e+  case res of+    Right _ -> do when (verbose c) $ putMsgS "checkExpr succeeded."+    Left (TypeError src doc) ->+      let printErrMsg = if verbose c+          then do+            printMessage SevError src ("Internal error in Async Rattus Plugin: single tick transformation did not preserve typing." $$ doc)+            putMsgS "-------- old --------"+            putMsg $ ppr (oldExpr c)+            putMsgS "-------- new --------"+            putMsg (ppr e)+            +          else do+            printMessage SevError noSrcSpan ("Internal error in Async Rattus Plugin: single tick transformation did not preserve typing." $$+                                  "Compile with flags \"-fplugin-opt AsyncRattus.Plugin:debug\" and \"-g2\" for detailed information")+      in do+        printErrMsg+        liftIO exitFailure+++checkExpr' :: Ctx -> Expr Var -> CoreM (Either TypeError CheckResult)+checkExpr' c (App e e') | isType e' || (not $ tcIsLiftedTypeKind $ typeKind $ exprType e')+  = checkExpr' c e+checkExpr' c@Ctx{current = cur, earlier = earl} expr@(App e e') =+  case Prim.isPrimExpr expr of+    Just (Prim.BoxApp _) ->+      checkExpr' (stabilize BoxApp c) e'+    Just (Prim.DelayApp f _) -> do+      let c' = case earl of+                 Nothing -> c{current = Set.empty, earlier = Just cur, allowGuardedRec = True}+                 Just earl' -> c{ current = Set.empty, earlier = Just cur, allowGuardedRec = True,+                                  hidden = hidden c `Map.union` Map.fromSet (const DelayApp) earl'}+      eRes <- checkExpr' c' e'+      case eRes of+        Left err -> return $ Left err+        Right (CheckResult {prim = Nothing}) -> return $ Left $ typeError c f (text "Each delay must contain an adv or select")+        Right _ -> return $ Right emptyCheckResult+    Just (Prim.AdvApp f _) | not (hasTick c) -> return $ Left $ typeError c f (text "can only use adv under delay")+    Just (Prim.AdvApp f (arg, _)) -> return $ Right $ CheckResult {prim = Just (f, mkClock1 arg)}+    Just (Prim.SelectApp f _ _) | not (hasTick c) -> return $ Left $ typeError c f (text "can only use select under delay")+    Just (Prim.SelectApp f (arg1, _) (arg2, _))-> return $ Right $ CheckResult {prim = Just (f, mkClock2 arg1 arg2)}+    Nothing -> checkBoth c e e'+checkExpr' c (Case e v _ alts) = do+    res <- checkExpr' c' e+    resAll <- mapM (\(Alt _ _ altE) -> checkExpr' c altE) alts+    foldM (fmap return . combine c) res resAll+  where c' = addVars [v] c+checkExpr' c (Lam v e)+  | isTyVar v || (not $ tcIsLiftedTypeKind $ typeKind $ varType v) = do+      is <- isStableConstr (varType v)+      let c' = case is of+            Nothing -> c+            Just t -> c{stableTypes = Set.insert t (stableTypes c)}+      checkExpr' c' e+  | otherwise = checkExpr' (addVars [v] (stabilizeLater c)) e+checkExpr' _ (Type _)  = return $ Right emptyCheckResult+checkExpr' _ (Lit _)  = return $ Right emptyCheckResult+checkExpr' _ (Coercion _)  = return $ Right emptyCheckResult+checkExpr' c (Tick (SourceNote span _name) e) =+  checkExpr' c{srcLoc = fromRealSrcSpan span} e+checkExpr' c (Tick _ e) = checkExpr' c e+checkExpr' c (Cast e _) = checkExpr' c e+checkExpr' c (Let (NonRec _ e1) e2) = do+  res1 <- checkExpr' c e1+  res2 <- checkExpr' c e2+  return $ combine c res1 res2+checkExpr' c (Let (Rec binds) e2) = do+    resAll <- mapM (\ (v,e) -> checkExpr' (c' v) e) binds+    res <- checkExpr' (addVars vs c) e2+    foldM (fmap return . combine c) res resAll+  where vs = map fst binds+        ctxHid = maybe (current c) (Set.union (current c)) (earlier c)+        c' v = c {current = Set.empty,+                  earlier = Nothing,+                  hidden =  hidden c `Map.union`+                   Map.fromSet (const (NestedRec v)) ctxHid,+                  recDef = recDef c `Set.union` Set.fromList vs }+checkExpr' c  (Var v)+  | tcIsLiftedTypeKind $ typeKind $ varType v =  case getScope c v of+             Hidden reason -> return $ Left $ typeError c v reason+             Visible -> return $ Right emptyCheckResult+  | otherwise = return $ Right emptyCheckResult++addVars :: [Var] -> Ctx -> Ctx+addVars v c = c{current = Set.fromList v `Set.union` current c }++checkBoth :: Ctx -> CoreExpr -> CoreExpr -> CoreM (Either TypeError CheckResult)+checkBoth c e e' = do+  c1 <- checkExpr' c e+  c2 <- checkExpr' c e'+  return $ combine c c1 c2++-- Combines two CheckResults such that the clocks therein are compatible.+-- If both CheckResults have PrimVars, one is picked arbitrarily.+combine :: Ctx -> Either TypeError CheckResult -> Either TypeError CheckResult -> Either TypeError CheckResult+combine c eRes1 eRes2 = do+  res1 <- eRes1+  res2 <- eRes2+  case (res1, res2) of+    (CheckResult (Just (_, cl1)), CheckResult (Just (_, cl2))) | cl1 == cl2 -> Right res2+    (CheckResult (Just _), CheckResult (Just (p, _))) -> Left $ typeError c p "Only one adv/select allowed in a delay"+    (CheckResult maybeP, CheckResult maybeP') -> Right $ CheckResult {prim = maybeP <|> maybeP'}
+ src/AsyncRattus/Plugin/Dependency.hs view
@@ -0,0 +1,503 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE GADTs #-}++-- | This module is used to perform a dependency analysis of top-level+-- function definitions, i.e. to find out which defintions are+-- (mutual) recursive. To this end, this module also provides+-- functions to compute, bound variables and variable occurrences.++module AsyncRattus.Plugin.Dependency (dependency, HasBV (..),printBinds) where++#if __GLASGOW_HASKELL__ >= 900+import GHC.Plugins+import GHC.Data.Bag+import GHC.Hs.Type+#else+import GhcPlugins+import Bag+#if __GLASGOW_HASKELL__ >= 810+import GHC.Hs.Types+#else+import HsTypes+#endif+#endif++#if __GLASGOW_HASKELL__ >= 810+import GHC.Hs.Extension+import GHC.Hs.Expr+import GHC.Hs.Pat+import GHC.Hs.Binds+#else +import HsExtension+import HsExpr+import HsPat+import HsBinds+#endif++#if __GLASGOW_HASKELL__ >= 904+import GHC.Parser.Annotation+#elif __GLASGOW_HASKELL__ >= 902+import Language.Haskell.Syntax.Extension+import GHC.Parser.Annotation+#endif+++import Data.Set (Set)+import qualified Data.Set as Set+import Data.Graph+import Data.Maybe+import Data.Either+import Prelude hiding ((<>))++++-- | Compute the dependencies of a bag of bindings, returning a list+-- of the strongly-connected components.+dependency :: Bag (LHsBindLR GhcTc GhcTc) -> [SCC (LHsBindLR GhcTc GhcTc, Set Var)]+dependency binds = map AcyclicSCC noDeps ++ catMaybes (map filterJust (stronglyConnComp (concat deps)))+  where (deps,noDeps) = partitionEithers $ map mkDep $ bagToList binds+        mkDep :: GenLocated l (HsBindLR GhcTc GhcTc) ->+                 Either [(Maybe (GenLocated l (HsBindLR GhcTc GhcTc), Set Var), Name, [Name])]+                 (GenLocated l (HsBindLR GhcTc GhcTc), Set Var)+        mkDep b =+          let dep = map varName $ Set.toList (getFV b)+              vars = getBV b in+          case Set.toList vars of+            (v:vs) -> Left ((Just (b,vars), varName v , dep) : map (\ v' -> (Nothing, varName v' , dep)) vs)+            [] -> Right (b,vars)+        filterJust (AcyclicSCC Nothing) = Nothing -- this should not happen+        filterJust (AcyclicSCC (Just b)) = Just (AcyclicSCC b)+        filterJust (CyclicSCC bs) = Just (CyclicSCC (catMaybes bs))+++printBinds (AcyclicSCC bind) = liftIO (putStr "acyclic bind: ") >> printBind (fst bind) >> liftIO (putStrLn "") +printBinds (CyclicSCC binds) = liftIO (putStr "cyclic binds: ") >> mapM_ (printBind . fst) binds >> liftIO (putStrLn "") +++printBind (L _ FunBind{fun_id = L _ name}) = +  liftIO $ putStr $ (getOccString name ++ " ")+printBind (L _ (VarBind {var_id = name})) =   liftIO $ putStr $ (getOccString name ++ " ")+#if __GLASGOW_HASKELL__ < 904+printBind (L _ (AbsBinds {abs_exports = exp})) = +#else+printBind (L _ (XHsBindsLR (AbsBinds {abs_exports = exp}))) = +#endif+  mapM_ (\ e -> liftIO $ putStr $ ((getOccString $ abe_poly e)  ++ " ")) exp+printBind _ = return ()+++-- | Computes the variables that are bound by a given piece of syntax.++class HasBV a where+  getBV :: a -> Set Var++instance HasBV (HsBindLR GhcTc GhcTc) where+  getBV (FunBind{fun_id = L _ v}) = Set.singleton v+  getBV (PatBind {pat_lhs = pat}) = getBV pat+  getBV (VarBind {var_id = v}) = Set.singleton v+  getBV PatSynBind{} = Set.empty+#if __GLASGOW_HASKELL__ < 900+  getBV (XHsBindsLR e) = getBV e+  getBV (AbsBinds {abs_exports = es}) = Set.fromList (map abe_poly es)+#elif __GLASGOW_HASKELL__ < 904+  getBV (AbsBinds {abs_exports = es}) = Set.fromList (map abe_poly es)+#else+  getBV (XHsBindsLR (AbsBinds {abs_exports = es})) = Set.fromList (map abe_poly es)+#endif+  +instance HasBV a => HasBV (GenLocated b a) where+  getBV (L _ e) = getBV e++instance HasBV a => HasBV [a] where+  getBV ps = foldl (\s p -> getBV p `Set.union` s) Set.empty ps++#if __GLASGOW_HASKELL__ >= 904+getRecFieldRhs = hfbRHS+#else+getRecFieldRhs = hsRecFieldArg+#endif++#if __GLASGOW_HASKELL__ >= 902+getConBV (PrefixCon _ ps) = getBV ps+#else+getConBV (PrefixCon ps) = getBV ps+#endif+getConBV (InfixCon p p') = getBV p `Set.union` getBV p'+getConBV (RecCon (HsRecFields {rec_flds = fs})) = foldl run Set.empty fs+      where run s (L _ f) = getBV (getRecFieldRhs f) `Set.union` s++#if __GLASGOW_HASKELL__ >= 900 && __GLASGOW_HASKELL__ < 904+instance HasBV CoPat where+  getBV CoPat {co_pat_inner = p} = getBV p+#elif __GLASGOW_HASKELL__ >= 904+instance HasBV XXPatGhcTc where+  getBV CoPat {co_pat_inner = p} = getBV p+  getBV (ExpansionPat _ p) = getBV p+#endif++instance HasBV (Pat GhcTc) where+  getBV (VarPat _ (L _ v)) = Set.singleton v+  getBV (LazyPat _ p) = getBV p+#if __GLASGOW_HASKELL__ >= 906+  getBV (AsPat _ (L _ v) _ p) = Set.insert v (getBV p)+#else+  getBV (AsPat _ (L _ v) p) = Set.insert v (getBV p)+#endif+  getBV (BangPat _ p) = getBV p+  getBV (ListPat _ ps) = getBV ps+  getBV (TuplePat _ ps _) = getBV ps+  getBV (SumPat _ p _ _) = getBV p+  getBV (ViewPat _ _ p) = getBV p++  getBV (SplicePat _ sp) =+    case sp of+#if __GLASGOW_HASKELL__ < 906+      HsTypedSplice _ _ v _ -> Set.singleton v+      HsSpliced _ _ (HsSplicedPat p) -> getBV p+      HsUntypedSplice _ _ v _ ->  Set.singleton v+      HsQuasiQuote _ p p' _ _ -> Set.fromList [p,p']+      _ -> Set.empty+#else+      HsUntypedSpliceExpr _ e -> getFV e+      HsQuasiQuote _ v _  -> Set.singleton v+#endif++  getBV (NPlusKPat _ (L _ v) _ _ _ _) = Set.singleton v+  getBV (NPat {}) = Set.empty+  getBV (XPat p) = getBV p+  getBV (WildPat {}) = Set.empty+  getBV (LitPat {}) = Set.empty+#if __GLASGOW_HASKELL__ >= 904  +  getBV (ParPat _ _ p _) = getBV p+#else+  getBV (ParPat _ p) = getBV p+#endif+#if __GLASGOW_HASKELL__ >= 900+  getBV (ConPat {pat_args = con}) = getConBV con+#else+  getBV (ConPatIn (L _ v) con) = Set.insert v (getConBV con)+  getBV (ConPatOut {pat_args = con}) = getConBV con+  getBV (CoPat _ _ p _) = getBV p+#endif+#if __GLASGOW_HASKELL__ >= 808+  getBV (SigPat _ p _) = getBV p+#else+  getBV (SigPat _ p)   = getBV p+#endif++#if __GLASGOW_HASKELL__ >= 904++#elif __GLASGOW_HASKELL__ >= 810+instance HasBV NoExtCon where+#else+instance HasBV NoExt where+#endif+#if __GLASGOW_HASKELL__ < 904+  getBV _ = Set.empty+#endif++-- | Syntax that may contain variables.+class HasFV a where+  -- | Compute the set of variables occurring in the given piece of+  -- syntax.  The name falsely suggests that returns free variables,+  -- but in fact it returns all variable occurrences, no matter+  -- whether they are free or bound.+  getFV :: a -> Set Var ++instance HasFV a => HasFV (GenLocated b a) where+  getFV (L _ e) = getFV e+  +instance HasFV a => HasFV [a] where+  getFV es = foldMap getFV es++instance HasFV a => HasFV (Bag a) where+  getFV es = foldMap getFV es++instance HasFV Var where+  getFV v = Set.singleton v++instance HasFV a => HasFV (MatchGroup GhcTc a) where+  getFV MG {mg_alts = alts} = getFV alts+#if __GLASGOW_HASKELL__ < 900+  getFV (XMatchGroup e) = getFV e+#endif+  +instance HasFV a => HasFV (Match GhcTc a) where+  getFV Match {m_grhss = rhss} = getFV rhss+#if __GLASGOW_HASKELL__ < 900+  getFV (XMatch e) = getFV e+#endif++instance HasFV (HsTupArg GhcTc) where+  getFV (Present _ e) = getFV e+  getFV Missing {} = Set.empty+#if __GLASGOW_HASKELL__ < 900+  getFV (XTupArg e) = getFV e+#endif++instance HasFV a => HasFV (GRHS GhcTc a) where+  getFV (GRHS _ g b) = getFV g `Set.union` getFV b+#if __GLASGOW_HASKELL__ < 900+  getFV (XGRHS e) = getFV e+#endif++instance HasFV a => HasFV (GRHSs GhcTc a) where+  getFV GRHSs {grhssGRHSs = rhs, grhssLocalBinds = lbs} =+    getFV rhs `Set.union` getFV lbs+#if __GLASGOW_HASKELL__ < 900+  getFV (XGRHSs e) = getFV e+#endif+++instance HasFV (HsLocalBindsLR GhcTc GhcTc) where+  getFV (HsValBinds _ bs) = getFV bs+  getFV (HsIPBinds _ bs) = getFV bs+  getFV EmptyLocalBinds {} = Set.empty+#if __GLASGOW_HASKELL__ < 900+  getFV (XHsLocalBindsLR e) = getFV e+#endif+  +instance HasFV (HsValBindsLR GhcTc GhcTc) where+  getFV (ValBinds _ b _) = getFV b+  getFV (XValBindsLR b) = getFV b++instance HasFV (NHsValBindsLR GhcTc) where+  getFV (NValBinds bs _) = foldMap (getFV . snd) bs++instance HasFV (HsBindLR GhcTc GhcTc) where+  getFV FunBind {fun_matches = ms} = getFV ms+  getFV PatBind {pat_rhs = rhs} = getFV rhs+  getFV VarBind {var_rhs = rhs} = getFV rhs+  getFV PatSynBind {} = Set.empty+#if __GLASGOW_HASKELL__ < 904+  getFV AbsBinds {abs_binds = bs} = getFV bs+#else+  getFV (XHsBindsLR AbsBinds {abs_binds = bs}) = getFV bs+#endif+#if __GLASGOW_HASKELL__ < 900+  getFV (XHsBindsLR e) = getFV e+#endif++instance HasFV (IPBind GhcTc) where+  getFV (IPBind _ _ e) = getFV e+#if __GLASGOW_HASKELL__ < 900+  getFV (XIPBind e) = getFV e+#endif++instance HasFV (HsIPBinds GhcTc) where+  getFV (IPBinds _ bs) = getFV bs+#if __GLASGOW_HASKELL__ < 900+  getFV (XHsIPBinds e) = getFV e+#endif+  +instance HasFV (ApplicativeArg GhcTc) where+#if __GLASGOW_HASKELL__ >= 810+  getFV ApplicativeArgOne { arg_expr = e }     = getFV e+  getFV ApplicativeArgMany {app_stmts = es, final_expr = e} = getFV es `Set.union` getFV e+#else+  getFV (ApplicativeArgOne _ _ e _) = getFV e+  getFV (ApplicativeArgMany _ es e _) = getFV es `Set.union` getFV e+#endif+#if __GLASGOW_HASKELL__ < 900+  getFV (XApplicativeArg e) = getFV e+#endif++instance HasFV (ParStmtBlock GhcTc GhcTc) where+  getFV (ParStmtBlock _ es _ _) = getFV es+#if __GLASGOW_HASKELL__ < 900+  getFV (XParStmtBlock e) = getFV e+#endif+  +instance HasFV a => HasFV (StmtLR GhcTc GhcTc a) where+  getFV (LastStmt _ e _ _) = getFV e+#if __GLASGOW_HASKELL__ >= 900+  getFV (BindStmt _ _ e) = getFV e+#else+  getFV (BindStmt _ _ e _ _) = getFV e+#endif+  getFV (ApplicativeStmt _ args _) = foldMap (getFV . snd) args+  getFV (BodyStmt _ e _ _) = getFV e+  getFV (LetStmt _ bs) = getFV bs+  getFV (ParStmt _ stms e _) = getFV stms `Set.union` getFV e+  getFV TransStmt{} = Set.empty -- TODO+  getFV RecStmt{} = Set.empty -- TODO+#if __GLASGOW_HASKELL__ < 900+  getFV (XStmtLR e) = getFV e+#endif++#if __GLASGOW_HASKELL__ >= 902+instance HasFV (HsRecFields GhcTc (GenLocated SrcSpanAnnA (HsExpr GhcTc))) where+#else+instance HasFV (HsRecordBinds GhcTc) where+#endif+  getFV HsRecFields{rec_flds = fs} = getFV fs++#if __GLASGOW_HASKELL__ >= 904+instance HasFV (HsFieldBind o (GenLocated SrcSpanAnnA (HsExpr GhcTc))) where+#elif __GLASGOW_HASKELL__ >= 902+instance HasFV (HsRecField' o (GenLocated SrcSpanAnnA (HsExpr GhcTc))) where+#else+instance HasFV (HsRecField' o (LHsExpr GhcTc)) where+#endif+  getFV rf  = getFV (getRecFieldRhs rf)++instance HasFV (ArithSeqInfo GhcTc) where+  getFV (From e) = getFV e+  getFV (FromThen e1 e2) = getFV e1 `Set.union` getFV e2+  getFV (FromTo e1 e2) = getFV e1 `Set.union` getFV e2+  getFV (FromThenTo e1 e2 e3) = getFV e1 `Set.union` getFV e2 `Set.union` getFV e3+  +#if __GLASGOW_HASKELL__ >= 904+instance HasFV (HsQuote GhcTc) where+#else+instance HasFV (HsBracket GhcTc) where+#endif+  getFV (ExpBr _ e) = getFV e+  getFV (VarBr _ _ e) = getFV e+  getFV _ = Set.empty++instance HasFV (HsCmd GhcTc) where+  getFV (HsCmdArrApp _ e1 e2 _ _) = getFV e1 `Set.union` getFV e2+  getFV (HsCmdArrForm _ e _ _ cmd) = getFV e `Set.union` getFV cmd+  getFV (HsCmdApp _ e1 e2) = getFV e1 `Set.union` getFV e2+  getFV (HsCmdLam _ l) = getFV l+  getFV (HsCmdCase _ _ mg) = getFV mg+  getFV (HsCmdIf _ _ e1 e2 e3) = getFV e1 `Set.union` getFV e2 `Set.union` getFV e3+  getFV (HsCmdDo _ cmd) = getFV cmd+#if __GLASGOW_HASKELL__ >= 904+  getFV (HsCmdPar _ _ cmd _) = getFV cmd+  getFV (HsCmdLet _ _ bs _ _) = getFV bs+#else+  getFV (HsCmdPar _ cmd) = getFV cmd+  getFV (HsCmdLet _ bs _) = getFV bs+#endif+#if __GLASGOW_HASKELL__ >= 904+  getFV (HsCmdLamCase _ _ mg) = getFV mg+#elif __GLASGOW_HASKELL__ >= 900+  getFV (HsCmdLamCase _ mg) = getFV mg+#else+  getFV (HsCmdWrap _ _ cmd) = getFV cmd+#endif+  getFV (XCmd e) = getFV e+  ++instance HasFV (HsCmdTop GhcTc) where+  getFV (HsCmdTop _ cmd) = getFV cmd+#if __GLASGOW_HASKELL__ < 900+  getFV (XCmdTop e) = getFV e+#endif++instance HasFV (HsExpr GhcTc) where+  getFV (HsVar _ v) = getFV v+  getFV HsUnboundVar {} = Set.empty+  getFV HsOverLabel {} = Set.empty+  getFV HsIPVar {} = Set.empty+  getFV HsOverLit {} = Set.empty+  getFV HsLit {} = Set.empty+  getFV (HsLam _ mg) = getFV mg+  getFV (HsApp _ e1 e2) = getFV e1 `Set.union` getFV e2      +  getFV (OpApp _ e1 e2 e3) = getFV e1 `Set.union` getFV e2 `Set.union` getFV e3+  getFV (NegApp _ e _) = getFV e+  getFV (SectionL _ e1 e2) = getFV e1 `Set.union` getFV e2+  getFV (SectionR _ e1 e2) = getFV e1 `Set.union` getFV e2+  getFV (ExplicitTuple _ es _) = getFV es+  getFV (ExplicitSum _ _ _ e) = getFV e+  getFV (HsCase _ e mg) = getFV e  `Set.union` getFV mg+  getFV (HsMultiIf _ es) = getFV es+  getFV (HsDo _ _ e) = getFV e+#if __GLASGOW_HASKELL__ >= 902+  getFV HsProjection {} = Set.empty+  getFV HsGetField {gf_expr = e} = getFV e+  getFV (ExplicitList _ es) = getFV es+  getFV (RecordUpd {rupd_expr = e, rupd_flds = fs}) =+    getFV e `Set.union` either getFV getFV fs+#else+  getFV (ExplicitList _ _ es) = getFV es+  getFV (RecordUpd {rupd_expr = e, rupd_flds = fs}) = getFV e `Set.union` getFV fs+#endif+  getFV (RecordCon {rcon_flds = fs}) = getFV fs+  getFV (ArithSeq _ _ e) = getFV e+#if __GLASGOW_HASKELL__ >= 906+  getFV HsTypedSplice{} = Set.empty+  getFV HsUntypedSplice{} = Set.empty+#else+  getFV HsSpliceE{} = Set.empty+#endif+  getFV (HsProc _ _ e) = getFV e+  getFV (HsStatic _ e) = getFV e+  getFV (XExpr e) = getFV e+#if __GLASGOW_HASKELL__ >= 904+  getFV (HsPar _ _ e _) = getFV e  +  getFV (HsLamCase _ _ mg) = getFV mg+  getFV (HsLet _ _ bs _ e) = getFV bs `Set.union` getFV e+  getFV HsRecSel {} = Set.empty+  getFV (HsTypedBracket _ e) = getFV e+  getFV (HsUntypedBracket _ e) = getFV e+#else  +  getFV (HsBinTick _ _ _ e) = getFV e+  getFV (HsTick _ _ e) = getFV e+  getFV (HsLet _ bs e) = getFV bs `Set.union` getFV e+  getFV (HsPar _ e) = getFV e+  getFV (HsLamCase _ mg) = getFV mg+  getFV HsConLikeOut {} = Set.empty+  getFV HsRecFld {} = Set.empty+  getFV (HsBracket _ e) = getFV e+  getFV HsRnBracketOut {} = Set.empty+  getFV HsTcBracketOut {} = Set.empty+#endif++#if __GLASGOW_HASKELL__ >= 906+  getFV (HsAppType _ e _ _) = getFV e+  getFV (ExprWithTySig _ e _) = getFV e  +#elif __GLASGOW_HASKELL__ >= 808+  getFV (HsAppType _ e _) = getFV e+  getFV (ExprWithTySig _ e _) = getFV e  +#else+  getFV (ExprWithTySig _ e)   = getFV e+  getFV (HsAppType _ e)   = getFV e+#endif++#if __GLASGOW_HASKELL__ >= 900+  getFV (HsIf _ e1 e2 e3) = getFV e1 `Set.union` getFV e2 `Set.union` getFV e3+  getFV (HsPragE _ _ e) = getFV e+#else+  getFV (HsIf _ _ e1 e2 e3) = getFV e1 `Set.union` getFV e2 `Set.union` getFV e3+  getFV (HsSCC _ _ _ e) = getFV e+  getFV (HsCoreAnn _ _ _ e) = getFV e+  getFV (HsTickPragma _ _ _ _ e) = getFV e+  getFV (HsWrap _ _ e) = getFV e+#endif++#if __GLASGOW_HASKELL__ < 810  +  getFV (HsArrApp _ e1 e2 _ _) = getFV e1 `Set.union` getFV e2+  getFV (HsArrForm _ e _ cmd) = getFV e `Set.union` getFV cmd+  getFV EWildPat {} = Set.empty+  getFV (EAsPat _ e1 e2) = getFV e1 `Set.union` getFV e2+  getFV (EViewPat _ e1 e2) = getFV e1 `Set.union` getFV e2+  getFV (ELazyPat _ e) = getFV e+#endif+++#if __GLASGOW_HASKELL__ >= 900+instance HasFV XXExprGhcTc where+  getFV (WrapExpr e) = getFV e+  getFV (ExpansionExpr (HsExpanded _e1 e2)) = getFV e2+#if __GLASGOW_HASKELL__ >= 904  +  getFV (HsTick _ e) = getFV e+  getFV (HsBinTick _ _ e) = getFV e+  getFV ConLikeTc{} = Set.empty+#endif+++instance HasFV (e GhcTc) => HasFV (HsWrap e) where+  getFV (HsWrap _ e) = getFV e+#elif __GLASGOW_HASKELL__ >= 810+instance HasFV NoExtCon where+  getFV _ = Set.empty+#else+instance HasFV NoExt where+  getFV _ = Set.empty+#endif
+ src/AsyncRattus/Plugin/PrimExpr.hs view
@@ -0,0 +1,110 @@+{-# LANGUAGE OverloadedStrings #-}++module AsyncRattus.Plugin.PrimExpr (+    Prim (..),+    PrimInfo (..),+    function,+    prim,+    isPrimExpr+) where++import Data.Map (Map)+import qualified Data.Map as Map+import GHC.Plugins+import AsyncRattus.Plugin.Utils+import Prelude hiding ((<>))++data Prim = Delay | Adv | Box | Select++-- DelayApp has the following fields: Var = delay f, T1 = value type, T2 = later type (O v a)+-- AdvApp has the following fields: Var = adv f, TypedArg = var and type for arg+data PrimInfo = DelayApp Var Type | AdvApp Var TypedArg | BoxApp Var | SelectApp Var TypedArg TypedArg++type TypedArg = (Var, Type)++data PartialPrimInfo = PartialPrimInfo {+  primPart :: Prim,+  functionPart :: Var,+  args :: [Var],+  typeArgs :: [Type]+}++instance Outputable PartialPrimInfo where+  ppr (PartialPrimInfo Delay f _ typeArgs) = text "PartialPrimInfo { prim = Delay, function = " <> ppr f <> text "args = (not printing since it should be undefined) , typeArgs = " <> ppr typeArgs +  ppr (PartialPrimInfo prim f args typeArgs) = text "PartialPrimInfo { prim = " <> ppr prim <> text ", function = " <> ppr f <> text ", args = " <> ppr args <> text ", typeArgs = " <> ppr typeArgs++instance Outputable Prim where+  ppr Delay = "delay"+  ppr Adv = "adv"+  ppr Select = "select"+  ppr Box = "box"++instance Outputable PrimInfo where+  ppr (DelayApp f _) = text "DelayApp - function " <> ppr f +  ppr (BoxApp f) = text "BoxApp - function " <> ppr f+  ppr (AdvApp f arg) = text "AdvApp - function " <> ppr f <> text " | arg " <> ppr arg+  ppr (SelectApp f arg arg2) = text "SelectApp - function " <> ppr f <> text " | arg " <> ppr arg <> text " | arg2 " <> ppr arg2+  +primMap :: Map FastString Prim+primMap = Map.fromList+  [("delay", Delay),+   ("adv", Adv),+   ("select", Select),+   ("box", Box)+   ]+++isPrim :: Var -> Maybe Prim+isPrim v = case getNameModule v of+    Just (name, mod) | isRattModule mod -> Map.lookup name primMap+    _ -> Nothing++createPartialPrimInfo :: Prim -> Var -> PartialPrimInfo+createPartialPrimInfo prim function =+  PartialPrimInfo {+    primPart = prim,+    functionPart = function,+    args = [],+    typeArgs = []+  }++function :: PrimInfo -> Var+function (DelayApp f _) = f+function (BoxApp f) = f+function (AdvApp f _) = f+function (SelectApp f _ _) = f++prim :: PrimInfo -> Prim+prim (DelayApp {}) = Delay+prim (BoxApp _) = Box+prim (AdvApp {}) = Adv+prim (SelectApp {}) = Select++validatePartialPrimInfo :: PartialPrimInfo -> Maybe PrimInfo+validatePartialPrimInfo (PartialPrimInfo Select f [arg2V, argV] [arg2T, argT]) = Just $ SelectApp f (argV, argT) (arg2V, arg2T)+validatePartialPrimInfo (PartialPrimInfo Delay f [_] [argT]) = Just $ DelayApp f argT+validatePartialPrimInfo (PartialPrimInfo {primPart = Box, functionPart = f}) = Just $ BoxApp f+validatePartialPrimInfo (PartialPrimInfo Adv f [argV] [argT]) = Just $ AdvApp f (argV, argT)+validatePartialPrimInfo _ = Nothing++isPrimExpr :: Expr Var -> Maybe PrimInfo+isPrimExpr expr = isPrimExpr' expr >>= validatePartialPrimInfo++-- App (App (App (App f type) arg) Type2) arg2+isPrimExpr' :: Expr Var -> Maybe PartialPrimInfo+isPrimExpr' (App e (Type t)) = case mPPI of+  Just pPI@(PartialPrimInfo {typeArgs = tArgs}) -> Just pPI {typeArgs = t : tArgs}+  Nothing -> Nothing+  where mPPI = isPrimExpr' e+isPrimExpr' (App e e') =+  case isPrimExpr' e of+    Just partPrimInfo@(PartialPrimInfo { primPart = Delay, args = args}) -> Just partPrimInfo {args = undefined : args}+    Just partPrimInfo@(PartialPrimInfo { args = args}) -> Just partPrimInfo {args = maybe args (:args) (getMaybeVar e')}+    _ -> Nothing+isPrimExpr' (Var v) = case isPrim v of+  Just p ->  Just $ createPartialPrimInfo p v+  Nothing -> Nothing+isPrimExpr' (Tick _ e) = isPrimExpr' e+isPrimExpr' (Lam v e)+  | isTyVar v || (not $ tcIsLiftedTypeKind $ typeKind $ varType v) = isPrimExpr' e+isPrimExpr' _ = Nothing
+ src/AsyncRattus/Plugin/ScopeCheck.hs view
@@ -0,0 +1,963 @@+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ImplicitParams #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE CPP #-}++++-- | This module implements the source plugin that checks the variable+-- scope of of Async Rattus programs.++module AsyncRattus.Plugin.ScopeCheck (checkAll) where++import AsyncRattus.Plugin.Utils+import AsyncRattus.Plugin.Dependency+import AsyncRattus.Plugin.Annotation++import Control.Monad.Trans.State.Strict+import Data.IORef++import Prelude hiding ((<>))++#if __GLASGOW_HASKELL__ >= 902+import GHC.Parser.Annotation+#endif++#if __GLASGOW_HASKELL__ >= 900+import GHC.Plugins+import GHC.Tc.Types+import GHC.Data.Bag+import GHC.Tc.Types.Evidence+#else+import GhcPlugins+import TcRnTypes+import TcEvidence+import Bag+#endif++#if __GLASGOW_HASKELL__ >= 810+import GHC.Hs.Extension+import GHC.Hs.Expr+import GHC.Hs.Pat+import GHC.Hs.Binds+#else +import HsExtension+import HsExpr+import HsPat+import HsBinds+#endif++import Data.Graph+import qualified Data.Set as Set+import qualified Data.Map as Map+import Data.Set (Set)+import Data.Map (Map)+import Data.List+import Data.List.NonEmpty (NonEmpty(..),(<|),nonEmpty)+import System.Exit+import Data.Either+import Data.Maybe++import Data.Data hiding (tyConName)++import Control.Monad++type ErrorMsg = (Severity,SrcSpan,SDoc)+type ErrorMsgsRef = IORef [ErrorMsg]++-- | The current context for scope checking+data Ctxt = Ctxt+  {+    errorMsgs :: ErrorMsgsRef,+    -- | Variables that are in scope now (i.e. occurring in the typing+    -- context but not to the left of a tick)+    current :: LCtxt,+    -- | Variables that are in the typing context, but to the left of a+    -- tick+    earlier :: Either NoTickReason (NonEmpty LCtxt),+    -- | Variables that have fallen out of scope. The map contains the+    -- reason why they have fallen out of scope.+    hidden :: Hidden,+    -- -- | Same as 'hidden' but for recursive variables.+    -- hiddenRec :: Hidden,+    -- | The current location information.+    srcLoc :: SrcSpan,+    -- | If we are in the body of a recursively defined function, this+    -- field contains the variables that are defined recursively+    -- (could be more than one due to mutual recursion or because of a+    -- recursive pattern definition) and the location of the recursive+    -- definition.+    recDef :: Maybe RecDef,+    -- | Type variables with a 'Stable' constraint attached to them.+    stableTypes :: Set Var,+    -- | A mapping from variables to the primitives that they are+    -- defined equal to. For example, a program could contain @let+    -- mydel = delay in mydel 1@, in which case @mydel@ is mapped to+    -- 'Delay'.+    primAlias :: Map Var Prim,+    -- | Allow general recursion.+    allowRecursion :: Bool}++++-- | The starting context for checking a top-level definition. For+-- non-recursive definitions, the argument is @Nothing@. Otherwise, it+-- contains the recursively defined variables along with the location+-- of the recursive definition.+emptyCtxt :: ErrorMsgsRef -> Maybe (Set Var,SrcSpan) -> Bool -> Ctxt+emptyCtxt em mvar allowRec =+  Ctxt { errorMsgs = em,+         current =  Set.empty,+         earlier = Left NoDelay,+         hidden = Map.empty,+         srcLoc = noLocationInfo,+         recDef = mvar,+         primAlias = Map.empty,+         stableTypes = Set.empty,+         allowRecursion = allowRec}++-- | A local context, consisting of a set of variables.+type LCtxt = Set Var++-- | The recursively defined variables + the position where the+-- recursive definition starts+type RecDef = (Set Var, SrcSpan)+++++data StableReason = StableRec SrcSpan | StableBox deriving Show++-- | Indicates, why a variable has fallen out of scope.+data HiddenReason = Stabilize StableReason | FunDef | DelayApp | AdvApp | SelectApp deriving Show++-- | Indicates, why there is no tick+data NoTickReason = NoDelay | TickHidden HiddenReason deriving Show++-- | Hidden context, containing variables that have fallen out of+-- context along with the reason why they have.+type Hidden = Map Var HiddenReason++-- | The 5 primitive Asynchronous Rattus operations.+data Prim = Delay | Adv | Select | Box | Unbox deriving Show++-- | This constraint is used to pass along the context implicitly via+-- an implicit parameter.+type GetCtxt = ?ctxt :: Ctxt+++type CheckM = StateT ([Maybe (Prim, SrcSpan)]) TcM++-- | This type class is implemented for each AST type @a@ for which we+-- can check whether it adheres to the scoping rules of Asynchronous Rattus.+class Scope a where+  -- | Check whether the argument is a scope correct piece of syntax+  -- in the given context.+  check :: GetCtxt => a -> CheckM Bool++-- | This is a variant of 'Scope' for syntax that can also bind+-- variables.+class ScopeBind a where+  -- | 'checkBind' checks whether its argument is scope-correct and in+  -- addition returns the the set of variables bound by it.+  checkBind :: GetCtxt => a -> CheckM (Bool,Set Var)+++-- | set the current context.+setCtxt :: Ctxt -> (GetCtxt => a) -> a +setCtxt c a = let ?ctxt = c in a+++-- | modify the current context.+modifyCtxt :: (Ctxt -> Ctxt) -> (GetCtxt => a) -> (GetCtxt => a)+modifyCtxt f a =+  let newc = f ?ctxt in+  let ?ctxt = newc in a++++#if __GLASGOW_HASKELL__ >= 902+getLocAnn' :: SrcSpanAnn' b -> SrcSpan+getLocAnn' = locA+++updateLoc :: SrcSpanAnn' b -> (GetCtxt => a) -> (GetCtxt => a)+updateLoc src = modifyCtxt (\c -> c {srcLoc = getLocAnn' src})++#else+getLocAnn' :: SrcSpan -> SrcSpan+getLocAnn' s = s+++updateLoc :: SrcSpan -> (GetCtxt => a) -> (GetCtxt => a)+updateLoc src = modifyCtxt (\c -> c {srcLoc = src})+#endif++-- | Check all definitions in the given module. If Scope errors are+-- found, the current execution is halted with 'exitFailure'.+checkAll :: TcGblEnv -> TcM ()+checkAll env = do+  let dep = dependency (tcg_binds env)+  let bindDep = filter (filterBinds (tcg_mod env) (tcg_ann_env env)) dep+  result <- mapM (checkSCC' (tcg_mod env) (tcg_ann_env env)) bindDep+  let (res,msgs) = foldl' (\(b,l) (b',l') -> (b && b', l ++ l')) (True,[]) result+  printAccErrMsgs msgs+  if res then return () else liftIO exitFailure+++printAccErrMsgs :: [ErrorMsg] -> TcM ()+printAccErrMsgs msgs = mapM_ printMsg (sortOn (\(_,l,_)->l) msgs)+  where printMsg (sev,loc,doc) = printMessage sev loc doc+++-- | This function checks whether a given top-level definition (either+-- a single non-recursive definition or a group of mutual recursive+-- definitions) is marked as Asynchronous Rattus code (via an annotation). In a+-- group of mutual recursive definitions, the whole group is+-- considered Asynchronous Rattus code if at least one of its constituents is+-- marked as such.+filterBinds :: Module -> AnnEnv -> SCC (LHsBindLR  GhcTc GhcTc, Set Var) -> Bool+filterBinds mod anEnv scc =+  case scc of+    (AcyclicSCC (_,vs)) -> any checkVar vs+    (CyclicSCC bs) -> any (any checkVar . snd) bs+  where checkVar :: Var -> Bool+        checkVar v =+          let anns = findAnns deserializeWithData anEnv (NamedTarget name) :: [AsyncRattus]+              annsMod = findAnns deserializeWithData anEnv (ModuleTarget mod) :: [AsyncRattus]+              name :: Name+              name = varName v+          in AsyncRattus `elem` anns || (not (NotAsyncRattus `elem` anns)  && AsyncRattus `elem` annsMod)++++instance Scope a => Scope (GenLocated SrcSpan a) where+  check (L l x) =  (\c -> c {srcLoc = l}) `modifyCtxt` check x+++#if __GLASGOW_HASKELL__ >= 902+instance Scope a => Scope (GenLocated (SrcSpanAnn' b) a) where+  check (L l x) =  updateLoc l $ check x+#endif++  +instance Scope a => Scope (Bag a) where+  check bs = fmap and (mapM check (bagToList bs))++instance Scope a => Scope [a] where+  check ls = fmap and (mapM check ls)+++instance Scope (Match GhcTc (GenLocated SrcAnno (HsExpr GhcTc))) where+  check Match{m_pats=ps,m_grhss=rhs} = addVars (getBV ps) `modifyCtxt` check rhs+#if __GLASGOW_HASKELL__ < 900+  check XMatch{} = return True+#endif++instance Scope (Match GhcTc (GenLocated SrcAnno (HsCmd GhcTc))) where+  check Match{m_pats=ps,m_grhss=rhs} = addVars (getBV ps) `modifyCtxt` check rhs+#if __GLASGOW_HASKELL__ < 900+  check XMatch{} = return True+#endif+++instance Scope (MatchGroup GhcTc (GenLocated SrcAnno (HsExpr GhcTc))) where+  check MG {mg_alts = alts} = check alts+#if __GLASGOW_HASKELL__ < 900+  check XMatchGroup {} = return True+#endif+++instance Scope (MatchGroup GhcTc (GenLocated SrcAnno (HsCmd GhcTc))) where+  check MG {mg_alts = alts} = check alts+#if __GLASGOW_HASKELL__ < 900+  check XMatchGroup {} = return True+#endif+++instance Scope a => ScopeBind (StmtLR GhcTc GhcTc a) where+  checkBind (LastStmt _ b _ _) =  ( , Set.empty) <$> check b+#if __GLASGOW_HASKELL__ >= 900+  checkBind (BindStmt _ p b) = do+#else+  checkBind (BindStmt _ p b _ _) = do+#endif+    let vs = getBV p+    let c' = addVars vs ?ctxt+    r <- setCtxt c' (check b)+    return (r,vs)+  checkBind (BodyStmt _ b _ _) = ( , Set.empty) <$> check b+  checkBind (LetStmt _ bs) = checkBind bs+  checkBind ParStmt{} = notSupported "monad comprehensions"+  checkBind TransStmt{} = notSupported "monad comprehensions"+  checkBind ApplicativeStmt{} = notSupported "applicative do notation"+  checkBind RecStmt{} = notSupported "recursive do notation"+#if __GLASGOW_HASKELL__ < 900+  checkBind XStmtLR {} = return (True,Set.empty)+#endif++instance ScopeBind a => ScopeBind [a] where+  checkBind [] = return (True,Set.empty)+  checkBind (x:xs) = do+    (r,vs) <- checkBind x+    (r',vs') <- addVars vs `modifyCtxt` (checkBind xs)+    return (r && r',vs `Set.union` vs')++instance ScopeBind a => ScopeBind (GenLocated SrcSpan a) where+  checkBind (L l x) =  (\c -> c {srcLoc = l}) `modifyCtxt` checkBind x++#if __GLASGOW_HASKELL__ >= 902+instance ScopeBind a => ScopeBind (GenLocated (SrcSpanAnn' b) a) where+  checkBind (L l x) =  updateLoc l $ checkBind x+#endif++instance Scope a => Scope (GRHS GhcTc a) where+  check (GRHS _ gs b) = do+    (r, vs) <- checkBind gs+    r' <- addVars vs `modifyCtxt`  (check b)+    return (r && r')+#if __GLASGOW_HASKELL__ < 900+  check XGRHS{} = return True+#endif++checkRec :: GetCtxt => LHsBindLR GhcTc GhcTc -> CheckM Bool+checkRec b =  liftM2 (&&) (checkPatBind b) (check b)++checkPatBind :: GetCtxt => LHsBindLR GhcTc GhcTc -> CheckM Bool+checkPatBind (L l b) = updateLoc l $ checkPatBind' b++checkPatBind' :: GetCtxt => HsBindLR GhcTc GhcTc -> CheckM Bool+checkPatBind' PatBind{} = do+  printMessage' SevError ("(Mutual) recursive pattern binding definitions are not supported in Asynchronous Rattus")+  return False+#if __GLASGOW_HASKELL__ < 904+checkPatBind' AbsBinds {abs_binds = binds} = +#else+checkPatBind' (XHsBindsLR AbsBinds {abs_binds = binds}) = +#endif+  liftM and (mapM checkPatBind (bagToList binds))++checkPatBind' _ = return True+++-- | Check the scope of a list of (mutual) recursive bindings. The+-- second argument is the set of variables defined by the (mutual)+-- recursive bindings+checkRecursiveBinds :: GetCtxt => [LHsBindLR GhcTc GhcTc] -> Set Var -> CheckM (Bool, Set Var)+checkRecursiveBinds bs vs = do+    res <- fmap and (mapM check' bs)+    return (res, vs)+    where check' b@(L l _) = fc (getLocAnn' l) `modifyCtxt` checkRec b+          fc l c = let+            ctxHid = either (const $ current c) (Set.union (current c) . Set.unions) (earlier c)+            in c {current = Set.empty,+                  earlier = Left (TickHidden $ Stabilize $ StableRec l),+                  hidden =  hidden c `Map.union`+                            (Map.fromSet (const (Stabilize (StableRec l))) ctxHid),+                  recDef = maybe (Just (vs,l)) (\(vs',_) -> Just (Set.union vs' vs,l)) (recDef c)+                   -- TODO fix location info of recDef (needs one location for each var)+                   }          +++#if __GLASGOW_HASKELL__ >= 902+instance ScopeBind (SCC (GenLocated SrcSpanAnnA (HsBindLR  GhcTc GhcTc), Set Var)) where+#else+instance ScopeBind (SCC (LHsBindLR GhcTc GhcTc, Set Var)) where+#endif+  checkBind (AcyclicSCC (b,vs)) = (, vs) <$> check b+  checkBind (CyclicSCC bs) = checkRecursiveBinds (map fst bs) (foldMap snd bs)+  +instance ScopeBind (HsValBindsLR GhcTc GhcTc) where+  checkBind (ValBinds _ bs _) = checkBind (dependency bs)+  +  checkBind (XValBindsLR (NValBinds binds _)) = checkBind binds+++instance ScopeBind (HsBindLR GhcTc GhcTc) where+  checkBind b = (, getBV b) <$> check b+++-- | Compute the set of variables defined by the given Haskell binder.+getAllBV :: GenLocated l (HsBindLR GhcTc GhcTc) -> Set Var+getAllBV (L _ b) = getAllBV' b where+  getAllBV' (FunBind{fun_id = L _ v}) = Set.singleton v+#if __GLASGOW_HASKELL__ < 904+  getAllBV' (AbsBinds {abs_exports = es, abs_binds = bs}) = Set.fromList (map abe_poly es) `Set.union` foldMap getBV bs+  getAllBV' XHsBindsLR{} = Set.empty+#else+  getAllBV' (XHsBindsLR (AbsBinds {abs_exports = es, abs_binds = bs})) = Set.fromList (map abe_poly es) `Set.union` foldMap getBV bs+#endif+  getAllBV' (PatBind {pat_lhs = pat}) = getBV pat+  getAllBV' (VarBind {var_id = v}) = Set.singleton v+  getAllBV' PatSynBind{} = Set.empty+++-- Check nested bindings+#if __GLASGOW_HASKELL__ >= 902+instance ScopeBind (RecFlag, Bag (GenLocated SrcSpanAnnA (HsBindLR GhcTc GhcTc))) where+#else+instance ScopeBind (RecFlag, LHsBinds GhcTc) where+#endif+  checkBind (NonRecursive, bs)  = checkBind $ bagToList bs+  checkBind (Recursive, bs) = checkRecursiveBinds bs' (foldMap getAllBV bs')+    where bs' = bagToList bs+++instance ScopeBind (HsLocalBindsLR GhcTc GhcTc) where+  checkBind (HsValBinds _ bs) = checkBind bs+  checkBind HsIPBinds {} = notSupported "implicit parameters"+  checkBind EmptyLocalBinds{} = return (True,Set.empty)+#if __GLASGOW_HASKELL__ < 900+  checkBind XHsLocalBindsLR{} = return (True,Set.empty)+#endif++#if __GLASGOW_HASKELL__ >= 902+type SrcAnno = SrcSpanAnnA+#else+type SrcAnno = SrcSpan+#endif+  +instance Scope (GRHSs GhcTc (GenLocated SrcAnno (HsExpr GhcTc))) where+  check GRHSs{grhssGRHSs = rhs, grhssLocalBinds = lbinds} = do+    (l,vs) <- checkBind lbinds+    r <- addVars vs `modifyCtxt` (check rhs)+    return (r && l)+#if __GLASGOW_HASKELL__ < 900+  check XGRHSs{} = return True+#endif++instance Scope (GRHSs GhcTc (GenLocated SrcAnno (HsCmd GhcTc))) where+  check GRHSs{grhssGRHSs = rhs, grhssLocalBinds = lbinds} = do+    (l,vs) <- checkBind lbinds+    r <- addVars vs `modifyCtxt` (check rhs)+    return (r && l)+#if __GLASGOW_HASKELL__ < 900+  check XGRHSs{} = return True+#endif++instance Show Var where+  show v = getOccString v+++tickHidden :: HiddenReason -> SDoc+tickHidden FunDef = "a function definition"+tickHidden DelayApp = "a nested application of delay"+tickHidden AdvApp = "an application of adv"+tickHidden SelectApp = "an application of select"+tickHidden (Stabilize StableBox) = "an application of box"+tickHidden (Stabilize (StableRec src)) = "a nested recursive definition (at " <> ppr src <> ")"++isSelect :: GetCtxt => LHsExpr GhcTc -> Bool+isSelect e =+  case isPrimExpr e of+    Just (Select, _) -> True+    _ -> False++instance Scope (HsExpr GhcTc) where+  check (HsVar _ (L _ v))+    | Just p <- isPrim v =+        case p of+          Unbox -> return True+          _ -> printMessageCheck SevError ("Defining an alias for " <> ppr v <> " is not allowed")+    | otherwise = case getScope v of+             Hidden reason -> printMessageCheck SevError reason+             Visible -> return True+             ImplUnboxed -> return True+               -- printMessageCheck SevWarning+               --  (ppr v <> text " is an external temporal function used under delay, which may cause time leaks.")+  check (HsApp _ (L _ (HsApp _ f arg)) arg2) | isSelect f =+    case earlier ?ctxt of+      Right (er :| ers) -> do+        res <- get+        case res of+            Just _ : _ -> printMessageCheck SevError ("only one adv or select may be used in the scope of a delay.")+            Nothing : pre -> do put pre+                                b1 <- mod `modifyCtxt` check arg+                                b2 <- mod `modifyCtxt` check arg2+                                modify (Just (Select, srcLoc ?ctxt) :)+                                return $ b1 && b2+            _ -> error "Asynchronous Rattus: internal error"+        where mod c =  c{earlier = case nonEmpty ers of+                                    Nothing -> Left $ TickHidden SelectApp+                                    Just ers' -> Right ers',+                        current = er,+                        hidden = hidden ?ctxt `Map.union`+                        Map.fromSet (const SelectApp) (current ?ctxt)}+      Left NoDelay -> printMessageCheck SevError "select may only be used in the scope of a delay."+      Left (TickHidden hr) -> printMessageCheck SevError ("select may only be used in the scope of a delay. "+                        <> " There is a delay, but its scope is interrupted by " <> tickHidden hr <> ".")+  check (HsApp _ e1 e2) =+    case isPrimExpr e1 of+    Just (p,_) -> case p of+      Box -> do+        ch <- stabilize StableBox `modifyCtxt` check e2+        return ch+      Unbox -> check e2+      Delay -> do modify (Nothing :)+                  b <- (\c -> c{current = Set.empty,+                           earlier = case earlier c of+                                      Left _ -> Right (current c :| [])+                                      Right cs -> Right (current c <| cs)})+                     `modifyCtxt` check e2+                  res <- get+                  case res of+                    Nothing : _ -> printMessageCheck SevError "No adv or select found in the scope of this occurrence of delay"+                    _ : pre -> put pre >> return b+                    _ -> error "Asynchronous Rattus: internal error"+      Adv -> case earlier ?ctxt of+        Right (er :| ers) -> do+          res <- get+          case res of+            Just _ : _ -> printMessageCheck SevError ("only one adv or select may be used in the scope of a delay.")+            Nothing : pre -> do put pre+                                b <- mod `modifyCtxt` check e2+                                modify (Just (Adv,srcLoc ?ctxt) :)+                                return b+            _ -> error "Asynchronous Rattus: internal error"+          where mod c =  c{earlier = case nonEmpty ers of+                                       Nothing -> Left $ TickHidden AdvApp+                                       Just ers' -> Right ers',+                           current = er,+                           hidden = hidden ?ctxt `Map.union`+                            Map.fromSet (const AdvApp) (current ?ctxt)}+        Left NoDelay -> printMessageCheck SevError ("adv may only be used in the scope of a delay.")+        Left (TickHidden hr) -> printMessageCheck SevError ("adv may only be used in the scope of a delay. "+                            <> " There is a delay, but its scope is interrupted by " <> tickHidden hr <> ".")+      Select -> printMessageCheck SevError ("select must be fully applied")+    _ -> liftM2 (&&) (check e1)  (check e2)+  check HsUnboundVar{}  = return True+#if __GLASGOW_HASKELL__ >= 904+  check (HsPar _ _ e _) = check e+  check (HsLamCase _ _ mg) = check mg+  check HsRecSel{} = return True+  check HsTypedBracket{} = notSupported "MetaHaskell"+  check HsUntypedBracket{} = notSupported "MetaHaskell"+#else+  check HsConLikeOut{} = return True+  check HsRecFld{} = return True+  check (HsPar _ e) = check e+  check (HsLamCase _ mg) = check mg+  check HsBracket{} = notSupported "MetaHaskell"+  check (HsTick _ _ e) = check e+  check (HsBinTick _ _ _ e) = check e+  check HsRnBracketOut{} = notSupported "MetaHaskell"+  check HsTcBracketOut{} = notSupported "MetaHaskell"+#endif+#if __GLASGOW_HASKELL__ >= 904+  check (HsLet _ _ bs _ e) = do+#else+  check (HsLet _ bs e) = do+#endif+    (l,vs) <- checkBind bs+    r <- addVars vs `modifyCtxt` (check e)+    return (r && l)+         +  check HsOverLabel{} = return True+  check HsIPVar{} = notSupported "implicit parameters"+  check HsOverLit{} = return True  +  check HsLit{} = return True+  check (OpApp _ e1 e2 e3) = and <$> mapM check [e1,e2,e3]+  check (HsLam _ mg) = check mg+  check (HsCase _ e1 e2) = (&&) <$> check e1 <*> check e2+  check (SectionL _ e1 e2) = (&&) <$> check e1 <*> check e2+  check (SectionR _ e1 e2) = (&&) <$> check e1 <*> check e2+  check (ExplicitTuple _ e _) = check e+  check (NegApp _ e _) = check e+  check (ExplicitSum _ _ _ e) = check e+  check (HsMultiIf _ e) = check e+#if __GLASGOW_HASKELL__ >= 902+  check (ExplicitList _ e) = check e+  check RecordUpd { rupd_expr = e, rupd_flds = fs} = (&&) <$> check e <*> either check check fs+  check HsProjection {} = return True+  check HsGetField {gf_expr = e} = check e+#else+  check (ExplicitList _ _ e) = check e+  check RecordUpd { rupd_expr = e, rupd_flds = fs} = (&&) <$> check e <*> check fs+#endif+  check RecordCon { rcon_flds = f} = check f+  check (ArithSeq _ _ e) = check e+#if __GLASGOW_HASKELL__ >= 906+  check HsTypedSplice{} = notSupported "Template Haskell"+  check HsUntypedSplice{} = notSupported "Template Haskell"+#else+  check HsSpliceE{} = notSupported "Template Haskell"+#endif+  check (HsProc _ _ e) = check e+  check (HsStatic _ e) = check e+  check (HsDo _ _ e) = fst <$> checkBind e+  check (XExpr e) = check e+#if __GLASGOW_HASKELL__ >= 906+  check (HsAppType _ e _ _) = check e+  check (ExprWithTySig _ e _) = check e+#elif __GLASGOW_HASKELL__ >= 808+  check (HsAppType _ e _) = check e+  check (ExprWithTySig _ e _) = check e+#else+  check (HsAppType _ e)  = check e+  check (ExprWithTySig _ e) = check e+#endif++#if __GLASGOW_HASKELL__ >= 900+  check (HsPragE _ _ e) = check e+  check (HsIf _ e1 e2 e3) = and <$> mapM check [e1,e2,e3]+#else+  check (HsSCC _ _ _ e) = check e+  check (HsCoreAnn _ _ _ e) = check e+  check (HsTickPragma _ _ _ _ e) = check e+  check (HsWrap _ _ e) = check e+  check (HsIf _ _ e1 e2 e3) = and <$> mapM check [e1,e2,e3]+#endif+#if __GLASGOW_HASKELL__ < 810+  check HsArrApp{} = impossible+  check HsArrForm{} = impossible+  check EWildPat{} = impossible+  check EAsPat{} = impossible+  check EViewPat{} = impossible+  check ELazyPat{} = impossible++impossible :: GetCtxt => TcM Bool+impossible = printMessageCheck SevError "This syntax should never occur after typechecking"+#endif++#if __GLASGOW_HASKELL__ >= 900+instance Scope XXExprGhcTc where+  check (WrapExpr (HsWrap _ e)) = check e+  check (ExpansionExpr (HsExpanded _ e)) = check e+#if __GLASGOW_HASKELL__ >= 904+  check ConLikeTc{} = return True+  check (HsTick _ e) = check e+  check (HsBinTick _ _ e) = check e+#endif+#elif __GLASGOW_HASKELL__ >= 810+instance Scope NoExtCon where+  check _ = return True+#else+instance Scope NoExt where+  check _ = return True+#endif++instance Scope (HsCmdTop GhcTc) where+  check (HsCmdTop _ e) = check e+#if __GLASGOW_HASKELL__ < 900+  check XCmdTop{} = return True+#endif+  +instance Scope (HsCmd GhcTc) where+  check (HsCmdArrApp _ e1 e2 _ _) = (&&) <$> check e1 <*> check e2+  check (HsCmdDo _ e) = fst <$> checkBind e+  check (HsCmdArrForm _ e1 _ _ e2) = (&&) <$> check e1 <*> check e2+  check (HsCmdApp _ e1 e2) = (&&) <$> check e1 <*> check e2+  check (HsCmdLam _ e) = check e+#if __GLASGOW_HASKELL__ >= 904+  check (HsCmdPar _ _ e _) = check e+  check (HsCmdLamCase _ _ e) = check e  +  check (HsCmdLet _ _ bs _ e) = do+#else+  check (HsCmdPar _ e) = check e+#if __GLASGOW_HASKELL__ >= 900+  check (HsCmdLamCase _ e) = check e+#endif+  check (HsCmdLet _ bs e) = do+#endif+    (l,vs) <- checkBind bs+    r <- addVars vs `modifyCtxt` (check e)+    return (r && l)++  check (HsCmdCase _ e1 e2) = (&&) <$> check e1 <*> check e2+  check (HsCmdIf _ _ e1 e2 e3) = (&&) <$> ((&&) <$> check e1 <*> check e2) <*> check e3+#if __GLASGOW_HASKELL__ >= 900+  check (XCmd (HsWrap _ e)) = check e+#else+  check (HsCmdWrap _ _ e) = check e+  check XCmd{} = return True+#endif+++instance Scope (ArithSeqInfo GhcTc) where+  check (From e) = check e+  check (FromThen e1 e2) = (&&) <$> check e1 <*> check e2+  check (FromTo e1 e2) = (&&) <$> check e1 <*> check e2+  check (FromThenTo e1 e2 e3) = (&&) <$> ((&&) <$> check e1 <*> check e2) <*> check e3++instance Scope a => Scope (HsRecFields GhcTc a) where+  check HsRecFields {rec_flds = fs} = check fs++++#if __GLASGOW_HASKELL__ >= 904+instance Scope b => Scope (HsFieldBind a b) where+  check HsFieldBind{hfbRHS = a} = check a+#else+instance Scope b => Scope (HsRecField' a b) where+  check HsRecField{hsRecFieldArg = a} = check a+#endif++instance Scope (HsTupArg GhcTc) where+  check (Present _ e) = check e+  check Missing{} = return True+#if __GLASGOW_HASKELL__ < 900+  check XTupArg{} = return True+#endif++instance Scope (HsBindLR GhcTc GhcTc) where+#if __GLASGOW_HASKELL__ >= 904+  check (XHsBindsLR AbsBinds {abs_binds = binds, abs_ev_vars  = ev})+#else+  check AbsBinds {abs_binds = binds, abs_ev_vars  = ev}+#endif+    = mod `modifyCtxt` check binds+      where mod c = c { stableTypes= stableTypes c `Set.union`+                        Set.fromList (mapMaybe (isStableConstr . varType) ev)}+  check FunBind{fun_matches= matches, fun_id = L _ v,+#if __GLASGOW_HASKELL__ >= 900+                fun_ext = wrapper} =+#else+                fun_co_fn = wrapper} =+#endif+      mod `modifyCtxt` check matches+    where mod c = c { stableTypes= stableTypes c `Set.union`+                      Set.fromList (stableConstrFromWrapper' wrapper)  `Set.union`+                      Set.fromList (extractStableConstr (varType v))}+  check PatBind{pat_lhs = lhs, pat_rhs=rhs} = addVars (getBV lhs) `modifyCtxt` check rhs+  check VarBind{var_rhs = rhs} = check rhs+  check PatSynBind {} = return True -- pattern synonyms are not supported+#if __GLASGOW_HASKELL__ < 900+  check XHsBindsLR {} = return True+#endif+++-- | Checks whether the given type is a type constraint of the form+-- @Stable a@ for some type variable @a@. In that case it returns the+-- type variable @a@.+isStableConstr :: Type -> Maybe TyVar+isStableConstr t = +  case splitTyConApp_maybe t of+    Just (con,[args]) ->+      case getNameModule con of+        Just (name, mod) ->+          if isRattModule mod && name == "Stable"+          then (getTyVar_maybe args)+          else Nothing+        _ -> Nothing                           +    _ ->  Nothing++++#if __GLASGOW_HASKELL__ >= 906+stableConstrFromWrapper' :: (HsWrapper , a) -> [TyVar]+stableConstrFromWrapper' (x , _) = stableConstrFromWrapper x+#else+stableConstrFromWrapper' :: HsWrapper -> [TyVar]+stableConstrFromWrapper' = stableConstrFromWrapper+#endif++stableConstrFromWrapper :: HsWrapper -> [TyVar]+stableConstrFromWrapper (WpCompose v w) = stableConstrFromWrapper v ++ stableConstrFromWrapper w+stableConstrFromWrapper (WpEvLam v) = maybeToList $ isStableConstr (varType v)+stableConstrFromWrapper _ = []+++-- | Given a type @(C1, ... Cn) => t@, this function returns the list+-- of type variables @[a1,...,am]@ for which there is a constraint+-- @Stable ai@ among @C1, ... Cn@.+extractStableConstr :: Type -> [TyVar]+#if __GLASGOW_HASKELL__ >= 900+extractStableConstr  = mapMaybe isStableConstr . map irrelevantMult . fst . splitFunTys . snd . splitForAllTys'+#else+extractStableConstr  = mapMaybe isStableConstr . fst . splitFunTys . snd . splitForAllTys'+#endif+++getSCCLoc :: SCC (LHsBindLR  GhcTc GhcTc, Set Var) -> SrcSpan+getSCCLoc (AcyclicSCC (L l _ ,_)) = getLocAnn' l+getSCCLoc (CyclicSCC ((L l _,_ ) : _)) = getLocAnn' l+getSCCLoc _ = noLocationInfo++checkSCC' ::  Module -> AnnEnv -> SCC (LHsBindLR  GhcTc GhcTc, Set Var) -> TcM (Bool, [ErrorMsg])+checkSCC' mod anEnv scc = do+  err <- liftIO (newIORef [])+  let allowRec = AllowRecursion `Set.member` getAnn mod anEnv scc+  res <- checkSCC allowRec err scc+  msgs <- liftIO (readIORef err)+  let anns = getAnn mod anEnv scc+  if ExpectWarning `Set.member` anns +    then if ExpectError `Set.member` anns+         then return (False,[(SevError, getSCCLoc scc, "Annotation to expect both warning and error is not allowed.")])+         else if any (\(s,_,_) -> case s of SevWarning -> True; _ -> False) msgs+              then return (res, filter (\(s,_,_) -> case s of SevWarning -> False; _ -> True) msgs)+              else return (False,[(SevError, getSCCLoc scc, "Warning was expected, but typechecking produced no warning.")])+    else if ExpectError `Set.member` anns+         then if res+              then return (False,[(SevError, getSCCLoc scc, "Error was expected, but typechecking produced no error.")])+              else return (True,[])+         else return (res, msgs)+getAnn :: forall a . (Data a, Ord a) => Module -> AnnEnv -> SCC (LHsBindLR  GhcTc GhcTc, Set Var) -> Set a+getAnn mod anEnv scc =+  case scc of+    (AcyclicSCC (_,vs)) -> Set.unions $ Set.map checkVar vs+    (CyclicSCC bs) -> Set.unions $ map (Set.unions . Set.map checkVar . snd) bs+  where checkVar :: Var -> Set a+        checkVar v =+          let anns = findAnns deserializeWithData anEnv (NamedTarget name) :: [a]+              annsMod = findAnns deserializeWithData anEnv (ModuleTarget mod) :: [a]+              name :: Name+              name = varName v+          in Set.fromList anns `Set.union` Set.fromList annsMod++++-- | Checks a top-level definition group, which is either a single+-- non-recursive definition or a group of (mutual) recursive+-- definitions.++checkSCC :: Bool -> ErrorMsgsRef -> SCC (LHsBindLR  GhcTc GhcTc, Set Var) -> TcM Bool+checkSCC allowRec errm (AcyclicSCC (b,_)) = setCtxt (emptyCtxt errm Nothing allowRec) (evalStateT (check b) [])++checkSCC allowRec errm (CyclicSCC bs) = (fmap and (mapM check' bs'))+  where bs' = map fst bs+        vs = foldMap snd bs+        check' b@(L l _) = setCtxt (emptyCtxt errm (Just (vs,getLocAnn' l)) allowRec) (evalStateT (checkRec b) [])++-- | Stabilizes the given context, i.e. remove all non-stable types+-- and any tick. This is performed on checking 'box', and+-- guarded recursive definitions. To provide better error messages a+-- reason has to be given as well.+stabilize :: StableReason -> Ctxt -> Ctxt+stabilize sr c = c+  {current = Set.empty,+   earlier = Left $ TickHidden hr,+   hidden = hidden c `Map.union` Map.fromSet (const hr) ctxHid}+  where ctxHid = either (const $ current c) (foldl' Set.union (current c)) (earlier c)+        hr = Stabilize sr++data VarScope = Hidden SDoc | Visible | ImplUnboxed+++-- | This function checks whether the given variable is in scope.+getScope  :: GetCtxt => Var -> VarScope+getScope v =+  case ?ctxt of+    Ctxt{recDef = Just (vs,_), earlier = e, allowRecursion = allowRec} | v `Set.member` vs ->+     if allowRec then Visible else+        case e of+          Right _ -> Visible+          Left NoDelay -> Hidden ("The (mutually) recursive call to " <> ppr v <> " must occur in the scope of a delay")+          Left (TickHidden hr) -> Hidden ("The (mutually) recursive call to " <> ppr v <> " must occur in the scope of a delay. "+                            <> "There is a delay, but its scope is interrupted by " <> tickHidden hr <> ".")+    _ ->  case Map.lookup v (hidden ?ctxt) of+            Just (Stabilize (StableRec rv)) ->+              if (isStable (stableTypes ?ctxt) (varType v)) || allowRecursion ?ctxt then Visible+              else Hidden ("Variable " <> ppr v <> " is no longer in scope:" $$+                       "It appears in a local recursive definition (at " <> ppr rv <> ")"+                       $$ "and is of type " <> ppr (varType v) <> ", which is not stable.")+            Just (Stabilize StableBox) ->+              if (isStable (stableTypes ?ctxt) (varType v)) then Visible+              else Hidden ("Variable " <> ppr v <> " is no longer in scope:" $$+                       "It occurs under " <> keyword "box" $$ "and is of type " <> ppr (varType v) <> ", which is not stable.")+            Just AdvApp -> Hidden ("Variable " <> ppr v <> " is no longer in scope: It occurs under adv.")+            Just SelectApp -> Hidden ("Variable " <> ppr v <> " is no longer in scope: It occurs under select.")+            Just DelayApp -> Hidden ("Variable " <> ppr v <> " is no longer in scope due to repeated application of delay")+            Just FunDef -> if (isStable (stableTypes ?ctxt) (varType v)) then Visible+              else Hidden ("Variable " <> ppr v <> " is no longer in scope: It occurs in a function that is defined under a delay, is a of a non-stable type " <> ppr (varType v) <> ", and is bound outside delay")+            Nothing+              | either (const False) (any (Set.member v)) (earlier ?ctxt) ->+                if isStable (stableTypes ?ctxt) (varType v) then Visible+                else Hidden ("Variable " <> ppr v <> " is no longer in scope:" $$+                         "It occurs under delay" $$ "and is of type " <> ppr (varType v) <> ", which is not stable.")+              | Set.member v (current ?ctxt) -> Visible+              | isTemporal (varType v) && isRight (earlier ?ctxt) && userFunction v+                -> ImplUnboxed+              | otherwise -> Visible++-- | A map from the syntax of a primitive of Asynchronous Rattus to 'Prim'.+primMap :: Map FastString Prim+primMap = Map.fromList+  [("Delay", Delay),+   ("delay", Delay),+   ("adv", Adv),+   ("select", Select),+   ("box", Box),+   ("unbox", Unbox)]+++-- | Checks whether a given variable is in fact an Asynchronous Rattus primitive.+isPrim :: GetCtxt => Var -> Maybe Prim+isPrim v+  | Just p <- Map.lookup v (primAlias ?ctxt) = Just p+  | otherwise = do+  (name,mod) <- getNameModule v+  if isRattModule mod then Map.lookup name primMap+  else Nothing+++-- | Checks whether a given expression is in fact a Asynchronous Rattus primitive.+isPrimExpr :: GetCtxt => LHsExpr GhcTc -> Maybe (Prim,Var)+isPrimExpr (L _ e) = isPrimExpr' e where+  isPrimExpr' :: GetCtxt => HsExpr GhcTc -> Maybe (Prim,Var)+  isPrimExpr' (HsVar _ (L _ v)) = fmap (,v) (isPrim v)+#if __GLASGOW_HASKELL__ >= 906+  isPrimExpr' (HsAppType _ e _ _) = isPrimExpr e+#elif __GLASGOW_HASKELL__ >= 808+  isPrimExpr' (HsAppType _ e _) = isPrimExpr e+#else+  isPrimExpr' (HsAppType _ e)   = isPrimExpr e+#endif++#if __GLASGOW_HASKELL__ < 900+  isPrimExpr' (HsSCC _ _ _ e) = isPrimExpr e+  isPrimExpr' (HsCoreAnn _ _ _ e) = isPrimExpr e+  isPrimExpr' (HsTickPragma _ _ _ _ e) = isPrimExpr e+  isPrimExpr' (HsWrap _ _ e) = isPrimExpr' e+#else+  isPrimExpr' (XExpr (WrapExpr (HsWrap _ e))) = isPrimExpr' e+  isPrimExpr' (XExpr (ExpansionExpr (HsExpanded _ e))) = isPrimExpr' e+  isPrimExpr' (HsPragE _ _ e) = isPrimExpr e+#endif+#if __GLASGOW_HASKELL__ < 904+  isPrimExpr' (HsTick _ _ e) = isPrimExpr e+  isPrimExpr' (HsBinTick _ _ _ e) = isPrimExpr e+  isPrimExpr' (HsPar _ e) = isPrimExpr e+#else+  isPrimExpr' (XExpr (HsTick _ e)) = isPrimExpr e+  isPrimExpr' (XExpr (HsBinTick _ _ e)) = isPrimExpr e+  isPrimExpr' (HsPar _ _ e _) = isPrimExpr e+#endif++  isPrimExpr' _ = Nothing+++-- | This type class provides default implementations for 'check' and+-- 'checkBind' for Haskell syntax that is not supported. These default+-- implementations simply print an error message.+class NotSupported a where+  notSupported :: GetCtxt => SDoc -> CheckM a++instance NotSupported Bool where+  notSupported doc = printMessageCheck SevError ("Asynchronous Rattus does not support " <> doc)++instance NotSupported (Bool,Set Var) where+  notSupported doc = (,Set.empty) <$> notSupported doc+++-- | Add variables to the current context.+addVars :: Set Var -> Ctxt -> Ctxt+addVars vs c = c{current = vs `Set.union` current c }++-- | Print a message with the current location.+printMessage' :: GetCtxt => Severity -> SDoc ->  CheckM ()+printMessage' sev doc =+  liftIO (modifyIORef (errorMsgs ?ctxt) ((sev ,srcLoc ?ctxt, doc) :))++-- | Print a message with the current location. Returns 'False', if+-- the severity is 'SevError' and otherwise 'True.+printMessageCheck :: GetCtxt =>  Severity -> SDoc -> CheckM Bool+printMessageCheck sev doc = printMessage' sev doc >>+  case sev of+    SevError -> return False+    _ -> return True
+ src/AsyncRattus/Plugin/SingleTick.hs view
@@ -0,0 +1,226 @@+-- | This module implements the translation from the multi-tick+-- calculus to the single tick calculus.++{-# LANGUAGE CPP #-}++module AsyncRattus.Plugin.SingleTick+  (toSingleTick) where++#if __GLASGOW_HASKELL__ >= 900+import GHC.Plugins+#else+import GhcPlugins+#endif++  +import AsyncRattus.Plugin.Utils+import Prelude hiding ((<>))+import Control.Monad.Trans.Writer.Strict+import Control.Monad.Trans.Class+import Data.List++-- | Transform the given expression from the multi-tick calculus into+-- the single tick calculus form.+toSingleTick :: CoreExpr -> CoreM CoreExpr+toSingleTick (Let (Rec bs) e) = do+  e' <- toSingleTick e+  bs' <- mapM (mapM toSingleTick) bs+  return (Let (Rec bs') e')+toSingleTick (Let (NonRec b e1) e2) = do+  e1' <- toSingleTick e1+  e2' <- toSingleTick e2+  return (Let (NonRec b e1') e2')+toSingleTick (Case e b ty alts) = do+  e' <- toSingleTick e+  alts' <- mapM ((\ (c,bs,f) -> fmap (\ x -> mkAlt c bs x) (toSingleTick f)) . getAlt) alts+  return (Case e' b ty alts')+toSingleTick (Cast e c) = do+  e' <- toSingleTick e+  return (Cast e' c)+toSingleTick (Tick t e) = do+  e' <- toSingleTick e+  return (Tick t e')+toSingleTick (Lam x e) = do+  (e', advs) <- runWriterT (extractAdv' e)+  advs' <- mapM (\ (x,a,b) -> fmap (\b' -> (x,a,b')) (toSingleTick b)) advs+  return (foldLets' advs' (Lam x e'))+toSingleTick (App e1 e2)+  | isDelayApp e1 = do+      (e2', advs) <- runWriterT (extractAdv e2)+      advs' <- mapM (mapM toSingleTick) advs+      return (foldLets advs' (App e1 e2'))+  | otherwise = do+      e1' <- toSingleTick e1+      e2' <- toSingleTick e2+      return (App e1' e2')++toSingleTick e@Type{} = return e+toSingleTick e@Var{} = return e+toSingleTick e@Lit{} = return e+toSingleTick e@Coercion{} = return e++foldLets :: [(Id,CoreExpr)] -> CoreExpr -> CoreExpr+foldLets ls e = foldl' (\e' (x,b) -> Let (NonRec x b) e') e ls++foldLets' :: [(Id,CoreExpr,CoreExpr)] -> CoreExpr -> CoreExpr+foldLets' ls e = foldl' (\e' (x,a,b) -> Let (NonRec x (App a b)) e') e ls++extractAdvApp :: CoreExpr -> CoreExpr -> WriterT [(Id,CoreExpr)] CoreM CoreExpr+extractAdvApp e1 e2+  | isVar e2 = return (App e1 e2)+  | otherwise = do+  x <- lift (mkSysLocalFromExpr (fsLit "adv") e2)+  tell [(x,e2)]+  return (App e1 (Var x))++-- removes casts and ticks from a tree+filterTree :: CoreExpr -> CoreExpr+filterTree (Cast e _) = filterTree e+filterTree (Tick _ e) = filterTree e+filterTree e = e+++extractSelectApp :: CoreExpr -> CoreExpr -> WriterT [(Id,CoreExpr)] CoreM CoreExpr+extractSelectApp e1 e2+  | isVar e' && isVar e2 = return (App e1 e2)+  | isVar e2 = do+    x <- lift (mkSysLocalFromExpr (fsLit "selectFreshVar") e')+    tell [(x, e')]+    return (App (App e (Var x)) e2)+  | isVar e' = do+    x <- lift (mkSysLocalFromExpr (fsLit "selectFreshVar") e2)+    tell [(x, e2)]+    return (App e1 (Var x))+  | otherwise = do+    x <- lift (mkSysLocalFromExpr (fsLit "selectFreshVar") e')+    y <- lift (mkSysLocalFromExpr (fsLit "selectFreshVar") e2)+    tell [(x, e')]+    tell [(y, e2)]+    return (App (App e (Var x)) (Var y))+  where (App e e') = filterTree e1+++-- This is used to pull adv out of delayed terms. The writer monad+-- returns mappings from fresh variables to terms that occur as+-- argument of adv.+-- +-- That is, occurrences of @adv t@ are replaced with @adv x@ (for some+-- fresh variable @x@) and the pair @(x,t)@ is returned in the+-- writer monad.+extractAdv :: CoreExpr -> WriterT [(Id,CoreExpr)] CoreM CoreExpr+extractAdv (App expr@(App e _) e2) | isSelectApp e = extractSelectApp expr e2+extractAdv e@(App e1 e2)+  | isAdvApp e1 = extractAdvApp e1 e2+  | isSelectApp e1 = extractSelectApp e1 e2+  | isDelayApp e1 = do+      (e2', advs) <- lift $ runWriterT (extractAdv e2)+      advs' <- mapM (mapM extractAdv) advs+      return (foldLets advs' (App e1 e2'))+  | isBoxApp e1 = lift $ toSingleTick e+  | otherwise = do+      e1' <- extractAdv e1+      e2' <- extractAdv e2+      return (App e1' e2')+extractAdv (Lam x e) = do+  (e', advs) <- lift $ runWriterT (extractAdv' e)+  advs' <- mapM (\ (x,a,b) -> fmap (\b' -> (x,b')) (extractAdvApp a b)) advs+  return (foldLets advs' (Lam x e'))+extractAdv (Case e b ty alts) = do+  e' <- extractAdv e+  alts' <- mapM ((\ (c,bs,f) -> fmap (\ x -> mkAlt c bs x) (extractAdv f)) . getAlt) alts+  return (Case e' b ty alts')+extractAdv (Cast e c) = do+  e' <- extractAdv e+  return (Cast e' c)+extractAdv (Tick t e) = do+  e' <- extractAdv e+  return (Tick t e')+extractAdv e@(Let Rec{} _) = lift $ toSingleTick e+extractAdv (Let (NonRec b e1) e2) = do+  e1' <- extractAdv e1+  e2' <- extractAdv e2+  return (Let (NonRec b e1') e2')+extractAdv e@Type{} = return e+extractAdv e@Var{} = return e+extractAdv e@Lit{} = return e+extractAdv e@Coercion{} = return e++-- This is used to pull adv out of lambdas. The writer monad returns+-- mappings from fresh variables to occurrences of adv and the term it+-- is applied to.+-- +-- That is occurrences of @adv t@ are replaced with a fresh variable+-- @x@ and the triple @(x,adv,t)@ is returned in the writer monad.+-- For select a b, the triple @(x, select a, b) is returned in the writer monad.+extractAdv' :: CoreExpr -> WriterT [(Id,CoreExpr,CoreExpr)] CoreM CoreExpr+extractAdv' e@(App e1 e2)+  | isAdvApp e1 = do+       x <- lift (mkSysLocalFromExpr (fsLit "adv") e)+       tell [(x,e1,e2)]+       return (Var x)+  | isSelectApp e1 = do+      x <- lift (mkSysLocalFromExpr (fsLit "select") e)+      tell [(x,e1,e2)]+      return (Var x)+  | isDelayApp e1 = do+      (e2', advs) <- lift $ runWriterT (extractAdv e2)+      advs' <- mapM (mapM extractAdv') advs+      return (foldLets advs' (App e1 e2'))+  | isBoxApp e1 = lift $ toSingleTick e+  | otherwise = do+      e1' <- extractAdv' e1+      e2' <- extractAdv' e2+      return (App e1' e2')+extractAdv' (Lam x e) = do+  e' <- extractAdv' e+  return (Lam x e')+extractAdv' (Case e b ty alts) = do+  e' <- extractAdv' e+  alts' <- mapM ((\ (c,bs,f) -> fmap (\ x -> mkAlt c bs x) (extractAdv' f)) . getAlt) alts+  return (Case e' b ty alts')+extractAdv' (Cast e c) = do+  e' <- extractAdv' e+  return (Cast e' c)+extractAdv' (Tick t e) = do+  e' <- extractAdv' e+  return (Tick t e')+extractAdv' e@(Let Rec{} _) = lift $ toSingleTick e+extractAdv' (Let (NonRec b e1) e2) = do+  e1' <- extractAdv' e1+  e2' <- extractAdv' e2+  return (Let (NonRec b e1') e2')+extractAdv' e@Type{} = return e+extractAdv' e@Var{} = return e+extractAdv' e@Lit{} = return e+extractAdv' e@Coercion{} = return e++++isDelayApp :: CoreExpr -> Bool+isDelayApp = isPrimApp (== "delay")++isBoxApp :: CoreExpr -> Bool+isBoxApp = isPrimApp (\occ -> occ == "Box" || occ == "box")++isAdvApp :: CoreExpr -> Bool+isAdvApp = isPrimApp (== "adv")++isSelectApp :: CoreExpr -> Bool+isSelectApp = isPrimApp (== "select")++isPrimApp :: (String -> Bool) -> CoreExpr -> Bool+isPrimApp p (App e e')+  | isType e' || not  (tcIsLiftedTypeKind(typeKind (exprType e'))) = isPrimApp p e+  | otherwise = False+isPrimApp p (Cast e _) = isPrimApp p e+isPrimApp p (Tick _ e) = isPrimApp p e+isPrimApp p (Var v) = isPrimVar p v+isPrimApp _ _ = False++isPrimVar :: (String -> Bool) -> Var -> Bool+isPrimVar p v = maybe False id $ do+  let name = varName v+  mod <- nameModule_maybe name+  let occ = getOccString name+  return (p occ+          && moduleNameString (moduleName mod) == "AsyncRattus.InternalPrimitives")
+ src/AsyncRattus/Plugin/StableSolver.hs view
@@ -0,0 +1,93 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-}+++-- | This module implements a constraint solver plugin for the+-- 'Stable' type class.++module AsyncRattus.Plugin.StableSolver (tcStable) where++import AsyncRattus.Plugin.Utils+    ( getNameModule, isRattModule, isStable )++import Prelude hiding ((<>))++#if __GLASGOW_HASKELL__ >= 900+import GHC.Plugins+  (Type, Var, CommandLineOption,tyConSingleDataCon,+   mkCoreConApps,getTyVar_maybe)+import GHC.Core+import GHC.Tc.Types.Evidence+import GHC.Core.Class+import GHC.Tc.Types+#else+import GhcPlugins+  (Type, Var, CommandLineOption,tyConSingleDataCon,+   mkCoreConApps,getTyVar_maybe)+import CoreSyn+import TcEvidence+import Class+import TcRnTypes+#endif++#if __GLASGOW_HASKELL__ >= 900+import GHC.Tc.Types.Constraint+#elif __GLASGOW_HASKELL__ >= 810+import Constraint+#endif++import Data.Set (Set)+import qualified Data.Set as Set+#if __GLASGOW_HASKELL__ >= 904+import GHC.Types.Unique.FM+#endif++++-- | Constraint solver plugin for the 'Stable' type class.+tcStable :: [CommandLineOption] -> Maybe TcPlugin+tcStable _ = Just $ TcPlugin+  { tcPluginInit = return ()+  , tcPluginSolve = \ () -> stableSolver+  , tcPluginStop = \ () -> return ()+#if __GLASGOW_HASKELL__ >= 904+  , tcPluginRewrite = \ () -> emptyUFM+#endif+  }+++wrap :: Class -> Type -> EvTerm+wrap cls ty = EvExpr appDc+  where+    tyCon = classTyCon cls+    dc = tyConSingleDataCon tyCon+    appDc = mkCoreConApps dc [Type ty]++solveStable :: Set Var -> (Type, (Ct,Class)) -> Maybe (EvTerm, Ct)+solveStable c (ty,(ct,cl))+  | isStable c ty = Just (wrap cl ty, ct)+  | otherwise = Nothing++#if __GLASGOW_HASKELL__ >= 904+stableSolver :: EvBindsVar -> [Ct] -> [Ct] -> TcPluginM TcPluginSolveResult+stableSolver _ given wanted = do+#else+stableSolver :: [Ct] -> [Ct] -> [Ct] -> TcPluginM TcPluginResult+stableSolver given _derived wanted = do+#endif++  let chSt = concatMap filterCt wanted+  let haveSt = Set.fromList $ concatMap (filterTypeVar . fst) $ concatMap filterCt given+  case mapM (solveStable haveSt) chSt of+    Just evs -> return $ TcPluginOk evs []+    Nothing -> return $ TcPluginOk [] []++  where filterCt ct@(CDictCan {cc_class = cl, cc_tyargs = [ty]})+          = case getNameModule cl of+              Just (name,mod)+                | isRattModule mod && name == "Stable" -> [(ty,(ct,cl))]+              _ -> []+        filterCt _ = []+        filterTypeVar ty = case getTyVar_maybe ty of+          Just v -> [v]+          Nothing -> []
+ src/AsyncRattus/Plugin/Strictify.hs view
@@ -0,0 +1,86 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-}+module AsyncRattus.Plugin.Strictify+  (strictifyExpr, SCxt (..)) where+import Prelude hiding ((<>))+import AsyncRattus.Plugin.Utils++#if __GLASGOW_HASKELL__ >= 900+import GHC.Plugins+#else+import GhcPlugins+#endif++#if __GLASGOW_HASKELL__ >= 902+import GHC.Types.Tickish+#endif++data SCxt = SCxt {srcSpan :: SrcSpan, checkStrictData :: Bool}++-- | Transforms all functions into strict functions. If the+-- 'checkStrictData' field of the 'SCxt' argument is set to @True@,+-- then this function also checks for use of non-strict data types and+-- produces warnings if it finds any.+strictifyExpr :: SCxt -> CoreExpr -> CoreM CoreExpr+strictifyExpr ss (Let (NonRec b e1) e2) = do+  e1' <- strictifyExpr ss e1+  e2' <- strictifyExpr ss e2+  return (Case e1' b (exprType e2) [mkAlt DEFAULT [] e2' ])+strictifyExpr ss (Case e b t alts) = do+  e' <- strictifyExpr ss e+  alts' <- mapM ((\(c,args,e) -> fmap (\e' -> mkAlt c args e' ) (strictifyExpr ss e)) . getAlt) alts+  return (Case e' b t alts')+strictifyExpr ss (Let (Rec es) e) = do+  es' <- mapM (\ (b,e) -> strictifyExpr ss e >>= \e'-> return (b,e')) es+  e' <- strictifyExpr ss e+  return (Let (Rec es') e')+strictifyExpr ss (Lam b e)+   | not (isCoVar b) && not (isTyVar b) && tcIsLiftedTypeKind(typeKind (varType b))+    = do+       e' <- strictifyExpr ss e+       b' <- mkSysLocalFromVar (fsLit "strict") b+       return (Lam b' (Case (varToCoreExpr b') b (exprType e) [mkAlt DEFAULT [] e' ]))+   | otherwise = do+       e' <- strictifyExpr ss e+       return (Lam b e')+strictifyExpr ss (Cast e c) = do+  e' <- strictifyExpr ss e+  return (Cast e' c)+strictifyExpr ss (Tick t@(SourceNote span _) e) = do+  e' <- strictifyExpr (ss{srcSpan = fromRealSrcSpan span}) e+  return (Tick t e')+strictifyExpr ss (App e1 e2@Lit{}) =+  do e1' <- strictifyExpr ss e1+     return (App e1' e2)+strictifyExpr ss (App e1 e2)+  | (checkStrictData ss && not (isType e2) && tcIsLiftedTypeKind(typeKind (exprType e2))+        && not (isStrict (exprType e2))) = +      if isDeepseqForce e2 || isLit e2 then+        do e1' <- strictifyExpr ss e1+           e2' <- strictifyExpr ss e2+           return (App e1' e2')+      else+        do (printMessage SevWarning (srcSpan ss)+               (text "The use of lazy type " <> ppr (exprType e2) <> " may lead to memory leaks. Use Control.DeepSeq.force on lazy types."))+           e1' <- strictifyExpr ss{checkStrictData = False} e1+           e2' <- strictifyExpr ss{checkStrictData = False} e2+           return (App e1' e2')+  | otherwise = do+      e1' <- strictifyExpr ss e1+      e2' <- strictifyExpr ss e2+      return (App e1' e2')+strictifyExpr _ss e = return e++isLit :: CoreExpr -> Bool+isLit Lit{} = True+isLit (App (Var v) Lit{}) +  | Just (name,mod) <- getNameModule v = mod == "GHC.CString" && name == "unpackCString#"+isLit _ = False+++isDeepseqForce :: CoreExpr -> Bool+isDeepseqForce (App (App (App (Var v) _) _) _) =+  case getNameModule v of+    Just (name, mod) -> mod == "Control.DeepSeq" && name == "force"+    _ -> False+isDeepseqForce _ = False
+ src/AsyncRattus/Plugin/Transform.hs view
@@ -0,0 +1,144 @@+{-# LANGUAGE TupleSections #-}+module AsyncRattus.Plugin.Transform (+    transform+) where++import GHC.Core.Opt.Monad+import GHC.Plugins+import AsyncRattus.Plugin.PrimExpr+import AsyncRattus.Plugin.Utils+import Data.Maybe (fromJust)+import Prelude hiding ((<>))+import Data.Functor ((<&>))+import Control.Applicative ((<|>))+import Data.Tuple (swap)++data Ctx = Ctx {+    fresh :: Maybe Var+}++emptyCtx :: Ctx+emptyCtx = Ctx {+    fresh = Nothing+}++replaceVar :: Var -> Var -> Expr Var ->  Expr Var+replaceVar match rep (Var v) = if v == match then Var rep else Var v+replaceVar match rep (App e e') = App (replaceVar match rep e) (replaceVar match rep e')+replaceVar match rep (Tick _ e) = replaceVar match rep e+replaceVar match rep (Lam v e) = Lam (if v == match then rep else v) (replaceVar match rep e)+replaceVar match rep (Let (NonRec b e') e) =+  Let (NonRec newB (replaceVar  match rep e')) (replaceVar match rep e)+  where newB = if b == match then rep else b+replaceVar match rep (Cast e _) = replaceVar match rep e+replaceVar match rep (Case e b t alts) =+  Case newExpr newB t (map (\(Alt con binds expr) -> Alt con (map (\v -> if v == match then rep else v) binds) (replaceVar match rep expr)) alts)+  where newExpr = replaceVar match rep e+        newB = if b == match then rep else b+replaceVar _ _ e = e++transformPrim :: Ctx -> Expr Var -> CoreM (Expr Var, PrimInfo)+transformPrim ctx expr@(App e e') = case isPrimExpr expr of+  Just primInfo@(AdvApp f _) -> do+    varAdv' <- adv'Var+    let newE = replaceVar f varAdv' e+    return (App (App newE e') (Var (fromJust $ fresh ctx)), primInfo)+  Just primInfo@(SelectApp f _ _) -> do+    varSelect' <- select'Var+    let newE = replaceVar f varSelect' e+    return (App (App newE e') (Var (fromJust $ fresh ctx)), primInfo)+  Just (DelayApp _ t) -> do+    bigDelayVar <- bigDelay+    inputValueV <- inputValueVar+    let inputValueType = mkTyConTy inputValueV +    inpVar <- mkSysLocalM (fsLit "inpV") inputValueType inputValueType+    let ctx' = ctx {fresh = Just inpVar}+    (newExpr, maybePrimInfo) <- transform' ctx' e'+    let primInfo = fromJust maybePrimInfo+    let lambdaExpr = Lam inpVar newExpr+    clockCode <- constructClockExtractionCode primInfo+    return (App (App (App (Var bigDelayVar) (Type t)) clockCode) lambdaExpr, primInfo)+  Just primInfo -> do+        error $ showSDocUnsafe $ text "transformPrim: Cannot transform " <> ppr (prim primInfo)+  Nothing -> error "Cannot transform non-prim applications"+transformPrim _ _ = do+  error "Cannot transform anything else than prim applications"+++transform :: CoreExpr -> CoreM CoreExpr+transform expr = fst <$> transform' emptyCtx expr++transform' :: Ctx -> CoreExpr -> CoreM (CoreExpr, Maybe PrimInfo)+transform' ctx expr@(App e e') = case isPrimExpr expr of+    Just (BoxApp _) -> do+        (newExpr, primInfo) <- transform' ctx e'+        return (App e newExpr, primInfo)+    Just _ -> do+        (newExpr, primInfo) <- transformPrim ctx expr+        return (newExpr, Just primInfo)+    Nothing -> do+        (newExpr, primInfo) <- transform' ctx e+        (newExpr', primInfo') <- transform' ctx e'+        return (App newExpr newExpr', primInfo <|> primInfo')+transform' ctx (Lam b rhs) = do+    (newExpr, primInfo) <- transform' ctx rhs+    return (Lam b newExpr, primInfo)+transform' ctx (Let (NonRec b rhs) e) = do+    (newRhs, primInfo) <- transform' ctx rhs+    (newExpr, primInfo') <- transform' ctx e+    return (Let (NonRec b newRhs) newExpr, primInfo <|> primInfo')+transform' ctx (Let (Rec binds) e) = do+    transformedBinds <- mapM (\(b, bindE) -> fmap (b,) (transform' ctx bindE)) binds+    (e', mPi) <- transform' ctx e+    let primInfos = map (\(_, (_, p)) -> p) transformedBinds+    let firstPrimInfo = foldl (<|>) mPi primInfos+    newBinds <- mapM (\(b, (e, _)) -> return (b, e)) transformedBinds+    return (Let (Rec newBinds) e', firstPrimInfo)+transform' ctx (Case e b t alts) = do+    -- The checking pass has ensured that there are not advances on different+    -- clocks. Thus we can just pick the first PrimInfo we find.+    (expr, primInfo) <- transform' ctx e++    -- For each alternative, transform it and save the maybePrimInfo-value+    transformed <- mapM (\(Alt con binds expr) -> transform' ctx expr <&> fmap (Alt con binds) . swap) alts++    -- Of all the primInfos we have, pick the first one. This is safe because+    -- the checking pass has ensured that the clocks of all primitives.+    let firstPrimInfo = foldl (\acc (p, _) -> acc <|> p) primInfo transformed+    let alts' = map snd transformed+    return (Case expr b t alts', firstPrimInfo)+transform' ctx (Cast e _) = transform' ctx e+transform' ctx (Tick _ e) = transform' ctx e+transform' _ e = return (e, Nothing)++constructClockExtractionCode :: PrimInfo -> CoreM CoreExpr+constructClockExtractionCode (AdvApp _ arg) = createClockCode arg+constructClockExtractionCode (SelectApp _ arg arg2) =+    clockUnion arg arg2+constructClockExtractionCode primInfo = error $ "Cannot construct clock for prim " ++ showSDocUnsafe (ppr (prim primInfo))+++createClockCode :: (Var, Type) -> CoreM CoreExpr+createClockCode (argV, argT) = do+    extractClock <- extractClockVar+    return $ App (App (Var extractClock) (Type argT)) (Var argV)++-- Generate code for union of two clocks.+-- clockUnion (aVar, aType) (bVar, bType) returns the AST for:+--  Set.union (extractClock aVar) (extractClock bVar)++clockUnion :: (Var,Type) -> (Var, Type) -> CoreM CoreExpr+clockUnion arg arg2 = do+    clock1Code <- createClockCode arg+    clock2Code <- createClockCode arg2+    unionVar' <- unionVar+    return $+        App+        (+            App+            (+                   (Var unionVar')+            )+            clock1Code+        )+        clock2Code
+ src/AsyncRattus/Plugin/Utils.hs view
@@ -0,0 +1,430 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE CPP #-}++module AsyncRattus.Plugin.Utils (+  printMessage,+  Severity(..),+  isRattModule,+  adv'Var,+  select'Var,+  bigDelay,+  inputValueVar,+  extractClockVar,+  unionVar,+  isGhcModule,+  getNameModule,+  isStable,+  isStrict,+  isTemporal,+  userFunction,+  typeClassFunction,+  getVar,+  getMaybeVar,+  getModuleFS,+  isVar,+  isType,+  mkSysLocalFromVar,+  mkSysLocalFromExpr,+  fromRealSrcSpan,+  noLocationInfo,+  mkAlt,+  getAlt,+  splitForAllTys')+  where+#if __GLASGOW_HASKELL__ >= 906+import GHC.Builtin.Types.Prim+import GHC.Tc.Utils.TcType+#endif+#if __GLASGOW_HASKELL__ >= 904+import qualified GHC.Data.Strict as Strict+import Control.Concurrent.MVar (readMVar)+#else+import Data.IORef (readIORef)+#endif  +#if __GLASGOW_HASKELL__ >= 902++import GHC.Utils.Logger+#endif++#if __GLASGOW_HASKELL__ >= 900+import GHC.Plugins+import GHC.Utils.Error+import GHC.Utils.Monad+#else+import GhcPlugins+import ErrUtils+import MonadUtils+#endif+++import GHC.Types.Name.Cache (NameCache(nsNames), lookupOrigNameCache, OrigNameCache)+import qualified GHC.Types.Name.Occurrence as Occurrence+import GHC.Types.TyThing++import Prelude hiding ((<>))+import Data.Set (Set)+import qualified Data.Set as Set+import Data.Char+import Data.Maybe+++getMaybeVar :: CoreExpr -> Maybe Var+getMaybeVar (App e e')+  | isType e' || not  (tcIsLiftedTypeKind (typeKind (exprType e'))) = getMaybeVar e+  | otherwise = Nothing+getMaybeVar (Cast e _) = getMaybeVar e+getMaybeVar (Tick _ e) = getMaybeVar e+getMaybeVar (Var v) = Just v+getMaybeVar _ = Nothing++getVar :: CoreExpr -> Var+getVar = fromJust . getMaybeVar++isVar :: CoreExpr -> Bool+isVar = isJust . getMaybeVar++isType Type {} = True+isType (App e _) = isType e+isType (Cast e _) = isType e+isType (Tick _ e) = isType e+isType _ = False++#if __GLASGOW_HASKELL__ >= 906+isFunTyCon = isArrowTyCon+repSplitAppTys = splitAppTysNoView+#endif+ +#if __GLASGOW_HASKELL__ >= 902+printMessage :: (HasDynFlags m, MonadIO m, HasLogger m) =>+                Severity -> SrcSpan -> SDoc -> m ()+#else+printMessage :: (HasDynFlags m, MonadIO m) =>+                Severity -> SrcSpan -> MsgDoc -> m ()+#endif++printMessage sev loc doc = do+#if __GLASGOW_HASKELL__ >= 906+  logger <- getLogger+  liftIO $ putLogMsg logger (logFlags logger)+    (MCDiagnostic sev (if sev == SevError then ErrorWithoutFlag else WarningWithoutFlag) Nothing) loc doc+#elif __GLASGOW_HASKELL__ >= 904+  logger <- getLogger+  liftIO $ putLogMsg logger (logFlags logger)+    (MCDiagnostic sev (if sev == SevError then ErrorWithoutFlag else WarningWithoutFlag)) loc doc+#elif __GLASGOW_HASKELL__ >= 902+   dflags <- getDynFlags+   logger <- getLogger+   liftIO $ putLogMsg logger dflags NoReason sev loc doc+#elif __GLASGOW_HASKELL__ >= 900  +  dflags <- getDynFlags+  liftIO $ putLogMsg dflags NoReason sev loc doc+#else+  dflags <- getDynFlags+  let sty = case sev of+              SevError   -> defaultErrStyle dflags+              SevWarning -> defaultErrStyle dflags+              SevDump    -> defaultDumpStyle dflags+              _          -> defaultUserStyle dflags+  liftIO $ putLogMsg dflags NoReason sev loc sty doc+#endif++#if __GLASGOW_HASKELL__ >= 902+instance Ord FastString where+   compare = uniqCompareFS+#endif++{-+******************************************************+*             Extracting variables                   *+******************************************************+-}+++origNameCache :: CoreM OrigNameCache+origNameCache = do+  hscEnv <- getHscEnv+#if __GLASGOW_HASKELL__ >= 904+  let nameCache = hsc_NC hscEnv+  liftIO $ readMVar (nsNames nameCache)+#else+  nameCache <- liftIO $ readIORef (hsc_NC hscEnv)+  return $ nsNames nameCache+#endif+++getNamedThingFromModuleAndOccName :: String -> OccName -> CoreM TyThing+getNamedThingFromModuleAndOccName moduleName occName = do+  origNameCache <- origNameCache+  let [mod] = filter ((moduleName ==) . unpackFS . getModuleFS) (moduleEnvKeys origNameCache)+  let name = fromJust $ lookupOrigNameCache origNameCache mod occName+  lookupThing name++getVarFromModule :: String -> String -> CoreM Var+getVarFromModule moduleName = fmap tyThingId . getNamedThingFromModuleAndOccName moduleName . mkOccName Occurrence.varName++getTyConFromModule :: String -> String -> CoreM TyCon+getTyConFromModule moduleName = fmap tyThingTyCon . getNamedThingFromModuleAndOccName moduleName . mkOccName Occurrence.tcName++adv'Var :: CoreM Var+adv'Var = getVarFromModule "AsyncRattus.InternalPrimitives" "adv'"++select'Var :: CoreM Var+select'Var = getVarFromModule "AsyncRattus.InternalPrimitives" "select'"++bigDelay :: CoreM Var+bigDelay = getVarFromModule "AsyncRattus.InternalPrimitives" "Delay"++inputValueVar :: CoreM TyCon+inputValueVar = getTyConFromModule "AsyncRattus.InternalPrimitives" "InputValue"++extractClockVar :: CoreM Var+extractClockVar = getVarFromModule "AsyncRattus.InternalPrimitives" "extractClock"++unionVar :: CoreM Var+unionVar = getVarFromModule "AsyncRattus.InternalPrimitives" "clockUnion"++rattModules :: Set FastString+rattModules = Set.fromList ["AsyncRattus.InternalPrimitives","AsyncRattus.Channels"]++getModuleFS :: Module -> FastString+getModuleFS = moduleNameFS . moduleName++isRattModule :: FastString -> Bool+isRattModule = (`Set.member` rattModules)++isGhcModule :: FastString -> Bool+isGhcModule = (== "GHC.Types")++getNameModule :: NamedThing a => a -> Maybe (FastString, FastString)+getNameModule v = do+  let name = getName v+  mod <- nameModule_maybe name+  return (getOccFS name,moduleNameFS (moduleName mod))+++-- | The set of stable built-in types.+ghcStableTypes :: Set FastString+ghcStableTypes = Set.fromList ["Int","Bool","Float","Double","Char", "IO"]++isGhcStableType :: FastString -> Bool+isGhcStableType = (`Set.member` ghcStableTypes)+++newtype TypeCmp = TC Type++instance Eq TypeCmp where+  (TC t1) == (TC t2) = eqType t1 t2++instance Ord TypeCmp where+  compare (TC t1) (TC t2) = nonDetCmpType t1 t2++isTemporal :: Type -> Bool+isTemporal t = isTemporalRec 0 Set.empty t+++isTemporalRec :: Int -> Set TypeCmp -> Type -> Bool+isTemporalRec d _ _ | d == 100 = False+isTemporalRec _ pr t | Set.member (TC t) pr = False+isTemporalRec d pr t = do+  let pr' = Set.insert (TC t) pr+  case splitTyConApp_maybe t of+    Nothing -> False+    Just (con,args) ->+      case getNameModule con of+        Nothing -> False+        Just (name,mod)+          -- If it's a Rattus type constructor check if it's a box+          | isRattModule mod && (name == "Box" || name == "O") -> True+          | isFunTyCon con -> or (map (isTemporalRec (d+1) pr') args)+          | isAlgTyCon con ->+            case algTyConRhs con of+              DataTyCon {data_cons = cons} -> or (map check cons)+                where check con = case dataConInstSig con args of+                        (_, _,tys) -> or (map (isTemporalRec (d+1) pr') tys)+              _ -> or (map (isTemporalRec (d+1) pr') args)+        _ -> False+++-- | Check whether the given type is stable. This check may use+-- 'Stable' constraints from the context.++isStable :: Set Var -> Type -> Bool+isStable c t = isStableRec c 0 Set.empty t++-- | Check whether the given type is stable. This check may use+-- 'Stable' constraints from the context.++isStableRec :: Set Var -> Int -> Set TypeCmp -> Type -> Bool+-- To prevent infinite recursion (when checking recursive types) we+-- keep track of previously checked types. This, however, is not+-- enough for non-regular data types. Hence we also have a counter.+isStableRec _ d _ _ | d == 100 = True+isStableRec _ _ pr t | Set.member (TC t) pr = True+isStableRec c d pr t = do+  let pr' = Set.insert (TC t) pr+  case splitTyConApp_maybe t of+    Nothing -> case getTyVar_maybe t of+      Just v -> -- if it's a type variable, check the context+        v `Set.member` c+      Nothing -> False+    Just (con,args) ->+      case getNameModule con of+        Nothing -> False+        Just (name,mod)+          -- If it's a Rattus type constructor check if it's a box+          | isRattModule mod && name == "Box" -> True+            -- If its a built-in type check the set of stable built-in types+          | isGhcModule mod -> isGhcStableType name+          {- deal with type synonyms (does not seem to be necessary (??))+           | Just (subst,ty,[]) <- expandSynTyCon_maybe con args ->+             isStableRec c (d+1) pr' (substTy (extendTvSubstList emptySubst subst) ty) -}+          | isAlgTyCon con ->+            case algTyConRhs con of+              DataTyCon {data_cons = cons, is_enum = enum}+                | enum -> True+                | and $ concatMap (map isSrcStrict'+                                   . dataConSrcBangs) $ cons ->+                  and  (map check cons)+                | otherwise -> False+                where check con = case dataConInstSig con args of+                        (_, _,tys) -> and (map (isStableRec c (d+1) pr') tys)+              TupleTyCon {} -> null args+              _ -> False+        _ -> False++++isStrict :: Type -> Bool+isStrict t = isStrictRec 0 Set.empty t++#if __GLASGOW_HASKELL__ >= 902+splitForAllTys' :: Type -> ([TyCoVar], Type)+splitForAllTys' = splitForAllTyCoVars+#else+splitForAllTys' = splitForAllTys+#endif++-- | Check whether the given type is stable. This check may use+-- 'Stable' constraints from the context.++isStrictRec :: Int -> Set TypeCmp -> Type -> Bool+-- To prevent infinite recursion (when checking recursive types) we+-- keep track of previously checked types. This, however, is not+-- enough for non-regular data types. Hence we also have a counter.+isStrictRec d _ _ | d == 100 = True+isStrictRec _ pr t | Set.member (TC t) pr = True+isStrictRec d pr t = do+  let pr' = Set.insert (TC t) pr+  let (_,t') = splitForAllTys' t+  let (c, tys) = repSplitAppTys t'+  if isJust (getTyVar_maybe c) then and (map (isStrictRec (d+1) pr') tys)+  else  case splitTyConApp_maybe t' of+    Nothing -> isJust (getTyVar_maybe t)+    Just (con,args) ->+      case getNameModule con of+        Nothing -> False+        Just (name,mod)+          | mod == "GHC.Num.Integer" && name == "Integer" -> True+          | mod == "Data.Text.Internal" && name == "Text" -> True+          -- If it's a Rattus type constructor check if it's a box+          | isRattModule mod && (name == "Box" || name == "O" || name == "Output") -> True+            -- If its a built-in type check the set of stable built-in types+          | isGhcModule mod -> isGhcStableType name+          {- deal with type synonyms (does not seem to be necessary (??))+           | Just (subst,ty,[]) <- expandSynTyCon_maybe con args ->+             isStrictRec c (d+1) pr' (substTy (extendTvSubstList emptySubst subst) ty) -}+          | isFunTyCon con -> True+          | isAlgTyCon con ->+            case algTyConRhs con of+              DataTyCon {data_cons = cons, is_enum = enum}+                | enum -> True+                | and $ (map (areSrcStrict args)) $ cons ->+                  and  (map check cons)+                | otherwise -> False+                where check con = case dataConInstSig con args of+                        (_, _,tys) -> and (map (isStrictRec (d+1) pr') tys)+              TupleTyCon {} -> null args+              NewTyCon {nt_rhs = ty} -> isStrictRec (d+1) pr' ty+              _ -> False+          | otherwise -> False++++++areSrcStrict :: [Type] -> DataCon -> Bool+areSrcStrict args con = and (zipWith check tys (dataConSrcBangs con))+  where (_, _,tys) = dataConInstSig con args+        check _ b = isSrcStrict' b++isSrcStrict' :: HsSrcBang -> Bool+isSrcStrict' (HsSrcBang _ _ SrcStrict) = True+isSrcStrict' (HsSrcBang _ SrcUnpack _) = True+isSrcStrict' _ =  False+++userFunction :: Var -> Bool+userFunction v+  | typeClassFunction v = True+  | otherwise = +    case getOccString (getName v) of+      (c : _)+        | isUpper c || c == '$' || c == ':' -> False+        | otherwise -> True+      _ -> False++typeClassFunction :: Var -> Bool+typeClassFunction v =+  case getOccString (getName v) of+    ('$' : 'c' : _) -> True+    ('$' : 'f' : _) -> True+    _ -> False++mkSysLocalFromVar :: MonadUnique m => FastString -> Var -> m Id+#if __GLASGOW_HASKELL__ >= 900++mkSysLocalFromVar lit v = mkSysLocalM lit (varMult v) (varType v)+#else+mkSysLocalFromVar lit v = mkSysLocalM lit (varType v)+#endif+ +mkSysLocalFromExpr :: MonadUnique m => FastString -> CoreExpr -> m Id+#if __GLASGOW_HASKELL__ >= 900+mkSysLocalFromExpr lit e = mkSysLocalM lit oneDataConTy (exprType e)+#else+mkSysLocalFromExpr lit e = mkSysLocalM lit (exprType e)+#endif+ + +fromRealSrcSpan :: RealSrcSpan -> SrcSpan+#if __GLASGOW_HASKELL__ >= 904+fromRealSrcSpan span = RealSrcSpan span Strict.Nothing+#elif __GLASGOW_HASKELL__ >= 900+fromRealSrcSpan span = RealSrcSpan span Nothing+#else+fromRealSrcSpan span = RealSrcSpan span+#endif++#if __GLASGOW_HASKELL__ >= 900+instance Ord SrcSpan where+  compare (RealSrcSpan s _) (RealSrcSpan t _) = compare s t+  compare RealSrcSpan{} _ = LT+  compare _ _ = GT+#endif++noLocationInfo :: SrcSpan+#if __GLASGOW_HASKELL__ >= 900+noLocationInfo = UnhelpfulSpan UnhelpfulNoLocationInfo+#else         +noLocationInfo = UnhelpfulSpan "<no location info>"+#endif++#if __GLASGOW_HASKELL__ >= 902+mkAlt c args e = Alt c args e+getAlt (Alt c args e) = (c, args, e)+#else+mkAlt c args e = (c, args, e)+getAlt alt = alt+#endif
+ src/AsyncRattus/Primitives.hs view
@@ -0,0 +1,19 @@+-- | The language primitives of Async Rattus. Note that the typing+--  rules for 'delay', 'adv','select' and 'box' are more restrictive+--  than the Haskell types that are indicated. The stricter Async+--  Rattus typing rules for these primitives are given below.++{-# LANGUAGE TypeOperators #-}+module AsyncRattus.Primitives+  (O+  ,Box+  ,Select(..)+  ,delay+  ,adv+  ,box+  ,unbox+  ,select+  ,never+  ,Stable+  ) where+import AsyncRattus.InternalPrimitives
+ src/AsyncRattus/Signal.hs view
@@ -0,0 +1,368 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -fplugin=AsyncRattus.Plugin #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE CPP #-}+++-- | Programming with signals.++module AsyncRattus.Signal+  ( map+  , mkInputSig+  , getInputSig+  , filterMap+  , filterMapAwait+  , filter+  , filterAwait+  , trigger+  , triggerAwait+  , mapAwait+  , switch+  , switchS+  , switchAwait+  , interleave+  , mkSig+  , mkBoxSig+  , current+  , future+  , const+  , scan+  , scanAwait+  , scanMap+  , Sig(..)+  , zipWith+  , zipWith3+  , zip+  , cond+  , integral+  , derivative+  )++where++import AsyncRattus+import AsyncRattus.Channels+import Prelude hiding (map, const, zipWith, zipWith3, zip, filter)+import Data.VectorSpace+import Data.Ratio ((%))++infixr 5 :::++-- | @Sig a@ is a stream of values of type @a@.+data Sig a = !a ::: !(O (Sig a))++instance Producer (Sig a) a where+  getCurrent p = Just' (current p)+  getNext p cb = cb (future p)++newtype SigMaybe a = SigMaybe (Sig (Maybe' a))++instance Producer (SigMaybe a) a where+  getCurrent (SigMaybe p) = current p+  getNext (SigMaybe p) cb = cb (delay (SigMaybe (adv (future p))))++++{-# ANN module AsyncRattus #-}++-- | Get the current value of a signal.+current :: Sig a -> a+current (x ::: _) = x+++-- | Get the future the signal.+future :: Sig a -> O (Sig a)+future (_ ::: xs) = xs++-- | Apply a function to the value of a signal.+map :: Box (a -> b) -> Sig a -> Sig b+map f (x ::: xs) = unbox f x ::: delay (map f (adv xs))++-- | Variant of 'getInput' that returns a signal instead of a boxed+-- delayed computation.+getInputSig :: IO (Box (O (Sig a)) :* (a -> IO ()))+getInputSig = do (s :* cb) <- getInput+                 return (mkBoxSig s :* cb)++-- | Turn a producer into a signal. This is a variant of 'mkInput'+-- that returns a signal instead of a boxed delayed computation.+mkInputSig :: Producer p a => p -> IO (Box (O (Sig a)))+mkInputSig p = mkBoxSig <$> mkInput p+++-- | This function is essentially the composition of 'filter' with+-- 'map'. The signal produced by @filterMap f s@ has the value @v@+-- whenever @s@ has the value @u@ such that @unbox f u = Just' v@.+filterMap :: Box (a -> Maybe' b) -> Sig a -> IO (Box (O (Sig b)))+filterMap f s = mkInputSig (SigMaybe (map f s))++-- | This function is similar to 'filterMap' but takes a delayed+-- signal (type @O (Sig a)@) as an argument instead of a signal (@Sig+-- a@).+filterMapAwait :: Box (a -> Maybe' b) -> O (Sig a) -> IO (Box (O (Sig b)))+filterMapAwait f s = mkInputSig (delay (SigMaybe (map f (adv s))))++-- | Filter the given signal using a predicate. The signal produced by+-- @filter p s@ contains only values from @s@ that satisfy the+-- predicate @p@.+filter :: Box (a -> Bool) -> Sig a -> IO (Box (O (Sig a)))+filter p = filterMap (box (\ x -> if unbox p x then Just' x else Nothing'))++-- | This function is similar to 'filter' but takes a delayed signal+-- (type @O (Sig a)@) as an argument instead of a signal (@Sig a@).+filterAwait :: Box (a -> Bool) -> O (Sig a) -> IO (Box (O (Sig a)))+filterAwait p = filterMapAwait (box (\ x -> if unbox p x then Just' x else Nothing'))+++-- | This function is a variant of 'zipWith'. Whereas @zipWith f xs+-- ys@ produces a new value whenever @xs@ or @ys@ produce a new value,+-- @trigger f xs ys@ only produces a new value when xs produces a new+-- value.+--+-- Example:+--+-- >                      xs:  1 2 3     2+-- >                      ys:  1     0 5 2+-- >+-- > zipWith (box (+)) xs ys:  2 3 4 3 8 4+-- > trigger (box (+)) xy ys:  2     3 8 4++trigger :: (Stable a, Stable b) => Box (a -> b -> c) -> Sig a -> Sig b -> IO (Box (Sig c))+trigger f (a ::: as) bs@(b:::_) = do s <- triggerAwait f as bs+                                     return (box (unbox f a b ::: unbox s))+-- | This function is similar to 'trigger' but takes a delayed signal+-- (type @O (Sig a)@) as an argument instead of a signal (@Sig a@).+triggerAwait :: Stable b => Box (a -> b -> c) -> O (Sig a) -> Sig b -> IO (Box (O (Sig c)))+triggerAwait f as bs = mkBoxSig <$> mkInput (box SigMaybe `mapO` (trig f as bs)) where+  trig :: Stable b => Box (a -> b -> c) -> O (Sig a) -> Sig b -> O (Sig (Maybe' c))+  trig f as (b ::: bs) =+    delay (case select as bs of+            Fst (a' ::: as') bs' -> Just' (unbox f a' b) ::: trig f as' (b ::: bs')+            Snd as' bs' -> Nothing' ::: trig f as' bs'+            Both (a' ::: as') (b' ::: bs') -> Just' (unbox f a' b') ::: trig f as' (b' ::: bs')+          )++-- | A version of @map@ for delayed signals.+mapAwait :: Box (a -> b) -> O (Sig a) -> O (Sig b)+mapAwait f d = delay (map f (adv d))++-- | Turns a boxed delayed computation into a delayed signal.+mkSig :: Box (O a) -> O (Sig a)+mkSig b = delay (adv (unbox b) ::: mkSig b)++-- | Variant of 'mkSig' that returns a boxed delayed signal+mkBoxSig :: Box (O a) -> Box (O (Sig a))+mkBoxSig b = box (mkSig b)+++-- | Construct a constant signal that never updates.+const :: a -> Sig a+const x = x ::: never++-- | Construct a signal by repeatedly applying a function to a given+-- start element. That is, @unfold (box f) x@ will produce the signal+-- @x ::: f x ::: f (f x) ::: ...@+-- unfold :: Stable a => Box (a -> a) -> a -> Sig a+-- unfold f x = x ::: delay (unfold f (unbox f x))++-- | Similar to Haskell's 'scanl'.+--+-- > scan (box f) x (v1 ::: v2 ::: v3 ::: ... ) == (x `f` v1) ::: ((x `f` v1) `f` v2) ::: ...+--+-- Note: Unlike 'scanl', 'scan' starts with @x `f` v1@, not @x@.++scan :: (Stable b) => Box(b -> a -> b) -> b -> Sig a -> Sig b+scan f acc (a ::: as) = acc' ::: delay (scan f acc' (adv as))+  where acc' = unbox f acc a++-- Like 'scan', but uses a delayed signal.+scanAwait :: (Stable b) => Box (b -> a -> b) -> b -> O (Sig a) -> Sig b+scanAwait f acc as = acc ::: delay (scan f acc (adv as))++-- | 'scanMap' is a composition of 'map' and 'scan':+--+-- > scanMap f g x === map g . scan f x+scanMap :: (Stable b) => Box (b -> a -> b) -> Box (b -> c) -> b -> Sig a -> Sig c+scanMap f p acc (a ::: as) =  unbox p acc' ::: delay (scanMap f p acc' (adv as))+  where acc' = unbox f acc a++-- | This function allows to switch from one signal to another one+-- dynamically. The signal defined by @switch xs ys@ first behaves+-- like @xs@, but as soon as @ys@ produces a new value, @switch xs ys@+-- behaves like @ys@.+--+-- Example:+--+-- >           xs: 1 2 3 4 5   6 7 8   9+-- >           ys:         1 2   3 4 5 6+-- >+-- > switch xs ys: 1 2 3 1 2 4   3 4 5 6+switch :: Sig a -> O (Sig a) -> Sig a+switch (x ::: xs) d = x ::: delay (case select xs d of+                                     Fst   xs'  d'  -> switch xs' d'+                                     Snd   _    d'  -> d'+                                     Both  _    d'  -> d')++-- | This function is similar to 'switch', but the (future) second+-- signal may depend on the last value of the first signal.+switchS :: Stable a => Sig a -> O (a -> Sig a) -> Sig a+switchS (x ::: xs) d = x ::: delay (case select xs d of+                                     Fst   xs'  d'  -> switchS xs' d'+                                     Snd   _    f  -> f x+                                     Both  _    f  -> f x)++-- | This function is similar to 'switch' but works on delayed signals+-- instead of signals.+switchAwait :: O (Sig a) -> O (Sig a) -> O (Sig a)+switchAwait xs ys = delay (case select xs ys of+                                  Fst  xs'  d'  -> switch xs' d'+                                  Snd  _    d'  -> d'+                                  Both _    d'  -> d')++-- | This function interleaves two signals producing a new value @v@+-- whenever either input stream produces a new value @v@. In case the+-- input signals produce a new value simultaneously, the function+-- argument is used break ties, i.e. to compute the new output value based+-- on the two new input values+--+-- Example:+--+-- >                         xs: 1 3   5 3 1 3+-- >                         ys:   0 2   4+-- >+-- > interleave (box (+)) xs ys: 1 3 2 5 7 1 3+interleave :: Box (a -> a -> a) -> O (Sig a) -> O (Sig a) -> O (Sig a)+interleave f xs ys = delay (case select xs ys of+                              Fst (x ::: xs') ys' -> x ::: interleave f xs' ys'+                              Snd xs' (y ::: ys') -> y ::: interleave f xs' ys'+                              Both (x ::: xs') (y ::: ys') -> unbox f x y ::: interleave f xs' ys')+++-- | This function is a variant of combines the values of two signals+-- using the function argument. @zipWith f xs ys@ produces a new value+-- @unbox f x y@ whenever @xs@ or @ys@ produce a new value, where @x@+-- and @y@ are the current values of @xs@ and @ys@, respectively.+--+-- Example:+--+-- >                      xs:  1 2 3     2+-- >                      ys:  1     0 5 2+-- >+-- > zipWith (box (+)) xs ys:  2 3 4 3 8 4++zipWith :: (Stable a, Stable b) => Box(a -> b -> c) -> Sig a -> Sig b -> Sig c+zipWith f (a ::: as) (b ::: bs) = unbox f a b ::: delay (+    case select as bs of+      Fst as' lbs -> zipWith f as' (b ::: lbs)+      Snd las bs' -> zipWith f (a ::: las) bs'+      Both as' bs' -> zipWith f as' bs'+  )++-- | Variant of 'zipWith' with three signals.+zipWith3 :: forall a b c d. (Stable a, Stable b, Stable c) => Box(a -> b -> c -> d) -> Sig a -> Sig b -> Sig c -> Sig d+zipWith3 f as bs cs = zipWith (box (\f x -> unbox f x)) cds cs+  where cds :: Sig (Box (c -> d))+        cds = zipWith (box (\a b -> box (\ c -> unbox f a b c))) as bs++-- | If-then-else lifted to signals. @cond bs xs ys@ produces a stream+-- whose value is taken from @xs@ whenever @bs@ is true and from @ys@+-- otherwise.+cond :: Stable a => Sig Bool -> Sig a -> Sig a -> Sig a+cond = zipWith3 (box (\b x y -> if b then x else y))+++-- | This is a special case of 'zipWith' using the tupling+-- function. That is,+--+-- > zip = zipWith (box (:*))+zip :: (Stable a, Stable b) => Sig a -> Sig b -> Sig (a:*b)+zip = zipWith (box (:*))++-- | Sampling interval (in microseconds) for the 'integral' and+-- 'derivative' functions.++dt :: Int+dt = 20000++-- | @integral x xs@ computes the integral of the signal @xs@ with the+-- constant @x@. For example, if @xs@ is the velocity of an object,+-- the signal @integral 0 xs@ describes the distance travelled by that+-- object.+integral :: forall a v . (VectorSpace v a, Eq v, Fractional a, Stable v, Stable a)+  => v -> Sig v -> Sig v+integral = int +  where int cur (x ::: xs)+          | x == zeroVector = cur ::: delay (int cur (adv xs))+          | otherwise = cur ::: delay (+              case select xs (unbox (timer dt)) of+                Fst xs' _ -> int cur xs'+                Snd xs' () -> int (dtf *^ (cur ^+^ x)) (x ::: xs')+                Both (x' ::: xs') () ->  int (dtf *^ (cur ^+^ x')) (x'::: xs'))+         -- sampling interval in seconds+        dtf :: a+        dtf = fromRational (fromIntegral dt % 1000000)+                +-- | Compute the derivative of a signal. For example, if @xs@ is the+-- velocity of an object, the signal @derivative xs@ describes the+-- acceleration travelled by that object.+derivative :: forall a v . (VectorSpace v a, Eq v, Fractional a, Stable v, Stable a)+  => Sig v -> Sig v+derivative xs = der zeroVector (current xs) xs where+  -- inverse sampling interval in seconds+  dtf :: a+  dtf = fromIntegral dt * 0.000001++  der :: v -> v -> Sig v -> Sig v+  der d last (x:::xs)+    | d == zeroVector = zeroVector ::: delay+                        (let x' ::: xs' = adv xs+                         in der ((x' ^-^ x) ^/ dtf) x (x' ::: xs'))+    | otherwise = d ::: delay (+        case select xs (unbox (timer dt)) of+          Fst xs' _ -> der d last xs'+          Snd xs' () -> der ((x ^-^ last) ^/ dtf) x (x ::: xs')+          Both (x' ::: xs') () ->  der ((x' ^-^ last) ^/ dtf) x' (x' ::: xs'))++-- Prevent functions from being inlined too early for the rewrite+-- rules to fire.++{-# NOINLINE [1] map #-}+{-# NOINLINE [1] const #-}+{-# NOINLINE [1] scan #-}+{-# NOINLINE [1] scanMap #-}+{-# NOINLINE [1] zip #-}+++{-# RULES++  "const/map" forall (f :: Stable b => Box (a -> b))  x.+    map f (const x) = let x' = unbox f x in const x' ;++  "map/map" forall f g xs.+    map f (map g xs) = map (box (unbox f . unbox g)) xs ;++  "map/scan" forall f p acc as.+    map p (scan f acc as) = scanMap f p acc as ;++  "zip/map" forall xs ys f.+    map f (zip xs ys) = let f' = unbox f in zipWith (box (\ x y -> f' (x :* y))) xs ys+#-}+++#if __GLASGOW_HASKELL__ >= 808+{-# RULES+  "scan/scan" forall f g b c as.+    scan g c (scan f b as) =+      let f' = unbox f; g' = unbox g in+      scanMap (box (\ (b:*c) a -> let b' = f' b a in (b':* g' c b'))) (box snd') (b:*c) as ;++  "scan/scanMap" forall f g p b c as.+    scan g c (scanMap f p b as) =+      let f' = unbox f; g' = unbox g; p' = unbox p in+      scanMap (box (\ (b:*c) a -> let b' = f' (p' b) a in (b':* g' c b'))) (box snd') (b:*c) as ;++#-}+#endif
+ src/AsyncRattus/Strict.hs view
@@ -0,0 +1,206 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE UndecidableInstances #-}++-- | This module contains strict versions of some standard data+-- structures.++++module AsyncRattus.Strict+  ( List(..),+    singleton,+    fromList,+    toList,+    init',+    reverse',+    (+++),+    listToMaybe',+    map',+    zip',+    zipWith',+    mapMaybe',+    (:*)(..),+    Maybe'(..),+    maybe',+    fromMaybe',+    fst',+    snd',+    curry',+    uncurry'+  )where++import Prelude hiding (map)+import Data.VectorSpace++infixr 2 :*+infixr 8 :!++-- | Strict list type.+data List a = Nil | !a :! !(List a)++singleton :: a -> List a+singleton x = x :! Nil++fromList :: [a] -> List a+fromList [] = Nil+fromList (x : xs) = x :! fromList xs++toList :: List a -> [a]+toList Nil = []+toList (x :! xs) = x : toList xs++-- | Remove the last element from a list if there is one, otherwise+-- return 'Nil'.+init' :: List a -> List a+init' Nil = Nil+init' (_ :! Nil) = Nil+init' (x :! xs) = x :! init' xs++-- | Reverse a list.+reverse' :: List a -> List a+reverse' l =  rev l Nil+  where+    rev Nil     a = a+    rev (x:!xs) a = rev xs (x:!a)+    +-- | Returns @'Nothing''@ on an empty list or @'Just'' a@ where @a@ is the+-- first element of the list.+listToMaybe' :: List a -> Maybe' a+listToMaybe' Nil = Nothing'+listToMaybe' (x :! _) = Just' x++-- | Append two lists.+(+++) :: List a -> List a -> List a+(+++) Nil     ys = ys+(+++) (x:!xs) ys = x :! xs +++ ys+++map' :: (a -> b) -> List a -> List b+map' _ Nil = Nil+map' f (x :! xs) = f x :! map' f xs++zip' :: List a -> List b -> List (a :* b)+zip' Nil _ = Nil+zip' _ Nil = Nil+zip' (x :! xs) (y :! ys) = (x :* y) :! zip' xs ys++zipWith' :: (a -> b -> c) -> List a -> List b -> List c+zipWith' _ Nil _ = Nil+zipWith' _ _ Nil = Nil+zipWith' f (x :! xs) (y :! ys) = f x y :! zipWith' f xs ys+++-- | A version of 'map' which can throw out elements.  In particular,+-- the function argument returns something of type @'Maybe'' b@.  If+-- this is 'Nothing'', no element is added on to the result list.  If+-- it is @'Just'' b@, then @b@ is included in the result list.+mapMaybe'          :: (a -> Maybe' b) -> List a -> List b+mapMaybe' _ Nil     = Nil+mapMaybe' f (x:!xs) =+ let rs = mapMaybe' f xs in+ case f x of+  Nothing' -> rs+  Just' r  -> r:!rs++instance Foldable List where+  +  foldMap f = run where+    run Nil = mempty+    run (x :! xs) = f x <> run xs+  foldr f = run where+    run b Nil = b+    run b (a :! as) = (run $! (f a b)) as+  foldl f = run where+    run a Nil = a+    run a (b :! bs) = (run $! (f a b)) bs+  elem a = run where+    run Nil = False+    run (x :! xs)+      | a == x = True+      | otherwise = run xs+    +  +instance Functor List where+  fmap = map'++instance Eq a => Eq (List a) where+  Nil == Nil = True+  Nil == _ = False+  _ == Nil = False+  (x :! xs) == (y :! ys) = if x == y then xs == ys else False++instance Show a => Show (List a) where+  show Nil = "Nil"+  show (x :! xs) = show x ++ " :! " ++ show xs++-- | Strict variant of 'Maybe'.+data Maybe' a = Just' !a | Nothing'++instance Eq a => Eq (Maybe' a) where+  Nothing' == Nothing' = True+  Just' x == Just' y = x == y+  _ == _ = False++instance Show a => Show (Maybe' a) where+  show Nothing' = "Nothing'"+  show (Just' x) = "Just' " ++ show x++-- | takes a default value, a function, and a 'Maybe'' value.  If the+-- 'Maybe'' value is 'Nothing'', the function returns the default+-- value.  Otherwise, it applies the function to the value inside the+-- 'Just'' and returns the result.+maybe' :: b -> (a -> b) -> Maybe' a -> b+maybe' n _ Nothing'  = n+maybe' _ f (Just' x) = f x++fromMaybe' :: a -> Maybe' a -> a+fromMaybe' _ (Just' x) = x+fromMaybe' d Nothing' = d++-- | Strict pair type.+data a :* b = !a :* !b++-- | First projection function.+fst' :: (a :* b) -> a+fst' (a:*_) = a++-- | Second projection function.+snd' :: (a :* b) -> b+snd' (_:*b) = b++curry' :: ((a :* b) -> c) -> a -> b -> c+curry' f x y = f (x :* y)++uncurry' :: (a -> b -> c) -> (a :* b) -> c+uncurry' f (x :* y) = f x y+++instance Functor ((:*) a) where+  fmap f (x:*y) = (x :* f y)+  +instance (Show a, Show b) => Show (a:*b) where+  show (a :* b) = "(" ++ show a ++ " :* " ++ show b ++ ")"++instance (Eq a, Eq b) => Eq (a :* b) where+  (x1 :* y1) == (x2 :* y2) = x1 == x2 && y1 == y2+++instance (VectorSpace v a, VectorSpace w a, Floating a, Eq a) => VectorSpace (v :* w) a where+  zeroVector = zeroVector :* zeroVector++  a *^ (x :* y) = (a *^ x) :* (a *^ y)++  (x :* y) ^/ a = (x ^/ a) :* (y ^/ a)++  negateVector (x :* y) = (negateVector x) :* (negateVector y)++  (x1 :* y1) ^+^ (x2 :* y2) = (x1 ^+^ x2) :* (y1 ^+^ y2)++  (x1 :* y1) ^-^ (x2 :* y2) = (x1 ^-^ x2) :* (y1 ^-^ y2)++  (x1 :* y1) `dot` (x2 :* y2) = (x1 `dot` x2) + (y1 `dot` y2)++  
+ test/IllTyped.hs view
@@ -0,0 +1,170 @@+{-# LANGUAGE RebindableSyntax #-}++module Main (module Main) where++import AsyncRattus+import AsyncRattus.Signal as S+import Prelude+import AsyncRattus.Plugin.Annotation (InternalAnn (..))+++{-# ANN module AsyncRattus #-}+++{-# ANN loopIndirect ExpectError #-}+loopIndirect :: Sig Int+loopIndirect = run+  where run :: Sig Int+        run = loopIndirect++{-# ANN loopIndirect' ExpectError #-}+loopIndirect' :: Sig Int+loopIndirect' = let run = loopIndirect' in run++{-# ANN nestedUnguard ExpectError #-}+nestedUnguard :: Sig Int+nestedUnguard = run 0+  where run :: Int -> Sig Int+        run 0 = nestedUnguard+        run n = n ::: delay (run (n-1))++{-# ANN advDelay ExpectError #-}+advDelay :: O (O a) -> O a+advDelay y = delay (let x = adv y in adv x)++{-# ANN advDelay' ExpectError #-}+advDelay' :: O a -> a+advDelay' y = let x = adv y in x++{-# ANN dblAdv ExpectError #-}+dblAdv :: O (O a) -> O a+dblAdv y = delay (adv (adv y))++{-# ANN advScope ExpectError #-}+advScope :: O (O Int -> Int)+advScope = delay (\x -> adv x)++{-# ANN advScope' ExpectError #-}+advScope' :: O (Int -> Int)+advScope' = delay (let f x =  adv (delay x) in f)++{-# ANN grec ExpectError #-}+grec :: a+grec = grec++{-# ANN boxStream ExpectError #-}+boxStream :: Sig Int -> Box (Sig Int)+boxStream s = box (0 ::: future s)++{-# ANN boxStream' ExpectError #-}+boxStream' :: Sig Int -> Box (Sig Int)+boxStream' s = box s++{-# ANN intDelay ExpectError #-}+intDelay :: Int -> O Int+intDelay = delay++{-# ANN intAdv ExpectError #-}+intAdv :: O Int -> Int+intAdv = adv+++{-# ANN newDelay ExpectError #-}+newDelay :: a -> O a+newDelay x = delay x++{-# ANN mutualLoop ExpectError #-}+mutualLoop :: a+mutualLoop = mutualLoop'++{-# ANN mutualLoop' ExpectError #-}+mutualLoop' :: a+mutualLoop' = mutualLoop++{-# ANN constUnstable ExpectError #-}+constUnstable :: a -> Sig a+constUnstable a = run+  where run = a ::: delay run++{-# ANN mapUnboxed ExpectError #-}+mapUnboxed :: (a -> b) -> Sig a -> Sig b+mapUnboxed f = run+  where run (x ::: xs) = f x ::: delay (run (adv xs))++{-# ANN mapUnboxedMutual ExpectError #-}+mapUnboxedMutual :: (a -> b) -> Sig a -> Sig b+mapUnboxedMutual f = run+  where run (x ::: xs) = f x ::: delay (run' (adv xs))+        run' (x ::: xs) = f x ::: delay (run (adv xs))++-- mutual recursive pattern definitions are not supported+-- foo1,foo2 :: Box (a -> b) -> Sig a -> Sig b+-- (foo1,foo2) = (\ f (x ::: xs) -> unbox f x ::: (delay (foo2 f) <#> xs),+--                \ f (x ::: xs) -> unbox f x ::: (delay (foo1 f) <#> xs))++{-# ANN nestedPattern ExpectError #-}+nestedPattern :: Box (a -> b) -> Sig a -> Sig b+nestedPattern = foo1 where+  foo1,foo2 :: Box (a -> b) -> Sig a -> Sig b+  (foo1,foo2) = (\ f (x ::: xs) -> unbox f x ::: (delay (foo2 f (adv xs))),+                 \ f (x ::: xs) -> unbox f x ::: (delay (foo1 f (adv xs))))+++data Input = Input {jump :: !Bool, move :: Move}+data Move = StartLeft | EndLeft | StartRight | EndRight | NoMove++{-# ANN constS ExpectError #-}+-- Input is not a stable type (it is not strict). Therefore this+-- should not type check.+constS :: Input -> Sig Input+constS a = a ::: delay (constS a)+++{-# ANN incompatibleAdv ExpectError #-}+incompatibleAdv :: O Int -> O Int -> O Int+incompatibleAdv li lk = delay (adv li + adv lk)++{-# ANN incompatibleAdvSelect ExpectError #-}+incompatibleAdvSelect :: O Int -> O Int -> O Int+incompatibleAdvSelect li lk = delay (select li lk `seq` adv li)++{-# ANN intPlusOne ExpectError #-}+intPlusOne :: O Int -> Int+intPlusOne laterI = adv laterI + 1++{-# ANN weirdPlusTwo ExpectError #-}+weirdPlusTwo :: O Int -> O Int+weirdPlusTwo x = delay (+        let doAdd = box ((+) 1)+            x' = x+            newLater = delay (unbox doAdd (adv x'))+        in unbox doAdd (adv newLater)+    )++{-# ANN stutter ExpectError #-}+stutter :: Int -> Sig Int+stutter n = n ::: delay (n ::: delay (stutter (n+1)))++{-# ANN advAlias ExpectError #-}+advAlias :: O a -> a+advAlias = adv++{-# ANN selectAlias ExpectError #-}+selectAlias :: O a -> O b -> Select a b+selectAlias = select++{-# ANN partialSelectApp ExpectError #-}+partialSelectApp :: O a -> (O b -> Select a b)+partialSelectApp l = select l++{-# ANN doubleAdv ExpectError #-}+doubleAdv :: O Int -> O Int+doubleAdv li =  delay (adv li + adv li)++{-# ANN addDelay ExpectError #-}+addDelay :: O Int -> O Int -> O Int+addDelay x y = delay (adv x + adv y)+++{-# ANN main NotAsyncRattus #-}+main = putStrLn "This file should not type check"
+ test/WellTyped.hs view
@@ -0,0 +1,138 @@+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE StrictData #-}+{-# OPTIONS -fplugin=AsyncRattus.Plugin #-}++module Main (module Main) where++import AsyncRattus+import AsyncRattus.Signal+import Data.Set as Set++++{-# ANN module AsyncRattus #-}++boxedInt :: Box Int+boxedInt = box 8+++lambdaUnderDelay :: O Int -> O ((Int -> Int -> Int) :* Int)+lambdaUnderDelay d = delay ((\x _ -> x) :* adv d)++sneakyLambdaUnderDelay :: O Int -> O ((Int -> Int -> Int) :* Int)+sneakyLambdaUnderDelay d = delay (let f x _ =  x in f :* adv d)+++lambdaUnderDelay' :: Int -> O Int -> O ((Int -> Int) :* Int)+lambdaUnderDelay' x d = delay ((\_ -> x) :* adv d)++sneakyLambdaUnderDelay' :: Int -> O Int -> O ((Int -> Int) :* Int)+sneakyLambdaUnderDelay' x d = delay ((let f _ =  x in f) :* adv d)+++scanBox :: Box(b -> a -> Box b) -> b -> Sig a -> Sig b+scanBox f acc (a ::: as) =  unbox acc' ::: delay (scanBox f (unbox acc') (adv as))+  where acc' = unbox f acc a++sumBox :: Sig Int -> Sig Int+sumBox = scanBox (box (\x y -> box (x + y))) 0++strMap :: Box (a -> b) -> Sig a -> Sig b+strMap f (x ::: xs) = unbox f x ::: delay (strMap f (adv xs))++strMap' :: Box (a -> b) -> Sig a -> Sig b+strMap' f = run+  where run (x ::: xs) = unbox f x ::: delay (run (adv xs))++++-- local mutual recursive definition+nestedMutual :: Sig Int -> Sig Int+nestedMutual = lbar1 (box (+1))+  where lbar1 :: Box (a -> b) -> Sig a -> Sig b+        lbar1 f (x ::: xs) = unbox f x ::: (delay (lbar2 f (adv xs)))++        lbar2 :: Box (a -> b) -> Sig a -> Sig b+        lbar2 f  (x ::: xs) = unbox f x ::: (delay (lbar1 f (adv xs)))++++-- mutual recursive definition+bar1 :: Box (a -> b) -> Sig a -> Sig b+bar1 f (x ::: xs) = unbox f x ::: delay (bar2 f (adv xs))++bar2 :: Box (a -> b) -> Sig a -> Sig b+bar2 f  (x ::: xs) = unbox f x ::: delay (bar1 f (adv xs))++stableDelay :: Stable a => Box (a -> a -> a) -> a -> O a -> O a+stableDelay f v l = delay (unbox f v (adv l))++patternBinding :: Sig Int -> Sig Int+patternBinding str = (x + 1) ::: (delay (patternBinding (adv xs)))+  where (x ::: xs) = sumBox str+++data Input a = Input {jump :: !a, move :: !Move}+data Move = StartLeft | EndLeft | StartRight | EndRight | NoMove++++-- The compiler plugin should detect that Input is a stable type and+-- thus remains in scope under the delay.+constS :: Stable a => Input a -> O Int -> Sig (Int :* Input a)+constS a l = (0 :* a) ::: delay ((adv l :* a) ::: never)++-- make sure that unit is recognized as stable+constU :: () -> O () -> Sig (() :* ())+constU a l = (() :* a) ::: delay ((adv l :* a) ::: never)+++scan1 :: (Stable b) => Box(b -> a -> b) -> b -> Sig a -> Sig b+scan1 f acc (a ::: as) =  acc' ::: delay (scan1 f acc' (adv as))+  where acc' = unbox f acc a++scan2 :: (Stable b) => Box(b -> a -> b) -> b -> Sig a -> Sig b+scan2 f = run+  where run acc (a ::: as) = let acc' = unbox f acc a+                             in acc' ::: delay (run acc' (adv as))++scanSet :: Sig Int -> Sig (Set Int)+scanSet = scan1 (box (\ s x -> Set.insert x s)) Set.empty++myMap :: Sig Int -> Sig Int+myMap (x ::: xs) = (x + 1) ::: delay (fst' (myMap (adv xs) :* nats never))++nats :: O Int -> Sig Int+nats l = 0 ::: delay (let (n ::: ns) = myMap (nats never) in (adv l + n) ::: ns)++nestedDelay :: Sig a -> Sig a+nestedDelay (a ::: as) = a ::: delay (let x ::: xs = adv as in x ::: delay (nestedDelay (adv xs)))+++naiveIf :: Bool -> O a -> O a -> O (Bool :* a)+naiveIf b x y = delay (b :* adv (if b then x else y))++naiveIf' :: Bool -> O a -> O a -> O (Bool :* a)+naiveIf' b x y = delay (b :* adv later)+    where+        later = case b of+            True -> x+            False -> y++advUnderLambda :: O Int -> O (a -> Int)+advUnderLambda y = delay (\_ -> adv y)+++dblAdv :: O (O a) -> O (O a)+dblAdv y = delay (delay (adv (adv y)))++delayAdvUnderLambda :: O () -> O (O Int -> O Int)+delayAdvUnderLambda d = delay (adv d `seq` \x -> delay (adv x))++-- This function is leaky unless the single tick transformation is+-- performed+leaky :: Sig () -> (() -> Bool) -> Sig Bool+leaky (() ::: d) p = p () ::: delay (let d' = adv d in (leaky d' (\ _ -> current (leaky d' (\ _ -> True)))))++{-# ANN main NotAsyncRattus #-}+main = putStrLn "This file should just type check"