#ifndef MIN_VERSION_base
#define MIN_VERSION_base(x,y,z) 0
#endif
module Ermine.Unification.Meta
(
Meta(Meta)
, metaId, metaValue, metaRef, metaHint
, MetaT, TypeM
, MetaK, KindM
, Depth, metaDepth, depthInf
, Rank, metaRank, bumpRank
, newMeta, newShallowMeta
, readMeta
, writeMeta
, semiprune
, zonk
, zonk_
, zonkWith
, zonkScope
, zonkScope_
, hasSkolems
, checkSkolems
, checkEscapes
, checkDistinct
, MetaEnv
, HasMetaEnv(..)
, MonadMeta(..)
, viewMeta
, M, runM, runM_, ioM
, throwM
, fresh
, remember
) where
import Bound
import Control.Applicative
import Control.Exception
import Control.Lens
import Data.IntSet.Lens (setOf)
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.Trans.Maybe
import Control.Monad.ST (ST, runST, stToIO)
import Control.Monad.ST.Class
import Control.Monad.ST.Unsafe
import Control.Monad.Trans
import Control.Monad.State.Lazy as Lazy
import Control.Monad.State.Strict as Strict
import Control.Monad.Writer.Class
import Data.Foldable (for_, traverse_)
import Data.Function (on)
import Data.IntMap
import Data.Monoid
import Data.STRef
import qualified Data.Set as Set
import Data.Text (pack)
import Data.Traversable
import Data.Word
import Ermine.Diagnostic
import Ermine.Syntax
import Ermine.Syntax.Hint
import Ermine.Syntax.Type
import Ermine.Syntax.Kind
import Ermine.Unification.Sharing
#ifdef HLINT
#endif
type Depth = Int
depthInf :: Depth
depthInf = maxBound
type Rank = Word
bumpRank :: STRef s Rank -> ST s ()
#if MIN_VERSION_base(4,6,0)
bumpRank w = modifySTRef' w (+1)
#else
bumpRank w = do
n <- readSTRef w
let n' = n + 1
n' `seq` writeSTRef w n'
#endif
data Meta s f a
= Meta { _metaValue :: a
, _metaHint :: Hint
, _metaId :: !Int
, _metaRef :: !(STRef s (Maybe (f (Meta s f a))))
, _metaDepth :: !(STRef s Depth)
, _metaRank :: !(STRef s Rank)
}
makeLenses ''Meta
type MetaT s = Meta s (Type (MetaK s)) (KindM s)
type TypeM s = Type (MetaK s) (MetaT s)
type MetaK s = Meta s Kind Bool
type KindM s = Kind (MetaK s)
data MetaEnv s = MetaEnv
{ _metaRendering :: Rendering
, _metaFresh :: !(STRef s Int)
, _metaMemory :: STRef s (IntMap (TypeM s))
}
makeClassy ''MetaEnv
instance HasRendering (MetaEnv s) where
rendering = metaRendering
instance Show a => Show (Meta s f a) where
showsPrec d (Meta a _ i _ _ _) = showParen (d > 10) $
showString "Meta " . showsPrec 11 a . showChar ' ' . showsPrec 11 i . showString " ..."
instance Eq (Meta s f a) where
(==) = (==) `on` view metaId
instance Ord (Meta s f a) where
compare = compare `on` view metaId
newMeta :: MonadMeta s m => a -> Hint -> m (Meta s f a)
newMeta a h = Meta a h <$> fresh
<*> liftST (newSTRef Nothing)
<*> liftST (newSTRef depthInf)
<*> liftST (newSTRef 0)
newShallowMeta :: MonadMeta s m => Depth -> a -> Hint -> m (Meta s f a)
newShallowMeta d a h =
Meta a h <$> fresh
<*> liftST (newSTRef Nothing)
<*> liftST (newSTRef d)
<*> liftST (newSTRef 0)
readMeta :: MonadMeta s m => Meta s f a -> m (Maybe (f (Meta s f a)))
readMeta (Meta _ _ _ r _ _) = liftST $ readSTRef r
writeMeta :: MonadMeta s m => Meta s f a -> f (Meta s f a) -> m ()
writeMeta (Meta _ _ _ r _ _) a = liftST $ writeSTRef r (Just a)
semiprune :: (Variable f, Monad f, MonadMeta s m) => f (Meta s f a) -> m (f (Meta s f a))
semiprune t0 = case preview _Var t0 of
Just v0 -> loop t0 v0
Nothing -> return t0
where
loop t1 v1 = readMeta v1 >>= \mb -> case mb of
Nothing -> return t1
Just t -> case preview _Var t of
Nothing -> return t
Just v -> do
fv <- loop t v
writeMeta v1 fv
return fv
zonk :: (MonadMeta s m, MonadWriter Any m, Traversable f, Monad f) => f (Meta s f a) -> m (f (Meta s f a))
zonk fs = zonkWith fs $ \_ -> return ()
zonk_ :: (MonadMeta s m, Traversable f, Monad f) => f (Meta s f a) -> m (f (Meta s f a))
zonk_ fs = runSharing fs $ zonk fs
zonkWith :: (MonadMeta s m, MonadWriter Any m, Traversable f, Monad f) => f (Meta s f a) -> (Meta s f a -> m ()) -> m (f (Meta s f a))
zonkWith fs0 tweak = go fs0 where
go fs = fmap join . for fs $ \m -> do
tweak m
readMeta m >>= \mv -> case mv of
Nothing -> return (return m)
Just fmf -> do
tell $ Any True
r <- go fmf
r <$ writeMeta m r
zonkScope :: (MonadMeta s m, MonadWriter Any m, Traversable f, Monad f)
=> Scope b f (Meta s f a) -> m (Scope b f (Meta s f a))
zonkScope (Scope e) = Scope <$> (traverse.traverse) zonk e
zonkScope_ :: (MonadMeta s m, Traversable f, Monad f)
=> Scope b f (Meta s f a) -> m (Scope b f (Meta s f a))
zonkScope_ (Scope e) = Scope <$> (traverse.traverse) zonk_ e
checkSkolems
:: MonadMeta s m
=> Maybe Depth -> LensLike' m ts (TypeM s) -> [MetaT s] -> ts -> m ts
checkSkolems md trav sks ts = do
for_ md $ checkEscapes ?? sks
hasSkolems trav sks ts
hasSkolems :: (Traversable f, Monad f, MonadMeta s m) => LensLike' m ts (f (Meta s f a)) -> [Meta s f a] -> ts -> m ts
hasSkolems trav sks = trav $ \t -> runSharing t $ zonkWith t tweak where
skids = setOf (traverse.metaId) sks
tweak v = when (has (ix $ v^.metaId) skids) $ fail "returning skolem"
checkEscapes
:: MonadMeta s m
=> Depth -> [Meta s f a] -> m ()
checkEscapes d =
traverse_ $ \s -> liftST (readSTRef $ s^.metaDepth) >>= \d' ->
when (d' < d) $ fail "skolem escapes to environment"
checkDistinct
:: (Traversable f, Monad f, Variable f, MonadMeta s m)
=> [Meta s f a] -> m [Meta s f a]
checkDistinct xs = do
ys <- for xs $ withSharing zonk . return
zs <- for ys $ maybe (fail "bound skolem") pure . preview _Var
zs <$ unless (Set.size (Set.fromList zs) == length zs) (fail "indistinct skolems")
data Result a
= Error !Diagnostic
| OK !Int a
instance Functor Result where
fmap f (OK n a) = OK n (f a)
fmap _ (Error d) = Error d
class (Applicative m, MonadST m, s ~ World m) => MonadMeta s m | m -> s where
askMeta :: m (MetaEnv s)
#ifndef HLINT
default askMeta :: (m ~ t n, MonadTrans t, MonadMeta s n) => m (MetaEnv s)
askMeta = lift askMeta
#endif
localMeta :: (MetaEnv s -> MetaEnv s) -> m a -> m a
viewMeta :: MonadMeta s m => Getting a (MetaEnv s) a -> m a
viewMeta l = view l <$> askMeta
instance MonadMeta s m => MonadMeta s (Lazy.StateT t m) where
localMeta f (Lazy.StateT m) = Lazy.StateT (localMeta f . m)
instance MonadMeta s m => MonadMeta s (Strict.StateT t m) where
localMeta f (Strict.StateT m) = Strict.StateT (localMeta f . m)
instance MonadMeta s m => MonadMeta s (SharingT m) where
localMeta f (SharingT m) = SharingT (localMeta f m)
newtype M s a = M { unM :: MetaEnv s -> ST s a }
instance Functor (M s) where
fmap f (M m) = M (fmap f . m)
instance Applicative (M s) where
pure = return
(<*>) = ap
instance Monad (M s) where
return = M . const . return
M m >>= k = M $ \ e -> do
a <- m e
unM (k a) e
fail s = M $ \e -> unsafeIOToST $ throwIO $! die e (pack s)
instance MonadST (M s) where
type World (M s) = s
liftST m = M $ const m
instance MonadMeta s (M s) where
askMeta = M $ \e -> return e
localMeta f (M m) = M (m . f)
instance MonadMeta s m => MonadMeta s (MaybeT m) where
askMeta = MaybeT $ Just <$> askMeta
localMeta f = MaybeT . localMeta f . runMaybeT
instance Monoid m => Monoid (M s m) where
mempty = pure mempty
mappend = liftA2 mappend
catchingST :: Getting (First a) SomeException a -> ST s r -> (a -> ST s r) -> ST s r
catchingST l m h = unsafeIOToST $ catchJust (preview l) (unsafeSTToIO m) (unsafeSTToIO . h)
throwM :: MonadST m => Diagnostic -> m a
throwM d = liftST $ unsafeIOToST (throwIO d)
runM :: Rendering -> (forall s. M s a) -> Either Diagnostic a
runM r m = runST $ do
i <- newSTRef 0
z <- newSTRef mempty
catchingST _Diagnostic (Right <$> unM m (MetaEnv r i z)) (return . Left)
runM_ :: Rendering -> (forall s. M s a) -> a
runM_ r m = runST $ do
i <- newSTRef 0
z <- newSTRef mempty
unM m (MetaEnv r i z)
ioM :: MonadIO m => Rendering -> (forall s. M s a) -> m a
ioM r m = liftIO $ stToIO $ do
i <- newSTRef 0
z <- newSTRef mempty
unM m (MetaEnv r i z)
fresh :: MonadMeta s m => m Int
fresh = do
s <- viewMeta metaFresh
liftST $ do
i <- readSTRef s
writeSTRef s $! i + 1
return i
remember :: MonadMeta s m => Int -> TypeM s -> m ()
remember i t = do
s <- viewMeta metaMemory
liftST $ do
m <- readSTRef s
writeSTRef s $! m & at i ?~ t