module GHC.Tc.Types.TH (
    SpliceType(..)
  , SpliceOrBracket(..)
  , ThStage(..)
  , PendingStuff(..)
  , ThLevel
  , topStage
  , topAnnStage
  , topSpliceStage
  , thLevel
  , impLevel
  , outerLevel
  ) where

import GHCi.RemoteTypes
import qualified Language.Haskell.TH as TH
import GHC.Tc.Types.Evidence
import GHC.Utils.Outputable
import GHC.Prelude
import GHC.Tc.Types.TcRef
import GHC.Tc.Types.Constraint
import GHC.Hs.Expr ( PendingTcSplice, PendingRnSplice )

---------------------------
-- Template Haskell stages and levels
---------------------------

data SpliceType = Typed | Untyped
data SpliceOrBracket = IsSplice | IsBracket

data ThStage    -- See Note [Template Haskell state diagram]
                -- and Note [Template Haskell levels] in GHC.Tc.Gen.Splice
    -- Start at:   Comp
    -- At bracket: wrap current stage in Brack
    -- At splice:  currently Brack: return to previous stage
    --             currently Comp/Splice: compile and run
  = Splice SpliceType -- Inside a top-level splice
                      -- This code will be run *at compile time*;
                      --   the result replaces the splice
                      -- Binding level = 0

  | RunSplice (TcRef [ForeignRef (TH.Q ())])
      -- Set when running a splice, i.e. NOT when renaming or typechecking the
      -- Haskell code for the splice. See Note [RunSplice ThLevel].
      --
      -- Contains a list of mod finalizers collected while executing the splice.
      --
      -- 'addModFinalizer' inserts finalizers here, and from here they are taken
      -- to construct an @HsSpliced@ annotation for untyped splices. See Note
      -- [Delaying modFinalizers in untyped splices] in GHC.Rename.Splice.
      --
      -- For typed splices, the typechecker takes finalizers from here and
      -- inserts them in the list of finalizers in the global environment.
      --
      -- See Note [Collecting modFinalizers in typed splices] in "GHC.Tc.Gen.Splice".

  | Comp        -- Ordinary Haskell code
                -- Binding level = 1

  | Brack                       -- Inside brackets
      ThStage                   --   Enclosing stage
      PendingStuff

data PendingStuff
  = RnPendingUntyped              -- Renaming the inside of an *untyped* bracket
      (TcRef [PendingRnSplice])   -- Pending splices in here

  | RnPendingTyped                -- Renaming the inside of a *typed* bracket

  | TcPending                     -- Typechecking the inside of a typed bracket
      (TcRef [PendingTcSplice])   --   Accumulate pending splices here
      (TcRef WantedConstraints)   --     and type constraints here
      QuoteWrapper                -- A type variable and evidence variable
                                  -- for the overall monad of
                                  -- the bracket. Splices are checked
                                  -- against this monad. The evidence
                                  -- variable is used for desugaring
                                  -- `lift`.


topStage, topAnnStage, topSpliceStage :: ThStage
topStage :: ThStage
topStage       = ThStage
Comp
topAnnStage :: ThStage
topAnnStage    = SpliceType -> ThStage
Splice SpliceType
Untyped
topSpliceStage :: ThStage
topSpliceStage = SpliceType -> ThStage
Splice SpliceType
Untyped

instance Outputable ThStage where
   ppr :: ThStage -> SDoc
ppr (Splice SpliceType
_)    = String -> SDoc
forall doc. IsLine doc => String -> doc
text String
"Splice"
   ppr (RunSplice TcRef [ForeignRef (Q ())]
_) = String -> SDoc
forall doc. IsLine doc => String -> doc
text String
"RunSplice"
   ppr ThStage
Comp          = String -> SDoc
forall doc. IsLine doc => String -> doc
text String
"Comp"
   ppr (Brack ThStage
s PendingStuff
_)   = String -> SDoc
forall doc. IsLine doc => String -> doc
text String
"Brack" SDoc -> SDoc -> SDoc
forall doc. IsLine doc => doc -> doc -> doc
<> SDoc -> SDoc
forall doc. IsLine doc => doc -> doc
parens (ThStage -> SDoc
forall a. Outputable a => a -> SDoc
ppr ThStage
s)

type ThLevel = Int
    -- NB: see Note [Template Haskell levels] in GHC.Tc.Gen.Splice
    -- Incremented when going inside a bracket,
    -- decremented when going inside a splice
    -- NB: ThLevel is one greater than the 'n' in Fig 2 of the
    --     original "Template meta-programming for Haskell" paper

impLevel, outerLevel :: ThLevel
impLevel :: ThLevel
impLevel = ThLevel
0    -- Imported things; they can be used inside a top level splice
outerLevel :: ThLevel
outerLevel = ThLevel
1  -- Things defined outside brackets

thLevel :: ThStage -> ThLevel
thLevel :: ThStage -> ThLevel
thLevel (Splice SpliceType
_)    = ThLevel
0
thLevel ThStage
Comp          = ThLevel
1
thLevel (Brack ThStage
s PendingStuff
_)   = ThStage -> ThLevel
thLevel ThStage
s ThLevel -> ThLevel -> ThLevel
forall a. Num a => a -> a -> a
+ ThLevel
1
thLevel (RunSplice TcRef [ForeignRef (Q ())]
_) = ThLevel
0 -- previously: panic "thLevel: called when running a splice"
                        -- See Note [RunSplice ThLevel].

{- Note [RunSplice ThLevel]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The 'RunSplice' stage is set when executing a splice, and only when running a
splice. In particular it is not set when the splice is renamed or typechecked.

However, this is not true. `reifyInstances` for example does rename the given type,
and these types may contain variables (#9262 allow free variables in reifyInstances).
Therefore here we assume that thLevel (RunSplice _) = 0.
Proper fix would probably require renaming argument `reifyInstances` separately prior
to evaluation of the overall splice.

'RunSplice' is needed to provide a reference where 'addModFinalizer' can insert
the finalizer (see Note [Delaying modFinalizers in untyped splices]), and
'addModFinalizer' runs when doing Q things. Therefore, It doesn't make sense to
set 'RunSplice' when renaming or typechecking the splice, where 'Splice',
'Brack' or 'Comp' are used instead.

-}