Skip to content

Commit 977dd35

Browse files
abelbraaksmabaronfel
authored andcommitted
Allow Int64.MinValue as valid nativeint literal, which it is (fixes #9524) (#9632)
* Fix lexer for -9223372036854775808n * Add tests for nativeint border-values * int32 -9223372036854775808L == 0 * Cleanup/improve code of lexing of integers * Cleanup/improve code of lexing of integers * Oops * Ensure one-time eval of int-to-string
1 parent c18d785 commit 977dd35

File tree

3 files changed

+55
-31
lines changed

3 files changed

+55
-31
lines changed

src/fsharp/LexFilter.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2291,7 +2291,7 @@ type LexFilterImpl (lightSyntaxStatus: LightSyntaxStatus, compilingFsLib, lexer,
22912291
| INT32(v, bad) -> delayMergedToken(INT32((if plus then v else -v), (plus && bad))) // note: '-' makes a 'bad' max int 'good'. '+' does not
22922292
| INT32_DOT_DOT(v, bad) -> delayMergedToken(INT32_DOT_DOT((if plus then v else -v), (plus && bad))) // note: '-' makes a 'bad' max int 'good'. '+' does not
22932293
| INT64(v, bad) -> delayMergedToken(INT64((if plus then v else -v), (plus && bad))) // note: '-' makes a 'bad' max int 'good'. '+' does not
2294-
| NATIVEINT v -> delayMergedToken(NATIVEINT(if plus then v else -v))
2294+
| NATIVEINT(v, bad) -> delayMergedToken(NATIVEINT((if plus then v else -v), (plus && bad))) // note: '-' makes a 'bad' max int 'good'. '+' does not
22952295
| IEEE32 v -> delayMergedToken(IEEE32(if plus then v else -v))
22962296
| IEEE64 v -> delayMergedToken(IEEE64(if plus then v else -v))
22972297
| DECIMAL v -> delayMergedToken(DECIMAL(if plus then v else System.Decimal.op_UnaryNegation v))

src/fsharp/lex.fsl

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,39 @@ open FSharp.Compiler.ParseHelpers
3030
open FSharp.Compiler.Parser
3131
open FSharp.Compiler.SyntaxTree
3232

33+
module Ranges =
34+
/// Whether valid as signed int8 when a minus sign is prepended, compares true to 0x80
35+
let isInt8BadMax x = 1 <<< 7 = x
36+
37+
/// Whether valid as signed int16 when a minus sign is prepended, compares true to 0x8000
38+
let isInt16BadMax x = 1 <<< 15 = x
39+
40+
/// Whether valid as signed int32 when a minus sign is prepended, compares as string against "2147483648".
41+
let isInt32BadMax = let max = string(1UL <<< 31) in fun s -> max = s
42+
43+
/// Whether valid as signed int64 when a minus sign is prepended, compares as string against "9223372036854775808".
44+
let isInt64BadMax = let max = string(1UL <<< 63) in fun s -> max = s
45+
46+
/// Get string from lexbuf
3347
let lexeme (lexbuf : UnicodeLexing.Lexbuf) = UnicodeLexing.Lexbuf.LexemeString lexbuf
3448

49+
/// Trim n chars from both side of a string
3550
let trimBoth (s:string) n m = s.Substring(n, s.Length - (n+m))
3651

52+
/// Trim n chars from both sides of lexbuf, return string
3753
let lexemeTrimBoth lexbuf n m = trimBoth (lexeme lexbuf) n m
3854

55+
/// Trim n chars from the right of lexbuf, return string
3956
let lexemeTrimRight lexbuf n = lexemeTrimBoth lexbuf 0 n
4057

58+
/// Trim n chars from the left of lexbuf, return string
4159
let lexemeTrimLeft lexbuf n = lexemeTrimBoth lexbuf n 0
4260

61+
/// Throw a lexing error with a message
4362
let fail args (lexbuf:UnicodeLexing.Lexbuf) msg dflt =
4463
let m = lexbuf.LexemeRange
4564
args.errorLogger.ErrorR(Error(msg,m))
46-
dflt
65+
dflt
4766

4867
//--------------------------
4968
// Integer parsing
@@ -313,88 +332,88 @@ rule token args skip = parse
313332

314333
| int8
315334
{ let n = lexemeTrimRightToInt32 args lexbuf 1
316-
if n > 0x80 || n < -0x80 then fail args lexbuf (FSComp.SR.lexOutsideEightBitSigned()) (INT8(0y,false))
317335
// Allow <max_int+1> to parse as min_int. Allowed only because we parse '-' as an operator.
318-
else if n = 0x80 then INT8(sbyte(-0x80), true (* 'true' = 'bad'*) )
319-
else INT8(sbyte n,false) }
336+
if Ranges.isInt8BadMax n then INT8(SByte.MinValue, true (* 'true' = 'bad'*) )
337+
else if n > int SByte.MaxValue || n < int SByte.MinValue then fail args lexbuf (FSComp.SR.lexOutsideEightBitSigned()) (INT8(0y, false))
338+
else INT8(sbyte n, false) }
320339

321340
| xint8
322341
{ let n = lexemeTrimRightToInt32 args lexbuf 1
323-
if n > 0xFF || n < 0 then fail args lexbuf (FSComp.SR.lexOutsideEightBitSignedHex()) (INT8(0y,false))
324-
else INT8(sbyte(byte(n)),false) }
342+
if n > int Byte.MaxValue || n < 0 then fail args lexbuf (FSComp.SR.lexOutsideEightBitSignedHex()) (INT8(0y, false))
343+
else INT8(sbyte(byte(n)), false) }
325344

