    {- |
    We add implicit imports are for public nested modules.  This allows
    using definitions from nested modules without having to explicitly import
    them, for example:

    module A where

      submodule B where
        x = 0x20

      y = x     // This works because of the implicit import of `B`

    Restriction:
    ============

    We only add impicit imports of modules that are syntactically visiable
    in the source code.  Consider the following example:

    module A where
      submodule M = F {X}   -- F,X are external modules (e.g., top-level)

    We will add an implicit import for `M`, but *NO* implicit imports for
    any modules imported vial `M` as those are not sytnactically visible
    in the source (i.e., we have to know what `F` refers to).

    This restriction allows us to add implicit imports before doing the
    `Imports` pass.
    -}

    module Cryptol.ModuleSystem.Renamer.ImplicitImports
      ( addImplicitNestedImports
      ) where

    import Data.List(partition)

    import Cryptol.Parser.Position(Range)
    import Cryptol.Utils.Ident(packModName)
    import Cryptol.Parser.AST

    {- | Add additional imports for modules nested withing this one -}
    addImplicitNestedImports :: [TopDecl PName] -> [TopDecl PName]
    addImplicitNestedImports :: [TopDecl PName] -> [TopDecl PName]
    addImplicitNestedImports = ([[Ident]], [TopDecl PName]) -> [TopDecl PName]
    forall a b. (a, b) -> b
    snd (([[Ident]], [TopDecl PName]) -> [TopDecl PName])
    -> ([TopDecl PName] -> ([[Ident]], [TopDecl PName]))
    -> [TopDecl PName]
    -> [TopDecl PName]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [TopDecl PName] -> ([[Ident]], [TopDecl PName])
    addImplicitNestedImports'

    {- | Returns:

      * declarations with additional imports and
      * the public module names of this module and its children.
    -}
    addImplicitNestedImports' ::
      [TopDecl PName] -> ([[Ident]], [TopDecl PName])
    addImplicitNestedImports' :: [TopDecl PName] -> ([[Ident]], [TopDecl PName])
    addImplicitNestedImports' [TopDecl PName]
    decls =
      ([[[Ident]]] -> [[Ident]]
    forall (t :: * -> *) a. Foldable t => t [a] -> [a]
    concat [[[Ident]]]
    exportedMods, [[TopDecl PName]] -> [TopDecl PName]
    forall (t :: * -> *) a. Foldable t => t [a] -> [a]
    concat [[TopDecl PName]]
    newDecls [TopDecl PName] -> [TopDecl PName] -> [TopDecl PName]
    forall a. [a] -> [a] -> [a]
    ++ [TopDecl PName]
    other)
      where
      ([TopDecl PName]
    mods,[TopDecl PName]
    other)            = (TopDecl PName -> Bool)
    -> [TopDecl PName] -> ([TopDecl PName], [TopDecl PName])
    forall a. (a -> Bool) -> [a] -> ([a], [a])
    partition TopDecl PName -> Bool
    forall name. TopDecl name -> Bool
    isNestedMod [TopDecl PName]
    decls
      ([[TopDecl PName]]
    newDecls,[[[Ident]]]
    exportedMods) = [([TopDecl PName], [[Ident]])] -> ([[TopDecl PName]], [[[Ident]]])
    forall a b. [(a, b)] -> ([a], [b])
    unzip ((TopDecl PName -> ([TopDecl PName], [[Ident]]))
    -> [TopDecl PName] -> [([TopDecl PName], [[Ident]])]
    forall a b. (a -> b) -> [a] -> [b]
    map TopDecl PName -> ([TopDecl PName], [[Ident]])
    processModule [TopDecl PName]
    mods)

    processModule :: TopDecl PName -> ([TopDecl PName], [[Ident]])
    processModule :: TopDecl PName -> ([TopDecl PName], [[Ident]])
    processModule ~dcl :: TopDecl PName
    dcl@(DModule TopLevel (NestedModule PName)
    m) =
      let NestedModule ModuleG PName PName
    m1 = TopLevel (NestedModule PName) -> NestedModule PName
    forall a. TopLevel a -> a
    tlValue TopLevel (NestedModule PName)
    m
      in
      case ModuleG PName PName -> ModuleDefinition PName
    forall mname name. ModuleG mname name -> ModuleDefinition name
    mDef ModuleG PName PName
    m1 of
        NormalModule [TopDecl PName]
    ds ->
          let ([[Ident]]
    childExs, [TopDecl PName]
    ds1) = [TopDecl PName] -> ([[Ident]], [TopDecl PName])
    addImplicitNestedImports' [TopDecl PName]
    ds
              mname :: Ident
    mname           = PName -> Ident
    getIdent (Located PName -> PName
    forall a. Located a -> a
    thing (ModuleG PName PName -> Located PName
    forall mname name. ModuleG mname name -> Located mname
    mName ModuleG PName PName
    m1))
              imps :: [[Ident]]
    imps            = ([Ident] -> [Ident]) -> [[Ident]] -> [[Ident]]
    forall a b. (a -> b) -> [a] -> [b]
    map (Ident
    mname Ident -> [Ident] -> [Ident]
    forall a. a -> [a] -> [a]
    :) ([] [Ident] -> [[Ident]] -> [[Ident]]
    forall a. a -> [a] -> [a]
    : [[Ident]]
    childExs) -- this & nested
              loc :: Range
    loc             = Located PName -> Range
    forall a. Located a -> Range
    srcRange (ModuleG PName PName -> Located PName
    forall mname name. ModuleG mname name -> Located mname
    mName ModuleG PName PName
    m1)
          in ( TopLevel (NestedModule PName) -> TopDecl PName
    forall name. TopLevel (NestedModule name) -> TopDecl name
    DModule TopLevel (NestedModule PName)
    m { tlValue :: NestedModule PName
    tlValue = ModuleG PName PName -> NestedModule PName
    forall name. ModuleG name name -> NestedModule name
    NestedModule ModuleG PName PName
    m1 { mDef :: ModuleDefinition PName
    mDef = [TopDecl PName] -> ModuleDefinition PName
    forall name. [TopDecl name] -> ModuleDefinition name
    NormalModule [TopDecl PName]
    ds1 } }
             TopDecl PName -> [TopDecl PName] -> [TopDecl PName]
    forall a. a -> [a] -> [a]
    : ([Ident] -> TopDecl PName) -> [[Ident]] -> [TopDecl PName]
    forall a b. (a -> b) -> [a] -> [b]
    map (Range -> [Ident] -> TopDecl PName
    mkImp Range
    loc) [[Ident]]
    imps
             , case TopLevel (NestedModule PName) -> ExportType
    forall a. TopLevel a -> ExportType
    tlExport TopLevel (NestedModule PName)
    m of
                 ExportType
    Public  -> [[Ident]]
    imps
                 ExportType
    Private -> []
             )

        FunctorInstance {} -> ([TopDecl PName
    dcl], [])
        InterfaceModule {} -> ([TopDecl PName
    dcl], [])

    isNestedMod :: TopDecl name -> Bool
    isNestedMod :: forall name. TopDecl name -> Bool
    isNestedMod TopDecl name
    d =
      case TopDecl name
    d of
        DModule TopLevel (NestedModule name)
    tl -> case TopLevel (NestedModule name) -> NestedModule name
    forall a. TopLevel a -> a
    tlValue TopLevel (NestedModule name)
    tl of
                        NestedModule ModuleG name name
    m -> Bool -> Bool
    not (ModuleG name name -> Bool
    forall mname nmae. ModuleG mname nmae -> Bool
    mIsFunctor ModuleG name name
    m)
        TopDecl name
    _          -> Bool
    False

    -- | Make a name qualifier out of a list of identifiers.
    isToQual :: [Ident] -> ModName
    isToQual :: [Ident] -> ModName
    isToQual [Ident]
    is = [Text] -> ModName
    packModName ((Ident -> Text) -> [Ident] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map Ident -> Text
    identText [Ident]
    is)

    -- | Make a module name out of a list of identifier.
    -- This is the name of the module we are implicitly importing.
    isToName :: [Ident] -> PName
    isToName :: [Ident] -> PName
    isToName [Ident]
    is = case [Ident]
    is of
                    [Ident
    i] -> Ident -> PName
    mkUnqual Ident
    i
                    [Ident]
    _   -> ModName -> Ident -> PName
    mkQual ([Ident] -> ModName
    isToQual ([Ident] -> [Ident]
    forall a. HasCallStack => [a] -> [a]
    init [Ident]
    is)) ([Ident] -> Ident
    forall a. HasCallStack => [a] -> a
    last [Ident]
    is)

    -- | Make an implicit import declaration.
    mkImp :: Range -> [Ident] -> TopDecl PName
    mkImp :: Range -> [Ident] -> TopDecl PName
    mkImp Range
    loc [Ident]
    xs =
      Located (ImportG (ImpName PName)) -> TopDecl PName
    forall name. Located (ImportG (ImpName name)) -> TopDecl name
    DImport
        Located
          { srcRange :: Range
    srcRange = Range
    loc
          , thing :: ImportG (ImpName PName)
    thing    = Import
                         { iModule :: ImpName PName
    iModule = PName -> ImpName PName
    forall name. name -> ImpName name
    ImpNested ([Ident] -> PName
    isToName [Ident]
    xs)
                         , iAs :: Maybe ModName
    iAs     = ModName -> Maybe ModName
    forall a. a -> Maybe a
    Just ([Ident] -> ModName
    isToQual [Ident]
    xs)
                         , iSpec :: Maybe ImportSpec
    iSpec   = Maybe ImportSpec
    forall a. Maybe a
    Nothing
                         , iInst :: Maybe (ModuleInstanceArgs PName)
    iInst   = Maybe (ModuleInstanceArgs PName)
    forall a. Maybe a
    Nothing
                         }
          }