0 | module Core.Termination.SizeChange
2 | import Core.Context.Log
4 | import Core.Termination.References
6 | import Libraries.Data.NameMap
8 | import Libraries.Data.SparseMatrix
11 | import Data.SortedSet
36 | Change = Matrix SizeChange
46 | CallSeq = List (FC, Name)
59 | WorkList = SortedSet (Name, Name, Graph)
73 | (==) a1 a2 = a1.change == a2.change
76 | compare a1 a2 = compare a1.change a2.change
79 | show a = show a.change ++ "\n via call seq: " ++ show a.seq ++ "\n"
88 | lookupMap : Name -> NameMap (NameMap v) -> NameMap v
89 | lookupMap n m = fromMaybe empty (lookup n m)
91 | lookupSet : Ord v => Name -> NameMap (SortedSet v) -> SortedSet v
92 | lookupSet n m = fromMaybe empty (lookup n m)
94 | lookupGraphs : Name -> Name -> SCSet -> SortedSet Graph
95 | lookupGraphs f g = lookupSet g . lookupMap f
98 | selectDom : Name -> SCSet -> SCSet
99 | selectDom f s = insert f (lookupMap f s) empty
102 | selectCod : Name -> SCSet -> SCSet
103 | selectCod g s = map (\m => insert g (lookupSet g m) empty) s
105 | foldl : (acc -> Name -> Name -> Graph -> acc) -> acc -> SCSet -> acc
107 | = foldlNames (\acc, f =>
108 | foldlNames (\acc, g =>
109 | foldl (\acc, c => f_acc acc f g c) acc)
113 | insertGraph : (f, g : Name) -> Graph -> SCSet -> SCSet
114 | insertGraph f g ch s
115 | = let s_f = lookupMap f s in
116 | let s_fg = lookupSet g s_f in
117 | insert f (insert g (insert ch s_fg) s_f) s
124 | composeChange : Change -> Change -> Change
131 | composeGraph : Graph -> Graph -> Graph
134 | (composeChange a1.change a2.change)
138 | preCompose : (f : Name) -> Graph ->
141 | (g : Name) -> (h : Name) -> Graph ->
143 | preCompose f ch1 s work _ h ch2
144 | = let ch : Graph = composeGraph ch1 ch2 in
145 | if contains ch (lookupGraphs f h s) then
148 | insert (f, h, ch) work
151 | postCompose : (h : Name) -> Graph ->
154 | (f : Name) -> (g : Name) -> Graph ->
156 | postCompose h ch2 s work f _ ch1
157 | = let ch : Graph = composeGraph ch1 ch2 in
158 | if contains ch (lookupGraphs f h s) then
161 | insert (f, h, ch) work
164 | addGraph : (f, g : Name) -> Graph ->
168 | addGraph f g ch work s_in
169 | = let s = insertGraph f g ch s_in
174 | after = selectDom g s
175 | work_pre = foldl (preCompose f ch s) work after
178 | before = selectCod f s
179 | work_post = foldl (postCompose g ch s) work_pre before in
182 | transitiveClosure work_post s
184 | transitiveClosure : WorkList ->
187 | transitiveClosure work s
190 | Just ((f, g, ch), work) =>
191 | addGraph f g ch work s
194 | prefixCallSeq : NameMap (FC, Name) -> (g : Name) -> CallSeq
195 | prefixCallSeq pred g = go g []
197 | go : Name -> CallSeq -> CallSeq
199 | = let Just (l, f) = lookup g pred
200 | | Nothing => seq in
204 | go f ((l, g) :: seq)
208 | checkNonDesc : (a : Graph) -> Maybe Graph
209 | checkNonDesc a = if any selfDecArc a.change then Nothing else Just a
211 | selfDecArc : (Nat, Vector1 SizeChange) -> Bool
212 | selfDecArc (i, xs) = lookupOrd i xs == Just Smaller
215 | findLoops : SCSet ->
216 | NameMap (Maybe (Graph ))
220 | let loops = filterEndos (\a => composeChange a.change a.change == a.change) s in
222 | map (foldMap checkNonDesc) loops
224 | filterEndos : (Graph -> Bool) -> SCSet -> NameMap (List Graph)
225 | filterEndos p = mapWithKey (\f, m => filter p (Prelude.toList (lookupSet f m)))
227 | findNonTerminatingLoop : SCSet -> Maybe (Name, Graph)
228 | findNonTerminatingLoop s = findNonTerminating (findLoops s)
232 | findNonTerminating : NameMap (Maybe Graph) -> Maybe (Name, Graph)
233 | findNonTerminating = foldlNames (\acc, g, m => map (g,) m <+> acc) empty
236 | setPrefixTerminating : {auto c : Ref Ctxt Defs} ->
237 | CallSeq -> Name -> Core ()
238 | setPrefixTerminating [] g = pure ()
239 | setPrefixTerminating (_ :: []) g = pure ()
240 | setPrefixTerminating ((l, f) :: p) g
241 | = do setTerminating l f (NotTerminating (BadPath p g))
242 | setPrefixTerminating p g
244 | addFunctions : {auto c : Ref Ctxt Defs} ->
247 | NameMap (FC, Name) ->
249 | Core (Either Terminating (WorkList, NameMap (FC, Name)))
250 | addFunctions defs [] pred work
251 | = pure $
Right (work, pred)
252 | addFunctions defs (d1 :: ds) pred work
253 | = do calls <- foldlC resolveCall [] d1.sizeChange
254 | let Nothing = find isNonTerminating calls
255 | | Just (d2, l, _) =>
256 | do let g = d2.fullname
257 | let init = prefixCallSeq pred d1.fullname ++ [(l, g)]
258 | setPrefixTerminating init g
259 | pure $
Left (NotTerminating (BadPath init g))
260 | let (ds, pred, work) = foldl addCall (ds, pred, work) (filter isUnchecked calls)
261 | addFunctions defs ds pred work
263 | resolveCall : List (GlobalDef, FC, (Name, Name, Graph)) ->
265 | Core (List (GlobalDef, FC, (Name, Name, Graph)))
266 | resolveCall calls (MkSCCall g ch l)
267 | = do Just d2 <- lookupCtxtExact g (gamma defs)
268 | | Nothing => do logC "totality.termination.calc" 7 $
do pure "Not found: \{show g}"
270 | pure ((d2, l, (d1.fullname, d2.fullname, MkGraph ch [(l, d2.fullname)])) :: calls)
272 | addCall : (List GlobalDef, NameMap (FC, Name), WorkList) ->
273 | (GlobalDef, FC, (Name, Name, Graph)) ->
274 | (List GlobalDef, NameMap (FC, Name), WorkList)
275 | addCall (ds, pred, work_in) (d2, l, c)
276 | = let work = insert c work_in in
277 | case lookup d2.fullname pred of
281 | (d2 :: ds, insert d2.fullname (l, d1.fullname) pred, work)
283 | isNonTerminating : (GlobalDef, _, _) -> Bool
284 | isNonTerminating (d2, _, _)
285 | = case d2.totality.isTerminating of
286 | NotTerminating _ => True
289 | isUnchecked : (GlobalDef, _, _) -> Bool
290 | isUnchecked (d2, _, _)
291 | = case d2.totality.isTerminating of
295 | initWork : {auto c : Ref Ctxt Defs} ->
298 | Core (Either Terminating (WorkList, NameMap (FC, Name)))
299 | initWork defs def = addFunctions defs [def] (insert def.fullname (def.location, def.fullname) empty) empty
302 | calcTerminating : {auto c : Ref Ctxt Defs} ->
303 | FC -> Name -> Core Terminating
304 | calcTerminating loc n
305 | = do defs <- get Ctxt
306 | logC "totality.termination.calc" 7 $
do pure $
"Calculating termination: " ++ show !(toFullNames n)
307 | Just def <- lookupCtxtExact n (gamma defs)
308 | | Nothing => undefinedName loc n
309 | IsTerminating <- totRefs defs (nub !(addCases defs (keys (refersTo def))))
311 | Right (work, pred) <- initWork defs def
312 | | Left bad => pure bad
313 | let s = transitiveClosure work initSCSet
314 | let Nothing = findNonTerminatingLoop s
315 | | Just (g, loop) =>
316 | ifThenElse (def.fullname == g)
317 | (pure $
NotTerminating (RecPath loop.seq))
318 | (do setTerminating EmptyFC g (NotTerminating (RecPath loop.seq))
319 | let init = prefixCallSeq pred g
320 | setPrefixTerminating init g
321 | pure $
NotTerminating (BadPath init g))
324 | addCases' : Defs -> NameMap () -> List Name -> Core (List Name)
325 | addCases' defs all [] = pure (keys all)
326 | addCases' defs all (n :: ns)
327 | = case lookup n all of
328 | Just _ => addCases' defs all ns
330 | if caseFn !(getFullName n)
331 | then case !(lookupCtxtExact n (gamma defs)) of
332 | Just def => addCases' defs (insert n () all)
333 | (keys (refersTo def) ++ ns)
334 | Nothing => addCases' defs (insert n () all) ns
335 | else addCases' defs (insert n () all) ns
337 | addCases : Defs -> List Name -> Core (List Name)
338 | addCases defs ns = addCases' defs empty ns