elm / compiler

Compiler for Elm, a functional language for reliable webapps.
https://elm-lang.org/
BSD 3-Clause "New" or "Revised" License
7.56k stars 662 forks source link

Large-ish numbers not parsed correctly in Elm 0.18, Windows x64 #1648

Open ebhasker opened 7 years ago

ebhasker commented 7 years ago

This seems to be a... Parser / code gen problem

SSCCE (with core and html libraries)

import Html exposing (Html, text)
main : Html msg
main = text (toString (2^31) ++ " versus " ++ toString (2147483648))

Compiling using elm-make under Windows 10, x64, gives me the following in the resulting javascript:

var _user$project$Main$main = _elm_lang$virtual_dom$Native_VirtualDom.staticProgram(
    _elm_lang$html$Html$text(
        A2(
            _elm_lang$core$Basics_ops['++'],
            _elm_lang$core$Basics$toString(
                Math.pow(2, 31)),
            A2(
                _elm_lang$core$Basics_ops['++'],
                ' versus ',
                _elm_lang$core$Basics$toString(-2147483648)))));

Note the minus sign, specifically. Suspecting a numerical overflow sort of problem.

Embedding this app and opening in any major browser shows the incorrect output:

2147483648 versus -2147483648

Note that this seems specific to the Windows version. Compiling the same example using elm-make under Linux (Ubuntu 16.04, x64 in my case) gives the javascript output:

var _user$project$Main$main = _elm_lang$virtual_dom$Native_VirtualDom.staticProgram(
    _elm_lang$html$Html$text(
        A2(
            _elm_lang$core$Basics_ops['++'],
            _elm_lang$core$Basics$toString(
                Math.pow(2, 31)),
            A2(
                _elm_lang$core$Basics_ops['++'],
                ' versus ',
                _elm_lang$core$Basics$toString(2147483648)))));

Which results in the correct output: 2147483648 versus 2147483648

Note: Numbers less than 2^31 seem to not raise the issue. So hopefully this is also a min-SSCCEE (unless you count negative numbers!)

Please let me know if you need more info about my architecture (i.e. if you cannot reproduce this somehow).

process-bot commented 7 years ago

Thanks for the issue! Make sure it satisfies this checklist. My human colleagues will appreciate it!

Here is what to expect next, and if anyone wants to comment, keep these things in mind.

zwilias commented 7 years ago

1246 for more "int literal weirdness", mentioning it here so these can be batched

ebhasker commented 7 years ago

Thanks @zwilias - that indeed looks extremely relevant.

itsgreggreg commented 7 years ago

Running OSX 10.11.6 and getting the incorrect output.

Ellie link for ref https://ellie-app.com/rjrWfjNdwa1/0

zwilias commented 7 years ago

Ah. Ellie is sort of a separate case (as it involves the elm-compiler compiled to JS through GHCJS), which makes this effect observable on all platforms due to GHCJS only using 32bits for number, independent of the platform is was compiled on.

Nevertheless, it's the same root-cause listed here: https://github.com/elm-lang/elm-compiler/issues/1246#issuecomment-313390964 - Haskell's Int type doesn't guarantee the same range as representable in the integer part of a JS number.

rtfeldman commented 6 years ago

Running OSX 10.11.6 and getting the incorrect output.

I can't reproduce this on 0.19 on macOS.

> String.fromInt (2^31) ++ " versus " ++ String.fromInt (2147483648)
"2147483648 versus 2147483648" : String

It may still be reproducible on Windows though!

dullbananas commented 3 years ago

still present in 0.19?

ebhasker commented 3 years ago

It's not present as is, but if you increase to 64-bit integers, the problem comes back.

For example, in elm repl (0.19.1 on Windows):

>elm repl
---- Elm 0.19.1 ----------------------------------------------------------------
Say :help for help and :exit to exit! More at <https://elm-lang.org/0.19.1/repl>
--------------------------------------------------------------------------------
> 9223372036854775807
9223372036854776000 : number
> 9223372036854775808
-9223372036854776000 : number
>

Overflow because we've reached the limit of 64-bit integers.

Underflow gives a syntax error at least, but for a value that should have been representable (-2^63 = -9223372036854775808)?

> -9223372036854775807
-9223372036854776000 : number
> -9223372036854775808
[stdin]:876
var $author$project$Elm_Repl$repl_input_value_ = --9223372036854775808;
                                                   ^^^^^^^^^^^^^^^^^^^

SyntaxError: Invalid left-hand side expression in prefix operation
    at new Script (vm.js:102:7)
    at createScript (vm.js:262:10)
    at Object.runInThisContext (vm.js:310:10)
    at internal/process/execution.js:81:19
    at [stdin]-wrapper:6:22
    at evalScript (internal/process/execution.js:80:60)
    at internal/main/eval_stdin.js:29:5
    at Socket.<anonymous> (internal/process/execution.js:209:5)
    at Socket.emit (events.js:412:35)
    at endReadableNT (internal/streams/readable.js:1317:12)

