Opened 2 years ago

Closed 2 years ago

Last modified 2 years ago

#13803 closed bug (fixed)

Panic while forcing the thunk for TyThing IsFile (regression)

Reported by: inaki Owned by:
Priority: normal Milestone: 8.2.1
Component: Compiler Version: 8.2.1-rc2
Keywords: hs-boot Cc: ezyang
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: Compile-time crash or panic Test Case:
Blocked By: Blocking:
Related Tickets: Differential Rev(s): Phab:D3742
Wiki Page:

Description

(Apologies in advance for the very non-minimal testcase. I have been unsuccessfully trying to reduce the testcase for a few hours now, but I am flying blind, and the bug seems to depend on the interaction of various modules. The panic is very reproducible for me, so I figured that better reporting even if I failed to obtain a simplified testcase. Maybe someone else has more luck isolating the issue.)

When compiling gi-gio-2.0.12 I consistently get the following GHC panic:

[259 of 293] Compiling GI.Gio.Interfaces.File ( GI/Gio/Interfaces/File.hs, dist/build/GI/Gio/Interfaces/File.o )
ghc: panic! (the 'impossible' happened)
  (GHC version 8.2.0.20170507 for x86_64-unknown-linux):
	tcIfaceGlobal (local): not found
  You are in a maze of twisty little passages, all alike.
  While forcing the thunk for TyThing IsFile
  which was lazily initialized by initIfaceCheck typecheckLoop,
  I tried to tie the knot, but I couldn't find IsFile
  in the current type environment.
  If you are developing GHC, please read Note [Tying the knot]
  and Note [Type-checking inside the knot].
  Consider rebuilding GHC with profiling for a better stack trace.
  Contents of current type environment: []
  Call stack:
      CallStack (from HasCallStack):
        prettyCurrentCallStack, called at compiler/utils/Outputable.hs:1134:58 in ghc:Outputable
        callStackDoc, called at compiler/utils/Outputable.hs:1138:37 in ghc:Outputable
        pprPanic, called at compiler/iface/TcIface.hs:1689:23 in ghc:TcIface

Please report this as a GHC bug:  http://www.haskell.org/ghc/reportabug

To compile this code you will need the glib2-devel package installed. To compile

$ cabal get gi-gio-2.0.12
$ cd gi-gio-2.0.12
$ cabal sandbox init
$ cabal install --dependencies-only
$ cabal build

I am running version 8.2.0.20170507, from https://copr.fedorainfracloud.org/coprs/petersen/ghc-8.2.1/ .

The same code compiles in versions 7.8.x, 7.10.x and 8.0.x.

Change History (20)

comment:1 Changed 2 years ago by inaki

Keywords: hs-boot added

A possibly related issue is 13710, although in the current code there are no record wildcards involved. I haven't tried the fix in issue:13710#comment:12.

comment:2 Changed 2 years ago by ezyang

Is it possible for you to at least post the autogenerated source code that the Setup script generates?

comment:4 Changed 2 years ago by RyanGlScott

Minimizing this bug is probably going to take a couple years off of my life...

But in any case, I've managed to reduce this down to six (!) files with one external dependency (haskell-gi-base):

module GIGioInterfacesFile where

