
This makes sure that we catch all leftover instances of using SYB to do generic operations that we should be using Polyplate for instead. Most modules should only import Data, and possibly Typeable.
886 lines
33 KiB
Haskell
886 lines
33 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/>.
|
|
-}
|
|
|
|
-- | Type inference and checking.
|
|
module Types
|
|
(
|
|
specTypeOfName, typeOfSpec, typeOfSpec', abbrevModeOfName, underlyingType, underlyingTypeOf, stripArrayType, abbrevModeOfVariable, abbrevModeOfSpec
|
|
, isRealType, isIntegerType, isNumericType, isCaseableType, isScalarType, isDataType, isCommunicableType, isSequenceType, isMobileType
|
|
, resolveUserType, isSafeConversion, isPreciseConversion, isImplicitConversionRain
|
|
, isOperator, functionOperator, builtInOperator, occamDefaultOperator, occamBuiltInOperatorFunctions, occamOperatorTranslateDefault
|
|
, returnTypesOfFunction
|
|
, BytesInResult(..), bytesInType, countReplicator, countStructured, computeStructured
|
|
|
|
, makeAbbrevAM, makeConstant, makeConstant', makeDimension, specificDimSize
|
|
, addOne, subOne, addExprs, subExprs, mulExprs, divExprs, remExprs
|
|
, addOneInt, subOneInt, addExprsInt, subExprsInt, mulExprsInt, divExprsInt
|
|
, dyadicExpr, dyadicExprInt
|
|
, addDimensions, applyDimension, removeFixedDimensions, trivialSubscriptType, subscriptType, unsubscriptType
|
|
, applyDirection
|
|
, recordFields, recordAttr, protocolItems, dirAttr
|
|
|
|
, leastGeneralSharedTypeRain
|
|
|
|
, ASTTypeable(..), findMeta
|
|
) where
|
|
|
|
import Control.Monad.State
|
|
import Data.Char
|
|
import Data.Generics (Data)
|
|
import qualified Data.Map as Map
|
|
import Data.Maybe
|
|
import Data.List
|
|
import Data.Ord
|
|
import qualified Data.Set as Set
|
|
|
|
import qualified AST as A
|
|
import CompState hiding (CSM) -- all these functions are read-only!
|
|
import Errors
|
|
import EvalLiterals
|
|
import Intrinsics
|
|
import Metadata
|
|
import PrettyShow
|
|
import ShowCode
|
|
import Traversal
|
|
import TypeSizes
|
|
import Utils
|
|
|
|
class ASTTypeable a where
|
|
astTypeOf :: (CSMR m, Die m) => a -> m A.Type
|
|
|
|
instance ASTTypeable A.Type where
|
|
astTypeOf = return
|
|
|
|
underlyingTypeOf :: (ASTTypeable a, CSMR m, Die m) => Meta -> a -> m A.Type
|
|
underlyingTypeOf m = underlyingType m <.< astTypeOf
|
|
|
|
-- | Gets the 'A.AbbrevMode' for a given 'A.Name' from the recorded types in the 'CompState'. Dies with an error if the name is unknown.
|
|
abbrevModeOfName :: (CSMR m, Die m) => A.Name -> m A.AbbrevMode
|
|
abbrevModeOfName n
|
|
= liftM A.ndAbbrevMode (lookupNameOrError n $ dieP (A.nameMeta n) $ "Could not find abbreviation mode in abbrevModeOfName for: " ++ (show $ A.nameName n))
|
|
|
|
instance ASTTypeable A.Name where
|
|
astTypeOf = typeOfName
|
|
|
|
instance ASTTypeable A.Formal where
|
|
astTypeOf (A.Formal _ t _) = return t
|
|
|
|
instance ASTTypeable A.Actual where
|
|
astTypeOf (A.ActualVariable v) = astTypeOf v
|
|
astTypeOf (A.ActualExpression e) = astTypeOf e
|
|
astTypeOf (A.ActualClaim v)
|
|
= do t <- typeOfVariable v
|
|
case t of
|
|
A.Chan attr innerT -> return $ A.Chan (attr
|
|
{ A.caWritingShared = A.Unshared
|
|
, A.caReadingShared = A.Unshared
|
|
}) innerT
|
|
A.ChanEnd A.DirInput _ innerT
|
|
-> return $ A.ChanEnd A.DirInput A.Unshared innerT
|
|
A.ChanEnd A.DirOutput _ innerT
|
|
-> return $ A.ChanEnd A.DirOutput A.Unshared innerT
|
|
A.ChanDataType dir _ innerT -> return $ A.ChanDataType dir A.Unshared innerT
|
|
_ -> dieP (findMeta v) "Item in claim not channel"
|
|
astTypeOf (A.ActualChannelArray (v:vs))
|
|
= do t <- typeOfVariable v
|
|
return $ A.Array [A.Dimension $ makeConstant (findMeta v) (length vs+1)] t
|
|
|
|
|
|
-- | Gets the 'A.Type' for a given 'A.Name' by looking at its definition in the 'CompState'. Dies with an error if the name is unknown.
|
|
typeOfName :: (CSMR m, Die m) => A.Name -> m A.Type
|
|
typeOfName n
|
|
= do st <- specTypeOfName n
|
|
t <- typeOfSpec st
|
|
case t of
|
|
Just t' -> return t'
|
|
Nothing -> dieP (findMeta n) $ "cannot type name " ++ pshow n ++
|
|
":" ++ show st
|
|
|
|
typeOfSpec' :: (CSMR m, Die m) => A.SpecType -> m (Maybe (A.Type, A.Type -> A.SpecType))
|
|
typeOfSpec' st
|
|
= case st of
|
|
A.Declaration a t -> return $ Just (t, A.Declaration a)
|
|
A.Is a b t c -> return $ Just (t, \t' -> A.Is a b t' c)
|
|
A.Retypes a b t c -> return $ Just (t, \t' -> A.Retypes a b t' c)
|
|
A.RetypesExpr a b t c
|
|
-> return $ Just (t, \t' -> A.RetypesExpr a b t' c)
|
|
A.Rep _ (A.For _ _ e _) -> do t <- astTypeOf e
|
|
return $ Just (t, error "typeOfSpec'")
|
|
A.Rep _ (A.ForEach _ e) ->
|
|
do t <- astTypeOf e
|
|
case t of
|
|
A.List t' -> return $ Just (t', error "typeOfSpec'")
|
|
A.Array _ t' -> return $ Just (t', error "typeOfSpec'")
|
|
_ -> return Nothing
|
|
_ -> return Nothing
|
|
|
|
typeOfSpec :: (CSMR m, Die m) => A.SpecType -> m (Maybe A.Type)
|
|
typeOfSpec = liftM (fmap fst) . typeOfSpec'
|
|
|
|
--{{{ identifying types
|
|
-- | Get the fields of a record type.
|
|
recordFields :: (CSMR m, Die m) => Meta -> A.Type -> m [(A.Name, A.Type)]
|
|
recordFields m (A.Record rec)
|
|
= do st <- specTypeOfName rec
|
|
case st of
|
|
A.RecordType _ _ fs -> return fs
|
|
_ -> dieP m "not record type"
|
|
recordFields m (A.ChanDataType A.DirInput _ n)
|
|
= do st <- specTypeOfName n
|
|
case st of
|
|
A.ChanBundleType _ _ fs -> return fs
|
|
_ -> dieP m "not record type"
|
|
-- Directions are flipped for the ! end:
|
|
recordFields m (A.ChanDataType A.DirOutput _ n)
|
|
= do st <- specTypeOfName n
|
|
case st of
|
|
A.ChanBundleType _ _ fs -> return [(n, flipDirOfEnd t) | (n, t) <- fs]
|
|
_ -> dieP m "not record type"
|
|
where
|
|
flipDirOfEnd (A.ChanEnd dir attr t) = A.ChanEnd (flipDir dir) attr t
|
|
flipDir A.DirInput = A.DirOutput
|
|
flipDir A.DirOutput = A.DirInput
|
|
recordFields m _ = dieP m "not record type"
|
|
|
|
recordAttr :: (CSMR m, Die m) => Meta -> A.Type -> m A.RecordAttr
|
|
recordAttr m (A.Record rec)
|
|
= do st <- specTypeOfName rec
|
|
case st of
|
|
A.RecordType _ attr _ -> return attr
|
|
_ -> dieP m "not record type"
|
|
recordAttr m _ = dieP m "not record type"
|
|
|
|
dirAttr :: A.Direction -> A.ChanAttributes -> A.ShareMode
|
|
dirAttr A.DirInput = A.caReadingShared
|
|
dirAttr A.DirOutput = A.caWritingShared
|
|
|
|
-- | Get the type of a record field.
|
|
typeOfRecordField :: (CSMR m, Die m) => Meta -> A.Type -> A.Name -> m A.Type
|
|
typeOfRecordField m t field
|
|
= do fs <- recordFields m t
|
|
checkJust (Just m, "unknown record field") $ lookup field fs
|
|
|
|
-- | Apply a plain subscript to a type.
|
|
plainSubscriptType :: (CSMR m, Die m) => Meta -> A.Type -> m A.Type
|
|
plainSubscriptType m (A.Array (_:ds) t)
|
|
= return $ case ds of
|
|
[] -> t
|
|
_ -> A.Array ds t
|
|
plainSubscriptType m (A.Mobile t) = plainSubscriptType m t
|
|
plainSubscriptType m t = diePC m $ formatCode "Subscript of non-array type: %" t
|
|
|
|
-- | Turn an expression into a 'Dimension'.
|
|
-- If the expression is constant, it'll produce 'Dimension'; if not, it'll
|
|
-- produce 'UnknownDimension'.
|
|
dimensionFromExpr :: A.Expression -> A.Dimension
|
|
dimensionFromExpr e
|
|
= if isConstant e
|
|
then A.Dimension $ e
|
|
else A.UnknownDimension
|
|
|
|
-- | Apply a subscript to a type, and return what the type is after it's been
|
|
-- subscripted.
|
|
subscriptType :: (CSMR m, Die m) => A.Subscript -> A.Type -> m A.Type
|
|
subscriptType sub A.Infer
|
|
= return $ A.Infer
|
|
subscriptType sub t@(A.UserDataType _)
|
|
= resolveUserType (findMeta sub) t >>= subscriptType sub
|
|
subscriptType (A.SubscriptFromFor m _ _ count) (A.Array (_:ds) t)
|
|
= return $ A.Array (dimensionFromExpr count : ds) t
|
|
subscriptType (A.SubscriptFrom m _ base) (A.Array (d:ds) t)
|
|
= return $ A.Array (dim : ds) t
|
|
where
|
|
dim = case d of
|
|
A.Dimension size -> dimensionFromExpr $ subExprsInt size base
|
|
_ -> A.UnknownDimension
|
|
subscriptType (A.SubscriptFor m _ count) (A.Array (_:ds) t)
|
|
= return $ A.Array (dimensionFromExpr count : ds) t
|
|
subscriptType (A.SubscriptField m tag) t = typeOfRecordField m t tag
|
|
subscriptType (A.Subscript m _ _) t = plainSubscriptType m t
|
|
subscriptType sub t = diePC (findMeta sub) $ formatCode "Unsubscriptable type: %" t
|
|
|
|
-- | The inverse of 'subscriptType': given a type that we know is the result of
|
|
-- a subscript, return what the type being subscripted is.
|
|
unsubscriptType :: A.Subscript -> A.Type -> Maybe A.Type
|
|
unsubscriptType _ A.Infer
|
|
= Just $ A.Infer
|
|
unsubscriptType (A.SubscriptFromFor _ _ _ _) t
|
|
= Just $ removeFixedDimension t
|
|
unsubscriptType (A.SubscriptFrom _ _ _) t
|
|
= Just $ removeFixedDimension t
|
|
unsubscriptType (A.SubscriptFor _ _ _) t
|
|
= Just $ removeFixedDimension t
|
|
unsubscriptType (A.SubscriptField m _) t
|
|
= Nothing
|
|
unsubscriptType (A.Subscript _ _ sub) t
|
|
= Just $ addDimensions [A.UnknownDimension] t
|
|
|
|
-- | Just remove the first dimension from an array type -- like doing
|
|
-- subscriptType with constant 0 as a subscript, but without the checking.
|
|
-- This is used for the couple of cases where we know it's safe and don't want
|
|
-- the usage check.
|
|
trivialSubscriptType :: (CSMR m, Die m) => Meta -> A.Type -> m A.Type
|
|
trivialSubscriptType _ A.Infer = return A.Infer
|
|
trivialSubscriptType m t@(A.UserDataType _)
|
|
= resolveUserType m t >>= trivialSubscriptType m
|
|
trivialSubscriptType _ (A.Array [d] t) = return t
|
|
trivialSubscriptType _ (A.Array (d:ds) t) = return $ A.Array ds t
|
|
trivialSubscriptType m (A.Mobile t) = trivialSubscriptType m t
|
|
trivialSubscriptType m t = diePC m $ formatCode "not plain array type: %" t
|
|
|
|
instance ASTTypeable A.Variable where
|
|
astTypeOf = typeOfVariable
|
|
|
|
-- | Gets the 'A.Type' of a 'A.Variable' by looking at the types recorded in the 'CompState'.
|
|
typeOfVariable :: (CSMR m, Die m) => A.Variable -> m A.Type
|
|
typeOfVariable (A.Variable m n) = typeOfName n
|
|
typeOfVariable (A.SubscriptedVariable m s v)
|
|
= typeOfVariable v >>= subscriptType s
|
|
typeOfVariable (A.DerefVariable m v)
|
|
= do t <- typeOfVariable v >>= resolveUserType m
|
|
case t of
|
|
A.Mobile innerT -> return innerT
|
|
_ -> diePC m $ formatCode "Dereference applied to non-mobile variable of type %" t
|
|
typeOfVariable (A.DirectedVariable m dir v)
|
|
= do t <- typeOfVariable v
|
|
case t of
|
|
A.ChanEnd dir' _ _ ->
|
|
if dir == dir'
|
|
then return t
|
|
else dieP m $ "Attempted to reverse direction of a channel-end"
|
|
A.Chan attr innerT -> return $ A.ChanEnd dir (dirAttr dir attr) innerT
|
|
A.Array ds (A.Chan attr innerT)
|
|
-> return $ A.Array ds (A.ChanEnd dir (dirAttr dir attr) innerT)
|
|
A.Array _ (A.ChanEnd dir' _ _) ->
|
|
if dir == dir'
|
|
then return t
|
|
else dieP m $ "Attempted to reverse direction of a channel-end"
|
|
A.Infer -> return $ A.ChanEnd dir A.Unshared A.Infer
|
|
_ -> diePC m $ formatCode "Direction specified on non-channel variable of type: %" t
|
|
typeOfVariable (A.VariableSizes m v)
|
|
= do t <- typeOfVariable v
|
|
case t of
|
|
A.Array ds _ -> return $ A.Array [A.Dimension $ makeConstant m $ length ds] A.Int
|
|
A.Mobile (A.Array ds _) -> return $ A.Array [A.Dimension $ makeConstant m $ length ds] A.Int
|
|
_ -> diePC m $ formatCode "Attempted to get size of non-array: % (type: %)" v t
|
|
|
|
-- | Get the abbreviation mode of a variable.
|
|
abbrevModeOfVariable :: (CSMR m, Die m) => A.Variable -> m A.AbbrevMode
|
|
abbrevModeOfVariable (A.Variable _ n) = abbrevModeOfName n
|
|
abbrevModeOfVariable (A.SubscriptedVariable _ sub v) = abbrevModeOfVariable v
|
|
abbrevModeOfVariable (A.DirectedVariable _ _ v) = abbrevModeOfVariable v
|
|
abbrevModeOfVariable (A.DerefVariable _ v) = return A.Original
|
|
abbrevModeOfVariable (A.VariableSizes {}) = return A.Original
|
|
|
|
instance ASTTypeable A.Expression where
|
|
astTypeOf = typeOfExpression
|
|
|
|
-- | Gets the 'A.Type' of an 'A.Expression'. This function assumes that the expression has already been type-checked.
|
|
typeOfExpression :: (CSMR m, Die m) => A.Expression -> m A.Type
|
|
typeOfExpression e
|
|
= case e of
|
|
A.MostPos m t -> return t
|
|
A.MostNeg m t -> return t
|
|
A.SizeType m t -> return A.Int
|
|
A.SizeExpr m t -> return A.Int
|
|
A.Conversion m cm t e -> return t
|
|
A.ExprVariable m v -> typeOfVariable v
|
|
A.Literal _ t _ -> return t
|
|
A.True m -> return A.Bool
|
|
A.False m -> return A.Bool
|
|
A.FunctionCall m n es -> liftM head $ returnTypesOfFunction n
|
|
A.IntrinsicFunctionCall m s _ -> liftM head $ returnTypesOfIntrinsic m s
|
|
A.SubscriptedExpr m s e ->
|
|
typeOfExpression e >>= subscriptType s
|
|
A.BytesInExpr m e -> return A.Int
|
|
A.BytesInType m t -> return A.Int
|
|
A.OffsetOf m t n -> return A.Int
|
|
A.AllocMobile _ t _ -> return t
|
|
A.CloneMobile _ e -> typeOfExpression e
|
|
A.IsDefined {} -> return A.Bool
|
|
--}}}
|
|
|
|
-- | Gets the return type(s) of a function call from the 'CompState'.
|
|
returnTypesOfFunction :: (CSMR m, Die m) => A.Name -> m [A.Type]
|
|
returnTypesOfFunction n
|
|
= do st <- specTypeOfName n
|
|
case st of
|
|
A.Function _ _ rs _ _ -> return rs
|
|
-- If it's not defined as a function, it might have been converted to a proc.
|
|
_ ->
|
|
do ps <- getCompState
|
|
checkJust (Just $ findMeta n, "not defined as a function") $
|
|
Map.lookup (A.nameName n) (csFunctionReturns ps)
|
|
|
|
returnTypesOfIntrinsic :: (CSMR m, Die m) => Meta -> String -> m [A.Type]
|
|
returnTypesOfIntrinsic m s
|
|
= do frontend <- getCompState >>* csFrontend
|
|
let intrinsicList = case frontend of
|
|
FrontendOccam -> intrinsicFunctions
|
|
FrontendRain -> rainIntrinsicFunctions
|
|
case lookup s intrinsicList of
|
|
Just (rts, _) -> return rts
|
|
Nothing -> dieP m $ "unknown intrinsic function " ++ s
|
|
|
|
-- | Get the items in a channel's protocol (for typechecking).
|
|
-- Returns Left if it's a simple protocol, Right if it's tagged.
|
|
protocolItems :: (ASTTypeable a, Data a, CSMR m, Die m) => Meta -> a -> m (Either [A.Type] [(A.Name, [A.Type])])
|
|
protocolItems m v
|
|
= do chanT <- astTypeOf v
|
|
t <- case chanT of
|
|
A.Chan _ t -> return t
|
|
A.ChanEnd _ _ t -> return t
|
|
_ -> dieP m $ "Expected a channel variable, but this is of type: " ++ show chanT
|
|
case t of
|
|
A.UserProtocol proto ->
|
|
do st <- specTypeOfName proto
|
|
case st of
|
|
A.Protocol _ ts -> return $ Left ts
|
|
A.ProtocolCase _ nts -> return $ Right nts
|
|
_ -> return $ Left [t]
|
|
|
|
-- | Gets the 'A.AbrrevMode' of a 'A.SpecType' directly.
|
|
abbrevModeOfSpec :: A.SpecType -> A.AbbrevMode
|
|
abbrevModeOfSpec s
|
|
= case s of
|
|
A.Is _ am _ _ -> am
|
|
A.Retypes _ am _ _ -> am
|
|
A.RetypesExpr _ am _ _ -> am
|
|
_ -> A.Original
|
|
|
|
-- | Add array dimensions to a type; if it's already an array it'll just add
|
|
-- the new dimensions to the existing array.
|
|
addDimensions :: [A.Dimension] -> A.Type -> A.Type
|
|
addDimensions newDs (A.Array ds t) = A.Array (newDs ++ ds) t
|
|
addDimensions ds t = A.Array ds t
|
|
|
|
-- | Set the first dimension of an array type.
|
|
applyDimension :: A.Dimension -> A.Type -> A.Type
|
|
applyDimension dim (A.Array (_:ds) t) = A.Array (dim : ds) t
|
|
applyDimension _ t = t
|
|
|
|
-- | Return a type with any enclosing arrays removed; useful for identifying
|
|
-- things that should be channel names, timer names, etc. in the parser.
|
|
stripArrayType :: A.Type -> A.Type
|
|
stripArrayType (A.Array _ t) = stripArrayType t
|
|
stripArrayType t = t
|
|
|
|
-- | Remove one fixed dimension from a type.
|
|
removeFixedDimension :: A.Type -> A.Type
|
|
removeFixedDimension (A.Array (A.Dimension _:ds) t) = A.Array (A.UnknownDimension:ds) t
|
|
removeFixedDimension t = t
|
|
|
|
-- | Remove any fixed array dimensions from a type.
|
|
removeFixedDimensions :: A.Type -> A.Type
|
|
removeFixedDimensions (A.Array ds t) = A.Array [A.UnknownDimension | _ <- ds] t
|
|
removeFixedDimensions t = t
|
|
|
|
-- | Given the abbreviation mode of something, return what the abbreviation
|
|
-- mode of something that abbreviated it would be.
|
|
makeAbbrevAM :: A.AbbrevMode -> A.AbbrevMode
|
|
makeAbbrevAM A.Original = A.Abbrev
|
|
makeAbbrevAM am = am
|
|
|
|
-- | Generate a constant expression from an integer -- for array sizes and the
|
|
-- like.
|
|
makeConstant :: Meta -> Int -> A.Expression
|
|
makeConstant m = makeConstant' m A.Int . toInteger
|
|
|
|
makeConstant' :: Meta -> A.Type -> Integer -> A.Expression
|
|
makeConstant' m t n = A.Literal m t $ A.IntLiteral m (show n)
|
|
|
|
-- | Generate a constant dimension from an integer.
|
|
makeDimension :: Meta -> Int -> A.Dimension
|
|
makeDimension m n = A.Dimension $ makeConstant m n
|
|
|
|
-- | Apply a direction to a type.
|
|
applyDirection :: Die m => Meta -> A.Direction -> A.Type -> m A.Type
|
|
applyDirection m dir (A.Array ds t)
|
|
= applyDirection m dir t >>* A.Array ds
|
|
applyDirection m dir (A.Chan ca t)
|
|
= return $ A.ChanEnd dir (dirAttr dir ca) t
|
|
applyDirection m _ t
|
|
= dieP m "Direction specified on non-channel type"
|
|
|
|
-- | Checks whether a given conversion can be done implicitly in Rain
|
|
-- Parameters are src dest
|
|
isImplicitConversionRain :: A.Type -> A.Type -> Bool
|
|
isImplicitConversionRain x y
|
|
= if (x == y)
|
|
then True
|
|
else if (x == A.Bool || y == A.Bool)
|
|
then False
|
|
else isSafeConversion x y
|
|
|
|
-- | Is a conversion between two types precise (i.e. do you need to specify
|
|
-- ROUND or TRUNC when doing it)?
|
|
isPreciseConversion :: A.Type -> A.Type -> Bool
|
|
isPreciseConversion A.Real32 A.Real64 = True
|
|
isPreciseConversion fromT toT
|
|
= fromT == toT || not (isRealType fromT || isRealType toT)
|
|
|
|
-- | Will a conversion between two types always succeed?
|
|
--Parameters are src dest
|
|
isSafeConversion :: A.Type -> A.Type -> Bool
|
|
isSafeConversion A.Real32 A.Real64 = True
|
|
isSafeConversion src dest = (src' == dest') || ((src' == A.Bool || isIntegerType src') && (dest' == A.Bool || isIntegerType dest') && (findCastRoute dest' src'))
|
|
where
|
|
src' = convInt src
|
|
dest' = convInt dest
|
|
|
|
--Turn Int into Int32:
|
|
convInt :: A.Type -> A.Type
|
|
convInt A.Int = A.Int32
|
|
convInt t = t
|
|
|
|
--Parameters are dest src
|
|
findCastRoute :: A.Type -> A.Type -> Bool
|
|
findCastRoute dest src
|
|
--Either a direct converstion is possible
|
|
= (elem (dest,src) possibleConversions)
|
|
--Or there exists some chained conversion:
|
|
|| (any (findCastRoute dest) (findDests src possibleConversions))
|
|
|
|
--Finds all the conversions from the src type using the given list of (dest,src)
|
|
--Note that the list must not allow any cycles! (or else we will engage in infinite recursion)
|
|
findDests :: A.Type -> [(A.Type,A.Type)] -> [A.Type]
|
|
findDests _ [] = []
|
|
findDests src ((dest,src'):ts) = if src == src' then dest : (findDests src ts) else findDests src ts
|
|
|
|
--Listed in order (dest, src)
|
|
--Signed numbers cannot be safely cast to unsigned numbers. So (A.UInt16, A.Int8) isn't possible
|
|
possibleConversions :: [(A.Type,A.Type)]
|
|
possibleConversions
|
|
= [
|
|
(A.Byte, A.Bool)
|
|
,(A.Int8, A.Bool)
|
|
|
|
,(A.Int16, A.Int8)
|
|
,(A.Int16, A.Byte)
|
|
,(A.Int32, A.Int16)
|
|
,(A.Int32, A.UInt16)
|
|
,(A.Int64, A.Int32)
|
|
,(A.Int64, A.UInt32)
|
|
|
|
,(A.UInt16, A.Byte)
|
|
,(A.UInt32, A.UInt16)
|
|
,(A.UInt64, A.UInt32)
|
|
]
|
|
|
|
|
|
-- | Works out the least-general type that all given types can be upcast to. Does not work with A.Int (as this function is expected only to be used by Rain)
|
|
-- As you would expect from the name, this function specifically follows the conversion rules for Rain.
|
|
leastGeneralSharedTypeRain :: [A.Type] -> Maybe A.Type
|
|
leastGeneralSharedTypeRain [] = Nothing
|
|
leastGeneralSharedTypeRain [t] = Just t
|
|
leastGeneralSharedTypeRain list@(t:ts)
|
|
= if (all ((==) t) ts) then Just t else
|
|
if (all isIntegerType list) then findInt list
|
|
else Nothing
|
|
where
|
|
findInt :: [A.Type] -> Maybe A.Type
|
|
findInt list = if null candidates
|
|
then Nothing
|
|
else Just $ snd $ maximumBy (comparing fst) candidates
|
|
where
|
|
candidates = if (all unsignedInt list) then (zip (map intSize list) list) else (allJustElseEmpty $ map findIntSigned list)
|
|
|
|
signedInt :: A.Type -> Bool
|
|
signedInt = not . unsignedInt
|
|
|
|
unsignedInt :: A.Type -> Bool
|
|
unsignedInt A.Byte = True
|
|
unsignedInt A.UInt16 = True
|
|
unsignedInt A.UInt32 = True
|
|
unsignedInt A.UInt64 = True
|
|
unsignedInt _ = False
|
|
|
|
intSize :: A.Type -> Int
|
|
intSize A.Byte = 1
|
|
intSize A.UInt16 = 2
|
|
intSize A.UInt32 = 4
|
|
intSize A.UInt64 = 8
|
|
intSize A.Int8 = 1
|
|
intSize A.Int16 = 2
|
|
intSize A.Int32 = 4
|
|
intSize A.Int64 = 8
|
|
intSize _ = 0 --should never happen
|
|
|
|
--If all the items in the list are Just x, returns a list of them all.
|
|
--If one (or more items) is Nothing, returns an empty list.
|
|
allJustElseEmpty :: [Maybe a] -> [a]
|
|
allJustElseEmpty ms = if (any isNothing ms) then [] else catMaybes ms
|
|
|
|
--For each item in the list, get an ordered list of types we can cast to.
|
|
findIntSigned :: A.Type -> Maybe (Int,A.Type)
|
|
findIntSigned t = if (signedInt t)
|
|
then Just (intSize t,t)
|
|
--if it's unsigned, we need to cast it up by one type, assuming it's not already the biggest size
|
|
else transformMaybe (\x -> (intSize x,x)) (case t of
|
|
A.Byte -> Just A.Int16
|
|
A.UInt16 -> Just A.Int32
|
|
A.UInt32 -> Just A.Int64
|
|
A.UInt64 -> Nothing)
|
|
|
|
--{{{ classes of types
|
|
-- | Scalar integer types.
|
|
isIntegerType :: A.Type -> Bool
|
|
isIntegerType t
|
|
= case t of
|
|
A.Byte -> True
|
|
A.UInt16 -> True
|
|
A.UInt32 -> True
|
|
A.UInt64 -> True
|
|
A.Int8 -> True
|
|
A.Int -> True
|
|
A.Int16 -> True
|
|
A.Int32 -> True
|
|
A.Int64 -> True
|
|
A.Time -> True
|
|
_ -> False
|
|
|
|
-- | Scalar real types.
|
|
isRealType :: A.Type -> Bool
|
|
isRealType t
|
|
= case t of
|
|
A.Real32 -> True
|
|
A.Real64 -> True
|
|
_ -> False
|
|
|
|
-- | Numeric types.
|
|
isNumericType :: A.Type -> Bool
|
|
isNumericType t = isIntegerType t || isRealType t
|
|
|
|
-- | Types that are permitted as 'Case' selectors.
|
|
isCaseableType :: A.Type -> Bool
|
|
isCaseableType A.Bool = True
|
|
isCaseableType t = isIntegerType t
|
|
|
|
-- | All scalar types.
|
|
isScalarType :: A.Type -> Bool
|
|
isScalarType A.Bool = True
|
|
isScalarType t = isIntegerType t || isRealType t
|
|
|
|
-- | Types that can be used to define 'DataType's.
|
|
isDataType :: A.Type -> Bool
|
|
-- This may change in the future.
|
|
isDataType = isCommunicableType
|
|
|
|
-- | Types that can be communicated across a channel.
|
|
isCommunicableType :: A.Type -> Bool
|
|
isCommunicableType (A.Array _ t) = isCommunicableType t
|
|
isCommunicableType (A.List t) = isCommunicableType t
|
|
isCommunicableType (A.Record _) = True
|
|
isCommunicableType (A.Mobile _) = True
|
|
isCommunicableType t = isScalarType t
|
|
|
|
-- | Types that support 'Size' and subscripting.
|
|
isSequenceType :: Bool -> A.Type -> Bool
|
|
isSequenceType _ (A.Array _ _) = True
|
|
isSequenceType _ (A.List _) = True
|
|
isSequenceType True (A.Mobile t) = isSequenceType False t
|
|
isSequenceType _ _ = False
|
|
|
|
isMobileType :: (CSMR m, Die m) => A.Type -> m Bool
|
|
isMobileType (A.Mobile {}) = return True
|
|
isMobileType t@(A.Record n) = recordAttr (A.nameMeta n) t >>* A.mobileRecord
|
|
isMobileType (A.ChanDataType {}) = return True
|
|
isMobileType _ = return False
|
|
|
|
--}}}
|
|
|
|
--{{{ sizes of types
|
|
-- | The size in bytes of a data type.
|
|
data BytesInResult =
|
|
BIJust A.Expression -- ^ Just that many bytes.
|
|
| BIOneFree A.Expression Int -- ^ An array type; A bytes, times unknown dimension B.
|
|
| BIManyFree -- ^ An array type with multiple unknown dimensions.
|
|
| BIUnknown -- ^ We can't tell the size at compile time.
|
|
deriving (Show, Eq)
|
|
|
|
-- | Make a fixed-size 'BytesInResult'.
|
|
justSize :: CSMR m => Int -> m BytesInResult
|
|
justSize n = return $ BIJust $ makeConstant emptyMeta n
|
|
|
|
-- | Given the C and C++ values (in that order), selects according to the
|
|
-- backend. If the backend is not recognised, the C sizes are used.
|
|
justSizeBackends :: CSMR m => Int -> Int -> m BytesInResult
|
|
justSizeBackends c cxx
|
|
= do backend <- getCompState >>* csBackend
|
|
case backend of
|
|
BackendCPPCSP -> justSize c
|
|
_ -> justSize cxx
|
|
|
|
-- | Return the size in bytes of a data type.
|
|
bytesInType :: (CSMR m, Die m) => A.Type -> m BytesInResult
|
|
bytesInType A.Bool = justSizeBackends cBoolSize cxxBoolSize
|
|
bytesInType A.Byte = justSize 1
|
|
bytesInType A.UInt16 = justSize 2
|
|
bytesInType A.UInt32 = justSize 4
|
|
bytesInType A.UInt64 = justSize 8
|
|
bytesInType A.Int8 = justSize 1
|
|
bytesInType A.Int = justSizeBackends cIntSize cxxIntSize
|
|
bytesInType A.Int16 = justSize 2
|
|
bytesInType A.Int32 = justSize 4
|
|
bytesInType A.Int64 = justSize 8
|
|
bytesInType A.Real32 = justSize 4
|
|
bytesInType A.Real64 = justSize 8
|
|
bytesInType a@(A.Array _ _) = bytesInArray 0 a
|
|
where
|
|
bytesInArray :: (CSMR m, Die m) => Int -> A.Type -> m BytesInResult
|
|
bytesInArray num (A.Array [] t) = bytesInType t
|
|
bytesInArray num (A.Array (d:ds) t)
|
|
= do ts <- bytesInArray (num + 1) (A.Array ds t)
|
|
case (d, ts) of
|
|
(A.Dimension n, BIJust m) -> return $ BIJust (mulExprsInt n m)
|
|
(A.Dimension n, BIOneFree m x) -> return $ BIOneFree (mulExprsInt n m) x
|
|
(A.UnknownDimension, BIJust m) -> return $ BIOneFree m num
|
|
(A.UnknownDimension, BIOneFree _ _) -> return BIManyFree
|
|
(_, _) -> return ts
|
|
bytesInType (A.Record n)
|
|
= do st <- specTypeOfName n
|
|
case st of
|
|
-- We can only do this for *packed* records -- for normal records,
|
|
-- the compiler might insert padding.
|
|
(A.RecordType _ (A.RecordAttr {A.packedRecord=True}) nts) -> bytesInList nts
|
|
_ -> return $ BIUnknown
|
|
where
|
|
bytesInList :: (CSMR m, Die m) => [(A.Name, A.Type)] -> m BytesInResult
|
|
bytesInList [] = justSize 0
|
|
bytesInList ((_, t):rest)
|
|
= do bi <- bytesInType t
|
|
br <- bytesInList rest
|
|
case (bi, br) of
|
|
(BIJust a, BIJust b) -> return $ BIJust (addExprsInt a b)
|
|
(_, _) -> return BIUnknown
|
|
bytesInType _ = return $ BIUnknown
|
|
--}}}
|
|
|
|
-- | Get the number of items a replicator produces.
|
|
countReplicator :: A.Replicator -> A.Expression
|
|
countReplicator (A.For _ _ count _) = count
|
|
|
|
-- | Get the number of items in a Structured as an expression.
|
|
countStructured :: Data a => A.Structured a -> A.Expression
|
|
countStructured = computeStructured (\m _ -> makeConstant m 1)
|
|
|
|
-- | Compute an expression over a Structured.
|
|
computeStructured :: Data a => (Meta -> a -> A.Expression) -> A.Structured a -> A.Expression
|
|
computeStructured f (A.Spec _ (A.Specification _ _ (A.Rep m rep)) s)
|
|
= mulExprsInt (countReplicator rep) (computeStructured f s)
|
|
computeStructured f (A.Spec _ _ s) = computeStructured f s
|
|
computeStructured f (A.ProcThen _ _ s) = computeStructured f s
|
|
computeStructured f (A.Only m x) = f m x
|
|
computeStructured f (A.Several m ss)
|
|
= case ss of
|
|
[] -> makeConstant m 0
|
|
_ -> foldl1 addExprsInt (map (computeStructured f) ss)
|
|
|
|
specificDimSize :: Int -> A.Variable -> A.Variable
|
|
specificDimSize n v = A.SubscriptedVariable (findMeta v) (A.Subscript (findMeta v) A.NoCheck
|
|
$ makeConstant (findMeta v) n) $ A.VariableSizes (findMeta v) v
|
|
|
|
|
|
functionOperator :: (CSMR m, Die m) => A.Name -> m (Maybe String)
|
|
functionOperator n
|
|
= lookupNameOrError n (dieP (A.nameMeta n) $ "Can't find operator definition for " ++ A.nameName n)
|
|
>>* A.ndOrigName
|
|
>>* (\op -> if isOperator op then Just op else Nothing)
|
|
|
|
-- Only gives back a Just result if it's a non-overridden operator
|
|
builtInOperator :: (CSMR m, Die m) => A.Name -> m (Maybe String)
|
|
builtInOperator n
|
|
= do mOp <- functionOperator n
|
|
return $ case mOp of
|
|
Nothing -> Nothing
|
|
Just op
|
|
| A.nameName n `Set.member` occamBuiltInOperatorFunctionsSet
|
|
-> Just op
|
|
| otherwise
|
|
-> Nothing
|
|
|
|
isOperator :: String -> Bool
|
|
isOperator op = any (== op) operatorNames
|
|
|
|
operatorNames :: [String]
|
|
operatorNames =
|
|
["??"
|
|
,"@@"
|
|
,"$$"
|
|
,"%"
|
|
,"%%"
|
|
,"&&"
|
|
,"<%"
|
|
,"%>"
|
|
,"<&"
|
|
,"&>"
|
|
,"<]"
|
|
,"[>"
|
|
,"<@"
|
|
,"@>"
|
|
,"@"
|
|
,"++"
|
|
,"!!"
|
|
,"=="
|
|
,"^"
|
|
,"-"
|
|
,"MINUS"
|
|
,"~"
|
|
,"NOT"
|
|
,"+"
|
|
,"*"
|
|
,"/"
|
|
,"\\"
|
|
,"REM"
|
|
,"PLUS"
|
|
,"TIMES"
|
|
,"AFTER"
|
|
,"/\\"
|
|
,"\\/"
|
|
,"><"
|
|
,"BITNOT"
|
|
,"BITAND"
|
|
,"BITOR"
|
|
,"<<"
|
|
,">>"
|
|
,"AND"
|
|
,"OR"
|
|
,"="
|
|
,"<>"
|
|
,"<="
|
|
,"<"
|
|
,">="
|
|
,">"
|
|
]
|
|
|
|
-- | This gives a default mapping from operator (such as "+") to a valid name string
|
|
-- to be used in the backend (i.e. the Tock support headers), such as "add", which
|
|
-- will later be suffixed by the types in question.
|
|
occamOperatorTranslateDefault :: String -> String
|
|
occamOperatorTranslateDefault "+" = "add"
|
|
occamOperatorTranslateDefault "-" = "subtr"
|
|
occamOperatorTranslateDefault "*" = "mul"
|
|
occamOperatorTranslateDefault "/" = "div"
|
|
occamOperatorTranslateDefault "TIMES" = "times"
|
|
occamOperatorTranslateDefault "PLUS" = "plus"
|
|
occamOperatorTranslateDefault "MINUS" = "minus"
|
|
occamOperatorTranslateDefault "AFTER" = "after"
|
|
occamOperatorTranslateDefault ">" = "more"
|
|
occamOperatorTranslateDefault "<" = "less"
|
|
occamOperatorTranslateDefault ">=" = "moreEq"
|
|
occamOperatorTranslateDefault "<=" = "lessEq"
|
|
occamOperatorTranslateDefault "=" = "eq"
|
|
occamOperatorTranslateDefault "<>" = "notEq"
|
|
occamOperatorTranslateDefault "\\" = "rem"
|
|
occamOperatorTranslateDefault "REM" = "REM"
|
|
occamOperatorTranslateDefault "/\\" = "and"
|
|
occamOperatorTranslateDefault "\\/" = "or"
|
|
occamOperatorTranslateDefault "><" = "xor"
|
|
occamOperatorTranslateDefault "<<" = "lshift"
|
|
occamOperatorTranslateDefault ">>" = "rshift"
|
|
occamOperatorTranslateDefault "AND" = "and"
|
|
occamOperatorTranslateDefault "OR" = "or"
|
|
occamOperatorTranslateDefault "NOT" = "not"
|
|
occamOperatorTranslateDefault "~" = "not"
|
|
occamOperatorTranslateDefault cs = "op_" ++ concatMap (show . ord) cs
|
|
|
|
occamDefaultOperator :: String -> [A.Type] -> String
|
|
occamDefaultOperator op ts = "occam_" ++ occamOperatorTranslateDefault op
|
|
++ concatMap (('_':) . showOccam) ts
|
|
|
|
occamBuiltInOperatorFunctions :: [String]
|
|
occamBuiltInOperatorFunctions
|
|
= [occamDefaultOperator n ts
|
|
| (n, _, ts) <- occamIntrinsicOperators]
|
|
|
|
occamBuiltInOperatorFunctionsSet :: Set.Set String
|
|
occamBuiltInOperatorFunctionsSet = Set.fromList occamBuiltInOperatorFunctions
|
|
|
|
-- | Add one to an expression.
|
|
addOne :: (CSMR m, Die m) => A.Expression -> m A.Expression
|
|
addOne e = addExprs (makeConstant m 1) e
|
|
where m = findMeta e
|
|
|
|
-- | Subtrace one from an expression.
|
|
subOne :: (CSMR m, Die m) => A.Expression -> m A.Expression
|
|
subOne e = subExprs e (makeConstant m 1)
|
|
where m = findMeta e
|
|
|
|
-- | Add one to an expression.
|
|
addOneInt :: A.Expression -> A.Expression
|
|
addOneInt e = addExprsInt (makeConstant m 1) e
|
|
where m = findMeta e
|
|
|
|
-- | Subtrace one from an expression.
|
|
subOneInt :: A.Expression -> A.Expression
|
|
subOneInt e = subExprsInt e (makeConstant m 1)
|
|
where m = findMeta e
|
|
|
|
type DyadicExpr = A.Expression -> A.Expression -> A.Expression
|
|
type DyadicExprM = (CSMR m, Die m) => A.Expression -> A.Expression -> m A.Expression
|
|
|
|
dyadicExpr' :: (A.Type, A.Type) -> String -> DyadicExpr
|
|
dyadicExpr' (t0, t1) op a b
|
|
= A.FunctionCall m (A.Name m $ occamDefaultOperator op [t0,t1]) [a, b]
|
|
where
|
|
m = findMeta a
|
|
|
|
dyadicExpr :: String -> DyadicExprM
|
|
dyadicExpr op a b = do ta <- astTypeOf a
|
|
tb <- astTypeOf b
|
|
return $ dyadicExpr' (ta, tb) op a b
|
|
|
|
dyadicExprInt :: String -> DyadicExpr
|
|
dyadicExprInt op = dyadicExpr' (A.Int, A.Int) op
|
|
|
|
-- | Add two expressions.
|
|
addExprs :: DyadicExprM
|
|
addExprs = dyadicExpr "+"
|
|
|
|
-- | Add two expressions.
|
|
subExprs :: DyadicExprM
|
|
subExprs = dyadicExpr "-"
|
|
|
|
-- | Multiply two expressions.
|
|
mulExprs :: DyadicExprM
|
|
mulExprs = dyadicExpr "*"
|
|
|
|
-- | Divide two expressions.
|
|
divExprs :: DyadicExprM
|
|
divExprs = dyadicExpr "/"
|
|
|
|
-- | Divide two expressions.
|
|
remExprs :: DyadicExprM
|
|
remExprs = dyadicExpr "\\"
|
|
|
|
-- | Add two expressions.
|
|
addExprsInt :: DyadicExpr
|
|
addExprsInt = dyadicExpr' (A.Int,A.Int) "+"
|
|
|
|
-- | Add two expressions.
|
|
subExprsInt :: DyadicExpr
|
|
subExprsInt = dyadicExpr' (A.Int,A.Int) "-"
|
|
|
|
-- | Multiply two expressions.
|
|
mulExprsInt :: DyadicExpr
|
|
mulExprsInt = dyadicExpr' (A.Int,A.Int) "*"
|
|
|
|
-- | Divide two expressions.
|
|
divExprsInt :: DyadicExpr
|
|
divExprsInt = dyadicExpr' (A.Int,A.Int) "/"
|