The string-to-number conversions int(s), uint(s), float(s), and the int, uint, and float fields of a #json struct (json.parse) all go through the same two runtime functions, gray_builtin_string_to_int and gray_builtin_string_to_float. They call strtoll / strtod without checking errno, accept whitespace, and copy the input into a 64-byte stack buffer. STANDARD.md §3.4 says "Conversions that would lose information or are invalid produce check-time or runtime errors", and strconv.to_int / to_uint / to_float already reject each case below.
| Input |
Actual |
Expected |
int("9223372036854775808") |
9223372036854775807 |
panic P0084 |
int("-99999999999999999999") |
-9223372036854775808 |
panic P0084 |
uint("18446744073709551615") |
9223372036854775807 |
18446744073709551615 |
uint("18446744073709551616") |
9223372036854775807 |
panic |
uint("-1") |
18446744073709551615 |
panic |
float("1e999") |
inf |
panic P0085 |
int(" 5"), int("5 ") |
5 |
panic |
int("42 apples") |
42 |
panic |
float("2.5 kg") |
2.5 |
panic |
float("0x10") |
16.0 |
panic (while int("0x10") panics) |
int("0" * 400 + "5") |
0 |
5 |
int("0" * 400 + "5x") |
0 |
panic |
float("7" + "0" * 400 + "x") |
7e+62 |
panic (the value is 7e400, which overflows) |
The last three rows come from the 64-byte buffer: anything past byte 63 is silently dropped, so trailing garbage is never seen and a long value is silently cut. The whitespace rows come from end being allowed to stop at a space.
json.parse into a #json struct uses the same functions, so a JSON document decodes to wrong values with no error:
| JSON |
Field type |
Actual |
{"a": 18446744073709551615} |
uint |
9223372036854775807 |
{"a": 99999999999999999999} |
int |
9223372036854775807 |
{"a": -1} |
uint |
18446744073709551615 |
Expected
Each row above panics (P0084 / P0085; malformed field values in json.parse are a json.parse failure per §7.5.2), or returns the exact value when the input is valid, the same as strconv.to_int / to_uint / to_float.
Actual
The values in the tables, with no diagnostic.
Reproduction
import @json
import @strings
#json
const T struct {
a uint
}
do main() {
println(int("9223372036854775808")) // 9223372036854775807, expected panic
println(uint("-1")) // 18446744073709551615, expected panic
println(float("1e999")) // inf, expected panic
println(int("42 apples")) // 42, expected panic
println(int(strings.repeat("0", 400) + "5")) // 0, expected 5
mut t T = json.parse("{\"a\": 18446744073709551615}")
println(t.a) // 9223372036854775807, expected 18446744073709551615
}
The string-to-number conversions
int(s),uint(s),float(s), and theint,uint, andfloatfields of a#jsonstruct (json.parse) all go through the same two runtime functions,gray_builtin_string_to_intandgray_builtin_string_to_float. They callstrtoll/strtodwithout checkingerrno, accept whitespace, and copy the input into a 64-byte stack buffer. STANDARD.md §3.4 says "Conversions that would lose information or are invalid produce check-time or runtime errors", andstrconv.to_int/to_uint/to_floatalready reject each case below.int("9223372036854775808")9223372036854775807P0084int("-99999999999999999999")-9223372036854775808P0084uint("18446744073709551615")922337203685477580718446744073709551615uint("18446744073709551616")9223372036854775807uint("-1")18446744073709551615float("1e999")infP0085int(" 5"),int("5 ")5int("42 apples")42float("2.5 kg")2.5float("0x10")16.0int("0x10")panics)int("0" * 400 + "5")05int("0" * 400 + "5x")0float("7" + "0" * 400 + "x")7e+627e400, which overflows)The last three rows come from the 64-byte buffer: anything past byte 63 is silently dropped, so trailing garbage is never seen and a long value is silently cut. The whitespace rows come from
endbeing allowed to stop at a space.json.parseinto a#jsonstruct uses the same functions, so a JSON document decodes to wrong values with no error:{"a": 18446744073709551615}uint9223372036854775807{"a": 99999999999999999999}int9223372036854775807{"a": -1}uint18446744073709551615Expected
Each row above panics (
P0084/P0085; malformed field values injson.parseare ajson.parsefailure per §7.5.2), or returns the exact value when the input is valid, the same asstrconv.to_int/to_uint/to_float.Actual
The values in the tables, with no diagnostic.
Reproduction