language: Check rational precision for rational literals. (#514)

* language: Check rational precision for rational literals.

A value like 0.00000000005 was truncated to 0.000000000. This change
will throw an error instead and abort the compilation. It also checks
for maximal/minimal bounds.
This commit is contained in:
Robin Krom 2019-04-16 12:51:48 +02:00 committed by GitHub
parent f1b077dd1a
commit 9f0b74176f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 90 additions and 2 deletions

View File

@ -101,12 +101,12 @@ import Data.List.Extra
import qualified Data.Map.Strict as MS
import Data.Maybe
import qualified Data.NameMap as NM
import Data.Ratio
import qualified Data.Set as Set
import Data.Tagged
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import Data.Tuple.Extra
import Data.Ratio
import "ghc-lib" GHC
import "ghc-lib" GhcPlugins as GHC hiding ((<>))
import "ghc-lib-parser" Pair
@ -240,6 +240,27 @@ convertInt64 x
| otherwise =
unsupported "Int literal out of bounds" (negate x)
convertRational :: Integer -> Integer -> ConvertM LF.Expr
convertRational num denom
=
-- the denominator needs to be a divisor of 10^10.
-- num % denom * 10^10 needs to fit within a 128bit signed number.
-- note that we can also get negative rationals here, hence we ask for upperBound128Bit - 1 as
-- upper limit.
if | 10 ^ maxPrecision `mod` denom == 0 &&
abs (r * 10 ^ maxPrecision) <= upperBound128Bit - 1 ->
pure $ EBuiltin $ BEDecimal $ fromRational r
| otherwise ->
unsupported
("Rational is out of bounds: " ++
show ((fromInteger num / fromInteger denom) :: Double) ++
". Maximal supported precision is e^-10, maximal range after multiplying with 10^10 is [10^38 -1, -10^38 + 1]")
(num, denom)
where
r = num % denom
upperBound128Bit = 10 ^ (38 :: Integer)
maxPrecision = 10 :: Integer
convertModule :: LF.Version -> MS.Map UnitId T.Text -> GhcModule -> Either Diagnostic LF.Module
convertModule lfVersion pkgMap mod0 = runConvertM (ConversionEnv (gmPath mod0) Nothing) $ do
definitions <- concat <$> traverse (convertBind env) (cm_binds x)
@ -588,7 +609,7 @@ convertExpr env0 e = do
ETmLam (varV1, field') $ ETmLam (varV2, record') $
ERecUpd (fromTCon record') (mkField $ unpackFS name) (EVar varV2) (EVar varV1)
go env (VarIs "fromRational") (LExpr (VarIs ":%" `App` tyInteger `App` Lit (LitNumber _ top _) `App` Lit (LitNumber _ bot _)) : args)
= fmap (, args) $ pure $ EBuiltin $ BEDecimal $ fromRational $ top % bot
= fmap (, args) $ convertRational top bot
go env (VarIs "negate") (tyInt : LExpr (VarIs "$fAdditiveInt") : LExpr (untick -> VarIs "fromInteger" `App` Lit (LitNumber _ x _)) : args)
= fmap (, args) $ convertInt64 (negate x)
go env (VarIs "fromInteger") (LExpr (Lit (LitNumber _ x _)) : args)

View File

@ -0,0 +1,12 @@
-- Copyright (c) 2019, Digital Asset (Switzerland) GmbH and/or its affiliates.
-- All rights reserved.
-- Test that rational negative literals bigger or equal -10^38 + 1 fail.
-- @ERROR Rational is out of bounds
daml 1.2
module RationalLowerBoundError where
-- -10^38 / 10^10
a = -10000000000000000000000000000.0000000000

View File

@ -0,0 +1,11 @@
-- Copyright (c) 2019, Digital Asset (Switzerland) GmbH and/or its affiliates.
-- All rights reserved.
-- Test that rational negative literals bigger or equal -10^38 + 1 pass.
daml 1.2
module RationalLowerBoundMax where
-- -10^38 + 1 / 10^10
a = -9999999999999999999999999999.9999999999

View File

@ -0,0 +1,10 @@
-- Copyright (c) 2019, Digital Asset (Switzerland) GmbH and/or its affiliates.
-- All rights reserved.
-- Test that the maximal precision of e^-10 in rational literals passes.
daml 1.2
module RationalPrecisionUpperBound where
a = 0.0000000005

View File

@ -0,0 +1,11 @@
-- Copyright (c) 2019, Digital Asset (Switzerland) GmbH and/or its affiliates.
-- All rights reserved.
-- Test that rational literals with precision higher then e^-10 fail.
-- @ERROR Rational is out of bounds
daml 1.2
module RationalPrecisionUpperBoundError where
a = 0.00000000005

View File

@ -0,0 +1,12 @@
-- Copyright (c) 2019, Digital Asset (Switzerland) GmbH and/or its affiliates.
-- All rights reserved.
-- Test that rational positive literals fail when bigger than 10^38 -1 after multiplying with 10^10.
-- @ERROR Rational is out of bounds
daml 1.2
module RationalUpperBound where
-- 10^38 / 10^10
a = 10000000000000000000000000000.0000000000

View File

@ -0,0 +1,11 @@
-- Copyright (c) 2019, Digital Asset (Switzerland) GmbH and/or its affiliates.
-- All rights reserved.
-- Test that rational positive literals smaller or equal 10^38 -1 after multiplying with 10^10 pass.
daml 1.2
module RationalUpperBoundMax where
-- 10^38 -1 / 10^10
a = 9999999999999999999999999999.9999999999