[commit: ghc] ghc-8.0: Fix a bug in ApplicativeDo (#11612) (e3020f2)

git at git.haskell.org git at git.haskell.org
Sat Feb 27 15:21:24 UTC 2016


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

On branch  : ghc-8.0
Link       : http://ghc.haskell.org/trac/ghc/changeset/e3020f26e322e0fcbc7ea2403479d6a734578bc8/ghc

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

commit e3020f26e322e0fcbc7ea2403479d6a734578bc8
Author: Simon Marlow <marlowsd at gmail.com>
Date:   Sat Feb 20 07:23:37 2016 +0000

    Fix a bug in ApplicativeDo (#11612)
    
    In some cases ApplicativeDo would miss some opportunities, due to a
    wrong calculation of free variables in RnExpr.segments.
    
    (cherry picked from commit 3259bf658662e7052ae91de2fa27baae8c84b7fa)


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

e3020f26e322e0fcbc7ea2403479d6a734578bc8
 compiler/rename/RnExpr.hs         | 27 +++++++++++++++++++++------
 testsuite/tests/ado/ado001.hs     | 12 ++++++++++++
 testsuite/tests/ado/ado001.stdout |  1 +
 3 files changed, 34 insertions(+), 6 deletions(-)

diff --git a/compiler/rename/RnExpr.hs b/compiler/rename/RnExpr.hs
index 616f259..9d1200a 100644
--- a/compiler/rename/RnExpr.hs
+++ b/compiler/rename/RnExpr.hs
@@ -1549,24 +1549,36 @@ segments stmts = map fst $ merge $ reverse $ map reverse $ walk (reverse stmts)
           _otherwise -> (seg,all_lets) : rest
       where
         rest = merge segs
-        all_lets = all (not . isBindStmt . fst) seg
+        all_lets = all (isLetStmt . fst) seg
 
+    -- walk splits the statement sequence into segments, traversing
+    -- the sequence from the back to the front, and keeping track of
+    -- the set of free variables of the current segment.  Whenever
+    -- this set of free variables is empty, we have a complete segment.
+    walk :: [(ExprLStmt Name, FreeVars)] -> [[(ExprLStmt Name, FreeVars)]]
     walk [] = []
     walk ((stmt,fvs) : stmts) = ((stmt,fvs) : seg) : walk rest
-      where (seg,rest) = chunter (fvs `intersectNameSet` allvars) stmts
+      where (seg,rest) = chunter fvs' stmts
+            (_, fvs') = stmtRefs stmt fvs
 
     chunter _ [] = ([], [])
     chunter vars ((stmt,fvs) : rest)
        | not (isEmptyNameSet vars)
        = ((stmt,fvs) : chunk, rest')
        where (chunk,rest') = chunter vars' rest
-             evars = fvs `intersectNameSet` allvars
-             pvars = mkNameSet (collectStmtBinders (unLoc stmt))
+             (pvars, evars) = stmtRefs stmt fvs
              vars' = (vars `minusNameSet` pvars) `unionNameSet` evars
     chunter _ rest = ([], rest)
 
-    isBindStmt (L _ BindStmt{}) = True
-    isBindStmt _ = False
+    stmtRefs stmt fvs
+      | isLetStmt stmt = (pvars, fvs' `minusNameSet` pvars)
+      | otherwise      = (pvars, fvs')
+      where fvs' = fvs `intersectNameSet` allvars
+            pvars = mkNameSet (collectStmtBinders (unLoc stmt))
+
+isLetStmt :: LStmt a b -> Bool
+isLetStmt (L _ LetStmt{}) = True
+isLetStmt _ = False
 
 -- | Find a "good" place to insert a bind in an indivisible segment.
 -- This is the only place where we use heuristics.  The current
@@ -1576,6 +1588,9 @@ splitSegment
   :: [(ExprLStmt Name, FreeVars)]
   -> ( [(ExprLStmt Name, FreeVars)]
      , [(ExprLStmt Name, FreeVars)] )
+splitSegment [one,two] = ([one],[two])
+  -- there is no choice when there are only two statements; this just saves
+  -- some work in a common case.
 splitSegment stmts
   | Just (lets,binds,rest) <- slurpIndependentStmts stmts
   =  if not (null lets)
diff --git a/testsuite/tests/ado/ado001.hs b/testsuite/tests/ado/ado001.hs
index 9f8f8da..e452cdd 100644
--- a/testsuite/tests/ado/ado001.hs
+++ b/testsuite/tests/ado/ado001.hs
@@ -109,6 +109,17 @@ test10 = do
   x5 <- e
   return (const () (x3,x4,x5))
 
+-- (a | b)
+-- This demonstrated a bug in RnExpr.segments (#11612)
+test11 :: M ()
+test11 = do
+  x1 <- a
+  let x2 = x1
+  x3 <- b
+  let x4 = c
+      x5 = x4
+  return (const () (x1,x2,x3,x4))
+
 main = mapM_ run
  [ test1
  , test2
@@ -120,6 +131,7 @@ main = mapM_ run
  , test8
  , test9
  , test10
+ , test11
  ]
 
 -- Testing code, prints out the structure of a monad/applicative expression
diff --git a/testsuite/tests/ado/ado001.stdout b/testsuite/tests/ado/ado001.stdout
index 93e300c..f7c48ca 100644
--- a/testsuite/tests/ado/ado001.stdout
+++ b/testsuite/tests/ado/ado001.stdout
@@ -8,3 +8,4 @@ a; (b | (c; (d; (e | (f; g)))))
 a; ((b | c) | d)
 ((a | (b; c)) | d) | e
 ((a | b); (c | d)) | e
+a | b



More information about the ghc-commits mailing list