Commit 5d48e67f authored by triple's avatar triple Committed by thomie

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
parent 8e12a215
...@@ -101,6 +101,11 @@ ...@@ -101,6 +101,11 @@
commands now take an optional count allowing the user to move forward or commands now take an optional count allowing the user to move forward or
backward in history several steps at a time. backward in history several steps at a time.
</para> </para>
<para>
Added commands <literal>:load!</literal> and <literal>:reload!</literal>,
effectively setting "-fdefer-type-errors" before loading a module and
unsetting it after loading if it has not been set before (#8353).
</para>
</listitem> </listitem>
</itemizedlist> </itemizedlist>
</sect3> </sect3>
......
...@@ -674,13 +674,13 @@ Prelude> ...@@ -674,13 +674,13 @@ Prelude>
<title>What's really in scope at the prompt?</title> <title>What's really in scope at the prompt?</title>
<para>When you type an expression at the prompt, what <para>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 GHCi provides a flexible
way to control exactly how the context for an expression is way to control exactly how the context for an expression is
constructed: constructed:
<itemizedlist> <itemizedlist>
<listitem><para> <listitem><para>
The <literal>:load</literal>, <literal>:add</literal>, The <literal>:load</literal>, <literal>:add</literal>,
and <literal>:reload</literal> commands (<xref linkend="ghci-load-scope"/>). and <literal>:reload</literal> commands (<xref linkend="ghci-load-scope"/>).
</para></listitem> </para></listitem>
<listitem><para> <listitem><para>
...@@ -702,8 +702,8 @@ Prelude> ...@@ -702,8 +702,8 @@ Prelude>
<sect3 id="ghci-load-scope"> <sect3 id="ghci-load-scope">
<title>The effect of <literal>:load</literal> on what is in scope</title> <title>The effect of <literal>:load</literal> on what is in scope</title>
<para> <para>
The <literal>:load</literal>, <literal>:add</literal>, and <literal>:reload</literal> The <literal>:load</literal>, <literal>:add</literal>, and <literal>:reload</literal>
commands (<xref linkend="loading-source-files"/> commands (<xref linkend="loading-source-files"/>
and <xref linkend="ghci-compiled"/>) affect the top-level scope. and <xref linkend="ghci-compiled"/>) affect the top-level scope.
Let's start with the simple cases; when you start Let's start with the simple cases; when you start
GHCi the prompt looks like this: GHCi the prompt looks like this:
...@@ -830,7 +830,7 @@ Prelude System.IO Map> ...@@ -830,7 +830,7 @@ Prelude System.IO Map>
<title>Controlling what is in scope with the <literal>:module</literal> command</title> <title>Controlling what is in scope with the <literal>:module</literal> command</title>
<para>Another way to manipulate the scope is to use the <para>Another way to manipulate the scope is to use the
<literal>:module</literal> command, whose syntax is this: <literal>:module</literal> command, whose syntax is this:
<screen> <screen>
:module <optional>+|-</optional> <optional>*</optional><replaceable>mod<subscript>1</subscript></replaceable> ... <optional>*</optional><replaceable>mod<subscript>n</subscript></replaceable> :module <optional>+|-</optional> <optional>*</optional><replaceable>mod<subscript>1</subscript></replaceable> ... <optional>*</optional><replaceable>mod<subscript>n</subscript></replaceable>
...@@ -881,7 +881,7 @@ Prelude System.IO Map> ...@@ -881,7 +881,7 @@ Prelude System.IO Map>
<literal>:load</literal></title> <literal>:load</literal></title>
<para>It might seem that <literal>:module</literal>/<literal>import</literal> and <para>It might seem that <literal>:module</literal>/<literal>import</literal> and
<literal>:load</literal>/<literal>:add</literal>/<literal>:reload</literal> <literal>:load</literal>/<literal>:add</literal>/<literal>:reload</literal>
do similar things: you can use both do similar things: you can use both
to bring a module into scope. However, there is a very important to bring a module into scope. However, there is a very important
difference. GHCi is concerned with two sets of modules:</para> difference. GHCi is concerned with two sets of modules:</para>
...@@ -907,7 +907,7 @@ Prelude System.IO Map> ...@@ -907,7 +907,7 @@ Prelude System.IO Map>
</listitem> </listitem>
</itemizedlist> </itemizedlist>
<para>You can add a module to the scope (via <literal>:module</literal> <para>You can add a module to the scope (via <literal>:module</literal>
or <literal>import</literal>) or <literal>import</literal>)
only if either (a) it is loaded, or only if either (a) it is loaded, or
(b) it is a module from a package that GHCi knows about. (b) it is a module from a package that GHCi knows about.
...@@ -2627,7 +2627,7 @@ T Int :: * -> * ...@@ -2627,7 +2627,7 @@ T Int :: * -> *
<varlistentry> <varlistentry>
<term> <term>
<literal>:load</literal> <optional><literal>*</literal></optional><replaceable>module</replaceable> ... <literal>:load</literal><optional><literal>!</literal></optional> <optional><literal>*</literal></optional><replaceable>module</replaceable> ...
<indexterm><primary><literal>:load</literal></primary></indexterm> <indexterm><primary><literal>:load</literal></primary></indexterm>
</term> </term>
<listitem> <listitem>
...@@ -2649,6 +2649,15 @@ T Int :: * -> * ...@@ -2649,6 +2649,15 @@ T Int :: * -> *
byte-code. Using the <literal>*</literal> prefix forces a byte-code. Using the <literal>*</literal> prefix forces a
module to be loaded as byte-code.</para> module to be loaded as byte-code.</para>
<para>Adding the optional "<literal>!</literal>" 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 <xref linkend="defer-type-errors" /> for further
motivation and details.</para>
<para>After a <literal>:load</literal> command, the current <para>After a <literal>:load</literal> command, the current
context is set to:</para> context is set to:</para>
...@@ -2785,7 +2794,7 @@ bar ...@@ -2785,7 +2794,7 @@ bar
<varlistentry> <varlistentry>
<term> <term>
<literal>:reload</literal> <literal>:reload</literal><optional><literal>!</literal></optional>
<indexterm><primary><literal>:reload</literal></primary></indexterm> <indexterm><primary><literal>:reload</literal></primary></indexterm>
</term> </term>
<listitem> <listitem>
...@@ -2794,6 +2803,15 @@ bar ...@@ -2794,6 +2803,15 @@ bar
or any dependent module, has changed. Note that this may or any dependent module, has changed. Note that this may
entail loading new modules, or dropping modules which are no entail loading new modules, or dropping modules which are no
longer indirectly required by the target.</para> longer indirectly required by the target.</para>
<para>Adding the optional "<literal>!</literal>" 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 <xref linkend="defer-type-errors" /> for further
motivation and details.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -3302,7 +3320,7 @@ Prelude> :set -fno-warn-incomplete-patterns -XNoMultiParamTypeClasses ...@@ -3302,7 +3320,7 @@ Prelude> :set -fno-warn-incomplete-patterns -XNoMultiParamTypeClasses
<title>Setting options for interactive evaluation only</title> <title>Setting options for interactive evaluation only</title>
<para> <para>
GHCi actually maintains <emphasis>two</emphasis> sets of options: GHCi actually maintains <emphasis>two</emphasis> sets of options:
<itemizedlist> <itemizedlist>
<listitem><para> <listitem><para>
The <emphasis>loading options</emphasis> apply when loading modules The <emphasis>loading options</emphasis> apply when loading modules
...@@ -3317,7 +3335,7 @@ The <literal>:set</literal> command modifies both, but there is ...@@ -3317,7 +3335,7 @@ The <literal>:set</literal> command modifies both, but there is
</para> </para>
<para> <para>
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 without having that option apply to loaded modules
too. For example too. For example
<screen> <screen>
......
...@@ -172,13 +172,15 @@ ghciCommands = [ ...@@ -172,13 +172,15 @@ ghciCommands = [
("issafe", keepGoing' isSafeCmd, completeModule), ("issafe", keepGoing' isSafeCmd, completeModule),
("kind", keepGoing' (kindOfType False), completeIdentifier), ("kind", keepGoing' (kindOfType False), completeIdentifier),
("kind!", keepGoing' (kindOfType True), completeIdentifier), ("kind!", keepGoing' (kindOfType True), completeIdentifier),
("load", keepGoingPaths loadModule_, completeHomeModuleOrFile), ("load", keepGoingPaths (loadModule_ False), completeHomeModuleOrFile),
("load!", keepGoingPaths (loadModule_ True), completeHomeModuleOrFile),
("list", keepGoing' listCmd, noCompletion), ("list", keepGoing' listCmd, noCompletion),
("module", keepGoing moduleCmd, completeSetModule), ("module", keepGoing moduleCmd, completeSetModule),
("main", keepGoing runMain, completeFilename), ("main", keepGoing runMain, completeFilename),
("print", keepGoing printCmd, completeExpression), ("print", keepGoing printCmd, completeExpression),
("quit", quit, noCompletion), ("quit", quit, noCompletion),
("reload", keepGoing' reloadModule, noCompletion), ("reload", keepGoing' (reloadModule False), noCompletion),
("reload!", keepGoing' (reloadModule True), noCompletion),
("run", keepGoing runRun, completeFilename), ("run", keepGoing runRun, completeFilename),
("script", keepGoing' scriptCmd, completeFilename), ("script", keepGoing' scriptCmd, completeFilename),
("set", keepGoing setCmd, completeSetOptions), ("set", keepGoing setCmd, completeSetOptions),
...@@ -256,11 +258,13 @@ defFullHelpText = ...@@ -256,11 +258,13 @@ defFullHelpText =
" :issafe [<mod>] display safe haskell information of module <mod>\n" ++ " :issafe [<mod>] display safe haskell information of module <mod>\n" ++
" :kind[!] <type> show the kind of <type>\n" ++ " :kind[!] <type> show the kind of <type>\n" ++
" (!: also print the normalised type)\n" ++ " (!: also print the normalised type)\n" ++
" :load [*]<module> ... load module(s) and their dependents\n" ++ " :load[!] [*]<module> ... load module(s) and their dependents\n" ++
" (!: defer type errors)\n" ++
" :main [<arguments> ...] run the main function with the given arguments\n" ++ " :main [<arguments> ...] run the main function with the given arguments\n" ++
" :module [+/-] [*]<mod> ... set the context for expression evaluation\n" ++ " :module [+/-] [*]<mod> ... set the context for expression evaluation\n" ++
" :quit exit GHCi\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 [<arguments> ...] run the function with the given arguments\n" ++ " :run function [<arguments> ...] run the function with the given arguments\n" ++
" :script <filename> run the script <filename>\n" ++ " :script <filename> run the script <filename>\n" ++
" :type <expr> show the type of <expr>\n" ++ " :type <expr> show the type of <expr>\n" ++
...@@ -1272,7 +1276,7 @@ editFile str = ...@@ -1272,7 +1276,7 @@ editFile str =
code <- liftIO $ system (cmd ++ cmdArgs) code <- liftIO $ system (cmd ++ cmdArgs)
when (code == ExitSuccess) when (code == ExitSuccess)
$ reloadModule "" $ reloadModule False ""
-- The user didn't specify a file so we pick one for them. -- 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, -- Our strategy is to pick the first module that failed to load,
...@@ -1418,11 +1422,24 @@ checkModule m = do ...@@ -1418,11 +1422,24 @@ checkModule m = do
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- :load, :add, :reload -- :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 :: [(FilePath, Maybe Phase)] -> InputT GHCi SuccessFlag
loadModule fs = timeIt (const Nothing) (loadModule' fs) loadModule fs = timeIt (const Nothing) (loadModule' fs)
loadModule_ :: [FilePath] -> InputT GHCi () loadModule_ :: Bool -> [FilePath] -> InputT GHCi ()
loadModule_ fs = loadModule (zip fs (repeat Nothing)) >> return () loadModule_ defer fs = deferredLoad defer (loadModule (zip fs (repeat Nothing)))
loadModule' :: [(FilePath, Maybe Phase)] -> InputT GHCi SuccessFlag loadModule' :: [(FilePath, Maybe Phase)] -> InputT GHCi SuccessFlag
loadModule' files = do loadModule' files = do
...@@ -1460,13 +1477,10 @@ addModule files = do ...@@ -1460,13 +1477,10 @@ addModule files = do
-- :reload -- :reload
reloadModule :: String -> InputT GHCi () reloadModule :: Bool -> String -> InputT GHCi ()
reloadModule m = do reloadModule defer m = deferredLoad defer load
_ <- doLoad True $ where load = doLoad True $
if null m then LoadAllTargets if null m then LoadAllTargets else LoadUpTo (GHC.mkModuleName m)
else LoadUpTo (GHC.mkModuleName m)
return ()
doLoad :: Bool -> LoadHowMuch -> InputT GHCi SuccessFlag doLoad :: Bool -> LoadHowMuch -> InputT GHCi SuccessFlag
doLoad retain_context howmuch = do doLoad retain_context howmuch = do
......
module Main where
a :: Int
a = 'p'
main :: IO ()
main = print "No errors!"
-- 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!
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'
...@@ -171,6 +171,7 @@ test('T8113', normal, ghci_script, ['T8113.script']) ...@@ -171,6 +171,7 @@ test('T8113', normal, ghci_script, ['T8113.script'])
test('T8172', when(opsys('mingw32'), normalise_drive_letter), test('T8172', when(opsys('mingw32'), normalise_drive_letter),
ghci_script, ['T8172.script']) ghci_script, ['T8172.script'])
test('T8215', normal, ghci_script, ['T8215.script']) test('T8215', normal, ghci_script, ['T8215.script'])
test('T8353', normal, ghci_script, ['T8353.script'])
test('T8357', normal, ghci_script, ['T8357.script']) test('T8357', normal, ghci_script, ['T8357.script'])
test('T8383', normal, ghci_script, ['T8383.script']) test('T8383', normal, ghci_script, ['T8383.script'])
test('T8469', normal, ghci_script, ['T8469.script']) test('T8469', normal, ghci_script, ['T8469.script'])
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment