Skip to content

Diverging function with inlined non-diverging unfolding leads to segfault

Summary

The following code produces "Impossible case alternative" error at runtime, if compiled with optimizations.

If NOINLINE pragma is used for integerToIntMaybe, the program segfaults instead.

-- IntegerToInt.hs
{-# LANGUAGE MagicHash #-}
module IntegerToInt where
import GHC.Integer.GMP.Internals (Integer (S#))
-- import GHC.Num.Integer (Integer (IS))
import GHC.Exts (Int (I#))

-- Like Data.Bits.toIntegralSized, but optimized for Integer and Int
integerToIntMaybe :: Integer -> Maybe Int
integerToIntMaybe (S# x) = Just (I# x)
-- integerToIntMaybe (IS x) = Just (I# x)
integerToIntMaybe _      = Nothing -- relies on Integer's invariant
{-# INLINE [0] integerToIntMaybe #-} -- --> Impossible case alternative
-- {-# NOINLINE integerToIntMaybe #-} -- --> Segmentation fault

minBoundIntAsInteger :: Integer
minBoundIntAsInteger = fromIntegral (minBound :: Int)
{-# INLINE minBoundIntAsInteger #-}

maxBoundIntAsInteger :: Integer
maxBoundIntAsInteger = fromIntegral (maxBound :: Int)
{-# INLINE maxBoundIntAsInteger #-}

-- Helper function
integerToIntMaybe2 :: Bool -> Integer -> Maybe Int
integerToIntMaybe2 _ x = integerToIntMaybe x
-- No error with
--   integerToIntMaybe2 _ = integerToIntMaybe
{-# INLINE [0] integerToIntMaybe2 #-}

{-# RULES
"integerToIntMaybe" [~0] forall x.
  integerToIntMaybe x = integerToIntMaybe2 (minBoundIntAsInteger <= x && x <= maxBoundIntAsInteger) x
"integerToIntMaybe2/small" forall x.
  integerToIntMaybe2 True x = Just (fromIntegral x)
"integerToIntMaybe2/large" forall x.
  integerToIntMaybe2 False x = Nothing
  #-}
-- Main.hs
import IntegerToInt

noinline :: a -> a
noinline x = x
{-# NOINLINE noinline #-}

main = do
  print (noinline integerToIntMaybe 3141) -- Just 3141 (at runtime)
  print (integerToIntMaybe (2^20)) -- Just 1048576 (at runtime)
  print (integerToIntMaybe 314159265398979323846264338327950) -- Nothing (at compile time)
  print (integerToIntMaybe 3141) -- Just 3141 (at compile time)

(The code is also available as a gist.)

Steps to reproduce

Compile the two files with optimizations, and run the resulting binary.

$ git clone https://gist.github.com/b9a9ebaa70d99ac223232e85b01fb50e.git IntegerToInt 
$ cd IntegerToInt
$ ghc -O2 Main.hs
$ ./Main
Just 3141
Main: Impossible case alternative

Thu program runs successfully if compiled without optimizations.

Expected behavior

It should print

Just 3141
Just 1048576
Nothing
Just 3141

without error, whether optimizations are enabled or not.

Environment

  • GHC versions used: 8.6.5, 8.8.4, 8.10.1, 8.11.0.20200725
  • The same error with ghc-bignum.
Edited by Sylvain Henry
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information