326345
| uint8
327346
{ let n = lexemeTrimRightToInt32 args lexbuf 2
328-
if n > 0xFF || n < 0 then fail args lexbuf (FSComp.SR.lexOutsideEightBitUnsigned()) (UINT8(0uy))
329-
else UINT8(byte n) }
347+
if n > int Byte.MaxValue || n < 0 then fail args lexbuf (FSComp.SR.lexOutsideEightBitUnsigned()) (UINT8(0uy))
348+
else UINT8(byte n) }
330349

331350
| int16
332351
{ let n = lexemeTrimRightToInt32 args lexbuf 1
333-
if n > 0x8000 || n < -0x8000 then fail args lexbuf (FSComp.SR.lexOutsideSixteenBitSigned()) (INT16(0s,false))
334352
// Allow <max_int+1> to parse as min_int. Allowed only because we parse '-' as an operator.
335-
else if n = 0x8000 then INT16(-0x8000s,true)
336-
else INT16(int16 n,false) }
353+
if Ranges.isInt16BadMax n then INT16(Int16.MinValue, true (* 'true' = 'bad'*) )
354+
else if n > int Int16.MaxValue || n < int Int16.MinValue then fail args lexbuf (FSComp.SR.lexOutsideSixteenBitSigned()) (INT16(0s, false))
355+
else INT16(int16 n, false) }
337356

338357
| xint16
339358
{ let n = lexemeTrimRightToInt32 args lexbuf 1
340-
if n > 0xFFFF || n < 0 then fail args lexbuf (FSComp.SR.lexOutsideSixteenBitSigned()) (INT16(0s,false))
341-
else INT16(int16(uint16(n)),false) }
359+
if n > int UInt16.MaxValue || n < 0 then fail args lexbuf (FSComp.SR.lexOutsideSixteenBitSigned()) (INT16(0s,false))
360+
else INT16(int16(uint16(n)), false) }
342361

343362
| uint16
344363
{ let n = lexemeTrimRightToInt32 args lexbuf 2
345-
if n > 0xFFFF || n < 0 then fail args lexbuf (FSComp.SR.lexOutsideSixteenBitUnsigned()) (UINT16(0us))
364+
if n > int UInt16.MaxValue || n < 0 then fail args lexbuf (FSComp.SR.lexOutsideSixteenBitUnsigned()) (UINT16(0us))
346365
else UINT16(uint16 n) }
347366

348367
| int '.' '.'
349368
{ let s = removeUnderscores (lexemeTrimRight lexbuf 2)
350369
// Allow <max_int+1> to parse as min_int. Allowed only because we parse '-' as an operator.
351-
if s = "2147483648" then INT32_DOT_DOT(-2147483648,true) else
370+
if Ranges.isInt32BadMax s then INT32_DOT_DOT(Int32.MinValue, true (* 'true' = 'bad'*) ) else
352371
let n = try int32 s with _ -> fail args lexbuf (FSComp.SR.lexOutsideThirtyTwoBitSigned()) 0
353-
INT32_DOT_DOT(n,false)
372+
INT32_DOT_DOT(n, false)
354373
}
355374

356375
| xint
357376
| int
358377
{ let s = removeUnderscores (lexeme lexbuf)
359378
// Allow <max_int+1> to parse as min_int. Allowed only because we parse '-' as an operator.
360-
if s = "2147483648" then INT32(-2147483648,true) else
379+
if Ranges.isInt32BadMax s then INT32(Int32.MinValue, true (* 'true' = 'bad'*) ) else
361380
let n =
362381
try int32 s with _ -> fail args lexbuf (FSComp.SR.lexOutsideThirtyTwoBitSigned()) 0
363-
INT32(n,false)
382+
INT32(n, false)
364383
}
365384

