Improve the treatment of 'seq' (Trac #2273)
[ghc-hetmet.git] / compiler / deSugar / DsUtils.lhs
index ef16317..4334a12 100644 (file)
@@ -163,14 +163,18 @@ mkDsApps fun args
                                     (arg_ty, res_ty) = splitFunTy fun_ty
 -----------
 mk_val_app :: CoreExpr -> CoreExpr -> Type -> Type -> CoreExpr
+mk_val_app (Var f `App` Type ty1 `App` Type _ `App` arg1) arg2 _ res_ty
+  | f == seqId         -- Note [Desugaring seq (1), (2)]
+  = Case arg1 case_bndr res_ty [(DEFAULT,[],arg2)]
+  where
+    case_bndr = case arg1 of
+                  Var v1 -> v1 -- Note [Desugaring seq (2)]
+                  _      -> mkWildId ty1
+
 mk_val_app fun arg arg_ty _    -- See Note [CoreSyn let/app invariant]
   | not (isUnLiftedType arg_ty) || exprOkForSpeculation arg
   = App fun arg                -- The vastly common case
 
-mk_val_app (Var f `App` Type ty1 `App` Type _ `App` arg1) arg2 _ res_ty
-  | f == seqId         -- Note [Desugaring seq]
-  = Case arg1 (mkWildId ty1) res_ty [(DEFAULT,[],arg2)]
-
 mk_val_app fun arg arg_ty res_ty
   = Case arg (mkWildId arg_ty) res_ty [(DEFAULT,[],App fun (Var arg_id))]
   where
@@ -178,8 +182,8 @@ mk_val_app fun arg arg_ty res_ty
                                -- because 'fun ' should not have a free wild-id
 \end{code}
 
-Note [Desugaring seq]  cf Trac #1031
-~~~~~~~~~~~~~~~~~~~~~
+Note [Desugaring seq (1)]  cf Trac #1031
+~~~~~~~~~~~~~~~~~~~~~~~~~
    f x y = x `seq` (y `seq` (# x,y #))
 
 The [CoreSyn let/app invariant] means that, other things being equal, because 
@@ -194,10 +198,41 @@ But that is bad for two reasons:
 Seq is very, very special!  So we recognise it right here, and desugar to
        case x of _ -> case y of _ -> (# x,y #)
 
-The special case would be valid for all calls to 'seq', but it's only *necessary*
-for ones whose second argument has an unlifted type. So we only catch the latter
-case here, to avoid unnecessary tests.
-
+Note [Desugaring seq (2)]  cf Trac #2231
+~~~~~~~~~~~~~~~~~~~~~~~~~
+Consider
+   let chp = case b of { True -> fst x; False -> 0 }
+   in chp `seq` ...chp...
+Here the seq is designed to plug the space leak of retaining (snd x)
+for too long.
+
+If we rely on the ordinary inlining of seq, we'll get
+   let chp = case b of { True -> fst x; False -> 0 }
+   case chp of _ { I# -> ...chp... }
+
+But since chp is cheap, and the case is an alluring contet, we'll
+inline chp into the case scrutinee.  Now there is only one use of chp,
+so we'll inline a second copy.  Alas, we've now ruined the purpose of
+the seq, by re-introducing the space leak:
+    case (case b of {True -> fst x; False -> 0}) of
+      I# _ -> ...case b of {True -> fst x; False -> 0}...
+
+We can try to avoid doing this by ensuring that the binder-swap in the
+case happens, so we get his at an early stage:
+   case chp of chp2 { I# -> ...chp2... }
+But this is fragile.  The real culprit is the source program.  Perhpas we
+should have said explicitly
+   let !chp2 = chp in ...chp2...
+
+But that's painful.  So the code here does a little hack to make seq
+more robust: a saturated application of 'seq' is turned *directly* into
+the case expression. So we desugar to:
+   let chp = case b of { True -> fst x; False -> 0 }
+   case chp of chp { I# -> ...chp... }
+Notice the shadowing of the case binder! And now all is well.
+
+The reason it's a hack is because if you define mySeq=seq, the hack
+won't work on mySeq.  
 
 %************************************************************************
 %*                                                                     *