packages feed

WidgetRattus-0.1: src/AsyncRattus/Plugin/Dependency.hs

{-# 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


import GHC.Plugins
import GHC.Data.Bag
import GHC.Hs.Type


import GHC.Hs.Extension
import GHC.Hs.Expr
import GHC.Hs.Pat
import GHC.Hs.Binds

#if __GLASGOW_HASKELL__ >= 904
import GHC.Parser.Annotation
#else
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__ < 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

getConBV (PrefixCon _ ps) = getBV ps
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__ < 904
instance HasBV CoPat where
  getBV CoPat {co_pat_inner = p} = getBV p
#else
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
  getBV (ConPat {pat_args = con}) = getConBV con
  getBV (SigPat _ p _) = getBV p

#if __GLASGOW_HASKELL__ < 904
instance HasBV NoExtCon where
  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
  
instance HasFV a => HasFV (Match GhcTc a) where
  getFV Match {m_grhss = rhss} = getFV rhss

instance HasFV (HsTupArg GhcTc) where
  getFV (Present _ e) = getFV e
  getFV Missing {} = Set.empty

instance HasFV a => HasFV (GRHS GhcTc a) where
  getFV (GRHS _ g b) = getFV g `Set.union` getFV b

instance HasFV a => HasFV (GRHSs GhcTc a) where
  getFV GRHSs {grhssGRHSs = rhs, grhssLocalBinds = lbs} =
    getFV rhs `Set.union` getFV lbs


instance HasFV (HsLocalBindsLR GhcTc GhcTc) where
  getFV (HsValBinds _ bs) = getFV bs
  getFV (HsIPBinds _ bs) = getFV bs
  getFV EmptyLocalBinds {} = Set.empty
  
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

instance HasFV (IPBind GhcTc) where
  getFV (IPBind _ _ e) = getFV e

instance HasFV (HsIPBinds GhcTc) where
  getFV (IPBinds _ bs) = getFV bs
  
instance HasFV (ApplicativeArg GhcTc) where
  getFV ApplicativeArgOne { arg_expr = e }     = getFV e
  getFV ApplicativeArgMany {app_stmts = es, final_expr = e} = getFV es `Set.union` getFV e

instance HasFV (ParStmtBlock GhcTc GhcTc) where
  getFV (ParStmtBlock _ es _ _) = getFV es
  
instance HasFV a => HasFV (StmtLR GhcTc GhcTc a) where
  getFV (LastStmt _ e _ _) = getFV e
  getFV (BindStmt _ _ e) = getFV e
  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


instance HasFV (HsRecFields GhcTc (GenLocated SrcSpanAnnA (HsExpr GhcTc))) where
  getFV HsRecFields{rec_flds = fs} = getFV fs

#if __GLASGOW_HASKELL__ >= 904
instance HasFV (HsFieldBind o (GenLocated SrcSpanAnnA (HsExpr GhcTc))) where
#else
instance HasFV (HsRecField' o (GenLocated SrcSpanAnnA (HsExpr 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
#else
  getFV (HsCmdLamCase _ mg) = getFV mg
#endif
  getFV (XCmd e) = getFV e


instance (HasFV a, HasFV b) => HasFV (Either a b) where
  getFV (Left x) = getFV x
  getFV (Right x) = getFV x

#if __GLASGOW_HASKELL__ >= 908
instance HasFV (LHsRecUpdFields GhcTc) where
  getFV RegularRecUpdFields {recUpdFields = x} = getFV x
  getFV OverloadedRecUpdFields {olRecUpdFields = x} = getFV x
#endif

instance HasFV (HsCmdTop GhcTc) where
  getFV (HsCmdTop _ cmd) = getFV cmd

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
  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` getFV fs
  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  
#else
  getFV (HsAppType _ e _) = getFV e
  getFV (ExprWithTySig _ e _) = getFV e  
#endif
  getFV (HsIf _ e1 e2 e3) = getFV e1 `Set.union` getFV e2 `Set.union` getFV e3
  getFV (HsPragE _ _ e) = getFV e


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