1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
|
{-# OPTIONS_FRONTEND -Wno-incomplete-patterns #-}
module Analysis.Termination
( terminationAnalysis, showTermination
, productivityAnalysis, showProductivity, Productivity(..)
) where
import Data.Char (isDigit)
import Data.List
import Data.SCC (scc)
import FlatCurry.Types
import FlatCurry.Goodies
import RW.Base
import System.IO
import Analysis.Types
import Analysis.ProgInfo
import Analysis.RootReplaced (rootCyclicAnalysis)
terminationAnalysis :: Analysis Bool
terminationAnalysis = dependencyFuncAnalysis "Terminating" False isTerminating
showTermination :: AOutFormat -> Bool -> String
showTermination AText True = "terminating"
showTermination ANote True = ""
showTermination AText False = "possibly non-terminating"
showTermination ANote False = "maybe not term."
isTerminating :: FuncDecl -> [(QName,Bool)] -> Bool
isTerminating (Func qfunc _ _ _ rule) calledFuncs = hasTermRule rule
where
hasTermRule (Rule args e) = hasTermExp (map (\a -> (a,[])) args) e
hasTermRule (External _) = True
hasTermExp _ (Var _) = True
hasTermExp _ (Lit _) = True
hasTermExp _ (Free _ _) = False
hasTermExp args (Let bs e) =
let sccs = scc ((:[]) . fst) (allVars . snd) bs
in if any (\scc -> any (`elem` concatMap allVars (map snd scc))
(map fst scc))
sccs
then False
else all (hasTermExp args) (e : map snd bs)
hasTermExp args (Or e1 e2) =
hasTermExp args e1 && hasTermExp args e2
hasTermExp args (Case _ e bs) =
hasTermExp args e &&
all (\ (Branch pt be) -> hasTermExp (addSmallerArgs args e pt) be) bs
hasTermExp args (Typed e _) = hasTermExp args e
hasTermExp args (Comb ct qf es) =
case ct of
ConsCall -> all (hasTermExp args) es
ConsPartCall _ -> all (hasTermExp args) es
_ -> (if qf == qfunc
then any isSmallerArg (zip args es)
else maybe False id (lookup qf calledFuncs)) &&
all (hasTermExp args) es
isSmallerArg ((_,sargs),exp) = case exp of
Var v -> v `elem` sargs
_ -> False
addSmallerArgs :: [(Int, [Int])] -> Expr -> Pattern -> [(Int, [Int])]
addSmallerArgs args de pat =
case de of
Var v -> maybe args
(\argpos -> let (av,vs) = args!!argpos
in replace (av, varsOf pat ++ vs) argpos args)
(findIndex (isInArg v) args)
_ -> args
where
varsOf (LPattern _) = []
varsOf (Pattern _ pargs) = pargs
isInArg v (argv,svs) = v==argv || v `elem` svs
data Productivity =
NoInfo
| Terminating
| DCalls [QName]
| Looping
deriving (Eq, Ord, Show, Read)
productivityAnalysis :: Analysis Productivity
productivityAnalysis =
combinedDependencyFuncAnalysis "Productive"
terminationAnalysis
NoInfo
isProductive
showProductivity :: AOutFormat -> Productivity -> String
showProductivity _ NoInfo = "no info"
showProductivity _ Terminating = "terminating"
showProductivity _ (DCalls qfs) =
"productive / calls: " ++ "[" ++ intercalate ", " (map snd qfs) ++ "]"
showProductivity _ Looping = "possibly looping"
lubProd :: Productivity -> Productivity -> Productivity
lubProd Looping _ = Looping
lubProd (DCalls _ ) Looping = Looping
lubProd (DCalls xs) (DCalls ys) = DCalls (sort (union xs ys))
lubProd (DCalls xs) Terminating = DCalls xs
lubProd (DCalls xs) NoInfo = DCalls xs
lubProd Terminating p = if p==NoInfo then Terminating else p
lubProd NoInfo p = p
isProductive :: ProgInfo Bool -> FuncDecl -> [(QName,Productivity)]
-> Productivity
isProductive terminfo (Func qf _ _ _ rule) calledFuncs = hasProdRule rule
where
hasProdRule (External _) = Terminating
hasProdRule (Rule _ e) =
case hasProdExp False e of
DCalls fs -> if qf `elem` fs then Looping else DCalls fs
prodinfo -> prodinfo
hasProdExp _ (Var _) = Terminating
hasProdExp _ (Lit _) = Terminating
hasProdExp bc (Free _ e) =
lubProd (DCalls []) (hasProdExp bc e)
hasProdExp bc (Let bs e) =
let sccs = scc ((:[]) . fst) (allVars . snd) bs
in if any (\scc -> any (`elem` concatMap allVars (map snd scc))
(map fst scc))
sccs
then Looping
else foldr lubProd (hasProdExp bc e)
(map (\ (_,be) -> hasProdExp bc be) bs)
hasProdExp bc (Or e1 e2) = lubProd (hasProdExp bc e1) (hasProdExp bc e2)
hasProdExp bc (Case _ e bs) =
foldr lubProd (hasProdExp bc e)
(map (\ (Branch _ be) -> hasProdExp bc be) bs)
hasProdExp bc (Typed e _) = hasProdExp bc e
hasProdExp bc (Comb ct qg es) = case ct of
ConsCall -> cprodargs
ConsPartCall _ -> cprodargs
FuncCall -> if qg == ("Prelude","?")
then fprodargs
else funCallInfo
FuncPartCall _ -> funCallInfo
where
cprodargs = foldr lubProd NoInfo (map (hasProdExp True) es)
fprodargs = foldr lubProd NoInfo (map (hasProdExp bc ) es)
funCallInfo =
let prodinfo = if fprodargs <= Terminating
then if maybe False id (lookupProgInfo qg terminfo)
then Terminating
else lubProd (DCalls [qg])
(maybe Looping id (lookup qg calledFuncs))
else Looping
in if not bc then prodinfo
else case prodinfo of
DCalls _ -> DCalls []
_ -> prodinfo
instance ReadWrite Productivity where
readRW _ ('0' : r0) = (NoInfo,r0)
readRW _ ('1' : r0) = (Terminating,r0)
readRW strs ('2' : r0) = (DCalls a',r1)
where
(a',r1) = readRW strs r0
readRW _ ('3' : r0) = (Looping,r0)
showRW _ strs0 NoInfo = (strs0,showChar '0')
showRW _ strs0 Terminating = (strs0,showChar '1')
showRW params strs0 (DCalls a') = (strs1,showChar '2' . show1)
where
(strs1,show1) = showRW params strs0 a'
showRW _ strs0 Looping = (strs0,showChar '3')
writeRW _ h NoInfo strs = hPutChar h '0' >> return strs
writeRW _ h Terminating strs = hPutChar h '1' >> return strs
writeRW params h (DCalls a') strs =
hPutChar h '2' >> writeRW params h a' strs
writeRW _ h Looping strs = hPutChar h '3' >> return strs
typeOf _ = monoRWType "Productivity"
|