packages feed

cryptol-3.6.0: src/Cryptol/ModuleSystem/Binds.hs

{-# Language BlockArguments #-}
{-# Language RecordWildCards #-}
{-# Language FlexibleInstances #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE PatternSynonyms #-}
module Cryptol.ModuleSystem.Binds
  ( BindsNames
  , ModKind(..)
  , modBuilder
  , newModParam
  , newFunctorInst
  , InModule(..)
  , defsOf
  , defsOfSig
  , defsOfPats
  ) where

import Data.Set(Set)
import qualified Data.Set as Set
import Data.Maybe(fromMaybe)
import Control.Monad(forM)
import qualified MonadLib as M

import Cryptol.Utils.Panic (panic)
import Cryptol.Utils.RecordMap(displayElements)
import Cryptol.Parser.Position
import Cryptol.Parser.Name(isSystemName, pattern UnQual)
import Cryptol.Parser.AST
import Cryptol.ModuleSystem.Renamer.Error
import Cryptol.ModuleSystem.Name
import Cryptol.ModuleSystem.NamingEnv


type ModBuilder = SupplyT (M.StateT [RenamerError] M.Id)

modBuilder :: ModBuilder a -> Supply -> ((a, [RenamerError]),Supply)
modBuilder m s = ((a,errs),s1)
  where ((a,s1),errs) = M.runId (M.runStateT [] (runSupplyT s m))

-- | These are the names "owned" by the signature.  These names are
-- used when resolving the signature.  They are also used to figure out what
-- names to instantiate when the signature is used.
signatureDefs :: ModPath -> Signature PName -> BuildNamingEnv
signatureDefs m sig =
     mconcat [ namingEnv (InModule loc p) | p <- sigTypeParams sig ]
  <> mconcat [ namingEnv (InModule loc p) | p <- sigFunParams sig ]
  <> mconcat [ namingEnv (InModule loc p) | p <- sigDecls sig ]
  where
  loc = Just m

defsOfSig :: ModPath -> Signature PName -> Supply -> (NamingEnv,Supply)
defsOfSig m sig = buildNamingEnv (signatureDefs m sig)
--------------------------------------------------------------------------------




--------------------------------------------------------------------------------
-- Computes the names introduced by various declarations.

-- | Things that define exported names.
class BindsNames a where
  namingEnv :: a -> BuildNamingEnv

newtype BuildNamingEnv = BuildNamingEnv { runBuild :: SupplyT M.Id NamingEnv }

buildNamingEnv :: BuildNamingEnv -> Supply -> (NamingEnv,Supply)
buildNamingEnv b supply = M.runId $ runSupplyT supply $ runBuild b

-- | Generate a 'NamingEnv' using an explicit supply.
defsOf :: BindsNames a => a -> Supply -> (NamingEnv,Supply)
defsOf = buildNamingEnv . namingEnv

instance Semigroup BuildNamingEnv where
  BuildNamingEnv a <> BuildNamingEnv b = BuildNamingEnv $
    do x <- a
       y <- b
       return (mappend x y)

instance Monoid BuildNamingEnv where
  mempty = BuildNamingEnv (pure mempty)

  mappend = (<>)

  mconcat bs = BuildNamingEnv $
    do ns <- sequence (map runBuild bs)
       return (mconcat ns)

instance BindsNames NamingEnv where
  namingEnv env = BuildNamingEnv (return env)
  {-# INLINE namingEnv #-}

instance BindsNames a => BindsNames (Maybe a) where
  namingEnv = foldMap namingEnv
  {-# INLINE namingEnv #-}

instance BindsNames a => BindsNames [a] where
  namingEnv = foldMap namingEnv
  {-# INLINE namingEnv #-}

-- | Generate a type renaming environment from the parameters that are bound by
-- this schema.
instance BindsNames (Schema PName) where
  namingEnv (Forall ps _ _ _) = foldMap namingEnv ps
  {-# INLINE namingEnv #-}



-- | Introduce the name
instance BindsNames (InModule (Bind PName)) where
  namingEnv (InModule mb b) = BuildNamingEnv $
    do let Located { .. } = bName b
       n <- case mb of
              Just m  -> newTop NSValue m thing (bFixity b) srcRange
              Nothing -> newLocal NSValue thing srcRange -- local fixitiies?

       return (singletonNS NSValue thing n)

-- | Generate the naming environment for a type parameter.
instance BindsNames (TParam PName) where
  namingEnv TParam { .. } = BuildNamingEnv $
    do let range = fromMaybe emptyRange tpRange
       n <- newLocal NSType tpName range
       return (singletonNS NSType tpName n)


instance BindsNames (InModule (TopDecl PName)) where
  namingEnv (InModule ns td) =
    case td of
      Decl d           -> namingEnv (InModule ns (tlValue d))
      DPrimType d      -> namingEnv (InModule ns (tlValue d))
      TDNewtype d      -> namingEnv (InModule ns (tlValue d))
      TDEnum d         -> namingEnv (InModule ns (tlValue d))
      DParamDecl {}    -> mempty -- shouldn't happen
      Include {}       -> mempty
      DImport {}       -> mempty -- Handled in renamer
      DModule m        -> namingEnv (InModule ns (tlValue m))
      DModParam {}     -> mempty -- Handled in renamer
      DInterfaceConstraint {} -> mempty
      

instance BindsNames (InModule (NestedModule PName)) where
  namingEnv (InModule mb (NestedModule mdef)) =
    case mb of
      Just m -> BuildNamingEnv $
        do
          let pnmame = mName mdef
          nm   <- newTop NSModule m (thing pnmame) Nothing (srcRange pnmame)
          pure (singletonNS NSModule (thing pnmame) nm)
      Nothing -> panic "BindsNames (InModule (NestedModule PName))" ["Nothing"]

instance BindsNames (InModule (PrimType PName)) where
  namingEnv (InModule mb PrimType { .. }) =
    case mb of
      Just m ->
        BuildNamingEnv $
          do let Located { .. } = primTName
             nm <- newTop NSType m thing primTFixity srcRange
             pure (singletonNS NSType thing nm)
      Nothing -> panic "BindsNames (InModule (PrimType PName))" ["Nothing"]

instance BindsNames (InModule (ParameterFun PName)) where
  namingEnv (InModule mb ParameterFun { .. }) =
    case mb of
      Just ns -> BuildNamingEnv $
        do
          let Located { .. } = pfName
          ntName <- newTop NSValue ns thing pfFixity srcRange
          return (singletonNS NSValue thing ntName)
      Nothing -> panic "BindsNames (InModule (ParameterFun PName))" ["Nothing"]

instance BindsNames (InModule (ParameterType PName)) where
  namingEnv (InModule mb ParameterType { .. }) =
    case mb of
      Just ns -> BuildNamingEnv $
        -- XXX: we don't seem to have a fixity environment at the type level
        do
          let Located { .. } = ptName
          ntName <- newTop NSType ns thing Nothing srcRange
          return (singletonNS NSType thing ntName)
      Nothing -> panic "BindsNames (InModule (ParameterType PName))" ["Nothing"]

instance BindsNames (InModule (Newtype PName)) where
  namingEnv (InModule mb Newtype { .. }) =
    case mb of
      Just ns -> BuildNamingEnv $
        do let Located { .. } = nName
           ntName    <- newTop NSType  ns thing Nothing srcRange
           ntConName <- newTop NSConstructor ns thing Nothing srcRange
           return (singletonNS NSType thing ntName `mappend`
                   singletonNS NSConstructor thing ntConName)
      Nothing -> panic "BindsNames (InModule (Newtype PName))" ["Nothing"]

instance BindsNames (InModule (EnumDecl PName)) where
  namingEnv (InModule (Just ns) EnumDecl { .. }) = BuildNamingEnv $
    do enName   <- newTop NSType ns (thing eName) Nothing (srcRange eName)
       conNames <- forM eCons \topc ->
                      do let c     = ecName (tlValue topc)
                             pname = thing c
                         cName <- newTop NSConstructor ns pname Nothing
                                                                  (srcRange c)
                         pure (singletonNS NSConstructor pname cName)
       pure (mconcat (singletonNS NSType (thing eName) enName : conNames))
  namingEnv _ = panic "namingEnv" ["Unreachable"]

-- | The naming environment for a single declaration.
instance BindsNames (InModule (Decl PName)) where
  namingEnv (InModule pfx d) = case d of
    DBind b                 -> namingEnv (InModule pfx b)
    DSignature ns _sig      -> foldMap qualBind ns
    DPragma ns _p           -> foldMap qualBind ns
    DType syn               -> qualType (tsName syn) (tsFixity syn)
    DProp syn               -> qualType (psName syn) (psFixity syn)
    DLocated d' _           -> namingEnv (InModule pfx d')
    DRec {}                 -> panic "namingEnv" [ "DRec" ]
    DPatBind _pat _e        -> panic "namingEnv" ["Unexpected pattern binding"]
    DFixity{}               -> panic "namingEnv" ["Unexpected fixity declaration"]

    where

    mkName ns ln fx = case pfx of
                        Just m  -> newTop ns m (thing ln) fx (srcRange ln)
                        Nothing -> newLocal ns (thing ln) (srcRange ln)

    qualBind ln = BuildNamingEnv $
      do n <- mkName NSValue ln Nothing
         return (singletonNS NSValue (thing ln) n)

    qualType ln f = BuildNamingEnv $
      do n <- mkName NSType ln f
         return (singletonNS NSType (thing ln) n)

instance BindsNames (InModule (SigDecl PName)) where
  namingEnv (InModule m d) =
    case d of
      SigTySyn ts _    -> namingEnv (InModule m (DType ts))
      SigPropSyn ps _  -> namingEnv (InModule m (DProp ps))


type PatsM = M.StateT (Set Ident) (SupplyT M.Id)

-- | We have a special case for this, because of the existential types
-- that we might encounter in the types of patterns.   In particular, if
-- we see `p : T` and `T` mentions a name `a` which is not otherwise defined,
-- we treat it as a new existential variable, and it is now considered defined
-- (i.e., other references to `a` should use the same name).
defsOfPats ::
  Set Ident {- ^ Unqalified type level names that are in scope -} ->
  [Pattern PName] {- ^ We want to know what names are introduced by these -} ->
  Supply -> (NamingEnv,Supply)
defsOfPats bound ps s =
  M.runId (runSupplyT s (fst <$> M.runStateT bound (defsOfPats' ps)))

defsOfPats' :: [Pattern PName] -> PatsM NamingEnv
defsOfPats' ps =
  case ps of
    []        -> pure mempty
    p : more  -> (<>) <$> defsOfPat p <*> defsOfPats' more

defsOfPat :: Pattern PName -> PatsM NamingEnv
defsOfPat pat =
  case pat of

    PVar x ->
      do
        y <- newLocal NSValue (thing x) (srcRange x)
        pure (singletonNS NSValue (thing x) y) 

    PCon _ xs     -> defsOfPats' xs
    PLocated p _r -> defsOfPat p
    PTyped p t    -> (<>) <$> defsOfTy t <*> defsOfPat p
    _             -> panic "namingEnv" ["Unexpected pattern"]

-- | Look for "naming" type variables in the type.
defsOfTy :: Type PName -> PatsM NamingEnv
defsOfTy ty =
  case ty of
    TFun a b -> defsOfTys [a,b]
    TSeq n t -> defsOfTys [n,t]
    TBit     -> pure mempty
    TNum {}  -> pure mempty
    TChar {} -> pure mempty

    TUser Located { thing = nm@(UnQual i), srcRange = rng  } [] ->
      do
        bound <- M.get
        if i `Set.member` bound
          then pure mempty
          else
            do
              y <- newLocal NSType nm rng
              M.sets (\b -> (singletonNS NSType nm y, Set.insert i b))

    TUser _ ts        -> defsOfTys ts
    TTyApp {}         -> panic "defsOfTy" ["TTyApp"]
    TRecord r         -> defsOfTys (map snd (displayElements r))
    TTuple ts         -> defsOfTys ts
    TWild             -> pure mempty
    TLocated t _      -> defsOfTy t
    TParens t _       -> defsOfTy t
    TInfix t1 _ _ t2  -> defsOfTys [t1,t2]
    
defsOfTys :: [Type PName] -> PatsM NamingEnv
defsOfTys tys =
  case tys of
    t : more  -> (<>) <$> defsOfTy t <*> defsOfTys more
    []        -> pure mempty



--------------------------------------------------------------------------------
-- Helpers

newTop ::
  FreshM m => Namespace -> ModPath -> PName -> Maybe Fixity -> Range -> m Name
newTop ns m thing fx rng =
  liftSupply (mkDeclared ns m src (getIdent thing) fx rng)
  where src = if isSystemName thing then SystemName else UserName

newLocal :: FreshM m => Namespace -> PName -> Range -> m Name
newLocal ns thing rng = liftSupply (mkLocalPName ns thing rng)

-- | Given a name in a signature, make a name for the parameter corresponding
-- to the signature.
newModParam :: FreshM m => ModPath -> Ident -> Range -> Name -> m Name
newModParam m i rng n = liftSupply (mkModParam m i rng n)

-- | Given a name in a functor, make a fresh name for the corresponding thing in
-- the instantiation.
--
-- The 'ModPath' should be the instantiation not the functor.
newFunctorInst :: FreshM m => ModPath -> Name -> m Name
newFunctorInst m n = liftSupply (freshNameFor m n)


{- | Do something in the context of a module.
If `Nothing` than we are working with a local declaration.
Otherwise we are at the top-level of the given module.

By wrapping types with this, we can pass the module path
to methods that need the extra information. -}
data InModule a = InModule (Maybe ModPath) a
                  deriving (Functor,Traversable,Foldable,Show)