Ticket #1012 (reopened bug)

Opened 1 year ago

Last modified 4 months ago

ghc panic with mutually recursive modules and template haskell

Reported by: guest Assigned to:
Priority: normal Milestone: 6.10 branch
Component: Template Haskell Version: 6.8.2
Severity: normal Keywords:
Cc: Difficulty: Unknown
Test Case: TH_import_loop Architecture: Multiple
Operating System: Multiple

Description

When compiling the files below using ghc --make Main.hs I get the following error:

[1 of 5] Compiling ModuleA[boot]    ( ModuleA.hs-boot, nothing )
[2 of 5] Compiling ModuleC          ( ModuleC.hs, ModuleC.o )
[3 of 5] Compiling ModuleB          ( ModuleB.hs, ModuleB.o )
Loading package base ... linking ... done.
Loading package template-haskell ... linking ... done.
ghc-6.6: panic! (the 'impossible' happened)
  (GHC version 6.6 for powerpc-apple-darwin):
        Maybe.fromJust: Nothing

ModuleA.hs:

module ModuleA where

import ModuleB

ModuleA.hs-boot:

module ModuleA where

ModuleB.hs:

{-# OPTIONS -fth #-}
module ModuleB where

import ModuleC

$(nothing)

ModuleC.hs:

module ModuleC where

import Language.Haskell.TH

import {-# SOURCE #-} ModuleA

nothing = return [] :: Q [Dec]

Main.hs:

module Main.hs

import ModuleA

main = return ()

Change History

01/24/07 09:46:25 changed by igloo

  • testcase set to TH_import_loop.
  • os changed from MacOS X to Multiple.
  • architecture changed from powerpc to Multiple.
  • milestone set to _|_.

Urk, this looks tricky. It will probably want to wait until we can use code spliced in earlier in a module in later splices in the same module.

01/31/07 01:02:47 changed by simonmar

This now gives a slightly improved error message, as a result of the fix for #936:

[1 of 5] Compiling A[boot]          ( A.hs-boot, nothing )
[2 of 5] Compiling C                ( C.hs, C.o )
[3 of 5] Compiling B                ( B.hs, B.o )
Loading package base ... linking ... done.
module main:A cannot be linked; it is only available as a boot module

Simon: please feel free to edit the error message if you can think of a way to improve it (compiler/ghci/Linker.hs). Should we close this bug? Perhaps document the shortcoming in the TH docs?

01/31/07 01:16:11 changed by simonpj

  • type changed from bug to merge.

I've updated the docs. Ian, please merge.

Wed Jan 31 09:14:51 GMT 2007 simonpj@microsoft.com

  • Add note about Template Haskell and mutual recursion

Simon

01/31/07 03:30:16 changed by igloo

  • status changed from new to closed.
  • resolution set to fixed.

Merged.

12/20/07 09:55:50 changed by fons

  • status changed from closed to reopened.
  • resolution deleted.

Don't know if this deserves its own ticket but it seems that ghci (or ghc --make) fails when running a compile-time function imported from a mutually-recursive group of modules which _doesn't_ include the module currently being compiled.

ModuleA.hs-boot

module ModuleA

ModuleA.hs

module ModuleA where
import ModuleC

ModuleC.hs

odule ModuleC where

import Language.Haskell.TH

import {-# SOURCE #-} ModuleA

nothing = return [] :: Q [Dec]

ModuleB.hs (makes use of a function of C but is _not_ included in the group of recursive modules)

{-# LANGUAGE TemplateHaskell #-}
module ModuleB where

import ModuleC

$(nothing)
$ ghci ModuleB.hs
GHCi, version 6.8.1: http://www.haskell.org/ghc/  :? for help
Loading package base ... linking ... done.
[1 of 4] Compiling ModuleA[boot]    ( ModuleA.hs-boot, interpreted )
[2 of 4] Compiling ModuleC          ( ModuleC.hs, interpreted )
[3 of 4] Compiling ModuleB          ( ModuleB.hs, interpreted )
module main:ModuleA cannot be linked; it is only available as a boot module
> 

Furthermore, it would be really useful if the error message indicated the compilation failed due to Template Haskell. (It took me a while to figure out why it was caused)

12/20/07 10:09:57 changed by fons

curiously enough compiling the following module doesn't cuase any problems.

Main.hs

module Main where

import ModuleB

main = return ()

$ ghc --make Main.hs
[1 of 5] Compiling ModuleA[boot]    ( ModuleA.hs-boot, ModuleA.o-boot )
[2 of 5] Compiling ModuleC          ( ModuleC.hs, ModuleC.o )
[3 of 5] Compiling ModuleA          ( ModuleA.hs, ModuleA.o )
[4 of 5] Compiling ModuleB          ( ModuleB.hs, ModuleB.o )
Loading package base ... linking ... done.
Loading package array-0.1.0.0 ... linking ... done.
Loading package packedstring-0.1.0.0 ... linking ... done.
Loading package containers-0.1.0.0 ... linking ... done.
Loading package pretty-1.0.0.0 ... linking ... done.
Loading package template-haskell ... linking ... done.
[5 of 5] Compiling Main             ( Main.hs, Main.o )
Linking Main ...

12/31/07 09:20:33 changed by fons

  • version changed from 6.6 to 6.8.2.
  • type changed from merge to bug.

12/31/07 09:20:46 changed by fons

  • component changed from Compiler to Template Haskell.

01/03/08 02:14:31 changed by simonmar

Since ModuleB is outside the ModuleA/ModuleC loop, it can import ModuleA without creating any new loops, and I bet this will work around the problem and get you unblocked.

What's happening is that the dependency analysis isn't figuring out that the real ModuleA must be compiled before ModuleB. I think the solution is something along the lines of: every module that depends on a module in a cycle, but is not a member of that cycle, should have an implicit dependency on each of the modules in the cycle.

01/03/08 02:14:59 changed by simonmar

  • milestone changed from _|_ to 6.8 branch.

(follow-up: ↓ 12 ) 01/04/08 04:27:13 changed by simonpj

  • status changed from reopened to closed.
  • resolution set to fixed.

Simon is right: B.hs depends on C.hs, which depends on A.hs-boot. I think it's quite accidental that A happens to be compiled before B in your "curiously enough" case, but after B in your original case.

All you need do is to make B depend on A, thus

{-# LANGUAGE TemplateHaskell #-}
module ModuleB where

import ModuleC
import ModuleA

$(nothing)

and it all works fine. Use -v to see the compilation order.

I will clarify the documentation, thus:

You can only run a function at compile time if it is imported from another module that is not part of a mutually-recursive group of modules that includes the module currently being compiled. Furthermore, all of the modules of the mutually-recursive group must be reachable by non-SOURCE imports from the module where the splice is to be run.

Fri Jan  4 12:19:39 GMT 2008  simonpj@microsoft.com
  * Document SOURCE pragma; clarify TH behavior for 
    mutually-recurive modules (Trac #1012)

It's not clear to me how to improve the error message, at least not without adding more plumbing to say "I'm in a Template Haskell splice". Let's see if it happens again.

Meanwhile I'll close the bug.

Simon

(in reply to: ↑ 11 ) 01/04/08 05:02:58 changed by fons

Replying to simonpj:

Simon is right: B.hs depends on C.hs, which depends on A.hs-boot. I think it's quite accidental that A happens to be compiled before B in your "curiously enough" case, but after B in your original case. All you need do is to make B depend on A, thus {{{ {-# LANGUAGE TemplateHaskell? #-} module ModuleB where import ModuleC import ModuleA $(nothing) }}} and it all works fine. Use -v to see the compilation order.

I finally managed to solve the problem "turning around" the circular dependencies of my project (tranforming the SOURCE imports of the loop in normal onces and vice versa). However, the workaround suggested by Simon should work as you said.

Meanwhile I'll close the bug.

As far as I understand, ghc's dependency analysis could be improved (otherwise a workaround would not be needed). I don't personally think this bug should be closed and include it as a limitation in the docs before considering simonmar's proposal, that is:

What's happening is that the dependency analysis isn't figuring out that the real ModuleA must be compiled before ModuleB. I think the solution is something along the lines of: every module that depends on a module in a cycle, but is not a member of that cycle, should have an implicit dependency on each of the modules in the cycle.

01/12/08 12:06:09 changed by igloo

  • status changed from closed to reopened.
  • resolution deleted.
  • milestone changed from 6.8 branch to _|_.

I agree that it is still a bug, but I've put it back in the _|_ milestone as it's easy to work-around, and I assume that if it was trivial to fix then SPJ would have done so.

01/16/08 04:46:18 changed by simonpj

  • milestone changed from _|_ to 6.10 branch.

Fair enough. I have taken a little look at this, based on fons's suggestion "every module M that depends on a module C in a cycle, but is not a member of that cycle, should have an implicit dependency on each of the modules C1.. Cn in the cycle.". Yes, I think that would not be too hard to do. There are two places to think about:

  • ghc --make: When deciding the up-sweep order, first do a SCC analysis finding strongly connected components of modules, and top-sort those components. Then linearise each component. That gives a linear order that respects fons's suggestion.
  • ghc -M: similar story, but less neat. We have to emit lots of extra dependencies in the makefile, so that M depends on C1..Cn.

Not very hard, but more than an hours work. Let's do it for 6.10.

Simon