Added a new function that checks that variables are initialised before they are read in a control-flow graph, and added tests for it (that all now pass)
This commit is contained in:
parent
75ed35f2e3
commit
cf0cc81ae4
|
@ -24,11 +24,14 @@ module RainUsageCheck where
|
||||||
|
|
||||||
import Control.Monad.Identity
|
import Control.Monad.Identity
|
||||||
import Data.Generics
|
import Data.Generics
|
||||||
|
import Data.Graph.Inductive
|
||||||
import Data.List
|
import Data.List
|
||||||
|
import qualified Data.Map as Map
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import qualified Data.Set as Set
|
import qualified Data.Set as Set
|
||||||
|
|
||||||
import qualified AST as A
|
import qualified AST as A
|
||||||
|
import FlowAlgorithms
|
||||||
import FlowGraph
|
import FlowGraph
|
||||||
|
|
||||||
-- In Rain, Deref can't nest with Dir in either way, so this doesn't need to be a recursive type:
|
-- In Rain, Deref can't nest with Dir in either way, so this doesn't need to be a recursive type:
|
||||||
|
@ -245,3 +248,41 @@ parUsageCheck proc
|
||||||
subscriptedArrays' (A.SubscriptedVariable _ _ v) = Just v
|
subscriptedArrays' (A.SubscriptedVariable _ _ v) = Just v
|
||||||
subscriptedArrays' _ = Nothing
|
subscriptedArrays' _ = Nothing
|
||||||
-}
|
-}
|
||||||
|
|
||||||
|
|
||||||
|
-- TODO have some sort of error-message return if the check fails or if the code fails
|
||||||
|
checkInitVar :: FlowGraph (Maybe Decl, Vars) -> Node -> Bool
|
||||||
|
checkInitVar graph startNode = and $ map (checkInitVar' varWrittenBefore) (map readNode (labNodes graph))
|
||||||
|
where
|
||||||
|
readNode :: (Node, FNode (Maybe Decl, Vars)) -> (Node, Set.Set Var)
|
||||||
|
readNode (n, Node (_,(_,Vars read _ _ _))) = (n,read)
|
||||||
|
|
||||||
|
writeNode :: FNode (Maybe Decl, Vars) -> Set.Set Var
|
||||||
|
writeNode (Node (_,(_,Vars _ _ written _))) = written
|
||||||
|
|
||||||
|
-- Nothing is treated as if were the set of all possible variables (easier than building that set):
|
||||||
|
nodeFunction :: (Node, EdgeLabel) -> Set.Set Var -> Maybe (Set.Set Var) -> Set.Set Var
|
||||||
|
nodeFunction (n,_) inputVal Nothing = Set.union inputVal (maybe Set.empty writeNode (lab graph n))
|
||||||
|
nodeFunction (n, EEndPar _) inputVal (Just prevAgg) = Set.unions [inputVal,prevAgg,maybe Set.empty writeNode (lab graph n)]
|
||||||
|
nodeFunction (n, _) inputVal (Just prevAgg) = Set.intersection prevAgg $ Set.union inputVal (maybe Set.empty writeNode (lab graph n))
|
||||||
|
|
||||||
|
graphFuncs :: GraphFuncs Node EdgeLabel (Set.Set Var)
|
||||||
|
graphFuncs = GF
|
||||||
|
{
|
||||||
|
nodeFunc = nodeFunction
|
||||||
|
,prevNodes = lpre graph
|
||||||
|
,nextNodes = lsuc graph
|
||||||
|
,initVal = Set.empty
|
||||||
|
,defVal = Set.empty
|
||||||
|
}
|
||||||
|
|
||||||
|
varWrittenBefore :: Map.Map Node (Set.Set Var)
|
||||||
|
varWrittenBefore = flowAlgorithm graphFuncs (nodes graph) startNode
|
||||||
|
|
||||||
|
checkInitVar' :: Map.Map Node (Set.Set Var) -> (Node, Set.Set Var) -> Bool
|
||||||
|
checkInitVar' writtenMap (n,v)
|
||||||
|
= case Map.lookup n writtenMap of
|
||||||
|
Nothing -> False
|
||||||
|
-- All read vars should be in the previously-written set
|
||||||
|
Just vs -> v `Set.isSubsetOf` vs
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,13 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
module RainUsageCheckTest (tests) where
|
module RainUsageCheckTest (tests) where
|
||||||
|
|
||||||
|
import Data.Graph.Inductive
|
||||||
import Prelude hiding (fail)
|
import Prelude hiding (fail)
|
||||||
import Test.HUnit
|
import Test.HUnit
|
||||||
|
|
||||||
|
|
||||||
import qualified AST as A
|
import qualified AST as A
|
||||||
|
import FlowGraph
|
||||||
import Metadata
|
import Metadata
|
||||||
import RainUsageCheck
|
import RainUsageCheck
|
||||||
import TestUtil
|
import TestUtil
|
||||||
|
@ -129,10 +131,62 @@ testParUsageCheck = TestList (map doTest tests)
|
||||||
--TODO add tests for initialising a variable before use.
|
--TODO add tests for initialising a variable before use.
|
||||||
--TODO especially test things like only initialising the variable in one part of an if
|
--TODO especially test things like only initialising the variable in one part of an if
|
||||||
|
|
||||||
|
buildTestFlowGraph :: [(Int, [Var], [Var], [Var])] -> [(Int, Int, EdgeLabel)] -> Int -> Int -> String -> FlowGraph (Maybe Decl, Vars)
|
||||||
|
buildTestFlowGraph ns es start end v
|
||||||
|
= mkGraph
|
||||||
|
([(-1,Node (emptyMeta,(Just $ ScopeIn v, emptyVars))),(-2,Node (emptyMeta,(Just $ ScopeOut v,emptyVars)))] ++ (map transNode ns))
|
||||||
|
([(-1,start,ESeq),(end,-2,ESeq)] ++ es)
|
||||||
|
where
|
||||||
|
transNode :: (Int, [Var], [Var], [Var]) -> (Int, FNode (Maybe Decl, Vars))
|
||||||
|
transNode (n,mr,mw,dw) = (n,Node (emptyMeta, (Nothing,vars mr mw dw [])))
|
||||||
|
|
||||||
|
testInitVar :: Test
|
||||||
|
testInitVar = TestList
|
||||||
|
[
|
||||||
|
testInitVarPass 0 [(0,[],[],[])] [] 0 0 "x"
|
||||||
|
,testInitVarPass 1 [(0,[],[],[Plain "x"])] [] 0 0 "x"
|
||||||
|
,testInitVarFail 2 [(0,[Plain "x"],[],[])] [] 0 0 "x"
|
||||||
|
,testInitVarFail 3 [(0,[Plain "x"],[],[Plain "x"])] [] 0 0 "x"
|
||||||
|
|
||||||
|
,testInitVarPass 10 [(0,[],[],[Plain "x"]), (1,[Plain "x"],[],[])] [(0,1,ESeq)] 0 1 "x"
|
||||||
|
,testInitVarFail 11 [(0,[],[],[Plain "x"]), (1,[Plain "x"],[],[])] [(1,0,ESeq)] 1 0 "x"
|
||||||
|
,testInitVarFail 12 [(0,[],[Plain "x"],[]), (1,[Plain "x"],[],[])] [(0,1,ESeq)] 0 1 "x"
|
||||||
|
,testInitVarPass 13 [(0,[],[],[Plain "x"]), (1,[Plain "x"],[],[])] [(0,1,EStartPar 0)] 0 1 "x"
|
||||||
|
,testInitVarPass 14 [(0,[],[],[Plain "x"]), (1,[Plain "x"],[],[])] [(0,1,EEndPar 0)] 0 1 "x"
|
||||||
|
|
||||||
|
,testInitVarPass 20 [(0,[],[],[]),(1,[],[],[Plain "x"]), (2,[],[],[Plain "x"]), (3,[Plain "x"],[],[])] [(0,1,ESeq),(0,2,ESeq),(1,3,ESeq),(2,3,ESeq)] 0 3 "x"
|
||||||
|
,testInitVarFail 21 [(0,[],[],[]),(1,[],[],[]), (2,[],[],[Plain "x"]), (3,[Plain "x"],[],[])] [(0,1,ESeq),(0,2,ESeq),(1,3,ESeq),(2,3,ESeq)] 0 3 "x"
|
||||||
|
,testInitVarFail 22 [(0,[],[],[]),(1,[],[Plain "x"],[]), (2,[],[],[Plain "x"]), (3,[Plain "x"],[],[])] [(0,1,ESeq),(0,2,ESeq),(1,3,ESeq),(2,3,ESeq)] 0 3 "x"
|
||||||
|
,testInitVarPass 23 [(0,[],[],[]),(1,[],[],[]), (2,[],[],[Plain "x"]), (3,[Plain "x"],[],[])] [(0,1,ESeq),(0,2,ESeq),(2,3,ESeq)] 0 3 "x"
|
||||||
|
,testInitVarPass 24 [(0,[],[],[Plain "x"]),(1,[],[],[]), (2,[],[],[]), (3,[Plain "x"],[],[])] [(0,1,ESeq),(0,2,ESeq),(1,3,ESeq),(2,3,ESeq)] 0 3 "x"
|
||||||
|
,testInitVarFail 25 [(0,[],[],[]),(1,[],[],[]), (2,[],[],[]), (3,[Plain "x"],[],[])] [(0,1,ESeq),(0,2,ESeq),(1,3,ESeq),(2,3,ESeq)] 0 3 "x"
|
||||||
|
|
||||||
|
-- This would fail CREW, but that's not what we're testing here:
|
||||||
|
,testInitVarPass 30 [(0,[],[],[]),(1,[],[],[Plain "x"]), (2,[],[],[Plain "x"]), (3,[Plain "x"],[],[])]
|
||||||
|
[(0,1,EStartPar 0),(0,2,EStartPar 0),(1,3,EEndPar 0),(2,3,EEndPar 0)] 0 3 "x"
|
||||||
|
,testInitVarPass 31 [(0,[],[],[]),(1,[],[],[Plain "x"]), (2,[],[],[]), (3,[Plain "x"],[],[])]
|
||||||
|
[(0,1,EStartPar 0),(0,2,EStartPar 0),(1,3,EEndPar 0),(2,3,EEndPar 0)] 0 3 "x"
|
||||||
|
,testInitVarPass 32 [(0,[],[],[Plain "x"]),(1,[],[],[]), (2,[],[],[]), (3,[Plain "x"],[],[])]
|
||||||
|
[(0,1,EStartPar 0),(0,2,EStartPar 0),(1,3,EEndPar 0),(2,3,EEndPar 0)] 0 3 "x"
|
||||||
|
,testInitVarFail 33 [(0,[],[],[]),(1,[],[],[]), (2,[],[],[]), (3,[Plain "x"],[],[])]
|
||||||
|
[(0,1,EStartPar 0),(0,2,EStartPar 0),(1,3,EEndPar 0),(2,3,EEndPar 0)] 0 3 "x"
|
||||||
|
|
||||||
|
|
||||||
|
-- TODO add tests with loops (and work out how to represent par loops)
|
||||||
|
]
|
||||||
|
where
|
||||||
|
testInitVarPass :: Int -> [(Int, [Var], [Var], [Var])] -> [(Int, Int, EdgeLabel)] -> Int -> Int -> String -> Test
|
||||||
|
testInitVarPass testNum ns es start end v = TestCase $ assertBool ("testInitVar " ++ show testNum) $ checkInitVar (buildTestFlowGraph ns es start end v) (-1)
|
||||||
|
|
||||||
|
testInitVarFail :: Int -> [(Int, [Var], [Var], [Var])] -> [(Int, Int, EdgeLabel)] -> Int -> Int -> String -> Test
|
||||||
|
testInitVarFail testNum ns es start end v = TestCase $ assertBool ("testInitVar " ++ show testNum) $ not $ checkInitVar (buildTestFlowGraph ns es start end v) (-1)
|
||||||
|
|
||||||
|
|
||||||
tests :: Test
|
tests :: Test
|
||||||
tests = TestList
|
tests = TestList
|
||||||
[
|
[
|
||||||
testGetVarProc
|
testGetVarProc
|
||||||
|
,testInitVar
|
||||||
-- ,testParUsageCheck
|
-- ,testParUsageCheck
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user