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.
\begin{code}
mkFailurePair :: CoreExpr -- Result type of the whole case expression
-> DsM (CoreBind, -- Binds the newly-created fail variable
\begin{code}
mkFailurePair :: CoreExpr -- Result type of the whole case expression
-> DsM (CoreBind, -- Binds the newly-created fail variable
- -- to either the expression or \ _ -> expression
- CoreExpr) -- Either the fail variable, or fail variable
- -- applied to unit tuple
+ -- to \ _ -> expression
+ CoreExpr) -- Fail variable applied to realWorld#
+-- See Note [Failure thunks and CPR]
- | isUnLiftedType ty = do
- fail_fun_var <- newFailLocalDs (unitTy `mkFunTy` ty)
- fail_fun_arg <- newSysLocalDs unitTy
- return (NonRec fail_fun_var (Lam fail_fun_arg expr),
- App (Var fail_fun_var) (Var unitDataConId))
-
- | otherwise = do
- fail_var <- newFailLocalDs ty
- return (NonRec fail_var expr, Var fail_var)
+ = do { fail_fun_var <- newFailLocalDs (realWorldStatePrimTy `mkFunTy` ty)
+ ; fail_fun_arg <- newSysLocalDs realWorldStatePrimTy
+ ; return (NonRec fail_fun_var (Lam fail_fun_arg expr),
+ App (Var fail_fun_var) (Var realWorldPrimId)) }
where
ty = exprType expr
\end{code}
where
ty = exprType expr
\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}
mkOptTickBox :: Maybe (Int,[Id]) -> CoreExpr -> DsM CoreExpr
mkOptTickBox Nothing e = return e
\begin{code}
mkOptTickBox :: Maybe (Int,[Id]) -> CoreExpr -> DsM CoreExpr
mkOptTickBox Nothing e = return e