Add quasi-quotation, courtesy of Geoffrey Mainland
[ghc-hetmet.git] / compiler / typecheck / TcSplice.lhs
index bc10f3e..50bbc3c 100644 (file)
@@ -6,7 +6,15 @@
 TcSplice: Template Haskell splices
 
 \begin{code}
-module TcSplice( tcSpliceExpr, tcSpliceDecls, tcBracket ) where
+{-# OPTIONS -w #-}
+-- The above warning supression flag is a temporary kludge.
+-- While working on this module you are encouraged to remove it and fix
+-- any warnings in the module. See
+--     http://hackage.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#Warnings
+-- for details
+
+module TcSplice( tcSpliceExpr, tcSpliceDecls, tcBracket,
+                 runQuasiQuoteExpr, runQuasiQuotePat ) where
 
 #include "HsVersions.h"
 
@@ -54,6 +62,7 @@ import Outputable
 import Unique
 import DynFlags
 import PackageConfig
+import Maybe
 import BasicTypes
 import Panic
 import FastString
@@ -64,8 +73,86 @@ import qualified Language.Haskell.TH.Syntax as TH
 
 import GHC.Exts                ( unsafeCoerce#, Int#, Int(..) )
 import Control.Monad   ( liftM )
+import qualified Control.Exception  as Exception( userErrors )
 \end{code}
 
+Note [Template Haskell levels]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+* Imported things are impLevel (= 0)
+
+* In GHCi, variables bound by a previous command are treated
+  as impLevel, because we have bytecode for them.
+
+* Variables are bound at the "current level"
+
+* The current level starts off at topLevel (= 1)
+
+* The level is decremented by splicing $(..)
+              incremented by brackets [| |]
+              incremented by name-quoting 'f
+
+When a variable is used, we compare 
+       bind:  binding level, and
+       use:   current level at usage site
+
+  Generally
+       bind > use      Always error (bound later than used)
+                       [| \x -> $(f x) |]
+                       
+       bind = use      Always OK (bound same stage as used)
+                       [| \x -> $(f [| x |]) |]
+
+       bind < use      Inside brackets, it depends
+                       Inside splice, OK
+                       Inside neither, OK
+
+  For (bind < use) inside brackets, there are three cases:
+    - Imported things  OK      f = [| map |]
+    - Top-level things OK      g = [| f |]
+    - Non-top-level    Only if there is a liftable instance
+                               h = \(x:Int) -> [| x |]
+
+See Note [What is a top-level Id?]
+
+Note [Quoting names]
+~~~~~~~~~~~~~~~~~~~~
+A quoted name 'n is a bit like a quoted expression [| n |], except that we 
+have no cross-stage lifting (c.f. TcExpr.thBrackId).  So, after incrementing
+the use-level to account for the brackets, the cases are:
+
+       bind > use                      Error
+       bind = use                      OK
+       bind < use      
+               Imported things         OK
+               Top-level things        OK
+               Non-top-level           Error
+
+See Note [What is a top-level Id?] in TcEnv.  Examples:
+
+  f 'map       -- OK; also for top-level defns of this module
+
+  \x. f 'x     -- Not ok (whereas \x. f [| x |] might have been ok, by
+               --                               cross-stage lifting
+
+  \y. [| \x. $(f 'y) |]        -- Not ok (same reason)
+
+  [| \x. $(f 'x) |]    -- OK
+
+
+Note [What is a top-level Id?]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+In the level-control criteria above, we need to know what a "top level Id" is.
+There are three kinds:
+  * Imported from another module               (GlobalId, ExternalName)
+  * Bound at the top level of this module      (ExternalName)
+  * In GHCi, bound by a previous stmt          (GlobalId)
+It's strange that there is no one criterion tht picks out all three, but that's
+how it is right now.  (The obvious thing is to give an ExternalName to GHCi Ids 
+bound in an earlier Stmt, but what module would you choose?  See 
+Note [Interactively-bound Ids in GHCi] in TcRnDriver.)
+
+The predicate we use is TcEnv.thTopLevelId.
+
 
 %************************************************************************
 %*                                                                     *
@@ -79,9 +166,15 @@ tcSpliceExpr  :: HsSplice Name -> BoxyRhoType -> TcM (HsExpr TcId)
 kcSpliceType  :: HsSplice Name -> TcM (HsType Name, TcKind)
        -- None of these functions add constraints to the LIE
 
+runQuasiQuoteExpr :: HsQuasiQuote Name -> TcM (LHsExpr RdrName)
+runQuasiQuotePat  :: HsQuasiQuote Name -> TcM (LPat RdrName)
+
 #ifndef GHCI
 tcSpliceExpr n e ty = pprPanic "Cant do tcSpliceExpr without GHCi" (ppr e)
 tcSpliceDecls e     = pprPanic "Cant do tcSpliceDecls without GHCi" (ppr e)
+
+runQuasiQuoteExpr q  = pprPanic "Cant do runQuasiQuoteExpr without GHCi" (ppr q)
+runQuasiQuotePat  q  = pprPanic "Cant do runQuasiQuotePat without GHCi" (ppr q)
 #else
 \end{code}
 
@@ -119,7 +212,7 @@ tcBracket brack res_ty
     getLIEVar                          `thenM` \ lie_var ->
 
     setStage (Brack next_level pending_splices lie_var) (
-       getLIE (tc_bracket brack)
+       getLIE (tc_bracket next_level brack)
     )                                  `thenM` \ (meta_ty, lie) ->
     tcSimplifyBracket lie              `thenM_`  
 
@@ -131,22 +224,34 @@ tcBracket brack res_ty
     returnM (noLoc (HsBracketOut brack pendings))
     }
 
-tc_bracket :: HsBracket Name -> TcM TcType
-tc_bracket (VarBr v) 
-  = tcMetaTy nameTyConName     -- Result type is Var (not Q-monadic)
+tc_bracket :: ThLevel -> HsBracket Name -> TcM TcType
+tc_bracket use_lvl (VarBr name)        -- Note [Quoting names]
+  = do { thing <- tcLookup name
+       ; case thing of
+           AGlobal _ -> return ()
+           ATcId { tct_level = bind_lvl, tct_id = id }
+               | thTopLevelId id       -- C.f thTopLevelId case of
+               -> keepAliveTc id       --     TcExpr.thBrackId
+               | otherwise
+               -> do { checkTc (use_lvl == bind_lvl)
+                               (quotedNameStageErr name) }
+           other -> pprPanic "th_bracket" (ppr name)
+
+       ; tcMetaTy nameTyConName        -- Result type is Var (not Q-monadic)
+       }
 
-tc_bracket (ExpBr expr) 
-  = newFlexiTyVarTy liftedTypeKind     `thenM` \ any_ty ->
-    tcMonoExpr expr any_ty             `thenM_`
-    tcMetaTy expQTyConName
+tc_bracket use_lvl (ExpBr expr) 
+  = do { any_ty <- newFlexiTyVarTy liftedTypeKind
+       ; tcMonoExpr expr any_ty
+       ; tcMetaTy expQTyConName }
        -- Result type is Expr (= Q Exp)
 
-tc_bracket (TypBr typ) 
-  = tcHsSigType ExprSigCtxt typ                `thenM_`
-    tcMetaTy typeQTyConName
+tc_bracket use_lvl (TypBr typ) 
+  = do { tcHsSigType ExprSigCtxt typ
+       ; tcMetaTy typeQTyConName }
        -- Result type is Type (= Q Typ)
 
-tc_bracket (DecBr decls)
+tc_bracket use_lvl (DecBr decls)
   = do {  tcTopSrcDecls emptyModDetails decls
        -- Typecheck the declarations, dicarding the result
        -- We'll get all that stuff later, when we splice it in
@@ -157,8 +262,12 @@ tc_bracket (DecBr decls)
        -- Result type is Q [Dec]
     }
 
-tc_bracket (PatBr _)
+tc_bracket use_lvl (PatBr _)
   = failWithTc (ptext SLIT("Tempate Haskell pattern brackets are not supported yet"))
+
+quotedNameStageErr v 
+  = sep [ ptext SLIT("Stage error: the non-top-level quoted name") <+> ppr (VarBr v)
+       , ptext SLIT("must be used at the same stage at which is is bound")]
 \end{code}
 
 
@@ -256,6 +365,80 @@ tcTopSpliceExpr expr meta_ty
 
 %************************************************************************
 %*                                                                     *
+       Quasi-quoting
+%*                                                                     *
+%************************************************************************
+
+Note [Quasi-quote overview]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The GHC "quasi-quote" extension is described by Geoff Mainland's paper
+"Why it's nice to be quoted: quasiquoting for Haskell" (Haskell
+Workshop 2007).
+
+Briefly, one writes
+       [:p| stuff |]
+and the arbitrary string "stuff" gets parsed by the parser 'p', whose
+type should be Language.Haskell.TH.Quote.QuasiQuoter.  'p' must be
+defined in another module, because we are going to run it here.  It's
+a bit like a TH splice:
+       $(p "stuff")
+
+However, you can do this in patterns as well as terms.  Becuase of this,
+the splice is run by the *renamer* rather than the type checker.
+
+\begin{code}
+runQuasiQuote :: Outputable hs_syn
+              => HsQuasiQuote Name     -- Contains term of type QuasiQuoter, and the String
+              -> Name                  -- Of type QuasiQuoter -> String -> Q th_syn
+              -> String                        -- Documentation string only
+              -> Name                  -- Name of th_syn type  
+              -> (SrcSpan -> th_syn -> Either Message hs_syn)
+              -> TcM hs_syn
+runQuasiQuote (HsQuasiQuote name quoter q_span quote) quote_selector desc meta_ty convert
+  = do { -- Check that the quoter is not locally defined, otherwise the TH
+          -- machinery will not be able to run the quasiquote.
+        ; this_mod <- getModule
+        ; let is_local = case nameModule_maybe quoter of
+                           Just mod | mod == this_mod -> True
+                                    | otherwise       -> False
+                           Nothing -> True
+       ; traceTc (text "runQQ" <+> ppr quoter <+> ppr is_local)
+        ; checkTc (not is_local) (quoteStageError quoter)
+
+         -- Build the expression 
+       ; let quoterExpr = L q_span $! HsVar $! quoter
+       ; let quoteExpr = L q_span $! HsLit $! HsString quote
+       ; let expr = L q_span $
+                    HsApp (L q_span $
+                           HsApp (L q_span (HsVar quote_selector)) quoterExpr) quoteExpr
+       ; recordThUse
+       ; meta_exp_ty <- tcMetaTy meta_ty
+
+       -- Typecheck the expression
+       ; zonked_q_expr <- tcTopSpliceExpr expr meta_exp_ty
+
+       -- Run the expression
+       ; traceTc (text "About to run" <+> ppr zonked_q_expr)
+       ; result <- runMeta convert zonked_q_expr
+       ; traceTc (text "Got result" <+> ppr result)
+       ; showSplice desc zonked_q_expr (ppr result)
+       ; return result
+       }
+
+runQuasiQuoteExpr quasiquote
+    = runQuasiQuote quasiquote quoteExpName "expression" expQTyConName convertToHsExpr
+
+runQuasiQuotePat quasiquote
+    = runQuasiQuote quasiquote quotePatName "pattern" patQTyConName convertToPat
+
+quoteStageError quoter
+  = sep [ptext SLIT("GHC stage restriction:") <+> ppr quoter,
+         nest 2 (ptext SLIT("is used in a quasiquote, and must be imported, not defined locally"))]
+\end{code}
+
+
+%************************************************************************
+%*                                                                     *
                Splicing a type
 %*                                                                     *
 %************************************************************************
@@ -361,6 +544,11 @@ runMetaE :: (SrcSpan -> TH.Exp -> Either Message (LHsExpr RdrName))
         -> TcM (LHsExpr RdrName)
 runMetaE  = runMeta
 
+runMetaP :: (SrcSpan -> TH.Pat -> Either Message (Pat RdrName))
+         -> LHsExpr Id          -- Of type (Q Pat)
+         -> TcM (Pat RdrName)
+runMetaP  = runMeta
+
 runMetaT :: (SrcSpan -> TH.Type -> Either Message (LHsType RdrName))
         -> LHsExpr Id          -- Of type (Q Type)
         -> TcM (LHsType RdrName)       
@@ -387,6 +575,7 @@ runMeta convert expr
            Right hval -> do
 
        {       -- Coerce it to Q t, and run it
+
                -- Running might fail if it throws an exception of any kind (hence tryAllM)
                -- including, say, a pattern-match exception in the code we are running
                --
@@ -394,23 +583,61 @@ runMeta convert expr
                -- exception-cacthing thing so that if there are any lurking 
                -- exceptions in the data structure returned by hval, we'll
                -- encounter them inside the try
-          either_th_syn <- tryAllM $ tryM $ TH.runQ $ unsafeCoerce# hval
-        ; case either_th_syn of
-            Left exn             -> failWithTc (mk_msg "run" exn)
-            Right (Left exn)     -> failM  -- Error already in Tc monad
-            Right (Right th_syn) -> do
-        { either_hs_syn <- tryAllM $ return $! convert (getLoc expr) th_syn
-        ; case either_hs_syn of
-            Left exn             -> failWithTc (mk_msg "interpret result of" exn)
-            Right (Left err)     -> do { addErrTc err; failM }
-            Right (Right hs_syn) -> return hs_syn
-        }}}}
+               --
+               -- See Note [Exceptions in TH] 
+         let expr_span = getLoc expr
+       ; either_tval <- tryAllM $
+                        setSrcSpan expr_span $ -- Set the span so that qLocation can
+                                               -- see where this splice is
+            do { th_syn <- TH.runQ (unsafeCoerce# hval)
+               ; case convert expr_span th_syn of
+                   Left err     -> failWithTc err
+                   Right hs_syn -> return hs_syn }
+
+       ; case either_tval of
+           Right v -> return v
+           Left exn | Just s <- Exception.userErrors exn
+                    , s == "IOEnv failure" 
+                    -> failM   -- Error already in Tc monad
+                    | otherwise -> failWithTc (mk_msg "run" exn)       -- Exception
+        }}}
   where
     mk_msg s exn = vcat [text "Exception when trying to" <+> text s <+> text "compile-time code:",
                         nest 2 (text (Panic.showException exn)),
                         nest 2 (text "Code:" <+> ppr expr)]
 \end{code}
 
+Note [Exceptions in TH]
+~~~~~~~~~~~~~~~~~~~~~~~
+Supppose we have something like this 
+       $( f 4 )
+where
+       f :: Int -> Q [Dec]
+       f n | n>3       = fail "Too many declarations"
+           | otherwise = ...
+
+The 'fail' is a user-generated failure, and should be displayed as a
+perfectly ordinary compiler error message, not a panic or anything
+like that.  Here's how it's processed:
+
+  * 'fail' is the monad fail.  The monad instance for Q in TH.Syntax
+    effectively transforms (fail s) to 
+       qReport True s >> fail
+    where 'qReport' comes from the Quasi class and fail from its monad
+    superclass.
+
+  * The TcM monad is an instance of Quasi (see TcSplice), and it implements
+    (qReport True s) by using addErr to add an error message to the bag of errors.
+    The 'fail' in TcM raises a UserError, with the uninteresting string
+       "IOEnv failure"
+
+  * So, when running a splice, we catch all exceptions; then for 
+       - a UserError "IOEnv failure", we assume the error is already 
+               in the error-bag (above)
+       - other errors, we add an error to the bag
+    and then fail
+
+
 To call runQ in the Tc monad, we need to make TcM an instance of Quasi:
 
 \begin{code}
@@ -422,10 +649,14 @@ instance TH.Quasi (IOEnv (Env TcGblEnv TcLclEnv)) where
   qReport True msg  = addErr (text msg)
   qReport False msg = addReport (text msg)
 
-  qCurrentModule = do { m <- getModule;
-                        return (moduleNameString (moduleName m)) }
-                -- ToDo: is throwing away the package name ok here?
-
+  qLocation = do { m <- getModule
+                ; l <- getSrcSpanM
+                ; return (TH.Loc { TH.loc_filename = unpackFS (srcSpanFile l)
+                                 , TH.loc_module   = moduleNameString (moduleName m)
+                                 , TH.loc_package  = packageIdString (modulePackageId m)
+                                 , TH.loc_start = (srcSpanStartLine l, srcSpanStartCol l)
+                                 , TH.loc_end = (srcSpanEndLine   l, srcSpanEndCol   l) }) }
+               
   qReify v = reify v
 
        -- For qRecover, discard error messages if