diff options
author | Simon Peyton Jones <simonpj@microsoft.com> | 2013-09-03 09:10:26 +0100 |
---|---|---|
committer | Simon Peyton Jones <simonpj@microsoft.com> | 2013-09-03 09:20:35 +0100 |
commit | dfa8ef031c83998c163bb94fb84ff8e02ef86cf8 (patch) | |
tree | 419031d9a9985af5319a21f1cb0e7ef09a9c9249 /compiler/simplCore/CoreMonad.lhs | |
parent | e52554768ad28bd0c191826100786b1aee3295dc (diff) | |
download | haskell-dfa8ef031c83998c163bb94fb84ff8e02ef86cf8.tar.gz |
Improve Linting in GHCi (fixes Trac #8215)
The original problem was that we weren't bringing varaibles bound in the
interactive context into scope before Linting the result of a top-level
declaration in GHCi. (We were doing this for expressions.)
Moreover I found that we weren't Linting the result of desugaring
a GHCi expression, which we really should be doing.
It took me a bit of time to unravel all this, and I did some refactoring
to make it easier next time.
* CoreMonad contains the Lint wrappers that get the right
environments into place. It always had endPass and lintPassResult
(which Lints bindings), but now it has lintInteractiveExpr.
* Both use a common function CoreMonad.interactiveInScope to find
those in-scope variables.
Quite a bit of knock-on effects from this, but nothing exciting.
Diffstat (limited to 'compiler/simplCore/CoreMonad.lhs')
-rw-r--r-- | compiler/simplCore/CoreMonad.lhs | 84 |
1 files changed, 69 insertions, 15 deletions
diff --git a/compiler/simplCore/CoreMonad.lhs b/compiler/simplCore/CoreMonad.lhs index 31547e14a2..0af8201170 100644 --- a/compiler/simplCore/CoreMonad.lhs +++ b/compiler/simplCore/CoreMonad.lhs @@ -50,7 +50,8 @@ module CoreMonad ( getAnnotations, getFirstAnnotations, -- ** Debug output - showPass, endPass, dumpPassResult, lintPassResult, dumpIfSet, + showPass, endPass, dumpPassResult, lintPassResult, + lintInteractiveExpr, dumpIfSet, -- ** Screen output putMsg, putMsgS, errorMsg, errorMsgS, @@ -70,7 +71,7 @@ import Name( Name ) import CoreSyn import PprCore import CoreUtils -import CoreLint ( lintCoreBindings ) +import CoreLint ( lintCoreBindings, lintExpr ) import HscTypes import Module import DynFlags @@ -78,12 +79,13 @@ import StaticFlags import Rules ( RuleBase ) import BasicTypes ( CompilerPhase(..) ) import Annotations -import Id ( Id ) import IOEnv hiding ( liftIO, failM, failWithM ) import qualified IOEnv ( liftIO ) import TcEnv ( tcLookupGlobal ) import TcRnMonad ( initTcForLookup ) +import Var +import VarSet import Outputable import FastString @@ -136,11 +138,12 @@ stuff before and after core passes, and do Core Lint when necessary. showPass :: DynFlags -> CoreToDo -> IO () showPass dflags pass = Err.showPass dflags (showPpr dflags pass) -endPass :: DynFlags -> CoreToDo -> CoreProgram -> [CoreRule] -> IO () -endPass dflags pass binds rules +endPass :: HscEnv -> CoreToDo -> CoreProgram -> [CoreRule] -> IO () +endPass hsc_env pass binds rules = do { dumpPassResult dflags mb_flag (ppr pass) (pprPassDetails pass) binds rules - ; lintPassResult dflags pass binds } + ; lintPassResult hsc_env pass binds } where + dflags = hsc_dflags hsc_env mb_flag = case coreDumpFlag pass of Just flag | dopt flag dflags -> Just flag | dopt Opt_D_verbose_core2core dflags -> Just flag @@ -178,12 +181,16 @@ dumpPassResult dflags mb_flag hdr extra_info binds rules , ptext (sLit "------ Local rules for imported ids --------") , pprRules rules ] -lintPassResult :: DynFlags -> CoreToDo -> CoreProgram -> IO () -lintPassResult dflags pass binds - = when (gopt Opt_DoCoreLinting dflags) $ - do { let (warns, errs) = lintCoreBindings binds +lintPassResult :: HscEnv -> CoreToDo -> CoreProgram -> IO () +lintPassResult hsc_env pass binds + | not (gopt Opt_DoCoreLinting dflags) + = return () + | otherwise + = do { let (warns, errs) = lintCoreBindings (interactiveInScope hsc_env) binds ; Err.showPass dflags ("Core Linted result of " ++ showPpr dflags pass) ; displayLintResults dflags pass warns errs binds } + where + dflags = hsc_dflags hsc_env displayLintResults :: DynFlags -> CoreToDo -> Bag Err.MsgDoc -> Bag Err.MsgDoc -> CoreProgram @@ -191,7 +198,7 @@ displayLintResults :: DynFlags -> CoreToDo displayLintResults dflags pass warns errs binds | not (isEmptyBag errs) = do { log_action dflags dflags Err.SevDump noSrcSpan defaultDumpStyle - (vcat [ banner "errors", Err.pprMessageBag errs + (vcat [ lint_banner "errors" (ppr pass), Err.pprMessageBag errs , ptext (sLit "*** Offending Program ***") , pprCoreBindings binds , ptext (sLit "*** End of Offense ***") ]) @@ -206,19 +213,66 @@ displayLintResults dflags pass warns errs binds , not opt_NoDebugOutput , showLintWarnings pass = log_action dflags dflags Err.SevDump noSrcSpan defaultDumpStyle - (banner "warnings" $$ Err.pprMessageBag warns) + (lint_banner "warnings" (ppr pass) $$ Err.pprMessageBag warns) | otherwise = return () where - banner string = ptext (sLit "*** Core Lint") <+> text string - <+> ptext (sLit ": in result of") <+> ppr pass - <+> ptext (sLit "***") + +lint_banner :: String -> SDoc -> SDoc +lint_banner string pass = ptext (sLit "*** Core Lint") <+> text string + <+> ptext (sLit ": in result of") <+> pass + <+> ptext (sLit "***") showLintWarnings :: CoreToDo -> Bool -- Disable Lint warnings on the first simplifier pass, because -- there may be some INLINE knots still tied, which is tiresomely noisy showLintWarnings (CoreDoSimplify _ (SimplMode { sm_phase = InitialPhase })) = False showLintWarnings _ = True + +lintInteractiveExpr :: String -> HscEnv -> CoreExpr -> IO () +lintInteractiveExpr what hsc_env expr + | not (gopt Opt_DoCoreLinting dflags) + = return () + | Just err <- lintExpr (interactiveInScope hsc_env) expr + = do { display_lint_err err + ; Err.ghcExit dflags 1 } + | otherwise + = return () + where + dflags = hsc_dflags hsc_env + + display_lint_err err + = do { log_action dflags dflags Err.SevDump noSrcSpan defaultDumpStyle + (vcat [ lint_banner "errors" (text what) + , err + , ptext (sLit "*** Offending Program ***") + , pprCoreExpr expr + , ptext (sLit "*** End of Offense ***") ]) + ; Err.ghcExit dflags 1 } + +interactiveInScope :: HscEnv -> [Var] +-- In GHCi we may lint expressions, or bindings arising from 'deriving' +-- clauses, that mention variables bound in the interactive context. +-- These are Local things (see Note [Interactively-bound Ids in GHCi] in TcRnDriver). +-- So we have to tell Lint about them, lest it reports them as out of scope. +-- +-- We do this by find local-named things that may appear free in interactive +-- context. This function is pretty revolting and quite possibly not quite right. +-- When we are not in GHCi, the interactive context (hsc_IC hsc_env) is empty +-- so this is a (cheap) no-op. +-- +-- See Trac #8215 for an example +interactiveInScope hsc_env + = tyvars ++ vars + where + ictxt = hsc_IC hsc_env + te = mkTypeEnvWithImplicits (ic_tythings ictxt ++ map AnId (ic_sys_vars ictxt)) + vars = typeEnvIds te + tyvars = varSetElems $ tyThingsTyVars $ typeEnvElts $ te + -- Why the type variables? How can the top level envt have free tyvars? + -- I think it's becuase of the GHCi debugger, which can bind variables + -- f :: [t] -> [t] + -- where t is a RuntimeUnk (see TcType) \end{code} |