Skip to content

Nasty interaction between MagicHash and CPP LANGUAGE pragmas

Found this problem when trying to use MagicHash and CPP in the same source file.

Small test program:

{-# LANGUAGE OverloadedStrings, MagicHash, BangPatterns  #-}
{-# LANGUAGE CPP #-}

import Data.ByteString (ByteString)
import Data.Int (Int64)

import qualified Data.ByteString.Char8 as B
import qualified Data.Char as C
import qualified Test.QuickCheck as QC

import GHC.Prim
import GHC.Types

readInt64 :: ByteString -> Int64
readInt64 bs =
        B.foldl' (\i c -> i * 10 + fromIntegral (mhDigitToInt c)) 0
             $ B.takeWhile C.isDigit bs

data Table = Table !Addr#

mhDigitToInt :: Char -> Int
mhDigitToInt (C# i) = I# (word2Int# $ indexWord8OffAddr# addr (ord# i))
  where
    !(Table addr) = table
    table :: Table
    table = Table
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
        \\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\x00\x00\x00\x00\x00\
        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"#


-- A QuickCheck property. Test that for a number >= 0, converting it to
-- a string using show and then reading the value back with the function
-- under test returns the original value.
-- The functions under test only work on Natural numbers (the Conent-Length
-- field in a HTTP header is always >= 0) so we check the absolute value of
-- the value that QuickCheck generates for us.
prop_read_show_idempotent :: Integral a => (ByteString -> a) -> a -> Bool
prop_read_show_idempotent freader x =
    let px = abs x
    in px == freader (B.pack $ show px)

main :: IO ()
main =
    QC.quickCheck (prop_read_show_idempotent readInt64)

Compile and running that as:

{{{ ghc -Wall test.hs -o test && ./test }}

generates an executable (with out any warnings) and when I run the executable it fails the QuickCheck test.

However, if I remove the LANGUAGE CPP pragma and recompile it, the program passes the QuickCheck test.

Using the two pragmas together makes correct code generate incorrect results.

Would like to see an error message if MagicHash and CPP are used in the same source file.

Trac metadata
Trac field Value
Version 7.2.2
Type Bug
TypeOfFailure OtherFailure
Priority normal
Resolution Unresolved
Component Compiler
Test case
Differential revisions
BlockedBy
Related
Blocking
CC
Operating system
Architecture
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information