diff options
author | Sylvain Henry <sylvain@haskus.fr> | 2021-02-12 16:17:28 +0100 |
---|---|---|
committer | Marge Bot <ben+marge-bot@smart-cactus.org> | 2021-02-18 13:48:51 -0500 |
commit | 94bbc45df30220369601a225df6a5baab6d2734a (patch) | |
tree | 98ae1e9ae41157b9e668a8c6756d7a6da5fa6faf | |
parent | 60ed2a65b22023a47c7215855e482cbe7a354f07 (diff) | |
download | haskell-94bbc45df30220369601a225df6a5baab6d2734a.tar.gz |
Use target Int/Word when detecting literal overflows (#17336)
And also for empty enumeration detection.
-rw-r--r-- | compiler/GHC/HsToCore/Match/Literal.hs | 130 |
1 files changed, 76 insertions, 54 deletions
diff --git a/compiler/GHC/HsToCore/Match/Literal.hs b/compiler/GHC/HsToCore/Match/Literal.hs index 525bd02976..6e9409989a 100644 --- a/compiler/GHC/HsToCore/Match/Literal.hs +++ b/compiler/GHC/HsToCore/Match/Literal.hs @@ -1,5 +1,8 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE MultiWayIf #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE AllowAmbiguousTypes #-} {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} @@ -63,7 +66,6 @@ import Data.Int import Data.List.NonEmpty (NonEmpty(..)) import qualified Data.List.NonEmpty as NEL import Data.Word -import Data.Proxy {- ************************************************************************ @@ -192,34 +194,46 @@ warnAboutOverflowedLiterals warnAboutOverflowedLiterals dflags lit | wopt Opt_WarnOverflowedLiterals dflags , Just (i, tc) <- lit - = if tc == intTyConName then check i tc (Proxy :: Proxy Int) - + = if -- These only show up via the 'HsOverLit' route - else if tc == int8TyConName then check i tc (Proxy :: Proxy Int8) - else if tc == int16TyConName then check i tc (Proxy :: Proxy Int16) - else if tc == int32TyConName then check i tc (Proxy :: Proxy Int32) - else if tc == int64TyConName then check i tc (Proxy :: Proxy Int64) - else if tc == wordTyConName then check i tc (Proxy :: Proxy Word) - else if tc == word8TyConName then check i tc (Proxy :: Proxy Word8) - else if tc == word16TyConName then check i tc (Proxy :: Proxy Word16) - else if tc == word32TyConName then check i tc (Proxy :: Proxy Word32) - else if tc == word64TyConName then check i tc (Proxy :: Proxy Word64) - else if tc == naturalTyConName then checkPositive i tc + | tc == intTyConName -> check i tc minInt maxInt + | tc == wordTyConName -> check i tc minWord maxWord + | tc == int8TyConName -> check i tc (min' @Int8) (max' @Int8) + | tc == int16TyConName -> check i tc (min' @Int16) (max' @Int16) + | tc == int32TyConName -> check i tc (min' @Int32) (max' @Int32) + | tc == int64TyConName -> check i tc (min' @Int64) (max' @Int64) + | tc == word8TyConName -> check i tc (min' @Word8) (max' @Word8) + | tc == word16TyConName -> check i tc (min' @Word16) (max' @Word16) + | tc == word32TyConName -> check i tc (min' @Word32) (max' @Word32) + | tc == word64TyConName -> check i tc (min' @Word64) (max' @Word64) + | tc == naturalTyConName -> checkPositive i tc -- These only show up via the 'HsLit' route - else if tc == intPrimTyConName then check i tc (Proxy :: Proxy Int) - else if tc == int8PrimTyConName then check i tc (Proxy :: Proxy Int8) - else if tc == int32PrimTyConName then check i tc (Proxy :: Proxy Int32) - else if tc == int64PrimTyConName then check i tc (Proxy :: Proxy Int64) - else if tc == wordPrimTyConName then check i tc (Proxy :: Proxy Word) - else if tc == word8PrimTyConName then check i tc (Proxy :: Proxy Word8) - else if tc == word32PrimTyConName then check i tc (Proxy :: Proxy Word32) - else if tc == word64PrimTyConName then check i tc (Proxy :: Proxy Word64) - - else return () + | tc == intPrimTyConName -> check i tc minInt maxInt + | tc == wordPrimTyConName -> check i tc minWord maxWord + | tc == int8PrimTyConName -> check i tc (min' @Int8) (max' @Int8) + | tc == int16PrimTyConName -> check i tc (min' @Int16) (max' @Int16) + | tc == int32PrimTyConName -> check i tc (min' @Int32) (max' @Int32) + | tc == int64PrimTyConName -> check i tc (min' @Int64) (max' @Int64) + | tc == word8PrimTyConName -> check i tc (min' @Word8) (max' @Word8) + | tc == word16PrimTyConName -> check i tc (min' @Word16) (max' @Word16) + | tc == word32PrimTyConName -> check i tc (min' @Word32) (max' @Word32) + | tc == word64PrimTyConName -> check i tc (min' @Word64) (max' @Word64) + + | otherwise -> return () | otherwise = return () where + -- use target Int/Word sizes! See #17336 + platform = targetPlatform dflags + (minInt,maxInt) = (platformMinInt platform, platformMaxInt platform) + (minWord,maxWord) = (0, platformMaxWord platform) + + min' :: forall a. (Integral a, Bounded a) => Integer + min' = fromIntegral (minBound :: a) + + max' :: forall a. (Integral a, Bounded a) => Integer + max' = fromIntegral (maxBound :: a) checkPositive :: Integer -> Name -> DsM () checkPositive i tc @@ -230,8 +244,7 @@ warnAboutOverflowedLiterals dflags lit <+> ptext (sLit "only supports positive numbers") ]) - check :: forall a. (Bounded a, Integral a) => Integer -> Name -> Proxy a -> DsM () - check i tc _proxy + check i tc minB maxB = when (i < minB || i > maxB) $ warnDs (Reason Opt_WarnOverflowedLiterals) (vcat [ text "Literal" <+> integer i @@ -239,8 +252,6 @@ warnAboutOverflowedLiterals dflags lit <+> integer minB <> text ".." <> integer maxB , sug ]) where - minB = toInteger (minBound :: a) - maxB = toInteger (maxBound :: a) sug | minB == -i -- Note [Suggest NegativeLiterals] , i > 0 , not (xopt LangExt.NegativeLiterals dflags) @@ -268,35 +279,46 @@ warnAboutEmptyEnumerations fam_envs dflags fromExpr mThnExpr toExpr | not $ wopt Opt_WarnEmptyEnumerations dflags = return () -- Numeric Literals - | Just from_ty@(from,_) <- getLHsIntegralLit fromExpr - , Just (_, tc) <- getNormalisedTyconName fam_envs from_ty - , Just mThn <- traverse getLHsIntegralLit mThnExpr - , Just (to,_) <- getLHsIntegralLit toExpr - , let check :: forall a. (Enum a, Num a) => Proxy a -> DsM () - check _proxy - = when (null enumeration) raiseWarning + | Just from_ty@(from',_) <- getLHsIntegralLit fromExpr + , Just (_, tc) <- getNormalisedTyconName fam_envs from_ty + , Just mThn' <- traverse getLHsIntegralLit mThnExpr + , Just (to',_) <- getLHsIntegralLit toExpr + = do + let + check :: forall a. (Integral a, Num a) => DsM () + check = when (null enumeration) raiseWarning where - enumeration :: [a] enumeration = case mThn of - Nothing -> [fromInteger from .. fromInteger to] - Just (thn,_) -> [fromInteger from, fromInteger thn .. fromInteger to] - - = if tc == intTyConName then check (Proxy :: Proxy Int) - else if tc == int8TyConName then check (Proxy :: Proxy Int8) - else if tc == int16TyConName then check (Proxy :: Proxy Int16) - else if tc == int32TyConName then check (Proxy :: Proxy Int32) - else if tc == int64TyConName then check (Proxy :: Proxy Int64) - else if tc == wordTyConName then check (Proxy :: Proxy Word) - else if tc == word8TyConName then check (Proxy :: Proxy Word8) - else if tc == word16TyConName then check (Proxy :: Proxy Word16) - else if tc == word32TyConName then check (Proxy :: Proxy Word32) - else if tc == word64TyConName then check (Proxy :: Proxy Word64) - else if tc == integerTyConName then check (Proxy :: Proxy Integer) - else if tc == naturalTyConName then check (Proxy :: Proxy Integer) - -- We use 'Integer' because otherwise a negative 'Natural' literal - -- could cause a compile time crash (instead of a runtime one). - -- See the T10930b test case for an example of where this matters. - else return () + Nothing -> [from .. to] + Just thn -> [from, thn .. to] + wrap :: forall a. (Integral a, Num a) => Integer -> Integer + wrap i = toInteger (fromIntegral i :: a) + from = wrap @a from' + to = wrap @a to' + mThn = fmap (wrap @a . fst) mThn' + + platform <- targetPlatform <$> getDynFlags + -- Be careful to use target Int/Word sizes! cf #17336 + if | tc == intTyConName -> case platformWordSize platform of + PW4 -> check @Int32 + PW8 -> check @Int64 + | tc == wordTyConName -> case platformWordSize platform of + PW4 -> check @Word32 + PW8 -> check @Word64 + | tc == int8TyConName -> check @Int8 + | tc == int16TyConName -> check @Int16 + | tc == int32TyConName -> check @Int32 + | tc == int64TyConName -> check @Int64 + | tc == word8TyConName -> check @Word8 + | tc == word16TyConName -> check @Word16 + | tc == word32TyConName -> check @Word32 + | tc == word64TyConName -> check @Word64 + | tc == integerTyConName -> check @Integer + | tc == naturalTyConName -> check @Integer + -- We use 'Integer' because otherwise a negative 'Natural' literal + -- could cause a compile time crash (instead of a runtime one). + -- See the T10930b test case for an example of where this matters. + | otherwise -> return () -- Char literals (#18402) | Just fromChar <- getLHsCharLit fromExpr |