liquidhaskell-boot-0.9.2.5.0: src/Language/Haskell/Liquid/GHC/Play.hs
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE FlexibleInstances #-}
{-# OPTIONS_GHC -Wno-incomplete-patterns #-} -- TODO(#1918): Only needed for GHC <9.0.1.
module Language.Haskell.Liquid.GHC.Play where
import Prelude hiding (error)
import Control.Arrow ((***))
import qualified Data.HashMap.Strict as M
import qualified Data.List as L
import qualified Data.Maybe as Mb
import Liquid.GHC.API as Ghc hiding (panic, showPpr)
import Language.Haskell.Liquid.GHC.Misc ()
import Language.Haskell.Liquid.Types.Errors
import Language.Haskell.Liquid.Types.Variance
-------------------------------------------------------------------------------
-- | Positivity Checker -------------------------------------------------------
-------------------------------------------------------------------------------
-- If the type constructor T is in the input list and its data constructors Di, Dj
-- use T in non strictly positive positions,
-- then (T,(Di, Dj)) will appear in the result list.
getNonPositivesTyCon :: [TyCon] -> [(TyCon, [DataCon])]
getNonPositivesTyCon tcs = Mb.mapMaybe go (M.toList $ makeOccurrences tcs)
where
go (tc,dcocs) = case filter (\(_,occ) -> elem tc (negOcc occ)) dcocs of
[] -> Nothing
xs -> Just (tc, fst <$> xs)
-- OccurrenceMap maps type constructors to their TyConOccurrence.
-- for each of their data constructor. For example, for the below data definition
-- data T a = P (T a) | N (T a -> Int) | Both (T a -> T a) | None
-- the entry below should get generated
-- OccurrenceMap
-- = T |-> [(P, [
-- (P, TyConOcc [T] [])
-- (N, TyConOcc [Int] [T])
-- (Both, TyConOcc [T] [T])
-- (None, TyConOcc [] [])
-- ])]
-- For positivity check, ultimately we only care about self occurences,
-- but we keep track of all the TyCons for the mutually inductive data types.
-- We separate the occurences per data constructor only to provide better error messages.
type OccurrenceMap = M.HashMap TyCon [(DataCon, TyConOccurrence)]
data TyConOccurrence
= TyConOcc { posOcc :: [TyCon] -- TyCons that occur in positive positions
, negOcc :: [TyCon] -- TyCons that occur in negative positions
}
deriving Eq
instance Monoid TyConOccurrence where
mempty = TyConOcc mempty mempty
instance Semigroup TyConOccurrence where
TyConOcc p1 n1 <> TyConOcc p2 n2 = TyConOcc (L.nub (p1 <> p2)) (L.nub (n1 <> n2))
instance Outputable TyConOccurrence where
ppr (TyConOcc pos neg) = text "pos" <+> ppr pos <+> text "neg" <+> ppr neg
instance Outputable OccurrenceMap where
ppr m = ppr (M.toList m)
makeOccurrences :: [TyCon] -> OccurrenceMap
makeOccurrences tycons
= let m0 = M.fromList [(tc, map (\dc -> (dc, makeOccurrence tcInfo (dctypes dc))) (tyConDataCons tc))
| tc <- tycons']
-- fixpoint to find occurrences of mutually recursive data definitons
in fix (\m -> foldl merge m tycons') m0
where
fix f x = let x' = f x in if x == x' then x else fix f x'
tcInfo = M.fromList $ zip tycons' (makeTyConVariance <$> tycons')
merge m tc = M.update (mergeList m) tc m
mergeList m xs = Just [(dc, mergeApp m am) | (dc,am) <- xs]
mergeApp m (TyConOcc pos neg) =
let TyConOcc pospos posneg = mconcat (findOccurrence m <$> pos)
TyConOcc negpos negneg = mconcat (findOccurrence m <$> neg)
-- Keep positive, flip negative
in TyConOcc (L.nub (pos <> pospos <> negneg)) (L.nub (neg <> negpos <> posneg))
tycontypes tc = concatMap dctypes $ tyConDataCons tc
dctypes dc = irrelevantMult <$> dataConOrigArgTys dc
-- Construct the map for all TyCons that appear in the definitions
tycons' = L.nub (concatMap tcs (concatMap tycontypes tycons) ++ tycons)
tcs (TyConApp tc' ts) = tc': concatMap tcs ts
tcs (AppTy t1 t2) = tcs t1 ++ tcs t2
tcs (ForAllTy _ t) = tcs t
tcs (FunTy _ _ t1 t2) = tcs t1 ++ tcs t2
tcs (TyVarTy _ ) = []
tcs (LitTy _) = []
tcs (CastTy _ _) = []
tcs (CoercionTy _) = []
makeOccurrence :: M.HashMap TyCon VarianceInfo -> [Type] -> TyConOccurrence
makeOccurrence tcInfo = foldl (go Covariant) mempty
where
go :: Variance -> TyConOccurrence -> Type -> TyConOccurrence
go p m (TyConApp tc ts) = addOccurrence p tc
$ foldl (\m' (t, v) -> go (v <> p) m' t) m
(zip ts (M.lookupDefault (repeat Bivariant) tc tcInfo))
go _ m (TyVarTy _ ) = m
go _ m (AppTy t1 t2) = go Bivariant (go Bivariant m t1) t2
go p m (ForAllTy _ t) = go p m t
go p m (FunTy _ _ t1 t2) = go p (go (flipVariance p) m t1) t2
go _ m (LitTy _) = m
go _ m (CastTy _ _) = m
go _ m (CoercionTy _) = m
addOccurrence p tc (TyConOcc pos neg)
= case p of
Covariant -> TyConOcc (L.nub (tc:pos)) neg
Contravariant -> TyConOcc pos (L.nub (tc:neg))
Bivariant -> TyConOcc (L.nub (tc:pos)) (L.nub (tc:neg))
Invariant -> TyConOcc pos neg
findOccurrence :: OccurrenceMap -> TyCon -> TyConOccurrence
findOccurrence m tc = mconcat (snd <$> M.lookupDefault mempty tc m)
isRecursivenewTyCon :: TyCon -> Bool
isRecursivenewTyCon c
| not (isNewTyCon c)
= False
isRecursivenewTyCon c
= go t
where
t = snd $ newTyConRhs c
go (AppTy t1 t2) = go t1 || go t2
go (TyConApp c' ts) = c == c' || any go ts
go (ForAllTy _ t1) = go t1
go (FunTy _ _ t1 t2) = go t1 || go t2
go (CastTy t1 _) = go t1
go _ = False
dataConImplicitIds :: DataCon -> [Id]
dataConImplicitIds dc = [ x | AnId x <- dataConImplicitTyThings dc]
class Subable a where
sub :: M.HashMap CoreBndr CoreExpr -> a -> a
subTy :: M.HashMap TyVar Type -> a -> a
instance Subable CoreExpr where
sub s (Var v) = M.lookupDefault (Var v) v s
sub _ (Lit l) = Lit l
sub s (App e1 e2) = App (sub s e1) (sub s e2)
sub s (Lam b e) = Lam b (sub s e)
sub s (Let b e) = Let (sub s b) (sub s e)
sub s (Case e b t a) = Case (sub s e) (sub s b) t (map (sub s) a)
sub s (Cast e c) = Cast (sub s e) c
sub s (Tick t e) = Tick t (sub s e)
sub _ (Type t) = Type t
sub _ (Coercion c) = Coercion c
subTy s (Var v) = Var (subTy s v)
subTy _ (Lit l) = Lit l
subTy s (App e1 e2) = App (subTy s e1) (subTy s e2)
subTy s (Lam b e) | isTyVar b = Lam v' (subTy s e)
where v' = case M.lookup b s of
Just (TyVarTy v) -> v
_ -> b
subTy s (Lam b e) = Lam (subTy s b) (subTy s e)
subTy s (Let b e) = Let (subTy s b) (subTy s e)
subTy s (Case e b t a) = Case (subTy s e) (subTy s b) (subTy s t) (map (subTy s) a)
subTy s (Cast e c) = Cast (subTy s e) (subTy s c)
subTy s (Tick t e) = Tick t (subTy s e)
subTy s (Type t) = Type (subTy s t)
subTy s (Coercion c) = Coercion (subTy s c)
instance Subable Coercion where
sub _ c = c
subTy _ _ = panic Nothing "subTy Coercion"
instance Subable (Alt Var) where
sub s (Alt a b e) = Alt a (map (sub s) b) (sub s e)
subTy s (Alt a b e) = Alt a (map (subTy s) b) (subTy s e)
instance Subable Var where
sub s v | M.member v s = subVar $ s M.! v
| otherwise = v
subTy s v = setVarType v (subTy s (varType v))
subVar :: Expr t -> Id
subVar (Var x) = x
subVar _ = panic Nothing "sub Var"
instance Subable (Bind Var) where
sub s (NonRec x e) = NonRec (sub s x) (sub s e)
sub s (Rec xes) = Rec ((sub s *** sub s) <$> xes)
subTy s (NonRec x e) = NonRec (subTy s x) (subTy s e)
subTy s (Rec xes) = Rec ((subTy s *** subTy s) <$> xes)
instance Subable Type where
sub _ e = e
subTy = substTysWith
substTysWith :: M.HashMap Var Type -> Type -> Type
substTysWith s tv@(TyVarTy v) = M.lookupDefault tv v s
substTysWith s (FunTy aaf m t1 t2) = FunTy aaf m (substTysWith s t1) (substTysWith s t2)
substTysWith s (ForAllTy v t) = ForAllTy v (substTysWith (M.delete (binderVar v) s) t)
substTysWith s (TyConApp c ts) = TyConApp c (map (substTysWith s) ts)
substTysWith s (AppTy t1 t2) = AppTy (substTysWith s t1) (substTysWith s t2)
substTysWith _ (LitTy t) = LitTy t
substTysWith s (CastTy t c) = CastTy (substTysWith s t) c
substTysWith _ (CoercionTy c) = CoercionTy c
substExpr :: M.HashMap Var Var -> CoreExpr -> CoreExpr
substExpr s = go
where
subsVar v = M.lookupDefault v v s
go (Var v) = Var $ subsVar v
go (Lit l) = Lit l
go (App e1 e2) = App (go e1) (go e2)
go (Lam x e) = Lam (subsVar x) (go e)
go (Let (NonRec x ex) e) = Let (NonRec (subsVar x) (go ex)) (go e)
go (Let (Rec xes) e) = Let (Rec [(subsVar x', go e') | (x',e') <- xes]) (go e)
go (Case e b t alts) = Case (go e) (subsVar b) t [Alt c (subsVar <$> xs) (go e') | Alt c xs e' <- alts]
go (Cast e c) = Cast (go e) c
go (Tick t e) = Tick t (go e)
go (Type t) = Type t
go (Coercion c) = Coercion c
mapType :: (Type -> Type) -> Type -> Type
mapType f = go
where
go t@(TyVarTy _) = f t
go (AppTy t1 t2) = f $ AppTy (go t1) (go t2)
go (TyConApp c ts) = f $ TyConApp c (go <$> ts)
go (FunTy aaf m t1 t2) = f $ FunTy aaf m (go t1) (go t2)
go (ForAllTy v t) = f $ ForAllTy v (go t)
go t@(LitTy _) = f t
go (CastTy t c) = CastTy (go t) c
go (CoercionTy c) = f $ CoercionTy c
stringClassArg :: Type -> Maybe Type
stringClassArg t | isFunTy t
= Nothing
stringClassArg t
= case (tyConAppTyCon_maybe t, tyConAppArgs_maybe t) of
(Just c, Just [t']) | isStringClassName == tyConName c
-> Just t'
_ -> Nothing