packages feed

cryptol-3.6.0: src/Cryptol/ModuleSystem/Renamer/Monad.hs

{-# Language BlockArguments, BangPatterns, ImportQualifiedPost, LambdaCase #-}
{-# Language GeneralisedNewtypeDeriving #-}
module Cryptol.ModuleSystem.Renamer.Monad
  ( 
    -- * Renamer monad
    RenameM
  , runRenamer
  , RenamerInfo(..)

    -- * Modules
  , lookupMod
  , addResolvedMod
  , addInstMod
  , addModAlias
  , addFakeMod
  , isResolvableMod
  , resolveModAlias
  , recordTopImport
  , getExternalDeps
  , Mod(..)

    -- * The current module
  , getCurModPath
  , getCurTopDefs
  , getCurDefNames
  , getCurBinds
  , getCurScope
  , getCurUnqualTypes
  , setThisModuleDefs
  , addModParams
  , addImported

    -- * Scopes
  , inSubmodule
  , inLocalScope
  , inLocalBindScope

  , setCurBind
  , resolveCurBind

    -- * Name generation
  , doDefGroup
  , doDefOrdGroup

    -- * Error reporting
  , recordError
  , addWarning
  , reportUnused
  , quit
  , getCurLoc
  , located
  , withLoc
  , reportUnboundName
  , noWarningsFor

    -- * Dependency tracking
  , recordNameUses
  , getDeps
  ) where

-- import Debug.Trace
-- import Cryptol.Utils.PP

import MonadLib
import Data.Maybe(fromMaybe,maybeToList)
import Data.Set(Set)
import Data.Set qualified as Set
import Data.Map(Map)
import Data.Map qualified as Map
import Data.Text qualified as Text

import Cryptol.Utils.Panic
import Cryptol.Utils.Ident
import Cryptol.Utils.PP(pp,(<+>))
import Cryptol.Parser.Name
import Cryptol.Parser.Position
import Cryptol.Parser.AST
import Cryptol.ModuleSystem.Name
import Cryptol.ModuleSystem.NamingEnv
import Cryptol.ModuleSystem.Interface
import Cryptol.ModuleSystem.Binds
import Cryptol.ModuleSystem.Renamer.Error
import Cryptol.ModuleSystem.Exports
import Cryptol.TypeCheck.Type(ModParamNames)
import Cryptol.TypeCheck.Type qualified as T

newtype RenameM a = R (ReaderT RO (ExceptionT () (StateT RW Lift)) a)
  deriving (Functor,Applicative,Monad)


-- | Information needed to do some renaming.
data RenamerInfo = RenamerInfo
  { renSupply   :: Supply     -- ^ Use to make new names
  , renContext  :: ModPath    -- ^ We are renaming things in here
  , renEnv      :: NamingEnv  -- ^ This is what's in scope
  , renIfaces   :: Map ModName (Either ModParamNames Iface)
    -- ^ External modules. These include normal modules, and functors
    -- (on the Right), as well as interfaces (on the Left)
  }


-- | Do some renaming.
runRenamer ::
  RenamerInfo ->
  RenameM a ->
  (Either [RenamerError] (a,Supply), [RenamerWarning])
runRenamer info (R m) = (res, reverse (renWarnings rwFin))
    where
    (mres, rwFin) = runLift (runStateT rw0 (runExceptionT (runReaderT ro0 m)))
    res =
      case renErrors rwFin of
        [] | Right a <- mres -> Right (a, newNames rwFin)
        es -> Left (reverse es)

    ro0 = RO {
      curBind = Nothing,
      localsEnv = mempty,
      localBindEnv = mempty,
      outEnv = renEnv info,
      outDefs = renEnv info,
      curModPath = renContext info,
      curLoc = emptyRange,
      don'tWarn = mempty,
      loadedIfaces =
        let hasIf x =
              case x of
                Left {} -> Nothing
                Right i -> Just i
        in Map.mapMaybe hasIf (renIfaces info)
    }

    rw0 = RW {
      defEnv = mempty,
      impEnv = mempty,
      modParams = mempty,
      externalDeps = mempty,
      newNames = renSupply info,
      renErrors = [],
      renWarnings = [],
      usedNames = Set.empty,
      knownMods =
        Map.unions [  
          case ent of
            Left ps -> Map.singleton (ImpTop t) (ModKnown (ifaceSigToMod ps))
            Right i -> ifaceToMod (ImpTop t) i
          | (t,ent) <- Map.toList (renIfaces info)
        ]

    }


data RO = RO {
  curModPath :: ModPath,
  -- ^ Current module that we are working on

  curLoc :: Range,
  -- ^ The source location where we are doing something

  loadedIfaces :: Map ModName Iface,
  -- ^ Interfaces for external loaded modules.
  -- We keep this so then if one of the modules is imported, we can
  -- collect its definitions in `externalDeps` to give the typechecker.

  curBind :: Maybe (Located PName, Name),
  -- ^ The current binding we are working on.  During NoPat we do
  -- a transformation like this:
  -- f x y = e   ~>   f = \/*f*/ x -> \/*f*/ y -> e
  -- We use `curBind` to resolve the `f` in the lambdas.  Specifically,
  -- we want to avoid getting confused in a situation like this:
  -- f f y =     ~>   f = \/*f*/ f -> \/*f*/ y -> e  
  -- If we are not careful the (f) in the second lambda could refer to the
  -- argument instead of the original binding.
  -- The /*f*/ is the 'FunDesc' in the 'EFun'

  localBindEnv :: NamingEnv,
  -- ^ Local names that are in scope, for resolving definition names

  localsEnv :: NamingEnv,
  -- ^ Local names that are in scope, for resolving name uses

  outEnv :: NamingEnv,
  -- ^ Things in an enclosing scope (for nested modules).  This is used
  -- for resolving names, and it includes definitions and imports in the
  -- outer scope of a module appropriately shadowed.

  outDefs :: NamingEnv,
  -- ^ Things defined in outer scopes.  This is not used for resolving names,
  -- but to report shadowing warnings.

  don'tWarn :: Set Name
  -- ^ Don't emit warnings for these names
}

data RW = RW {

  knownMods :: ModMap,
  -- ^ Information about previously processed modules

  externalDeps :: IfaceDecls,
  -- ^ Interface declarations for imported external modules.
  -- We track this so we can give it to the type checker.
  
  defEnv :: NamingEnv,
  -- ^ Things defined in the current module

  modParams :: !(Map Ident (Range, NamingEnv)),
  -- ^ Information about the module parameters of the current module

  impEnv :: NamingEnv,
  -- ^ Things imported in the current scope

  newNames :: !Supply,
  -- ^ Used to generate unique names when renaming

  renErrors :: [RenamerError],
  -- ^ Errors we found

  renWarnings :: [RenamerWarning],
  -- ^ Warnings we'd like to emit.

  usedNames :: Set Name
  -- ^ Every time we resove a name use we record it here.
  -- In this way we can determine the dependencies of things.
}


modParamEnv :: Map Ident (Range, NamingEnv) -> NamingEnv
modParamEnv = mconcat . map snd . Map.elems

--------------------------------------------------------------------------------
-- Module Manipulation
--------------------------------------------------------------------------------


-- | Information about a processed module.
data Mod = Mod
  { modKind      :: ModKind               -- ^ What sort of thing are we
  , modDefines   :: Set Name              -- ^ Things defined by this module.
  , modPublic    :: !(Set Name)           -- ^ These are the exported names
  }

-- | A dummy module to use as placeholder for error situations
emptyMod :: ModKind -> Mod
emptyMod k = Mod {
  modKind = k,
  modDefines = mempty,
  modPublic = mempty
}


-- | Lookup a known module
lookupMod :: ImpName Name -> Maybe ModKind -> RenameM Mod
lookupMod = lookupMod' Set.empty

lookupMod' :: Set (ImpName Name) -> ImpName Name -> Maybe ModKind -> RenameM Mod
lookupMod' visited nm mbExpected
  | nm `Set.member` visited =
      do loc <- getCurLoc
         case nm of
           ImpNested x ->
             recordError (ImportTooSoon loc (nameIdent x))
           ImpTop {} -> panic "lookupMod" ["cycle with top-level module"]
         pure (emptyMod (fromMaybe AModule mbExpected))
  | otherwise =
  do
    rw <- R get
    case Map.lookup nm (knownMods rw) of
      Just (ModKnown mo) ->
        case mbExpected of
          Nothing -> pure mo
          Just expected
            | expected == actual -> pure mo
            | otherwise ->
              do
                loc <- getCurLoc
                recordError (ModuleKindMismatch loc nm expected actual)
                pure (emptyMod expected)
              where actual = modKind mo
      Just (ModAlias target) -> lookupMod' (Set.insert nm visited) target mbExpected
      Just ModTodo ->
        do
          loc <- getCurLoc
          case nm of
            ImpNested x ->
              recordError (ImportTooSoon loc (nameIdent x))
            ImpTop {} -> panic "lookupMod" ["ModTodo"]
          pure (emptyMod (fromMaybe AModule mbExpected))
      Just ModFake ->
          pure (emptyMod (fromMaybe AModule mbExpected))

      Nothing ->
        panic "lookupMod" ["Resolved name, but unknown module"]

recordTopImport :: ModName -> RenameM ()
recordTopImport = go Set.empty
  where
  go visited m
    | m `Set.member` visited = pure ()
    | otherwise =
      do
        ro <- R ask
        case Map.lookup m (loadedIfaces ro) of
          Just ifa ->
            do
              R (sets_ \rw ->
                  rw { externalDeps = ifDefines ifa <> externalDeps rw })
              {- The interface may contain submodule aliases whose targets
                 live in a different top-level module.  We need to also
                 bring the decls of those modules into scope so that the
                 type checker can find the types of names accessed through
                 the alias. -}
              let visited' = Set.insert m visited
              forM_ (Map.elems (ifModuleAliases (ifDefines ifa))) \case
                ImpTop t -> go visited' t
                ImpNested n ->
                  case nameTopModuleMaybe n of
                    Just t  -> go visited' t
                    Nothing -> pure ()

          -- This can happen if the module is of the wrong kind (e.g.,
          -- importing an interface as a module). The error is already
          -- reported by lookupMod.
          Nothing -> pure ()

getExternalDeps :: RenameM IfaceDecls
getExternalDeps = R (externalDeps <$> get)

data ModStatus = ModKnown Mod | ModAlias (ImpName Name) | ModFake | ModTodo

type ModMap = Map (ImpName Name) ModStatus

-- | Make a `Mod` from the public declarations in a top-level module's interface.
-- This is used to handle imports.
ifaceToMod :: ImpName Name -> IfaceG name -> ModMap
ifaceToMod nm iface =
  ifaceNamesToMod iface (ifParams iface) nm (ifNames iface)

-- | Like 'ifaceToMod' but checks 'ifIsSignature' to determine the kind.
ifaceToFunctorMod :: ImpName Name -> IfaceG Name -> ModMap
ifaceToFunctorMod nm iface
  | ifIsSignature iface =
      ifaceNamesToMod iface (ifParams iface) nm (ifNames iface)
  | otherwise = ifaceToMod nm iface

-- | Generate a module or functor from the given names.
ifaceNamesToMod ::
    IfaceG topname -> Map Ident T.ModParam -> ImpName Name -> IfaceNames name -> ModMap
ifaceNamesToMod iface params nm names =
  Map.unions (Map.fromList ((nm,ModKnown mo) : sigs ++ aliases) : funs ++ nest)
  where
  sigs =
    [ (ImpNested k, ModKnown (ifaceSigToMod v)) | (k,v) <- Map.toList (ifSignatures decls) ]
  funs =
    [ ifaceToFunctorMod (ImpNested k) v | (k,v) <- Map.toList (ifFunctors decls) ]
  nest =
    [ ifaceNamesToMod iface mempty (ImpNested k) v
    | (k,v) <- Map.toList (ifModules decls) ]
  aliases =
    [ (ImpNested k, ModAlias v) | (k,v) <- Map.toList (ifModuleAliases decls) ]
  mo = Mod
    { modKind    = if ifIsSignature iface then AnIfaceFunctor
                   else if null params then AModule
                   else AFunctor
    , modDefines = Set.fromList namesFromPs `Set.union` defs
    , modPublic  = ifsPublic names
    }
  defs      = ifsDefines names `Set.union` namesFromSigParams
  isLocal x = x `Set.member` ifsDefines names
  decls     = filterIfaceDecls isLocal (ifDefines iface)
  namesFromPs =
    [ pnm
    | mp <- Map.elems params
    , let nms   = T.mpParameters mp
    , pnm <- Map.keys (T.mpnTypes nms) ++
             Map.keys (T.mpnFuns nms) ++
             Map.keys (T.mpnTySyn nms)
    ]
  namesFromSigParams =
    case ifSigOwnParams (ifDefines iface) of
      Nothing  -> Set.empty
      Just nms -> Map.keysSet (pdTypes nms) `Set.union`
                  Map.keysSet (pdFuns nms)

-- | Generate a module corresponding to an interface module.
ifaceSigToMod :: ModParamNames -> Mod
ifaceSigToMod ps = Mod
  { modKind      = ASignature
  , modDefines   = env
  , modPublic    = env
  }
  where
  env = namingEnvNames (modParamNamesNamingEnv ps)

-- | Add a module that was generated when instantiating a functor
addInstMod :: Name -> Mod -> RenameM ()
addInstMod x y =
  R (sets_ \rw -> rw { knownMods = Map.insert (ImpNested x) (ModKnown y) (knownMods rw) })

-- | Add a module alias: the name refers to another module.
addModAlias :: Name -> ImpName Name -> RenameM ()
addModAlias x target =
  R (sets_ \rw -> rw { knownMods = Map.insert (ImpNested x) (ModAlias target) (knownMods rw) })

-- | Register a module name as fake (for error recovery).
addFakeMod :: Name -> RenameM ()
addFakeMod x =
  R (sets_ \rw -> rw { knownMods = Map.insert (ImpNested x) ModFake (knownMods rw) })

-- | Check if a module can be resolved (is defined and not in a cycle).
isResolvableMod :: ImpName Name -> RenameM Bool
isResolvableMod nm =
  do rw <- R get
     pure (go Set.empty (knownMods rw) nm)
  where
  go visited mp x
    | x `Set.member` visited = False
    | otherwise =
      case Map.lookup x mp of
        Just (ModKnown {})  -> True
        Just (ModAlias tgt) -> go (Set.insert x visited) mp tgt
        _                   -> False

-- | Follow alias chains to get the fully-resolved target.
-- Returns the input unchanged if it is not an alias or if there is a cycle.
resolveModAlias :: ImpName Name -> RenameM (ImpName Name)
resolveModAlias nm =
  do rw <- R get
     pure (go Set.empty (knownMods rw) nm)
  where
  go visited mp x
    | x `Set.member` visited = x
    | otherwise =
      case Map.lookup x mp of
        Just (ModAlias tgt) -> go (Set.insert x visited) mp tgt
        _                   -> x

addResolvedMod :: Set Name -> ModuleG Name Name -> RenameM ()
addResolvedMod names mo =
  do
    let nm = ImpNested (thing (mName mo))
    summary <-
      case mDef mo of
        NormalModule ds ->
          pure Mod {
              modKind = if any isParamDecl ds
                            then AFunctor else AModule,
              modDefines = names,
              modPublic = Set.unions (map (`exported` expSpec) allNamespaces)
            }
          where expSpec = exportedDecls ds
            
        FunctorInstance f _ modInst kind ->
          do
            -- Here we don't validate the functor again, to avoid duplicated
            -- error.
            fmo <- withLoc (srcRange f) (lookupMod (thing f) Nothing)
            -- If there was an error, and the thing we are instantiating
            -- is *not* a functor `inst` would be empty.  We just leave
            -- the name as is in this case, which shouldn't matter as we'll
            -- stop after the renamer due to errors.
            let inst = modInstMap modInst
                remap x = Map.findWithDefault x x inst
                -- Virtual submodules created for parameters are not in the
                -- original functor, so they won't be reached by remapping.
                vpNames = Set.fromList
                              [ vpmName s | s <- modInstVirtParamMods modInst ]
                k = case kind of
                      ModuleInst    -> AModule
                      SignatureInst -> ASignature

            pure Mod {
              modKind = k,
              modDefines = names,
              modPublic = Set.map remap (modPublic fmo)
                            `Set.union` vpNames
            }

        InterfaceModule sig ->
          pure Mod {
            modKind = if any isSigIfaceImport (sigImports sig)
                        then AnIfaceFunctor
                        else ASignature,
            modDefines = names,
            modPublic = names
          }
        ModuleAlias {} ->
          panic "addResolvedMod" ["ModuleAlias handled in renameModuleAlias"]
    R (sets_ \rw -> rw { knownMods = Map.insert nm (ModKnown summary) (knownMods rw) })



--------------------------------------------------------------------------------
-- The current module
--------------------------------------------------------------------------------

-- | What module we are currently processing.
getCurModPath :: RenameM ModPath
getCurModPath = R (curModPath <$> ask)

-- | Get just the things defined in the current module
getCurTopDefs :: RenameM NamingEnv
getCurTopDefs = R (defEnv <$> get)

-- | Get names that should be defined in a module.
-- Note that for functors we include names that come from parameters,
-- because when making an instantiation we may generate specialized versions
-- of the original module's name.
getCurDefNames :: RenameM (Set Name)
getCurDefNames =
  do
    rw <- R get
    pure (Set.union (namingEnvNames (defEnv rw)) (namingEnvNames (modParamEnv (modParams rw))))

-- | Get things defined in the current module, and any local bindings in scope.
-- Used for resolving name definitions.
-- Note that this does not include module parameters as these don't have
-- an explicit binding site that needs renaming.
getCurBinds :: RenameM NamingEnv
getCurBinds = R
  do
    ro <- ask
    rw <- get
    pure (localBindEnv ro `shadowing` defEnv rw)


-- | Compute the current scope, for resolving name uses.
getCurScope :: RenameM NamingEnv
getCurScope = R
  do
    ro <- ask
    rw <- get
    pure $
      localsEnv ro `shadowing`
      defEnv    rw `shadowing`
      modParamEnv (modParams rw) `shadowing`
      impEnv    rw `shadowing`
      outEnv    ro

getCurUnqualTypes :: RenameM (Set Ident)
getCurUnqualTypes =
  do
    scope <- getCurScope
    pure (Set.fromList [ i | UnQual i <- Map.keys (namespaceMap NSType scope) ])

-- | Set the definition for the current module.
setThisModuleDefs :: NamingEnv -> RenameM ()
setThisModuleDefs env =
  R (sets_ \rw -> rw { defEnv = env,
                       knownMods = todoMods `Map.union` knownMods rw
                     })
  where
  todoMods =
    Map.fromList 
      [ (ImpNested x,ModTodo)
      | x <- Set.toList (namingEnvNames env), nameNamespace x == NSModule
      ]

-- | Add names from module parameters to the current scope.
-- It is an error if the module parameters conflict with the
-- definitions in a module.
addModParams :: Located Ident -> NamingEnv -> RenameM ()
addModParams nm env =
  do
    errs <- R (sets upd)
    mapM_ recordError errs
    unless (null errs) quit
  where
  upd rw =
    let nms    = modParams rw
        newEnv = env <> modParamEnv nms
        errs   =
          [ MultipleModParams (thing nm) [r,srcRange nm]
          | (r,_) <- maybeToList (Map.lookup (thing nm) nms) ] ++
          map OverlappingSyms (findAmbig newEnv)
    in (errs, rw { modParams = Map.insert (thing nm) (srcRange nm, newEnv) nms })

-- | Add some names that came from an import.
addImported :: Range -> NamingEnv -> RenameM ()
addImported rng env =
  do
    R (sets_ \rw -> rw { impEnv = env <> impEnv rw })
    outDs <- R (outDefs <$> ask)
    forM_ (findShadowing env outDs) \(_,_,xs) ->
      addWarning (SymbolShadowed (ImportShadower rng) xs)

setCurBind :: Located PName -> Name -> RenameM a -> RenameM a
setCurBind p n (R m) = R (mapReader upd m)
  where upd r = r { curBind = Just (p,n) }

resolveCurBind :: Bool -> PName -> RenameM Name
resolveCurBind fromP p =
  do
    mb <- R (curBind <$> ask)
    case mb of
      Just (p',n)
        | thing p' == p -> pure n
        | fromP,
          let i = identText (getIdent (thing p')),
          let j = identText (getIdent p),
          j `Text.isPrefixOf` i -> pure n
        | otherwise ->
          panic "resolveCurBind"
                [ "Unexpected current binding"
                , "Expected: " ++ show (pp (thing p') <+> pp (srcRange p'))
                , "Actual: " ++ show (pp p)
                ]
      Nothing ->
          panic "resolveCurBind" ["No current binding", "Actual: " ++ show (pp p) ]

-- | Set the names of bindings for the duration of a computation.
inLocalBindScope :: Bool -> NamingEnv -> RenameM a -> RenameM a
inLocalBindScope checkUsed env (R m) =
  do
    a <- R (mapReader upd m)
    used <- R (usedNames <$> get)
    let unused = namingEnvNames env `Set.difference` used
    when checkUsed (mapM_ reportUnused unused)
    scope <- getCurScope -- XXX: is this too much, we'll get warning for shadowing imported things too...
    mapM_ reportShadowed (findShadowing env scope)
    pure a
  where
  upd ro = ro {
    localBindEnv = env,
    localsEnv = env `shadowing` localsEnv ro
  }

-- | Do something that will only modify the local scope, and restore
-- it after the computation.  Usually we use `inLocalBindScope`, but
-- we use this for list comprehensions, because the binders in the arms
-- need to be combined when processing the "head" of the comprehension.
inLocalScope :: NamingEnv -> RenameM a -> RenameM a
inLocalScope env (R m) =
  do
    a     <- R (mapReader upd m)
    used <- R (usedNames <$> get)
    let unused = namingEnvNames env `Set.difference` used
    mapM_ reportUnused (Set.toList unused)
    pure a
  where
  upd ro = ro {
    localsEnv = env `shadowing` localsEnv ro
  }

-- | Do some renaming in the context of a nested module.
inSubmodule :: Ident -> RenameM a -> RenameM a
inSubmodule x (R m) = R
  do
    rw <- get
    let defs = defEnv rw
        pars = modParams rw
        imps = impEnv rw
        
    let upd ro =
          let ds = defs `shadowing` modParamEnv pars
          in
            ro {
              curModPath = Nested (curModPath ro) x,
              outEnv     = ds `shadowing` imps `shadowing` outEnv ro,
              outDefs    = ds `shadowing` outDefs ro
            }

    set rw {
      defEnv      = mempty,
      impEnv      = mempty,
      modParams   = mempty
    }
    a <- mapReader upd m
    sets \rw1 -> 
      let bound = Set.unions
                    (map namingEnvNames
                      [ defEnv rw1, impEnv rw1, modParamEnv (modParams rw1) ])
      in
        (a,
        rw1 { defEnv      = defs,
              modParams   = pars,
              impEnv      = imps,
              usedNames   = usedNames rw1 `Set.difference` bound
            } )


--------------------------------------------------------------------------------
-- Error reporting
--------------------------------------------------------------------------------

recordError :: RenamerError -> RenameM ()
recordError e = R (sets_ \rw -> rw { renErrors = e : renErrors rw })

noWarningsFor :: Set Name -> RenameM a -> RenameM a
noWarningsFor xs (R m) = R (mapReader upd m)
  where upd ro = ro { don'tWarn = Set.union xs (don'tWarn ro) }

addWarning :: RenamerWarning -> RenameM ()
addWarning e = R (sets_ \rw -> rw { renWarnings = e : renWarnings rw })

reportUnused :: Name -> RenameM ()
reportUnused n
  | nameSrc n == UserName =
    case Text.uncons (identText (nameIdent n)) of
      Just ('_',_) -> pure ()
      _ ->
        do
          ws <- R (don'tWarn <$> ask)
          unless (n `Set.member` ws) (addWarning (UnusedName n))
  | otherwise = pure ()

reportShadowed :: (PName, Name, [Name]) -> RenameM ()
reportShadowed (x,y,z) = addWarning (SymbolShadowed (DefShadower x y) z)

quit :: RenameM a
quit = R (raise ())

getCurLoc :: RenameM Range
getCurLoc = R (curLoc <$> ask)

-- | Annotate something with the current range.
located :: a -> RenameM (Located a)
located a =
  do loc <- getCurLoc
     return Located { thing = a, srcRange = loc }

withLoc :: HasLoc loc => loc -> RenameM a -> RenameM a
withLoc th (R m) = R
  case getLoc th of
    Nothing -> m
    Just r -> mapReader (\ro -> ro { curLoc = r }) m

-- | Generate an error for a name that we cannot resolve.
-- We try to give a hint, if the name appears in a different name space.
reportUnboundName :: Namespace -> PName -> NamingEnv -> RenameM Name
reportUnboundName expected qn scope =
  do 
    let others     = [ ns | ns <- allNamespaces
                          , ns /= expected
                          , Just _ <- [lookupNS ns qn scope] ]
    nm <- located qn
    case others of
      -- name exists in a different namespace
      actual : _ -> recordError (WrongNamespace expected actual nm)
      -- the value is just missing
      [] -> recordError (UnboundName expected nm)

    -- traceM ("UNDEFINED NAME IN " ++ show (pp expected) ++ ": " ++ show (pp qn) ++ "\n" ++ show (debugHidePreludeNames (pp scope)))

    mkFakeName expected qn


--------------------------------------------------------------------------------
-- Name generation
--------------------------------------------------------------------------------

instance FreshM RenameM where
  liftSupply f =
    R (sets \rw ->
      case f (newNames rw) of
        (a,s1) -> (a, rw1)
          where !rw1 = rw { newNames = s1 })

-- | Make names for a bunch of things defined together.
-- Check that they all have distinct names.
-- We also return the names defined by each entry.
-- This is useful for when we need to rearrange the entries in dependency
-- order.
doDefOrdGroup :: BindsNames a => [a] -> RenameM (NamingEnv,[Set Name])
doDefOrdGroup as =
  do
    envs <- mapM (liftSupply . defsOf) as
    let env = mconcat envs
        errs = findAmbig env
    mapM_ (recordError . OverlappingSyms) errs
    when (not (null errs)) quit
    pure (env, map namingEnvNames envs)


-- | Make names for a bunch of things defined together.
-- Check that they all have distinct names.
doDefGroup :: (Supply -> (NamingEnv, Supply)) -> RenameM NamingEnv
doDefGroup m =
  do
    env <- liftSupply m
    let errs = findAmbig env
    mapM_ (recordError . OverlappingSyms) (findAmbig env)
    when (not (null errs)) quit
    pure env

-- | Assuming an error has been recorded already, construct a fake name that's
-- not expected to make it out of the renamer.
mkFakeName :: Namespace -> PName -> RenameM Name
mkFakeName ns pn =
  do
    loc <- getCurLoc
    nm <-
      liftSupply (mkDeclared ns (TopModule undefinedModName)
                               SystemName (getIdent pn) Nothing loc)
    R (sets_ \rw -> rw { defEnv = singletonNS ns pn nm `shadowing` defEnv rw,
                         knownMods =
                          case ns of
                            NSModule -> Map.insert (ImpNested nm) ModFake (knownMods rw)
                            _ -> knownMods rw })
    pure nm 


--------------------------------------------------------------------------------
-- Dependency Tracking
--------------------------------------------------------------------------------

-- | Collect all names used while running the given computation.
-- Note that the names of the sub-computation are *NOT* added to the dependencies.
getDeps :: RenameM a -> RenameM (a, Set Name)
getDeps (R m) = R
  do
    curUses <- sets \rw -> (usedNames rw, rw { usedNames = Set.empty })
    a <- m
    sets \rw -> ((a,usedNames rw), rw { usedNames = usedNames rw <> curUses })

-- | Add some dependencies for the current thing we are working on.
recordNameUses :: Set Name -> RenameM ()
recordNameUses xs =
  R (sets_ \rw -> rw { usedNames = Set.union xs (usedNames rw) })