#2684: Stack overflow in stream-fusion library with GHC 6.10 RC
------------------------------+---------------------------------------------
 Reporter:  dons              |          Owner:                  
     Type:  bug               |         Status:  new             
 Priority:  high              |      Milestone:  6.10.1          
Component:  Compiler          |        Version:  6.9             
 Severity:  major             |     Resolution:                  
 Keywords:                    |     Difficulty:  Unknown         
 Testcase:                    |   Architecture:  Unknown/Multiple
       Os:  Unknown/Multiple  |  
------------------------------+---------------------------------------------
Comment (by simonpj):

 I know more or less what is happening now.  Here's the diagnosis.
 Stream.2.hs contains this:
 {{{
 mapx f (x:xs) = f x : mapx f xs
 {-# NOINLINE [1] mapx #-}

 {-# RULES
     "mapx -> fusible" [~1] forall f xs.
         mapx f xs = unstream (mapy f (stream xs))
     "mapx -> unfused" [1] forall f xs.
         unstream (mapy f (stream xs)) = mapx f xs
   #-}
 }}}
 (Note that the second of these rules is an orphan.)
 After applying the `fusible` rule, at the end of Phase 1 GHC sees
 {{{
 mapx f (x:xs) = f x : unstream (mapy f (stream xs))
 }}}
 Now `mapx` looks non-recursive; after all, `mapy` is imported.  So when
 the occurrence analyser runs in Phase 0 `mapx` is not marked as a loop
 breaker and can be inlined freely.  However the `unfused` rule fires, and
 makes `mapx` recursive after all.  Before the occurrence analyser runs
 again, though, the simplifier processes the next definition:
 {{{
 initsx (x:xs) = [] : mapx (x:) (initsx xs)
 }}}
 Since `mapx` is not a loop breaker, it's just inlined repeatedly; result
 infinite loop.

 You may say that the occurrence analyser should be run again ''after''
 finishing each definition.  But that is only sticking plaster.  Suppose we
 had
 {{{
   mapx x = mapy x
 }}}
 (non-recursive, no rules) and imported that into a scope where there was
 an orphan rule
 {{{
   RULES "x" mapy = mapx
 }}}
 Then you can see that would make GHC loop.  (Inline `mapx`, run the RULE,
 and repeat.)


 What to do?  In general it's very hard to do much about RULES that give
 rise to loops, esp when they are orphans.  When they are defined in the
 same module as the function they are RULES for, GHC is pretty good; but
 otherwise it's difficult.

 In this case the obvious thing is to add
 {{{
 {-# NOINLINE [~1] mapx #-}
 }}}
 to stop it being inlined after Phase 1.  That should cure the loop, but
 I'm not sure if the optimisations you have in mind would still work.
 (Curiously the exact inverse annotation is provided in the test case,
 saying don't inline ''until'' Phase 1.)

 I'm surprised 6.8.3 works on this example; I suspect it's a fluke.
 (Should check this.)

 Bottom line: I can't see a quick fix.  Need to discuss how important this
 pattern is.  At least we know what the problem is.

 Simon

-- 
Ticket URL: <http://hackage.haskell.org/trac/ghc/ticket/2684#comment:3>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
_______________________________________________
Glasgow-haskell-bugs mailing list
[email protected]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-bugs

Reply via email to