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