366385
| xint32
367386
| int32
368387
{ let s = removeUnderscores (lexemeTrimRight lexbuf 1)
369388
// Allow <max_int+1> to parse as min_int. Allowed only because we parse '-' as an operator.
370-
if s = "2147483648" then INT32(-2147483648,true) else
371-
let n =
389+
if Ranges.isInt32BadMax s then INT32(Int32.MinValue, true (* 'true' = 'bad'*) ) else
390+
let n =
372391
try int32 s with _ -> fail args lexbuf (FSComp.SR.lexOutsideThirtyTwoBitSigned()) 0
373-
INT32(n,false)
392+
INT32(n, false)
374393
}
375394

376395
| uint32
377396
{
378397
let s = removeUnderscores (lexemeTrimRight lexbuf 1)
379398
let n =
380399
try int64 s with _ -> fail args lexbuf (FSComp.SR.lexOutsideThirtyTwoBitUnsigned()) 0L
381-
if n > 0xFFFFFFFFL || n < 0L then fail args lexbuf (FSComp.SR.lexOutsideThirtyTwoBitUnsigned()) (UINT32(0u)) else
400+
if n > int64 UInt32.MaxValue || n < 0L then fail args lexbuf (FSComp.SR.lexOutsideThirtyTwoBitUnsigned()) (UINT32(0u)) else
382401
UINT32(uint32 (uint64 n)) }
383402

384403
| uint32l
385404
{
386405
let s = removeUnderscores (lexemeTrimRight lexbuf 2)
387406
let n =
388407
try int64 s with _ -> fail args lexbuf (FSComp.SR.lexOutsideThirtyTwoBitUnsigned()) 0L
389-
if n > 0xFFFFFFFFL || n < 0L then fail args lexbuf (FSComp.SR.lexOutsideThirtyTwoBitUnsigned()) (UINT32(0u)) else
408+
if n > int64 UInt32.MaxValue || n < 0L then fail args lexbuf (FSComp.SR.lexOutsideThirtyTwoBitUnsigned()) (UINT32(0u)) else
390409
UINT32(uint32 (uint64 n)) }
391410

392411
| int64
393412
{ let s = removeUnderscores (lexemeTrimRight lexbuf 1)
394413
// Allow <max_int+1> to parse as min_int. Stupid but allowed because we parse '-' as an operator.
395-
if s = "9223372036854775808" then INT64(-9223372036854775808L,true) else
414+
if Ranges.isInt64BadMax s then INT64(Int64.MinValue, true (* 'true' = 'bad'*) ) else
396415
let n =
397-
try int64 s with _ -> fail args lexbuf (FSComp.SR.lexOutsideSixtyFourBitSigned()) 0L
416+
try int64 s with _ -> fail args lexbuf (FSComp.SR.lexOutsideSixtyFourBitSigned()) 0L
398417
INT64(n,false)
399418
}
400419

@@ -405,9 +424,13 @@ rule token args skip = parse
405424
UINT64(n) }
406425

407426
| nativeint
408-
{ try
409-
NATIVEINT(int64 (removeUnderscores (lexemeTrimRight lexbuf 1)))
410-
with _ -> fail args lexbuf (FSComp.SR.lexOutsideNativeSigned()) (NATIVEINT(0L)) }
427+
{ let s = removeUnderscores (lexemeTrimRight lexbuf 1)
428+
// Allow <max_nativeint+1> to parse as min_nativeint. Stupid but allowed because we parse '-' as an operator.
429+
if Ranges.isInt64BadMax s then NATIVEINT(Int64.MinValue, true) else
430+
let n =
431+
try int64 s with _ -> fail args lexbuf (FSComp.SR.lexOutsideNativeSigned()) 0L
432+
NATIVEINT(n,false)
433+
}
411434

412435
| unativeint
413436
{ try

src/fsharp/pars.fsy

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,13 +175,13 @@ let rangeOfLongIdent(lid:LongIdent) =
175175
%token <int16 * bool> INT16
176176
%token <int32 * bool> INT32 INT32_DOT_DOT
177177
%token <int64 * bool> INT64
178+
%token <int64 * bool> NATIVEINT
178179

179180
%token <byte> UINT8
180181
%token <uint16> UINT16
181182
%token <uint32> UINT32
182183
%token <uint64> UINT64
183184
%token <uint64> UNATIVEINT
184-
%token <int64> NATIVEINT
185185
%token <single> IEEE32
186186
%token <double> IEEE64
187187
%token <char> CHAR
@@ -2808,7 +2808,8 @@ rawConstant:
28082808
{ SynConst.UInt64 $1 }
28092809

28102810
| NATIVEINT
2811-
{ SynConst.IntPtr $1 }
2811+
{ if snd $1 then errorR(Error(FSComp.SR.lexOutsideNativeSigned(), lhs parseState))
2812+
SynConst.IntPtr (fst $1) }
28122813

28132814
| UNATIVEINT
28142815
{ SynConst.UIntPtr $1 }

0 commit comments

Comments
 (0)