From 5d48e67fac952f7188fc9ebcfbf6e3ccb9b75705 Mon Sep 17 00:00:00 2001 From: Benjamin Bykowski Date: Sun, 5 Jul 2015 01:38:22 +0200 Subject: [PATCH] Easy way to defer type errors (implements #8353) Added load! and reload! commands, effectively setting "-fdefer-type-errors" before loading a file and unsetting it after loading if it has not been set before. Differential Revision: https://phabricator.haskell.org/D960 --- docs/users_guide/7.12.1-notes.xml | 5 +++ docs/users_guide/ghci.xml | 40 +++++++++++++++------ ghc/InteractiveUI.hs | 42 +++++++++++++++-------- testsuite/tests/ghci/scripts/Defer03.hs | 7 ++++ testsuite/tests/ghci/scripts/T8353.script | 22 ++++++++++++ testsuite/tests/ghci/scripts/T8353.stderr | 25 ++++++++++++++ testsuite/tests/ghci/scripts/all.T | 1 + 7 files changed, 117 insertions(+), 25 deletions(-) create mode 100755 testsuite/tests/ghci/scripts/Defer03.hs create mode 100644 testsuite/tests/ghci/scripts/T8353.script create mode 100644 testsuite/tests/ghci/scripts/T8353.stderr diff --git a/docs/users_guide/7.12.1-notes.xml b/docs/users_guide/7.12.1-notes.xml index f217b91cdd..dc191d2638 100644 --- a/docs/users_guide/7.12.1-notes.xml +++ b/docs/users_guide/7.12.1-notes.xml @@ -101,6 +101,11 @@ commands now take an optional count allowing the user to move forward or backward in history several steps at a time. + + Added commands :load! and :reload!, + effectively setting "-fdefer-type-errors" before loading a module and + unsetting it after loading if it has not been set before (#8353). + diff --git a/docs/users_guide/ghci.xml b/docs/users_guide/ghci.xml index a1271e1ac1..399cda90ea 100644 --- a/docs/users_guide/ghci.xml +++ b/docs/users_guide/ghci.xml @@ -674,13 +674,13 @@ Prelude> What's really in scope at the prompt? When you type an expression at the prompt, what - identifiers and types are in scope? + identifiers and types are in scope? GHCi provides a flexible way to control exactly how the context for an expression is constructed: - The :load, :add, + The :load, :add, and :reload commands (). @@ -702,8 +702,8 @@ Prelude> The effect of <literal>:load</literal> on what is in scope - The :load, :add, and :reload - commands ( + The :load, :add, and :reload + commands ( and ) affect the top-level scope. Let's start with the simple cases; when you start GHCi the prompt looks like this: @@ -830,7 +830,7 @@ Prelude System.IO Map> Controlling what is in scope with the <literal>:module</literal> command Another way to manipulate the scope is to use the - :module command, whose syntax is this: + :module command, whose syntax is this: :module +|- *mod1 ... *modn @@ -881,7 +881,7 @@ Prelude System.IO Map> :load It might seem that :module/import and - :load/:add/:reload + :load/:add/:reload do similar things: you can use both to bring a module into scope. However, there is a very important difference. GHCi is concerned with two sets of modules: @@ -907,7 +907,7 @@ Prelude System.IO Map> - You can add a module to the scope (via :module + You can add a module to the scope (via :module or import) only if either (a) it is loaded, or (b) it is a module from a package that GHCi knows about. @@ -2627,7 +2627,7 @@ T Int :: * -> * - :load *module ... + :load! *module ... :load @@ -2649,6 +2649,15 @@ T Int :: * -> * byte-code. Using the * prefix forces a module to be loaded as byte-code. + Adding the optional "!" turns type + errors into warnings while loading. This allows to use the + portions of the module that are correct, even if there are + type errors in some definitions. Effectively, the + "-fdefer-type-errors" flag is set before loading and unset + after loading if the flag has not already been set + before. See for further + motivation and details. + After a :load command, the current context is set to: @@ -2785,7 +2794,7 @@ bar - :reload + :reload! :reload @@ -2794,6 +2803,15 @@ bar or any dependent module, has changed. Note that this may entail loading new modules, or dropping modules which are no longer indirectly required by the target. + + Adding the optional "!" turns type + errors into warnings while loading. This allows to use the + portions of the module that are correct, even if there are + type errors in some definitions. Effectively, the + "-fdefer-type-errors" flag is set before loading and unset + after loading if the flag has not already been set + before. See for further + motivation and details. @@ -3302,7 +3320,7 @@ Prelude> :set -fno-warn-incomplete-patterns -XNoMultiParamTypeClasses Setting options for interactive evaluation only - GHCi actually maintains two sets of options: + GHCi actually maintains two sets of options: The loading options apply when loading modules @@ -3317,7 +3335,7 @@ The :set command modifies both, but there is - It is often useful to change the interactive options, + It is often useful to change the interactive options, without having that option apply to loaded modules too. For example diff --git a/ghc/InteractiveUI.hs b/ghc/InteractiveUI.hs index 3912198ed1..cd58fc2fff 100644 --- a/ghc/InteractiveUI.hs +++ b/ghc/InteractiveUI.hs @@ -172,13 +172,15 @@ ghciCommands = [ ("issafe", keepGoing' isSafeCmd, completeModule), ("kind", keepGoing' (kindOfType False), completeIdentifier), ("kind!", keepGoing' (kindOfType True), completeIdentifier), - ("load", keepGoingPaths loadModule_, completeHomeModuleOrFile), + ("load", keepGoingPaths (loadModule_ False), completeHomeModuleOrFile), + ("load!", keepGoingPaths (loadModule_ True), completeHomeModuleOrFile), ("list", keepGoing' listCmd, noCompletion), ("module", keepGoing moduleCmd, completeSetModule), ("main", keepGoing runMain, completeFilename), ("print", keepGoing printCmd, completeExpression), ("quit", quit, noCompletion), - ("reload", keepGoing' reloadModule, noCompletion), + ("reload", keepGoing' (reloadModule False), noCompletion), + ("reload!", keepGoing' (reloadModule True), noCompletion), ("run", keepGoing runRun, completeFilename), ("script", keepGoing' scriptCmd, completeFilename), ("set", keepGoing setCmd, completeSetOptions), @@ -256,11 +258,13 @@ defFullHelpText = " :issafe [] display safe haskell information of module \n" ++ " :kind[!] show the kind of \n" ++ " (!: also print the normalised type)\n" ++ - " :load [*] ... load module(s) and their dependents\n" ++ + " :load[!] [*] ... load module(s) and their dependents\n" ++ + " (!: defer type errors)\n" ++ " :main [ ...] run the main function with the given arguments\n" ++ " :module [+/-] [*] ... set the context for expression evaluation\n" ++ " :quit exit GHCi\n" ++ - " :reload reload the current module set\n" ++ + " :reload[!] reload the current module set\n" ++ + " (!: defer type errors)\n" ++ " :run function [ ...] run the function with the given arguments\n" ++ " :script run the script \n" ++ " :type show the type of \n" ++ @@ -1272,7 +1276,7 @@ editFile str = code <- liftIO $ system (cmd ++ cmdArgs) when (code == ExitSuccess) - $ reloadModule "" + $ reloadModule False "" -- The user didn't specify a file so we pick one for them. -- Our strategy is to pick the first module that failed to load, @@ -1418,11 +1422,24 @@ checkModule m = do ----------------------------------------------------------------------------- -- :load, :add, :reload +-- | Sets '-fdefer-type-errors' if 'defer' is true, executes 'load' and unsets +-- '-fdefer-type-errors' again if it has not been set before +deferredLoad :: Bool -> InputT GHCi SuccessFlag -> InputT GHCi () +deferredLoad defer load = do + flags <- getDynFlags + deferredBefore <- return (gopt Opt_DeferTypeErrors flags) + when (defer) $ Monad.void $ + GHC.setProgramDynFlags $ gopt_set flags Opt_DeferTypeErrors + Monad.void $ load + flags <- getDynFlags + when (not deferredBefore) $ Monad.void $ + GHC.setProgramDynFlags $ gopt_unset flags Opt_DeferTypeErrors + loadModule :: [(FilePath, Maybe Phase)] -> InputT GHCi SuccessFlag loadModule fs = timeIt (const Nothing) (loadModule' fs) -loadModule_ :: [FilePath] -> InputT GHCi () -loadModule_ fs = loadModule (zip fs (repeat Nothing)) >> return () +loadModule_ :: Bool -> [FilePath] -> InputT GHCi () +loadModule_ defer fs = deferredLoad defer (loadModule (zip fs (repeat Nothing))) loadModule' :: [(FilePath, Maybe Phase)] -> InputT GHCi SuccessFlag loadModule' files = do @@ -1460,13 +1477,10 @@ addModule files = do -- :reload -reloadModule :: String -> InputT GHCi () -reloadModule m = do - _ <- doLoad True $ - if null m then LoadAllTargets - else LoadUpTo (GHC.mkModuleName m) - return () - +reloadModule :: Bool -> String -> InputT GHCi () +reloadModule defer m = deferredLoad defer load + where load = doLoad True $ + if null m then LoadAllTargets else LoadUpTo (GHC.mkModuleName m) doLoad :: Bool -> LoadHowMuch -> InputT GHCi SuccessFlag doLoad retain_context howmuch = do diff --git a/testsuite/tests/ghci/scripts/Defer03.hs b/testsuite/tests/ghci/scripts/Defer03.hs new file mode 100755 index 0000000000..b91a7ac8d0 --- /dev/null +++ b/testsuite/tests/ghci/scripts/Defer03.hs @@ -0,0 +1,7 @@ +module Main where + +a :: Int +a = 'p' + +main :: IO () +main = print "No errors!" diff --git a/testsuite/tests/ghci/scripts/T8353.script b/testsuite/tests/ghci/scripts/T8353.script new file mode 100644 index 0000000000..d93fb6e4cb --- /dev/null +++ b/testsuite/tests/ghci/scripts/T8353.script @@ -0,0 +1,22 @@ +-- Test :load! and :reload! + +-- main is independent of functions with type errors and thus +-- executable after load +-- warnings on type errors are emitted nevertheless +:load! Defer03 + +-- fails to load module due to type errors +:load Defer03 + +-- succeeds again to load module +:reload! + +-- also succeeds, because the module has not been touched since load +:reload + +-- now, after touching, reloading should fail +:! touch Defer03.hs +:reload + +-- using the deferred version of reload, loading should succeed again +:reload! diff --git a/testsuite/tests/ghci/scripts/T8353.stderr b/testsuite/tests/ghci/scripts/T8353.stderr new file mode 100644 index 0000000000..7303142d10 --- /dev/null +++ b/testsuite/tests/ghci/scripts/T8353.stderr @@ -0,0 +1,25 @@ + +Defer03.hs:4:5: warning: + Couldn't match expected type ‘Int’ with actual type ‘Char’ + In the expression: 'p' + In an equation for ‘a’: a = 'p' + +Defer03.hs:4:5: error: + Couldn't match expected type ‘Int’ with actual type ‘Char’ + In the expression: 'p' + In an equation for ‘a’: a = 'p' + +Defer03.hs:4:5: warning: + Couldn't match expected type ‘Int’ with actual type ‘Char’ + In the expression: 'p' + In an equation for ‘a’: a = 'p' + +Defer03.hs:4:5: error: + Couldn't match expected type ‘Int’ with actual type ‘Char’ + In the expression: 'p' + In an equation for ‘a’: a = 'p' + +Defer03.hs:4:5: warning: + Couldn't match expected type ‘Int’ with actual type ‘Char’ + In the expression: 'p' + In an equation for ‘a’: a = 'p' diff --git a/testsuite/tests/ghci/scripts/all.T b/testsuite/tests/ghci/scripts/all.T index 747e708aad..1efa0099f1 100755 --- a/testsuite/tests/ghci/scripts/all.T +++ b/testsuite/tests/ghci/scripts/all.T @@ -171,6 +171,7 @@ test('T8113', normal, ghci_script, ['T8113.script']) test('T8172', when(opsys('mingw32'), normalise_drive_letter), ghci_script, ['T8172.script']) test('T8215', normal, ghci_script, ['T8215.script']) +test('T8353', normal, ghci_script, ['T8353.script']) test('T8357', normal, ghci_script, ['T8357.script']) test('T8383', normal, ghci_script, ['T8383.script']) test('T8469', normal, ghci_script, ['T8469.script']) -- GitLab