Commit 2cc854b7 authored by Merijn Verstraaten's avatar Merijn Verstraaten Committed by Austin Seipp

Add -fdefer-typed-holes flag which defers hole errors to runtime.

Summary:
As proposed by Richard on Trac. This patch adds a new flag -fdefer-typed-holes
and changes the semantics of the -fno-warn-typed-holes flag.

To summarise, by default GHC has typed holes enabled and produces a compile
error when it encounters a typed hole.

When -fdefer-type-errors OR -fdefer-typed-holes is enabled, hole errors are
converted to warnings and result in runtime errors when evaluated.

The warning flag -fwarn-typed-holes is on by default. Without -fdefer-type-errors
or -fdefer-typed-holes this flag is a no-op, since typed holes are an error
under these conditions. If either of the defer flags are enabled (converting
typed hole errors into warnings) the -fno-warn-typed-holes flag disables the
warnings. This means compilation silently succeeds and evaluating a hole will
produce a runtime error.

The rationale behind allowing typed holes warnings to be silenced is that tools
like Syntastic for vim highlight warnings and hole warnings may be undesirable.
Signed-off-by: Merijn Verstraaten's avatarMerijn Verstraaten <merijn@inconsistent.nl>

Test Plan: validate

Reviewers: austin, simonpj, thomie

Reviewed By: simonpj, thomie

Subscribers: Fuuzetsu, thomie, carter

Differential Revision: https://phabricator.haskell.org/D442

GHC Trac Issues: #9497

Conflicts:
	compiler/main/DynFlags.hs
