tock-mirror/pass/Properties.hs
Adam Sampson 3340e95806 Make agg_typesDone include constantsFolded.
This is so that constant folding gets done early; it's important that it
doesn't happen after anything that tries to pull values into variables later
on.
2008-03-17 15:47:57 +00:00

354 lines
12 KiB
Haskell

{-
Tock: a compiler for parallel languages
Copyright (C) 2007 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 Properties
( agg_namesDone
, agg_functionsGone
, agg_typesDone
, afterRemoved
, allChansToAnyOrProtocol
, arrayLiteralsExpanded
, arraySizesDeclared
, assignFlattened
, assignParRemoved
, constantsFolded
, declarationsUnique
, declarationTypesRecorded
, declaredNamesResolved
, eachRangeTransformed
, eachTransformed
, expressionTypesChecked
, freeNamesToArgs
, functionCallsRemoved
, functionsRemoved
, inferredTypesRecorded
, inputCaseRemoved
, intLiteralsInBounds
, mainTagged
, nestedPulled
, noInt
, outExpressionRemoved
, parsIdentified
, parsWrapped
, parUsageChecked
, processTypesChecked
, rainParDeclarationsPulledUp
, rangeTransformed
, seqInputsFlattened
, slicesSimplified
, subscriptsPulledUp
, typesResolvedInAST
, typesResolvedInState
, waitForRemoved
)
where
import Control.Monad
import Data.Generics
import Data.Int
import Data.List
import qualified Data.Map as Map
import Data.Maybe
import Data.Word
import Text.Regex
import qualified AST as A
import CompState
import Errors
import Metadata
import Pass
import PrettyShow
import Types
import Utils
agg_namesDone :: [Property]
agg_namesDone = [declarationsUnique, declarationTypesRecorded, inferredTypesRecorded, declaredNamesResolved]
agg_typesDone :: [Property]
agg_typesDone = [expressionTypesChecked, inferredTypesRecorded, processTypesChecked, typesResolvedInAST, typesResolvedInState, constantsFolded]
agg_functionsGone :: [Property]
agg_functionsGone = [functionCallsRemoved, functionsRemoved]
-- Mark out all the checks I still need to implement:
checkTODO :: Monad m => A.AST -> m ()
checkTODO _ = return ()
-- For properties that can't easily be tested (such as properties that are themselves tests anyway!)
nocheck :: Monad m => A.AST -> m ()
nocheck _ = return ()
getDeclaredNames :: A.AST -> [A.Name]
getDeclaredNames = everything (++) ([] `mkQ` find)
where
find :: A.Specification -> [A.Name]
find (A.Specification _ n (A.Declaration {})) = [n]
find _ = []
checkNull :: (Data a, Die m) => String -> [a] -> m ()
checkNull _ [] = return ()
checkNull s xs = dieP (findMeta xs) $ "Property check " ++ show s ++ " failed: " ++ pshow xs
isNonceOrUnique :: String -> Bool
isNonceOrUnique nm = isJust $ matchRegex (mkRegex ".*_[a-z][0-9]+$") nm
declaredNamesResolved :: Property
declaredNamesResolved = Property "declaredNamesResolved" $
checkNull "namesResolved" . filter (not . isNonceOrUnique . A.nameName) . getDeclaredNames
noInt :: Property
noInt = Property "noInt" $
checkNull "noInt" . listify (== A.Int)
declarationTypesRecorded :: Property
declarationTypesRecorded = Property "declarationTypesRecorded" $ \t ->
do let decls = getDeclaredNames t
st <- getCompState
mapM_ (checkName (csNames st)) decls
where
checkName :: Die m => Map.Map String A.NameDef -> A.Name -> m ()
checkName nms n = case Map.lookup (A.nameName n) nms of
Nothing -> dieP m $ "Type of name " ++ show (A.nameName n) ++ " was not recorded"
Just nd -> when (A.ndName nd /= A.nameName n) $
dieP m $ "Name not recorded correctly: " ++ show (A.nameName n)
where m = A.nameMeta n
declarationsUnique :: Property
declarationsUnique = Property "declarationsUnique" $
checkDupes . sort . getDeclaredNames
where
checkDupes :: Die m => [A.Name] -> m ()
checkDupes [] = return ()
checkDupes (n:[]) = return ()
checkDupes (n:n':ns)
= do when (A.nameName n == A.nameName n') $
dieP (A.nameMeta n) $ "Duplicate definition of name (names not uniquified successfully?) " ++ show (A.nameName n) ++ " with: " ++ show (A.nameMeta n')
checkDupes (n':ns)
constantsFolded :: Property
constantsFolded = Property "constantsFolded" checkTODO
intLiteralsInBounds :: Property
intLiteralsInBounds = Property "intLiteralsInBounds" $
mapM_ check . everything (++) ([] `mkQ` find)
where
find :: A.Expression -> [(Meta, A.Type, String)]
find (A.Literal m t (A.IntLiteral _ s)) = [(m,t,s)]
find _ = []
toPair :: (Monad m, Integral a) => [a] -> m (Integer, Integer)
toPair [x,y] = return (toInteger x, toInteger y)
occToBounds :: Die m => Meta -> A.Type -> m (Integer, Integer)
occToBounds _ A.Byte = toPair ([minBound, maxBound] :: [Word8])
occToBounds _ A.UInt16 = toPair ([minBound, maxBound] :: [Word16])
occToBounds _ A.UInt32 = toPair ([minBound, maxBound] :: [Word32])
occToBounds _ A.UInt64 = toPair ([minBound, maxBound] :: [Word64])
occToBounds _ A.Int8 = toPair ([minBound, maxBound] :: [Int8])
occToBounds _ A.Int16 = toPair ([minBound, maxBound] :: [Int16])
occToBounds _ A.Int32 = toPair ([minBound, maxBound] :: [Int32])
occToBounds _ A.Int = toPair ([minBound, maxBound] :: [Int32])
occToBounds _ A.Int64 = toPair ([minBound, maxBound] :: [Int64])
occToBounds m t = dieP m $ "Type " ++ show t ++ " is not an integer type"
check :: Die m => (Meta, A.Type, String) -> m ()
check (m, t, s)
= do (low, high) <- occToBounds m t
when (n < low) $ dieP m $ "Integer not within lower bound: " ++ s
when (n > high) $ dieP m $ "Integer not within upper bound: " ++ s
where
n :: Integer
n = read s
expressionTypesChecked :: Property
expressionTypesChecked = Property "expressionTypesChecked" nocheck
processTypesChecked :: Property
processTypesChecked = Property "processTypesChecked" nocheck
eachRangeTransformed :: Property
eachRangeTransformed = Property "eachRangeTransformed" checkTODO
eachTransformed :: Property
eachTransformed = Property "eachTransformed" checkTODO
rangeTransformed :: Property
rangeTransformed = Property "rangeTransformed" checkTODO
rainParDeclarationsPulledUp :: Property
rainParDeclarationsPulledUp = Property "rainParDeclarationsPulledUp" checkTODO
inferredTypesRecorded :: Property
inferredTypesRecorded = Property "inferredTypesRecorded" checkTODO
findUDT :: A.Type -> Bool
findUDT (A.UserDataType {}) = True
findUDT _ = False
typesResolvedInAST :: Property
typesResolvedInAST = Property "typesResolvedInAST" $
checkNull "typesResolvedInAST" . listify findUDT
typesResolvedInState :: Property
typesResolvedInState = Property "typesResolvedInState" $
\t -> checkNull "typesResolvedInState" . listify findUDT =<< getCompState
checkAllExprVariable :: Die m => [A.Expression] -> m ()
checkAllExprVariable = mapM_ check
where
check :: Die m => A.Expression -> m ()
check (A.ExprVariable {}) = return ()
check e = dieP (findMeta e) $ "Found something that was not an expression variable: " ++ pshow e
findOutputExprs :: A.OutputItem -> [A.Expression]
findOutputExprs (A.OutExpression _ e) = [e]
findOutputExprs (A.OutCounted _ ce ae) = [ce, ae]
outExpressionRemoved :: Property
outExpressionRemoved = Property "outExpressionRemoved" $
checkAllExprVariable . everything (++) ([] `mkQ` findOutputExprs)
findInputCase :: A.InputMode -> Bool
findInputCase (A.InputCase {}) = True
findInputCase _ = False
inputCaseRemoved :: Property
inputCaseRemoved = Property "inputCaseRemoved" $
checkNull "inputCaseRemoved" . listify findInputCase
findParAssign :: A.Process -> Bool
findParAssign (A.Assign _ (_:_:_) _) = True
findParAssign _ = False
assignParRemoved :: Property
assignParRemoved = Property "assignParRemoved" $
checkNull "assignParRemoved" . listify findParAssign
findParWithProcess :: A.Process -> Bool
findParWithProcess (A.Par _ _ s) = findParProcess s
where
-- We don't use listify here because it would descend into the declarations
-- of the processes (for the wrapped PARs) and find A.Structured A.Process items
-- in SEQs in there
findParProcess :: A.Structured A.Process -> Bool
findParProcess (A.Only _ (A.ProcCall {})) = False
findParProcess (A.Only {}) = True
findParProcess (A.Rep _ _ s) = findParProcess s
findParProcess (A.ProcThen _ _ s) = findParProcess s
findParProcess (A.Spec _ _ s) = findParProcess s
findParProcess (A.Several _ ss) = or $ map findParProcess ss
findParWithProcess _ = False
parsWrapped :: Property
parsWrapped = Property "parsWrapped" $
checkNull "parsWrapped" . listify findParWithProcess
findAssignVars :: A.Process -> [A.Variable]
findAssignVars (A.Assign _ vs _) = vs
findAssignVars _ = []
filterArrayAndRecord :: (CSMR m, Die m) => A.Variable -> m Bool
filterArrayAndRecord v
= do t <- typeOfVariable v
return $ case t of
A.Array {} -> True
A.Record {} -> True
_ -> False
assignFlattened :: Property
assignFlattened = Property "assignFlattened" $
checkNull "assignFlattened" <.< (filterM filterArrayAndRecord . everything (++) ([] `mkQ` findAssignVars))
parUsageChecked :: Property
parUsageChecked = Property "parUsageChecked" nocheck
freeNamesToArgs :: Property
freeNamesToArgs = Property "freeNamesToArgs" checkTODO
nestedPulled :: Property
nestedPulled = Property "nestedPulled" checkTODO
findFunctions :: A.SpecType -> Bool
findFunctions (A.Function {}) = True
findFunctions _ = False
functionsRemoved :: Property
functionsRemoved = Property "functionsRemoved" $
checkNull "functionsRemoved" . listify findFunctions
afterRemoved :: Property
afterRemoved = Property "afterRemoved" $
checkNull "afterRemoved" . listify (== A.After)
arrayLiteralsExpanded :: Property
arrayLiteralsExpanded = Property "arrayLiteralsExpanded" checkTODO
findFunctionCalls :: A.Expression -> Bool
findFunctionCalls (A.FunctionCall {}) = True
findFunctionCalls _ = False
findFunctionCallLists :: A.ExpressionList -> Bool
findFunctionCallLists (A.FunctionCallList {}) = True
findFunctionCallLists _ = False
functionCallsRemoved :: Property
functionCallsRemoved = Property "functionCallsRemoved" $
\t -> checkNull "functionCallsRemoved/1" (listify findFunctionCalls t) >> checkNull "functionCallsRemoved/2" (listify findFunctionCallLists t)
subscriptsPulledUp :: Property
subscriptsPulledUp = Property "subscriptsPulledUp" checkTODO
parsIdentified :: Property
parsIdentified = Property "parsIdentified" nocheck
findWaitFor :: A.Alternative -> Bool
findWaitFor (A.AlternativeWait _ A.WaitFor _ _) = True
findWaitFor _ = False
waitForRemoved :: Property
waitForRemoved = Property "waitForRemoved" $
checkNull "waitForRemoved" . listify findWaitFor
allChansToAnyOrProtocol :: Property
allChansToAnyOrProtocol = Property "allChansToAnyOrProtocol" checkTODO
mainTagged :: Property
mainTagged = Property "mainTagged" nocheck
-- We don't check this because not having a main process may be valid in the future
-- so there's no easy way to check if the main process has been looked for or not
seqInputsFlattened :: Property
seqInputsFlattened = Property "seqInputsFlattened" $ checkNull "seqInputsFlattened" . listify findMultipleInputs
where
findMultipleInputs :: A.InputMode -> Bool
findMultipleInputs (A.InputSimple _ (_:_:_)) = True
findMultipleInputs _ = False
arraySizesDeclared :: Property
arraySizesDeclared = Property "arraySizesDeclared" nocheck
slicesSimplified :: Property
slicesSimplified = Property "slicesSimplified" $
checkNull "slicesSimplified" . listify findJustFromOrFor
where
findJustFromOrFor :: A.Subscript -> Bool
findJustFromOrFor (A.SubscriptFrom {}) = True
findJustFromOrFor (A.SubscriptFor {}) = True
findJustFromOrFor _ = False