blob: c078e886ca365b07c131abbad788b2e47418f8d8 [file] [log] [blame] [edit]
-- |
-- Module : Minigent.Reorganiser
-- Copyright : (c) Data61 2018-2019
-- Commonwealth Science and Research Organisation (CSIRO)
-- ABN 41 687 119 230
-- License : BSD3
--
-- this compiler pass is responsible for taking the raw output of the parser
-- and processing it into something the rest of the compiler can understand.
--
-- Specifically, it alerts about duplicate definitions, warns about missing
-- type signatures, and does a pass over each expression to resolve
-- function names (which are indistinguishable from variables to the parser)
-- and check that the types mentioned in the expression only includes
-- in-scope polymorphic type variables.
--
-- May be used qualified or unqualified.
module Minigent.Reorganiser
( reorg
, Error
)
where
import Minigent.Syntax
import Minigent.Syntax.Utils
import Minigent.Environment
import qualified Minigent.Syntax.Utils.Row as Row
import Minigent.Syntax.Utils (mapRecPars)
import Control.Monad.Trans.Writer.Strict
import qualified Data.Map as M
import qualified Data.Set as S
import Data.List (nub, (\\), intersperse )
type Error = String
sanityCheckType :: [VarName] -> Type -> Writer [Error] ()
sanityCheckType tvs t = do
let leftovers = nub (typeVariables t) \\ tvs
let nsp = nonStrictlyPositiveVars t
if leftovers /= [] then
tell ["Type variables used unquantified:" ++ concat (intersperse ", " leftovers)]
else if nsp /= [] then
tell ["Recursive parameters occuring non-strictly positive: " ++ concat (intersperse ", " nsp)]
else return ()
sanityCheckExpr :: GlobalEnvironments -> [VarName] -> [VarName] -> Expr -> Writer [Error] Expr
sanityCheckExpr envs tvs vs exp = check vs exp
where
check vs exp = case exp of
PrimOp o es -> PrimOp o <$> mapM (check vs) es
Con cn e -> Con cn <$> check vs e
Var n -> do
if n `elem` vs
then pure exp
else case M.lookup n (types envs) of
Nothing -> tell ["Variable out of scope: " ++ n] >> pure exp
Just _ -> pure (TypeApp n [])
TypeApp f ts -> do
case M.lookup f (types envs) of
Nothing -> tell ["Function not defined: " ++ f]
Just (Forall qts _ _) -> do
if length ts > length qts
then tell ["Too many type arguments given to: " ++ f]
else mapM_ (sanityCheckType tvs) ts
pure exp
Sig e t -> do
sanityCheckType tvs t
e' <- check vs e
pure (Sig e' t)
Apply e1 e2 -> Apply <$> check vs e1 <*> check vs e2
Struct fs -> Struct <$> mapM (\(f,e) -> (,) f <$> check vs e) fs
If e1 e2 e3 -> If <$> check vs e1 <*> check vs e2 <*> check vs e3
Let v e1 e2 -> Let v <$> check vs e1 <*> check (v:vs) e2
LetBang bvs v e1 e2 -> do
let leftovers = nub bvs \\ vs
if null leftovers
then return ()
else tell ["Let! applied to out of scope vars: " ++ concat (intersperse ", " leftovers)]
LetBang bvs v <$> check vs e1 <*> check (v:vs) e2
Take r f v e1 e2 -> Take r f v <$> check vs e1 <*> check (r:v:vs) e2
Put e1 f e2 -> Put <$> check vs e1 <*> pure f <*> check vs e2
Case e c v1 e1 v2 e2 -> Case <$> check vs e
<*> pure c <*> pure v1 <*> check (v1:vs) e1
<*> pure v2 <*> check (v2:vs) e2
Esac e c v1 e1 -> Esac <$> check vs e
<*> pure c <*> pure v1 <*> check (v1:vs) e1
Member e f -> Member <$> check vs e <*> pure f
e -> pure e
reorganiseTopLevel :: RawTopLevel -> GlobalEnvironments -> Writer [Error] GlobalEnvironments
reorganiseTopLevel (TypeSig f pt) envs = do
case M.lookup f (types envs) of
Just _ -> tell ["Duplicate type signature for " ++ f]
Nothing -> return ()
-- Embed recursive parameters
let pt'@(Forall tvs c t) = embedRecPars pt
if nub tvs /= tvs
then tell ["Duplicate quantified type variable in type signature for " ++ f]
else return ()
sanityCheckType (nub tvs) t
return (envs { types = M.insert f pt' (types envs) })
reorganiseTopLevel (Equation f x e) envs = do
case M.lookup f (defns envs) of
Just _ -> tell ["Duplicate equation of " ++ f]
Nothing -> return ()
tvs <- case M.lookup f (types envs) of
Just (Forall tvs _ _) -> return tvs
Nothing -> tell ["No type given for " ++ f] >> return []
e' <- sanityCheckExpr envs tvs [x] e
return (envs { defns = M.insert f (x,e') (defns envs) })
-- | Given a PolyType definition, changes all recursive parameter references from TypeVar to RecPar
embedRecPars :: PolyType -> PolyType
embedRecPars (Forall vs cs t) = Forall vs cs $ erp False M.empty t
where
erp :: Bool -> M.Map RecParName Type -> Type -> Type
erp b c (AbsType n s ts) = AbsType n s $ map (erp b c) ts
erp b c (Variant row) = Variant $ Row.mapEntries (\(Entry n t tk) -> Entry n (erp b c t) tk) row
erp b c (Bang t) = Bang $ erp b c t
-- Check add (if it exists), the recursive parameter to the context
erp b c r@(Record rp row s) =
Record rp (Row.mapEntries (\(Entry n t tk) -> Entry n (erp b (addRecPar rp) t) tk) row) s
where addRecPar p = case p of
Rec v -> (M.insert v r c)
_ -> c
erp b c (Function x y) = Function (erp b c x) (erp b c y)
-- Here, we insert `Nothing' if we are embedding a recursive parameter inside a context
-- (indicated by `b'), and the context otherwise
erp b c (TypeVar n)
| M.member n c = RecPar n (if b then Nothing else Just (M.map (erp True c) c))
| otherwise = TypeVar n
erp b c (TypeVarBang n)
| M.member n c = RecParBang n (if b then Nothing else Just (M.map (erp True c) c))
| otherwise = TypeVarBang n
erp _ _ t = t
-- | Checks that variables only occur strictly positive.
nonStrictlyPositiveVars :: Type -> [VarName]
nonStrictlyPositiveVars t = sp t M.empty
where
-- Map if variables in scope are in argument position
-- Bool is True if we are in the argument position of some function currently
sp :: Type -> M.Map RecParName Bool -> [VarName]
sp (PrimType _) m = []
sp (AbsType _ _ ts) m = concatMap (\t -> sp t m) ts
sp (Variant r) m =
concatMap (\(Entry _ t _) -> sp t m) (Row.entries r)
sp (Bang t) m = sp t m
-- Type variables have been replaced with embedded recursive parameters at this point
sp (TypeVar v) m = []
sp (TypeVarBang v) m = []
-- Records are special - only here can we pick up recursive parameters
sp (Record rp r _) m =
concatMap (\(Entry _ t _) -> sp t (addRecPar rp)) (Row.entries r)
where addRecPar p = case p of
Rec v -> (M.insert v False m)
_ -> m
-- Mark that we are in the argument position for all variables in the context
sp (Function x y) m =
sp x (M.map (const True) m) ++ sp y m
-- If we are in the argument position then return the recursive parameter name
sp (RecPar n _) m
| Just True <- M.lookup n m = [n]
| otherwise = []
sp (RecParBang n _) m
| Just True <- M.lookup n m = [n]
| otherwise = []
sp _ _ = error "strictlyPositive"
reorganise :: [RawTopLevel] -> GlobalEnvironments -> Writer [Error] GlobalEnvironments
reorganise [] envs = return envs
reorganise (x:xs) envs = reorganise xs =<< reorganiseTopLevel x envs
reorg :: [RawTopLevel] -> (GlobalEnvironments, [Error])
reorg tls = runWriter (reorganise tls emptyGlobalEnvironments)