diff --git a/doc/langdef.md b/doc/langdef.md index ccc7daf8..7fd8c983 100644 --- a/doc/langdef.md +++ b/doc/langdef.md @@ -1057,6 +1057,18 @@ Arithmetic operations raise an error when the results exceed the range of the integer type (int, uint) or the timestamp or duration type. An error is also raised for conversions which exceed the range of the target type. +There are a few additional considerations to keep in mind with respect to +how and when certain types will overflow: + +* Duration values are limited to a single int64 value, or roughly +-290 years. +* Timestamp values are limited to the range of values which can be serialized + as a string: ["0001-01-01T00:00:00Z", "9999-12-31T23:59:59.999999999Z"]. +* Double to int conversions are limited to (minInt, maxInt) non-inclusive. + +Note, that whether the minimum or maximum integer value will roundtrip successfully +int -> double -> int can be compiler dependent which is the motivation for the +conservative round-tripping behavior. + ### Timezones Timezones are expressed in the following grammar: diff --git a/tests/simple/testdata/conversions.textproto b/tests/simple/testdata/conversions.textproto index 73874311..e162d40c 100644 --- a/tests/simple/testdata/conversions.textproto +++ b/tests/simple/testdata/conversions.textproto @@ -205,6 +205,22 @@ section { expr: "int(double(36028797018963969))" value { int64_value: 36028797018963968 } } + test { + name: "double_int_max_range" + description: "The double(2^63-1) cast produces a floating point value outside the int range" + expr: "int(9223372036854775807.0)" + eval_error { + errors: { message: "range" } + } + } + test { + name: "double_int_min_range" + description: "The double(-2^63) cast produces a floating point value outside the int range" + expr: "int(-9223372036854775808)" + eval_error { + errors: { message: "range" } + } + } test { name: "double_range" expr: "int(1e99)" @@ -469,13 +485,15 @@ section { value { uint64_value: 36028797018963968 } } test { - name: "double_range_beyond_uint" - description: "Checks conversion of integer outside uint range." - expr: "uint(1e19)" - value: { uint64_value: 10000000000000000000 } + name: "double_uint_max_range" + description: "The exact conversion of uint max as a double does not round trip." + expr: "int(18446744073709551615.0)" + eval_error { + errors: { message: "range" } + } } test { - name: "double_range_err" + name: "double_range_beyond_uint" expr: "uint(6.022e23)" eval_error { errors { message: "range" } diff --git a/tests/simple/testdata/timestamps.textproto b/tests/simple/testdata/timestamps.textproto index 25305c40..f6cc402e 100644 --- a/tests/simple/testdata/timestamps.textproto +++ b/tests/simple/testdata/timestamps.textproto @@ -225,6 +225,16 @@ section { expr: "duration('600s') + duration('50s') == duration('650s')" value: { bool_value: true } } + test { + name: "add_time_to_duration_nanos_negative" + expr: "timestamp('0001-01-01T00:00:01.000000001Z') + duration('-999999999ns') == timestamp('0001-01-01T00:00:00.000000002Z')" + value: { bool_value: true } + } + test { + name: "add_time_to_duration_nanos_positive" + expr: "timestamp('0001-01-01T00:00:01.999999999Z') + duration('999999999ns') == timestamp('0001-01-01T00:00:02.999999998Z')" + value: { bool_value: true } + } test { name: "subtract_duration_from_time" expr: "timestamp('2009-02-13T23:10:00Z') - duration('600s') == timestamp('2009-02-13T23:00:00Z')" @@ -367,14 +377,42 @@ section { } test { name: "add_duration_under" - expr: "timestamp('0001-01-01T00:00:00Z') - duration('10s')" + expr: "timestamp('0001-01-01T00:00:00Z') + duration('-1s')" eval_error { errors { message: "range" } } } test { name: "add_duration_over" - expr: "timestamp('9999-12-31T23:59:59Z') + duration('10s')" + expr: "timestamp('9999-12-31T23:59:59Z') + duration('1s')" + eval_error { + errors { message: "range" } + } + } + test { + name: "add_duration_nanos_over" + expr: "timestamp('9999-12-31T23:59:59.999999999Z') + duration('1ns')" + eval_error { + errors { message: "range" } + } + } + test { + name: "add_duration_nanos_under" + expr: "timestamp('0001-01-01T00:00:00Z') + duration('-1ns')" + eval_error { + errors { message: "range" } + } + } + test { + name: "sub_time_duration_over" + expr: "timestamp('9999-12-31T23:59:59Z') - timestamp('0001-01-01T00:00:00Z')" + eval_error { + errors { message: "range" } + } + } + test { + name: "sub_time_duration_under" + expr: "timestamp('0001-01-01T00:00:00Z') - timestamp('9999-12-31T23:59:59Z')" eval_error { errors { message: "range" } } @@ -411,4 +449,18 @@ section { errors { message: "range" } } } + test { + name: "sub_under" + expr: "duration('-200000000000s') - duration('200000000000s')" + eval_error { + errors { message: "range" } + } + } + test { + name: "sub_over" + expr: "duration('200000000000s') - duration('-200000000000s')" + eval_error { + errors { message: "range" } + } + } }