on d66da21 help sign in
Idris-dev/src/Core/Elaborate.hs
661 lines 23 kB history raw
1{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, PatternGuards #-}
3{- A high level language of tactic composition, for building
4 elaborators from a high level language into the core theory.
6 This is our interface to proof construction, rather than
7 ProofState, because this gives us a language to build derived
8 tactics out of the primitives.
9-}
11module Core.Elaborate(module Core.Elaborate,
12 module Core.ProofState) where
14import Core.ProofState
15import Core.TT
16import Core.Evaluate
17import Core.Typecheck
18import Core.Unify
20import Control.Monad.State
21import Data.Char
22import Data.List
23import Debug.Trace
25import Util.Pretty
27-- I don't really want this here, but it's useful for the test shell
28data Command = Theorem Name Raw
29 | Eval Raw
30 | Quit
31 | Print Name
32 | Tac (Elab ())
34data ElabState aux = ES (ProofState, aux) String (Maybe (ElabState aux))
35 deriving Show
37type Elab' aux a = StateT (ElabState aux) TC a
38type Elab a = Elab' () a
40proof :: ElabState aux -> ProofState
41proof (ES (p, _) _ _) = p
43-- Insert a 'proofSearchFail' error if necessary to shortcut any further
44-- fruitless searching
45proofFail :: Elab' aux a -> Elab' aux a
46proofFail e = do s <- get
47 case runStateT e s of
48 OK (a, s') -> do put s'
49 return a
50 Error err -> lift $ Error (ProofSearchFail err)
52saveState :: Elab' aux ()
53saveState = do e@(ES p s _) <- get
54 put (ES p s (Just e))
56loadState :: Elab' aux ()
57loadState = do (ES p s e) <- get
58 case e of
59 Just st -> put st
60 _ -> fail "Nothing to undo"
62errAt :: String -> Name -> Elab' aux a -> Elab' aux a
63errAt thing n elab = do s <- get
64 case runStateT elab s of
65 OK (a, s') -> do put s'
66 return a
67 Error (At f e) ->
68 lift $ Error (At f (Elaborating thing n e))
69 Error e -> lift $ Error (Elaborating thing n e)
71erun :: FC -> Elab' aux a -> Elab' aux a
72erun f elab = do s <- get
73 case runStateT elab s of
74 OK (a, s') -> do put s'
75 return a
76 Error (ProofSearchFail (At f e))
77 -> lift $ Error (ProofSearchFail (At f e))
78 Error (At f e) -> lift $ Error (At f e)
79 Error e -> lift $ Error (At f e)
81runElab :: aux -> Elab' aux a -> ProofState -> TC (a, ElabState aux)
82runElab a e ps = runStateT e (ES (ps, a) "" Nothing)
84execElab :: aux -> Elab' aux a -> ProofState -> TC (ElabState aux)
85execElab a e ps = execStateT e (ES (ps, a) "" Nothing)
87initElaborator :: Name -> Context -> Type -> ProofState
88initElaborator = newProof
90elaborate :: Context -> Name -> Type -> aux -> Elab' aux a -> TC (a, String)
91elaborate ctxt n ty d elab = do let ps = initElaborator n ctxt ty
92 (a, ES ps' str _) <- runElab d elab ps
93 return (a, str)
95updateAux :: (aux -> aux) -> Elab' aux ()
96updateAux f = do ES (ps, a) l p <- get
97 put (ES (ps, f a) l p)
99getAux :: Elab' aux aux
100getAux = do ES (ps, a) _ _ <- get
101 return a
103unifyLog :: Bool -> Elab' aux ()
104unifyLog log = do ES (ps, a) l p <- get
105 put (ES (ps { unifylog = log }, a) l p)
107processTactic' t = do ES (p, a) logs prev <- get
108 (p', log) <- lift $ processTactic t p
109 put (ES (p', a) (logs ++ log) prev)
110 return ()
112-- Some handy gadgets for pulling out bits of state
114-- get the global context
115get_context :: Elab' aux Context
116get_context = do ES p _ _ <- get
117 return (context (fst p))
119-- update the context
120-- (should only be used for adding temporary definitions or all sorts of
121-- stuff could go wrong)
122set_context :: Context -> Elab' aux ()
123set_context ctxt = do ES (p, a) logs prev <- get
124 put (ES (p { context = ctxt }, a) logs prev)
126-- get the proof term
127get_term :: Elab' aux Term
128get_term = do ES p _ _ <- get
129 return (pterm (fst p))
131-- get the proof term
132update_term :: (Term -> Term) -> Elab' aux ()
133update_term f = do ES (p,a) logs prev <- get
134 let p' = p { pterm = f (pterm p) }
135 put (ES (p', a) logs prev)
137-- get the local context at the currently in focus hole
138get_env :: Elab' aux Env
139get_env = do ES p _ _ <- get
140 lift $ envAtFocus (fst p)
142get_holes :: Elab' aux [Name]
143get_holes = do ES p _ _ <- get
144 return (holes (fst p))
146get_probs :: Elab' aux Fails
147get_probs = do ES p _ _ <- get
148 return (problems (fst p))
150-- get the current goal type
151goal :: Elab' aux Type
152goal = do ES p _ _ <- get
153 b <- lift $ goalAtFocus (fst p)
154 return (binderTy b)
156-- Get the guess at the current hole, if there is one
157get_guess :: Elab' aux Type
158get_guess = do ES p _ _ <- get
159 b <- lift $ goalAtFocus (fst p)
160 case b of
161 Guess t v -> return v
162 _ -> fail "Not a guess"
164-- typecheck locally
165get_type :: Raw -> Elab' aux Type
166get_type tm = do ctxt <- get_context
167 env <- get_env
168 (val, ty) <- lift $ check ctxt env tm
169 return (finalise ty)
171get_type_val :: Raw -> Elab' aux (Term, Type)
172get_type_val tm = do ctxt <- get_context
173 env <- get_env
174 (val, ty) <- lift $ check ctxt env tm
175 return (finalise val, finalise ty)
177-- get holes we've deferred for later definition
178get_deferred :: Elab' aux [Name]
179get_deferred = do ES p _ _ <- get
180 return (deferred (fst p))
182checkInjective :: (Term, Term, Term) -> Elab' aux ()
183checkInjective (tm, l, r) = do ctxt <- get_context
184 if isInj ctxt tm then return ()
185 else lift $ tfail (NotInjective tm l r)
186 where isInj ctxt (P _ n _)
187 | isConName n ctxt = True
188 isInj ctxt (App f a) = isInj ctxt f
189 isInj ctxt (Constant _) = True
190 isInj ctxt (TType _) = True
191 isInj ctxt (Bind _ (Pi _) sc) = True
192 isInj ctxt _ = False
194-- get instance argument names
195get_instances :: Elab' aux [Name]
196get_instances = do ES p _ _ <- get
197 return (instances (fst p))
199-- given a desired hole name, return a unique hole name
200unique_hole = unique_hole' False
202unique_hole' :: Bool -> Name -> Elab' aux Name
203unique_hole' reusable n
204 = do ES p _ _ <- get
205 let bs = bound_in (pterm (fst p)) ++
206 bound_in (ptype (fst p))
207 n' <- uniqueNameCtxt (context (fst p)) n (holes (fst p)
208 ++ bs ++ dontunify (fst p) ++ usedns (fst p))
209 ES (p, a) s u <- get
210 -- Hmm: Do we need this level of uniqueness?
211 let p' = p -- if reusable then p else p { usedns = n' : usedns p }
212 put (ES (p', a) s u)
213 return n'
214 where
215 bound_in (Bind n b sc) = n : bi b ++ bound_in sc
216 where
217 bi (Let t v) = bound_in t ++ bound_in v
218 bi (Guess t v) = bound_in t ++ bound_in v
219 bi b = bound_in (binderTy b)
220 bound_in (App f a) = bound_in f ++ bound_in a
221 bound_in _ = []
223uniqueNameCtxt :: Context -> Name -> [Name] -> Elab' aux Name
224uniqueNameCtxt ctxt n hs
225 | n `elem` hs = uniqueNameCtxt ctxt (nextName n) hs
226 | [_] <- lookupTy n ctxt = uniqueNameCtxt ctxt (nextName n) hs
227 | otherwise = return n
229elog :: String -> Elab' aux ()
230elog str = do ES p logs prev <- get
231 put (ES p (logs ++ str ++ "\n") prev)
233getLog :: Elab' aux String
234getLog = do ES p logs _ <- get
235 return logs
237-- The primitives, from ProofState
239attack :: Elab' aux ()
240attack = processTactic' Attack
242claim :: Name -> Raw -> Elab' aux ()
243claim n t = processTactic' (Claim n t)
245exact :: Raw -> Elab' aux ()
246exact t = processTactic' (Exact t)
248fill :: Raw -> Elab' aux ()
249fill t = processTactic' (Fill t)
251match_fill :: Raw -> Elab' aux ()
252match_fill t = processTactic' (MatchFill t)
254prep_fill :: Name -> [Name] -> Elab' aux ()
255prep_fill n ns = processTactic' (PrepFill n ns)
257complete_fill :: Elab' aux ()
258complete_fill = processTactic' CompleteFill
260solve :: Elab' aux ()
261solve = processTactic' Solve
263start_unify :: Name -> Elab' aux ()
264start_unify n = processTactic' (StartUnify n)
266end_unify :: Elab' aux ()
267end_unify = processTactic' EndUnify
269regret :: Elab' aux ()
270regret = processTactic' Regret
272compute :: Elab' aux ()
273compute = processTactic' Compute
275computeLet :: Name -> Elab' aux ()
276computeLet n = processTactic' (ComputeLet n)
278simplify :: Elab' aux ()
279simplify = processTactic' Simplify
281hnf_compute :: Elab' aux ()
282hnf_compute = processTactic' HNF_Compute
284eval_in :: Raw -> Elab' aux ()
285eval_in t = processTactic' (EvalIn t)
287check_in :: Raw -> Elab' aux ()
288check_in t = processTactic' (CheckIn t)
290intro :: Maybe Name -> Elab' aux ()
291intro n = processTactic' (Intro n)
293introTy :: Raw -> Maybe Name -> Elab' aux ()
294introTy ty n = processTactic' (IntroTy ty n)
296forall :: Name -> Raw -> Elab' aux ()
297forall n t = processTactic' (Forall n t)
299letbind :: Name -> Raw -> Raw -> Elab' aux ()
300letbind n t v = processTactic' (LetBind n t v)
302expandLet :: Name -> Term -> Elab' aux ()
303expandLet n v = processTactic' (ExpandLet n v)
305rewrite :: Raw -> Elab' aux ()
306rewrite tm = processTactic' (Rewrite tm)
308equiv :: Raw -> Elab' aux ()
309equiv tm = processTactic' (Equiv tm)
311patvar :: Name -> Elab' aux ()
312patvar n = do env <- get_env
313 if (n `elem` map fst env) then do apply (Var n) []; solve
314 else do n' <- case n of
315 UN _ -> return n
316 MN _ _ -> unique_hole n
317 NS _ _ -> return n
318 processTactic' (PatVar n')
320patbind :: Name -> Elab' aux ()
321patbind n = processTactic' (PatBind n)
323focus :: Name -> Elab' aux ()
324focus n = processTactic' (Focus n)
326movelast :: Name -> Elab' aux ()
327movelast n = processTactic' (MoveLast n)
329matchProblems :: Elab' aux ()
330matchProblems = processTactic' MatchProblems
332unifyProblems :: Elab' aux ()
333unifyProblems = processTactic' UnifyProblems
335defer :: Name -> Elab' aux ()
336defer n = do n' <- unique_hole n
337 processTactic' (Defer n')
339deferType :: Name -> Raw -> [Name] -> Elab' aux ()
340deferType n ty ns = processTactic' (DeferType n ty ns)
342instanceArg :: Name -> Elab' aux ()
343instanceArg n = processTactic' (Instance n)
345setinj :: Name -> Elab' aux ()
346setinj n = processTactic' (SetInjective n)
348proofstate :: Elab' aux ()
349proofstate = processTactic' ProofState
351reorder_claims :: Name -> Elab' aux ()
352reorder_claims n = processTactic' (Reorder n)
354qed :: Elab' aux Term
355qed = do processTactic' QED
356 ES p _ _ <- get
357 return (pterm (fst p))
359undo :: Elab' aux ()
360undo = processTactic' Undo
362prepare_apply :: Raw -> [Bool] -> Elab' aux [Name]
363prepare_apply fn imps =
364 do ty <- get_type fn
365 ctxt <- get_context
366 env <- get_env
367 -- let claims = getArgs ty imps
368 -- claims <- mkClaims (normalise ctxt env ty) imps []
369 claims <- mkClaims (finalise ty) imps [] (map fst env)
370 ES (p, a) s prev <- get
371 -- reverse the claims we made so that args go left to right
372 let n = length (filter not imps)
373 let (h : hs) = holes p
374 put (ES (p { holes = h : (reverse (take n hs) ++ drop n hs) }, a) s prev)
375-- case claims of
376-- [] -> return ()
377-- (h : _) -> reorder_claims h
378 return claims
379 where
380 mkClaims (Bind n' (Pi t_in) sc) (i : is) claims hs =
381 do let t = rebind hs t_in
382 n <- unique_hole (mkMN n')
383-- when (null claims) (start_unify n)
384 let sc' = instantiate (P Bound n t) sc
385-- trace ("CLAIMING " ++ show (n, t) ++ " with " ++ show hs) $
386 claim n (forget t)
387 when i (movelast n)
388 mkClaims sc' is (n : claims) hs
389 mkClaims t [] claims _ = return (reverse claims)
390 mkClaims _ _ _ _
391 | Var n <- fn
392 = do ctxt <- get_context
393 case lookupTy n ctxt of
394 [] -> lift $ tfail $ NoSuchVariable n
395 _ -> lift $ tfail $ TooManyArguments n
396 | otherwise = fail $ "Too many arguments for " ++ show fn
398 doClaim ((i, _), n, t) = do claim n t
399 when i (movelast n)
401 mkMN n@(MN _ _) = n
402 mkMN n@(UN x) = MN 1000 x
403 mkMN n@(SN s) = MN 1000 (show s)
404 mkMN (NS n xs) = NS (mkMN n) xs
406 rebind hs (Bind n t sc)
407 | n `elem` hs = let n' = uniqueName n hs in
408 Bind n' (fmap (rebind hs) t) (rebind (n':hs) sc)
409 | otherwise = Bind n (fmap (rebind hs) t) (rebind (n:hs) sc)
410 rebind hs (App f a) = App (rebind hs f) (rebind hs a)
411 rebind hs t = t
413apply, match_apply :: Raw -> [(Bool, Int)] -> Elab' aux [Name]
414apply = apply' fill
415match_apply = apply' match_fill
417apply' :: (Raw -> Elab' aux ()) -> Raw -> [(Bool, Int)] -> Elab' aux [Name]
418apply' fillt fn imps =
419 do args <- prepare_apply fn (map fst imps)
420 env <- get_env
421 g <- goal
422 fillt (raw_apply fn (map Var args))
423 -- _Don't_ solve the arguments we're specifying by hand.
424 -- (remove from unified list before calling end_unify)
425 -- HMMM: Actually, if we get it wrong, the typechecker will complain!
426 -- so do nothing
427 ptm <- get_term
428 hs <- get_holes
429 ES (p, a) s prev <- get
430 let dont = nub $ head hs : dontunify p ++
431 if null imps then [] -- do all we can
432 else
433 map fst (filter (not.snd) (zip args (map fst imps)))
434 let (n, hunis) = -- trace ("AVOID UNIFY: " ++ show (fn, dont) ++ "\n" ++ show ptm) $
435 unified p
436 let unify = -- trace ("Not done " ++ show hs) $
437 dropGiven dont hunis hs
438 let notunify = -- trace ("Not done " ++ show hs) $
439 keepGiven dont hunis hs
440 put (ES (p { dontunify = dont, unified = (n, unify),
441 notunified = notunify ++ notunified p }, a) s prev)
442 ptm <- get_term
443 g <- goal
444-- trace ("Goal " ++ show g ++ "\n" ++ show (fn, imps, unify) ++ "\n" ++ show ptm) $
445 end_unify
446 ptm <- get_term
447 return (map (updateUnify unify) args)
448 where updateUnify hs n = case lookup n hs of
449 Just (P _ t _) -> t
450 _ -> n
452apply2 :: Raw -> [Maybe (Elab' aux ())] -> Elab' aux ()
453apply2 fn elabs =
454 do args <- prepare_apply fn (map isJust elabs)
455 fill (raw_apply fn (map Var args))
456 elabArgs args elabs
457 ES (p, a) s prev <- get
458 let (n, hs) = unified p
459 end_unify
460 solve
461 where elabArgs [] [] = return ()
462 elabArgs (n:ns) (Just e:es) = do focus n; e
463 elabArgs ns es
464 elabArgs (n:ns) (_:es) = elabArgs ns es
466 isJust (Just _) = False
467 isJust _ = True
469apply_elab :: Name -> [Maybe (Int, Elab' aux ())] -> Elab' aux ()
470apply_elab n args =
471 do ty <- get_type (Var n)
472 ctxt <- get_context
473 env <- get_env
474 claims <- doClaims (hnf ctxt env ty) args []
475 prep_fill n (map fst claims)
476 let eclaims = sortBy (\ (_, x) (_,y) -> priOrder x y) claims
477 elabClaims [] False claims
478 complete_fill
479 end_unify
480 where
481 priOrder Nothing Nothing = EQ
482 priOrder Nothing _ = LT
483 priOrder _ Nothing = GT
484 priOrder (Just (x, _)) (Just (y, _)) = compare x y
486 doClaims (Bind n' (Pi t) sc) (i : is) claims =
487 do n <- unique_hole (mkMN n')
488 when (null claims) (start_unify n)
489 let sc' = instantiate (P Bound n t) sc
490 claim n (forget t)
491 case i of
492 Nothing -> return ()
493 Just _ -> -- don't solve by unification as there is an explicit value
494 do ES (p, a) s prev <- get
495 put (ES (p { dontunify = n : dontunify p }, a) s prev)
496 doClaims sc' is ((n, i) : claims)
497 doClaims t [] claims = return (reverse claims)
498 doClaims _ _ _ = fail $ "Wrong number of arguments for " ++ show n
500 elabClaims failed r []
501 | null failed = return ()
502 | otherwise = if r then elabClaims [] False failed
503 else return ()
504 elabClaims failed r ((n, Nothing) : xs) = elabClaims failed r xs
505 elabClaims failed r (e@(n, Just (_, elaboration)) : xs)
506 | r = try (do ES p _ _ <- get
507 focus n; elaboration; elabClaims failed r xs)
508 (elabClaims (e : failed) r xs)
509 | otherwise = do ES p _ _ <- get
510 focus n; elaboration; elabClaims failed r xs
512 mkMN n@(MN _ _) = n
513 mkMN n@(UN x) = MN 1000 x
514 mkMN (NS n ns) = NS (mkMN n) ns
516-- If the goal is not a Pi-type, invent some names and make it a pi type
517checkPiGoal :: Name -> Elab' aux ()
518checkPiGoal n
519 = do g <- goal
520 case g of
521 Bind _ (Pi _) _ -> return ()
522 _ -> do a <- unique_hole (MN 0 "pargTy")
523 b <- unique_hole (MN 0 "pretTy")
524 f <- unique_hole (MN 0 "pf")
525 claim a RType
526 claim b RType
527 claim f (RBind n (Pi (Var a)) (Var b))
528 movelast a
529 movelast b
530 fill (Var f)
531 solve
532 focus f
534simple_app :: Elab' aux () -> Elab' aux () -> String -> Elab' aux ()
535simple_app fun arg appstr =
536 do a <- unique_hole (MN 0 "argTy")
537 b <- unique_hole (MN 0 "retTy")
538 f <- unique_hole (MN 0 "f")
539 s <- unique_hole (MN 0 "s")
540 claim a RType
541 claim b RType
542 claim f (RBind (MN 0 "aX") (Pi (Var a)) (Var b))
543 tm <- get_term
544 start_unify s
545 claim s (Var a)
546 prep_fill f [s]
547 focus f; fun
548 focus s; arg
549 tm <- get_term
550 ps <- get_probs
551 complete_fill
552 hs <- get_holes
553 env <- get_env
554 -- We don't need a and b in the hole queue any more since they were
555 -- just for checking f, so discard them if they are still there.
556 -- If they haven't been solved, regret will fail
557 when (a `elem` hs) $ do focus a; regretWith (CantInferType appstr)
558 when (b `elem` hs) $ do focus b; regretWith (CantInferType appstr)
559 end_unify
560 where regretWith err = try regret
561 (lift $ tfail err)
563-- Abstract over an argument of unknown type, giving a name for the hole
564-- which we'll fill with the argument type too.
565arg :: Name -> Name -> Elab' aux ()
566arg n tyhole = do ty <- unique_hole tyhole
567 claim ty RType
568 forall n (Var ty)
570-- try a tactic, if it adds any unification problem, return an error
571no_errors :: Elab' aux () -> Elab' aux ()
572no_errors tac
573 = do ps <- get_probs
574 tac
575 ps' <- get_probs
576 if (length ps' > length ps) then
577 case reverse ps' of
578 ((x,y,env,err) : _) ->
579 let env' = map (\(x, b) -> (x, binderTy b)) env in
580 lift $ tfail $ CantUnify False x y err env' 0
581 else return ()
583-- Try a tactic, if it fails, try another
584try :: Elab' aux a -> Elab' aux a -> Elab' aux a
585try t1 t2 = try' t1 t2 False
587try' :: Elab' aux a -> Elab' aux a -> Bool -> Elab' aux a
588try' t1 t2 proofSearch
589 = do s <- get
590 ps <- get_probs
591 case prunStateT 999999 False ps t1 s of
592 OK ((v, _), s') -> do put s'
593 return v
594 Error e1 -> if recoverableErr e1 then
595 do case runStateT t2 s of
596 OK (v, s') -> do put s'; return v
597 Error e2 -> if score e1 >= score e2
598 then lift (tfail e1)
599 else lift (tfail e2)
600 else lift (tfail e1)
601 where recoverableErr err@(CantUnify r _ _ _ _ _)
602 = -- traceWhen r (show err) $
603 r || proofSearch
604 recoverableErr (ProofSearchFail _) = False
605 recoverableErr _ = True
607tryWhen :: Bool -> Elab' aux a -> Elab' aux a -> Elab' aux a
608tryWhen True a b = try a b
609tryWhen False a b = a
612-- Try a selection of tactics. Exactly one must work, all others must fail
613tryAll :: [(Elab' aux a, String)] -> Elab' aux a
614tryAll xs = tryAll' [] 999999 (cantResolve, 0) (map fst xs)
615 where
616 cantResolve :: Elab' aux a
617 cantResolve = lift $ tfail $ CantResolveAlts (map snd xs)
619 tryAll' :: [Elab' aux a] -> -- successes
620 Int -> -- most problems
621 (Elab' aux a, Int) -> -- smallest failure
622 [Elab' aux a] -> -- still to try
623 Elab' aux a
624 tryAll' [res] pmax _ [] = res
625 tryAll' (_:_) pmax _ [] = cantResolve
626 tryAll' [] pmax (f, _) [] = f
627 tryAll' cs pmax f (x:xs)
628 = do s <- get
629 ps <- get_probs
630 case prunStateT pmax True ps x s of
631 OK ((v, newps), s') ->
632 do let cs' = if (newps < pmax)
633 then [do put s'; return v]
634 else (do put s'; return v) : cs
635 tryAll' cs' newps f xs
636 Error err -> do put s
637 if (score err) < 100
638 then tryAll' cs pmax (better err f) xs
639 else tryAll' [] pmax (better err f) xs -- give up
642 better err (f, i) = let s = score err in
643 if (s >= i) then (lift (tfail err), s)
644 else (f, i)
646prunStateT pmax zok ps x s
647 = case runStateT x s of
648 OK (v, s'@(ES (p, _) _ _)) ->
649 let newps = length (problems p) - length ps
650 newpmax = if newps < 0 then 0 else newps in
651 if (newpmax > pmax || (not zok && newps > 0)) -- length ps == 0 && newpmax > 0))
652 then case reverse (problems p) of
653 ((_,_,_,e):_) -> Error e
654 else OK ((v, newpmax), s')
655 Error e -> Error e
657qshow :: Fails -> String
658qshow fs = show (map (\ (x, y, _, _) -> (x, y)) fs)
660dumpprobs [] = ""
661dumpprobs ((_,_,_,e):es) = show e ++ "\n" ++ dumpprobs es