{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
module Language.PureScript.TypeChecker.Monad where
import Prelude.Compat
import Control.Arrow (second)
import Control.Monad.Error.Class (MonadError(..))
import Control.Monad.State
import Control.Monad.Writer.Class (MonadWriter(..), censor)
import Data.Maybe
import qualified Data.Map as M
import Data.Text (Text)
import qualified Data.List.NonEmpty as NEL
import Language.PureScript.Crash (internalError)
import Language.PureScript.Environment
import Language.PureScript.Errors
import Language.PureScript.Kinds
import Language.PureScript.Names
import Language.PureScript.TypeClassDictionaries
import Language.PureScript.Types
data Substitution = Substitution
{ substType :: M.Map Int SourceType
, substKind :: M.Map Int SourceKind
}
emptySubstitution :: Substitution
emptySubstitution = Substitution M.empty M.empty
data CheckState = CheckState
{ checkEnv :: Environment
, checkNextType :: Int
, checkNextKind :: Int
, checkNextSkolem :: Int
, checkNextSkolemScope :: Int
, checkCurrentModule :: Maybe ModuleName
, checkSubstitution :: Substitution
, checkHints :: [ErrorMessageHint]
}
emptyCheckState :: Environment -> CheckState
emptyCheckState env = CheckState env 0 0 0 0 Nothing emptySubstitution []
type Unknown = Int
bindNames
:: MonadState CheckState m
=> M.Map (Qualified Ident) (SourceType, NameKind, NameVisibility)
-> m a
-> m a
bindNames newNames action = do
orig <- get
modify $ \st -> st { checkEnv = (checkEnv st) { names = newNames `M.union` (names . checkEnv $ st) } }
a <- action
modify $ \st -> st { checkEnv = (checkEnv st) { names = names . checkEnv $ orig } }
return a
bindTypes
:: MonadState CheckState m
=> M.Map (Qualified (ProperName 'TypeName)) (SourceKind, TypeKind)
-> m a
-> m a
bindTypes newNames action = do
orig <- get
modify $ \st -> st { checkEnv = (checkEnv st) { types = newNames `M.union` (types . checkEnv $ st) } }
a <- action
modify $ \st -> st { checkEnv = (checkEnv st) { types = types . checkEnv $ orig } }
return a
withScopedTypeVars
:: (MonadState CheckState m, MonadWriter MultipleErrors m)
=> ModuleName
-> [(Text, SourceKind)]
-> m a
-> m a
withScopedTypeVars mn ks ma = do
orig <- get
forM_ ks $ \(name, _) ->
when (Qualified (Just mn) (ProperName name) `M.member` types (checkEnv orig)) $
tell . errorMessage $ ShadowedTypeVar name
bindTypes (M.fromList (map (\(name, k) -> (Qualified (Just mn) (ProperName name), (k, ScopedTypeVar))) ks)) ma
withErrorMessageHint
:: (MonadState CheckState m, MonadError MultipleErrors m)
=> ErrorMessageHint
-> m a
-> m a
withErrorMessageHint hint action = do
orig <- get
modify $ \st -> st { checkHints = hint : checkHints st }
a <- rethrow (addHint hint) action
modify $ \st -> st { checkHints = checkHints orig }
return a
getHints :: MonadState CheckState m => m [ErrorMessageHint]
getHints = gets (reverse . checkHints)
rethrowWithPositionTC
:: (MonadState CheckState m, MonadError MultipleErrors m)
=> SourceSpan
-> m a
-> m a
rethrowWithPositionTC pos = withErrorMessageHint (positionedError pos)
warnAndRethrowWithPositionTC
:: (MonadState CheckState m, MonadError MultipleErrors m, MonadWriter MultipleErrors m)
=> SourceSpan
-> m a
-> m a
warnAndRethrowWithPositionTC pos = rethrowWithPositionTC pos . warnWithPosition pos
withTypeClassDictionaries
:: MonadState CheckState m
=> [NamedDict]
-> m a
-> m a
withTypeClassDictionaries entries action = do
orig <- get
let mentries =
M.fromListWith (M.unionWith (M.unionWith (<>)))
[ (mn, M.singleton className (M.singleton (tcdValue entry) (pure entry)))
| entry@TypeClassDictionaryInScope{ tcdValue = Qualified mn _, tcdClassName = className }
<- entries
]
modify $ \st -> st { checkEnv = (checkEnv st) { typeClassDictionaries = M.unionWith (M.unionWith (M.unionWith (<>))) (typeClassDictionaries . checkEnv $ st) mentries } }
a <- action
modify $ \st -> st { checkEnv = (checkEnv st) { typeClassDictionaries = typeClassDictionaries . checkEnv $ orig } }
return a
getTypeClassDictionaries
:: (MonadState CheckState m)
=> m (M.Map (Maybe ModuleName) (M.Map (Qualified (ProperName 'ClassName)) (M.Map (Qualified Ident) (NEL.NonEmpty NamedDict))))
getTypeClassDictionaries = typeClassDictionaries . checkEnv <$> get
lookupTypeClassDictionaries
:: (MonadState CheckState m)
=> Maybe ModuleName
-> m (M.Map (Qualified (ProperName 'ClassName)) (M.Map (Qualified Ident) (NEL.NonEmpty NamedDict)))
lookupTypeClassDictionaries mn = fromMaybe M.empty . M.lookup mn . typeClassDictionaries . checkEnv <$> get
lookupTypeClassDictionariesForClass
:: (MonadState CheckState m)
=> Maybe ModuleName
-> Qualified (ProperName 'ClassName)
-> m (M.Map (Qualified Ident) (NEL.NonEmpty NamedDict))
lookupTypeClassDictionariesForClass mn cn = fromMaybe M.empty . M.lookup cn <$> lookupTypeClassDictionaries mn
bindLocalVariables
:: (MonadState CheckState m)
=> [(Ident, SourceType, NameVisibility)]
-> m a
-> m a
bindLocalVariables bindings =
bindNames (M.fromList $ flip map bindings $ \(name, ty, visibility) -> (Qualified Nothing name, (ty, Private, visibility)))
bindLocalTypeVariables
:: (MonadState CheckState m)
=> ModuleName
-> [(ProperName 'TypeName, SourceKind)]
-> m a
-> m a
bindLocalTypeVariables moduleName bindings =
bindTypes (M.fromList $ flip map bindings $ \(pn, kind) -> (Qualified (Just moduleName) pn, (kind, LocalTypeVariable)))
makeBindingGroupVisible :: (MonadState CheckState m) => m ()
makeBindingGroupVisible = modifyEnv $ \e -> e { names = M.map (\(ty, nk, _) -> (ty, nk, Defined)) (names e) }
withBindingGroupVisible :: (MonadState CheckState m) => m a -> m a
withBindingGroupVisible action = preservingNames $ makeBindingGroupVisible >> action
preservingNames :: (MonadState CheckState m) => m a -> m a
preservingNames action = do
orig <- gets (names . checkEnv)
a <- action
modifyEnv $ \e -> e { names = orig }
return a
lookupVariable
:: (e ~ MultipleErrors, MonadState CheckState m, MonadError e m)
=> Qualified Ident
-> m SourceType
lookupVariable qual = do
env <- getEnv
case M.lookup qual (names env) of
Nothing -> throwError . errorMessage $ NameIsUndefined (disqualify qual)
Just (ty, _, _) -> return ty
getVisibility
:: (e ~ MultipleErrors, MonadState CheckState m, MonadError e m)
=> Qualified Ident
-> m NameVisibility
getVisibility qual = do
env <- getEnv
case M.lookup qual (names env) of
Nothing -> throwError . errorMessage $ NameIsUndefined (disqualify qual)
Just (_, _, vis) -> return vis
checkVisibility
:: (e ~ MultipleErrors, MonadState CheckState m, MonadError e m)
=> Qualified Ident
-> m ()
checkVisibility name@(Qualified _ var) = do
vis <- getVisibility name
case vis of
Undefined -> throwError . errorMessage $ CycleInDeclaration var
_ -> return ()
lookupTypeVariable
:: (e ~ MultipleErrors, MonadState CheckState m, MonadError e m)
=> ModuleName
-> Qualified (ProperName 'TypeName)
-> m SourceKind
lookupTypeVariable currentModule (Qualified moduleName name) = do
env <- getEnv
case M.lookup (Qualified (Just $ fromMaybe currentModule moduleName) name) (types env) of
Nothing -> throwError . errorMessage $ UndefinedTypeVariable name
Just (k, _) -> return k
getEnv :: (MonadState CheckState m) => m Environment
getEnv = checkEnv <$> get
getLocalContext :: MonadState CheckState m => m Context
getLocalContext = do
env <- getEnv
return [ (ident, ty') | (Qualified Nothing ident@Ident{}, (ty', _, Defined)) <- M.toList (names env) ]
putEnv :: (MonadState CheckState m) => Environment -> m ()
putEnv env = modify (\s -> s { checkEnv = env })
modifyEnv :: (MonadState CheckState m) => (Environment -> Environment) -> m ()
modifyEnv f = modify (\s -> s { checkEnv = f (checkEnv s) })
runCheck :: (Functor m) => StateT CheckState m a -> m (a, Environment)
runCheck = runCheck' (emptyCheckState initEnvironment)
runCheck' :: (Functor m) => CheckState -> StateT CheckState m a -> m (a, Environment)
runCheck' st check = second checkEnv <$> runStateT check st
guardWith :: (MonadError e m) => e -> Bool -> m ()
guardWith _ True = return ()
guardWith e False = throwError e
captureSubstitution
:: MonadState CheckState m
=> m a
-> m (a, Substitution)
captureSubstitution = capturingSubstitution (,)
capturingSubstitution
:: MonadState CheckState m
=> (a -> Substitution -> b)
-> m a
-> m b
capturingSubstitution f ma = do
a <- ma
subst <- gets checkSubstitution
return (f a subst)
withFreshSubstitution
:: MonadState CheckState m
=> m a
-> m a
withFreshSubstitution ma = do
orig <- get
modify $ \st -> st { checkSubstitution = emptySubstitution }
a <- ma
modify $ \st -> st { checkSubstitution = checkSubstitution orig }
return a
withoutWarnings
:: MonadWriter w m
=> m a
-> m (a, w)
withoutWarnings = censor (const mempty) . listen
unsafeCheckCurrentModule
:: forall m
. (MonadError MultipleErrors m, MonadState CheckState m)
=> m ModuleName
unsafeCheckCurrentModule = checkCurrentModule <$> get >>= \case
Nothing -> internalError "No module name set in scope"
Just name -> pure name