{-| Module : Idris.Core.Typecheck Description : Idris' type checker. License : BSD3 Maintainer : The Idris Community. -} {-# LANGUAGE DeriveFunctor, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, PatternGuards #-} module Idris.Core.Typecheck where import Idris.Core.Evaluate import Idris.Core.TT import Control.Monad.State -- To check conversion, normalise each term wrt the current environment. -- Since we haven't converted everything to de Bruijn indices yet, we'll have to -- deal with alpha conversion - we do this by making each inner term de Bruijn -- indexed with 'finalise' convertsC :: Context -> Env -> Term -> Term -> StateT UCs TC () convertsC ctxt env x y = do let hs = map fstEnv (filter isHole env) c1 <- convEq ctxt hs x y if c1 then return () else do c2 <- convEq ctxt hs (finalise (normalise ctxt env x)) (finalise (normalise ctxt env y)) if c2 then return () else lift $ tfail (CantConvert (finalise (normalise ctxt env x)) (finalise (normalise ctxt env y)) (errEnv env)) converts :: Context -> Env -> Term -> Term -> TC () converts ctxt env x y = let hs = map fstEnv (filter isHole env) in case convEq' ctxt hs x y of OK True -> return () _ -> case convEq' ctxt hs (finalise (normalise ctxt env x)) (finalise (normalise ctxt env y)) of OK True -> return () _ -> tfail (CantConvert (finalise (normalise ctxt env x)) (finalise (normalise ctxt env y)) (errEnv env)) isHole (n, _, Hole _) = True isHole _ = False errEnv = map (\(x, _, b) -> (x, binderTy b)) isType :: Context -> Env -> Term -> TC () isType ctxt env tm = isType' (normalise ctxt env tm) where isType' tm | isUniverse tm = return () | otherwise = fail (showEnv env tm ++ " is not a Type") convType :: String -> Context -> Env -> Term -> StateT UCs TC () convType tcns ctxt env tm = do (v, cs) <- get put (v + 1, cs) case normalise ctxt env tm of UType _ -> return () _ -> convertsC ctxt env tm (TType (UVar tcns v)) recheck :: String -> Context -> Env -> Raw -> Term -> TC (Term, Type, UCs) recheck = recheck_borrowing False [] recheck_borrowing :: Bool -> [Name] -> String -> Context -> Env -> Raw -> Term -> TC (Term, Type, UCs) recheck_borrowing uniq_check bs tcns ctxt env tm orig = let v = next_tvar ctxt in case runStateT (check' False tcns ctxt env tm) (v, []) of -- holes banned Error (IncompleteTerm _) -> Error $ IncompleteTerm orig Error e -> Error e OK ((tm, ty), constraints) -> do when uniq_check $ checkUnique bs ctxt env tm return (tm, ty, constraints) check :: Context -> Env -> Raw -> TC (Term, Type) check ctxt env tm -- Holes allowed, so constraint namespace doesn't matter = evalStateT (check' True [] ctxt env tm) (0, []) check' :: Bool -> String -> Context -> Env -> Raw -> StateT UCs TC (Term, Type) check' holes tcns ctxt env top = do (tm, ty, _) <- chk Rig1 (TType (UVar tcns (-5))) Nothing env top return (tm, ty) where smaller (UType NullType) _ = UType NullType smaller _ (UType NullType) = UType NullType smaller (UType u) _ = UType u smaller _ (UType u) = UType u smaller x _ = x astate | holes = MaybeHoles | otherwise = Complete chk :: RigCount -> -- 'sigma' in Bob Atkey's QTT paper, except that -- for implementation purposes it could be 0, 1 or omega when -- checking variable usage. Type -> -- uniqueness level Maybe UExp -> -- universe for kind Env -> Raw -> StateT UCs TC (Term, Type, [Name]) chk rigc u lvl env (Var n) | Just (i, erig, ty) <- lookupTyEnv n env = case rigSafe holes erig rigc n of Nothing -> return (P Bound n ty, ty, used rigc n) Just msg -> lift $ tfail $ Msg msg -- If we're elaborating, we don't want the private names; if we're -- checking an already elaborated term, we do | [P nt n' ty] <- lookupP_all (not holes) True n ctxt = return (P nt n' ty, ty, []) -- -- If the names are ambiguous, require it to be fully qualified -- | [P nt n' ty] <- lookupP_all (not holes) True n ctxt -- = return (P nt n' ty, ty, []) | otherwise = do lift $ tfail $ NoSuchVariable n where rigSafe True _ _ n = Nothing rigSafe _ Rig1 RigW n = Just ("Trying to use linear name " ++ show n ++ " in non-linear context") rigSafe _ Rig0 RigW n = Just ("Trying to use irrelevant name " ++ show n ++ " in relevant context") rigSafe _ Rig0 Rig1 n = Just ("Trying to use irrelevant name " ++ show n ++ " in relevant context") rigSafe _ _ _ n = Nothing used Rig0 n = [] used _ n = [n] chk rigc u lvl env ap@(RApp f RType) | not holes -- special case to reduce constraintss = do (fv, fty, fns) <- chk rigc u Nothing env f let fty' = case uniqueBinders (map fstEnv env) (finalise fty) of ty@(Bind x (Pi _ i s k) t) -> ty _ -> uniqueBinders (map fstEnv env) $ case normalise ctxt env fty of ty@(Bind x (Pi _ i s k) t) -> ty _ -> normalise ctxt env fty case fty' of Bind x (Pi rig i (TType v') k) t -> do (v, cs) <- get put (v+1, ULT (UVar tcns v) v' : cs) let apty = simplify initContext env (Bind x (Let rig (TType v') (TType (UVar tcns v))) t) return (App Complete fv (TType (UVar tcns v)), apty, fns) Bind x (Pi rig i s k) t -> do (av, aty, _) <- chk rigc u Nothing env RType convertsC ctxt env aty s let apty = simplify initContext env (Bind x (Let rig aty av) t) return (App astate fv av, apty, fns) t -> lift $ tfail $ NonFunctionType fv fty chk rigc u lvl env ap@(RApp f a) = do (fv, fty, fns) <- chk rigc u Nothing env f let (rigf, fty') = case uniqueBinders (map fstEnv env) (finalise fty) of ty@(Bind x (Pi rig i s k) t) -> (rig, ty) _ -> case normalise ctxt env fty of ty@(Bind x (Pi rig i s k) t) -> (rig, uniqueBinders (map fstEnv env) ty) _ -> (RigW, uniqueBinders (map fstEnv env) (normalise ctxt env fty)) -- This is an error, caught below... (av, aty, ans_in) <- chk (rigMult rigf rigc) u Nothing env a -- usage in 'a' doesn't count if the binder has multiplicity 0 let ans = case rigf of Rig0 -> [] _ -> ans_in case fty' of Bind x (Pi rig i s k) t -> do convertsC ctxt env aty s let apty = simplify initContext env (Bind x (Let rig aty av) t) return (App astate fv av, apty, fns ++ ans) t -> lift $ tfail $ NonFunctionType fv fty chk rigc u lvl env RType | holes = return (TType (UVal 0), TType (UVal 0), []) | otherwise = do (v, cs) <- get let c = ULT (UVar tcns v) (UVar tcns (v+1)) put (v+2, (c:cs)) return (TType (UVar tcns v), TType (UVar tcns (v+1)), []) chk rigc u lvl env (RUType un) | holes = return (UType un, TType (UVal 0), []) | otherwise = do -- TODO! Issue #1715 on the issue tracker. -- https://github.com/idris-lang/Idris-dev/issues/1715 -- (v, cs) <- get -- let c = ULT (UVar v) (UVar (v+1)) -- put (v+2, (c:cs)) -- return (TType (UVar v), TType (UVar (v+1))) return (UType un, TType (UVal 0), []) chk rigc u lvl env (RConstant Forgot) = return (Erased, Erased, []) chk rigc u lvl env (RConstant c) = return (Constant c, constType c, []) where constType (I _) = Constant (AType (ATInt ITNative)) constType (BI _) = Constant (AType (ATInt ITBig)) constType (Fl _) = Constant (AType ATFloat) constType (Ch _) = Constant (AType (ATInt ITChar)) constType (Str _) = Constant StrType constType (B8 _) = Constant (AType (ATInt (ITFixed IT8))) constType (B16 _) = Constant (AType (ATInt (ITFixed IT16))) constType (B32 _) = Constant (AType (ATInt (ITFixed IT32))) constType (B64 _) = Constant (AType (ATInt (ITFixed IT64))) constType TheWorld = Constant WorldType constType Forgot = Erased constType _ = TType (UVal 0) chk rigc u lvl env (RBind n (Pi rig i s k) t) = do (sv, st, sns) <- chk Rig0 u Nothing (envZero env) s (v, cs) <- get (kv, kt, _) <- chk Rig0 u Nothing (envZero env) k -- no need to validate these constraints, they are independent put (v+1, cs) let maxu = case lvl of Nothing -> UVar tcns v Just v' -> v' (tv, tt, tns) <- chk Rig0 st (Just maxu) ((n, Rig0, Pi Rig0 i sv kv) : envZero env) t case (normalise ctxt env st, normalise ctxt env tt) of (TType su, TType tu) -> do when (not holes) $ do (v, cs) <- get put (v, ULE su maxu : ULE tu maxu : cs) let k' = TType (UVar tcns v) `smaller` st `smaller` kv `smaller` u return (Bind n (Pi rig i (uniqueBinders (map fstEnv env) sv) k') (pToV n tv), k', sns ++ tns) (un, un') -> let k' = st `smaller` kv `smaller` un `smaller` un' `smaller` u in return (Bind n (Pi rig i (uniqueBinders (map fstEnv env) sv) k') (pToV n tv), k', sns ++ tns) chk rigc_in u lvl env (RBind n b sc) = do (b', bt', bns) <- checkBinder b (scv, sct, scns) <- chk rigc (smaller bt' u) Nothing ((n, getCount b, b'):env) sc checkUsageOK (getCount b) scns discharge n b' bt' (pToV n scv) (pToV n sct) (bns ++ scns) where -- should only check at 0 or 1 (as in Bob's paper) but rigc_in -- might be RigW, since that's how we check in the Var rule that -- a linearly bound name is only used in a linear context. -- So, here, check the scope at multiplicity 1 rigc = case rigc_in of Rig0 -> Rig0 _ -> Rig1 getCount (Pi rig _ _ _) = rigMult rigc rig getCount (PVar rig _) = rigMult rigc rig getCount (Lam rig _) = rigMult rigc rig getCount (Let rig _ _) = rigMult rigc rig getCount _ = rigMult rigc RigW checkUsageOK Rig0 _ = return () checkUsageOK RigW _ = return () checkUsageOK Rig1 ns = let used = length (filter (==n) ns) in if used == 1 then return () else lift $ tfail $ Msg $ "There are " ++ (show used) ++ " uses of linear name " ++ show n checkBinder (Lam rig t) = do (tv, tt, _) <- chk Rig0 u Nothing (envZero env) t convType tcns ctxt env tt return (Lam rig tv, tt, []) checkBinder (Let rig t v) = do (tv, tt, _) <- chk Rig0 u Nothing (envZero env) t (vv, vt, vns) <- chk (rigMult rig rigc) u Nothing env v convertsC ctxt env vt tv convType tcns ctxt env tt return (Let rig tv vv, tt, vns) checkBinder (NLet t v) = do (tv, tt, _) <- chk Rig0 u Nothing (envZero env) t (vv, vt, vns) <- chk rigc u Nothing env v convertsC ctxt env vt tv convType tcns ctxt env tt return (NLet tv vv, tt, vns) checkBinder (Hole t) | not holes = lift $ tfail (IncompleteTerm undefined) | otherwise = do (tv, tt, _) <- chk Rig0 u Nothing (envZero env) t convType tcns ctxt env tt return (Hole tv, tt, []) checkBinder (GHole i ns t) = do (tv, tt, _) <- chk Rig0 u Nothing (envZero env) t convType tcns ctxt env tt return (GHole i ns tv, tt, []) checkBinder (Guess t v) | not holes = lift $ tfail (IncompleteTerm undefined) | otherwise = do (tv, tt, _) <- chk Rig0 u Nothing (envZero env) t (vv, vt, vns) <- chk rigc u Nothing env v convertsC ctxt env vt tv convType tcns ctxt env tt return (Guess tv vv, tt, vns) checkBinder (PVar rig t) = do (tv, tt, _) <- chk Rig0 u Nothing (envZero env) t convType tcns ctxt env tt return (PVar rig tv, tt, []) checkBinder (PVTy t) = do (tv, tt, _) <- chk Rig0 u Nothing (envZero env) t convType tcns ctxt env tt return (PVTy tv, tt, []) discharge n (Lam r t) bt scv sct ns = return (Bind n (Lam r t) scv, Bind n (Pi r Nothing t bt) sct, ns) discharge n (Pi r i t k) bt scv sct ns = return (Bind n (Pi r i t k) scv, sct, ns) discharge n (Let r t v) bt scv sct ns = return (Bind n (Let r t v) scv, Bind n (Let r t v) sct, ns) discharge n (NLet t v) bt scv sct ns = return (Bind n (NLet t v) scv, Bind n (Let RigW t v) sct, ns) discharge n (Hole t) bt scv sct ns = return (Bind n (Hole t) scv, sct, ns) discharge n (GHole i ns t) bt scv sct uns = return (Bind n (GHole i ns t) scv, sct, uns) discharge n (Guess t v) bt scv sct ns = return (Bind n (Guess t v) scv, sct, ns) discharge n (PVar r t) bt scv sct ns = return (Bind n (PVar r t) scv, Bind n (PVTy t) sct, ns) discharge n (PVTy t) bt scv sct ns = return (Bind n (PVTy t) scv, sct, ns) -- Number of times a name can be used data UniqueUse = Never -- no more times | Once -- at most once more | LendOnly -- only under 'lend' | Many -- unlimited deriving Eq -- If any binders are of kind 'UniqueType' or 'AllTypes' and the name appears -- in the scope more than once, this is an error. checkUnique :: [Name] -> Context -> Env -> Term -> TC () checkUnique borrowed ctxt env tm = evalStateT (chkBinders env (explicitNames tm)) [] where isVar (P _ _ _) = True isVar (V _) = True isVar _ = False chkBinders :: Env -> Term -> StateT [(Name, (UniqueUse, Universe))] TC () chkBinders env (V i) | length env > i = chkName (fstEnv (env!!i)) chkBinders env (P _ n _) = chkName n -- 'lending' a unique or nulltype variable doesn't count as a use, -- but we still can't lend something that's already been used. chkBinders env (App _ (App _ (P _ (NS (UN lend) [owner]) _) t) a) | isVar a && owner == txt "Ownership" && (lend == txt "lend" || lend == txt "Read") = do chkBinders env t -- Check the type normally st <- get -- Remove the 'LendOnly' names from the unusable set put (filter (\(n, (ok, _)) -> ok /= LendOnly) st) chkBinders env a put st -- Reset the old state after checking the argument chkBinders env (App _ f a) = do chkBinders env f; chkBinders env a chkBinders env (Bind n b t) = do chkBinderName env n b st <- get case b of Let _ t v -> chkBinders env v _ -> return () chkBinders ((n, Rig0, b) : env) t chkBinders env t = return () chkBinderName :: Env -> Name -> Binder Term -> StateT [(Name, (UniqueUse, Universe))] TC () chkBinderName env n b = do let rawty = forgetEnv (map fstEnv env) (binderTy b) (_, kind) <- lift $ check ctxt env rawty case kind of UType UniqueType -> do ns <- get if n `elem` borrowed then put ((n, (LendOnly, NullType)) : ns) else put ((n, (Once, UniqueType)) : ns) UType NullType -> do ns <- get put ((n, (Many, NullType)) : ns) UType AllTypes -> do ns <- get put ((n, (Once, AllTypes)) : ns) _ -> return () chkName n = do ns <- get case lookup n ns of Nothing -> return () Just (Many, k) -> return () Just (Never, k) -> lift $ tfail (UniqueError k n) Just (LendOnly, k) -> lift $ tfail (UniqueError k n) Just (Once, k) -> put ((n, (Never, k)) : filter (\x -> fst x /= n) ns)