module Cryptol.TypeCheck.Monad
( module Cryptol.TypeCheck.Monad
, module Cryptol.TypeCheck.InferTypes
) where
import Cryptol.ModuleSystem.Name (FreshM(..),Supply)
import Cryptol.Parser.Position
import qualified Cryptol.Parser.AST as P
import Cryptol.TypeCheck.AST
import Cryptol.TypeCheck.Subst
import Cryptol.TypeCheck.Unify(mgu, Result(..), UnificationError(..))
import Cryptol.TypeCheck.InferTypes
import qualified Cryptol.TypeCheck.Solver.CrySAT as CrySAT
import Cryptol.Utils.PP(pp, (<+>), Doc, text, quotes)
import Cryptol.Utils.Panic(panic)
import qualified Control.Applicative as A
import Control.Monad.Fix(MonadFix(..))
import qualified Data.Map as Map
import qualified Data.Set as Set
import Data.Map (Map)
import Data.Set (Set)
import Data.List(find, minimumBy, groupBy, sortBy)
import Data.Maybe(mapMaybe)
import Data.Function(on)
import MonadLib hiding (mapM)
import GHC.Generics (Generic)
import Control.DeepSeq
import Prelude ()
import Prelude.Compat
data InferInput = InferInput
{ inpRange :: Range
, inpVars :: Map Name Schema
, inpTSyns :: Map Name TySyn
, inpNewtypes :: Map Name Newtype
, inpNameSeeds :: NameSeeds
, inpMonoBinds :: Bool
, inpSolverConfig :: SolverConfig
, inpPrimNames :: !PrimMap
, inpSupply :: !Supply
} deriving Show
data NameSeeds = NameSeeds
{ seedTVar :: !Int
, seedGoal :: !Int
} deriving (Show, Generic, NFData)
nameSeeds :: NameSeeds
nameSeeds = NameSeeds { seedTVar = 10, seedGoal = 0 }
data InferOutput a
= InferFailed [(Range,Warning)] [(Range,Error)]
| InferOK [(Range,Warning)] NameSeeds Supply a
deriving Show
runInferM :: TVars a => InferInput -> InferM a -> IO (InferOutput a)
runInferM info (IM m) = CrySAT.withSolver (inpSolverConfig info) $ \solver ->
do rec ro <- return RO { iRange = inpRange info
, iVars = Map.map ExtVar (inpVars info)
, iTVars = []
, iTSyns = fmap mkExternal (inpTSyns info)
, iNewtypes = fmap mkExternal (inpNewtypes info)
, iSolvedHasLazy = iSolvedHas finalRW
, iMonoBinds = inpMonoBinds info
, iSolver = solver
, iPrimNames = inpPrimNames info
}
(result, finalRW) <- runStateT rw
$ runReaderT ro m
let theSu = iSubst finalRW
defSu = defaultingSubst theSu
warns = [(r,apSubst theSu w) | (r,w) <- iWarnings finalRW ]
case iErrors finalRW of
[] ->
case (iCts finalRW, iHasCts finalRW) of
(cts,[])
| nullGoals cts
-> return $ InferOK warns
(iNameSeeds finalRW)
(iSupply finalRW)
(apSubst defSu result)
(cts,has) -> return $ InferFailed warns
$ dropErrorsFromSameLoc
[ ( goalRange g
, UnsolvedGoal False (apSubst theSu g)
) | g <- fromGoals cts ++ map hasGoal has
]
errs -> return $ InferFailed warns
$ dropErrorsFromSameLoc
[(r,apSubst theSu e) | (r,e) <- errs]
where
mkExternal x = (IsExternal, x)
rw = RW { iErrors = []
, iWarnings = []
, iSubst = emptySubst
, iExistTVars = []
, iNameSeeds = inpNameSeeds info
, iCts = emptyGoals
, iHasCts = []
, iSolvedHas = Map.empty
, iSupply = inpSupply info
}
dropErrorsFromSameLoc = map chooseBestError . groupBy ((==) `on` fst)
. sortBy (cmpRange `on` fst)
addErrorSize (r,e) = (length (show (pp e)), (r,e))
chooseBestError = snd . minimumBy (compare `on` fst) . map addErrorSize
cmpRange (Range x y z) (Range a b c) = compare (x,y,z) (a,b,c)
newtype InferM a = IM { unIM :: ReaderT RO (StateT RW IO) a }
data DefLoc = IsLocal | IsExternal
data RO = RO
{ iRange :: Range
, iVars :: Map Name VarType
, iTVars :: [TParam]
, iTSyns :: Map Name (DefLoc, TySyn)
, iNewtypes :: Map Name (DefLoc, Newtype)
, iSolvedHasLazy :: Map Int (Expr -> Expr)
, iMonoBinds :: Bool
, iSolver :: CrySAT.Solver
, iPrimNames :: !PrimMap
}
data RW = RW
{ iErrors :: ![(Range,Error)]
, iWarnings :: ![(Range,Warning)]
, iSubst :: !Subst
, iExistTVars :: [Map Name Type]
, iSolvedHas :: Map Int (Expr -> Expr)
, iNameSeeds :: !NameSeeds
, iCts :: !Goals
, iHasCts :: ![HasGoal]
, iSupply :: !Supply
}
instance Functor InferM where
fmap f (IM m) = IM (fmap f m)
instance A.Applicative InferM where
pure = return
(<*>) = ap
instance Monad InferM where
return x = IM (return x)
fail x = IM (fail x)
IM m >>= f = IM (m >>= unIM . f)
instance MonadFix InferM where
mfix f = IM (mfix (unIM . f))
instance FreshM InferM where
liftSupply f = IM $
do rw <- get
let (a,s') = f (iSupply rw)
set rw { iSupply = s' }
return a
io :: IO a -> InferM a
io m = IM $ inBase m
inRange :: Range -> InferM a -> InferM a
inRange r (IM m) = IM $ mapReader (\ro -> ro { iRange = r }) m
inRangeMb :: Maybe Range -> InferM a -> InferM a
inRangeMb Nothing m = m
inRangeMb (Just r) m = inRange r m
curRange :: InferM Range
curRange = IM $ asks iRange
recordError :: Error -> InferM ()
recordError e =
do r <- curRange
IM $ sets_ $ \s -> s { iErrors = (r,e) : iErrors s }
recordWarning :: Warning -> InferM ()
recordWarning w =
do r <- curRange
IM $ sets_ $ \s -> s { iWarnings = (r,w) : iWarnings s }
getSolver :: InferM CrySAT.Solver
getSolver =
do RO { .. } <- IM ask
return iSolver
getPrimMap :: InferM PrimMap
getPrimMap =
do RO { .. } <- IM ask
return iPrimNames
newGoal :: ConstraintSource -> Prop -> InferM Goal
newGoal goalSource goal =
do goalRange <- curRange
return Goal { .. }
newGoals :: ConstraintSource -> [Prop] -> InferM ()
newGoals src ps = addGoals =<< mapM (newGoal src) ps
getGoals :: InferM [Goal]
getGoals =
do goals <- applySubst =<< IM (sets $ \s -> (iCts s, s { iCts = emptyGoals }))
return (fromGoals goals)
addGoals :: [Goal] -> InferM ()
addGoals gs = IM $ sets_ $ \s -> s { iCts = foldl (flip insertGoal) (iCts s) gs }
collectGoals :: InferM a -> InferM (a, [Goal])
collectGoals m =
do origGs <- applySubst =<< getGoals'
a <- m
newGs <- getGoals
setGoals' origGs
return (a, newGs)
where
getGoals' = IM $ sets $ \ RW { .. } -> (iCts, RW { iCts = emptyGoals, .. })
setGoals' gs = IM $ sets $ \ RW { .. } -> ((), RW { iCts = gs, .. })
newHasGoal :: P.Selector -> Type -> Type -> InferM (Expr -> Expr)
newHasGoal l ty f =
do goalName <- newGoalName
g <- newGoal CtSelector (pHas l ty f)
IM $ sets_ $ \s -> s { iHasCts = HasGoal goalName g : iHasCts s }
solns <- IM $ fmap iSolvedHasLazy ask
return $ case Map.lookup goalName solns of
Just e1 -> e1
Nothing -> panic "newHasGoal" ["Unsolved has goal in result"]
addHasGoal :: HasGoal -> InferM ()
addHasGoal g = IM $ sets_ $ \s -> s { iHasCts = g : iHasCts s }
getHasGoals :: InferM [HasGoal]
getHasGoals = do gs <- IM $ sets $ \s -> (iHasCts s, s { iHasCts = [] })
applySubst gs
solveHasGoal :: Int -> (Expr -> Expr) -> InferM ()
solveHasGoal n e =
IM $ sets_ $ \s -> s { iSolvedHas = Map.insert n e (iSolvedHas s) }
newName :: (NameSeeds -> (a , NameSeeds)) -> InferM a
newName upd = IM $ sets $ \s -> let (x,seeds) = upd (iNameSeeds s)
in (x, s { iNameSeeds = seeds })
newGoalName :: InferM Int
newGoalName = newName $ \s -> let x = seedGoal s
in (x, s { seedGoal = x + 1})
newTVar :: Doc -> Kind -> InferM TVar
newTVar src k = newTVar' src Set.empty k
newTVar' :: Doc -> Set TVar -> Kind -> InferM TVar
newTVar' src extraBound k =
do bound <- getBoundInScope
let vs = Set.union extraBound bound
newName $ \s -> let x = seedTVar s
in (TVFree x k vs src, s { seedTVar = x + 1 })
newTParam :: Maybe Name -> Kind -> InferM TParam
newTParam nm k = newName $ \s -> let x = seedTVar s
in (TParam { tpUnique = x
, tpKind = k
, tpName = nm
}
, s { seedTVar = x + 1 })
newType :: Doc -> Kind -> InferM Type
newType src k = TVar `fmap` newTVar src k
unify :: Type -> Type -> InferM [Prop]
unify t1 t2 =
do t1' <- applySubst t1
t2' <- applySubst t2
case mgu t1' t2' of
OK (su1,ps) -> extendSubst su1 >> return ps
Error err ->
do case err of
UniTypeLenMismatch _ _ -> recordError (TypeMismatch t1' t2')
UniTypeMismatch s1 s2 -> recordError (TypeMismatch s1 s2)
UniKindMismatch k1 k2 -> recordError (KindMismatch k1 k2)
UniRecursive x t -> recordError (RecursiveType (TVar x) t)
UniNonPolyDepends x vs -> recordError
(TypeVariableEscaped (TVar x) vs)
UniNonPoly x t -> recordError (NotForAll x t)
return []
applySubst :: TVars t => t -> InferM t
applySubst t =
do su <- getSubst
return (apSubst su t)
getSubst :: InferM Subst
getSubst = IM $ fmap iSubst get
extendSubst :: Subst -> InferM ()
extendSubst su = IM $ sets_ $ \s -> s { iSubst = su @@ iSubst s }
varsWithAsmps :: InferM (Set TVar)
varsWithAsmps =
do env <- IM $ fmap (Map.elems . iVars) ask
fromEnv <- forM env $ \v ->
case v of
ExtVar sch -> getVars sch
CurSCC _ t -> getVars t
sels <- IM $ fmap (map (goal . hasGoal) . iHasCts) get
fromSels <- mapM getVars sels
fromEx <- (getVars . concatMap Map.elems) =<< IM (fmap iExistTVars get)
return (Set.unions fromEnv `Set.union` Set.unions fromSels
`Set.union` fromEx)
where
getVars x = fvs `fmap` applySubst x
lookupVar :: Name -> InferM VarType
lookupVar x =
do mb <- IM $ asks $ Map.lookup x . iVars
case mb of
Just t -> return t
Nothing ->
do mbNT <- lookupNewtype x
case mbNT of
Just nt -> return (ExtVar (newtypeConType nt))
Nothing -> do recordError $ UndefinedVariable x
a <- newType (text "type of" <+> pp x) KType
return $ ExtVar $ Forall [] [] a
lookupTVar :: Name -> InferM (Maybe Type)
lookupTVar x = IM $ asks $ fmap (TVar . tpVar) . find this . iTVars
where this tp = tpName tp == Just x
lookupTSyn :: Name -> InferM (Maybe TySyn)
lookupTSyn x = fmap (fmap snd . Map.lookup x) getTSyns
lookupNewtype :: Name -> InferM (Maybe Newtype)
lookupNewtype x = fmap (fmap snd . Map.lookup x) getNewtypes
existVar :: Name -> Kind -> InferM Type
existVar x k =
do scopes <- iExistTVars <$> IM get
case msum (map (Map.lookup x) scopes) of
Just ty -> return ty
Nothing ->
case scopes of
[] ->
do recordError $ ErrorMsg $
text "Undefined type" <+> quotes (pp x)
newType (text "undefined existential type varible" <+>
quotes (pp x)) k
sc : more ->
do ty <- newType (text "existential type variable"
<+> quotes (pp x)) k
IM $ sets_ $ \s -> s{ iExistTVars = Map.insert x ty sc : more }
return ty
getTSyns :: InferM (Map Name (DefLoc,TySyn))
getTSyns = IM $ asks iTSyns
getNewtypes :: InferM (Map Name (DefLoc,Newtype))
getNewtypes = IM $ asks iNewtypes
getTVars :: InferM (Set Name)
getTVars = IM $ asks $ Set.fromList . mapMaybe tpName . iTVars
getBoundInScope :: InferM (Set TVar)
getBoundInScope = IM $ asks $ Set.fromList . map tpVar . iTVars
getMonoBinds :: InferM Bool
getMonoBinds = IM (asks iMonoBinds)
checkTShadowing :: String -> Name -> InferM ()
checkTShadowing this new =
do ro <- IM ask
rw <- IM get
let shadowed =
do _ <- Map.lookup new (iTSyns ro)
return "type synonym"
`mplus`
do guard (new `elem` mapMaybe tpName (iTVars ro))
return "type variable"
`mplus`
do _ <- msum (map (Map.lookup new) (iExistTVars rw))
return "type"
case shadowed of
Nothing -> return ()
Just that ->
recordError $ ErrorMsg $
text "Type" <+> text this <+> quotes (pp new) <+>
text "shadows an existing" <+>
text that <+> text "with the same name."
withTParam :: TParam -> InferM a -> InferM a
withTParam p (IM m) =
do case tpName p of
Just x -> checkTShadowing "variable" x
Nothing -> return ()
IM $ mapReader (\r -> r { iTVars = p : iTVars r }) m
withTParams :: [TParam] -> InferM a -> InferM a
withTParams ps m = foldr withTParam m ps
withTySyn :: TySyn -> InferM a -> InferM a
withTySyn t (IM m) =
do let x = tsName t
checkTShadowing "synonym" x
IM $ mapReader (\r -> r { iTSyns = Map.insert x (IsLocal,t) (iTSyns r) }) m
withNewtype :: Newtype -> InferM a -> InferM a
withNewtype t (IM m) =
IM $ mapReader
(\r -> r { iNewtypes = Map.insert (ntName t) (IsLocal,t)
(iNewtypes r) }) m
withVarType :: Name -> VarType -> InferM a -> InferM a
withVarType x s (IM m) =
IM $ mapReader (\r -> r { iVars = Map.insert x s (iVars r) }) m
withVarTypes :: k = TVar `a>withVarTypes ) }) m
) }?1n> }?1n> }}?1n> }TVar `a>IM $ mapReader (Or }TVar IsLocal-- | The su'>ro)
< class='hs-varid'>return "type variable"
`mplus`
do _ do [oass='hs-comment'>-- | The su'>ro)
< class='hs-varid'>return"Unexpected value in record selection"
n class='hs-conid'>Functor InferM }?1n> }TVar/span>?1n> }newGs <- getGoals
(hlahVarrd'>caseFunctor[oass='hs-comment'>-- | The su'>ropan>?1n> }TVar/span>?1n> <- marrd'>caseFunctor[oass='hs-comment'>-- | The su'>ropan>?1n>pan>?1n> }-paramNeeds ->=-- | The su'>rowithVarTypes ->==withVarTypes ->=withVarTypes
}
) =span class='hs-layout'>}?1n> -- | The su'>roof
-- | The su'>n> =span class='h
-- | The su'>n> =span class='h
- name."of-> InferM return "type variable"
return > =span class='h
-- | The su'>n> =span class='h
- name."InferM (Set IM(Set
}Set(su1[oass='hs-comment'>-- | The class='hs-keyglyph'>-> InferM extcomment'>-- | The class='hs-keyglyph'>-> }-- |>->id'>extcomment'>-- | Thean> }withVarTypes ->==withVarTypes ->(InferM
< line-594"> n class='hs-conid'ut'>}-varid'>s ->=s ->=< name."
,tSet( ->(InferM
< line-594"> n clas cla0">< line-594"> (,InferM
< line-594"> n clas cla0">< line-594"> su
text > Ma/span>t) tetttttttttttttes quotess-varid'>k1 tetttttttttttttes quotess-varid'>k1 tetttttttttttttes quotess-var cyout'>,InferM this new =
a name="line-566"> this new rid'>m
->< line-594">.-varid'>extcomment'>-- | The > tetttttttttttttes < line-594"> (insert ( n clas cla0">< line-594"> tetiDPtttttttttes quotess-var cyr new =
a name="line-566"> ?1n> }?1n> fmap snd . Map.}?1n> fmap 186id'>quotess-var cyrv of
186id'>qu-varmsout'>}?1n> fmap 186id'>qu-varmsout'>}?1n> fmap s-var cyrv of
1 s='hs-varid'>fmap of s-keyword's='hs-keyglyph'><- IM IM return { iTSyns pan> mapReader
fmap pan> mapspan> (recordError $ quotess-var cyr new x quotess-var cyr pan> mapReads-var cyr ?1n> s-var cyrv s-var cyr ?1n> s-vhs-varid'>fmap pan> makan>s-var cyr pan> makan>s-var cyr n class='hs-keyglyph'>&lass='hs-varid'>fromSels <- Name)
getTVars = insert ( of
1 s='hs-varid'>fmap recordError $ pan> makan>s-var cyr n class='hs-keyglyph'>lass='hs-var /span>s-vhs-varid'>fss='hs-keyglyph'>pan> 1 s='hs-varid'>fmap n class='hs-keyglyph'>lass='hs-var /span>s-vhs-varid'>fss='hs-keyglyph'>pan> 1 s='hn> ?1n> (1 s='hn> getTclass='hs-layout'>(mapReader ( r) })recordError $ pan> makan>sass='hs-varid'>new) (iExistTVars ?1n> ) (iExistTVars ?1n> (caseFunctor[oass='hs-comment'>-- | The su'>ropan>?1n> ) (iExistTVars ?1n> ?1n> ?1n> pan> mas='hs-varid'>recordError s (IM (IM caseFunctor[oass='hs-comment'>-- | The su'>ropan>?of
ss name="line-179"> pan> mas='hs-varid'>recordError s (lass='hs-var /span>s-vhs-varid'e, Located Type, Type)
-- | Infer the type of one arm of a list comprehs='hs-varttttttttttclaclass='hs-varid'yns ?1n> recordError=
a npan> = nan s='hstttttttttttttt='hs-varid'>recordError=
a npan> = nan s='hstttttttttttttt='hs-varid'hs-kean class='hs-layout'>(1 s='hn> <`'>nan s='hsttttttttttteyglyph'>lass='hs-var /span>s-vhs-varid'>fss='hs-keyglyph'>pan> 1 s=an s='hstttttttttttttt='hs-varid'>recordError=
a npan> Set
}Set(su1[oass='hs-comment'>-- | The classs-layout'>) where
TVars
pan s='hstttttttttttttttttttts-va'hs-kean class='hs-layout'>(TVars
pan sn>
a npan> new) () (happy_x_1
pan clatttan>
pan clatttan>( (IM $ sets $iExistTVars ass='hs-layout'>( 0hs-layou $ 0hs-layou $pan clatttan>[]
-- | The classs-layout'>) wheres-varid'>iExistTVars ?5an clatttan> Control.DeepSeq.NFss='su-var -hs-varid'e,>
pan clatttan>[]layou manpan> = -- | The su'>roof
-- | The su'>n> =span class='h
-- | The su'>n> =span class='h
pan s='hstttttttttttttttttttts-va'hs-kean class='hs-layout'>(TVars
pan s='hstttttttttttttttttttts-va'hs-kean class='hs-layoutyis, Inc.
pan s='hssstttttttttttttttttts-va'hs-kean class='hs-layout'>(TVars
( -- | The su'>ro->
(
(TVarslasWarning ->s-vhs-varid'>fspan>) getTSynsass='hs-layout'>(s
pan s='hstss='hs-layout'>) -- | The su'>n> =span class='h
= claepalass='hs-kssis, Inc.
ass='hs-layout'>(=
do case tpName iRange $ sets_ $ \
pan s='hsslass='hs-ncpss='hs-lapan class='su-vae="line-576"> do casepan cl'hs-kean claoIin> quotess-var cyout'>,pan s='hsslass='hs-ncpclass='hpan> =
do -- | Th.Classes.Eqarid'>manpan>$= do =< name."
$ \
beI='hs-keyword'>of.
panseyglyph'>=< name."
pan s='hstttttttttttttttttttts-va'hs-kean class='hs-la-comment'>-- License : class='hs-varop'>$
do ==== Just x
s='hstttttttttttttttttttts-va'hs-kean class='hs-layoutspan>
ma="line-569"> text > Ma/span> ?1n> text > Ma/span> ->=ro
pan s='hstttttttttttttttttttts-va'hs-kean class='hs-la-commens-varid'>fspan> IM "type synonym"
'>quotess-var cyout'>,panss='hs-var /span>s-vhs-varid'e, Located Type, Type)
-- | Infer the type of one arm of a list comprehs='hs-varttttttttttclaclass='hs-vtpan> [oass='hs-comment'>-- | The su'>ro
'>quotess-var cyout'>,-- | The classs-layout'>) -- | The classs-layout'>) tpName Cryptol.TypeCheck.Instantiate text > name="line-4">pan s='hstss='hs-layout'>) 1-layout'>) wheres-varid'>iExistTVars tiate
Data() id'ff\xff\xffs='hs-varid'>tiateData[oass='hs-ns-varid'>"> id'ff\xff\xffs='hs-varid'>tiatepan s='hssstttttttttttttttttts-va'hs-kean class='hs-laspan> { iTSyns IM []
[oass='hs-ns-varid'>">-- | The jf a list comprehs='hs-vartttttt comprehs='hs-vartttttt comprehs='hs-vartttttt comprehs='hs-varttt IM fspan> IM <12ter c-lapan class='su-vae="line-576"> name="line-4">pan s='hslspan> ) InferM ta/p>{ iTSyns e'e, Located Type-va'hs-keyglyph'>ss='hs-keyglyph'>=
a name="line-566"> this =
a name= 186id'>qu-varmsout'>}?1n> fmapquotess-v /span>s-var cyrv