X-Git-Url: http://git.megacz.com/?p=ghc-hetmet.git;a=blobdiff_plain;f=compiler%2Ftypecheck%2FTcSplice.lhs;h=9ec400d7226d7cc4e4c5d876a128b81be5961018;hp=b0896064d008993592770f6e249f747685b7b2bd;hb=3f1b316d7035c55cd712cd39a9981339bcef2e8c;hpb=17b297d97d327620ed6bfab942f8992b2446f1bf diff --git a/compiler/typecheck/TcSplice.lhs b/compiler/typecheck/TcSplice.lhs index b089606..9ec400d 100644 --- a/compiler/typecheck/TcSplice.lhs +++ b/compiler/typecheck/TcSplice.lhs @@ -6,11 +6,11 @@ TcSplice: Template Haskell splices \begin{code} -{-# OPTIONS_GHC -w #-} +{-# 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/WorkingConventions#Warnings +-- http://hackage.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#Warnings -- for details module TcSplice( tcSpliceExpr, tcSpliceDecls, tcBracket ) where @@ -61,6 +61,7 @@ import Outputable import Unique import DynFlags import PackageConfig +import Maybe import BasicTypes import Panic import FastString @@ -71,8 +72,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. + %************************************************************************ %* * @@ -126,7 +205,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_` @@ -138,22 +217,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 @@ -164,8 +255,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} @@ -394,6 +489,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 -- @@ -401,23 +497,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}