tock-mirror/frontends/RainTypes.hs
Neil Brown c8b724d2be Merged the latest set of changes from the trunk into the Polyplate branch
I also added the import list to all the Data.Generics imports in the tests (as I did for the other modules recently)
2009-04-10 20:38:29 +00:00

333 lines
14 KiB
Haskell

{-
Tock: a compiler for parallel languages
Copyright (C) 2007, 2008 University of Kent
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
-}
module RainTypes (constantFoldPass, performTypeUnification) where
import Control.Monad.State
import Data.Generics (Data, showConstr, toConstr, Typeable)
import Data.List
import qualified Data.Map as Map
import Data.Maybe
import Data.IORef
import qualified AST as A
import CompState
import Errors
import EvalConstants
import Metadata
import Pass
import qualified Properties as Prop
import ShowCode
import Traversal
import Types
import TypeUnification
import UnifyType
import Utils
-- This is a bit of a hack for this file:
data M = M Meta A.Type deriving (Typeable, Data)
instance ASTTypeable M where
astTypeOf (M m t) = return t
instance FindMeta M where
findMeta (M m t) = m
data RainTypeState = RainTypeState {
csUnifyLookup :: Map.Map UnifyIndex UnifyValue,
csUnifyPairs :: [(UnifyValue, UnifyValue)]
}
startState :: RainTypeState
startState = RainTypeState {
csUnifyLookup = Map.empty,
csUnifyPairs = []
}
type RainTypeM = StateT RainTypeState PassM
type RainTypePassType = forall t. t -> StateT RainTypeState PassM t
type RainTypeCheckOn a = forall t. PolyplateSpine t (OneOpQ (RainTypeM ()) a) ()
(RainTypeM ()) => t -> RainTypeM ()
type RainTypeCheckOn2 a b = forall t.
(PolyplateSpine t (TwoOpQ (RainTypeM ()) a b) () (RainTypeM ())
) => t -> RainTypeM ()
type RainTypeCheck a = a -> RainTypeM ()
instance Die RainTypeM where
dieReport = lift . dieReport
instance CSMR RainTypeM where
getCompState = lift getCompState
lookupMapElseMutVar :: A.TypeRequirements -> UnifyIndex -> RainTypeM (TypeExp A.Type)
lookupMapElseMutVar reqs k
= do st <- get
let m = csUnifyLookup st
case Map.lookup k m of
Just v -> return v
Nothing -> do r <- liftIO $ newIORef (reqs, Nothing)
let UnifyIndex (mt,_) = k
v = MutVar mt r
m' = Map.insert k v m
put st {csUnifyLookup = m'}
return v
ttte :: Meta -> String -> (A.Type -> A.Type) -> A.Type -> RainTypeM (TypeExp A.Type)
ttte m c f t = typeToTypeExp m t >>= \t' -> return $ OperType m c (\[x] -> f x) [t']
-- Transforms the given type into a typeexp, such that the only inner types
-- left will be the primitive types (integer types, float types, bool, time). Arrays
-- (which would require unification of dimensions and such) are not supported,
-- neither are records.
-- User data types should not be present in the input.
typeToTypeExp :: Meta -> A.Type -> RainTypeM (TypeExp A.Type)
typeToTypeExp m (A.List t) = ttte m "[]" A.List t
typeToTypeExp m (A.ChanEnd A.DirInput at t) = ttte m "?" (A.ChanEnd A.DirInput at) t
typeToTypeExp m (A.ChanEnd A.DirOutput at t) = ttte m "!" (A.ChanEnd A.DirOutput at) t
typeToTypeExp m (A.Chan at t) = ttte m "channel" (A.Chan at) t
typeToTypeExp m (A.Mobile t) = ttte m "MOBILE" A.Mobile t
typeToTypeExp _ (A.UnknownVarType reqs en)
= case en of
Left n -> lookupMapElseMutVar reqs (UnifyIndex (A.nameMeta n, Right n))
Right (m, i) -> lookupMapElseMutVar reqs (UnifyIndex (m, Left i))
typeToTypeExp _ (A.UnknownNumLitType m id n)
= do r <- liftIO . newIORef $ Left [(m,n)]
let v = NumLit m r
st <- get
let mp = csUnifyLookup st
put st {csUnifyLookup = Map.insert (UnifyIndex (m,Left id)) v mp}
return v
typeToTypeExp m t = return $ OperType m (show t) (const t) []
markUnify :: (ASTTypeable a, ASTTypeable b, FindMeta a, FindMeta b, Data a, Data b) => a -> b -> RainTypeM ()
markUnify x y
= do tx <- astTypeOf x
ty <- astTypeOf y
tex <- typeToTypeExp (findMeta x) tx
tey <- typeToTypeExp (findMeta y) ty
modify $ \st -> st {csUnifyPairs = (tex,tey) : csUnifyPairs st}
performTypeUnification ::
-- | A shorthand for prerequisites when you need to spell them out:
(PolyplateSpine t (OneOpQ (RainTypeM ()) A.Specification) () (RainTypeM ())
,PolyplateSpine t (OneOpQ (RainTypeM ()) A.Process) () (RainTypeM ())
,PolyplateSpine t (OneOpQ (RainTypeM ()) A.Expression) () (RainTypeM ())
,PolyplateSpine t (TwoOpQ (RainTypeM ()) A.Process A.Expression) () (RainTypeM ())
,PolyplateSpine t (TwoOpQ (RainTypeM ()) A.Process A.Choice) () (RainTypeM ())
,PolyplateSpine t (TwoOpQ (RainTypeM ()) A.Process A.Alternative) () (RainTypeM ())
,PolyplateM t () (OneOpM PassM A.Type) PassM
,PolyplateM t (OneOpM PassM A.Type) () PassM
) => Pass t
performTypeUnification = rainOnlyPass "Rain Type Checking"
([Prop.noInt] ++ Prop.agg_namesDone)
[Prop.expressionTypesChecked, Prop.functionTypesChecked, Prop.processTypesChecked, Prop.retypesChecked]
(\x -> flip evalStateT startState $ do -- First, we copy the known types into the unify map:
st <- get
ul <- getCompState >>= (shift . csNames)
put st {csUnifyPairs = [], csUnifyLookup = ul}
-- Then we markup all the types in the tree:
markConditionalTypes x
markParamPass x
markAssignmentTypes x
markCommTypes x
markPoisonTypes x
markReplicators x
markExpressionTypes x
-- Then, we do the unification:
prs <- get >>* csUnifyPairs
mapM_ (lift . uncurry unifyType) prs
-- Now put the types back in a map, and replace them through the tree:
l <- get >>* csUnifyLookup
ts <- lift $ mapMapM (\v -> fromTypeExp v) l
lift $ get >>= substituteUnknownTypes ts >>= put
lift $ substituteUnknownTypes ts x)
where
shift :: Map.Map String A.NameDef -> RainTypeM (Map.Map UnifyIndex UnifyValue)
shift = liftM (Map.fromList . catMaybes) . mapM shift' . Map.toList
where
shift' :: (String, A.NameDef) -> RainTypeM (Maybe (UnifyIndex, UnifyValue))
shift' (rawName, d) = do mt <- typeOfSpec (A.ndSpecType d)
case mt of
Nothing -> return Nothing
Just t -> do te <- typeToTypeExp (A.ndMeta d) t
return $ Just (UnifyIndex (A.ndMeta d, Right name), te)
where
name = A.Name {A.nameName = rawName, A.nameMeta = A.ndMeta d}
substituteUnknownTypes :: Map.Map UnifyIndex A.Type -> PassTypeOn A.Type
substituteUnknownTypes mt = applyBottomUpM sub
where
sub :: A.Type -> PassM A.Type
sub (A.UnknownVarType _ (Left n)) = lookup $ UnifyIndex (A.nameMeta n, Right n)
sub (A.UnknownVarType _ (Right (m,i))) = lookup $ UnifyIndex (m,Left i)
sub (A.UnknownNumLitType m i _) = lookup $ UnifyIndex (m, Left i)
sub t = return t
lookup :: UnifyIndex -> PassM A.Type
lookup u@(UnifyIndex(m,_)) = case Map.lookup u mt of
Just t -> return t
Nothing -> dieP m "Could not deduce type"
markReplicators :: RainTypeCheckOn A.Specification
markReplicators = checkDepthM mark
where
mark :: RainTypeCheck A.Specification
mark (A.Specification m n (A.Rep _ (A.ForEach _m e)))
= astTypeOf n >>= \t -> markUnify (M m $ A.List t) e
mark _ = return ()
-- | Folds all constants.
constantFoldPass :: PassOn A.Expression
constantFoldPass = rainOnlyPass "Fold all constant expressions"
([Prop.noInt] ++ Prop.agg_namesDone ++ [Prop.inferredTypesRecorded])
[Prop.constantsFolded, Prop.constantsChecked]
(applyBottomUpM doExpression)
where
doExpression :: A.Expression -> PassM A.Expression
doExpression = (liftM (\(x,_,_) -> x)) . constantFold
-- | A pass that finds all the 'A.ProcCall' and 'A.FunctionCall' in the
-- AST, and checks that the actual parameters are valid inputs, given
-- the 'A.Formal' parameters in the process's type
markParamPass :: RainTypeCheckOn2 A.Process A.Expression
markParamPass = checkDepthM2 matchParamPassProc matchParamPassFunc
where
--Picks out the parameters of a process call, checks the number is correct, and maps doParam over them
matchParamPassProc :: RainTypeCheck A.Process
matchParamPassProc (A.ProcCall m n actualParams)
= do def <- lookupNameOrError n $ dieP m ("Process name is unknown: \"" ++ (show $ A.nameName n) ++ "\"")
case A.ndSpecType def of
A.Proc _ _ expectedParams _ ->
if (length expectedParams) == (length actualParams)
then mapM_ (uncurry markUnify) (zip expectedParams actualParams)
else dieP m $ "Wrong number of parameters given to process call; expected: " ++ show (length expectedParams) ++ " but found: " ++ show (length actualParams)
_ -> dieP m $ "You cannot run things that are not processes, such as: \"" ++ (show $ A.nameName n) ++ "\""
matchParamPassProc _ = return ()
--Picks out the parameters of a function call, checks the number is correct, and maps doExpParam over them
matchParamPassFunc :: RainTypeCheck A.Expression
matchParamPassFunc (A.FunctionCall m n actualParams)
= do def <- lookupNameOrError n $ dieP m ("Function name is unknown: \"" ++ (show $ A.nameName n) ++ "\"")
case A.ndSpecType def of
A.Function _ _ _ expectedParams _ ->
if (length expectedParams) == (length actualParams)
then mapM_ (uncurry markUnify) (zip expectedParams actualParams)
else dieP m $ "Wrong number of parameters given to function call; expected: " ++ show (length expectedParams) ++ " but found: " ++ show (length actualParams)
_ -> dieP m $ "Attempt to make a function call with something"
++ " that is not a function: \"" ++ A.nameName n
++ "\"; is actually: " ++ showConstr (toConstr $
A.ndSpecType def)
matchParamPassFunc _ = return ()
-- | Checks the types in expressions
markExpressionTypes :: RainTypeCheckOn A.Expression
markExpressionTypes = checkDepthM checkExpression
where
-- TODO also check in a later pass that the op is valid
checkExpression :: RainTypeCheck A.Expression
-- checkExpression (A.Dyadic _ _ lhs rhs)
-- = markUnify lhs rhs
checkExpression (A.Literal m t (A.ArrayListLiteral m' es))
= checkListElems (markUnify (M m t) . M m') es
checkExpression _ = return ()
checkListElems :: RainTypeCheck A.Type -> RainTypeCheck (A.Structured A.Expression)
checkListElems ch (A.Only _ e) = astTypeOf e >>= ch
checkListElems ch (A.Several _ es) = mapM_ (checkListElems (ch . A.List)) es
checkListElems ch (A.Spec _ _ s) = checkListElems ch s
checkListElems ch (A.ProcThen _ _ s) = checkListElems ch s
-- | Checks the types in assignments
markAssignmentTypes :: RainTypeCheckOn A.Process
markAssignmentTypes = checkDepthM checkAssignment
where
checkAssignment :: RainTypeCheck A.Process
checkAssignment (A.Assign m [v] (A.ExpressionList _ [e]))
= do am <- abbrevModeOfVariable v
when (am == A.ValAbbrev) $
diePC m $ formatCode "Cannot assign to a constant variable: %" v
-- Assignments also includes assignments to function names,
-- so we need a little extra logic:
case v of
A.Variable _ n ->
do st <- specTypeOfName n
case st of
A.Function m _ [t] _ _ -> markUnify (M m t) e
_ -> markUnify v e
_ -> markUnify v e
checkAssignment (A.Assign m _ _) = dieInternal (Just m,"Rain checker found occam-style assignment")
checkAssignment st = return ()
-- | Checks the types in if and while conditionals
markConditionalTypes :: RainTypeCheckOn2 A.Process A.Choice
markConditionalTypes = checkDepthM2 checkWhile checkIf
where
checkWhile :: RainTypeCheck A.Process
checkWhile w@(A.While m exp _)
= markUnify exp (M m A.Bool)
checkWhile _ = return ()
checkIf :: RainTypeCheck A.Choice
checkIf c@(A.Choice m exp _)
= markUnify exp (M m A.Bool)
-- | Marks types in poison statements
markPoisonTypes :: RainTypeCheckOn A.Process
markPoisonTypes = checkDepthM checkPoison
where
checkPoison :: RainTypeCheck A.Process
checkPoison (A.InjectPoison m ch)
= do u <- lift getUniqueIdentifer
markUnify ch (M m $ A.UnknownVarType (A.TypeRequirements True) $ Right (m, u))
checkPoison _ = return ()
-- | Checks the types in inputs and outputs, including inputs in alts
markCommTypes :: RainTypeCheckOn2 A.Process A.Alternative
markCommTypes = checkDepthM2 checkInputOutput checkAltInput
where
checkInput :: A.Variable -> A.Variable -> Meta -> a -> RainTypeM ()
checkInput chanVar destVar m p
= astTypeOf destVar >>= markUnify chanVar . M (findMeta destVar) . A.ChanEnd A.DirInput A.Unshared
checkWait :: RainTypeCheck A.InputMode
checkWait (A.InputTimerFor m exp) = markUnify (M m A.Time) exp
checkWait (A.InputTimerAfter m exp) = markUnify (M m A.Time) exp
checkWait (A.InputTimerRead m (A.InVariable m' v)) = markUnify (M m A.Time) v
checkWait _ = return ()
checkInputOutput :: RainTypeCheck A.Process
checkInputOutput p@(A.Input m chanVar (A.InputSimple _ [A.InVariable _ destVar]))
= checkInput chanVar destVar m p
checkInputOutput (A.Input _ _ im@(A.InputTimerFor {})) = checkWait im
checkInputOutput (A.Input _ _ im@(A.InputTimerAfter {})) = checkWait im
checkInputOutput (A.Input _ _ im@(A.InputTimerRead {})) = checkWait im
checkInputOutput p@(A.Output m chanVar [A.OutExpression m' srcExp])
= astTypeOf srcExp >>= markUnify chanVar . M m' . A.ChanEnd A.DirOutput A.Unshared
checkInputOutput _ = return ()
checkAltInput :: RainTypeCheck A.Alternative
checkAltInput a@(A.Alternative m _ chanVar (A.InputSimple _ [A.InVariable _ destVar]) body)
= checkInput chanVar destVar m a
checkAltInput (A.Alternative m _ _ im@(A.InputTimerFor {}) _) = checkWait im
checkAltInput (A.Alternative m _ _ im@(A.InputTimerAfter {}) _) = checkWait im
checkAltInput _ = return ()