Search code examples
haskellwarningsghc

Enable -Woverflowed-literals for custom numeric types


By default GHC enables the -Woverflowed-literals warning to emit a message if a literal's precision is too great for its type:

OverflowedLiterals.hs:10:12: warning: [GHC-97441] [-Woverflowed-literals]
    Literal 258 is out of the Word8 range 0..255
   |
10 |     print (258 :: Word8)
   |            ^^^

OverflowedLiterals.hs:15:12: warning: [GHC-97441] [-Woverflowed-literals]
    Literal 9223372036854775817 is out of the Int range -9223372036854775808..9223372036854775807
   |
15 |     print (9223372036854775817 :: Int)
   |            ^^^^^^^^^^^^^^^^^^^

Is there a way to enable this warning for a custom numeric type?

For example, I'd want this code

data Word4 = Word4 Bool Bool Bool Bool
instance Num Word4 where
  ...

main = print (17 :: Word4)

To cause the compiler to complain

StackoverflowExample:LINE:COL: warning: [GHC-97441] [-Woverflowed-literals]
    Literal 17 is out of the Word4 range 0..15

Solution

  • No, or at least not with ghc at the time of writing. The code to check this is located here [GitHub]:

    warnAboutOverflowedLiterals dflags lit
     | wopt Opt_WarnOverflowedLiterals dflags
     , Just (i, tc) <- lit
     = if
        -- These only show up via the 'HsOverLit' route
        | sameUnique tc intTyConName        -> check i tc minInt         maxInt
        | sameUnique tc wordTyConName       -> check i tc minWord        maxWord
        | sameUnique tc int8TyConName       -> check i tc (min' @Int8)   (max' @Int8)
        | sameUnique tc int16TyConName      -> check i tc (min' @Int16)  (max' @Int16)
        | sameUnique tc int32TyConName      -> check i tc (min' @Int32)  (max' @Int32)
        | sameUnique tc int64TyConName      -> check i tc (min' @Int64)  (max' @Int64)
        | sameUnique tc word8TyConName      -> check i tc (min' @Word8)  (max' @Word8)
        | sameUnique tc word16TyConName     -> check i tc (min' @Word16) (max' @Word16)
        | sameUnique tc word32TyConName     -> check i tc (min' @Word32) (max' @Word32)
        | sameUnique tc word64TyConName     -> check i tc (min' @Word64) (max' @Word64)
        | sameUnique tc naturalTyConName    -> checkPositive i tc
    
        -- These only show up via the 'HsLit' route
        | sameUnique tc intPrimTyConName    -> check i tc minInt         maxInt
        | sameUnique tc wordPrimTyConName   -> check i tc minWord        maxWord
        | sameUnique tc int8PrimTyConName   -> check i tc (min' @Int8)   (max' @Int8)
        | sameUnique tc int16PrimTyConName  -> check i tc (min' @Int16)  (max' @Int16)
        | sameUnique tc int32PrimTyConName  -> check i tc (min' @Int32)  (max' @Int32)
        | sameUnique tc int64PrimTyConName  -> check i tc (min' @Int64)  (max' @Int64)
        | sameUnique tc word8PrimTyConName  -> check i tc (min' @Word8)  (max' @Word8)
        | sameUnique tc word16PrimTyConName -> check i tc (min' @Word16) (max' @Word16)
        | sameUnique tc word32PrimTyConName -> check i tc (min' @Word32) (max' @Word32)
        | sameUnique tc word64PrimTyConName -> check i tc (min' @Word64) (max' @Word64)
    

    It thus hardcodes the different (implicit) data constructors to work with Int, Word, Int16, Word32, etc. and thus checks with the corresponding lower and upperbounds.

    Strictly speaking, it might perhaps be possible, but currently not implemented. It will likely be a bit complicated to check if we know that the type to which 17 for example is converted, is of a type that is an instance of Num and Bounded, since it just translates 17 to fromInteger 17.