parent 624a7c5a
......@@ -396,6 +396,7 @@ data GeneralFlag
| Opt_GhciHistory
| Opt_HelpfulErrors
| Opt_DeferTypeErrors
| Opt_DeferTypedHoles
| Opt_Parallel
| Opt_GranMacros
| Opt_PIC
......@@ -2769,6 +2770,7 @@ fFlags = [
flagSpec ( "cmm-sink", Opt_CmmSink, nop ),
flagSpec ( "cse", Opt_CSE, nop ),
flagSpec ( "defer-type-errors", Opt_DeferTypeErrors, nop ),
flagSpec ( "defer-typed-holes", Opt_DeferTypedHoles, nop ),
flagSpec ( "dicts-cheap", Opt_DictsCheap, nop ),
flagSpec ( "dicts-strict", Opt_DictsStrict, nop ),
flagSpec ( "dmd-tx-dict-sel", Opt_DmdTxDictSel, nop ),
......@@ -3053,8 +3055,11 @@ default_PIC platform =
(OSDarwin, ArchX86_64) -> [Opt_PIC]
_ -> []
impliedFlags :: [(ExtensionFlag, TurnOnFlag, ExtensionFlag)]
impliedFlags
impliedGFlags :: [(GeneralFlag, TurnOnFlag, GeneralFlag)]
impliedGFlags = [(Opt_DeferTypeErrors, turnOn, Opt_DeferTypedHoles)]
impliedXFlags :: [(ExtensionFlag, TurnOnFlag, ExtensionFlag)]
impliedXFlags
-- See Note [Updating flag description in the User's Guide]
= [ (Opt_RankNTypes, turnOn, Opt_ExplicitForAll)
, (Opt_ScopedTypeVariables, turnOn, Opt_ExplicitForAll)
......@@ -3399,9 +3404,18 @@ setGeneralFlag f = upd (setGeneralFlag' f)
unSetGeneralFlag f = upd (unSetGeneralFlag' f)
setGeneralFlag' :: GeneralFlag -> DynFlags -> DynFlags
setGeneralFlag' f dflags = gopt_set dflags f
setGeneralFlag' f dflags = foldr ($) (gopt_set dflags f) deps
where
deps = [ if turn_on then setGeneralFlag' d
else unSetGeneralFlag' d
| (f', turn_on, d) <- impliedGFlags, f' == f ]
-- When you set f, set the ones it implies
-- NB: use setGeneralFlag recursively, in case the implied flags
-- implies further flags
unSetGeneralFlag' :: GeneralFlag -> DynFlags -> DynFlags
unSetGeneralFlag' f dflags = gopt_unset dflags f
-- When you un-set f, however, we don't un-set the things it implies
--------------------------
setWarningFlag, unSetWarningFlag :: WarningFlag -> DynP ()
......@@ -3418,7 +3432,7 @@ setExtensionFlag' f dflags = foldr ($) (xopt_set dflags f) deps
where
deps = [ if turn_on then setExtensionFlag' d
else unSetExtensionFlag' d
| (f', turn_on, d) <- impliedFlags, f' == f ]
| (f', turn_on, d) <- impliedXFlags, f' == f ]
-- When you set f, set the ones it implies
-- NB: use setExtensionFlag recursively, in case the implied flags
-- implies further flags
......
......@@ -88,8 +88,7 @@ finishHsVar name
rnExpr (HsVar v)
= do { mb_name <- lookupOccRn_maybe v
; case mb_name of {
Nothing -> do { opt_TypeHoles <- woptM Opt_WarnTypedHoles
; if opt_TypeHoles && startsWithUnderscore (rdrNameOcc v)
Nothing -> do { if startsWithUnderscore (rdrNameOcc v)
then return (HsUnboundVar v, emptyFVs)
else do { n <- reportUnboundName v; finishHsVar n } } ;
Just name
......@@ -300,11 +299,7 @@ Since all the symbols are reservedops we can simply reject them.
We return a (bogus) EWildPat in each case.
\begin{code}
rnExpr e@EWildPat = do { holes <- woptM Opt_WarnTypedHoles
; if holes
then return (hsHoleExpr, emptyFVs)
else patSynErr e
}
rnExpr EWildPat = return (hsHoleExpr, emptyFVs)
rnExpr e@(EAsPat {}) = patSynErr e
rnExpr e@(EViewPat {}) = patSynErr e
rnExpr e@(ELazyPat {}) = patSynErr e
......
......@@ -1881,7 +1881,7 @@ simplifyDeriv pred tvs theta
where p = ctPred ct
-- If we are deferring type errors, simply ignore any insoluble
-- constraints. Tney'll come up again when we typecheck the
-- constraints. They'll come up again when we typecheck the
-- generated instance declaration
; defer <- goptM Opt_DeferTypeErrors
; unless defer (reportAllUnsolved (residual_wanted { wc_flat = bad }))
......
......@@ -43,6 +43,7 @@ import DynFlags
import StaticFlags ( opt_PprStyle_Debug )
import ListSetOps ( equivClasses )
import Control.Monad ( when )
import Data.Maybe
import Data.List ( partition, mapAccumL, zip4, nub, sortBy )
\end{code}
......@@ -98,22 +99,29 @@ compilation. The errors are turned into warnings in `reportUnsolved`.
reportUnsolved :: WantedConstraints -> TcM (Bag EvBind)
reportUnsolved wanted
= do { binds_var <- newTcEvBinds
; defer <- goptM Opt_DeferTypeErrors
; report_unsolved (Just binds_var) defer wanted
; defer_errors <- goptM Opt_DeferTypeErrors
; defer_holes <- goptM Opt_DeferTypedHoles
; warn_holes <- woptM Opt_WarnTypedHoles
; report_unsolved (Just binds_var) defer_errors defer_holes
warn_holes wanted
; getTcEvBinds binds_var }
reportAllUnsolved :: WantedConstraints -> TcM ()
-- Report all unsolved goals, even if -fdefer-type-errors is on
-- See Note [Deferring coercion errors to runtime]
reportAllUnsolved wanted = report_unsolved Nothing False wanted
reportAllUnsolved wanted = do
warn_holes <- woptM Opt_WarnTypedHoles
report_unsolved Nothing False False warn_holes wanted
report_unsolved :: Maybe EvBindsVar -- cec_binds
-> Bool -- cec_defer
-> Bool -- cec_defer_type_errors
-> Bool -- cec_defer_holes
-> Bool -- cec_warn_holes
-> WantedConstraints -> TcM ()
-- Important precondition:
-- WantedConstraints are fully zonked and unflattened, that is,
-- zonkWC has already been applied to these constraints.
report_unsolved mb_binds_var defer wanted
report_unsolved mb_binds_var defer_errors defer_holes warn_holes wanted
| isEmptyWC wanted
= return ()
| otherwise
......@@ -127,7 +135,9 @@ report_unsolved mb_binds_var defer wanted
free_tvs = tyVarsOfWC wanted
err_ctxt = CEC { cec_encl = []
, cec_tidy = tidy_env
, cec_defer = defer
, cec_defer_type_errors = defer_errors
, cec_defer_holes = defer_holes
, cec_warn_holes = warn_holes
, cec_suppress = False -- See Note [Suppressing error messages]
, cec_binds = mb_binds_var }
......@@ -152,8 +162,16 @@ data ReportErrCtxt
-- into warnings, and emit evidence bindings
-- into 'ev' for unsolved constraints
, cec_defer :: Bool -- True <=> -fdefer-type-errors
-- Irrelevant if cec_binds = Nothing
, cec_defer_type_errors :: Bool -- True <=> -fdefer-type-errors
-- Defer type errors until runtime
-- Irrelevant if cec_binds = Nothing
, cec_defer_holes :: Bool -- True <=> -fdefer-typed-holes
-- Turn typed holes into runtime errors
-- Irrelevant if cec_binds = Nothing
, cec_warn_holes :: Bool -- True <=> -fwarn-typed-holes
-- Controls whether holes produce warnings
, cec_suppress :: Bool -- True <=> More important errors have occurred,
-- so create bindings if need be, but
-- don't issue any more errors/warnings
......@@ -231,7 +249,7 @@ reportFlats ctxt flats -- Here 'flats' includes insolble goals
-- Like Int ~ Bool (incl nullary TyCons)
-- or Int ~ t a (AppTy on one side)
("Utterly wrong", utterly_wrong, True, mkGroupReporter mkEqErr)
, ("Holes", is_hole, True, mkUniReporter mkHoleError)
, ("Holes", is_hole, True, mkHoleReporter mkHoleError)
-- Report equalities of form (a~ty). They are usually
-- skolem-equalities, and they cause confusing knock-on
......@@ -318,13 +336,13 @@ mkSkolReporter ctxt cts
(EqPred ty1 _, EqPred ty2 _) -> ty1 `cmpType` ty2
_ -> pprPanic "mkSkolReporter" (ppr ct1 $$ ppr ct2)
mkUniReporter :: (ReportErrCtxt -> Ct -> TcM ErrMsg) -> Reporter
mkHoleReporter :: (ReportErrCtxt -> Ct -> TcM ErrMsg) -> Reporter
-- Reports errors one at a time
mkUniReporter mk_err ctxt
mkHoleReporter mk_err ctxt
= mapM_ $ \ct ->
do { err <- mk_err ctxt ct
; maybeReportError ctxt err
; maybeAddDeferredBinding ctxt err ct }
; maybeReportHoleError ctxt err
; maybeAddDeferredHoleBinding ctxt err ct }
mkGroupReporter :: (ReportErrCtxt -> [Ct] -> TcM ErrMsg)
-- Make error message for a group
......@@ -345,22 +363,30 @@ reportGroup mk_err ctxt cts
-- Add deferred bindings for all
-- But see Note [Always warn with -fdefer-type-errors]
maybeReportHoleError :: ReportErrCtxt -> ErrMsg -> TcM ()
maybeReportHoleError ctxt err
| cec_defer_holes ctxt
= when (cec_warn_holes ctxt)
(reportWarning (makeIntoWarning err))
| otherwise
= reportError err
maybeReportError :: ReportErrCtxt -> ErrMsg -> TcM ()
-- Report the error and/or make a deferred binding for it
maybeReportError ctxt err
| cec_defer ctxt -- See Note [Always warn with -fdefer-type-errors]
-- See Note [Always warn with -fdefer-type-errors]
| cec_defer_type_errors ctxt
= reportWarning (makeIntoWarning err)
| cec_suppress ctxt
= return ()
| otherwise
= reportError err
maybeAddDeferredBinding :: ReportErrCtxt -> ErrMsg -> Ct -> TcM ()
addDeferredBinding :: ReportErrCtxt -> ErrMsg -> Ct -> TcM ()
-- See Note [Deferring coercion errors to runtime]
maybeAddDeferredBinding ctxt err ct
addDeferredBinding ctxt err ct
| CtWanted { ctev_pred = pred, ctev_evar = ev_id } <- ctEvidence ct
-- Only add deferred bindings for Wanted constraints
, isHoleCt ct || cec_defer ctxt -- And it's a hole or we have -fdefer-type-errors
, Just ev_binds_var <- cec_binds ctxt -- We have somewhere to put the bindings
= do { dflags <- getDynFlags
; let err_msg = pprLocErrMsg err
......@@ -373,6 +399,20 @@ maybeAddDeferredBinding ctxt err ct
| otherwise -- Do not set any evidence for Given/Derived
= return ()
maybeAddDeferredHoleBinding :: ReportErrCtxt -> ErrMsg -> Ct -> TcM ()
maybeAddDeferredHoleBinding ctxt err ct
| cec_defer_holes ctxt
= addDeferredBinding ctxt err ct
| otherwise
= return ()
maybeAddDeferredBinding :: ReportErrCtxt -> ErrMsg -> Ct -> TcM ()
maybeAddDeferredBinding ctxt err ct
| cec_defer_type_errors ctxt
= addDeferredBinding ctxt err ct
| otherwise
= return ()
tryReporters :: [ReporterSpec] -> Reporter -> Reporter
-- Use the first reporter in the list whose predicate says True
tryReporters reporters deflt ctxt cts
......
......@@ -1535,7 +1535,7 @@ runPlans [p] = p
runPlans (p:ps) = tryTcLIE_ (runPlans ps) p
-- | Typecheck (and 'lift') a stmt entered by the user in GHCi into the
-- GHCi 'environemnt'.
-- GHCi 'environment'.
--
-- By 'lift' and 'environment we mean that the code is changed to
-- execute properly in an IO monad. See Note [Interactively-bound Ids
......
......@@ -1363,11 +1363,28 @@
<row>
<entry><option>-fdefer-type-errors</option></entry>
<entry>Defer as many type errors as possible until runtime.</entry>
<entry>
Turn type errors into warnings, <link linkend="defer-type-errors">
deferring the error until runtime</link>. Implies
<option>-fdefer-typed-holes</option>.
</entry>
<entry>dynamic</entry>
<entry><option>-fno-defer-type-errors</option></entry>
</row>
<row>
<entry><option>-fdefer-typed-holes</option></entry>
<entry>
Convert <link linkend="typed-holes">typed hole</link> errors
into warnings, <link linkend="defer-type-errors">deferring the
error until runtime</link>. Implied by
<option>-fdefer-type-errors</option>. See also
<option>-fwarn-typed-holes</option>.
</entry>
<entry>dynamic</entry>
<entry><option>-fno-defer-typed-holes</option></entry>
</row>
<row>
<entry><option>-fhelpful-errors</option></entry>
<entry>Make suggestions for mis-spelled names.</entry>
......@@ -1626,7 +1643,11 @@
<row>
<entry><option>-fwarn-typed-holes</option></entry>
<entry>Enable <link linkend="typed-holes">holes</link> in expressions.</entry>
<entry>
Report warnings when <link linkend="typed-holes">typed hole</link>
errors are <link linkend="defer-type-errors">deferred until
runtime</link>. See <option>-fdefer-typed-holes</option>.
</entry>
<entry>dynamic</entry>
<entry><option>-fno-warn-typed-holes</option></entry>
</row>
......
......@@ -3,7 +3,7 @@
<indexterm><primary>language, GHC</primary></indexterm>
<indexterm><primary>extensions, GHC</primary></indexterm>
As with all known Haskell systems, GHC implements some extensions to
the language. They can all be enabled or disabled by commandline flags
the language. They can all be enabled or disabled by command line flags
or language pragmas. By default GHC understands the most recent Haskell
version it supports, plus a handful of extensions.
</para>
......@@ -8423,31 +8423,37 @@ with <option>-XNoMonoLocalBinds</option> but type inference becomes less predica
<sect1 id="typed-holes">
<title>Typed Holes</title>
<para>Typed hole support is enabled with the option
<option>-fwarn-typed-holes</option>, which is enabled by default.</para>
<para>
This option allows special placeholders, written with a leading underscore (e.g. "<literal>_</literal>",
"<literal>_foo</literal>", "<literal>_bar</literal>"), to be used as an expression.
During compilation these holes will generate an error message describing what type is expected there,
information about the origin of any free type variables, and a list of local bindings
that might help fill the hole with actual code.
Typed holes are a feature of GHC that allows special placeholders written with
a leading underscore (e.g., "<literal>_</literal>", "<literal>_foo</literal>",
"<literal>_bar</literal>"), to be used as expressions. During compilation these
holes will generate an error message that describes which type is expected at
the hole's location, information about the origin of any free type variables,
and a list of local bindings that might help fill the hole with actual code.
Typed holes are always enabled in GHC.
</para>
<para>
The goal of the typed holes warning is not to change the type system, but to help with writing Haskell
code. Typed holes can be used to obtain extra information from the type checker, which might otherwise be hard
to get.
Normally, using GHCi, users can inspect the (inferred) type signatures of all top-level bindings.
However, this method is less convenient with terms which are not defined on top-level or
inside complex expressions. Holes allow to check the type of the term you're about to write.
The goal of typed holes is to help with writing Haskell code rather than to
change the type system. Typed holes can be used to obtain extra information
from the type checker, which might otherwise be hard to get. Normally, using
GHCi, users can inspect the (inferred) type signatures of all top-level
bindings. However, this method is less convenient with terms that are not
defined on top-level or inside complex expressions. Holes allow the user to
check the type of the term they are about to write.
</para>
<para>
Holes work together well with <link linkend="defer-type-errors">deferring type errors to runtime</link>:
with <literal>-fdefer-type-errors</literal>, the error from a hole is also deferred, effctively making the hole
typecheck just like <literal>undefined</literal>, but with the added benefit that it will show its warning message
if it gets evaluated. This way, other parts of the code can still be executed and tested.
To run and test a piece of code containing holes, use the
<literal>-fdefer-typed-holes</literal> flag. This flag defers errors
produced by typed holes and converts them into warnings. The result is that
typed hole errors are converted into warnings (controlled by
<literal>-fwarn-typed-holes</literal>). The result is that a hole will behave
like <literal>undefined</literal>, but with the added benefits that it shows a
warning at compile time and will show another warning message if it gets
evaluated. This behaviour follows that of the
<literal>-fdefer-type-errors</literal> option, which implies
<literal>-fdefer-typed-holes</literal>. See <xref linkend="defer-type-errors"/>.
</para>
<para>
......@@ -8557,6 +8563,15 @@ main = print "b"
errors are deferred to runtime. Type errors will still be emitted as
warnings, but will not prevent compilation.
</para>
<para>
This flag implies the <literal>-fdefer-typed-holes</literal> flag,
which enables this behaviour for <link linkend="typed-holes">typed holes
</link>. Should you so wish, it is possible to enable
<literal>-fdefer-type-errors</literal> without enabling
<literal>-fdefer-typed-holes</literal>, by explicitly specifying
<literal>-fno-defer-typed-holes</literal> on the commandline after the
<literal>-fdefer-type-errors</literal> flag.
</para>
<para>
At runtime, whenever a term containing a type error would need to be
evaluated, the error is converted into a runtime exception.
......
......@@ -1131,14 +1131,11 @@ test.hs:(5,4)-(6,7):
<indexterm><primary><option>-fwarn-typed-holes</option></primary>
</indexterm>
<indexterm><primary>warnings</primary></indexterm>
<para>When the compiler encounters an unbound local
variable prefixed with <literal>_</literal>, or encounters
the literal <literal>_</literal> on the right-hand side of
an expression, the error message for the unbound term
includes the type it needs to type check. It works
particularly well with <link
linkend="defer-type-errors">deferred type errors</link>.
See <xref linkend="typed-holes"/></para>
<para>
Determines whether the compiler reports typed holes warnings. Has
no effect unless typed holes errors are deferred until runtime.
See <xref linkend="typed-holes"/> and <xref linkend="defer-type-errors"/>
</para>
<para>This warning is on by default.</para>
</listitem>
......@@ -1159,6 +1156,27 @@ test.hs:(5,4)-(6,7):
</listitem>
</varlistentry>
<varlistentry>
<term><option>-fdefer-typed-holes</option>:</term>
<listitem>
<indexterm><primary><option>-fdefer-typed-holes</option></primary>
</indexterm>
<indexterm><primary>warnings</primary></indexterm>
<para>
Defer typed holes errors until runtime. This will turn the errors
produced by <link linked="typed-holes">typed holes</link> into
warnings. Using a value that depends on a typed hole produces a
runtime error, the same as <option>-fdefer-type-errors</option>
(which implies this option). See <xref linkend="typed-holes"/>
and <xref linkend="defer-type-errors"/>.
</para>
<para>
Implied by <option>-fdefer-type-errors</option>. See also
<option>-fwarn-typed-holes</option>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-fhelpful-errors</option>:</term>
<listitem>
......
mod71.hs:4:9: Pattern syntax in expression context: _
mod71.hs:4:9:
Found hole ‘_’ with type: t1
Where: ‘t1’ is a rigid type variable bound by
the inferred type of f :: (t1 -> a -> t) -> t at mod71.hs:4:1
Relevant bindings include
x :: t1 -> a -> t (bound at mod71.hs:4:3)
f :: (t1 -> a -> t) -> t (bound at mod71.hs:4:1)
In the first argument of ‘x’, namely ‘_’
In the expression: x _ 1
In an equation for ‘f’: f x = x _ 1
......@@ -2,5 +2,3 @@
rnfail016.hs:6:7: Pattern syntax in expression context: x@x
rnfail016.hs:7:7: Pattern syntax in expression context: ~x
rnfail016.hs:8:7: Pattern syntax in expression context: _
T9497a.hs:2:8: Warning:
Found hole ‘_main’ with type: IO ()
Relevant bindings include main :: IO () (bound at T9497a.hs:2:1)
In the expression: _main
In an equation for ‘main’: main = _main
......@@ -428,3 +428,6 @@ test('T9404b', normal, compile, [''])
test('T7220', normal, compile, [''])
test('T7220a', normal, compile_fail, [''])
test('T9151', normal, compile, [''])
test('T9497a', normal, compile, ['-fdefer-typed-holes'])
test('T9497b', normal, compile, ['-fdefer-typed-holes -fno-warn-typed-holes'])
test('T9497c', normal, compile, ['-fdefer-type-errors -fno-warn-typed-holes'])
T9497d.hs:2:8:
Found hole ‘_main’ with type: IO ()
Relevant bindings include main :: IO () (bound at T9497d.hs:2:1)
In the expression: _main
In an equation for ‘main’: main = _main
......@@ -343,3 +343,4 @@ test('T9774', normal, compile_fail, [''])
test('T9318', normal, compile_fail, [''])
test('T9201', normal, compile_fail, [''])
test('T9109', normal, compile_fail, [''])
test('T9497d', normal, compile_fail, ['-fdefer-type-errors -fno-defer-typed-holes'])
T9497a-run: T9497a-run.hs:2:8:
Found hole ‘_main’ with type: IO ()
Relevant bindings include
main :: IO () (bound at T9497a-run.hs:2:1)
In the expression: _main
In an equation for ‘main’: main = _main
(deferred type error)
T9497b-run: T9497b-run.hs:2:8:
Found hole ‘_main’ with type: IO ()
Relevant bindings include
main :: IO () (bound at T9497b-run.hs:2:1)
In the expression: _main
In an equation for ‘main’: main = _main
(deferred type error)
T9497c-run: T9497c-run.hs:2:8:
Found hole ‘_main’ with type: IO ()
Relevant bindings include
main :: IO () (bound at T9497c-run.hs:2:1)
In the expression: _main
In an equation for ‘main’: main = _main
(deferred type error)
......@@ -112,3 +112,6 @@ test('TcTypeNatSimpleRun', normal, compile_and_run, [''])
test('T8119', normal, ghci_script, ['T8119.script'])
test('T8492', normal, compile_and_run, [''])
test('T8739', normal, compile_and_run, [''])
test('T9497a-run', [exit_code(1)], compile_and_run, ['-fdefer-typed-holes'])
test('T9497b-run', [exit_code(1)], compile_and_run, ['-fdefer-typed-holes -fno-warn-typed-holes'])
test('T9497c-run', [exit_code(1)], compile_and_run, ['-fdefer-type-errors -fno-warn-typed-holes'])
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