Commit 8a25c54e authored by simonpj@microsoft.com's avatar simonpj@microsoft.com
Browse files

Fix Trac #3403: interaction of CPR and pattern-match failure

A fine bug report (#3403) demonstrated that we were losing the tail
call property when a complicated pattern match was involved.  After
a bit of investigation I discovered that the culprit was the failure
join-point introduced by the pattern matcher.  It was a zero-argument
thunk, which is not very CPR-friendly, and that interacted badly with
CPR worker/wrapper.

It's easy to fix, the same way that we fix other join points, by supplying
a dummy argument (that is not really passed at runtime.
parent a0d2e0fb
...@@ -674,23 +674,36 @@ Now @fail.33@ is a function, so it can be let-bound. ...@@ -674,23 +674,36 @@ Now @fail.33@ is a function, so it can be let-bound.
\begin{code} \begin{code}
mkFailurePair :: CoreExpr -- Result type of the whole case expression mkFailurePair :: CoreExpr -- Result type of the whole case expression
-> DsM (CoreBind, -- Binds the newly-created fail variable -> DsM (CoreBind, -- Binds the newly-created fail variable
-- to either the expression or \ _ -> expression -- to \ _ -> expression
CoreExpr) -- Either the fail variable, or fail variable CoreExpr) -- Fail variable applied to realWorld#
-- applied to unit tuple -- See Note [Failure thunks and CPR]
mkFailurePair expr mkFailurePair expr
| isUnLiftedType ty = do = do { fail_fun_var <- newFailLocalDs (realWorldStatePrimTy `mkFunTy` ty)
fail_fun_var <- newFailLocalDs (unitTy `mkFunTy` ty) ; fail_fun_arg <- newSysLocalDs realWorldStatePrimTy
fail_fun_arg <- newSysLocalDs unitTy ; return (NonRec fail_fun_var (Lam fail_fun_arg expr),
return (NonRec fail_fun_var (Lam fail_fun_arg expr), App (Var fail_fun_var) (Var realWorldPrimId)) }
App (Var fail_fun_var) (Var unitDataConId))
| otherwise = do
fail_var <- newFailLocalDs ty
return (NonRec fail_var expr, Var fail_var)
where where
ty = exprType expr ty = exprType expr
\end{code} \end{code}
Note [Failure thunks and CPR]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When we make a failure point we ensure that it
does not look like a thunk. Example:
let fail = \rw -> error "urk"
in case x of
[] -> fail realWorld#
(y:ys) -> case ys of
[] -> fail realWorld#
(z:zs) -> (y,z)
Reason: we know that a failure point is always a "join point" and is
entered at most once. Adding a dummy 'realWorld' token argument makes
it clear that sharing is not an issue. And that in turn makes it more
CPR-friendly. This matters a lot: if you don't get it right, you lose
the tail call property. For example, see Trac #3403.
\begin{code} \begin{code}
mkOptTickBox :: Maybe (Int,[Id]) -> CoreExpr -> DsM CoreExpr mkOptTickBox :: Maybe (Int,[Id]) -> CoreExpr -> DsM CoreExpr
mkOptTickBox Nothing e = return e mkOptTickBox Nothing e = return e
......
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