The Q :: * -> * type constructor defined in Language.Haskell.TH.Syntax is an abstract type representing computations which have access to the compile-time environment of the module in which the computation is run. The Q type also handles variable substituion, called name capture by TH (and discussed here.) All splices have type Q X for some X.
The compile-time environment includes:
The Q type also has the ability to generate fresh names, with the function newName :: String -> Q Name. Note that the name is not bound anywhere implicitly, so the user must bind it themselves, and so making sure the resulting use of the name is well-scoped is the responsibility of the user.
Q has instances for Functor,Monad,Applicative and this is the main interface for manipulating Q values, along with the combinators provided in Language.Haskell.TH.Lib, which define a helper function for every constructor of the TH ast of the form:
LitE :: Lit -> Exp
litE :: Lit -> ExpQ
AppE :: Exp -> Exp -> Exp 
appE :: ExpQ -> ExpQ -> ExpQ
Note that ExpQ, TypeQ, DecsQ and PatQ are synonyms for the AST types which are typically stored inside the Q type.
The TH library provides a function runQ :: Quasi m => Q a -> m a, and there is an instance Quasi IO, so it would seem that the Q type is just a fancy IO. However, the use of runQ :: Q a -> IO a produces an IO action which does not have access to any compile-time environment - this is only available in the actual Q type. Such IO actions will fail at runtime if trying to access said environment.