content: {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE LambdaCase #-}
  
  -- |
  -- Module      : Jikka.Core.Convert.RemoveUnusedVars
  -- Description : removes unused variables. / 使われていない変数を削除します。
  -- Copyright   : (c) Kimiyuki Onaka, 2020
  -- License     : Apache License 2.0
  -- Maintainer  : kimiyuki95@gmail.com
  -- Stability   : experimental
  -- Portability : portable
  --
  -- `Jikka.Language.Core.RemoveUnusedVars` remove unused variables from exprs.
  module Jikka.Core.Convert.RemoveUnusedVars
    ( run,
      run',
    )
  where
  
  import Jikka.Common.Error
  import Jikka.Core.Language.Expr
  import Jikka.Core.Language.FreeVars (isUnusedVar)
  import Jikka.Core.Language.Lint
  import Jikka.Core.Language.Util
  
  runExpr :: [(VarName, Type)] -> Expr -> Expr
  runExpr _ = mapSubExpr go []
    where
      go _ = \case
        Let x _ _ e2 | x `isUnusedVar` e2 -> e2
        e -> e
  
  -- | TODO: Remove `ToplevelLet` if its variable is not used.
  runToplevelExpr :: [(VarName, Type)] -> ToplevelExpr -> ToplevelExpr
  runToplevelExpr _ = \case
    ToplevelLetRec f args ret body cont ->
      if isUnusedVar f body
        then ToplevelLet f (curryFunTy (map snd args) ret) (curryLam args body) cont
        else ToplevelLetRec f args ret body cont
    e -> e
  
  run' :: Program -> Program
  run' = mapToplevelExprProgram runToplevelExpr . mapExprProgram (mapSubExpr runExpr)
  
  -- | `run` removes unused variables in given programs.
  --
  -- This also removes variables for recursion, i.e. "rec" flags.
  -- `ToplevelLetRec` may becomes `ToplevelLet`.
  --
  -- For example, this converts
  --
  -- > let rec solve x =
  -- >     let y = 0
  -- >     in x
  -- > in solve
  --
  -- to
  --
  -- > let solve x =
  -- >     x
  -- > in solve
  run :: MonadError Error m => Program -> m Program
  run prog = wrapError' "Jikka.Core.Convert.RemoveUnusedVars" $ do
    precondition $ do
      lint prog
    prog <- return $ run' prog
    postcondition $ do
      lint prog
    return prog
  