Another experiment:

> a: Int
| a = -9223372036854775807
|
-9223372036854776000 : Int
> a = -9223372036854775807
-9223372036854776000 : number
> a: Int
| a = -9223372036854775808
|
[stdin]:876
var $author$project$Elm_Repl$a = --9223372036854775808;
                                   ^^^^^^^^^^^^^^^^^^^

SyntaxError: Invalid left-hand side expression in prefix operation
    at new Script (vm.js:102:7)
    at createScript (vm.js:262:10)
    at Object.runInThisContext (vm.js:310:10)
    at internal/process/execution.js:81:19
    at [stdin]-wrapper:6:22
    at evalScript (internal/process/execution.js:80:60)
    at internal/main/eval_stdin.js:29:5
    at Socket.<anonymous> (internal/process/execution.js:209:5)
    at Socket.emit (events.js:412:35)
    at endReadableNT (internal/streams/readable.js:1317:12)
>

In a bizarre twist, if you add another digit (and preserve the previous ones), you get weirdness again (cycles back to 0):

> -92233720368547758070
[stdin]:876
var $author$project$Elm_Repl$repl_input_value_ = --10;
                                                   ^^

SyntaxError: Invalid left-hand side expression in prefix operation
    at new Script (vm.js:102:7)
    at createScript (vm.js:262:10)
    at Object.runInThisContext (vm.js:310:10)
    at internal/process/execution.js:81:19
    at [stdin]-wrapper:6:22
    at evalScript (internal/process/execution.js:80:60)
    at internal/main/eval_stdin.js:29:5
    at Socket.<anonymous> (internal/process/execution.js:209:5)
    at Socket.emit (events.js:412:35)
    at endReadableNT (internal/streams/readable.js:1317:12)
> -92233720368547758071
[stdin]:876
var $author$project$Elm_Repl$repl_input_value_ = --9;
                                                   ^

SyntaxError: Invalid left-hand side expression in prefix operation
    at new Script (vm.js:102:7)
    at createScript (vm.js:262:10)
    at Object.runInThisContext (vm.js:310:10)
    at internal/process/execution.js:81:19
    at [stdin]-wrapper:6:22
    at evalScript (internal/process/execution.js:80:60)
    at internal/main/eval_stdin.js:29:5
    at Socket.<anonymous> (internal/process/execution.js:209:5)
    at Socket.emit (events.js:412:35)
    at endReadableNT (internal/streams/readable.js:1317:12)
> -92233720368547758080
0 : number
> -9223372036854775808092233720368547758070
[stdin]:876
var $author$project$Elm_Repl$repl_input_value_ = --10;
                                                   ^^

SyntaxError: Invalid left-hand side expression in prefix operation
    at new Script (vm.js:102:7)
    at createScript (vm.js:262:10)
    at Object.runInThisContext (vm.js:310:10)
    at internal/process/execution.js:81:19
    at [stdin]-wrapper:6:22
    at evalScript (internal/process/execution.js:80:60)
    at internal/main/eval_stdin.js:29:5
    at Socket.<anonymous> (internal/process/execution.js:209:5)
    at Socket.emit (events.js:412:35)
    at endReadableNT (internal/streams/readable.js:1317:12)
> -9223372036854775808092233720368547758080
0 : number
> -92233720368547758080922337203685477580809223372036854775808092233720368547758070
[stdin]:876
var $author$project$Elm_Repl$repl_input_value_ = --10;
                                                   ^^

SyntaxError: Invalid left-hand side expression in prefix operation
    at new Script (vm.js:102:7)
    at createScript (vm.js:262:10)
    at Object.runInThisContext (vm.js:310:10)
    at internal/process/execution.js:81:19
    at [stdin]-wrapper:6:22
    at evalScript (internal/process/execution.js:80:60)
    at internal/main/eval_stdin.js:29:5
    at Socket.<anonymous> (internal/process/execution.js:209:5)
    at Socket.emit (events.js:412:35)
    at endReadableNT (internal/streams/readable.js:1317:12)
> -92233720368547758080922337203685477580809223372036854775808092233720368547758080
0 : number

I can keep going, but you get the gist... 😄

The "double negative sign" seems to point to some strange substitution that happens ('-' + '-10' ?)

This seems related to: https://github.com/elm/compiler/issues/1246

I guess the question transforms into, what should Elm do with an integer overflow / underflow?

My vote is for some sort of make error. Or at least a warning that cautions folks about numbers that are too big/small.