on d34b7fc help sign in
Idris-dev/src/Idris/Coverage.hs
701 lines 30 kB history raw
1{-# LANGUAGE PatternGuards #-}
3module Idris.Coverage where
5import Idris.Core.TT
6import Idris.Core.Evaluate
7import Idris.Core.CaseTree
9import Idris.AbsSyntax
10import Idris.Delaborate
11import Idris.Error
12import Idris.Output (iputStrLn)
14import Data.List
15import Data.Either
16import Data.Maybe
17import Debug.Trace
19import Control.Monad.State.Strict
21-- Generate a pattern from an 'impossible' LHS. (Need this to eliminate the
22-- pattern clauses which have been provided explicitly from new clause
23-- generation.)
25mkPatTm :: PTerm -> Idris Term
26mkPatTm t = do i <- getIState
27 let timp = addImpl' True [] [] i t
28 evalStateT (toTT (mapPT deNS timp)) 0
29 where
30 toTT (PRef _ n) = do i <- lift getIState
31 case lookupDef n (tt_ctxt i) of
32 [TyDecl nt _] -> return $ P nt n Erased
33 _ -> return $ P Ref n Erased
34 toTT (PApp _ t args) = do t' <- toTT t
35 args' <- mapM (toTT . getTm) args
36 return $ mkApp t' args'
37 -- For alternatives, pick the first and drop the namespaces. It doesn't
38 -- really matter which is taken since matching will ignore the namespace.
39 toTT (PAlternative _ (a : as)) = toTT a
40 toTT _ = do v <- get
41 put (v + 1)
42 return (P Bound (sMN v "imp") Erased)
44 deNS (PRef f (NS n _)) = PRef f n
45 deNS t = t
47-- Given a list of LHSs, generate a extra clauses which cover the remaining
48-- cases. The ones which haven't been provided are marked 'absurd' so that the
49-- checker will make sure they can't happen.
51-- This will only work after the given clauses have been typechecked and the
52-- names are fully explicit!
54genClauses :: FC -> Name -> [Term] -> [PTerm] -> Idris [PTerm]
55genClauses fc n xs given
56 = do i <- getIState
57 let lhs_tms = map (\x -> delab' i x True True) xs
58 -- if a placeholder was given, don't bother generating cases for it
59 let lhs_tms' = zipWith mergePlaceholders lhs_tms given
60 let lhss = map pUnApply lhs_tms'
62 let argss = transpose lhss
63 let all_args = map (genAll i) argss
64 logLvl 5 $ "COVERAGE of " ++ show n
65 logLvl 5 $ show lhss
66 logLvl 5 $ show (map length argss) ++ "\n" ++ show (map length all_args)
67 logLvl 10 $ show argss ++ "\n" ++ show all_args
68 logLvl 1 $ "Original: \n" ++
69 showSep "\n" (map (\t -> showTm i (delab' i t True True)) xs)
70 -- add an infinite supply of explicit arguments to update the possible
71 -- cases for (the return type may be variadic, or function type, so
72 -- there may be more case splitting that the idris_implicits record
73 -- suggests)
74 let parg = case lookupCtxt n (idris_implicits i) of
75 (p : _) -> p ++ repeat (PExp 0 [] Placeholder)
76 _ -> repeat (pexp Placeholder)
77 let tryclauses = mkClauses parg all_args
78 logLvl 2 $ show (length tryclauses) ++ " initially to check"
79 logLvl 1 $ showSep "\n" (map (showTm i) tryclauses)
80 let new = filter (noMatch i) (nub tryclauses)
81 logLvl 1 $ show (length new) ++ " clauses to check for impossibility"
82 logLvl 3 $ "New clauses: \n" ++ showSep "\n" (map (showTm i) new)
83-- ++ " from:\n" ++ showSep "\n" (map (showImp True) tryclauses)
84 return new
85-- return (map (\t -> PClause n t [] PImpossible []) new)
86 where getLHS i term
87 | (f, args) <- unApply term = map (\t -> delab' i t True True) args
88 | otherwise = []
90 pUnApply (PApp _ _ args) = map getTm args
91 pUnApply _ = []
93 -- Return whether the given clause matches none of the input clauses
94 -- (xs)
95 noMatch i tm = all (\x -> case matchClause i (delab' i x True True) tm of
96 Right ms -> False
97 Left miss -> True) xs
100 mergePlaceholders :: PTerm -> PTerm -> PTerm
101 mergePlaceholders x Placeholder = Placeholder
102 mergePlaceholders (PApp fc f args) (PApp fc' f' args')
103 = PApp fc' f' (zipWith mergePArg args args')
104 where mergePArg x y = let xtm = mergePlaceholders (getTm x) (getTm y) in
105 x { getTm = xtm}
106 mergePlaceholders x _ = x
108 mkClauses :: [PArg] -> [[PTerm]] -> [PTerm]
109 mkClauses parg args
110 | all (== [Placeholder]) args = []
111 mkClauses parg args
112 = do args' <- mkArg args
113 let tm = PApp fc (PRef fc n) (zipWith upd args' parg)
114 return tm
115 where
116 mkArg :: [[PTerm]] -> [[PTerm]]
117 mkArg [] = return []
118 mkArg (a : as) = do a' <- a
119 as' <- mkArg as
120 return (a':as')
122fnub xs = fnub' [] xs
124fnub' acc (x : xs) | x `qelem` acc = fnub' acc (filter (not.(quickEq x)) xs)
125 | otherwise = fnub' (x : acc) xs
126fnub' acc [] = acc
128-- quick check for constructor equality
129quickEq :: PTerm -> PTerm -> Bool
130quickEq (PConstant n) (PConstant n') = n == n'
131quickEq (PRef _ n) (PRef _ n') = n == n'
132quickEq (PApp _ t as) (PApp _ t' as')
133 | length as == length as'
134 = quickEq t t' && and (zipWith quickEq (map getTm as) (map getTm as'))
135quickEq Placeholder Placeholder = True
136quickEq x y = False
138qelem x [] = False
139qelem x (y : ys) | x `quickEq` y = True
140 | otherwise = qelem x ys
142-- FIXME: Just look for which one is the deepest, then generate all
143-- possibilities up to that depth.
145genAll :: IState -> [PTerm] -> [PTerm]
146genAll i args
147 = case filter (/=Placeholder) $ fnub (concatMap otherPats (fnub args)) of
148 [] -> [Placeholder]
149 xs -> inventConsts xs
150 where
151 -- if they're constants, invent a new one to make sure that
152 -- constants which are not explicitly handled are covered
153 inventConsts cs@(PConstant c : _) = map PConstant (ic' (mapMaybe getConst cs))
154 where getConst (PConstant c) = Just c
155 getConst _ = Nothing
156 inventConsts xs = xs
158 -- try constants until they're not in the list.
159 -- FIXME: It is, of course, possible that someone has enumerated all
160 -- the constants and matched on them (maybe in generated code) and this
161 -- will be really slow. This is sufficiently unlikely that we won't
162 -- worry for now...
164 ic' xs@(I _ : _) = firstMissing xs (lotsOfNums I)
165 ic' xs@(BI _ : _) = firstMissing xs (lotsOfNums BI)
166 ic' xs@(Fl _ : _) = firstMissing xs (lotsOfNums Fl)
167 ic' xs@(B8 _ : _) = firstMissing xs (lotsOfNums B8)
168 ic' xs@(B16 _ : _) = firstMissing xs (lotsOfNums B16)
169 ic' xs@(B32 _ : _) = firstMissing xs (lotsOfNums B32)
170 ic' xs@(B64 _ : _) = firstMissing xs (lotsOfNums B64)
171 ic' xs@(Ch _ : _) = firstMissing xs lotsOfChars
172 ic' xs@(Str _ : _) = firstMissing xs lotsOfStrings
173 -- TODO: Bit vectors
174 -- The rest are types with only one case
175 ic' xs = xs
177 firstMissing cs (x : xs) | x `elem` cs = firstMissing cs xs
178 | otherwise = x : cs
180 lotsOfNums t = map t [0..]
181 lotsOfChars = map Ch ['a'..]
182 lotsOfStrings = map Str (map (("some string " ++).show) [1..])
184 conForm (PApp _ (PRef fc n) _) = isConName n (tt_ctxt i)
185 conForm (PRef fc n) = isConName n (tt_ctxt i)
186 conForm _ = False
188 nubMap f acc [] = acc
189 nubMap f acc (x : xs) = nubMap f (fnub' acc (f x)) xs
191 otherPats :: PTerm -> [PTerm]
192 otherPats o@(PRef fc n) = ops fc n [] o
193 otherPats o@(PApp _ (PRef fc n) xs) = ops fc n xs o
194 otherPats o@(PPair fc _ l r)
195 = ops fc pairCon
196 ([pimp (sUN "A") Placeholder True,
197 pimp (sUN "B") Placeholder True] ++
198 [pexp l, pexp r]) o
199 otherPats o@(PDPair fc p t _ v)
200 = ops fc (sUN "Ex_intro")
201 ([pimp (sUN "a") Placeholder True,
202 pimp (sUN "P") Placeholder True] ++
203 [pexp t,pexp v]) o
204 otherPats o@(PConstant c) = return o
205 otherPats arg = return Placeholder
207 ops fc n xs o
208 | (TyDecl c@(DCon _ arity) ty : _) <- lookupDef n (tt_ctxt i)
209 = do xs' <- mapM otherPats (map getExpTm xs)
210 let p = resugar (PApp fc (PRef fc n) (zipWith upd xs' xs))
211 let tyn = getTy n (tt_ctxt i)
212 case lookupCtxt tyn (idris_datatypes i) of
213 (TI ns _ _ _ : _) -> p : map (mkPat fc) (ns \\ [n])
214 _ -> [p]
215 ops fc n arg o = return Placeholder
217 getExpTm (PImp _ True _ _ _) = Placeholder -- machine inferred, no point!
218 getExpTm t = getTm t
220 -- put it back to its original form
221 resugar (PApp _ (PRef fc (UN ei)) [_,_,t,v])
222 | ei == txt "Ex_intro"
223 = PDPair fc TypeOrTerm (getTm t) Placeholder (getTm v)
224 resugar (PApp _ (PRef fc n) [_,_,l,r])
225 | n == pairCon
226 = PPair fc IsTerm (getTm l) (getTm r)
227 resugar t = t
229 dropForce force (x : xs) i | i `elem` force
230 = upd Placeholder x : dropForce force xs (i + 1)
231 dropForce force (x : xs) i = x : dropForce force xs (i + 1)
232 dropForce _ [] _ = []
235 getTy n ctxt = case lookupTy n ctxt of
236 (t : _) -> case unApply (getRetTy t) of
237 (P _ tyn _, _) -> tyn
238 x -> error $ "Can't happen getTy 1 " ++ show (n, x)
239 _ -> error "Can't happen getTy 2"
241 mkPat fc x = case lookupCtxt x (idris_implicits i) of
242 (pargs : _)
243 -> PApp fc (PRef fc x) (map (upd Placeholder) pargs)
244 _ -> error "Can't happen - genAll"
246upd p' p = p { getTm = p' }
248-- Check whether function and all descendants cover all cases (partial is
249-- okay, as long as it's due to recursion)
251checkAllCovering :: FC -> [Name] -> Name -> Name -> Idris ()
252checkAllCovering fc done top n | not (n `elem` done)
253 = do i <- get
254 case lookupTotal n (tt_ctxt i) of
255 [tot@(Partial NotCovering)] ->
256 tclift $ tfail (At fc (Msg (show top ++ " is " ++ show tot
257 ++ " due to " ++ show n)))
258 _ -> return ()
259 case lookupCtxt n (idris_callgraph i) of
260 [cg] -> mapM_ (checkAllCovering fc (n : done) top)
261 (map fst (calls cg))
262 _ -> return ()
263checkAllCovering _ _ _ _ = return ()
265-- Check if, in a given type n, the constructor cn : ty is strictly positive,
266-- and update the context accordingly
268checkPositive :: Name -> (Name, Type) -> Idris ()
269checkPositive n (cn, ty')
270 = do let ty = delazy ty'
271 let p = cp ty
272 i <- getIState
273 let tot = if p then Total (args ty) else Partial NotPositive
274 let ctxt' = setTotal cn tot (tt_ctxt i)
275 putIState (i { tt_ctxt = ctxt' })
276 logLvl 5 $ "Constructor " ++ show cn ++ " is " ++ show tot
277 addIBC (IBCTotal cn tot)
278 where
279 args t = [0..length (getArgTys t)-1]
281 cp (Bind n (Pi aty) sc) = posArg aty && cp sc
282 cp t = True
284 posArg (Bind _ (Pi nty) sc)
285 | (P _ n' _, args) <- unApply nty
286 = n /= n' && posArg sc
287 posArg t = True
289calcProd :: IState -> FC -> Name -> [([Name], Term, Term)] -> Idris Totality
290calcProd i fc topn pats
291 = cp topn pats []
292 where
293 -- every application of n must be in an argument of a coinductive
294 -- constructor, in every function reachable from here in the
295 -- call graph.
296 cp n pats done = do patsprod <- mapM (prodRec n done) pats
297 if (and patsprod)
298 then return Productive
299 else return (Partial NotProductive)
301 prodRec :: Name -> [Name] -> ([Name], Term, Term) -> Idris Bool
302 prodRec n done _ | n `elem` done = return True
303 prodRec n done (_, _, tm) = prod n done False (delazy' True tm)
305 prod :: Name -> [Name] -> Bool -> Term -> Idris Bool
306 prod n done ok ap@(App _ _)
307 | (P nt f _, args) <- unApply ap
308 = do recOK <- checkProdRec (n:done) f
309 let ctxt = tt_ctxt i
310 let [ty] = lookupTy f ctxt -- must exist!
311 let co = cotype nt f ty in
312 if (not recOK) then return False else
313 if f == topn
314 then do argsprod <- mapM (prod n done co) args
315 return (and (ok : argsprod) )
316 else do argsprod <- mapM (prod n done co) args
317 return (and argsprod)
318 prod n done ok (App f a) = liftM2 (&&) (prod n done False f)
319 (prod n done False a)
320 prod n done ok (Bind _ (Let t v) sc)
321 = liftM2 (&&) (prod n done False v) (prod n done False v)
322 prod n done ok (Bind _ b sc) = prod n done ok sc
323 prod n done ok t = return True
325 checkProdRec :: [Name] -> Name -> Idris Bool
326 checkProdRec done f
327 = case lookupCtxt f (idris_patdefs i) of
328 [(def, _)] -> do ok <- mapM (prodRec f done) def
329 return (and ok)
330 _ -> return True -- defined elsewhere, can't call topn
332 cotype (DCon _ _) n ty
333 | (P _ t _, _) <- unApply (getRetTy ty)
334 = case lookupCtxt t (idris_datatypes i) of
335 [TI _ True _ _] -> True
336 _ -> False
337 cotype nt n ty = False
339-- Calculate the totality of a function from its patterns.
340-- Either follow the size change graph (if inductive) or check for
341-- productivity (if coinductive)
343calcTotality :: FC -> Name -> [([Name], Term, Term)] -> Idris Totality
344calcTotality fc n pats
345 = do i <- getIState
346 let opts = case lookupCtxt n (idris_flags i) of
347 [fs] -> fs
348 _ -> []
349 case mapMaybe (checkLHS i) (map (\ (_, l, r) -> l) pats) of
350 (failure : _) -> return failure
351 _ -> if (Coinductive `elem` opts)
352 then calcProd i fc n pats
353 else checkSizeChange n
354 where
355 checkLHS i (P _ fn _)
356 = case lookupTotal fn (tt_ctxt i) of
357 [Partial _] -> return (Partial (Other [fn]))
358 _ -> Nothing
359 checkLHS i (App f a) = mplus (checkLHS i f) (checkLHS i a)
360 checkLHS _ _ = Nothing
362checkTotality :: [Name] -> FC -> Name -> Idris Totality
363checkTotality path fc n
364 | n `elem` path = return (Partial (Mutual (n : path)))
365 | otherwise = do
366 t <- getTotality n
367 updateContext (simplifyCasedef n)
368 ctxt <- getContext
369 i <- getIState
370 let opts = case lookupCtxt n (idris_flags i) of
371 [fs] -> fs
372 _ -> []
373 t' <- case t of
374 Unchecked ->
375 case lookupDef n ctxt of
376 [CaseOp _ _ _ _ pats _] ->
377 do t' <- if AssertTotal `elem` opts
378 then return $ Total []
379 else calcTotality fc n pats
380 setTotality n t'
381 addIBC (IBCTotal n t')
382 return t'
383 _ -> return $ Total []
384 x -> return x
385 case t' of
386 Total _ -> return t'
387 Productive -> return t'
388 e -> do w <- cmdOptType WarnPartial
389 if TotalFn `elem` opts
390 then totalityError t'
391 else do when (w && not (PartialFn `elem` opts)) $
392 warnPartial n t'
393 return t'
394 where
395 totalityError t = tclift $ tfail (At fc (Msg (show n ++ " is " ++ show t)))
397 warnPartial n t
398 = do i <- getIState
399 case lookupDef n (tt_ctxt i) of
400 [x] -> do
401 iputStrLn $ show fc ++ ":Warning - " ++ show n ++ " is " ++ show t
402-- ++ "\n" ++ show x
403-- let cg = lookupCtxtName Nothing n (idris_callgraph i)
404-- iputStrLn (show cg)
407checkDeclTotality :: (FC, Name) -> Idris Totality
408checkDeclTotality (fc, n)
409 = do logLvl 2 $ "Checking " ++ show n ++ " for totality"
410-- buildSCG (fc, n)
411-- logLvl 2 $ "Built SCG"
412 i <- getIState
413 t <- checkTotality [] fc n
414 case t of
415 -- if it's not total, it can't reduce, to keep
416 -- typechecking decidable
417 p@(Partial _) -> do setAccessibility n Frozen
418 addIBC (IBCAccess n Frozen)
419 logLvl 5 $ "HIDDEN: "
420 ++ show n ++ show p
421 _ -> return ()
422 return t
425-- Calculate the size change graph for this definition
427-- SCG for a function f consists of a list of:
428-- (g, [(a1, sizechange1), (a2, sizechange2), ..., (an, sizechangen)])
430-- where g is a function called
431-- a1 ... an are the arguments of f in positions 1..n of g
432-- sizechange1 ... sizechange2 is how their size has changed wrt the input
433-- to f
434-- Nothing, if the argument is unrelated to the input
436buildSCG :: (FC, Name) -> Idris ()
437buildSCG (_, n) = do
438 ist <- getIState
439 case lookupCtxt n (idris_callgraph ist) of
440 [cg] -> case lookupDef n (tt_ctxt ist) of
441 [CaseOp _ _ _ pats _ cd] ->
442 let (args, sc) = cases_totcheck cd in
443 do logLvl 2 $ "Building SCG for " ++ show n ++ " from\n"
444 ++ show pats ++ "\n" ++ show sc
445 let newscg = buildSCG' ist (rights pats) args
446 logLvl 5 $ show newscg
447 addToCG n ( cg { scg = newscg } )
448 [] -> logLvl 5 $ "Could not build SCG for " ++ show n ++ "\n"
449 x -> error $ "buildSCG: " ++ show (n, x)
451delazy = delazy' False -- not lazy codata
452delazy' all t@(App f a)
453 | (P _ (UN l) _, [_, _, arg]) <- unApply t,
454 l == txt "Force" = delazy' all arg
455 | (P _ (UN l) _, [P _ (UN lty) _, _, arg]) <- unApply t,
456 l == txt "Delay" && (all || lty == txt "LazyEval") = delazy arg
457 | (P _ (UN l) _, [P _ (UN lty) _, arg]) <- unApply t,
458 l == txt "Lazy'" && (all || lty == txt "LazyEval") = delazy' all arg
459delazy' all (App f a) = App (delazy' all f) (delazy' all a)
460delazy' all (Bind n b sc) = Bind n (fmap (delazy' all) b) (delazy' all sc)
461delazy' all t = t
463data Guardedness = Toplevel | Unguarded | Guarded
465buildSCG' :: IState -> [(Term, Term)] -> [Name] -> [SCGEntry]
466buildSCG' ist pats args = nub $ concatMap scgPat pats where
467 scgPat (lhs, rhs) = let lhs' = delazy lhs
468 rhs' = delazy rhs
469 (f, pargs) = unApply (dePat lhs') in
470 findCalls Toplevel (dePat rhs') (patvars lhs') pargs
472 findCalls guarded ap@(App f a) pvs pargs
473 -- under a call to "assert_total", don't do any checking, just believe
474 -- that it is total.
475 | (P _ (UN at) _, [_, _]) <- unApply ap,
476 at == txt "assert_total" = []
477 -- under a call to "Delay LazyCodata", don't do any checking of the
478 -- immediate call, as long as the call is guarded.
479 -- Then check its arguments
480 | (P _ (UN del) _, [_,_,arg]) <- unApply ap,
481 guarded <- Guarded,
482 del == txt "Delay"
483 = let (capp, args) = unApply arg in
484 concatMap (\x -> findCalls Toplevel x pvs pargs) args
485 | (P _ n _, args) <- unApply ap
486 = let nguarded = case guarded of
487 Unguarded -> Unguarded
488 _ -> if isConName n (tt_ctxt ist)
489 then Guarded
490 else Unguarded in
491 mkChange n args pargs ++
492 concatMap (\x -> findCalls nguarded x pvs pargs) args
493 findCalls guarded (App f a) pvs pargs
494 = findCalls Unguarded f pvs pargs ++ findCalls Unguarded a pvs pargs
495 findCalls guarded (Bind n (Let t v) e) pvs pargs
496 = findCalls Unguarded v pvs pargs ++ findCalls guarded e (n : pvs) pargs
497 findCalls guarded (Bind n _ e) pvs pargs
498 = findCalls guarded e (n : pvs) pargs
499 findCalls guarded (P _ f _ ) pvs pargs
500 | not (f `elem` pvs) = [(f, [])]
501 findCalls _ _ _ _ = []
503 expandToArity n args
504 = case lookupTy n (tt_ctxt ist) of
505 [ty] -> expand 0 (normalise (tt_ctxt ist) [] ty) args
506 _ -> args
507 where expand i (Bind n (Pi _) sc) (x : xs) = x : expand (i + 1) sc xs
508 expand i (Bind n (Pi _) sc) [] = Just (i, Same) : expand (i + 1) sc []
509 expand i _ xs = xs
511 mkChange n args pargs = [(n, expandToArity n (sizes args))]
512 where
513 sizes [] = []
514 sizes (a : as) = checkSize a pargs 0 : sizes as
516 -- find which argument in pargs <a> is smaller than, if any
517 checkSize a (p : ps) i
518 | a == p = Just (i, Same)
519 | (P _ (UN as) _, [_,arg,_]) <- unApply a,
520 as == txt "assert_smaller" && arg == p
521 = Just (i, Smaller)
522 | smaller Nothing a (p, Nothing) = Just (i, Smaller)
523 | otherwise = checkSize a ps (i + 1)
524 checkSize a [] i = Nothing
526 -- the smaller thing we find must be the same type as <a>, and
527 -- not be coinductive - so carry the type of the constructor we've
528 -- gone under.
530 smaller (Just tyn) a (t, Just tyt)
531 | a == t = isInductive (fst (unApply (getRetTy tyn)))
532 (fst (unApply (getRetTy tyt)))
533 smaller ty a (ap@(App f s), _)
534 | (P (DCon _ _) n _, args) <- unApply ap
535 = let tyn = getType n in
536 any (smaller (ty `mplus` Just tyn) a)
537 (zip args (map toJust (getArgTys tyn)))
538 -- check higher order recursive arguments
539 smaller ty (App f s) a = smaller ty f a
540 smaller _ _ _ = False
542 toJust (n, t) = Just t
544 getType n = case lookupTy n (tt_ctxt ist) of
545 [ty] -> delazy ty -- must exist
547 isInductive (P _ nty _) (P _ nty' _) =
548 let co = case lookupCtxt nty (idris_datatypes ist) of
549 [TI _ x _ _] -> x
550 _ -> False in
551 nty == nty' && not co
552 isInductive _ _ = False
554 dePat (Bind x (PVar ty) sc) = dePat (instantiate (P Bound x ty) sc)
555 dePat t = t
557 patvars (Bind x (PVar _) sc) = x : patvars sc
558 patvars _ = []
560checkSizeChange :: Name -> Idris Totality
561checkSizeChange n = do
562 ist <- getIState
563 case lookupCtxt n (idris_callgraph ist) of
564 [cg] -> do let ms = mkMultiPaths ist [] (scg cg)
565 logLvl 5 ("Multipath for " ++ show n ++ ":\n" ++
566 "from " ++ show (scg cg) ++ "\n" ++
567 show (length ms) ++ "\n" ++
568 showSep "\n" (map show ms))
569 logLvl 6 (show cg)
570 -- every multipath must have an infinitely descending
571 -- thread, then the function terminates
572 -- also need to checks functions called are all total
573 -- (Unchecked is okay as we'll spot problems here)
574 let tot = map (checkMP ist (getArity ist n)) ms
575 logLvl 4 $ "Generated " ++ show (length tot) ++ " paths"
576 logLvl 6 $ "Paths for " ++ show n ++ " yield " ++ (show tot)
577 return (noPartial tot)
578 [] -> do logLvl 5 $ "No paths for " ++ show n
579 return Unchecked
580 where getArity ist n
581 = case lookupTy n (tt_ctxt ist) of
582 [ty] -> arity (normalise (tt_ctxt ist) [] ty)
583 _ -> error "Can't happen: checkSizeChange.getArity"
585type MultiPath = [SCGEntry]
587mkMultiPaths :: IState -> MultiPath -> [SCGEntry] -> [MultiPath]
588mkMultiPaths ist path [] = [reverse path]
589mkMultiPaths ist path cg
590 = concat (map extend cg)
591 where extend (nextf, args)
592 | (nextf, args) `elem` path = [ reverse ((nextf, args) : path) ]
593 | [Unchecked] <- lookupTotal nextf (tt_ctxt ist)
594 = case lookupCtxt nextf (idris_callgraph ist) of
595 [ncg] -> mkMultiPaths ist ((nextf, args) : path) (scg ncg)
596 _ -> [ reverse ((nextf, args) : path) ]
597 | otherwise = [ reverse ((nextf, args) : path) ]
599-- do (nextf, args) <- cg
600-- if ((nextf, args) `elem` path)
601-- then return (reverse ((nextf, args) : path))
602-- else case lookupCtxt nextf (idris_callgraph ist) of
603-- [ncg] -> mkMultiPaths ist ((nextf, args) : path) (scg ncg)
604-- _ -> return (reverse ((nextf, args) : path))
606-- If any route along the multipath leads to infinite descent, we're fine.
607-- Try a route beginning with every argument.
608-- If we reach a point we've been to before, but with a smaller value,
609-- that means there is an infinitely descending path from that argument.
611checkMP :: IState -> Int -> MultiPath -> Totality
612checkMP ist i mp = if i > 0
613 then let paths = (map (tryPath 0 [] mp) [0..i-1]) in
614-- trace ("Paths " ++ show paths) $
615 collapse paths
616 else tryPath 0 [] mp 0
617 where
618 tryPath' d path mp arg
619 = let res = tryPath d path mp arg in
620 trace (show mp ++ "\n" ++ show arg ++ " " ++ show res) res
622 tryPath :: Int -> [((SCGEntry, Int), Int)] -> MultiPath -> Int -> Totality
623 tryPath desc path [] _ = Total []
624-- tryPath desc path ((UN "believe_me", _) : _) arg
625-- = Partial BelieveMe
626 -- if we get to a constructor, it's fine as long as it's strictly positive
627 tryPath desc path ((f, _) : es) arg
628 | [TyDecl (DCon _ _) _] <- lookupDef f (tt_ctxt ist)
629 = case lookupTotal f (tt_ctxt ist) of
630 [Total _] -> Unchecked -- okay so far
631 [Partial _] -> Partial (Other [f])
632 x -> error (show x)
633 | [TyDecl (TCon _ _) _] <- lookupDef f (tt_ctxt ist)
634 = Total []
635 tryPath desc path (e@(f, args) : es) arg
636 | e `elem` es && allNothing args = Partial (Mutual [f])
637 tryPath desc path (e@(f, nextargs) : es) arg
638 | Just d <- lookup (e, arg) path
639 = if desc > 0
640 then -- trace ("Descent " ++ show (desc - d) ++ " "
641 -- ++ show (path, e)) $
642 Total []
643 else Partial (Mutual (map (fst . fst . fst) path ++ [f]))
644 | e `elem` map (fst . fst) path
645 && not (f `elem` map fst es)
646 = Partial (Mutual (map (fst . fst . fst) path ++ [f]))
647 | [Unchecked] <- lookupTotal f (tt_ctxt ist) =
648 let argspos = case collapseNothing (zip nextargs [0..]) of
649 [] -> [(Nothing, 0)]
650 x -> x
651-- trace (show (argspos, nextargs, path)) $
652 pathres =
653 do (a, pos) <- argspos
654 case a of
655 Nothing -> -- don't know, but if the
656 -- rest definitely terminates without
657 -- any cycles with route so far,
658 -- then we might yet be total
659 case collapse (map (tryPath 0 (((e, arg), 0):path) es)
660 [0..length nextargs - 1]) of
661 Total _ -> return Unchecked
662 x -> return x
663 Just (nextarg, sc) ->
664 if nextarg == arg then
665 case sc of
666 Same -> return $ tryPath desc (((e, arg), desc) : path)
667 es pos
668 Smaller -> return $ tryPath (desc+1)
669 (((e, arg), desc) : path)
670 es
671 pos
672 _ -> trace ("Shouldn't happen " ++ show e) $
673 return (Partial Itself)
674 else return Unchecked in
675-- trace (show (desc, argspos, path, es, pathres)) $
676 collapse' Unchecked pathres
678 | [Total a] <- lookupTotal f (tt_ctxt ist) = Total a
679 | [Partial _] <- lookupTotal f (tt_ctxt ist) = Partial (Other [f])
680 | otherwise = Unchecked
682allNothing xs = null (collapseNothing (zip xs [0..]))
684collapseNothing ((Nothing, _) : xs)
685 = filter (\ (x, _) -> case x of
686 Nothing -> False
687 _ -> True) xs
688collapseNothing (x : xs) = x : collapseNothing xs
689collapseNothing [] = []
691noPartial (Partial p : xs) = Partial p
692noPartial (_ : xs) = noPartial xs
693noPartial [] = Total []
695collapse xs = collapse' Unchecked xs
696collapse' def (Total r : xs) = Total r
697collapse' def (Unchecked : xs) = collapse' def xs
698collapse' def (d : xs) = collapse' d xs
699-- collapse' Unchecked [] = Total []
700collapse' def [] = def