[commit: ghc] master: Comments about stricteness of catch# (e161634)

git at git.haskell.org git at git.haskell.org
Fri Jul 24 11:31:02 UTC 2015


Repository : ssh://git@git.haskell.org/ghc

On branch  : master
Link       : http://ghc.haskell.org/trac/ghc/changeset/e1616343de946f872fb0da7689ff242dc345793f/ghc

>---------------------------------------------------------------

commit e1616343de946f872fb0da7689ff242dc345793f
Author: Simon Peyton Jones <simonpj at microsoft.com>
Date:   Fri Jul 24 10:32:01 2015 +0100

    Comments about stricteness of catch#
    
    In "Improve strictness analysis for exceptions"
       commit 7c0fff41789669450b02dc1db7f5d7babba5dee6
       Author: Simon Peyton Jones <simonpj at microsoft.com>
       Date:   Tue Jul 21 12:28:42 2015 +0100
    
    I made catch# strict in its first argument.  But today I found
    a very old comment suggesting the opposite.  I disagree with the
    old comment, but I've elaborated the Note, which I reproduce here:
    
        {- Note [Strictness for mask/unmask/catch]
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        Consider this example, which comes from GHC.IO.Handle.Internals:
           wantReadableHandle3 f ma b st
             = case ... of
                 DEFAULT -> case ma of MVar a -> ...
                 0#      -> maskAsynchExceptions#
                               (\st -> case ma of MVar a -> ...)
        The outer case just decides whether to mask exceptions, but we
        don't want thereby to hide the strictness in 'ma'!  Hence the use
        of strictApply1Dmd.
    
        For catch, we know that the first branch will be evaluated, but
        not necessarily the second.  Hence strictApply1Dmd and
        lazyApply1Dmd
    
        Howver, consider
            catch# (\st -> case x of ...) (..handler..) st
        We'll see that the entire thing is strict in 'x', so 'x' may be
        evaluated before the catch#.  So fi evaluting 'x' causes a
        divide-by-zero exception, it won't be caught.  This seems
        acceptable:
          - x might be evaluated somewhere else outside the catch# anyway
          - It's an imprecise eception anyway.  Synchronous exceptions (in
            the IO monad) will never move in this way.
        There was originally a comment
          "Catch is actually strict in its first argument
           but we don't want to tell the strictness
           analyser about that, so that exceptions stay inside it."
        but tracing it back through the commit logs did not give any
        rationale.  And making catch# lazy has performance costs for
        everyone.


>---------------------------------------------------------------

e1616343de946f872fb0da7689ff242dc345793f
 compiler/prelude/primops.txt.pp | 43 +++++++++++++++++++++++++++--------------
 1 file changed, 28 insertions(+), 15 deletions(-)

diff --git a/compiler/prelude/primops.txt.pp b/compiler/prelude/primops.txt.pp
index c29e9d8..9b107f2 100644
--- a/compiler/prelude/primops.txt.pp
+++ b/compiler/prelude/primops.txt.pp
@@ -1919,18 +1919,34 @@ primop  CasMutVarOp "casMutVar#" GenPrimOp
 section "Exceptions"
 ------------------------------------------------------------------------
 
--- Note [Strictness for mask/unmask/catch]
--- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--- Consider this example, which comes from GHC.IO.Handle.Internals:
---    wantReadableHandle3 f ma b st
---      = case ... of
---          DEFAULT -> case ma of MVar a -> ...
---          0#      -> maskAsynchExceptions# (\st -> case ma of MVar a -> ...)
--- The outer case just decides whether to mask exceptions, but we don't want
--- thereby to hide the strictness in 'ma'!  Hence the use of strictApply1Dmd.
---
--- For catch, we know that the first branch will be evaluated, but not
--- necessarily the second.  Hence strictApply1Dmd and lazyApply1Dmd
+{- Note [Strictness for mask/unmask/catch]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Consider this example, which comes from GHC.IO.Handle.Internals:
+   wantReadableHandle3 f ma b st
+     = case ... of
+         DEFAULT -> case ma of MVar a -> ...
+         0#      -> maskAsynchExceptions# (\st -> case ma of MVar a -> ...)
+The outer case just decides whether to mask exceptions, but we don't want
+thereby to hide the strictness in 'ma'!  Hence the use of strictApply1Dmd.
+
+For catch, we know that the first branch will be evaluated, but not
+necessarily the second.  Hence strictApply1Dmd and lazyApply1Dmd
+
+Howver, consider
+    catch# (\st -> case x of ...) (..handler..) st
+We'll see that the entire thing is strict in 'x', so 'x' may be evaluated
+before the catch#.  So fi evaluting 'x' causes a divide-by-zero exception,
+it won't be caught.  This seems acceptable:
+  - x might be evaluated somewhere else outside the catch# anyway
+  - It's an imprecise eception anyway.  Synchronous exceptions (in the
+    IO monad) will never move in this way.
+There was originally a comment
+  "Catch is actually strict in its first argument
+   but we don't want to tell the strictness
+   analyser about that, so that exceptions stay inside it."
+but tracing it back through the commit logs did not give any
+rationale.  And making catch# lazy has performance costs for everyone.
+-}
 
 primop  CatchOp "catch#" GenPrimOp
           (State# RealWorld -> (# State# RealWorld, a #) )
@@ -1938,9 +1954,6 @@ primop  CatchOp "catch#" GenPrimOp
        -> State# RealWorld
        -> (# State# RealWorld, a #)
    with
-        -- Catch is actually strict in its first argument
-        -- but we don't want to tell the strictness
-        -- analyser about that, so that exceptions stay inside it.
    strictness  = { \ _arity -> mkClosedStrictSig [strictApply1Dmd,lazyApply2Dmd,topDmd] topRes }
                  -- See Note [Strictness for mask/unmask/catch]
    out_of_line = True



More information about the ghc-commits mailing list