X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=compiler%2Ftypecheck%2FTcSplice.lhs;h=67c197d370115855ab40d0e7346c19802df518b7;hb=a4559bd612c25561bbd3e4f4959ac3fdbb767fb3;hp=c7dccc4b40669aae658deaa8d39f42fb047e5c5c;hpb=5ecf452603a0c79e4fed0062b79bcaff4449b213;p=ghc-hetmet.git diff --git a/compiler/typecheck/TcSplice.lhs b/compiler/typecheck/TcSplice.lhs index c7dccc4..67c197d 100644 --- a/compiler/typecheck/TcSplice.lhs +++ b/compiler/typecheck/TcSplice.lhs @@ -6,6 +6,13 @@ TcSplice: Template Haskell splices \begin{code} +{-# 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 ) where #include "HsVersions.h" @@ -54,6 +61,7 @@ import Outputable import Unique import DynFlags import PackageConfig +import Maybe import BasicTypes import Panic import FastString @@ -64,6 +72,7 @@ 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} @@ -387,6 +396,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 +404,58 @@ 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] + either_tval <- tryAllM $ do + { th_syn <- TH.runQ (unsafeCoerce# hval) + ; case convert (getLoc expr) 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} @@ -501,8 +546,7 @@ lookupThName th_name@(TH.Name occ flavour) Nothing | not (isSrcRdrName rdr_name) -- Exact, Orig -> lookupImportedName rdr_name | otherwise -- Unqual, Qual - -> do { - mb_name <- lookupSrcOcc_maybe rdr_name + -> do { mb_name <- lookupSrcOcc_maybe rdr_name ; case mb_name of Just name -> return name Nothing -> failWithTc (notInScope th_name) }