on master help sign in
Make coverage checking more conservative

If an argument is a non-concrete type (i.e. a function type which calculates its type from other arguments) then we can't merely generate all the possible patterns from existing patterns, since there could be any number of constructors of types we haven't thought of. We now take the conservative approach of checking the catch-all pattern for such cases, so that coverage depends entirely on the other concrete patterns being complete. There is likely a more precise way to do this, but this more conservative way is at least sound. Fixes #1292

commit9f202dc97590c83a488b9abfa9028905c5b86cf1 authorEdwin Brady <ecb10@st-andrews.ac.uk> dateSun 14 Feb 2016, 20:53 (11 years ago) parentbe0ab25
6 files changed+72−16
idris.cabal+3 src/Idris/Coverage.hs+29−14 src/Idris/Elab/Clause.hs+4−2 test/totality011/expected+2 test/totality011/run+3 test/totality011/totality011.lidr+31
idris.cabal+3
@@ -751,6 +751,9 @@ Extra-source-files:
751751 test/totality010/run
752752 test/totality010/*.idr
753753 test/totality010/expected
754+ test/totality011/run
755+ test/totality011/*.lidr
756+ test/totality011/expected
754757
755758 test/tutorial001/run
756759 test/tutorial001/*.idr
src/Idris/Coverage.hs+29−14
@@ -59,10 +59,16 @@ genClauses fc n xs given
5959 let lhs_tms' = zipWith mergePlaceholders lhs_tms
6060 (map (stripUnmatchable i) (map flattenArgs given))
6161 let lhss = map pUnApply lhs_tms'
62+ nty <- case lookupTyExact n (tt_ctxt i) of
63+ Just t -> return (normalise (tt_ctxt i) [] t)
64+ _ -> fail "Can't happen - genClauses, lookupTyExact"
6265
6366 let argss = transpose lhss
64− let all_args = map (genAll i) argss
67+ let all_args = map (genAll i) (zip (genPH i nty) argss)
6568 logCoverage 5 $ "COVERAGE of " ++ show n
69+ logCoverage 5 $ "using type " ++ show nty
70+ logCoverage 5 $ "non-concrete args " ++ show (genPH i nty)
71+
6672 logCoverage 5 $ show (lhs_tms, lhss)
6773 logCoverage 5 $ show (map length argss) ++ "\n" ++ show (map length all_args)
6874 logCoverage 10 $ show argss ++ "\n" ++ show all_args
@@ -89,6 +95,18 @@ genClauses fc n xs given
8995 | (f, args) <- unApply term = map (\t -> delab' i t True True) args
9096 | otherwise = []
9197
98+ -- if an argument has a non-concrete type (i.e. it's a function
99+ -- that calculates something from a previous argument) we'll also
100+ -- need to generate a placeholder pattern for it since it could be
101+ -- anything based on earlier values
102+ genPH :: IState -> Type -> [Bool]
103+ genPH ist (Bind n (Pi _ ty _) sc) = notConcrete ist ty : genPH ist sc
104+ genPH ist ty = []
105+
106+ notConcrete ist t | (P _ n _, args) <- unApply t,
107+ not (isConName n (tt_ctxt ist)) = True
108+ notConcrete _ _ = False
109+
92110 pUnApply (PApp _ f args) = map getTm args
93111 pUnApply _ = []
94112
@@ -172,16 +190,14 @@ recoverableCoverage ctxt (Elaborating _ _ _ e) = recoverableCoverage ctxt e
172190recoverableCoverage ctxt (ElaboratingArg _ _ _ e) = recoverableCoverage ctxt e
173191recoverableCoverage _ _ = False
174192
175−-- FIXME: Just look for which one is the deepest, then generate all
176−-- possibilities up to that depth.
177−-- This and below issues for this function are tracked as Issue #1741 on the issue tracker.
178−-- https://github.com/idris-lang/Idris-dev/issues/1741
179−genAll :: IState -> [PTerm] -> [PTerm]
180−genAll i args
193+genAll :: IState -> (Bool, [PTerm]) -> [PTerm]
194+genAll i (addPH, args)
181195 = case filter (/=Placeholder) $ fnub (concatMap otherPats (fnub args)) of
182196 [] -> [Placeholder]
183− xs -> inventConsts xs
197+ xs -> ph $ inventConsts xs
184198 where
199+ ph ts = if addPH then Placeholder : ts else ts
200+
185201 -- if they're constants, invent a new one to make sure that
186202 -- constants which are not explicitly handled are covered
187203 inventConsts cs@(PConstant fc c : _) = map (PConstant NoFC) (ic' (mapMaybe getConst cs))
@@ -204,7 +220,6 @@ genAll i args
204220 ic' xs@(B64 _ : _) = firstMissing xs (lotsOfNums B64)
205221 ic' xs@(Ch _ : _) = firstMissing xs lotsOfChars
206222 ic' xs@(Str _ : _) = firstMissing xs lotsOfStrings
207− -- TODO: Bit vectors
208223 -- The rest are types with only one case
209224 ic' xs = xs
210225
@@ -219,19 +234,19 @@ genAll i args
219234 nubMap f acc (x : xs) = nubMap f (fnub' acc (f x)) xs
220235
221236 otherPats :: PTerm -> [PTerm]
222− otherPats o@(PRef fc hl n) = ops fc n [] o
223− otherPats o@(PApp _ (PRef fc hl n) xs) = ops fc n xs o
237+ otherPats o@(PRef fc hl n) = ph $ ops fc n [] o
238+ otherPats o@(PApp _ (PRef fc hl n) xs) = ph $ ops fc n xs o
224239 otherPats o@(PPair fc hls _ l r)
225− = ops fc pairCon
240+ = ph $ ops fc pairCon
226241 ([pimp (sUN "A") Placeholder True,
227242 pimp (sUN "B") Placeholder True] ++
228243 [pexp l, pexp r]) o
229244 otherPats o@(PDPair fc hls p t _ v)
230− = ops fc sigmaCon
245+ = ph $ ops fc sigmaCon
231246 ([pimp (sUN "a") Placeholder True,
232247 pimp (sUN "P") Placeholder True] ++
233248 [pexp t,pexp v]) o
234− otherPats o@(PConstant _ c) = inventConsts [o] -- return o
249+ otherPats o@(PConstant _ c) = ph $ inventConsts [o] -- return o
235250 otherPats arg = return Placeholder
236251
237252 ops fc n xs o
src/Idris/Elab/Clause.hs+4−2
@@ -165,9 +165,12 @@ elabClauses info' fc opts n_in cs =
165165 cov <- coverage
166166 pmissing <-
167167 if cov && not (hasDefault pats_raw)
168− then do missing <- genClauses fc n (map getLHS pdef) cs_full
168+ then do -- Generate clauses from the given possible cases
169+ missing <- genClauses fc n (map getLHS pdef) cs_full
169170 -- missing <- genMissing n scargs sc
170171 missing' <- filterM (checkPossible info fc True n) missing
172+ -- Filter out the ones which match one of the
173+ -- given cases (including impossible ones)
171174 let clhs = map getLHS pdef
172175 logElab 2 $ "Must be unreachable:\n" ++
173176 showSep "\n" (map showTmImpls missing') ++
@@ -312,7 +315,6 @@ elabClauses info' fc opts n_in cs =
312315 , (f, args) <- unApply tm = all (isPatVar pvs) args
313316 hasDefault _ = False
314317
315−
316318 getLHS (_, l, _) = l
317319
318320 simple_lhs ctxt (Right (x, y)) = Right (normalise ctxt [] x, y)
test/totality011/expectednew file+2
@@ -0,0 +1,2 @@
1+totality011.lidr:22:1:
2+Main.weCanOnlyGetOlder is not total as there are missing cases
test/totality011/runnew file+3
@@ -0,0 +1,3 @@
1+#!/usr/bin/env bash
2+${IDRIS:-idris} $@ totality011.lidr --check
3+rm -f *.ibc
test/totality011/totality011.lidrnew file+31
@@ -0,0 +1,31 @@
1+> %default total
2+
3+> X : Nat -> Type
4+> Y : (t : Nat) -> X t -> Type
5+> step : (t : Nat) -> (x : X t) -> Y t x -> X (S t)
6+
7+> Pred : X t -> X (S t) -> Type
8+> Pred {t} x x' = Subset (Y t x) (\ y => (x' = step t x y))
9+
10+> ReachableFrom : X t'' -> X t -> Type
11+> ReachableFrom {t'' = Z } {t} x'' x =
12+> (Z = t , x'' = x)
13+> ReachableFrom {t'' = S t'} {t} x'' x =
14+> Either (S t' = t , x'' = x)
15+> (Subset (X t') (\ x' => (x' `ReachableFrom` x , x' `Pred` x'')))
16+
17+> weCanOnlyGetOlder : (x'' : X t'') ->
18+> (x : X t) ->
19+> x'' `ReachableFrom` x ->
20+> t'' `GTE` t
21+
22+> weCanOnlyGetOlder {t'' = Z} {t = Z} _ _ _ = LTEZero
23+> weCanOnlyGetOlder {t'' = Z} {t = S m} _ _ (Zeqt , _) =
24+> void (uninhabited u) where
25+> u : Z = S m
26+> u = trans Zeqt Refl
27+
28+> weCanOnlyGetOlder {t'' = S _} {t = S _} _ _ _ = ?foo
29+
30+-- > weCanOnlyGetOlder {t'' = S _} {t = S _} _ _ (_, _) = ?foo
31+