如何为自定义数值类型启用-Woverflowed-literals警告?
GHC默认启用-Woverflowed-literals警告,当字面量超出内置数值类型(如Word8、Int)的范围时,会输出编译提示,示例如下:
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)
| ^^^^^^^^^^^^^^^^^^^
针对自定义数值类型(比如你示例中的Word4),GHC原生的-Woverflowed-literals不直接支持,但可以通过两种方式实现类似的编译期警告:
方法一:使用TemplateHaskell做编译期检查
通过TemplateHaskell编写宏,替代直接的字面量转换,在编译阶段验证数值是否在自定义类型的范围内,超出则抛出警告。示例代码如下:
{-# LANGUAGE TemplateHaskell #-} import Language.Haskell.TH import Language.Haskell.TH.Syntax data Word4 = Word4 Bool Bool Bool Bool deriving (Show) -- 定义Word4的取值范围 instance Bounded Word4 where minBound = Word4 False False False False -- 对应数值0 maxBound = Word4 True True True True -- 对应数值15 -- 编译期检查并转换整数为Word4 word4 :: Integer -> Q Exp word4 n = do let minVal = 0 maxVal = 15 if n < minVal || n > maxVal then do -- 输出自定义警告 reportWarning $ "Literal " ++ show n ++ " is out of the Word4 range " ++ show minVal ++ ".." ++ show maxVal -- 返回默认值或处理逻辑 [| Word4 False False False False |] else do -- 将整数转换为4位二进制对应的Bool值 let bits = map (testBit n) [0..3] [| Word4 $(pure $ LitE $ BoolL $ bits !! 3) $(pure $ LitE $ BoolL $ bits !! 2) $(pure $ LitE $ BoolL $ bits !! 1) $(pure $ LitE $ BoolL $ bits !! 0) |] main = print $(word4 17)
编译这段代码时,GHC会输出你期望的警告信息。
方法二:编写GHC插件扩展检查逻辑
如果希望完全复用-Woverflowed-literals的原生机制,可以编写GHC插件,在类型检查阶段拦截自定义数值类型的字面量转换操作,检查其范围并触发同类型的警告。这种方式实现复杂度较高,但能和GHC原生警告系统无缝整合。
需要注意:如果仅手动实现Num实例的fromInteger方法,GHC不会触发编译期警告——因为fromInteger是运行时执行的逻辑,而-Woverflowed-literals是编译期的静态检查。
内容的提问来源于stack exchange,提问作者rampion

