diff options
| author | Paul Eggert | 2018-03-29 10:16:29 -0700 |
|---|---|---|
| committer | Paul Eggert | 2018-03-29 11:03:07 -0700 |
| commit | 6b3d01dad415230ad0bd0d01a05351d7a8b0e8c3 (patch) | |
| tree | d49bf4520d6afc830aa9d016be3d57349b251c4f | |
| parent | 3409fe0362c52127c52f854a7300f4dde4b8fffe (diff) | |
| download | emacs-6b3d01dad415230ad0bd0d01a05351d7a8b0e8c3.tar.gz emacs-6b3d01dad415230ad0bd0d01a05351d7a8b0e8c3.zip | |
Lisp reader now checks for integer overflow
* doc/lispref/numbers.texi (Integer Basics), etc/NEWS:
Document this.
* src/lisp.h (S2N_IGNORE_TRAILING, S2N_OVERFLOW_TO_FLOAT):
New constants.
* src/lread.c (string_to_number): Change trailing bool arg to
integer argument with flags, to support S2N_OVERFLOW_TO_FLOAT.
All uses changed.
* test/src/editfns-tests.el (read-large-integer): New test.
| -rw-r--r-- | doc/lispref/numbers.texi | 14 | ||||
| -rw-r--r-- | etc/NEWS | 7 | ||||
| -rw-r--r-- | src/data.c | 9 | ||||
| -rw-r--r-- | src/lisp.h | 3 | ||||
| -rw-r--r-- | src/lread.c | 35 | ||||
| -rw-r--r-- | src/process.c | 2 | ||||
| -rw-r--r-- | test/src/editfns-tests.el | 22 |
7 files changed, 62 insertions, 30 deletions
diff --git a/doc/lispref/numbers.texi b/doc/lispref/numbers.texi index c2cb6651d47..2fed2b642fd 100644 --- a/doc/lispref/numbers.texi +++ b/doc/lispref/numbers.texi | |||
| @@ -55,16 +55,13 @@ chapter assume the minimum integer width of 30 bits. | |||
| 55 | 55 | ||
| 56 | The Lisp reader reads an integer as a nonempty sequence | 56 | The Lisp reader reads an integer as a nonempty sequence |
| 57 | of decimal digits with optional initial sign and optional | 57 | of decimal digits with optional initial sign and optional |
| 58 | final period. A decimal integer that is out of the | 58 | final period. |
| 59 | Emacs range is treated as a floating-point number. | ||
| 60 | 59 | ||
| 61 | @example | 60 | @example |
| 62 | 1 ; @r{The integer 1.} | 61 | 1 ; @r{The integer 1.} |
| 63 | 1. ; @r{The integer 1.} | 62 | 1. ; @r{The integer 1.} |
| 64 | +1 ; @r{Also the integer 1.} | 63 | +1 ; @r{Also the integer 1.} |
| 65 | -1 ; @r{The integer @minus{}1.} | 64 | -1 ; @r{The integer @minus{}1.} |
| 66 | 9000000000000000000 | ||
| 67 | ; @r{The floating-point number 9e18.} | ||
| 68 | 0 ; @r{The integer 0.} | 65 | 0 ; @r{The integer 0.} |
| 69 | -0 ; @r{The integer 0.} | 66 | -0 ; @r{The integer 0.} |
| 70 | @end example | 67 | @end example |
| @@ -94,6 +91,15 @@ For example: | |||
| 94 | #24r1k @result{} 44 | 91 | #24r1k @result{} 44 |
| 95 | @end example | 92 | @end example |
| 96 | 93 | ||
| 94 | If an integer is outside the Emacs range, the Lisp reader ordinarily | ||
| 95 | signals an overflow. However, if a too-large plain integer ends in a | ||
| 96 | period, the Lisp reader treats it as a floating-point number instead. | ||
| 97 | This lets an Emacs Lisp program specify a large integer that is | ||
| 98 | quietly approximated by a floating-point number on machines with | ||
| 99 | limited word width. For example, @samp{536870912.} is a | ||
| 100 | floating-point number if Emacs integers are only 30 bits wide and is | ||
| 101 | an integer otherwise. | ||
| 102 | |||
| 97 | To understand how various functions work on integers, especially the | 103 | To understand how various functions work on integers, especially the |
| 98 | bitwise operators (@pxref{Bitwise Operations}), it is often helpful to | 104 | bitwise operators (@pxref{Bitwise Operations}), it is often helpful to |
| 99 | view the numbers in their binary form. | 105 | view the numbers in their binary form. |
| @@ -352,6 +352,13 @@ as new-style, bind the new variable 'force-new-style-backquotes' to t. | |||
| 352 | integer, Emacs now signals an error if the number is too large for the | 352 | integer, Emacs now signals an error if the number is too large for the |
| 353 | implementation to format (Bug#30408). | 353 | implementation to format (Bug#30408). |
| 354 | 354 | ||
| 355 | +++ | ||
| 356 | ** The Lisp reader now signals an overflow for plain decimal integers | ||
| 357 | that do not end in '.' and are outside Emacs range. Formerly the Lisp | ||
| 358 | reader silently converted them to floating-point numbers, and signaled | ||
| 359 | overflow only for integers with a radix that are outside machine range | ||
| 360 | (Bug#30408). | ||
| 361 | |||
| 355 | --- | 362 | --- |
| 356 | ** Some functions and variables obsolete since Emacs 22 have been removed: | 363 | ** Some functions and variables obsolete since Emacs 22 have been removed: |
| 357 | archive-mouse-extract, assoc-ignore-case, assoc-ignore-representation, | 364 | archive-mouse-extract, assoc-ignore-case, assoc-ignore-representation, |
diff --git a/src/data.c b/src/data.c index a7fab1ef58a..6f23a26757a 100644 --- a/src/data.c +++ b/src/data.c | |||
| @@ -2716,9 +2716,7 @@ present, base 10 is used. BASE must be between 2 and 16 (inclusive). | |||
| 2716 | If the base used is not 10, STRING is always parsed as an integer. */) | 2716 | If the base used is not 10, STRING is always parsed as an integer. */) |
| 2717 | (register Lisp_Object string, Lisp_Object base) | 2717 | (register Lisp_Object string, Lisp_Object base) |
| 2718 | { | 2718 | { |
| 2719 | register char *p; | 2719 | int b; |
| 2720 | register int b; | ||
| 2721 | Lisp_Object val; | ||
| 2722 | 2720 | ||
| 2723 | CHECK_STRING (string); | 2721 | CHECK_STRING (string); |
| 2724 | 2722 | ||
| @@ -2732,11 +2730,12 @@ If the base used is not 10, STRING is always parsed as an integer. */) | |||
| 2732 | b = XINT (base); | 2730 | b = XINT (base); |
| 2733 | } | 2731 | } |
| 2734 | 2732 | ||
| 2735 | p = SSDATA (string); | 2733 | char *p = SSDATA (string); |
| 2736 | while (*p == ' ' || *p == '\t') | 2734 | while (*p == ' ' || *p == '\t') |
| 2737 | p++; | 2735 | p++; |
| 2738 | 2736 | ||
| 2739 | val = string_to_number (p, b, true); | 2737 | int flags = S2N_IGNORE_TRAILING | S2N_OVERFLOW_TO_FLOAT; |
| 2738 | Lisp_Object val = string_to_number (p, b, flags); | ||
| 2740 | return NILP (val) ? make_number (0) : val; | 2739 | return NILP (val) ? make_number (0) : val; |
| 2741 | } | 2740 | } |
| 2742 | 2741 | ||
diff --git a/src/lisp.h b/src/lisp.h index f0c0c5a14a5..b931d23bf38 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -3899,7 +3899,8 @@ LOADHIST_ATTACH (Lisp_Object x) | |||
| 3899 | } | 3899 | } |
| 3900 | extern int openp (Lisp_Object, Lisp_Object, Lisp_Object, | 3900 | extern int openp (Lisp_Object, Lisp_Object, Lisp_Object, |
| 3901 | Lisp_Object *, Lisp_Object, bool); | 3901 | Lisp_Object *, Lisp_Object, bool); |
| 3902 | extern Lisp_Object string_to_number (char const *, int, bool); | 3902 | enum { S2N_IGNORE_TRAILING = 1, S2N_OVERFLOW_TO_FLOAT = 2 }; |
| 3903 | extern Lisp_Object string_to_number (char const *, int, int); | ||
| 3903 | extern void map_obarray (Lisp_Object, void (*) (Lisp_Object, Lisp_Object), | 3904 | extern void map_obarray (Lisp_Object, void (*) (Lisp_Object, Lisp_Object), |
| 3904 | Lisp_Object); | 3905 | Lisp_Object); |
| 3905 | extern void dir_warning (const char *, Lisp_Object); | 3906 | extern void dir_warning (const char *, Lisp_Object); |
diff --git a/src/lread.c b/src/lread.c index 381f9cf20c5..a774524ee43 100644 --- a/src/lread.c +++ b/src/lread.c | |||
| @@ -2339,7 +2339,7 @@ character_name_to_code (char const *name, ptrdiff_t name_len) | |||
| 2339 | monstrosities like "U+-0000". */ | 2339 | monstrosities like "U+-0000". */ |
| 2340 | Lisp_Object code | 2340 | Lisp_Object code |
| 2341 | = (name[0] == 'U' && name[1] == '+' | 2341 | = (name[0] == 'U' && name[1] == '+' |
| 2342 | ? string_to_number (name + 1, 16, false) | 2342 | ? string_to_number (name + 1, 16, 0) |
| 2343 | : call2 (Qchar_from_name, make_unibyte_string (name, name_len), Qt)); | 2343 | : call2 (Qchar_from_name, make_unibyte_string (name, name_len), Qt)); |
| 2344 | 2344 | ||
| 2345 | if (! RANGED_INTEGERP (0, code, MAX_UNICODE_CHAR) | 2345 | if (! RANGED_INTEGERP (0, code, MAX_UNICODE_CHAR) |
| @@ -2693,7 +2693,7 @@ read_integer (Lisp_Object readcharfun, EMACS_INT radix) | |||
| 2693 | invalid_syntax (buf); | 2693 | invalid_syntax (buf); |
| 2694 | } | 2694 | } |
| 2695 | 2695 | ||
| 2696 | return string_to_number (buf, radix, false); | 2696 | return string_to_number (buf, radix, 0); |
| 2697 | } | 2697 | } |
| 2698 | 2698 | ||
| 2699 | 2699 | ||
| @@ -3502,7 +3502,7 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list) | |||
| 3502 | 3502 | ||
| 3503 | if (!quoted && !uninterned_symbol) | 3503 | if (!quoted && !uninterned_symbol) |
| 3504 | { | 3504 | { |
| 3505 | Lisp_Object result = string_to_number (read_buffer, 10, false); | 3505 | Lisp_Object result = string_to_number (read_buffer, 10, 0); |
| 3506 | if (! NILP (result)) | 3506 | if (! NILP (result)) |
| 3507 | return unbind_to (count, result); | 3507 | return unbind_to (count, result); |
| 3508 | } | 3508 | } |
| @@ -3667,16 +3667,17 @@ substitute_in_interval (INTERVAL interval, void *arg) | |||
| 3667 | } | 3667 | } |
| 3668 | 3668 | ||
| 3669 | 3669 | ||
| 3670 | /* Convert STRING to a number, assuming base BASE. Return a fixnum if | 3670 | /* Convert STRING to a number, assuming base BASE. When STRING has |
| 3671 | STRING has integer syntax and fits in a fixnum, else return the | 3671 | floating point syntax and BASE is 10, return a nearest float. When |
| 3672 | nearest float if STRING has either floating point or integer syntax | 3672 | STRING has integer syntax, return a fixnum if the integer fits, and |
| 3673 | and BASE is 10, else return nil. If IGNORE_TRAILING, consider just | 3673 | signal an overflow otherwise (unless BASE is 10 and STRING ends in |
| 3674 | the longest prefix of STRING that has valid floating point syntax. | 3674 | period or FLAGS & S2N_OVERFLOW_TO_FLOAT is nonzero; in this case, |
| 3675 | Signal an overflow if BASE is not 10 and the number has integer | 3675 | return a nearest float instead). Otherwise, return nil. If FLAGS |
| 3676 | syntax but does not fit. */ | 3676 | & S2N_IGNORE_TRAILING is nonzero, consider just the longest prefix |
| 3677 | of STRING that has valid syntax. */ | ||
| 3677 | 3678 | ||
| 3678 | Lisp_Object | 3679 | Lisp_Object |
| 3679 | string_to_number (char const *string, int base, bool ignore_trailing) | 3680 | string_to_number (char const *string, int base, int flags) |
| 3680 | { | 3681 | { |
| 3681 | char const *cp = string; | 3682 | char const *cp = string; |
| 3682 | bool float_syntax = 0; | 3683 | bool float_syntax = 0; |
| @@ -3759,9 +3760,10 @@ string_to_number (char const *string, int base, bool ignore_trailing) | |||
| 3759 | || (state & ~INTOVERFLOW) == (LEAD_INT|E_EXP)); | 3760 | || (state & ~INTOVERFLOW) == (LEAD_INT|E_EXP)); |
| 3760 | } | 3761 | } |
| 3761 | 3762 | ||
| 3762 | /* Return nil if the number uses invalid syntax. If IGNORE_TRAILING, accept | 3763 | /* Return nil if the number uses invalid syntax. If FLAGS & |
| 3763 | any prefix that matches. Otherwise, the entire string must match. */ | 3764 | S2N_IGNORE_TRAILING, accept any prefix that matches. Otherwise, |
| 3764 | if (! (ignore_trailing | 3765 | the entire string must match. */ |
| 3766 | if (! (flags & S2N_IGNORE_TRAILING | ||
| 3765 | ? ((state & LEAD_INT) != 0 || float_syntax) | 3767 | ? ((state & LEAD_INT) != 0 || float_syntax) |
| 3766 | : (!*cp && ((state & ~(INTOVERFLOW | DOT_CHAR)) == LEAD_INT | 3768 | : (!*cp && ((state & ~(INTOVERFLOW | DOT_CHAR)) == LEAD_INT |
| 3767 | || float_syntax)))) | 3769 | || float_syntax)))) |
| @@ -3776,7 +3778,7 @@ string_to_number (char const *string, int base, bool ignore_trailing) | |||
| 3776 | /* Unfortunately there's no simple and accurate way to convert | 3778 | /* Unfortunately there's no simple and accurate way to convert |
| 3777 | non-base-10 numbers that are out of C-language range. */ | 3779 | non-base-10 numbers that are out of C-language range. */ |
| 3778 | if (base != 10) | 3780 | if (base != 10) |
| 3779 | xsignal1 (Qoverflow_error, build_string (string)); | 3781 | flags = 0; |
| 3780 | } | 3782 | } |
| 3781 | else if (n <= (negative ? -MOST_NEGATIVE_FIXNUM : MOST_POSITIVE_FIXNUM)) | 3783 | else if (n <= (negative ? -MOST_NEGATIVE_FIXNUM : MOST_POSITIVE_FIXNUM)) |
| 3782 | { | 3784 | { |
| @@ -3785,6 +3787,9 @@ string_to_number (char const *string, int base, bool ignore_trailing) | |||
| 3785 | } | 3787 | } |
| 3786 | else | 3788 | else |
| 3787 | value = n; | 3789 | value = n; |
| 3790 | |||
| 3791 | if (! (state & DOT_CHAR) && ! (flags & S2N_OVERFLOW_TO_FLOAT)) | ||
| 3792 | xsignal1 (Qoverflow_error, build_string (string)); | ||
| 3788 | } | 3793 | } |
| 3789 | 3794 | ||
| 3790 | /* Either the number uses float syntax, or it does not fit into a fixnum. | 3795 | /* Either the number uses float syntax, or it does not fit into a fixnum. |
diff --git a/src/process.c b/src/process.c index 2aaa238f60c..ed2cab7b51f 100644 --- a/src/process.c +++ b/src/process.c | |||
| @@ -6842,7 +6842,7 @@ SIGCODE may be an integer, or a symbol whose name is a signal name. */) | |||
| 6842 | { | 6842 | { |
| 6843 | Lisp_Object tem = Fget_process (process); | 6843 | Lisp_Object tem = Fget_process (process); |
| 6844 | if (NILP (tem)) | 6844 | if (NILP (tem)) |
| 6845 | tem = string_to_number (SSDATA (process), 10, true); | 6845 | tem = string_to_number (SSDATA (process), 10, S2N_OVERFLOW_TO_FLOAT); |
| 6846 | process = tem; | 6846 | process = tem; |
| 6847 | } | 6847 | } |
| 6848 | else if (!NUMBERP (process)) | 6848 | else if (!NUMBERP (process)) |
diff --git a/test/src/editfns-tests.el b/test/src/editfns-tests.el index 6e1f730166d..442ad089375 100644 --- a/test/src/editfns-tests.el +++ b/test/src/editfns-tests.el | |||
| @@ -142,27 +142,41 @@ | |||
| 142 | (should (string-equal (format "%#05X" #x10) "0X010")) | 142 | (should (string-equal (format "%#05X" #x10) "0X010")) |
| 143 | (should (string-equal (format "%#04x" 0) "0000"))) | 143 | (should (string-equal (format "%#04x" 0) "0000"))) |
| 144 | 144 | ||
| 145 | ;;; Test Bug#30408. | 145 | |
| 146 | ;;; Tests for Bug#30408. | ||
| 147 | |||
| 146 | (ert-deftest format-%d-large-float () | 148 | (ert-deftest format-%d-large-float () |
| 147 | (should (string-equal (format "%d" 18446744073709551616.0) | 149 | (should (string-equal (format "%d" 18446744073709551616.0) |
| 148 | "18446744073709551616")) | 150 | "18446744073709551616")) |
| 149 | (should (string-equal (format "%d" -18446744073709551616.0) | 151 | (should (string-equal (format "%d" -18446744073709551616.0) |
| 150 | "-18446744073709551616"))) | 152 | "-18446744073709551616"))) |
| 151 | 153 | ||
| 152 | ;;; Another test for Bug#30408. | ||
| 153 | ;;; Perhaps Emacs will be improved someday to return the correct | 154 | ;;; Perhaps Emacs will be improved someday to return the correct |
| 154 | ;;; answer for positive numbers instead of overflowing; in | 155 | ;;; answer for positive numbers instead of overflowing; in |
| 155 | ;;; that case this test will need to be changed. In the meantime make | 156 | ;;; that case these tests will need to be changed. In the meantime make |
| 156 | ;;; sure Emacs is reporting the overflow correctly. | 157 | ;;; sure Emacs is reporting the overflow correctly. |
| 157 | (ert-deftest format-%x-large-float () | 158 | (ert-deftest format-%x-large-float () |
| 158 | (should-error (format "%x" 18446744073709551616.0) | 159 | (should-error (format "%x" 18446744073709551616.0) |
| 159 | :type 'overflow-error)) | 160 | :type 'overflow-error)) |
| 161 | (ert-deftest read-large-integer () | ||
| 162 | (should-error (read (format "%d0" most-negative-fixnum)) | ||
| 163 | :type 'overflow-error) | ||
| 164 | (should-error (read (format "%+d" (* -8.0 most-negative-fixnum))) | ||
| 165 | :type 'overflow-error) | ||
| 166 | (should-error (read (substring (format "%d" most-negative-fixnum) 1)) | ||
| 167 | :type 'overflow-error) | ||
| 168 | (should-error (read (format "#x%x" most-negative-fixnum)) | ||
| 169 | :type 'overflow-error) | ||
| 170 | (should-error (read (format "#o%o" most-negative-fixnum)) | ||
| 171 | :type 'overflow-error) | ||
| 172 | (should-error (read (format "#32rG%x" most-positive-fixnum)) | ||
| 173 | :type 'overflow-error)) | ||
| 160 | 174 | ||
| 161 | ;;; Another test for Bug#30408. | ||
| 162 | (ert-deftest format-%o-invalid-float () | 175 | (ert-deftest format-%o-invalid-float () |
| 163 | (should-error (format "%o" -1e-37) | 176 | (should-error (format "%o" -1e-37) |
| 164 | :type 'overflow-error)) | 177 | :type 'overflow-error)) |
| 165 | 178 | ||
| 179 | |||
| 166 | ;;; Check format-time-string with various TZ settings. | 180 | ;;; Check format-time-string with various TZ settings. |
| 167 | ;;; Use only POSIX-compatible TZ values, since the tests should work | 181 | ;;; Use only POSIX-compatible TZ values, since the tests should work |
| 168 | ;;; even if tzdb is not in use. | 182 | ;;; even if tzdb is not in use. |