ghc-9.14.1: GHC/Types/Name/Cache.hs
{-# LANGUAGE RankNTypes #-}
-- | The Name Cache
module GHC.Types.Name.Cache
( NameCache (..)
, newNameCache
, newNameCacheWith
, initNameCache
, takeUniqFromNameCache
, updateNameCache'
, updateNameCache
-- * OrigNameCache
, OrigNameCache
, lookupOrigNameCache
, extendOrigNameCache'
, extendOrigNameCache
-- * Known-key names
, knownKeysOrigNameCache
, isKnownOrigName_maybe
)
where
import GHC.Prelude
import GHC.Unit.Module
import GHC.Types.Name
import GHC.Types.Unique.Supply
import GHC.Builtin.Types
import GHC.Builtin.Names
import GHC.Builtin.Utils
import GHC.Utils.Outputable
import GHC.Utils.Panic
import Control.Applicative
import Control.Concurrent.MVar
import Control.Monad
{-
Note [The Name Cache]
~~~~~~~~~~~~~~~~~~~~~
The Name Cache makes sure that, during any invocation of GHC, each
External Name "M.x" has one, and only one globally-agreed Unique.
* The first time we come across M.x we make up a Unique and record that
association in the Name Cache.
* When we come across "M.x" again, we look it up in the Name Cache,
and get a hit.
The functions newGlobalBinder, allocateGlobalBinder do the main work.
When you make an External name, you should probably be calling one
of them.
Names in a NameCache are always stored as a Global, and have the SrcLoc of their
binding locations. Actually that's not quite right. When we first encounter
the original name, we might not be at its binding site (e.g. we are reading an
interface file); so we give it 'noSrcLoc' then. Later, when we find its binding
site, we fix it up.
Note [Built-in syntax and the OrigNameCache]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Built-in syntax refers to names that are always in scope and can't be imported
or exported. Such names come in two varieties:
* Simple names (finite): `[]`, `:`, `->`
* Families of names (infinite):
* boxed tuples `()`, `(,)`, `(,,)`, `(,,,)`, ...
* unboxed tuples `(##)`, `(#,#)`, `(#,,#)`, ...
* unboxed sum type syntax `(#|#)`, `(#||#)`, `(#|||#)`, ...
* unboxed sum data syntax `(#_|#)`, `(#|_#)`, `(#_||#), ...
Concretely, a built-in name is a WiredIn Name that has a BuiltInSyntax flag.
Historically, GHC used to avoid putting any built-in syntax in the OrigNameCache
to avoid dealing with infinite families of names (tuples and sums). This measure
has become inadequate with the introduction of NoListTuplePuns (GHC Proposal #475).
Nowadays tuples and sums also use Names that are WiredIn, but are not BuiltInSyntax:
* boxed tuples (tycons): Unit, Solo, Tuple2, Tuple3, Tuple4, ...
* unboxed tuples (tycons): Unit#, Solo#, Tuple2#, Tuple3#, Tuple4#, ...
* constraint tuples (tycons): CUnit, CSolo, CTuple2, CTuple3, CTuple4, ...
* one-tuples (datacons): MkSolo, MkSolo#
We can't put infinitely many names in a finite data structure (OrigNameCache).
So we deal with them in lookupOrigNameCache by means of isInfiniteFamilyOrigName_maybe.
At the same time, simple finite built-in names (`[]`, `:`, `->`) can be put in
the OrigNameCache without any issues (they end up there because they're
knownKeyNames). It doesn't matter that they're built-in syntax.
One might wonder: what's the point of having any built-in syntax in the
OrigNameCache at all? Good question; after all,
1) The parser emits built-in and punned syntax directly as Exact RdrNames
2) Template Haskell conversion (GHC.ThToHs) matches on built-in and punned
syntax directly to immediately produce Exact names (GHC.ThToHs.thRdrName)
3) Loading of interface files encodes names via Uniques, as detailed in
Note [Symbol table representation of names] in GHC.Iface.Binary
It turns out that we end up looking up built-in syntax in the cache when we
generate Haddock documentation. E.g. if we don't find tuple data constructors
there, hyperlinks won't work as expected. Test case: haddockHtmlTest (Bug923.hs)
-}
-- | The NameCache makes sure that there is just one Unique assigned for
-- each original name; i.e. (module-name, occ-name) pair and provides
-- something of a lookup mechanism for those names.
data NameCache = NameCache
{ nsUniqChar :: {-# UNPACK #-} !Char
, nsNames :: {-# UNPACK #-} !(MVar OrigNameCache)
}
-- | Per-module cache of original 'OccName's given 'Name's
type OrigNameCache = ModuleEnv (OccEnv Name)
takeUniqFromNameCache :: NameCache -> IO Unique
takeUniqFromNameCache (NameCache c _) = uniqFromTag c
lookupOrigNameCache :: OrigNameCache -> Module -> OccName -> Maybe Name
lookupOrigNameCache nc mod occ = lookup_infinite <|> lookup_normal
where
-- See Note [Known-key names], 3(c) in GHC.Builtin.Names
-- and Note [Infinite families of known-key names]
lookup_infinite = isInfiniteFamilyOrigName_maybe mod occ
lookup_normal = do
occ_env <- lookupModuleEnv nc mod
lookupOccEnv occ_env occ
extendOrigNameCache' :: OrigNameCache -> Name -> OrigNameCache
extendOrigNameCache' nc name
= assertPpr (isExternalName name) (ppr name) $
extendOrigNameCache nc (nameModule name) (nameOccName name) name
extendOrigNameCache :: OrigNameCache -> Module -> OccName -> Name -> OrigNameCache
extendOrigNameCache nc mod occ name
= extendModuleEnvWith combine nc mod (unitOccEnv occ name)
where
combine _ occ_env = extendOccEnv occ_env occ name
-- | Initialize a new name cache
newNameCache :: IO NameCache
newNameCache = newNameCacheWith 'r' knownKeysOrigNameCache
-- | This is a version of `newNameCache` that lets you supply your
-- own unique tag and set of known key names. This can go wrong if the tag
-- supplied is one reserved by GHC for internal purposes. See #26055 for
-- an example.
--
-- Use `newNameCache` when possible.
newNameCacheWith :: Char -> OrigNameCache -> IO NameCache
newNameCacheWith c nc = NameCache c <$> newMVar nc
-- | This takes a tag for uniques to be generated and the list of knownKeyNames
-- These must be initialized properly to ensure that names generated from this
-- NameCache do not conflict with known key names.
--
-- Use `newNameCache` or `newNameCacheWith` instead
{-# DEPRECATED initNameCache "Use newNameCache or newNameCacheWith instead" #-}
initNameCache :: Char -> [Name] -> IO NameCache
initNameCache c names = newNameCacheWith c (initOrigNames names)
initOrigNames :: [Name] -> OrigNameCache
initOrigNames names = foldl' extendOrigNameCache' emptyModuleEnv names
-- | Update the name cache with the given function
updateNameCache'
:: NameCache
-> (OrigNameCache -> IO (OrigNameCache, c)) -- The updating function
-> IO c
updateNameCache' (NameCache _c nc) upd_fn = modifyMVar' nc upd_fn
-- this should be in `base`
modifyMVar' :: MVar a -> (a -> IO (a,b)) -> IO b
modifyMVar' m f = modifyMVar m $ f >=> \c -> fst c `seq` pure c
-- | Update the name cache with the given function
--
-- Additionally, it ensures that the given Module and OccName are evaluated.
-- If not, chaos can ensue:
-- we read the name-cache
-- then pull on mod (say)
-- which does some stuff that modifies the name cache
-- This did happen, with tycon_mod in GHC.IfaceToCore.tcIfaceAlt (DataAlt..)
updateNameCache
:: NameCache
-> Module
-> OccName
-> (OrigNameCache -> IO (OrigNameCache, c))
-> IO c
updateNameCache name_cache !_mod !_occ upd_fn
= updateNameCache' name_cache upd_fn
{-# NOINLINE knownKeysOrigNameCache #-}
knownKeysOrigNameCache :: OrigNameCache
knownKeysOrigNameCache = initOrigNames knownKeyNames
isKnownOrigName_maybe :: Module -> OccName -> Maybe Name
isKnownOrigName_maybe = lookupOrigNameCache knownKeysOrigNameCache