{-| Module : IRTS.Compiler Description : Coordinates the compilation process. License : BSD3 Maintainer : The Idris Community. -} {-# LANGUAGE CPP, FlexibleContexts, MultiWayIf, NamedFieldPuns, PatternGuards, TypeSynonymInstances #-} module IRTS.Compiler(compile, generate) where import Idris.AbsSyntax import Idris.ASTUtils import Idris.Core.CaseTree import Idris.Core.Evaluate import Idris.Core.TT import Idris.Erasure import Idris.Error import Idris.Options import Idris.Output import IRTS.CodegenC import IRTS.CodegenCommon import IRTS.Defunctionalise import IRTS.DumpBC import IRTS.Exports import IRTS.Inliner import IRTS.LangOpts import IRTS.Portable import IRTS.Simplified import Prelude hiding (id, (.)) import Control.Category import Control.Monad.State import Data.List import qualified Data.Map as M import Data.Maybe (maybe) import Data.Ord import qualified Data.Set as S import System.Directory import System.Exit import System.IO import System.Process -- | Compile to simplified forms and return CodegenInfo compile :: Codegen -> FilePath -> Maybe Term -> Idris CodegenInfo compile codegen f mtm = do logCodeGen 1 "Compiling Output." iReport 2 "Compiling Output." checkMVs -- check for undefined metavariables checkTotality -- refuse to compile if there are totality problems exports <- findExports let rootNames = case mtm of Nothing -> [] Just t -> freeNames t logCodeGen 1 "Running Erasure Analysis" iReport 3 "Running Erasure Analysis" reachableNames <- performUsageAnalysis (rootNames ++ getExpNames exports) maindef <- case mtm of Nothing -> return [] Just tm -> do md <- irMain tm logCodeGen 1 $ "MAIN: " ++ show md return [(sMN 0 "runMain", md)] objs <- getObjectFiles codegen libs <- getLibs codegen flags <- getFlags codegen hdrs <- getHdrs codegen impdirs <- rankedImportDirs f ttDeclarations <- getDeclarations reachableNames defsIn <- mkDecls reachableNames -- if no 'main term' given, generate interface files let iface = case mtm of Nothing -> True Just _ -> False let defs = defsIn ++ maindef -- Inlined top level LDecl made here let defsInlined = inlineAll defs let defsUniq = map (allocUnique (addAlist defsInlined emptyContext)) defsInlined logCodeGen 1 "Inlining" dumpCases <- getDumpCases case dumpCases of Nothing -> return () Just f -> runIO $ writeFile f (showCaseTrees defsUniq) let (nexttag, tagged) = addTags 65536 (liftAll defsInlined) let ctxtIn = addAlist tagged emptyContext logCodeGen 1 "Defunctionalising" iReport 3 "Defunctionalising" let defuns_in = defunctionalise nexttag ctxtIn logCodeGen 5 $ show defuns_in logCodeGen 1 "Inlining" iReport 3 "Inlining" let defuns = inline defuns_in logCodeGen 5 $ show defuns logCodeGen 1 "Resolving variables for CG" let checked = simplifyDefs defuns (toAlist defuns) outty <- outputTy dumpDefun <- getDumpDefun case dumpDefun of Nothing -> return () Just f -> runIO $ writeFile f (dumpDefuns defuns) triple <- Idris.AbsSyntax.targetTriple cpu <- Idris.AbsSyntax.targetCPU logCodeGen 1 "Generating Code." iReport 2 "Generating Code." case checked of OK c -> do return $ CodegenInfo f outty triple cpu hdrs impdirs objs libs flags NONE c (toAlist defuns) tagged iface exports ttDeclarations Error e -> ierror e where checkMVs = do i <- getIState case map fst (idris_metavars i) \\ primDefs of [] -> return () ms -> do iputStrLn $ "WARNING: There are incomplete holes:\n " ++ show ms iputStrLn "\nEvaluation of any of these will crash at run time." return () checkTotality = do i <- getIState case idris_totcheckfail i of [] -> return () ((fc, msg):fs) -> ierror . At fc . Msg $ "Cannot compile:\n " ++ msg generate :: Codegen -> FilePath -> CodegenInfo -> IO () generate codegen mainmod ir = case codegen of -- Built-in code generators (FIXME: lift these out!) Via _ "c" -> codegenC ir -- Any external code generator Via fm cg -> do input <- case fm of IBCFormat -> return mainmod JSONFormat -> do tempdir <- getTemporaryDirectory (fn, h) <- openTempFile tempdir "idris-cg.json" writePortable h ir hClose h return fn let cmd = "idris-codegen-" ++ cg args = [input, "-o", outputFile ir] ++ compilerFlags ir exit <- rawSystem cmd (if interfaces ir then "--interface" : args else args) when (exit /= ExitSuccess) $ putStrLn ("FAILURE: " ++ show cmd ++ " " ++ show args) Bytecode -> dumpBC (simpleDecls ir) (outputFile ir) irMain :: TT Name -> Idris LDecl irMain tm = do i <- irTerm (sMN 0 "runMain") M.empty [] tm return $ LFun [] (sMN 0 "runMain") [] (LForce i) mkDecls :: [Name] -> Idris [(Name, LDecl)] mkDecls used = do i <- getIState let ds = filter (\(n, d) -> n `elem` used || isCon d) $ ctxtAlist (tt_ctxt i) decls <- mapM build ds return decls getDeclarations :: [Name] -> Idris ([(Name, TTDecl)]) getDeclarations used = do i <- getIState let ds = filter (\(n, (d,_,_,_,_,_)) -> n `elem` used || isCon d) $ ((toAlist . definitions . tt_ctxt) i) return ds showCaseTrees :: [(Name, LDecl)] -> String showCaseTrees = showSep "\n\n" . map showCT . sortBy (comparing defnRank) where showCT (n, LFun opts f args lexp) = showOpts ++ show n ++ " " ++ showSep " " (map show args) ++ " =\n\t" ++ show lexp where showOpts | Inline `elem` opts = "%inline " | otherwise = "" showCT (n, LConstructor c t a) = "data " ++ show n ++ " " ++ show a defnRank :: (Name, LDecl) -> String defnRank (n, LFun _ _ _ _) = "1" ++ nameRank n defnRank (n, LConstructor _ _ _) = "2" ++ nameRank n nameRank :: Name -> String nameRank (UN s) = "1" ++ show s nameRank (MN i s) = "2" ++ show s ++ show i nameRank (NS n ns) = "3" ++ concatMap show (reverse ns) ++ nameRank n nameRank (SN sn) = "4" ++ snRank sn nameRank n = "5" ++ show n snRank :: SpecialName -> String snRank (WhereN i n n') = "1" ++ nameRank n' ++ nameRank n ++ show i snRank (ImplementationN n args) = "2" ++ nameRank n ++ concatMap show args snRank (ParentN n s) = "3" ++ nameRank n ++ show s snRank (MethodN n) = "4" ++ nameRank n snRank (CaseN _ n) = "5" ++ nameRank n snRank (ImplementationCtorN n) = "7" ++ nameRank n snRank (WithN i n) = "8" ++ nameRank n ++ show i isCon (TyDecl _ _) = True isCon _ = False build :: (Name, Def) -> Idris (Name, LDecl) build (n, d) = do i <- getIState case getPrim n i of Just (ar, op) -> let args = map (\x -> sMN x "op") [0..] in return (n, (LFun [] n (take ar args) (LOp op (map LV (take ar args))))) _ -> do def <- mkLDecl n d logCodeGen 3 $ "Compiled " ++ show n ++ " =\n\t" ++ show def return (n, def) where getPrim n i | Just (ar, op) <- lookup n (idris_scprims i) = Just (ar, op) | Just ar <- lookup n (S.toList (idris_externs i)) = Just (ar, LExternal n) getPrim n i = Nothing declArgs args inl n (LLam xs x) = declArgs (args ++ xs) inl n x declArgs args inl n x = LFun (if inl then [Inline] else []) n args x mkLDecl n (Function tm _) = declArgs [] True n <$> irTerm n M.empty [] tm mkLDecl n (CaseOp ci _ _ _ pats cd) = declArgs [] (case_inlinable ci || caseName n) n <$> irTree n args sc where (args, sc) = cases_runtime cd -- Always attempt to inline functions arising from 'case' expressions caseName (SN (CaseN _ _)) = True caseName (SN (WithN _ _)) = True caseName (NS n _) = caseName n caseName _ = False mkLDecl n (TyDecl (DCon tag arity _) _) = LConstructor n tag . length <$> fgetState (cg_usedpos . ist_callgraph n) mkLDecl n (TyDecl (TCon t a) _) = return $ LConstructor n (-1) a mkLDecl n _ = return $ (declArgs [] True n LNothing) -- postulate, never run data VarInfo = VI { viMethod :: Maybe Name } deriving Show type Vars = M.Map Name VarInfo irTerm :: Name -> Vars -> [Name] -> Term -> Idris LExp irTerm top vs env tm@(App _ f a) = do ist <- getIState case unApply tm of (P _ n _, args) | n `elem` map fst (idris_metavars ist) \\ primDefs -> return $ LError $ "ABORT: Attempt to evaluate hole " ++ show n (P _ (UN m) _, args) | m == txt "mkForeignPrim" -> doForeign vs env (reverse (drop 4 args)) -- drop implicits (P _ (UN u) _, [_, arg]) | u == txt "unsafePerformPrimIO" -> irTerm top vs env arg (P _ (UN u) _, _) | u == txt "assert_unreachable" -> return $ LError $ "ABORT: Reached an unreachable case in " ++ show top (P _ (UN u) _, [_, msg]) | u == txt "idris_crash" -> do msg' <- irTerm top vs env msg return $ LOp LCrash [msg'] -- TMP HACK - until we get inlining. (P _ (UN r) _, [_, _, _, _, _, arg]) | r == txt "replace" -> irTerm top vs env arg -- 'void' doesn't have any pattern clauses and only gets called on -- erased things in higher order contexts (also a TMP HACK...) (P _ (UN r) _, _) | r == txt "void" -> return LNothing -- Laziness, the old way (P _ (UN l) _, [_, arg]) | l == txt "lazy" -> error "lazy has crept in somehow" (P _ (UN l) _, [_, arg]) | l == txt "force" -> LForce <$> irTerm top vs env arg -- Laziness, the new way (P _ (UN l) _, [_, _, arg]) | l == txt "Delay" -> LLazyExp <$> irTerm top vs env arg (P _ (UN l) _, [_, _, arg]) | l == txt "Force" -> LForce <$> irTerm top vs env arg (P _ (UN a) _, [_, _, _, arg]) | a == txt "assert_smaller" -> irTerm top vs env arg (P _ (UN a) _, [_, arg]) | a == txt "assert_total" -> irTerm top vs env arg (P _ (UN p) _, [_, arg]) | p == txt "par" -> do arg' <- irTerm top vs env arg return $ LOp LPar [LLazyExp arg'] (P _ (UN pf) _, [arg]) | pf == txt "prim_fork" -> do arg' <- irTerm top vs env arg return $ LOp LFork [LLazyExp arg'] (P _ (UN m) _, [_,size,t]) | m == txt "malloc" -> irTerm top vs env t (P _ (UN tm) _, [_,t]) | tm == txt "trace_malloc" -> irTerm top vs env t -- TODO -- This case is here until we get more general inlining. It's just -- a really common case, and the laziness hurts... (P _ (NS (UN be) [b,p]) _, [_,x,(App _ (App _ (App _ (P _ (UN d) _) _) _) t), (App _ (App _ (App _ (P _ (UN d') _) _) _) e)]) | be == txt "ifThenElse" , d == txt "Delay" , d' == txt "Delay" , b == txt "Bool" , p == txt "Prelude" -> do x' <- irTerm top vs env x t' <- irTerm top vs env t e' <- irTerm top vs env e return (LCase Shared x' [LConCase 0 (sNS (sUN "False") ["Bool","Prelude"]) [] e' ,LConCase 1 (sNS (sUN "True" ) ["Bool","Prelude"]) [] t' ]) -- data constructor (P (DCon t arity _) n _, args) -> do detag <- fgetState (opt_detaggable . ist_optimisation n) used <- map fst <$> fgetState (cg_usedpos . ist_callgraph n) let isNewtype = length used == 1 && detag let argsPruned = [a | (i,a) <- zip [0..] args, i `elem` used] -- The following code removes fields from data constructors -- and performs the newtype optimisation. -- -- The general rule here is: -- Everything we get as input is not touched by erasure, -- so it conforms to the official arities and types -- and we can reason about it like it's plain TT. -- -- It's only the data that leaves this point that's erased -- and possibly no longer typed as the original TT version. -- -- Especially, underapplied constructors must yield functions -- even if all the remaining arguments are erased -- (the resulting function *will* be applied, to NULLs). -- -- This will probably need rethinking when we get erasure from functions. -- "padLams" will wrap our term in LLam-bdas and give us -- the "list of future unerased args" coming from these lambdas. -- -- We can do whatever we like with the list of unerased args, -- hence it takes a lambda: \unerased_argname_list -> resulting_LExp. let padLams = padLambdas used (length args) arity case compare (length args) arity of -- overapplied GT -> ifail ("overapplied data constructor: " ++ show tm ++ "\nDEBUG INFO:\n" ++ "Arity: " ++ show arity ++ "\n" ++ "Arguments: " ++ show args ++ "\n" ++ "Pruned arguments: " ++ show argsPruned) -- exactly saturated EQ | isNewtype -> irTerm top vs env (head argsPruned) -- compile Nat-likes as bigints | Just LikeZ <- isLikeNat ist n -> irTerm top vs env $ Constant (BI 0) -- compile Nat-likes as bigints | Just LikeS <- isLikeNat ist n -> irTerm top vs env $ mkApp (P Ref (sUN "prim__addBigInt") Erased) (Constant (BI 1) : argsPruned) | otherwise -- not newtype, plain data ctor -> buildApp (LV n) argsPruned -- not saturated, underapplied LT | isNewtype -- newtype , length argsPruned == 1 -- and we already have the value -> padLams . (\tm [] -> tm) -- the [] asserts there are no unerased args <$> irTerm top vs env (head argsPruned) | isNewtype -- newtype but the value is not among args yet -> return . padLams $ \[vn] -> LApp False (LV n) [LV vn] -- compile Nat-likes as bigints -- it seems that prim applications needn't be saturated | Just LikeS <- isLikeNat ist n -> irTerm top vs env $ App Complete (P Ref (sUN "prim__addBigInt") Erased) (Constant $ BI 1) -- not a newtype, just apply to a constructor | otherwise -> padLams . applyToNames <$> buildApp (LV n) argsPruned -- type constructor (P (TCon t a) n _, args) -> return LNothing -- an external name applied to arguments (P _ n _, args) | S.member (n, length args) (idris_externs ist) -> do LOp (LExternal n) <$> mapM (irTerm top vs env) args -- a name applied to arguments (P _ n _, args) -> do case lookup n (idris_scprims ist) of -- if it's a primitive that is already saturated, -- compile to the corresponding op here already to save work Just (arity, op) | length args == arity -> LOp op <$> mapM (irTerm top vs env) args -- otherwise, just apply the name _ -> applyName n ist args -- turn de bruijn vars into regular named references and try again (V i, args) -> irTerm top vs env $ mkApp (P Bound (env !! i) Erased) args (f, args) -> LApp False <$> irTerm top vs env f <*> mapM (irTerm top vs env) args where buildApp :: LExp -> [Term] -> Idris LExp buildApp e [] = return e buildApp e xs = LApp False e <$> mapM (irTerm top vs env) xs applyToNames :: LExp -> [Name] -> LExp applyToNames tm [] = tm applyToNames tm ns = LApp False tm $ map LV ns padLambdas :: [Int] -> Int -> Int -> ([Name] -> LExp) -> LExp padLambdas used startIdx endSIdx mkTerm = LLam allNames $ mkTerm nonerasedNames where allNames = [sMN i "sat" | i <- [startIdx .. endSIdx-1]] nonerasedNames = [sMN i "sat" | i <- [startIdx .. endSIdx-1], i `elem` used] applyName :: Name -> IState -> [Term] -> Idris LExp applyName n ist args = LApp False (LV n) <$> mapM (irTerm top vs env . erase) (zip [0..] args) where erase (i, x) | i >= arity || i `elem` used = x | otherwise = Erased arity = case fst4 <$> lookupCtxtExact n (definitions . tt_ctxt $ ist) of Just (CaseOp ci ty tys def tot cdefs) -> length tys Just (TyDecl (DCon tag ar _) _) -> ar Just (TyDecl Ref ty) -> length $ getArgTys ty Just (Operator ty ar op) -> ar Just def -> error $ "unknown arity: " ++ show (n, def) Nothing -> 0 -- no definition, probably local name => can't erase anything -- name for purposes of usage info lookup uName | Just n' <- viMethod =<< M.lookup n vs = n' | otherwise = n used = maybe [] (map fst . usedpos) $ lookupCtxtExact uName (idris_callgraph ist) fst4 (x,_,_,_,_,_) = x irTerm top vs env (P _ n _) = return $ LV n irTerm top vs env (V i) | i >= 0 && i < length env = return $ LV (env!!i) | otherwise = ifail $ "bad de bruijn index: " ++ show i irTerm top vs env (Bind n (Lam _ _) sc) = LLam [n'] <$> irTerm top vs (n':env) sc where n' = uniqueName n env irTerm top vs env (Bind n (Let _ _ v) sc) = LLet n <$> irTerm top vs env v <*> irTerm top vs (n : env) sc irTerm top vs env (Bind _ _ _) = return $ LNothing irTerm top vs env (Proj t (-1)) = do t' <- irTerm top vs env t return $ LOp (LMinus (ATInt ITBig)) [t', LConst (BI 1)] irTerm top vs env (Proj t i) = LProj <$> irTerm top vs env t <*> pure i irTerm top vs env (Constant TheWorld) = return LNothing irTerm top vs env (Constant c) = return (LConst c) irTerm top vs env (TType _) = return LNothing irTerm top vs env Erased = return LNothing irTerm top vs env Impossible = return LNothing data LikeNat = LikeZ | LikeS isLikeNat :: IState -> Name -> Maybe LikeNat isLikeNat ist cn -- Nat itself is special-cased in Idris/DataOpts.hs, -- which will have already happened at this point. -- If the optimisation is disabled then nothing looks like Nat. | GeneralisedNatHack `notElem` opt_optimise (idris_options ist) = Nothing | Just cTy <- lookupTyExact cn $ tt_ctxt ist , (P TCon{} tyN _, _) <- unApply $ getRetTy cTy , Just (z, s) <- natLikeCtors tyN = if | cn == z -> Just LikeZ | cn == s -> Just LikeS | otherwise -> error $ "isLikeNat: constructor not found in its own family: " ++ show (cn, tyN) | otherwise = Nothing where natLikeCtors :: Name -> Maybe (Name, Name) natLikeCtors tyN = case lookupCtxtExact tyN $ idris_datatypes ist of Just TI{con_names = [z, s]} | 0 <- getUsedCount z , looksLikeS s -> Just (z, s) Just TI{con_names = [s, z]} | 0 <- getUsedCount z , looksLikeS s -> Just (z, s) _ -> Nothing getUsedCount :: Name -> Int getUsedCount n = maybe 0 (length . usedpos) $ lookupCtxtExact n $ idris_callgraph ist looksLikeS :: Name -> Bool looksLikeS cn | Just [(i, _)] <- fmap usedpos $ lookupCtxtExact cn $ idris_callgraph ist , Just cTy <- lookupTyExact cn $ tt_ctxt ist , [recTy] <- [recTy | (j, (_n, recTy)) <- zip [0..] (getArgTys cTy), j == i] , (P TCon{} recTyN _, _) <- unApply recTy , (P TCon{} tyN _, _) <- unApply $ getRetTy cTy , recTyN == tyN = True | otherwise = False doForeign :: Vars -> [Name] -> [Term] -> Idris LExp doForeign vs env (ret : fname : world : args) = do args' <- mapM splitArg args let fname' = toFDesc fname let ret' = toFDesc ret return $ LForeign ret' fname' args' where splitArg tm | (_, [_,_,l,r]) <- unApply tm -- pair, two implicits = do let l' = toFDesc l r' <- irTerm (sMN 0 "__foreignCall") vs env r return (l', r') splitArg _ = ifail $ "Badly formed foreign function call: " ++ show (ret : fname : world : args) toFDesc (Constant (Str str)) = FStr str toFDesc tm | (P _ n _, []) <- unApply tm = FCon (deNS n) | (P _ n _, as) <- unApply tm = FApp (deNS n) (map toFDesc as) toFDesc _ = FUnknown deNS (NS n _) = n deNS n = n doForeign vs env xs = ifail "Badly formed foreign function call" irTree :: Name -> [Name] -> SC -> Idris LExp irTree top args tree = do logCodeGen 3 $ "Compiling " ++ show args ++ "\n" ++ show tree LLam args <$> irSC top M.empty tree irSC :: Name -> Vars -> SC -> Idris LExp irSC top vs (STerm t) = irTerm top vs [] t irSC top vs (UnmatchedCase str) = return $ LError str irSC top vs (ProjCase tm alts) = do tm' <- irTerm top vs [] tm alts' <- mapM (irAlt top vs tm') alts return $ LCase Shared tm' alts' -- Transform matching on Delay to applications of Force. irSC top vs (Case up n [ConCase (UN delay) i [_, _, n'] sc]) | delay == txt "Delay" = do sc' <- irSC top vs sc -- mkForce n' n sc return $ lsubst n' (LForce (LV n)) sc' -- There are two transformations in this case: -- -- 1. Newtype-case elimination: -- case {e0} of -- wrap({e1}) -> P({e1}) ==> P({e0}) -- -- This is important because newtyped constructors are compiled away entirely -- and we need to do that everywhere. -- -- 2. Unused-case elimination (only valid for singleton branches): -- case {e0} of ==> P -- C(x,y) -> P[... x,y not used ...] -- -- This is important for runtime because sometimes we case on irrelevant data: -- -- In the example above, {e0} will most probably have been erased -- so this vain projection would make the resulting program segfault -- because the code generator still emits a PROJECT(...) G-machine instruction. -- -- Hence, we check whether the variables are used at all -- and erase the casesplit if they are not. -- irSC top vs (Case up n [alt]) = do replacement <- case alt of ConCase cn a ns sc -> do detag <- fgetState (opt_detaggable . ist_optimisation cn) used <- map fst <$> fgetState (cg_usedpos . ist_callgraph cn) if detag && length used == 1 then return . Just $ substSC (ns !! head used) n sc else return Nothing _ -> return Nothing case replacement of Just sc -> irSC top vs sc _ -> do alt' <- irAlt top vs (LV n) alt return $ case namesBoundIn alt' `usedIn` subexpr alt' of [] -> subexpr alt' -- strip the unused top-most case _ -> LCase up (LV n) [alt'] where namesBoundIn :: LAlt -> [Name] namesBoundIn (LConCase cn i ns sc) = ns namesBoundIn (LConstCase c sc) = [] namesBoundIn (LDefaultCase sc) = [] subexpr :: LAlt -> LExp subexpr (LConCase _ _ _ e) = e subexpr (LConstCase _ e) = e subexpr (LDefaultCase e) = e -- FIXME: When we have a non-singleton case-tree of the form -- -- case {e0} of -- C(x) => ... -- ... => ... -- -- and C is detaggable (the only constructor of the family), we can be sure -- that the first branch will be always taken -- so we add special handling -- to remove the dead default branch. -- -- If we don't do so and C is newtype-optimisable, we will miss this newtype -- transformation and the resulting code will probably segfault. -- -- This work-around is not entirely optimal; the best approach would be -- to ensure that such case trees don't arise in the first place. -- irSC top vs (Case up n alts@[ConCase cn a ns sc, DefaultCase sc']) = do detag <- fgetState (opt_detaggable . ist_optimisation cn) if detag then irSC top vs (Case up n [ConCase cn a ns sc]) else do likeNat <- isLikeNat <$> getIState <*> pure cn case likeNat of -- the annoying case: LikeS is translated into a default case -- so we need to change the original DefaultCase to Z-case -- and reorder it before this one Just LikeS -> do zCase <- LConstCase (BI 0) <$> irSC top vs sc' sCase <- irAlt top vs (LV n) (ConCase cn a ns sc) return $ LCase up (LV n) [zCase, sCase] -- the usual case _ -> LCase up (LV n) <$> mapM (irAlt top vs (LV n)) alts irSC top vs sc@(Case up n alts) = getIState >>= rhs where rhs ist | [ConCase cns as nss scs, ConCase cnz az nsz scz] <- alts , Just LikeS <- isLikeNat ist cns = do -- reorder to make the Z case come first zCase <- LConstCase (BI 0) <$> irSC top vs scz sCase <- irAlt top vs (LV n) (ConCase cns as nss scs) return $ LCase up (LV n) [zCase, sCase] | otherwise = do -- check that neither alternative needs the newtype optimisation, -- see comment above goneWrong <- or <$> mapM isDetaggable alts when goneWrong $ ifail ("irSC: non-trivial case-match on detaggable data: " ++ show sc) -- everything okay LCase up (LV n) <$> mapM (irAlt top vs (LV n)) alts isDetaggable (ConCase cn _ _ _) = fgetState $ opt_detaggable . ist_optimisation cn isDetaggable _ = return False irSC top vs ImpossibleCase = return LNothing irAlt :: Name -> Vars -> LExp -> CaseAlt -> Idris LAlt -- this leaves out all unused arguments of the constructor irAlt top vs tm (ConCase n t args sc) = do likeNat <- isLikeNat <$> getIState <*> pure n used <- map fst <$> fgetState (cg_usedpos . ist_callgraph n) let usedArgs = [a | (i,a) <- zip [0..] args, i `elem` used] case likeNat of -- like S Just LikeS -> do sc' <- irSC top vs sc case usedArgs of [pv] -> return $ LDefaultCase $ LLet pv ( LOp (LMinus (ATInt ITBig)) [ tm , LConst (BI 1) ] ) sc' _ -> ifail $ "irAlt/LikeS: multiple used args: " ++ show usedArgs -- like Z Just LikeZ -> LConstCase (BI 0) <$> irSC top vs sc -- not like Nat Nothing -> LConCase (-1) n usedArgs <$> irSC top (methodVars `M.union` vs) sc where methodVars = case n of SN (ImplementationCtorN interfaceName) -> M.fromList [(v, VI { viMethod = Just $ mkFieldName n i }) | (v,i) <- zip args [0..]] _ -> M.empty -- not an implementation constructor irAlt top vs _ (ConstCase x rhs) | matchable x = LConstCase x <$> irSC top vs rhs | matchableTy x = LDefaultCase <$> irSC top vs rhs where matchable (I _) = True matchable (BI _) = True matchable (Ch _) = True matchable (Str _) = True matchable (B8 _) = True matchable (B16 _) = True matchable (B32 _) = True matchable (B64 _) = True matchable _ = False matchableTy (AType (ATInt ITNative)) = True matchableTy (AType (ATInt ITBig)) = True matchableTy (AType (ATInt ITChar)) = True matchableTy StrType = True matchableTy (AType (ATInt (ITFixed IT8))) = True matchableTy (AType (ATInt (ITFixed IT16))) = True matchableTy (AType (ATInt (ITFixed IT32))) = True matchableTy (AType (ATInt (ITFixed IT64))) = True matchableTy _ = False irAlt top vs tm (SucCase n rhs) = do rhs' <- irSC top vs rhs return $ LDefaultCase (LLet n (LOp (LMinus (ATInt ITBig)) [tm, LConst (BI 1)]) rhs') irAlt top vs _ (ConstCase c rhs) = ifail $ "Can't match on (" ++ show c ++ ")" irAlt top vs _ (DefaultCase rhs) = LDefaultCase <$> irSC top vs rhs