summaryrefslogtreecommitdiff
path: root/compiler/rename/RnExpr.hs
diff options
context:
space:
mode:
authorSimon Marlow <marlowsd@gmail.com>2017-06-29 19:39:45 -0400
committerBen Gamari <ben@smart-cactus.org>2017-06-29 19:39:46 -0400
commit1ef4156e45dcb258f6ef05cfb909547b8e3beb0f (patch)
tree223ba08829f2da6c62d21116358cfe725ec4b353 /compiler/rename/RnExpr.hs
parent9b514dedf090c5e21e3be38d174cf1390e21879f (diff)
downloadhaskell-1ef4156e45dcb258f6ef05cfb909547b8e3beb0f.tar.gz
Prevent ApplicativeDo from applying to strict pattern matches (#13875)
Test Plan: * New unit tests * validate Reviewers: dfeuer, simonpj, niteria, bgamari, austin, erikd Reviewed By: dfeuer Subscribers: rwbarton, thomie GHC Trac Issues: #13875 Differential Revision: https://phabricator.haskell.org/D3681
Diffstat (limited to 'compiler/rename/RnExpr.hs')
-rw-r--r--compiler/rename/RnExpr.hs62
1 files changed, 56 insertions, 6 deletions
diff --git a/compiler/rename/RnExpr.hs b/compiler/rename/RnExpr.hs
index 2c779d2853..c5c75ab671 100644
--- a/compiler/rename/RnExpr.hs
+++ b/compiler/rename/RnExpr.hs
@@ -1635,12 +1635,8 @@ stmtTreeToStmts
-- the bind form, which would give rise to a Monad constraint.
stmtTreeToStmts monad_names ctxt (StmtTreeOne (L _ (BindStmt pat rhs _ _ _),_))
tail _tail_fvs
- | isIrrefutableHsPat pat, (False,tail') <- needJoin monad_names tail
- -- WARNING: isIrrefutableHsPat on (HsPat Name) doesn't have enough info
- -- to know which types have only one constructor. So only
- -- tuples come out as irrefutable; other single-constructor
- -- types, and newtypes, will not. See the code for
- -- isIrrefuatableHsPat
+ | not (isStrictPattern pat), (False,tail') <- needJoin monad_names tail
+ -- See Note [ApplicativeDo and strict patterns]
= mkApplicativeStmt ctxt [ApplicativeArgOne pat rhs] False tail'
stmtTreeToStmts _monad_names _ctxt (StmtTreeOne (s,_)) tail _tail_fvs =
@@ -1715,6 +1711,8 @@ segments stmts = map fst $ merge $ reverse $ map reverse $ walk (reverse stmts)
chunter _ [] = ([], [])
chunter vars ((stmt,fvs) : rest)
| not (isEmptyNameSet vars)
+ || isStrictPatternBind stmt
+ -- See Note [ApplicativeDo and strict patterns]
= ((stmt,fvs) : chunk, rest')
where (chunk,rest') = chunter vars' rest
(pvars, evars) = stmtRefs stmt fvs
@@ -1727,6 +1725,58 @@ segments stmts = map fst $ merge $ reverse $ map reverse $ walk (reverse stmts)
where fvs' = fvs `intersectNameSet` allvars
pvars = mkNameSet (collectStmtBinders (unLoc stmt))
+ isStrictPatternBind :: ExprLStmt GhcRn -> Bool
+ isStrictPatternBind (L _ (BindStmt pat _ _ _ _)) = isStrictPattern pat
+ isStrictPatternBind _ = False
+
+{-
+Note [ApplicativeDo and strict patterns]
+
+A strict pattern match is really a dependency. For example,
+
+do
+ (x,y) <- A
+ z <- B
+ return C
+
+The pattern (_,_) must be matched strictly before we do B. If we
+allowed this to be transformed into
+
+ (\(x,y) -> \z -> C) <$> A <*> B
+
+then it could be lazier than the standard desuraging using >>=. See #13875
+for more examples.
+
+Thus, whenever we have a strict pattern match, we treat it as a
+dependency between that statement and the following one. The
+dependency prevents those two statements from being performed "in
+parallel" in an ApplicativeStmt, but doesn't otherwise affect what we
+can do with the rest of the statements in the same "do" expression.
+-}
+
+isStrictPattern :: LPat id -> Bool
+isStrictPattern (L _ pat) =
+ case pat of
+ WildPat{} -> False
+ VarPat{} -> False
+ LazyPat{} -> False
+ AsPat _ p -> isStrictPattern p
+ ParPat p -> isStrictPattern p
+ ViewPat _ p _ -> isStrictPattern p
+ SigPatIn p _ -> isStrictPattern p
+ SigPatOut p _ -> isStrictPattern p
+ BangPat{} -> True
+ TuplePat{} -> True
+ SumPat{} -> True
+ PArrPat{} -> True
+ ConPatIn{} -> True
+ ConPatOut{} -> True
+ LitPat{} -> True
+ NPat{} -> True
+ NPlusKPat{} -> True
+ SplicePat{} -> True
+ _otherwise -> panic "isStrictPattern"
+
isLetStmt :: LStmt a b -> Bool
isLetStmt (L _ LetStmt{}) = True
isLetStmt _ = False