packages feed

large-records-0.2.0.0: src/Data/Record/Internal/GHC/Fresh.hs

{-# LANGUAGE DerivingStrategies         #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Data.Record.Internal.GHC.Fresh (
    MonadFresh(..)
  , runFreshHsc
  ) where

import Data.IORef
import Control.Monad.Reader

import Data.Record.Internal.GHC.Shim

class Monad m => MonadFresh m where
  -- | Construct a fresh name for use in term level expressions
  --
  -- NOTES:
  --
  -- o These names should be used for module exports.
  -- o These names should be used for exactly /one/ binder.
  -- o The resulting name has the same 'NameSpace' as the argument.
  freshName :: LRdrName -> m LRdrName

newtype Fresh a = WrapFresh { unwrapFresh :: ReaderT (IORef NameCache) IO a }
  deriving newtype (Functor, Applicative, Monad)

instance MonadFresh Fresh where
  freshName (L l name) = WrapFresh $ ReaderT $ \nc_var ->
      atomicModifyIORef nc_var aux
    where
      aux :: NameCache -> (NameCache, LRdrName)
      aux nc = (
            nc { nsUniqs = us }
          , L l $ Exact $
              mkInternalName newUniq (newOccName (rdrNameOcc name)) l
          )
        where
          (newUniq, us) = takeUniqFromSupply (nsUniqs nc)

      -- Even when we generate fresh names, ghc can still complain about name
      -- shadowing, because this check only considers the 'OccName', not the
      -- unique. We therefore prefix the name with an underscore to avoid the
      -- warning.
      newOccName :: OccName -> OccName
      newOccName n = mkOccName (occNameSpace n) . ("_" ++) $ occNameString n

runFresh :: Fresh a -> IORef NameCache -> IO a
runFresh = runReaderT . unwrapFresh

runFreshHsc :: Fresh a -> Hsc a
runFreshHsc fa = do
    env <- getHscEnv
    liftIO $ runFresh fa (hsc_NC env)