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