import Data.GI.Base.ShortPrelude -- from haskell-gi-base
import {-# SOURCE #-} qualified GIGioObjectsFileEnumerator as Gio.FileEnumerator
import {-# SOURCE #-} qualified GIGioObjectsMountOperation as Gio.MountOperation

class IsFile o
module GIGioInterfacesFile where

class IsFile o
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
module GIGioObjectsFileEnumerator where

import GHC.Exts (Constraint)
import {-# SOURCE #-} qualified GIGioInterfacesFile as Gio.File

class IsFileEnumerator o

class AttrInfo info where
  type family AttrSetTypeConstraint info :: * -> Constraint

data FileEnumeratorContainerPropertyInfo
instance AttrInfo FileEnumeratorContainerPropertyInfo where
    type AttrSetTypeConstraint FileEnumeratorContainerPropertyInfo = Gio.File.IsFile
module GIGioObjectsFileEnumerator where

class IsFileEnumerator o
module GIGioObjectsMountOperation where

class IsMountOperation o
module GIGioObjectsMountOperation where

class IsMountOperation o
$ /opt/ghc/8.2.1/bin/ghci GIGioInterfacesFile.hs
GHCi, version 8.2.0.20170522: http://www.haskell.org/ghc/  :? for help
Loaded GHCi configuration from /home/rgscott/.ghci
[1 of 6] Compiling GIGioInterfacesFile[boot] ( GIGioInterfacesFile.hs-boot, interpreted )
[2 of 6] Compiling GIGioObjectsFileEnumerator[boot] ( GIGioObjectsFileEnumerator.hs-boot, interpreted )
[3 of 6] Compiling GIGioObjectsFileEnumerator ( GIGioObjectsFileEnumerator.hs, interpreted )
[4 of 6] Compiling GIGioObjectsMountOperation[boot] ( GIGioObjectsMountOperation.hs-boot, interpreted )
[5 of 6] Compiling GIGioInterfacesFile ( GIGioInterfacesFile.hs, interpreted )
ghc: panic! (the 'impossible' happened)
  (GHC version 8.2.0.20170522 for x86_64-unknown-linux):
	tcIfaceGlobal (local): not found
  You are in a maze of twisty little passages, all alike.
  While forcing the thunk for TyThing IsFile
  which was lazily initialized by initIfaceCheck typecheckLoop,
  I tried to tie the knot, but I couldn't find IsFile
  in the current type environment.
  If you are developing GHC, please read Note [Tying the knot]
  and Note [Type-checking inside the knot].
  Consider rebuilding GHC with profiling for a better stack trace.
  Contents of current type environment: []
  Call stack:
      CallStack (from HasCallStack):
        prettyCurrentCallStack, called at compiler/utils/Outputable.hs:1133:58 in ghc:Outputable
        callStackDoc, called at compiler/utils/Outputable.hs:1137:37 in ghc:Outputable
        pprPanic, called at compiler/iface/TcIface.hs:1689:23 in ghc:TcIface

The annoying bit is that I haven't figure out how to eliminate that import Data.GI.Base.ShortPrelude from the haskell-gi-base library.

Last edited 2 years ago by RyanGlScott (previous) (diff)

comment:5 Changed 2 years ago by RyanGlScott

Hrm, it looks like you could also use this instead for GIGioInterfacesFile.hs:

module GIGioInterfacesFile where

import Data.Text
import {-# SOURCE #-} qualified GIGioObjectsFileEnumerator as Gio.FileEnumerator
import {-# SOURCE #-} qualified GIGioObjectsMountOperation as Gio.MountOperation

class IsFile o

That still requires an external dependency (text), but it's a far easier dependency to install than haskell-gi-base, at least.

comment:6 Changed 2 years ago by RyanGlScott

I can also confirm that the workaround that ezyang posted in https://ghc.haskell.org/trac/ghc/ticket/13710#comment:12 does not fix this problem.

comment:7 Changed 2 years ago by RyanGlScott

OK, here's a version of GIGioInterfacesFile with no external dependencies:

module GIGioInterfacesFile where

import Control.DeepSeq ()
import {-# SOURCE #-} qualified GIGioObjectsFileEnumerator as Gio.FileEnumerator
import {-# SOURCE #-} qualified GIGioObjectsMountOperation as Gio.MountOperation

class IsFile o

I have yet to figure out why the import of Control.DeepSeq is needed here.

comment:8 Changed 2 years ago by RyanGlScott

Now with even fewer internal dependencies:

{-# LANGUAGE TypeFamilies #-}
module D (D) where

type family D a
type instance D Int = Int
module GIGioInterfacesFile where

import D ()
import {-# SOURCE #-} qualified GIGioObjectsFileEnumerator as Gio.FileEnumerator
import {-# SOURCE #-} qualified GIGioObjectsMountOperation as Gio.MountOperation

class IsFile o

It looks like type family instances are the culprit.

comment:9 Changed 2 years ago by RyanGlScott

OK, here is as small as I can possibly make this: a "meager" five files:

-- D.hs
{-# LANGUAGE TypeFamilies #-}
module D (D) where

type family D a
type instance D Int = Int
-- E.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE TypeFamilies #-}
module E where

import                GHC.Exts (Constraint)
import {-# SOURCE #-} Y

class C i where
  type CF i :: * -> Constraint

data E
instance C E where
    type CF E = Y
-- E.hs-boot
module E where
-- Y.hs
module Y where

import                D ()
import {-# SOURCE #-} E

class Y o
-- Y.hs-boot
module Y where

class Y o
$ /opt/ghc/8.2.1/bin/ghci Y.hs
GHCi, version 8.2.0.20170522: http://www.haskell.org/ghc/  :? for help
Loaded GHCi configuration from /home/rgscott/.ghci
[1 of 5] Compiling D                ( D.hs, interpreted )
[2 of 5] Compiling E[boot]          ( E.hs-boot, interpreted )
[3 of 5] Compiling Y[boot]          ( Y.hs-boot, interpreted )
[4 of 5] Compiling E                ( E.hs, interpreted )
[5 of 5] Compiling Y                ( Y.hs, interpreted )
ghc: panic! (the 'impossible' happened)
  (GHC version 8.2.0.20170522 for x86_64-unknown-linux):
	tcIfaceGlobal (local): not found
  You are in a maze of twisty little passages, all alike.
  While forcing the thunk for TyThing Y
  which was lazily initialized by initIfaceCheck typecheckLoop,
  I tried to tie the knot, but I couldn't find Y
  in the current type environment.
  If you are developing GHC, please read Note [Tying the knot]
  and Note [Type-checking inside the knot].
  Consider rebuilding GHC with profiling for a better stack trace.
  Contents of current type environment: []
  Call stack:
      CallStack (from HasCallStack):
        prettyCurrentCallStack, called at compiler/utils/Outputable.hs:1133:58 in ghc:Outputable
        callStackDoc, called at compiler/utils/Outputable.hs:1137:37 in ghc:Outputable
        pprPanic, called at compiler/iface/TcIface.hs:1689:23 in ghc:TcIface

Note that the name of D.hs actually matters here. For instance, if you rename D.hs to Z.hs, it'll actually compile!

$ /opt/ghc/8.2.1/bin/ghci Y.hs
GHCi, version 8.2.0.20170522: http://www.haskell.org/ghc/  :? for help
Loaded GHCi configuration from /home/rgscott/.ghci
[1 of 5] Compiling E[boot]          ( E.hs-boot, interpreted )
[2 of 5] Compiling Y[boot]          ( Y.hs-boot, interpreted )
[3 of 5] Compiling E                ( E.hs, interpreted )
[4 of 5] Compiling Z                ( Z.hs, interpreted )
[5 of 5] Compiling Y                ( Y.hs, interpreted )
Ok, modules loaded: E, E, Y, Y, Z.

comment:10 Changed 2 years ago by simonpj

Cc: ezyang added

Edward is the expert here. I'm cc'ing him in the hope he can help.

comment:11 Changed 2 years ago by ezyang

This is a family instance consistency check problem, as can be seen when you turn on tracing:

checkForConflictsghc-stage2: panic! (the 'impossible' happened)
  (GHC version 8.3.20170517 for x86_64-unknown-linux):
        tcIfaceGlobal (local): not found

We begin checking for conflicts immediately before the panic.

The problem is reminiscent of #11062 (fixed in 25b70a29f6236b591252bf5a361a1547f0ffee51).

Based on the minimized example, and reading the code, it looks like we're eagerly tugging on the RHS of a type family when loading it in for a conflict check. Should be an easy fix.

Last edited 2 years ago by ezyang (previous) (diff)

comment:12 Changed 2 years ago by ezyang

This appears to fix the problem but we may need to figure out something more efficient, since adding the forkM here affects ALL axioms we load from interfaces.

diff --git a/compiler/iface/TcIface.hs b/compiler/iface/TcIface.hs
index 3a6a4070d2..0fad1da50b 100644
--- a/compiler/iface/TcIface.hs
+++ b/compiler/iface/TcIface.hs
@@ -807,7 +807,7 @@ tc_iface_decl _parent ignore_prags
 tc_iface_decl _ _ (IfaceAxiom { ifName = tc_name, ifTyCon = tc
                               , ifAxBranches = branches, ifRole = role })
   = do { tc_tycon    <- tcIfaceTyCon tc
-       ; tc_branches <- tc_ax_branches branches
+       ; tc_branches <- forkM (text "Axiom branches" <+> ppr tc_name) $ tc_ax_branches branches
        ; let axiom = CoAxiom { co_ax_unique   = nameUnique tc_name
                              , co_ax_name     = tc_name
                              , co_ax_tc       = tc_tycon

comment:13 Changed 2 years ago by simonpj

It also means that less work is done (and perhaps even fewer interface files read, which is big) if the axiom is never actually needed. So it might be more efficient. Not reading interface files too eagerly is one of the original reasons for the whole forkM story in TcIface.

Would you like to try?

Please comment the change, with a reference to this ticket.

comment:14 Changed 2 years ago by ezyang

It turns out this is not enough, if we actually need to test for overlap. Investigating.

comment:15 Changed 2 years ago by ezyang

So, here is a test case which is not solved by the test above:

-- F.hs
{-# LANGUAGE TypeFamilies #-}
module F where
type family F a :: *

-- A.hs-boot
module A where
data T

-- B.hs
{-# LANGUAGE TypeFamilies #-}
module B where
import {-# SOURCE #-} A
import F
type instance F T = Int

-- C.hs
{-# LANGUAGE TypeFamilies #-}
module C where
import {-# SOURCE #-} A
import F
type instance F T = Bool

-- A.hs
module A where
import B
import C

Right now, we decide to defer a type family consistency check if the family was recursively defined. If the RHS refers to a recursively defined type, there's no problem: we don't need to look at it for consistency checking. But if the LHS is recursively defined, as is in this example, we DO need to defer the check.

But it's a bit irritating to figure out whether or not there's actually a reference to a recursively defined type in the LHS, since this involves traversing the LHS types, and if we're not careful we'll end up pulling in the TyThing anyway. There are two other possibilities: (1) always defer checking instances which are defined inside the recursive look (by looking at the Name of the axiom), or (2) annotating IfaceAxiom with the set of boot types its LHS refers to, for easy checking. Not entirely sure what the best action is.

comment:16 Changed 2 years ago by ezyang

Differential Rev(s): Phab:D3742
Status: newpatch

comment:17 Changed 2 years ago by Ben Gamari <ben@…>

In fdb6a5b/ghc:

Make IfaceAxiom typechecking lazier.

Fixes #13803, but adds a note about a yet to be fixed #13981.

Signed-off-by: Edward Z. Yang <ezyang@cs.stanford.edu>

Test Plan: validate

Reviewers: bgamari, austin

Reviewed By: bgamari

Subscribers: simonpj, rwbarton, thomie

GHC Trac Issues: #13803

Differential Revision: https://phabricator.haskell.org/D3742

comment:18 Changed 2 years ago by bgamari

Milestone: 8.2.1
Status: patchmerge

It looks like we'll want to merge this for 8.2.1.

comment:19 Changed 2 years ago by bgamari

Resolution: fixed
Status: mergeclosed

comment:20 Changed 2 years ago by inaki

Thanks for the fix, I just gave it a try. Unfortunately the original issue still seems to be present (perhaps I am hitting #13981?). The error message I am getting is basically the same as before:

<no location info>: error:
    ghc-stage2: panic! (the 'impossible' happened)
  (GHC version 8.2.0.20170720 for x86_64-unknown-linux):
	tcIfaceGlobal (local): not found
  You are in a maze of twisty little passages, all alike.
  While forcing the thunk for TyThing IsFile
  which was lazily initialized by initIfaceCheck typecheckLoop,
  I tried to tie the knot, but I couldn't find IsFile
  in the current type environment.
  If you are developing GHC, please read Note [Tying the knot]
  and Note [Type-checking inside the knot].
  Consider rebuilding GHC with profiling for a better stack trace.
  Contents of current type environment: []
  Call stack:
      CallStack (from HasCallStack):
        prettyCurrentCallStack, called at compiler/utils/Outputable.hs:1133:58 in ghc:Outputable
        callStackDoc, called at compiler/utils/Outputable.hs:1137:37 in ghc:Outputable
        pprPanic, called at compiler/iface/TcIface.hs:1696:23 in ghc:TcIface

Please report this as a GHC bug:  http://www.haskell.org/ghc/reportabug

This is when trying to compile gi-gio, following the same steps as in the original report, but using GHC 8.2 from git.

Note: See TracTickets for help on using tickets.