GHC/Types/Var/Env.hs

    {-
    (c) The University of Glasgow 2006
    (c) The GRASP/AQUA Project, Glasgow University, 1992-1998
    -}

    module GHC.Types.Var.Env (
            -- * Var, Id and TyVar environments (maps)
            VarEnv, IdEnv, TyVarEnv, CoVarEnv, TyCoVarEnv,

            -- ** Manipulating these environments
            emptyVarEnv, unitVarEnv, mkVarEnv, mkVarEnv_Directly,
            elemVarEnv, disjointVarEnv,
            extendVarEnv, extendVarEnv_C, extendVarEnv_Acc,
            extendVarEnvList,
            plusVarEnv, plusVarEnv_C, plusVarEnv_CD, plusMaybeVarEnv_C,
            plusVarEnvList, alterVarEnv,
            delVarEnvList, delVarEnv,
            minusVarEnv,
            lookupVarEnv, lookupVarEnv_NF, lookupWithDefaultVarEnv,
            mapVarEnv, zipVarEnv,
            modifyVarEnv, modifyVarEnv_Directly,
            isEmptyVarEnv,
            elemVarEnvByKey,
            filterVarEnv, restrictVarEnv,
            partitionVarEnv,

            -- * Deterministic Var environments (maps)
            DVarEnv, DIdEnv, DTyVarEnv,

            -- ** Manipulating these environments
            emptyDVarEnv, mkDVarEnv,
            dVarEnvElts,
            extendDVarEnv, extendDVarEnv_C,
            extendDVarEnvList,
            lookupDVarEnv, elemDVarEnv,
            isEmptyDVarEnv, foldDVarEnv, nonDetStrictFoldDVarEnv,
            mapDVarEnv, filterDVarEnv,
            modifyDVarEnv,
            alterDVarEnv,
            plusDVarEnv, plusDVarEnv_C,
            unitDVarEnv,
            delDVarEnv,
            delDVarEnvList,
            minusDVarEnv,
            partitionDVarEnv,
            anyDVarEnv,

            -- * The InScopeSet type
            InScopeSet,

            -- ** Operations on InScopeSets
            emptyInScopeSet, mkInScopeSet, delInScopeSet,
            extendInScopeSet, extendInScopeSetList, extendInScopeSetSet,
            getInScopeVars, lookupInScope, lookupInScope_Directly,
            unionInScope, elemInScopeSet, uniqAway,
            varSetInScope,
            unsafeGetFreshLocalUnique,

            -- * The RnEnv2 type
            RnEnv2,

            -- ** Operations on RnEnv2s
            mkRnEnv2, rnBndr2, rnBndrs2, rnBndr2_var,
            rnOccL, rnOccR, inRnEnvL, inRnEnvR, rnOccL_maybe, rnOccR_maybe,
            rnBndrL, rnBndrR, nukeRnEnvL, nukeRnEnvR, rnSwap,
            delBndrL, delBndrR, delBndrsL, delBndrsR,
            addRnInScopeSet,
            rnEtaL, rnEtaR,
            rnInScope, rnInScopeSet, lookupRnInScope,
            rnEnvL, rnEnvR,

            -- * TidyEnv and its operation
            TidyEnv,
            emptyTidyEnv, mkEmptyTidyEnv, delTidyEnvList
        ) where

    import GHC.Prelude
    import qualified Data.IntMap.Strict as IntMap -- TODO: Move this to UniqFM

    import GHC.Types.Name.Occurrence
    import GHC.Types.Name
    import GHC.Types.Var as Var
    import GHC.Types.Var.Set
    import GHC.Types.Unique.Set
    import GHC.Types.Unique.FM
    import GHC.Types.Unique.DFM
    import GHC.Types.Unique
    import GHC.Utils.Misc
    import GHC.Utils.Panic
    import GHC.Data.Maybe
    import GHC.Utils.Outputable

    {-
    ************************************************************************
    *                                                                      *
                    In-scope sets
    *                                                                      *
    ************************************************************************
    -}

    -- | A set of variables that are in scope at some point
    -- "Secrets of the Glasgow Haskell Compiler inliner" Section 3.2 provides
    -- the motivation for this abstraction.
    newtype InScopeSet = InScope VarSet
            -- Note [Lookups in in-scope set]
            -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            -- We store a VarSet here, but we use this for lookups rather than just
            -- membership tests. Typically the InScopeSet contains the canonical
            -- version of the variable (e.g. with an informative unfolding), so this
            -- lookup is useful (see, for instance, Note [In-scope set as a
            -- substitution]).

    instance Outputable InScopeSet where
      ppr (InScope s) =
        text "InScope" <+>
        braces (fsep (map (ppr . Var.varName) (nonDetEltsUniqSet s)))
                          -- It's OK to use nonDetEltsUniqSet here because it's
                          -- only for pretty printing
                          -- In-scope sets get big, and with -dppr-debug
                          -- the output is overwhelming

    emptyInScopeSet :: InScopeSet
    emptyInScopeSet = InScope emptyVarSet

    getInScopeVars ::  InScopeSet -> VarSet
    getInScopeVars (InScope vs) = vs

    mkInScopeSet :: VarSet -> InScopeSet
    mkInScopeSet in_scope = InScope in_scope

    extendInScopeSet :: InScopeSet -> Var -> InScopeSet
    extendInScopeSet (InScope in_scope) v
       = InScope (extendVarSet in_scope v)

    extendInScopeSetList :: InScopeSet -> [Var] -> InScopeSet
    extendInScopeSetList (InScope in_scope) vs
       = InScope $ foldl' extendVarSet in_scope vs

    extendInScopeSetSet :: InScopeSet -> VarSet -> InScopeSet
    extendInScopeSetSet (InScope in_scope) vs
       = InScope (in_scope `unionVarSet` vs)

    delInScopeSet :: InScopeSet -> Var -> InScopeSet
    delInScopeSet (InScope in_scope) v = InScope (in_scope `delVarSet` v)

    elemInScopeSet :: Var -> InScopeSet -> Bool
    elemInScopeSet v (InScope in_scope) = v `elemVarSet` in_scope

    -- | Look up a variable the 'InScopeSet'.  This lets you map from
    -- the variable's identity (unique) to its full value.
    lookupInScope :: InScopeSet -> Var -> Maybe Var
    lookupInScope (InScope in_scope) v  = lookupVarSet in_scope v

    lookupInScope_Directly :: InScopeSet -> Unique -> Maybe Var
    lookupInScope_Directly (InScope in_scope) uniq
      = lookupVarSet_Directly in_scope uniq

    unionInScope :: InScopeSet -> InScopeSet -> InScopeSet
    unionInScope (InScope s1) (InScope s2)
      = InScope (s1 `unionVarSet` s2)

    varSetInScope :: VarSet -> InScopeSet -> Bool
    varSetInScope vars (InScope s1) = vars `subVarSet` s1

    {-
    Note [Local uniques]
    ~~~~~~~~~~~~~~~~~~~~
    Sometimes one must create conjure up a unique which is unique in a particular
    context (but not necessarily globally unique). For instance, one might need to
    create a fresh local identifier which does not shadow any of the locally
    in-scope variables.  For this we purpose we provide 'uniqAway'.

    'uniqAway' is implemented in terms of the 'unsafeGetFreshLocalUnique'
    operation, which generates an unclaimed 'Unique' from an 'InScopeSet'. To
    ensure that we do not conflict with uniques allocated by future allocations
    from 'UniqSupply's, Uniques generated by 'unsafeGetFreshLocalUnique' are
    allocated into a dedicated region of the unique space (namely the X tag).

    Note that one must be quite carefully when using uniques generated in this way
    since they are only locally unique. In particular, two successive calls to
    'uniqAway' on the same 'InScopeSet' will produce the same unique.
     -}

    -- | @uniqAway in_scope v@ finds a unique that is not used in the
    -- in-scope set, and gives that to v. See Note [Local uniques].
    uniqAway :: InScopeSet -> Var -> Var
    -- It starts with v's current unique, of course, in the hope that it won't
    -- have to change, and thereafter uses the successor to the last derived unique
    -- found in the in-scope set.
    uniqAway in_scope var
      | var `elemInScopeSet` in_scope = uniqAway' in_scope var      -- Make a new one
      | otherwise                     = var                         -- Nothing to do

    uniqAway' :: InScopeSet -> Var -> Var
    -- This one *always* makes up a new variable
    uniqAway' in_scope var
      = setVarUnique var (unsafeGetFreshLocalUnique in_scope)

    -- | @unsafeGetFreshUnique in_scope@ finds a unique that is not in-scope in the
    -- given 'InScopeSet'. This must be used very carefully since one can very easily
    -- introduce non-unique 'Unique's this way. See Note [Local uniques].
    unsafeGetFreshLocalUnique :: InScopeSet -> Unique
    unsafeGetFreshLocalUnique (InScope set)
      | Just (uniq,_) <- IntMap.lookupLT (getKey maxLocalUnique) (ufmToIntMap $ getUniqSet set)
      , let uniq' = mkLocalUnique uniq
      , not $ uniq' `ltUnique` minLocalUnique
      = incrUnique uniq'

      | otherwise
      = minLocalUnique

    {-
    ************************************************************************
    *                                                                      *
                    Dual renaming
    *                                                                      *
    ************************************************************************
    -}

    -- | Rename Environment 2
    --
    -- When we are comparing (or matching) types or terms, we are faced with
    -- \"going under\" corresponding binders.  E.g. when comparing:
    --
    -- > \x. e1     ~   \y. e2
    --
    -- Basically we want to rename [@x@ -> @y@] or [@y@ -> @x@], but there are lots of
    -- things we must be careful of.  In particular, @x@ might be free in @e2@, or
    -- y in @e1@.  So the idea is that we come up with a fresh binder that is free
    -- in neither, and rename @x@ and @y@ respectively.  That means we must maintain:
    --
    -- 1. A renaming for the left-hand expression
    --
    -- 2. A renaming for the right-hand expressions
    --
    -- 3. An in-scope set
    --
    -- Furthermore, when matching, we want to be able to have an 'occurs check',
    -- to prevent:
    --
    -- > \x. f   ~   \y. y
    --
    -- matching with [@f@ -> @y@].  So for each expression we want to know that set of
    -- locally-bound variables. That is precisely the domain of the mappings 1.
    -- and 2., but we must ensure that we always extend the mappings as we go in.
    --
    -- All of this information is bundled up in the 'RnEnv2'
    data RnEnv2
      = RV2 { envL     :: VarEnv Var        -- Renaming for Left term
            , envR     :: VarEnv Var        -- Renaming for Right term
            , in_scope :: InScopeSet }      -- In scope in left or right terms

    -- The renamings envL and envR are *guaranteed* to contain a binding
    -- for every variable bound as we go into the term, even if it is not
    -- renamed.  That way we can ask what variables are locally bound
    -- (inRnEnvL, inRnEnvR)

    mkRnEnv2 :: InScopeSet -> RnEnv2
    mkRnEnv2 vars = RV2     { envL     = emptyVarEnv
                            , envR     = emptyVarEnv
                            , in_scope = vars }

    addRnInScopeSet :: RnEnv2 -> VarSet -> RnEnv2
    addRnInScopeSet env vs
      | isEmptyVarSet vs = env
      | otherwise        = env { in_scope = extendInScopeSetSet (in_scope env) vs }

    rnInScope :: Var -> RnEnv2 -> Bool
    rnInScope x env = x `elemInScopeSet` in_scope env

    rnInScopeSet :: RnEnv2 -> InScopeSet
    rnInScopeSet = in_scope

    -- | Retrieve the left mapping
    rnEnvL :: RnEnv2 -> VarEnv Var
    rnEnvL = envL

    -- | Retrieve the right mapping
    rnEnvR :: RnEnv2 -> VarEnv Var
    rnEnvR = envR

    rnBndrs2 :: RnEnv2 -> [Var] -> [Var] -> RnEnv2
    -- ^ Applies 'rnBndr2' to several variables: the two variable lists must be of equal length
    rnBndrs2 env bsL bsR = foldl2 rnBndr2 env bsL bsR

    rnBndr2 :: RnEnv2 -> Var -> Var -> RnEnv2
    -- ^ @rnBndr2 env bL bR@ goes under a binder @bL@ in the Left term,
    --                       and binder @bR@ in the Right term.
    -- It finds a new binder, @new_b@,
    -- and returns an environment mapping @bL -> new_b@ and @bR -> new_b@
    rnBndr2 env bL bR = fst $ rnBndr2_var env bL bR

    rnBndr2_var :: RnEnv2 -> Var -> Var -> (RnEnv2, Var)
    -- ^ Similar to 'rnBndr2' but returns the new variable as well as the
    -- new environment
    rnBndr2_var (RV2 { envL = envL, envR = envR, in_scope = in_scope }) bL bR
      = (RV2 { envL            = extendVarEnv envL bL new_b   -- See Note
             , envR            = extendVarEnv envR bR new_b   -- [Rebinding]
             , in_scope = extendInScopeSet in_scope new_b }, new_b)
      where
            -- Find a new binder not in scope in either term
        new_b | not (bL `elemInScopeSet` in_scope) = bL
              | not (bR `elemInScopeSet` in_scope) = bR
              | otherwise                          = uniqAway' in_scope bL

            -- Note [Rebinding]
            -- If the new var is the same as the old one, note that
            -- the extendVarEnv *deletes* any current renaming
            -- E.g.   (\x. \x. ...)  ~  (\y. \z. ...)
            --
            --   Inside \x  \y      { [x->y], [y->y],       {y} }
            --       \x  \z         { [x->x], [y->y, z->x], {y,x} }

    rnBndrL :: RnEnv2 -> Var -> (RnEnv2, Var)
    -- ^ Similar to 'rnBndr2' but used when there's a binder on the left
    -- side only.
    rnBndrL (RV2 { envL = envL, envR = envR, in_scope = in_scope }) bL
      = (RV2 { envL     = extendVarEnv envL bL new_b
             , envR     = envR
             , in_scope = extendInScopeSet in_scope new_b }, new_b)
      where
        new_b = uniqAway in_scope bL

    rnBndrR :: RnEnv2 -> Var -> (RnEnv2, Var)
    -- ^ Similar to 'rnBndr2' but used when there's a binder on the right
    -- side only.
    rnBndrR (RV2 { envL = envL, envR = envR, in_scope = in_scope }) bR
      = (RV2 { envR     = extendVarEnv envR bR new_b
             , envL     = envL
             , in_scope = extendInScopeSet in_scope new_b }, new_b)
      where
        new_b = uniqAway in_scope bR

    rnEtaL :: RnEnv2 -> Var -> (RnEnv2, Var)
    -- ^ Similar to 'rnBndrL' but used for eta expansion
    -- See Note [Eta expansion]
    rnEtaL (RV2 { envL = envL, envR = envR, in_scope = in_scope }) bL
      = (RV2 { envL     = extendVarEnv envL bL new_b
             , envR     = extendVarEnv envR new_b new_b     -- Note [Eta expansion]
             , in_scope = extendInScopeSet in_scope new_b }, new_b)
      where
        new_b = uniqAway in_scope bL

    rnEtaR :: RnEnv2 -> Var -> (RnEnv2, Var)
    -- ^ Similar to 'rnBndr2' but used for eta expansion
    -- See Note [Eta expansion]
    rnEtaR (RV2 { envL = envL, envR = envR, in_scope = in_scope }) bR
      = (RV2 { envL     = extendVarEnv envL new_b new_b     -- Note [Eta expansion]
             , envR     = extendVarEnv envR bR new_b
             , in_scope = extendInScopeSet in_scope new_b }, new_b)
      where
        new_b = uniqAway in_scope bR

    delBndrL, delBndrR :: RnEnv2 -> Var -> RnEnv2
    delBndrL rn@(RV2 { envL = env, in_scope = in_scope }) v
      = rn { envL = env `delVarEnv` v, in_scope = in_scope `extendInScopeSet` v }
    delBndrR rn@(RV2 { envR = env, in_scope = in_scope }) v
      = rn { envR = env `delVarEnv` v, in_scope = in_scope `extendInScopeSet` v }

    delBndrsL, delBndrsR :: RnEnv2 -> [Var] -> RnEnv2
    delBndrsL rn@(RV2 { envL = env, in_scope = in_scope }) v
      = rn { envL = env `delVarEnvList` v, in_scope = in_scope `extendInScopeSetList` v }
    delBndrsR rn@(RV2 { envR = env, in_scope = in_scope }) v
      = rn { envR = env `delVarEnvList` v, in_scope = in_scope `extendInScopeSetList` v }

    rnOccL, rnOccR :: RnEnv2 -> Var -> Var
    -- ^ Look up the renaming of an occurrence in the left or right term
    rnOccL (RV2 { envL = env }) v = lookupVarEnv env v `orElse` v
    rnOccR (RV2 { envR = env }) v = lookupVarEnv env v `orElse` v

    rnOccL_maybe, rnOccR_maybe :: RnEnv2 -> Var -> Maybe Var
    -- ^ Look up the renaming of an occurrence in the left or right term
    rnOccL_maybe (RV2 { envL = env }) v = lookupVarEnv env v
    rnOccR_maybe (RV2 { envR = env }) v = lookupVarEnv env v

    inRnEnvL, inRnEnvR :: RnEnv2 -> Var -> Bool
    -- ^ Tells whether a variable is locally bound
    inRnEnvL (RV2 { envL = env }) v = v `elemVarEnv` env
    inRnEnvR (RV2 { envR = env }) v = v `elemVarEnv` env

    lookupRnInScope :: RnEnv2 -> Var -> Var
    lookupRnInScope env v = lookupInScope (in_scope env) v `orElse` v

    nukeRnEnvL, nukeRnEnvR :: RnEnv2 -> RnEnv2
    -- ^ Wipe the left or right side renaming
    nukeRnEnvL env = env { envL = emptyVarEnv }
    nukeRnEnvR env = env { envR = emptyVarEnv }

    rnSwap :: RnEnv2 -> RnEnv2
    -- ^ swap the meaning of left and right
    rnSwap (RV2 { envL = envL, envR = envR, in_scope = in_scope })
      = RV2 { envL = envR, envR = envL, in_scope = in_scope }

    {-
    Note [Eta expansion]
    ~~~~~~~~~~~~~~~~~~~~
    When matching
         (\x.M) ~ N
    we rename x to x' with, where x' is not in scope in
    either term.  Then we want to behave as if we'd seen
         (\x'.M) ~ (\x'.N x')
    Since x' isn't in scope in N, the form (\x'. N x') doesn't
    capture any variables in N.  But we must nevertheless extend
    the envR with a binding [x' -> x'], to support the occurs check.
    For example, if we don't do this, we can get silly matches like
            forall a.  (\y.a)  ~   v
    succeeding with [a -> v y], which is bogus of course.

    ************************************************************************
    *                                                                      *
                    Tidying
    *                                                                      *
    ************************************************************************
    -}

    -- | Tidy Environment
    --
    -- When tidying up print names, we keep a mapping of in-scope occ-names
    -- (the 'TidyOccEnv') and a Var-to-Var of the current renamings
    type TidyEnv = (TidyOccEnv, VarEnv Var)

    emptyTidyEnv :: TidyEnv
    emptyTidyEnv = (emptyTidyOccEnv, emptyVarEnv)

    mkEmptyTidyEnv :: TidyOccEnv -> TidyEnv
    mkEmptyTidyEnv occ_env = (occ_env, emptyVarEnv)

    delTidyEnvList :: TidyEnv -> [Var] -> TidyEnv
    delTidyEnvList (occ_env, var_env) vs = (occ_env', var_env')
      where
        occ_env' = occ_env `delTidyOccEnvList` map (occNameFS . getOccName) vs
        var_env' = var_env `delVarEnvList` vs

    {-
    ************************************************************************
    *                                                                      *
    \subsection{@VarEnv@s}
    *                                                                      *
    ************************************************************************
    -}

    -- We would like this to be `UniqFM Var elt`
    -- but the code uses various key types.
    -- So for now make it explicitly untyped

    -- | Variable Environment
    type VarEnv elt     = UniqFM Var elt

    -- | Identifier Environment
    type IdEnv elt      = UniqFM Id elt

    -- | Type Variable Environment
    type TyVarEnv elt   = UniqFM Var elt

    -- | Type or Coercion Variable Environment
    type TyCoVarEnv elt = UniqFM TyCoVar elt

    -- | Coercion Variable Environment
    type CoVarEnv elt   = UniqFM CoVar elt

    emptyVarEnv       :: VarEnv a
    mkVarEnv          :: [(Var, a)] -> VarEnv a
    mkVarEnv_Directly :: [(Unique, a)] -> VarEnv a
    zipVarEnv         :: [Var] -> [a] -> VarEnv a
    unitVarEnv        :: Var -> a -> VarEnv a
    alterVarEnv       :: (Maybe a -> Maybe a) -> VarEnv a -> Var -> VarEnv a
    extendVarEnv      :: VarEnv a -> Var -> a -> VarEnv a
    extendVarEnv_C    :: (a->a->a) -> VarEnv a -> Var -> a -> VarEnv a
    extendVarEnv_Acc  :: (a->b->b) -> (a->b) -> VarEnv b -> Var -> a -> VarEnv b
    plusVarEnv        :: VarEnv a -> VarEnv a -> VarEnv a
    plusVarEnvList    :: [VarEnv a] -> VarEnv a
    extendVarEnvList  :: VarEnv a -> [(Var, a)] -> VarEnv a

    partitionVarEnv   :: (a -> Bool) -> VarEnv a -> (VarEnv a, VarEnv a)
    restrictVarEnv    :: VarEnv a -> VarSet -> VarEnv a
    delVarEnvList     :: VarEnv a -> [Var] -> VarEnv a
    delVarEnv         :: VarEnv a -> Var -> VarEnv a
    minusVarEnv       :: VarEnv a -> VarEnv b -> VarEnv a
    plusVarEnv_C      :: (a -> a -> a) -> VarEnv a -> VarEnv a -> VarEnv a
    plusVarEnv_CD     :: (a -> a -> a) -> VarEnv a -> a -> VarEnv a -> a -> VarEnv a
    plusMaybeVarEnv_C :: (a -> a -> Maybe a) -> VarEnv a -> VarEnv a -> VarEnv a
    mapVarEnv         :: (a -> b) -> VarEnv a -> VarEnv b
    modifyVarEnv      :: (a -> a) -> VarEnv a -> Var -> VarEnv a

    isEmptyVarEnv     :: VarEnv a -> Bool
    lookupVarEnv      :: VarEnv a -> Var -> Maybe a
    filterVarEnv      :: (a -> Bool) -> VarEnv a -> VarEnv a
    lookupVarEnv_NF   :: VarEnv a -> Var -> a
    lookupWithDefaultVarEnv :: VarEnv a -> a -> Var -> a
    elemVarEnv        :: Var -> VarEnv a -> Bool
    elemVarEnvByKey   :: Unique -> VarEnv a -> Bool
    disjointVarEnv    :: VarEnv a -> VarEnv a -> Bool

    elemVarEnv       = elemUFM
    elemVarEnvByKey  = elemUFM_Directly
    disjointVarEnv   = disjointUFM
    alterVarEnv      = alterUFM
    extendVarEnv     = addToUFM
    extendVarEnv_C   = addToUFM_C
    extendVarEnv_Acc = addToUFM_Acc
    extendVarEnvList = addListToUFM
    plusVarEnv_C     = plusUFM_C
    plusVarEnv_CD    = plusUFM_CD
    plusMaybeVarEnv_C = plusMaybeUFM_C
    delVarEnvList    = delListFromUFM
    delVarEnv        = delFromUFM
    minusVarEnv      = minusUFM
    plusVarEnv       = plusUFM
    plusVarEnvList   = plusUFMList
    lookupVarEnv     = lookupUFM
    filterVarEnv     = filterUFM
    lookupWithDefaultVarEnv = lookupWithDefaultUFM
    mapVarEnv        = mapUFM
    mkVarEnv         = listToUFM
    mkVarEnv_Directly= listToUFM_Directly
    emptyVarEnv      = emptyUFM
    unitVarEnv       = unitUFM
    isEmptyVarEnv    = isNullUFM
    partitionVarEnv       = partitionUFM

    restrictVarEnv env vs = filterUFM_Directly keep env
      where
        keep u _ = u `elemVarSetByKey` vs

    zipVarEnv tyvars tys   = mkVarEnv (zipEqual "zipVarEnv" tyvars tys)
    lookupVarEnv_NF env id = case lookupVarEnv env id of
                             Just xx -> xx
                             Nothing -> panic "lookupVarEnv_NF: Nothing"

    {-
    @modifyVarEnv@: Look up a thing in the VarEnv,
    then mash it with the modify function, and put it back.
    -}

    modifyVarEnv mangle_fn env key
      = case (lookupVarEnv env key) of
          Nothing -> env
          Just xx -> extendVarEnv env key (mangle_fn xx)

    modifyVarEnv_Directly :: (a -> a) -> UniqFM key a -> Unique -> UniqFM key a
    modifyVarEnv_Directly mangle_fn env key
      = case (lookupUFM_Directly env key) of
          Nothing -> env
          Just xx -> addToUFM_Directly env key (mangle_fn xx)

    -- Deterministic VarEnv
    -- See Note [Deterministic UniqFM] in GHC.Types.Unique.DFM for explanation why we need
    -- DVarEnv.

    -- | Deterministic Variable Environment
    type DVarEnv elt = UniqDFM Var elt

    -- | Deterministic Identifier Environment
    -- Sadly not always indexed by Id, but it is in the common case.
    type DIdEnv elt = UniqDFM Var elt

    -- | Deterministic Type Variable Environment
    type DTyVarEnv elt = UniqDFM TyVar elt

    emptyDVarEnv :: DVarEnv a
    emptyDVarEnv = emptyUDFM

    dVarEnvElts :: DVarEnv a -> [a]
    dVarEnvElts = eltsUDFM

    mkDVarEnv :: [(Var, a)] -> DVarEnv a
    mkDVarEnv = listToUDFM

    extendDVarEnv :: DVarEnv a -> Var -> a -> DVarEnv a
    extendDVarEnv = addToUDFM

    minusDVarEnv :: DVarEnv a -> DVarEnv a' -> DVarEnv a
    minusDVarEnv = minusUDFM

    lookupDVarEnv :: DVarEnv a -> Var -> Maybe a
    lookupDVarEnv = lookupUDFM

    foldDVarEnv :: (a -> b -> b) -> b -> DVarEnv a -> b
    foldDVarEnv = foldUDFM

    -- See Note [Deterministic UniqFM] to learn about nondeterminism.
    -- If you use this please provide a justification why it doesn't introduce
    -- nondeterminism.
    nonDetStrictFoldDVarEnv :: (a -> b -> b) -> b -> DVarEnv a -> b
    nonDetStrictFoldDVarEnv = nonDetStrictFoldUDFM

    mapDVarEnv :: (a -> b) -> DVarEnv a -> DVarEnv b
    mapDVarEnv = mapUDFM

    filterDVarEnv      :: (a -> Bool) -> DVarEnv a -> DVarEnv a
    filterDVarEnv = filterUDFM

    alterDVarEnv :: (Maybe a -> Maybe a) -> DVarEnv a -> Var -> DVarEnv a
    alterDVarEnv = alterUDFM

    plusDVarEnv :: DVarEnv a -> DVarEnv a -> DVarEnv a
    plusDVarEnv = plusUDFM

    plusDVarEnv_C :: (a -> a -> a) -> DVarEnv a -> DVarEnv a -> DVarEnv a
    plusDVarEnv_C = plusUDFM_C

    unitDVarEnv :: Var -> a -> DVarEnv a
    unitDVarEnv = unitUDFM

    delDVarEnv :: DVarEnv a -> Var -> DVarEnv a
    delDVarEnv = delFromUDFM

    delDVarEnvList :: DVarEnv a -> [Var] -> DVarEnv a
    delDVarEnvList = delListFromUDFM

    isEmptyDVarEnv :: DVarEnv a -> Bool
    isEmptyDVarEnv = isNullUDFM

    elemDVarEnv :: Var -> DVarEnv a -> Bool
    elemDVarEnv = elemUDFM

    extendDVarEnv_C :: (a -> a -> a) -> DVarEnv a -> Var -> a -> DVarEnv a
    extendDVarEnv_C = addToUDFM_C

    modifyDVarEnv :: (a -> a) -> DVarEnv a -> Var -> DVarEnv a
    modifyDVarEnv mangle_fn env key
      = case (lookupDVarEnv env key) of
          Nothing -> env
          Just xx -> extendDVarEnv env key (mangle_fn xx)

    partitionDVarEnv :: (a -> Bool) -> DVarEnv a -> (DVarEnv a, DVarEnv a)
    partitionDVarEnv = partitionUDFM

    extendDVarEnvList :: DVarEnv a -> [(Var, a)] -> DVarEnv a
    extendDVarEnvList = addListToUDFM

    anyDVarEnv :: (a -> Bool) -> DVarEnv a -> Bool
    anyDVarEnv = anyUDFM