@@ -1,9 +1,10 @@
11{-# LANGUAGE PatternGuards #-}
22
3−module Idris.ProofSearch(trivial, trivialHoles, proofSearch) where
3+module Idris.ProofSearch(trivial, trivialHoles, proofSearch, resolveTC) where
44
55import Idris.Core.Elaborate hiding (Tactic(..))
66import Idris.Core.TT
7+import Idris.Core.Unify
78import Idris.Core.Evaluate
89import Idris.Core.CaseTree
910import Idris.Core.Typecheck
@@ -24,14 +25,16 @@ import Debug.Trace
2425trivial :: (PTerm -> ElabD ()) -> IState -> ElabD ()
2526trivial = trivialHoles [] []
2627
27−trivialHoles :: [Name] -> [(Name, Int)] -> (PTerm -> ElabD ()) -> IState -> ElabD ()
28+trivialHoles :: [Name] -> -- user visible names, when working
29+ -- in interactive mode
30+ [(Name, Int)] -> (PTerm -> ElabD ()) -> IState -> ElabD ()
2831trivialHoles psnames ok elab ist
29− = try' (do elab (PApp (fileFC "prf") (PRef (fileFC "prf") [] eqCon) [pimp (sUN "A") Placeholder False, pimp (sUN "x") Placeholder False])
30− return ())
31− (do env <- get_env
32− g <- goal
33− tryAll env
34− return ()) True
32+ = try' (do elab (PApp (fileFC "prf") (PRef (fileFC "prf") [] eqCon) [pimp (sUN "A") Placeholder False, pimp (sUN "x") Placeholder False])
33+ return ())
34+ (do env <- get_env
35+ g <- goal
36+ tryAll env
37+ return ()) True
3538 where
3639 tryAll [] = fail "No trivial solution"
3740 tryAll ((x, b):xs)
@@ -41,7 +44,7 @@ trivialHoles psnames ok elab ist
4144 g <- goal
4245 -- anywhere but the top is okay for a hole, if holesOK set
4346 if -- all (\n -> not (n `elem` badhs)) (freeNames (binderTy b))
44− holesOK hs (binderTy b) && (null psnames || x `elem` psnames)
47+ (holesOK hs (binderTy b) && (null psnames || x `elem` psnames))
4548 then try' (elab (PRef (fileFC "prf") [] x))
4649 (tryAll xs) True
4750 else tryAll xs
@@ -60,6 +63,50 @@ trivialHoles psnames ok elab ist
6063 | (n, p) `elem` ok = holeArgsOK hs n (p + 1) as
6164 | otherwise = holesOK hs a && holeArgsOK hs n (p + 1) as
6265
66+trivialTCs :: [(Name, Int)] -> (PTerm -> ElabD ()) -> IState -> ElabD ()
67+trivialTCs ok elab ist
68+ = try' (do elab (PApp (fileFC "prf") (PRef (fileFC "prf") [] eqCon) [pimp (sUN "A") Placeholder False, pimp (sUN "x") Placeholder False])
69+ return ())
70+ (do env <- get_env
71+ g <- goal
72+ tryAll env
73+ return ()) True
74+ where
75+ tryAll [] = fail "No trivial solution"
76+ tryAll ((x, b):xs)
77+ = do -- if type of x has any holes in it, move on
78+ hs <- get_holes
79+ let badhs = hs -- filter (flip notElem holesOK) hs
80+ g <- goal
81+ env <- get_env
82+ -- anywhere but the top is okay for a hole, if holesOK set
83+ if -- all (\n -> not (n `elem` badhs)) (freeNames (binderTy b))
84+ (holesOK hs (binderTy b) && tcArg env (binderTy b))
85+ then try' (elab (PRef (fileFC "prf") [] x))
86+ (tryAll xs) True
87+ else tryAll xs
88+
89+ tcArg env ty
90+ | (P _ n _, args) <- unApply (getRetTy (normalise (tt_ctxt ist) env ty))
91+ = case lookupCtxtExact n (idris_classes ist) of
92+ Just _ -> True
93+ _ -> False
94+ | otherwise = False
95+
96+ holesOK hs ap@(App _ _ _)
97+ | (P _ n _, args) <- unApply ap
98+ = holeArgsOK hs n 0 args
99+ holesOK hs (App _ f a) = holesOK hs f && holesOK hs a
100+ holesOK hs (P _ n _) = not (n `elem` hs)
101+ holesOK hs (Bind n b sc) = holesOK hs (binderTy b) &&
102+ holesOK hs sc
103+ holesOK hs _ = True
104+
105+ holeArgsOK hs n p [] = True
106+ holeArgsOK hs n p (a : as)
107+ | (n, p) `elem` ok = holeArgsOK hs n (p + 1) as
108+ | otherwise = holesOK hs a && holeArgsOK hs n (p + 1) as
109+
63110cantSolveGoal :: ElabD a
64111cantSolveGoal = do g <- goal
65112 env <- get_env
@@ -179,7 +226,9 @@ proofSearch rec fromProver ambigok deferonfail maxDepth elab fn nroot psnames hi
179226 ty <- goal
180227 when (S.member ty tys) $ fail "Been here before"
181228 let tys' = S.insert ty tys
182− try' (trivialHoles psnames [] elab ist)
229+ try' (try' (trivialHoles psnames [] elab ist)
230+ (resolveTC False False 20 ty nroot elab ist)
231+ True)
183232 (try' (try' (resolveByCon (d - 1) locs tys')
184233 (resolveByLocals (d - 1) locs tys')
185234 True)
@@ -188,9 +237,14 @@ proofSearch rec fromProver ambigok deferonfail maxDepth elab fn nroot psnames hi
188237 then fail "cantSolveGoal"
189238 else do attack; defer [] nroot; solve) True) True
190239
191− getFn d Nothing = []
192− getFn d (Just f) | d < maxDepth-1 = [f]
240+ -- get recursive function name. Only user given names make sense.
241+ getFn d (Just f) | d < maxDepth-1 && usersname f = [f]
193242 | otherwise = []
243+ getFn d _ = []
244+
245+ usersname (UN _) = True
246+ usersname (NS n _) = usersname n
247+ usersname _ = False
194248
195249 resolveByCon d locs tys
196250 = do t <- goal
@@ -273,3 +327,182 @@ checkConstructor ist (n : ns) =
273327 any conHead args
274328 conHead t | (P _ n _, _) <- unApply t = isConName n (tt_ctxt ist)
275329 | otherwise = False
330+
331+-- | Resolve type classes. This will only pick up 'normal' instances, never
332+-- named instances (which is enforced by 'findInstances').
333+resolveTC :: Bool -- ^ using default Int
334+ -> Bool -- ^ allow metavariables in the goal
335+ -> Int -- ^ depth
336+ -> Term -- ^ top level goal, for error messages
337+ -> Name -- ^ top level function name, to prevent loops
338+ -> (PTerm -> ElabD ()) -- ^ top level elaborator
339+ -> IState -> ElabD ()
340+resolveTC def mvok depth top fn elab ist
341+ = do hs <- get_holes
342+ resTC' [] def hs depth top fn elab ist
343+
344+resTC' tcs def topholes 0 topg fn elab ist = fail $ "Can't resolve type class"
345+resTC' tcs def topholes 1 topg fn elab ist = try' (trivial elab ist) (resolveTC def False 0 topg fn elab ist) True
346+resTC' tcs defaultOn topholes depth topg fn elab ist
347+ = do compute
348+ g <- goal
349+ -- Resolution can proceed only if there is something concrete in the
350+ -- determining argument positions. Keep track of the holes in the
351+ -- non-determining position, because it's okay for 'trivial' to solve
352+ -- those holes and no others.
353+ let (argsok, okholePos) = case tcArgsOK g topholes of
354+ Nothing -> (False, [])
355+ Just hs -> (True, hs)
356+ if not argsok -- && not mvok)
357+ then lift $ tfail $ CantResolve True topg
358+ else do
359+ ptm <- get_term
360+ ulog <- getUnifyLog
361+ hs <- get_holes
362+ env <- get_env
363+ t <- goal
364+ let (tc, ttypes) = unApply (getRetTy t)
365+ let okholes = case tc of
366+ P _ n _ -> zip (repeat n) okholePos
367+ _ -> []
368+
369+ traceWhen ulog ("Resolving class " ++ show g ++ "\nin" ++ show env ++ "\n" ++ show okholes) $
370+ try' (trivialTCs okholes elab ist)
371+ (do addDefault t tc ttypes
372+ let stk = map fst (filter snd $ elab_stack ist)
373+ let insts = findInstances ist t
374+ blunderbuss t depth stk (stk ++ insts)) True
375+ where
376+ -- returns Just hs if okay, where hs are holes which are okay in the
377+ -- goal, or Nothing if not okay to proceed
378+ tcArgsOK ty hs | (P _ nc _, as) <- unApply (getRetTy ty), nc == numclass && defaultOn
379+ = Just []
380+ tcArgsOK ty hs -- if any determining arguments are metavariables, postpone
381+ = let (f, as) = unApply (getRetTy ty) in
382+ case f of
383+ P _ cn _ -> case lookupCtxtExact cn (idris_classes ist) of
384+ Just ci -> tcDetArgsOK 0 (class_determiners ci) hs as
385+ Nothing -> if any (isMeta hs) as
386+ then Nothing
387+ else Just []
388+ _ -> if any (isMeta hs) as
389+ then Nothing
390+ else Just []
391+
392+ -- return the list of argument positions which can safely be a hole
393+ -- or Nothing if one of the determining arguments is a hole
394+ tcDetArgsOK i ds hs (x : xs)
395+ | i `elem` ds = if isMeta hs x
396+ then Nothing
397+ else tcDetArgsOK (i + 1) ds hs xs
398+ | otherwise = do rs <- tcDetArgsOK (i + 1) ds hs xs
399+ case x of
400+ P _ n _ -> Just (i : rs)
401+ _ -> Just rs
402+ tcDetArgsOK _ _ _ [] = Just []
403+
404+ isMeta :: [Name] -> Term -> Bool
405+ isMeta ns (P _ n _) = n `elem` ns
406+ isMeta _ _ = False
407+
408+ notHole hs (P _ n _, c)
409+ | (P _ cn _, _) <- unApply (getRetTy c),
410+ n `elem` hs && isConName cn (tt_ctxt ist) = False
411+ | Constant _ <- c = not (n `elem` hs)
412+ notHole _ _ = True
413+
414+ -- HACK! Rather than giving a special name, better to have some kind
415+ -- of flag in ClassInfo structure
416+ chaser (UN nm)
417+ | ('@':'@':_) <- str nm = True -- old way
418+ chaser (SN (ParentN _ _)) = True
419+ chaser (NS n _) = chaser n
420+ chaser _ = False
421+
422+ numclass = sNS (sUN "Num") ["Classes","Prelude"]
423+
424+ addDefault t num@(P _ nc _) [P Bound a _] | nc == numclass && defaultOn
425+ = do focus a
426+ fill (RConstant (AType (ATInt ITBig))) -- default Integer
427+ solve
428+ addDefault t f as
429+ | all boundVar as = return () -- True -- fail $ "Can't resolve " ++ show t
430+ addDefault t f a = return () -- trace (show t) $ return ()
431+
432+ boundVar (P Bound _ _) = True
433+ boundVar _ = False
434+
435+ blunderbuss t d stk [] = do -- c <- get_env
436+ -- ps <- get_probs
437+ lift $ tfail $ CantResolve False topg
438+ blunderbuss t d stk (n:ns)
439+ | n /= fn -- && (n `elem` stk)
440+ = tryCatch (resolve n d)
441+ (\e -> case e of
442+ CantResolve True _ -> lift $ tfail e
443+ _ -> blunderbuss t d stk ns)
444+ | otherwise = blunderbuss t d stk ns
445+
446+ introImps = do g <- goal
447+ case g of
448+ (Bind _ (Pi _ _ _) sc) -> do attack; intro Nothing
449+ num <- introImps
450+ return (num + 1)
451+ _ -> return 0
452+
453+ solven 0 = return ()
454+ solven n = do solve; solven (n - 1)
455+
456+ resolve n depth
457+ | depth == 0 = fail $ "Can't resolve type class"
458+ | otherwise
459+ = do lams <- introImps
460+ t <- goal
461+ let (tc, ttypes) = trace (show t) $ unApply (getRetTy t)
462+-- if (all boundVar ttypes) then resolveTC (depth - 1) fn insts ist
463+-- else do
464+ -- if there's a hole in the goal, don't even try
465+ let imps = case lookupCtxtName n (idris_implicits ist) of
466+ [] -> []
467+ [args] -> map isImp (snd args) -- won't be overloaded!
468+ xs -> error "The impossible happened - overloading is not expected here!"
469+ ps <- get_probs
470+ tm <- get_term
471+ args <- map snd <$> try' (apply (Var n) imps)
472+ (match_apply (Var n) imps) True
473+ solven lams -- close any implicit lambdas we introduced
474+ ps' <- get_probs
475+ when (length ps < length ps' || unrecoverable ps') $
476+ fail "Can't apply type class"
477+-- traceWhen (all boundVar ttypes) ("Progress: " ++ show t ++ " with " ++ show n) $
478+ mapM_ (\ (_,n) -> do focus n
479+ t' <- goal
480+ let (tc', ttype) = unApply (getRetTy t')
481+ let got = fst (unApply (getRetTy t))
482+ let depth' = if tc' `elem` tcs
483+ then depth - 1 else depth
484+ resTC' (got : tcs) defaultOn topholes depth' topg fn elab ist)
485+ (filter (\ (x, y) -> not x) (zip (map fst imps) args))
486+ -- if there's any arguments left, we've failed to resolve
487+ hs <- get_holes
488+ ulog <- getUnifyLog
489+ solve
490+ traceWhen ulog ("Got " ++ show n) $ return ()
491+ where isImp (PImp p _ _ _ _) = (True, p)
492+ isImp arg = (False, priority arg)
493+
494+-- | Find the names of instances that have been designeated for
495+-- searching (i.e. non-named instances or instances from Elab scripts)
496+findInstances :: IState -> Term -> [Name]
497+findInstances ist t
498+ | (P _ n _, _) <- unApply (getRetTy t)
499+ = case lookupCtxt n (idris_classes ist) of
500+ [CI _ _ _ _ _ ins _] ->
501+ [n | (n, True) <- ins, accessible n]
502+ _ -> []
503+ | otherwise = []
504+ where accessible n = case lookupDefAccExact n False (tt_ctxt ist) of
505+ Just (_, Hidden) -> False
506+ _ -> True
507+
508+