Opened 22 months ago

Closed 22 months ago

Last modified 21 months ago

#14561 closed bug (fixed)

Panic on levity polymorphic very unsafe coerce

Reported by: goldfire Owned by:
Priority: normal Milestone:
Component: Compiler Version: 8.3
Keywords: LevityPolymorphism Cc: andrewthad
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Test Case: polykinds/T14561
Blocked By: Blocking:
Related Tickets: Differential Rev(s):
Wiki Page:


{-# LANGUAGE TypeInType #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE MagicHash #-}

module LevityId where

import GHC.Types
import GHC.Prim

levityPolymorphicId :: forall (a :: TYPE r). a -> a
levityPolymorphicId = unsafeCoerce#
  (GHC version 8.3.20170928 for x86_64-apple-darwin):
  typePrimRep (a_aW0 :: TYPE r_aVZ)
  Call stack:
      CallStack (from HasCallStack):
        callStackDoc, called at compiler/utils/Outputable.hs:1144:37 in ghc:Outputable
        pprPanic, called at compiler/simplStg/RepType.hs:358:5 in ghc:RepType
        runtimeRepPrimRep, called at compiler/simplStg/RepType.hs:344:5 in ghc:RepType
        kindPrimRep, called at compiler/simplStg/RepType.hs:307:18 in ghc:RepType
        typePrimRep, called at compiler/simplStg/UnariseStg.hs:666:8 in ghc:UnariseStg

Please report this as a GHC bug:

As originally mentioned here.

Change History (9)

comment:1 Changed 22 months ago by simonpj

Ah, hum. Lint rejects the program thus

*** Core Lint errors : in result of Desugar (after optimization) ***
<no location info>: warning:
    [in body of lambda with binder v_B1 :: a_aVG]
    Levity-polymorphic binder: v_B1 :: (a_aVG :: TYPE r_aVF)
*** Offending Program ***
levityPolymorphicId :: forall (a :: TYPE r). a -> a
  = \ (@ (r_aVF :: RuntimeRep))
      (@ (a_aVG :: TYPE r_aVF))
      (v_B1 :: a_aVG) ->

This happens because we want to allow

unsafeCoerce# (x::Int#)

and expand it to

x |> (UnsafeCo ...)

So we give unsafeCoerce# the extremely dodgy unfolding

/\r1 r2 (a:Type r1) (b:Type r2) (x:a). x |> UnsafeCo ...

It's dodgy because it has a bad lambda: we can't lambda-bind a levity-polymorphic variable x. It only works when unsafeCoerce# is saturated.

This is tiresome. The only paths forward I can see are

  • Do not allow unsafeCoerce on unboxed values. I don't know how much it is used.
  • Check that all uses are saturated. Perhaps in the desugarer.

The latter would be least destabilising.

Any thoughts?

comment:2 Changed 22 months ago by goldfire

I strongly prefer the latter. It makes me nervous not to have a true back-door to the type system (which we would lose if we dropped the levity-polymorphism from unsafeCoerce#).

It should be easy enough to check for saturation in the desugarer.

But, actually, there's a third option:

  • Allow silly uses of unsafeCoerce# to cause a panic.

The user is clearly taking on the risk of a runtime crash with unsafeCoerce#. We could just say they also risk taking on a compile-time crash. In this case, I think the fix (the saturation check) is easy enough, but I don't feel strongly committed to eradicating a hard-to-fix panic if the user abuses the type system with such a low-level operation.

comment:3 Changed 22 months ago by andrewthad

Cc: andrewthad added

comment:4 Changed 22 months ago by simonmar

There are lots of unsafeCoerce# in the GHCi debugger (RtClosureInspect) where we need to coerce unknown but pointed types into unpointed types like MutVar#. There's another instance of this kind of coercion in newStablePtrPrimMVar in base.

I don't know of any places where we legitimately coerce between boxed and unboxed types, but I think it would be nice if we could retain the ability to do so, for debugging (e.g. printing out pointer values) & general hacking purposes.

comment:5 Changed 22 months ago by simonpj

I don't want "silly uses" of unsafeCoerce# to cause a panic. GHC should never panic.

This ticket is about an un-saturated use of unsafeCoerce#; we can easily check for that.

Simon M raises the question of coercing between boxed and unboxed types. That seems very dodgy: the GC now won't see that pointer. But I suppose if you want to print an address there is no other way to do so. Regardless, it's a separate point to the saturation one, which is the point of this ticket.

comment:6 Changed 22 months ago by Simon Peyton Jones <simonpj@…>

In e40db7b1/ghc:

Detect levity-polymorphic uses of unsafeCoerce#

This bug was shown up by Trac #14561. The deguarer carefully
detects unsaturated and levity-polymorphic uses of primops,
but not of things like unsafeCoerce#.

The fix is simple: see Note [Levity-polymorphic Ids] in Id.

comment:7 Changed 22 months ago by Simon Peyton Jones <simonpj@…>

In 321b420/ghc:

Tidy up of wired-in names

Two things here:

* While debugging Trac #14561 I found it hard to understand
  ghcPrimIds and magicIds in MkId.  This patch adds more
  structure and comments.

* I also discovered that ($) no longer needs to be a wiredInId
  because we now have levity polymorphism.  So I took dollarId
  out of MkId; and gave it a levity-polymorphic type in GHC.Base

comment:8 Changed 22 months ago by simonpj

Resolution: fixed
Status: newclosed
Test Case: polykinds/T14561

The fix was easier than I thought: see comment:6

comment:9 Changed 21 months ago by Simon Peyton Jones <simonpj@…>

In aef4dee8/ghc:

Add missing stderr for Trac #14561
Note: See TracTickets for help on using tickets.