Added proper error message to monomorphizer; made subst a monoid

This commit is contained in:
sebastian 2023-05-15 23:40:15 +02:00
parent 4a635162a3
commit f77793a132
4 changed files with 83 additions and 90 deletions

View file

@ -144,7 +144,7 @@ main' opts s =
when opts.logIL (printToErr "\n-- Lambda Lifter --" >> log lifted) when opts.logIL (printToErr "\n-- Lambda Lifter --" >> log lifted)
let monomorphized = monomorphize lifted monomorphized <- fromErr $ monomorphize lifted
when opts.logIL (printToErr "\n -- Monomorphizer --" >> log monomorphized) when opts.logIL (printToErr "\n -- Monomorphizer --" >> log monomorphized)

View file

@ -28,7 +28,7 @@ module Monomorphizer.Monomorphizer (monomorphize, morphExp, morphBind) where
import Control.Monad.Reader (MonadReader (ask, local), import Control.Monad.Reader (MonadReader (ask, local),
Reader, asks, runReader) ReaderT, asks, runReaderT)
import Control.Monad.State (MonadState, import Control.Monad.State (MonadState,
StateT (runStateT), gets, StateT (runStateT), gets,
modify) modify)
@ -38,22 +38,20 @@ import Grammar.Print (printTree)
import Monomorphizer.DataTypeRemover (removeDataTypes) import Monomorphizer.DataTypeRemover (removeDataTypes)
import qualified Monomorphizer.MonomorphizerIr as O import qualified Monomorphizer.MonomorphizerIr as O
import qualified Monomorphizer.MorbIr as M import qualified Monomorphizer.MorbIr as M
-- import TypeChecker.TypeCheckerIr (Ident (Ident))
import LambdaLifterIr (Ident (..)) import LambdaLifterIr (Ident (..))
-- import TypeChecker.TypeCheckerIr qualified as T
import qualified LambdaLifterIr as L import qualified LambdaLifterIr as L
import Data.Maybe (fromJust, catMaybes) import Data.Maybe (fromJust, catMaybes)
import Data.Tuple.Extra (secondM) import Data.Tuple.Extra (secondM)
import Debug.Trace (trace) import Control.Monad.Except (throwError, Except, runExcept, MonadError)
import Test.QuickCheck.State (State(expected)) import Data.List (foldl')
{- | EnvM is the monad containing the read-only state as well as the {- | EnvM is the monad containing the read-only state as well as the
output state containing monomorphized functions and to-be monomorphized output state containing monomorphized functions and to-be monomorphized
data type declarations. data type declarations.
-} -}
newtype EnvM a = EnvM (StateT Output (Reader Env) a) newtype EnvM a = EnvM (StateT Output (ReaderT Env (Except String)) a)
deriving (Functor, Applicative, Monad, MonadState Output, MonadReader Env) deriving (Functor, Applicative, Monad, MonadState Output, MonadReader Env, MonadError String)
type Output = Map.Map Ident Outputted type Output = Map.Map Ident Outputted
@ -106,25 +104,26 @@ isConsMarked ident = gets (Map.member ident)
-- | Finds main bind. -- | Finds main bind.
getMain :: EnvM L.Bind getMain :: EnvM L.Bind
getMain = asks (\env -> case Map.lookup (Ident "main") (input env) of getMain = do
Just mainBind -> mainBind env <- ask
Nothing -> error "main not found in monomorphizer!" case Map.lookup (Ident "main") (input env) of
) Just mainBind -> return mainBind
Nothing -> throwError "main not found in monomorphizer!"
{- | Makes a kv pair list of polymorphic to monomorphic mappings, throws runtime {- | Makes a kv pair list of polymorphic to monomorphic mappings, throws runtime
error when encountering different structures between the two arguments. Debug: error when encountering different structures between the two arguments. Debug:
First argument is the name of the bind. First argument is the name of the bind.
-} -}
mapTypes :: Ident -> L.Type -> M.Type -> [(Ident, M.Type)] mapTypes :: Ident -> L.Type -> M.Type -> EnvM [(Ident, M.Type)]
mapTypes _ident (L.TLit _) (M.TLit _) = [] mapTypes _ident (L.TLit _) (M.TLit _) = return []
mapTypes _ident (L.TVar (L.MkTVar i1)) tm = [(i1, tm)] mapTypes _ident (L.TVar (L.MkTVar i1)) tm = return [(i1, tm)]
mapTypes ident (L.TFun pt1 pt2) (M.TFun mt1 mt2) = mapTypes ident (L.TFun pt1 pt2) (M.TFun mt1 mt2) =
mapTypes ident pt1 mt1 (++) <$> mapTypes ident pt1 mt1 <*> mapTypes ident pt2 mt2
++ mapTypes ident pt2 mt2
mapTypes ident (L.TData tIdent pTs) (M.TData mIdent mTs) = mapTypes ident (L.TData tIdent pTs) (M.TData mIdent mTs) =
if tIdent /= mIdent if tIdent /= mIdent
then error "the data type names of monomorphic and polymorphic data types does not match" then throwError "the data type names of monomorphic and polymorphic data types does not match"
else foldl (\xs (p, m) -> mapTypes ident p m ++ xs) [] (zip pTs mTs) else foldl' (\xs (p, m) -> do x <- mapTypes ident p m; (++x) <$> xs) (return []) (zip pTs mTs)
-- This is a proper callstack error as a previous phase has a bug.
mapTypes ident t1 t2 = error $ "in bind: '" ++ printTree ident ++ "', " ++ mapTypes ident t1 t2 = error $ "in bind: '" ++ printTree ident ++ "', " ++
"structure of types not the same: '" ++ printTree t1 ++ "', '" ++ printTree t2 ++ "'" "structure of types not the same: '" ++ printTree t1 ++ "', '" ++ printTree t2 ++ "'"
@ -166,11 +165,12 @@ morphBind expectedType b@(L.Bind (ident, btype) args (exp, expt)) = do
-- The "new name" is used to find out if it is already marked or not. -- The "new name" is used to find out if it is already marked or not.
let name' = newFuncName expectedType b let name' = newFuncName expectedType b
bindMarked <- isBindMarked name' bindMarked <- isBindMarked name'
mt <- mapTypes ident btype expectedType
local local
( \env -> ( \env ->
env env
{ locals = Set.fromList (map fst args) { locals = Set.fromList (map fst args)
, polys = Map.fromList (mapTypes ident btype expectedType) , polys = Map.fromList mt
} }
) )
$ do $ do
@ -196,11 +196,12 @@ morphBind expectedType b@(L.BindC cxt (ident, btype) args (exp, expt)) = do
-- The "new name" is used to find out if it is already marked or not. -- The "new name" is used to find out if it is already marked or not.
let name' = newFuncName expectedType b let name' = newFuncName expectedType b
bindMarked <- isBindMarked name' bindMarked <- isBindMarked name'
mt <- mapTypes ident btype expectedType
local local
( \env -> ( \env ->
env env
{ locals = Set.fromList (map fst args) { locals = Set.fromList (map fst args)
, polys = Map.fromList (mapTypes ident btype expectedType) , polys = Map.fromList mt
} }
) )
$ do $ do
@ -234,8 +235,7 @@ morphArg (ident, t) = do
-- | Gets the data bind from the name of a constructor. -- | Gets the data bind from the name of a constructor.
getInputData :: Ident -> EnvM (Maybe L.Data) getInputData :: Ident -> EnvM (Maybe L.Data)
getInputData ident = do getInputData ident = do
env <- ask asks (Map.lookup ident . dataDefs)
return $ Map.lookup ident (dataDefs env)
{- | Monomorphize a constructor using it's global name. Constructors may {- | Monomorphize a constructor using it's global name. Constructors may
appear as expressions in the tree, or as patterns in case-expressions. appear as expressions in the tree, or as patterns in case-expressions.
@ -248,12 +248,13 @@ morphCons expectedType ident newIdent = do
-- closures can have unbound variables -- closures can have unbound variables
Nothing -> pure () Nothing -> pure ()
Just d -> do Just d -> do
modify (\output -> Map.insert newIdent (Data expectedType d) output) modify (Map.insert newIdent (Data expectedType d))
-- | Converts literals from input to output tree. -- | Converts literals from input to output tree.
convertLit :: L.Lit -> M.Lit convertLit :: L.Lit -> M.Lit
convertLit (L.LInt v) = M.LInt v convertLit (L.LInt v) = M.LInt v
convertLit (L.LChar v) = M.LChar v convertLit (L.LChar v) = M.LChar v
convertLit l = error $ "Unexpected lit in monomorphizer: '" ++ printTree l ++ "'"
-- | Monomorphizes an expression, given an expected type. -- | Monomorphizes an expression, given an expected type.
@ -292,7 +293,7 @@ morphExp expectedType exp = case exp of
else do else do
bind <- getInputBind ident bind <- getInputBind ident
case bind of case bind of
Nothing -> error $ "unbound variable: '" ++ printTree ident ++ "'" Nothing -> throwError $ "unbound variable: '" ++ printTree ident ++ "'"
Just bind' -> do Just bind' -> do
-- New bind to process -- New bind to process
newBindName <- morphBind expectedType bind' newBindName <- morphBind expectedType bind'
@ -356,8 +357,7 @@ morphPattern p expectedType = case p of
-- Exampel: List a => a -> List a -- Exampel: List a => a -> List a
convertConsTypeToDataType :: M.Type -> [M.Type] -> M.Type convertConsTypeToDataType :: M.Type -> [M.Type] -> M.Type
convertConsTypeToDataType inner (t:ts) = convertConsTypeToDataType (M.TFun t inner) ts convertConsTypeToDataType = foldl (flip M.TFun)
convertConsTypeToDataType inner [] = inner
-- | Creates a new identifier for a function with an assigned type. -- | Creates a new identifier for a function with an assigned type.
@ -381,28 +381,26 @@ newName t (Ident str) = Ident $ str ++ "$" ++ newName' t
newName' (M.TData (Ident str) ts) = str ++ foldl (\s t -> s ++ "." ++ newName' t) "" ts newName' (M.TData (Ident str) ts) = str ++ foldl (\s t -> s ++ "." ++ newName' t) "" ts
-- | Monomorphization step. -- | Monomorphization step.
monomorphize :: L.Program -> O.Program monomorphize :: L.Program -> Either String O.Program
monomorphize (L.Program defs) = monomorphize (L.Program defs) = do
removeDataTypes $ op <- runEnvM Map.empty (createEnv defs) monomorphize'
M.Program let prg = getDefsFromOutput op
( getDefsFromOutput return . removeDataTypes $ M.Program prg
(runEnvM Map.empty (createEnv defs) monomorphize')
)
where where
monomorphize' :: EnvM () monomorphize' :: EnvM ()
monomorphize' = do monomorphize' = do
mainBind <- getMain mainBind <- getMain
case mainBind of case mainBind of
(L.BindC _ _ _ _) -> error "main should not be a BindC node" (L.BindC {}) -> error "main should not be a BindC node"
main@(L.Bind _ _ (_, mainType)) -> case getMonoFromMono mainType of main@(L.Bind _ _ (_, mainType)) -> case getMonoFromMono mainType of
Nothing -> error "main should be monomorphic" Nothing -> throwError "main should be monomorphic"
Just mainTypeMono -> do Just mainTypeMono -> do
morphBind mainTypeMono main morphBind mainTypeMono main
return () return ()
-- | Runs and gives the output binds. -- | Runs and gives the output binds.
runEnvM :: Output -> Env -> EnvM () -> Output runEnvM :: Output -> Env -> EnvM () -> Either String Output
runEnvM o env (EnvM stateM) = snd $ runReader (runStateT stateM o) env runEnvM o env (EnvM stateM) = snd <$> runExcept (runReaderT (runStateT stateM o) env)
-- | Creates the environment based on the input binds. -- | Creates the environment based on the input binds.
createEnv :: [L.Def] -> Env createEnv :: [L.Def] -> Env
@ -485,7 +483,7 @@ createNewData ((consIdent, consType, polyData) : input) o =
getDataType :: M.Type -> M.Type getDataType :: M.Type -> M.Type
getDataType (M.TFun _t1 t2) = getDataType t2 getDataType (M.TFun _t1 t2) = getDataType t2
getDataType tData@(M.TData _ _) = tData getDataType tData@(M.TData _ _) = tData
getDataType _ = error "???" getDataType _ = error "Bug in previous phase of compilation"
addLocal :: Ident -> Env -> Env addLocal :: Ident -> Env -> Env

View file

@ -16,7 +16,7 @@ import Control.Monad.State
import Control.Monad.Writer import Control.Monad.Writer
import Data.Coerce (coerce) import Data.Coerce (coerce)
import Data.Function (on) import Data.Function (on)
import Data.List (foldl', nub) import Data.List (foldl')
import Data.List.Extra (unsnoc) import Data.List.Extra (unsnoc)
import Data.Map (Map) import Data.Map (Map)
import Data.Map qualified as M import Data.Map qualified as M
@ -28,14 +28,6 @@ import Grammar.Print (printTree)
import TypeChecker.TypeCheckerIr (T, T') import TypeChecker.TypeCheckerIr (T, T')
import TypeChecker.TypeCheckerIr qualified as T import TypeChecker.TypeCheckerIr qualified as T
{-
TODO
Prettifying the types of generated variables does only need to be done when
presenting the types to the user, i.e, when the user has made a mistake.
For succesfully typed programs the types only need to match.
-}
-- | Type check a program -- | Type check a program
typecheck :: Program -> Either String (T.Program' Type, [Warning]) typecheck :: Program -> Either String (T.Program' Type, [Warning])
typecheck = onLeft msg . run . checkPrg typecheck = onLeft msg . run . checkPrg
@ -245,7 +237,7 @@ algoW = \case
"does not match inferred type" "does not match inferred type"
quote $ printTree t' quote $ printTree t'
) )
let comp = sub1 `compose` sub0 let comp = sub1 <> sub0
return (comp, (apply comp e', t)) return (comp, (apply comp e', t))
-- \| ------------------ -- \| ------------------
@ -309,7 +301,7 @@ algoW = \case
(s2, (e1', t1)) <- algoW e1 (s2, (e1', t1)) <- algoW e1
s3 <- exprErr (unify t0 int) err s3 <- exprErr (unify t0 int) err
s4 <- exprErr (unify t1 int) err s4 <- exprErr (unify t1 int) err
let comp = s4 `compose` s3 `compose` s2 `compose` s1 let comp = s4 <> s3 <> s2 <> s1
return return
( comp ( comp
, apply comp (T.EAdd (e0', t0) (e1', t1), int) , apply comp (T.EAdd (e0', t0) (e1', t1), int)
@ -327,7 +319,7 @@ algoW = \case
(s1, (e1', t1)) <- algoW e1 (s1, (e1', t1)) <- algoW e1
s2 <- unify (apply s1 t0) (TFun t1 fr) s2 <- unify (apply s1 t0) (TFun t1 fr)
let t = apply s2 fr let t = apply s2 fr
let comp = s2 `compose` s1 `compose` s0 let comp = s2 <> s1 <> s0
return (comp, apply comp (T.EApp (e0', t0) (e1', t1), t)) return (comp, apply comp (T.EApp (e0', t0) (e1', t1), t))
-- \| Γ ⊢ e₀ : τ, S₀ S₀Γ, x : S̅₀Γ̅(τ) ⊢ e₁ : τ', S₁ -- \| Γ ⊢ e₀ : τ, S₀ S₀Γ, x : S̅₀Γ̅(τ) ⊢ e₁ : τ', S₁
@ -344,7 +336,7 @@ algoW = \case
let t' = generalize (apply s1 env) t0 let t' = generalize (apply s1 env) t0
withBinding (coerce name) t' $ do withBinding (coerce name) t' $ do
(s2, (e1', t2)) <- algoW e1 (s2, (e1', t2)) <- algoW e1
let comp = s2 `compose` s1 let comp = s2 <> s1
return return
( comp ( comp
, apply , apply
@ -354,7 +346,7 @@ algoW = \case
ECase caseExpr injs -> do ECase caseExpr injs -> do
(sub, (e', t)) <- algoW caseExpr (sub, (e', t)) <- algoW caseExpr
(subst, injs, ret_t) <- checkCase t injs (subst, injs, ret_t) <- checkCase t injs
let comp = subst `compose` sub let comp = subst <> sub
return (comp, apply comp (T.ECase (e', t) injs, ret_t)) return (comp, apply comp (T.ECase (e', t) injs, ret_t))
checkCase :: Type -> [Branch] -> Infer (Subst, [T.Branch' Type], Type) checkCase :: Type -> [Branch] -> Infer (Subst, [T.Branch' Type], Type)
@ -367,18 +359,18 @@ checkCase expT brnchs = do
(sub1, _) <- (sub1, _) <-
foldM foldM
( \(sub, acc) x -> ( \(sub, acc) x ->
(\a -> (a `compose` sub, a `apply` acc)) <$> unify x acc (\a -> (a <> sub, a `apply` acc)) <$> unify x acc
) )
(nullSubst, expT) (nullSubst, expT)
branchTs branchTs
(sub2, returns_type) <- (sub2, returns_type) <-
foldM foldM
( \(sub, acc) x -> ( \(sub, acc) x ->
(\a -> (a `compose` sub, a `apply` acc)) <$> unify x acc (\a -> (a <> sub, a `apply` acc)) <$> unify x acc
) )
(nullSubst, head returns) (nullSubst, head returns)
(tail returns) (tail returns)
let comp = sub2 `compose` sub1 `compose` sub0 let comp = sub2 <> sub1 <> sub0
return (comp, apply comp injs, apply comp returns_type) return (comp, apply comp injs, apply comp returns_type)
inferBranch :: Branch -> Infer (Subst, Type, T.Branch' Type, Type) inferBranch :: Branch -> Infer (Subst, Type, T.Branch' Type, Type)
@ -463,7 +455,7 @@ unify t0 t1 = case (t0, t1) of
(TFun a b, TFun c d) -> do (TFun a b, TFun c d) -> do
s1 <- unify a c s1 <- unify a c
s2 <- unify (apply s1 b) (apply s1 d) s2 <- unify (apply s1 b) (apply s1 d)
return $ s2 `compose` s1 return $ s2 <> s1
(TVar a, t@(TData _ _)) -> return $ singleton a t (TVar a, t@(TData _ _)) -> return $ singleton a t
(t@(TData _ _), TVar b) -> return $ singleton b t (t@(TData _ _), TVar b) -> return $ singleton b t
(TVar a, t) -> occurs a t (TVar a, t) -> occurs a t
@ -575,7 +567,7 @@ fresh :: Infer Type
fresh = do fresh = do
n <- gets count n <- gets count
modify (\st -> st{count = succ (count st)}) modify (\st -> st{count = succ (count st)})
return $ TVar $ MkTVar $ LIdent $ show n return . TVar . MkTVar . LIdent $ letters !! n
-- Is the left more general than the right -- Is the left more general than the right
(<<=) :: Type -> Type -> Infer Bool (<<=) :: Type -> Type -> Infer Bool
@ -730,13 +722,15 @@ instance SubstType (T T.Ident Type) where
nullSubst :: Subst nullSubst :: Subst
nullSubst = mempty nullSubst = mempty
-- | Compose two substitution sets {- | Compose two substitution sets
The monoid instance of Subst uses this definition
-}
compose :: Subst -> Subst -> Subst compose :: Subst -> Subst -> Subst
compose m1@(Subst m1') (Subst m2) = Subst $ M.map (apply m1) m2 `M.union` m1' compose m1@(Subst m1') (Subst m2) = Subst $ M.map (apply m1) m2 `M.union` m1'
-- | Compose a list of substitution sets into one -- | Compose a list of substitution sets into one
composeAll :: [Subst] -> Subst composeAll :: [Subst] -> Subst
composeAll = foldl' compose nullSubst composeAll = mconcat
{- | Convert a function with arguments to its pointfree version {- | Convert a function with arguments to its pointfree version
> makeLambda (add x y = x + y) = add = \x. \y. x + y > makeLambda (add x y = x + y) = add = \x. \y. x + y
@ -914,5 +908,5 @@ uncatchableErr msg = throwError $ Error msg False
quote :: String -> String quote :: String -> String
quote s = "'" ++ s ++ "'" quote s = "'" ++ s ++ "'"
letters :: [T.Ident] letters :: [String]
letters = map T.Ident $ [1 ..] >>= flip replicateM ['a' .. 'z'] letters = [1 ..] >>= flip replicateM ['a' .. 'z']

View file

@ -60,7 +60,6 @@ newtype TVar = MkTVar Ident
type T' a t = (a t, t) type T' a t = (a t, t)
type T a t = (a, t) type T a t = (a, t)
data Bind' t = Bind (T Ident t) [T Ident t] (T' Exp' t) data Bind' t = Bind (T Ident t) [T Ident t] (T' Exp' t)
deriving (Eq, Ord, Show, Functor) deriving (Eq, Ord, Show, Functor)
@ -74,7 +73,8 @@ instance Print t => Print (Program' t) where
prt i (Program sc) = prt i sc prt i (Program sc) = prt i sc
instance Print t => Print (Bind' t) where instance Print t => Print (Bind' t) where
prt i (Bind sig parms rhs) = concatD prt i (Bind sig parms rhs) =
concatD
[ prtSig sig [ prtSig sig
, prt i parms , prt i parms
, doc $ showString "=" , doc $ showString "="
@ -93,7 +93,8 @@ instance (Print a, Print t) => Print (T a t) where
prt i (x, t) = noT prt i (x, t) = noT
where where
noT = prt i x noT = prt i x
withT = concatD withT =
concatD
[ doc $ showString "(" [ doc $ showString "("
, prt i x , prt i x
, doc $ showString ":" , doc $ showString ":"