module Language.Fortran.Analysis.Types ( analyseTypes, analyseTypesWithEnv, extractTypeEnv, TypeEnv ) where
import Language.Fortran.AST
import Prelude hiding (lookup)
import Data.Map (insert)
import qualified Data.Map as M
import Data.Maybe (maybeToList)
import Control.Monad.State.Strict
import Data.Generics.Uniplate.Data
import Data.Data
import Language.Fortran.Analysis
import Language.Fortran.Intrinsics
import Language.Fortran.ParserMonad (FortranVersion(..))
type TypeEnv = M.Map Name IDType
type Infer a = State InferState a
data InferState = InferState { langVersion :: FortranVersion
, intrinsics :: IntrinsicsTable
, environ :: TypeEnv
, entryPoints :: M.Map Name (Name, Maybe Name) }
deriving Show
type InferFunc t = t -> Infer ()
analyseTypes :: Data a => ProgramFile (Analysis a) -> (ProgramFile (Analysis a), TypeEnv)
analyseTypes = analyseTypesWithEnv M.empty
analyseTypesWithEnv :: Data a => TypeEnv -> ProgramFile (Analysis a) -> (ProgramFile (Analysis a), TypeEnv)
analyseTypesWithEnv env pf@(ProgramFile mi _) = fmap environ . runInfer (miVersion mi) env $ do
mapM_ intrinsicsExp (allExpressions pf)
mapM_ programUnit (allProgramUnits pf)
mapM_ declarator (allDeclarators pf)
mapM_ statement (allStatements pf)
eps <- gets (M.toList . entryPoints)
forM eps $ \ (eName, (fName, mRetName)) -> do
mFType <- getRecordedType fName
case mFType of
Just (IDType fVType fCType) -> do
recordMType fVType fCType eName
maybe (return ()) (error "Entry points with result variables unsupported" >> recordMType fVType Nothing) mRetName
_ -> return ()
annotateTypes pf
extractTypeEnv :: forall a. Data a => ProgramFile (Analysis a) -> TypeEnv
extractTypeEnv pf = M.union puEnv expEnv
where
puEnv = M.fromList [ (n, ty) | pu <- universeBi pf :: [ProgramUnit (Analysis a)]
, Named n <- [puName pu]
, ty <- maybeToList (idType (getAnnotation pu)) ]
expEnv = M.fromList [ (n, ty) | e <- universeBi pf :: [Expression (Analysis a)]
, let n = varName e
, ty <- maybeToList (idType (getAnnotation e)) ]
type TransType f g a = (f (Analysis a) -> Infer (f (Analysis a))) -> g (Analysis a) -> Infer (g (Analysis a))
annotateTypes :: Data a => ProgramFile (Analysis a) -> Infer (ProgramFile (Analysis a))
annotateTypes pf = (transformBiM :: Data a => TransType Expression ProgramFile a) annotateExpression pf >>=
(transformBiM :: Data a => TransType ProgramUnit ProgramFile a) annotateProgramUnit
intrinsicsExp :: Data a => InferFunc (Expression (Analysis a))
intrinsicsExp (ExpSubscript _ _ nexp _) = intrinsicsHelper nexp
intrinsicsExp (ExpFunctionCall _ _ nexp _) = intrinsicsHelper nexp
intrinsicsExp _ = return ()
intrinsicsHelper nexp | isNamedExpression nexp = do
itab <- gets intrinsics
case getIntrinsicReturnType (srcName nexp) itab of
Just itype -> do
let n = varName nexp
recordCType CTIntrinsic n
_ -> return ()
intrinsicsHelper _ = return ()
programUnit :: Data a => InferFunc (ProgramUnit (Analysis a))
programUnit pu@(PUFunction _ _ mRetType _ _ _ mRetVar blocks _)
| Named n <- puName pu = do
recordCType CTFunction n
case (mRetType, mRetVar) of
(Just (TypeSpec _ _ baseType _), Just v) -> recordBaseType baseType n >> recordBaseType baseType (varName v)
(Just (TypeSpec _ _ baseType _), _) -> recordBaseType baseType n
_ -> return ()
forM_ blocks $ \ block ->
sequence_ [ recordEntryPoint n (varName v) (fmap varName mRetVar) | (StEntry _ _ v _ mRetVar) <- allStatements block ]
programUnit pu@(PUSubroutine _ _ _ _ _ blocks _) | Named n <- puName pu = do
recordCType CTSubroutine n
forM_ blocks $ \ block ->
sequence_ [ recordEntryPoint n (varName v) Nothing | (StEntry _ _ v _ _) <- allStatements block ]
programUnit _ = return ()
declarator :: Data a => InferFunc (Declarator (Analysis a))
declarator (DeclArray _ _ v _ _ _) = recordCType CTArray (varName v)
declarator _ = return ()
statement :: Data a => InferFunc (Statement (Analysis a))
statement (StDeclaration _ _ (TypeSpec _ _ baseType _) mAttrAList declAList)
| mAttrs <- maybe [] aStrip mAttrAList
, isArray <- any isAttrDimension mAttrs
, isParam <- any isAttrParameter mAttrs
, decls <- aStrip declAList = do
env <- gets environ
forM_ decls $ \ decl -> case decl of
DeclArray _ _ v _ _ _ -> recordType baseType CTArray (varName v)
DeclVariable _ _ v (Just _) _ -> recordType baseType CTVariable (varName v)
DeclVariable _ _ v Nothing _ -> recordType baseType cType n
where
n = varName v
cType | isArray = CTArray
| isParam = CTParameter
| Just (IDType _ (Just ct)) <- M.lookup n env = ct
| otherwise = CTVariable
statement (StExpressionAssign _ _ (ExpSubscript _ _ v ixAList) _)
| all isIxSingle (aStrip ixAList) = do
let n = varName v
mIDType <- getRecordedType n
case mIDType of
Just (IDType mBT (Just CTArray)) -> return ()
_ -> recordCType CTFunction n
statement (StFunction _ _ v _ _) = recordCType CTFunction (varName v)
statement (StDimension _ _ declAList) = do
let decls = aStrip declAList
forM_ decls $ \ decl -> case decl of
DeclArray _ _ v _ _ _ -> recordCType CTArray (varName v)
_ -> return ()
statement _ = return ()
annotateExpression :: Data a => Expression (Analysis a) -> Infer (Expression (Analysis a))
annotateExpression e@(ExpValue _ _ (ValVariable _)) = maybe e (flip setIDType e) `fmap` getRecordedType (varName e)
annotateExpression e@(ExpValue _ _ (ValIntrinsic _)) = maybe e (flip setIDType e) `fmap` getRecordedType (varName e)
annotateExpression e = return e
annotateProgramUnit :: Data a => ProgramUnit (Analysis a) -> Infer (ProgramUnit (Analysis a))
annotateProgramUnit pu | Named n <- puName pu = maybe pu (flip setIDType pu) `fmap` getRecordedType n
annotateProgramUnit pu = return pu
inferState0 v = InferState { environ = M.empty, entryPoints = M.empty, langVersion = v, intrinsics = getVersionIntrinsics v }
runInfer :: FortranVersion -> TypeEnv -> State InferState a -> (a, InferState)
runInfer v env = flip runState ((inferState0 v) { environ = env })
recordType :: BaseType -> ConstructType -> Name -> Infer ()
recordType bt ct n = modify $ \ s -> s { environ = insert n (IDType (Just bt) (Just ct)) (environ s) }
recordMType :: Maybe BaseType -> Maybe ConstructType -> Name -> Infer ()
recordMType bt ct n = modify $ \ s -> s { environ = insert n (IDType (bt) (ct)) (environ s) }
recordCType :: ConstructType -> Name -> Infer ()
recordCType ct n = modify $ \ s -> s { environ = M.alter changeFunc n (environ s) }
where changeFunc mIDType = Just (IDType (mIDType >>= idVType) (Just ct))
recordBaseType :: BaseType -> Name -> Infer ()
recordBaseType bt n = modify $ \ s -> s { environ = M.alter changeFunc n (environ s) }
where changeFunc mIDType = Just (IDType (Just bt) (mIDType >>= idCType))
recordEntryPoint :: Name -> Name -> Maybe Name -> Infer ()
recordEntryPoint fn en mRetName = modify $ \ s -> s { entryPoints = M.insert en (fn, mRetName) (entryPoints s) }
getRecordedType :: Name -> Infer (Maybe IDType)
getRecordedType n = gets (M.lookup n . environ)
setIDType :: Annotated f => IDType -> f (Analysis a) -> f (Analysis a)
setIDType ty x
| a@(Analysis {}) <- getAnnotation x = setAnnotation (a { idType = Just ty }) x
| otherwise = x
getIDType :: (Annotated f, Data a) => f (Analysis a) -> Maybe IDType
getIDType x = idType (getAnnotation x)
setCType :: (Annotated f, Data a) => ConstructType -> f (Analysis a) -> f (Analysis a)
setCType ct x
| a@(Analysis { idType = Nothing }) <- getAnnotation x = setAnnotation (a { idType = Just (IDType Nothing (Just ct)) }) x
| a@(Analysis { idType = Just it }) <- getAnnotation x = setAnnotation (a { idType = Just (it { idCType = Just ct }) }) x
type UniFunc f g a = f (Analysis a) -> [g (Analysis a)]
allProgramUnits :: Data a => UniFunc ProgramFile ProgramUnit a
allProgramUnits = universeBi
allDeclarators :: Data a => UniFunc ProgramFile Declarator a
allDeclarators = universeBi
allStatements :: (Data a, Data (f (Analysis a))) => UniFunc f Statement a
allStatements = universeBi
allExpressions :: (Data a, Data (f (Analysis a))) => UniFunc f Expression a
allExpressions = universeBi
isAttrDimension (AttrDimension {}) = True
isAttrDimension _ = False
isAttrParameter (AttrParameter {}) = True
isAttrParameter _ = False
isIxSingle (IxSingle {}) = True
isIxSingle _ = False