diff --git a/common/values/error_value.cc b/common/values/error_value.cc index 892b58d85..800864404 100644 --- a/common/values/error_value.cc +++ b/common/values/error_value.cc @@ -88,6 +88,62 @@ ErrorValue IndexOutOfBoundsError(ptrdiff_t index) { absl::InvalidArgumentError(absl::StrCat("index out of bounds: ", index))); } +ErrorValue IntDivisionByZeroError() { + return ErrorValue(absl::InvalidArgumentError("int division by zero")); +} + +ErrorValue UintDivisionByZeroError() { + return ErrorValue(absl::InvalidArgumentError("uint division by zero")); +} + +ErrorValue IntModuloByZeroError() { + return ErrorValue(absl::InvalidArgumentError("int modulo by zero")); +} + +ErrorValue UintModuloByZeroError() { + return ErrorValue(absl::InvalidArgumentError("uint modulo by zero")); +} + +ErrorValue IntOverflowError() { + return ErrorValue(absl::OutOfRangeError("int overflow")); +} + +ErrorValue UintOverflowError() { + return ErrorValue(absl::OutOfRangeError("uint overflow")); +} + +ErrorValue DurationOverflowError() { + return ErrorValue(absl::OutOfRangeError("duration overflow")); +} + +ErrorValue TimestampOverflowError() { + return ErrorValue(absl::OutOfRangeError("timestamp overflow")); +} + +ErrorValue DoubleToIntOutOfRangeError() { + return ErrorValue(absl::OutOfRangeError("double out of int range")); +} + +ErrorValue DoubleToUintOutOfRangeError() { + return ErrorValue(absl::OutOfRangeError("double out of uint range")); +} + +ErrorValue IntToUintOutOfRangeError() { + return ErrorValue(absl::OutOfRangeError("int out of uint range")); +} + +ErrorValue UintToIntOutOfRangeError() { + return ErrorValue(absl::OutOfRangeError("uint out of int range")); +} + +ErrorValue Int64ToInt32OutOfRangeError() { + return ErrorValue(absl::OutOfRangeError("int64 out of int32_t range")); +} + +ErrorValue Uint64ToUint32OutOfRangeError() { + return ErrorValue(absl::OutOfRangeError("uint64 out of uint32_t range")); +} + bool IsNoSuchField(const ErrorValue& value) { return absl::IsNotFound(value.NativeValue()) && absl::StartsWith(value.NativeValue().message(), "no_such_field"); diff --git a/common/values/error_value.h b/common/values/error_value.h index d83666bbe..a68298308 100644 --- a/common/values/error_value.h +++ b/common/values/error_value.h @@ -183,6 +183,34 @@ ErrorValue IndexOutOfBoundsError(size_t index); ErrorValue IndexOutOfBoundsError(ptrdiff_t index); +ErrorValue IntDivisionByZeroError(); + +ErrorValue UintDivisionByZeroError(); + +ErrorValue IntModuloByZeroError(); + +ErrorValue UintModuloByZeroError(); + +ErrorValue IntOverflowError(); + +ErrorValue UintOverflowError(); + +ErrorValue DurationOverflowError(); + +ErrorValue TimestampOverflowError(); + +ErrorValue DoubleToIntOutOfRangeError(); + +ErrorValue DoubleToUintOutOfRangeError(); + +ErrorValue IntToUintOutOfRangeError(); + +ErrorValue UintToIntOutOfRangeError(); + +ErrorValue Int64ToInt32OutOfRangeError(); + +ErrorValue Uint64ToUint32OutOfRangeError(); + // Catch other integrals and forward them to the above ones. This is needed to // avoid ambiguous overload issues for smaller integral types like `int`. template diff --git a/eval/eval/function_step_test.cc b/eval/eval/function_step_test.cc index a3a3e31ca..8d626db11 100644 --- a/eval/eval/function_step_test.cc +++ b/eval/eval/function_step_test.cc @@ -1137,9 +1137,9 @@ TEST_F(DirectFunctionStepTest, ErrorHandlingCall) { Activation activation; ASSERT_OK_AND_ASSIGN(auto value, plan->Evaluate(activation, &arena_)); - EXPECT_THAT(value, - test::IsCelError(StatusIs(absl::StatusCode::kInvalidArgument, - testing::HasSubstr("divide by zero")))); + EXPECT_THAT(value, test::IsCelError( + StatusIs(absl::StatusCode::kInvalidArgument, + testing::HasSubstr("division by zero")))); } TEST_F(DirectFunctionStepTest, NoOverload) { diff --git a/eval/public/structs/BUILD b/eval/public/structs/BUILD index 4ee28f6e7..56f921be8 100644 --- a/eval/public/structs/BUILD +++ b/eval/public/structs/BUILD @@ -124,6 +124,7 @@ cc_library( "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", "@com_google_protobuf//:protobuf", ], ) diff --git a/eval/public/structs/cel_proto_wrap_util.cc b/eval/public/structs/cel_proto_wrap_util.cc index a039cc330..f0e65295b 100644 --- a/eval/public/structs/cel_proto_wrap_util.cc +++ b/eval/public/structs/cel_proto_wrap_util.cc @@ -814,7 +814,7 @@ google::protobuf::Message* Int32FromValue(const google::protobuf::Message* proto if (!value.GetValue(&val)) { return nullptr; } - if (!cel::internal::CheckedInt64ToInt32(val).ok()) { + if (!cel::internal::CheckedInt64ToInt32(val)) { return nullptr; } int32_t ival = static_cast(val); @@ -882,7 +882,7 @@ google::protobuf::Message* UInt32FromValue(const google::protobuf::Message* prot if (!value.GetValue(&val)) { return nullptr; } - if (!cel::internal::CheckedUint64ToUint32(val).ok()) { + if (!cel::internal::CheckedUint64ToUint32(val)) { return nullptr; } uint32_t ival = static_cast(val); diff --git a/eval/public/structs/field_access_impl.cc b/eval/public/structs/field_access_impl.cc index 790c17827..41d84789b 100644 --- a/eval/public/structs/field_access_impl.cc +++ b/eval/public/structs/field_access_impl.cc @@ -30,6 +30,7 @@ #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/strings/substitute.h" +#include "absl/types/optional.h" #include "eval/public/structs/cel_proto_wrap_util.h" #include "internal/casts.h" #include "internal/overflow.h" @@ -357,9 +358,9 @@ class FieldSetter { if (!cel_value.GetValue(&value)) { return false; } - absl::StatusOr checked_cast = + absl::optional checked_cast = cel::internal::CheckedInt64ToInt32(value); - if (!checked_cast.ok()) { + if (!checked_cast) { return false; } static_cast(this)->SetInt32(*checked_cast); @@ -371,7 +372,7 @@ class FieldSetter { if (!cel_value.GetValue(&value)) { return false; } - if (!cel::internal::CheckedUint64ToUint32(value).ok()) { + if (!cel::internal::CheckedUint64ToUint32(value)) { return false; } static_cast(this)->SetUInt32(value); @@ -437,7 +438,7 @@ class FieldSetter { if (!cel_value.GetValue(&value)) { return false; } - if (!cel::internal::CheckedInt64ToInt32(value).ok()) { + if (!cel::internal::CheckedInt64ToInt32(value)) { return false; } static_cast(this)->SetEnum(value); diff --git a/internal/BUILD b/internal/BUILD index 59f42520c..d721d6d96 100644 --- a/internal/BUILD +++ b/internal/BUILD @@ -84,14 +84,12 @@ cc_library( cc_library( name = "overflow", - srcs = ["overflow.cc"], hdrs = ["overflow.h"], deps = [ - ":status_macros", ":time", - "@com_google_absl//absl/status", - "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/base:config", "@com_google_absl//absl/time", + "@com_google_absl//absl/types:optional", ], ) @@ -101,9 +99,10 @@ cc_test( deps = [ ":overflow", ":testing", + ":time", "@com_google_absl//absl/functional:function_ref", - "@com_google_absl//absl/status", "@com_google_absl//absl/time", + "@com_google_absl//absl/types:optional", ], ) diff --git a/internal/overflow.cc b/internal/overflow.cc deleted file mode 100644 index 0c01bfe4e..000000000 --- a/internal/overflow.cc +++ /dev/null @@ -1,339 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "internal/overflow.h" - -#include -#include -#include - -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/time/time.h" -#include "internal/status_macros.h" -#include "internal/time.h" - -namespace cel::internal { -namespace { - -constexpr int64_t kInt32Max = std::numeric_limits::max(); -constexpr int64_t kInt32Min = std::numeric_limits::lowest(); -constexpr int64_t kInt64Max = std::numeric_limits::max(); -constexpr int64_t kInt64Min = std::numeric_limits::lowest(); -constexpr uint64_t kUint32Max = std::numeric_limits::max(); -ABSL_ATTRIBUTE_UNUSED constexpr uint64_t kUint64Max = - std::numeric_limits::max(); -constexpr uint64_t kUintToIntMax = static_cast(kInt64Max); -constexpr double kDoubleToIntMax = static_cast(kInt64Max); -constexpr double kDoubleToIntMin = static_cast(kInt64Min); -const double kDoubleTwoTo64 = std::ldexp(1.0, 64); // 1.0 * 2^64 - -const absl::Duration kOneSecondDuration = absl::Seconds(1); -const int64_t kOneSecondNanos = absl::ToInt64Nanoseconds(kOneSecondDuration); -// Number of seconds between `0001-01-01T00:00:00Z` and Unix epoch. -const int64_t kMinUnixTime = - absl::ToInt64Seconds(MinTimestamp() - absl::UnixEpoch()); - -// Number of seconds between `9999-12-31T23:59:59.999999999Z` and Unix epoch. -const int64_t kMaxUnixTime = - absl::ToInt64Seconds(MaxTimestamp() - absl::UnixEpoch()); - -absl::Status CheckRange(bool valid_expression, - absl::string_view error_message) { - return valid_expression ? absl::OkStatus() - : absl::OutOfRangeError(error_message); -} - -absl::Status CheckArgument(bool valid_expression, - absl::string_view error_message) { - return valid_expression ? absl::OkStatus() - : absl::InvalidArgumentError(error_message); -} - -// Determine whether the duration is finite. -bool IsFinite(absl::Duration d) { - return d != absl::InfiniteDuration() && d != -absl::InfiniteDuration(); -} - -// Determine whether the time is finite. -bool IsFinite(absl::Time t) { - return t != absl::InfiniteFuture() && t != absl::InfinitePast(); -} - -} // namespace - -absl::StatusOr CheckedAdd(int64_t x, int64_t y) { -#if ABSL_HAVE_BUILTIN(__builtin_add_overflow) - int64_t sum; - if (!__builtin_add_overflow(x, y, &sum)) { - return sum; - } - return absl::OutOfRangeError("integer overflow"); -#else - CEL_RETURN_IF_ERROR(CheckRange( - y > 0 ? x <= kInt64Max - y : x >= kInt64Min - y, "integer overflow")); - return x + y; -#endif -} - -absl::StatusOr CheckedSub(int64_t x, int64_t y) { -#if ABSL_HAVE_BUILTIN(__builtin_sub_overflow) - int64_t diff; - if (!__builtin_sub_overflow(x, y, &diff)) { - return diff; - } - return absl::OutOfRangeError("integer overflow"); -#else - CEL_RETURN_IF_ERROR(CheckRange( - y < 0 ? x <= kInt64Max + y : x >= kInt64Min + y, "integer overflow")); - return x - y; -#endif -} - -absl::StatusOr CheckedNegation(int64_t v) { -#if ABSL_HAVE_BUILTIN(__builtin_mul_overflow) - int64_t prod; - if (!__builtin_mul_overflow(v, -1, &prod)) { - return prod; - } - return absl::OutOfRangeError("integer overflow"); -#else - CEL_RETURN_IF_ERROR(CheckRange(v != kInt64Min, "integer overflow")); - return -v; -#endif -} - -absl::StatusOr CheckedMul(int64_t x, int64_t y) { -#if ABSL_HAVE_BUILTIN(__builtin_mul_overflow) - int64_t prod; - if (!__builtin_mul_overflow(x, y, &prod)) { - return prod; - } - return absl::OutOfRangeError("integer overflow"); -#else - CEL_RETURN_IF_ERROR( - CheckRange(!((x == -1 && y == kInt64Min) || (y == -1 && x == kInt64Min) || - (x > 0 && y > 0 && x > kInt64Max / y) || - (x < 0 && y < 0 && x < kInt64Max / y) || - // Avoid dividing kInt64Min by -1, use whichever value of x - // or y is positive as the divisor. - (x > 0 && y < 0 && y < kInt64Min / x) || - (x < 0 && y > 0 && x < kInt64Min / y)), - "integer overflow")); - return x * y; -#endif -} - -absl::StatusOr CheckedDiv(int64_t x, int64_t y) { - CEL_RETURN_IF_ERROR( - CheckRange(x != kInt64Min || y != -1, "integer overflow")); - CEL_RETURN_IF_ERROR(CheckArgument(y != 0, "divide by zero")); - return x / y; -} - -absl::StatusOr CheckedMod(int64_t x, int64_t y) { - CEL_RETURN_IF_ERROR( - CheckRange(x != kInt64Min || y != -1, "integer overflow")); - CEL_RETURN_IF_ERROR(CheckArgument(y != 0, "modulus by zero")); - return x % y; -} - -absl::StatusOr CheckedAdd(uint64_t x, uint64_t y) { -#if ABSL_HAVE_BUILTIN(__builtin_add_overflow) - uint64_t sum; - if (!__builtin_add_overflow(x, y, &sum)) { - return sum; - } - return absl::OutOfRangeError("unsigned integer overflow"); -#else - CEL_RETURN_IF_ERROR( - CheckRange(x <= kUint64Max - y, "unsigned integer overflow")); - return x + y; -#endif -} - -absl::StatusOr CheckedSub(uint64_t x, uint64_t y) { -#if ABSL_HAVE_BUILTIN(__builtin_sub_overflow) - uint64_t diff; - if (!__builtin_sub_overflow(x, y, &diff)) { - return diff; - } - return absl::OutOfRangeError("unsigned integer overflow"); -#else - CEL_RETURN_IF_ERROR(CheckRange(y <= x, "unsigned integer overflow")); - return x - y; -#endif -} - -absl::StatusOr CheckedMul(uint64_t x, uint64_t y) { -#if ABSL_HAVE_BUILTIN(__builtin_mul_overflow) - uint64_t prod; - if (!__builtin_mul_overflow(x, y, &prod)) { - return prod; - } - return absl::OutOfRangeError("unsigned integer overflow"); -#else - CEL_RETURN_IF_ERROR( - CheckRange(y == 0 || x <= kUint64Max / y, "unsigned integer overflow")); - return x * y; -#endif -} - -absl::StatusOr CheckedDiv(uint64_t x, uint64_t y) { - CEL_RETURN_IF_ERROR(CheckArgument(y != 0, "divide by zero")); - return x / y; -} - -absl::StatusOr CheckedMod(uint64_t x, uint64_t y) { - CEL_RETURN_IF_ERROR(CheckArgument(y != 0, "modulus by zero")); - return x % y; -} - -absl::StatusOr CheckedAdd(absl::Duration x, absl::Duration y) { - CEL_RETURN_IF_ERROR( - CheckRange(IsFinite(x) && IsFinite(y), "integer overflow")); - // absl::Duration can handle +- infinite durations, but the Go time.Duration - // implementation caps the durations to those expressible within a single - // int64_t rather than (seconds int64_t, nanos int32_t). - // - // The absl implementation mirrors the protobuf implementation which supports - // durations on the order of +- 10,000 years, but Go only supports +- 290 year - // durations. - // - // Since Go is the more conservative of the implementations and 290 year - // durations seem quite reasonable, this code mirrors the conservative - // overflow behavior which would be observed in Go. - CEL_ASSIGN_OR_RETURN(int64_t nanos, CheckedAdd(absl::ToInt64Nanoseconds(x), - absl::ToInt64Nanoseconds(y))); - return absl::Nanoseconds(nanos); -} - -absl::StatusOr CheckedSub(absl::Duration x, absl::Duration y) { - CEL_RETURN_IF_ERROR( - CheckRange(IsFinite(x) && IsFinite(y), "integer overflow")); - CEL_ASSIGN_OR_RETURN(int64_t nanos, CheckedSub(absl::ToInt64Nanoseconds(x), - absl::ToInt64Nanoseconds(y))); - return absl::Nanoseconds(nanos); -} - -absl::StatusOr CheckedNegation(absl::Duration v) { - CEL_RETURN_IF_ERROR(CheckRange(IsFinite(v), "integer overflow")); - CEL_ASSIGN_OR_RETURN(int64_t nanos, - CheckedNegation(absl::ToInt64Nanoseconds(v))); - return absl::Nanoseconds(nanos); -} - -absl::StatusOr CheckedAdd(absl::Time t, absl::Duration d) { - CEL_RETURN_IF_ERROR( - CheckRange(IsFinite(t) && IsFinite(d), "timestamp overflow")); - // First we break time into its components by truncating and subtracting. - const int64_t s1 = absl::ToUnixSeconds(t); - const int64_t ns1 = (t - absl::FromUnixSeconds(s1)) / absl::Nanoseconds(1); - - // Second we break duration into its components by dividing and modulo. - // Truncate to seconds. - const int64_t s2 = d / kOneSecondDuration; - // Get remainder. - const int64_t ns2 = absl::ToInt64Nanoseconds(d % kOneSecondDuration); - - // Add seconds first, detecting any overflow. - CEL_ASSIGN_OR_RETURN(int64_t s, CheckedAdd(s1, s2)); - // Nanoseconds cannot overflow as nanos are normalized to [0, 999999999]. - absl::Duration ns = absl::Nanoseconds(ns2 + ns1); - - // Normalize nanoseconds to be positive and carry extra nanos to seconds. - if (ns < absl::ZeroDuration() || ns >= kOneSecondDuration) { - // Add seconds, or no-op if nanseconds negative (ns never < -999_999_999ns) - CEL_ASSIGN_OR_RETURN(s, CheckedAdd(s, ns / kOneSecondDuration)); - ns -= (ns / kOneSecondDuration) * kOneSecondDuration; - // Subtract a second to make the nanos positive. - if (ns < absl::ZeroDuration()) { - CEL_ASSIGN_OR_RETURN(s, CheckedAdd(s, -1)); - ns += kOneSecondDuration; - } - } - // Check if the the number of seconds from Unix epoch is within our acceptable - // range. - CEL_RETURN_IF_ERROR( - CheckRange(s >= kMinUnixTime && s <= kMaxUnixTime, "timestamp overflow")); - - // Return resulting time. - return absl::FromUnixSeconds(s) + ns; -} - -absl::StatusOr CheckedSub(absl::Time t, absl::Duration d) { - CEL_ASSIGN_OR_RETURN(auto neg_duration, CheckedNegation(d)); - return CheckedAdd(t, neg_duration); -} - -absl::StatusOr CheckedSub(absl::Time t1, absl::Time t2) { - CEL_RETURN_IF_ERROR( - CheckRange(IsFinite(t1) && IsFinite(t2), "integer overflow")); - // First we break time into its components by truncating and subtracting. - const int64_t s1 = absl::ToUnixSeconds(t1); - const int64_t ns1 = (t1 - absl::FromUnixSeconds(s1)) / absl::Nanoseconds(1); - const int64_t s2 = absl::ToUnixSeconds(t2); - const int64_t ns2 = (t2 - absl::FromUnixSeconds(s2)) / absl::Nanoseconds(1); - - // Subtract seconds first, detecting any overflow. - CEL_ASSIGN_OR_RETURN(int64_t s, CheckedSub(s1, s2)); - // Nanoseconds cannot overflow as nanos are normalized to [0, 999999999]. - absl::Duration ns = absl::Nanoseconds(ns1 - ns2); - - // Scale the seconds result to nanos. - CEL_ASSIGN_OR_RETURN(const int64_t t, CheckedMul(s, kOneSecondNanos)); - // Add the seconds (scaled to nanos) to the nanosecond value. - CEL_ASSIGN_OR_RETURN(const int64_t v, - CheckedAdd(t, absl::ToInt64Nanoseconds(ns))); - return absl::Nanoseconds(v); -} - -absl::StatusOr CheckedDoubleToInt64(double v) { - CEL_RETURN_IF_ERROR( - CheckRange(std::isfinite(v) && v < kDoubleToIntMax && v > kDoubleToIntMin, - "double out of int64_t range")); - return static_cast(v); -} - -absl::StatusOr CheckedDoubleToUint64(double v) { - CEL_RETURN_IF_ERROR( - CheckRange(std::isfinite(v) && v >= 0 && v < kDoubleTwoTo64, - "double out of uint64_t range")); - return static_cast(v); -} - -absl::StatusOr CheckedInt64ToUint64(int64_t v) { - CEL_RETURN_IF_ERROR(CheckRange(v >= 0, "int64 out of uint64_t range")); - return static_cast(v); -} - -absl::StatusOr CheckedInt64ToInt32(int64_t v) { - CEL_RETURN_IF_ERROR( - CheckRange(v >= kInt32Min && v <= kInt32Max, "int64 out of int32_t range")); - return static_cast(v); -} - -absl::StatusOr CheckedUint64ToInt64(uint64_t v) { - CEL_RETURN_IF_ERROR( - CheckRange(v <= kUintToIntMax, "uint64 out of int64_t range")); - return static_cast(v); -} - -absl::StatusOr CheckedUint64ToUint32(uint64_t v) { - CEL_RETURN_IF_ERROR( - CheckRange(v <= kUint32Max, "uint64 out of uint32_t range")); - return static_cast(v); -} - -} // namespace cel::internal diff --git a/internal/overflow.h b/internal/overflow.h index 15a60eaf1..32bc35895 100644 --- a/internal/overflow.h +++ b/internal/overflow.h @@ -15,113 +15,281 @@ #ifndef THIRD_PARTY_CEL_CPP_COMMON_OVERFLOW_H_ #define THIRD_PARTY_CEL_CPP_COMMON_OVERFLOW_H_ +#include #include +#include -#include "absl/status/statusor.h" +#include "absl/base/config.h" #include "absl/time/time.h" +#include "absl/types/optional.h" +#include "internal/time.h" namespace cel::internal { // Add two int64_t values together. -// If overflow is detected, return an absl::StatusCode::kOutOfRangeError, e.g. +// If overflow is detected, return absl::nullopt, e.g. // int64_t_max + 1 -absl::StatusOr CheckedAdd(int64_t x, int64_t y); +inline absl::optional CheckedAdd(int64_t x, int64_t y) { +#if ABSL_HAVE_BUILTIN(__builtin_add_overflow) + int64_t sum; + if (__builtin_add_overflow(x, y, &sum)) { + return absl::nullopt; + } + return sum; +#else + if (y > int64_t{0} ? x <= std::numeric_limits::max() - y + : x >= std::numeric_limits::min() - y) { + return x + y; + } + return absl::nullopt; +#endif +} // Subtract two int64_t values from each other. -// If overflow is detected, return an absl::StatusCode::kOutOfRangeError. e.g. +// If overflow is detected, return absl::nullopt. e.g. // int64_t_min - 1 -absl::StatusOr CheckedSub(int64_t x, int64_t y); +inline absl::optional CheckedSub(int64_t x, int64_t y) { +#if ABSL_HAVE_BUILTIN(__builtin_sub_overflow) + int64_t diff; + if (__builtin_sub_overflow(x, y, &diff)) { + return absl::nullopt; + } + return diff; +#else + if (y < int64_t{0} ? x <= std::numeric_limits::max() + y + : x >= std::numeric_limits::min() + y) { + return x - y; + } + return absl::nullopt; +#endif +} // Negate an int64_t value. -// If overflow is detected, return an absl::StatusCode::kOutOfRangeError, e.g. +// If overflow is detected, return absl::nullopt, e.g. // negate(int64_t_min) -absl::StatusOr CheckedNegation(int64_t v); +inline absl::optional CheckedNegation(int64_t v) { +#if ABSL_HAVE_BUILTIN(__builtin_mul_overflow) + int64_t prod; + if (__builtin_mul_overflow(v, int64_t{-1}, &prod)) { + return absl::nullopt; + } + return prod; +#else + if (v != std::numeric_limits::min()) { + return -v; + } + return absl::nullopt; +#endif +} // Multiply two int64_t values together. -// If overflow is detected, return an absl::StatusCode::kOutOfRangeError. e.g. +// If overflow is detected, return absl::nullopt. e.g. // 2 * int64_t_max -absl::StatusOr CheckedMul(int64_t x, int64_t y); +inline absl::optional CheckedMul(int64_t x, int64_t y) { +#if ABSL_HAVE_BUILTIN(__builtin_mul_overflow) + int64_t prod; + if (__builtin_mul_overflow(x, y, &prod)) { + return absl::nullopt; + } + return prod; +#else + if (!((x == int64_t{-1} && y == std::numeric_limits::min()) || + (y == int64_t{-1} && x == std::numeric_limits::min()) || + (x > int64_t{0} && y > int64_t{0} && + x > std::numeric_limits::max() / y) || + (x < int64_t{0} && y < int64_t{0} && + x < std::numeric_limits::max() / y) || + // Avoid dividing std::numeric_limits::min() by -1, use + // whichever value of x or y is positive as the divisor. + (x > int64_t{0} && y < int64_t{0} && + y < std::numeric_limits::min() / x) || + (x < int64_t{0} && y > int64_t{0} && + x < std::numeric_limits::min() / y))) { + return x * y; + } + return absl::nullopt; +#endif +} // Divide one int64_t value into another. -// If overflow is detected, return an absl::StatusCode::kOutOfRangeError, e.g. +// If overflow is detected, return absl::nullopt, e.g. // int64_t_min / -1 -absl::StatusOr CheckedDiv(int64_t x, int64_t y); +inline absl::optional CheckedDiv(int64_t x, int64_t y) { + if (!((x == std::numeric_limits::min() && y == int64_t{-1}) || + y == int64_t{0})) { + return x / y; + } + return absl::nullopt; +} // Compute the modulus of x into y. -// If overflow is detected, return an absl::StatusCode::kOutOfRangeError, e.g. +// If overflow is detected, return absl::nullopt, e.g. // int64_t_min % -1 -absl::StatusOr CheckedMod(int64_t x, int64_t y); +inline absl::optional CheckedMod(int64_t x, int64_t y) { + if (!((x == std::numeric_limits::min() && y == int64_t{-1}) || + y == int64_t{0})) { + return x % y; + } + return absl::nullopt; +} // Add two uint64_t values together. -// If overflow is detected, return an absl::StatusCode::kOutOfRangeError, e.g. +// If overflow is detected, return absl::nullopt, e.g. // uint64_t_max + 1 -absl::StatusOr CheckedAdd(uint64_t x, uint64_t y); +inline absl::optional CheckedAdd(uint64_t x, uint64_t y) { +#if ABSL_HAVE_BUILTIN(__builtin_add_overflow) + uint64_t sum; + if (__builtin_add_overflow(x, y, &sum)) { + return absl::nullopt; + } + return sum; +#else + if (x <= std::numeric_limits::max() - y) { + return x + y; + } + return absl::nullopt; +#endif +} // Subtract two uint64_t values from each other. -// If overflow is detected, return an absl::StatusCode::kOutOfRangeError, e.g. +// If overflow is detected, return absl::nullopt, e.g. // 1 - uint64_t_max -absl::StatusOr CheckedSub(uint64_t x, uint64_t y); +inline absl::optional CheckedSub(uint64_t x, uint64_t y) { +#if ABSL_HAVE_BUILTIN(__builtin_sub_overflow) + uint64_t diff; + if (__builtin_sub_overflow(x, y, &diff)) { + return absl::nullopt; + } + return diff; +#else + if (y <= x) { + return x - y; + } + return absl::nullopt; +#endif +} // Multiply two uint64_t values together. -// If overflow is detected, return an absl::StatusCode::kOutOfRangeError, e.g. +// If overflow is detected, return absl::nullopt, e.g. // 2 * uint64_t_max -absl::StatusOr CheckedMul(uint64_t x, uint64_t y); +inline absl::optional CheckedMul(uint64_t x, uint64_t y) { +#if ABSL_HAVE_BUILTIN(__builtin_mul_overflow) + uint64_t prod; + if (__builtin_mul_overflow(x, y, &prod)) { + return absl::nullopt; + } + return prod; +#else + if (y == uint64_t{0} || x <= std::numeric_limits::max() / y) { + return x * y; + } + return absl::nullopt; +#endif +} // Divide one uint64_t value into another. -absl::StatusOr CheckedDiv(uint64_t x, uint64_t y); +inline absl::optional CheckedDiv(uint64_t x, uint64_t y) { + if (y != uint64_t{0}) { + return x / y; + } + return absl::nullopt; +} // Compute the modulus of x into y. // If 'y' is zero, the function will return an // absl::StatusCode::kInvalidArgumentError, e.g. 1 / 0. -absl::StatusOr CheckedMod(uint64_t x, uint64_t y); +inline absl::optional CheckedMod(uint64_t x, uint64_t y) { + if (y != uint64_t{0}) { + return x % y; + } + return absl::nullopt; +} // Add two durations together. -// If overflow is detected, return an absl::StatusCode::kOutOfRangeError, e.g. +// If overflow is detected, return absl::nullopt, e.g. // duration(int64_t_max, "ns") + duration(int64_t_max, "ns") // // Note, absl::Duration is effectively an int64_t under the covers, which means // the same cases that would result in overflow for int64_t values would hold // true for absl::Duration values. -absl::StatusOr CheckedAdd(absl::Duration x, absl::Duration y); +inline absl::optional CheckedAdd(absl::Duration x, + absl::Duration y) { + absl::Duration result = x + y; + if (result < MinDuration() || result > MaxDuration()) { + return absl::nullopt; + } + return result; +} // Subtract two durations from each other. -// If overflow is detected, return an absl::StatusCode::kOutOfRangeError, e.g. +// If overflow is detected, return absl::nullopt, e.g. // duration(int64_t_min, "ns") - duration(1, "ns") // // Note, absl::Duration is effectively an int64_t under the covers, which means // the same cases that would result in overflow for int64_t values would hold // true for absl::Duration values. -absl::StatusOr CheckedSub(absl::Duration x, absl::Duration y); +inline absl::optional CheckedSub(absl::Duration x, + absl::Duration y) { + absl::Duration result = x - y; + if (result < MinDuration() || result > MaxDuration()) { + return absl::nullopt; + } + return result; +} // Negate a duration. -// If overflow is detected, return an absl::StatusCode::kOutOfRangeError, e.g. +// If overflow is detected, return absl::nullopt, e.g. // negate(duration(int64_t_min, "ns")). -absl::StatusOr CheckedNegation(absl::Duration v); +inline absl::optional CheckedNegation(absl::Duration v) { + absl::Duration result = -v; + if (result < MinDuration() || result > MaxDuration()) { + return absl::nullopt; + } + return result; +} // Add an absl::Time and absl::Duration value together. -// If overflow is detected, return an absl::StatusCode::kOutOfRangeError, e.g. +// If overflow is detected, return absl::nullopt, e.g. // timestamp(unix_epoch_max) + duration(1, "ns") // // Valid time values must be between `0001-01-01T00:00:00Z` (-62135596800s) and // `9999-12-31T23:59:59.999999999Z` (253402300799s). -absl::StatusOr CheckedAdd(absl::Time t, absl::Duration d); +inline absl::optional CheckedAdd(absl::Time t, absl::Duration d) { + absl::Time result = t + d; + if (result < MinTimestamp() || result > MaxTimestamp()) { + return absl::nullopt; + } + return result; +} // Subtract an absl::Time and absl::Duration value together. -// If overflow is detected, return an absl::StatusCode::kOutOfRangeError, e.g. +// If overflow is detected, return absl::nullopt, e.g. // timestamp(unix_epoch_min) - duration(1, "ns") // // Valid time values must be between `0001-01-01T00:00:00Z` (-62135596800s) and // `9999-12-31T23:59:59.999999999Z` (253402300799s). -absl::StatusOr CheckedSub(absl::Time t, absl::Duration d); +inline absl::optional CheckedSub(absl::Time t, absl::Duration d) { + absl::Time result = t - d; + if (result < MinTimestamp() || result > MaxTimestamp()) { + return absl::nullopt; + } + return result; +} // Subtract two absl::Time values from each other to produce an absl::Duration. -// If overflow is detected, return an absl::StatusCode::kOutOfRangeError, e.g. +// If overflow is detected, return absl::nullopt, e.g. // timestamp(unix_epoch_min) - timestamp(unix_epoch_max) -absl::StatusOr CheckedSub(absl::Time t1, absl::Time t2); +inline absl::optional CheckedSub(absl::Time t1, absl::Time t2) { + absl::Duration result = t1 - t2; + if (result < MinDuration() || result > MaxDuration()) { + return absl::nullopt; + } + return result; +} // Convert a double value to an int64_t if possible. // If the double exceeds the values representable in an int64_t the function -// will return an absl::StatusCode::kOutOfRangeError. +// will return absl::nullopt. // // Only finite double values may be converted to an int64_t. CEL may also reject // some conversions if the value falls into a range where overflow would be @@ -134,11 +302,18 @@ absl::StatusOr CheckedSub(absl::Time t1, absl::Time t2); // environment dependent and the implementation must err on the side of caution // and reject possibly valid values which might be invalid based on environment // settings. -absl::StatusOr CheckedDoubleToInt64(double v); +inline absl::optional CheckedDoubleToInt64(double v) { + if (std::isfinite(v) && + v > static_cast(std::numeric_limits::min()) && + v < static_cast(std::numeric_limits::max())) { + return static_cast(v); + } + return absl::nullopt; +} // Convert a double value to a uint64_t if possible. // If the double exceeds the values representable in a uint64_t the function -// will return an absl::StatusCode::kOutOfRangeError. +// will return absl::nullopt. // // Only finite double values may be converted to a uint64_t. CEL may also reject // some conversions if the value falls into a range where overflow would be @@ -151,27 +326,54 @@ absl::StatusOr CheckedDoubleToInt64(double v); // environment dependent and the implementation must err on the side of caution // and reject possibly valid values which might be invalid based on environment // settings. -absl::StatusOr CheckedDoubleToUint64(double v); +inline absl::optional CheckedDoubleToUint64(double v) { + if (std::isfinite(v) && v >= 0.0 && + v < static_cast(std::numeric_limits::max())) { + return static_cast(v); + } + return absl::nullopt; +} // Convert an int64_t value to a uint64_t value if possible. // If the int64_t exceeds the values representable in a uint64_t the function -// will return an absl::StatusCode::kOutOfRangeError. -absl::StatusOr CheckedInt64ToUint64(int64_t v); +// will return absl::nullopt. +inline absl::optional CheckedInt64ToUint64(int64_t v) { + if (v < int64_t{0}) { + return absl::nullopt; + } + return static_cast(v); +} // Convert an int64_t value to an int32_t value if possible. // If the int64_t exceeds the values representable in an int32_t the function -// will return an absl::StatusCode::kOutOfRangeError. -absl::StatusOr CheckedInt64ToInt32(int64_t v); +// will return absl::nullopt. +inline absl::optional CheckedInt64ToInt32(int64_t v) { + if (v < std::numeric_limits::min() || + v > std::numeric_limits::max()) { + return absl::nullopt; + } + return static_cast(v); +} // Convert a uint64_t value to an int64_t value if possible. // If the uint64_t exceeds the values representable in an int64_t the function -// will return an absl::StatusCode::kOutOfRangeError. -absl::StatusOr CheckedUint64ToInt64(uint64_t v); +// will return absl::nullopt. +inline absl::optional CheckedUint64ToInt64(uint64_t v) { + if (v > static_cast(std::numeric_limits::max())) { + return absl::nullopt; + } + return static_cast(v); +} // Convert a uint64_t value to a uint32_t value if possible. // If the uint64_t exceeds the values representable in a uint32_t the function -// will return an absl::StatusCode::kOutOfRangeError. -absl::StatusOr CheckedUint64ToUint32(uint64_t v); +// will return absl::nullopt. +inline absl::optional CheckedUint64ToUint32(uint64_t v) { + if (v > std::numeric_limits::max()) { + return absl::nullopt; + } + return static_cast(v); +} } // namespace cel::internal diff --git a/internal/overflow_test.cc b/internal/overflow_test.cc index 1dfb6c4ba..9b8d89e40 100644 --- a/internal/overflow_test.cc +++ b/internal/overflow_test.cc @@ -20,33 +20,26 @@ #include #include "absl/functional/function_ref.h" -#include "absl/status/status.h" #include "absl/time/time.h" +#include "absl/types/optional.h" #include "internal/testing.h" +#include "internal/time.h" namespace cel::internal { namespace { -using ::testing::HasSubstr; using ::testing::ValuesIn; template struct TestCase { std::string test_name; - absl::FunctionRef()> op; - absl::StatusOr result; + absl::FunctionRef()> op; + absl::optional result; }; template void ExpectResult(const T& test_case) { - auto result = test_case.op(); - ASSERT_EQ(result.status().code(), test_case.result.status().code()); - if (result.ok()) { - EXPECT_EQ(*result, *test_case.result); - } else { - EXPECT_THAT(result.status().message(), - HasSubstr(test_case.result.status().message())); - } + EXPECT_EQ(test_case.op(), test_case.result); } using IntTestCase = TestCase; @@ -64,10 +57,10 @@ INSTANTIATE_TEST_SUITE_P( {"MinusOneAddZero", [] { return CheckedAdd(-1L, 0); }, -1L}, {"OneAddIntMax", [] { return CheckedAdd(1L, std::numeric_limits::max()); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, {"MinusOneAddIntMin", [] { return CheckedAdd(-1L, std::numeric_limits::lowest()); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, // Subtraction tests. {"TwoSubThree", [] { return CheckedSub(2L, 3L); }, -1L}, @@ -81,7 +74,7 @@ INSTANTIATE_TEST_SUITE_P( return CheckedSub(std::numeric_limits::max(), std::numeric_limits::lowest()); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, // Multiplication tests. {"TwoMulThree", [] { return CheckedMul(2L, 3L); }, 6L}, @@ -90,13 +83,13 @@ INSTANTIATE_TEST_SUITE_P( {"TwoMulMinusThree", [] { return CheckedMul(2L, -3L); }, -6L}, {"TwoMulIntMax", [] { return CheckedMul(2L, std::numeric_limits::max()); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, {"MinusOneMulIntMin", [] { return CheckedMul(-1L, std::numeric_limits::lowest()); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, {"IntMinMulMinusOne", [] { return CheckedMul(std::numeric_limits::lowest(), -1L); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, {"IntMinMulZero", [] { return CheckedMul(std::numeric_limits::lowest(), 0); }, 0}, @@ -114,18 +107,16 @@ INSTANTIATE_TEST_SUITE_P( {"TenDivMinusOne", [] { return CheckedDiv(10L, -1L); }, -10}, {"MinusTenDivMinusOne", [] { return CheckedDiv(-10L, -1L); }, 10}, {"MinusTenDivTwo", [] { return CheckedDiv(-10L, 2L); }, -5}, - {"OneDivZero", [] { return CheckedDiv(1L, 0L); }, - absl::InvalidArgumentError("divide by zero")}, + {"OneDivZero", [] { return CheckedDiv(1L, 0L); }, absl::nullopt}, {"IntMinDivMinusOne", [] { return CheckedDiv(std::numeric_limits::lowest(), -1L); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, // Modulus cases. {"ZeroModTwo", [] { return CheckedMod(0, 2L); }, 0}, {"TwoModTwo", [] { return CheckedMod(2L, 2L); }, 0}, {"ThreeModTwo", [] { return CheckedMod(3L, 2L); }, 1L}, - {"TwoModZero", [] { return CheckedMod(2L, 0); }, - absl::InvalidArgumentError("modulus by zero")}, + {"TwoModZero", [] { return CheckedMod(2L, 0); }, absl::nullopt}, {"IntMinModTwo", [] { return CheckedMod(std::numeric_limits::lowest(), 2L); }, 0}, @@ -134,13 +125,13 @@ INSTANTIATE_TEST_SUITE_P( 0}, {"IntMinModMinusOne", [] { return CheckedMod(std::numeric_limits::lowest(), -1L); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, // Negation cases. {"NegateOne", [] { return CheckedNegation(1L); }, -1L}, {"NegateMinInt64", [] { return CheckedNegation(std::numeric_limits::lowest()); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, // Numeric conversion cases for uint -> int, double -> int {"Uint64Conversion", [] { return CheckedUint64ToInt64(1UL); }, 1L}, @@ -155,14 +146,14 @@ INSTANTIATE_TEST_SUITE_P( return CheckedUint64ToInt64( static_cast(std::numeric_limits::max())); }, - absl::OutOfRangeError("out of int64_t range")}, + absl::nullopt}, {"DoubleConversion", [] { return CheckedDoubleToInt64(100.1); }, 100L}, {"DoubleInt64MaxConversionError", [] { return CheckedDoubleToInt64( static_cast(std::numeric_limits::max())); }, - absl::OutOfRangeError("out of int64_t range")}, + absl::nullopt}, {"DoubleInt64MaxMinus512Conversion", [] { return CheckedDoubleToInt64( @@ -180,31 +171,30 @@ INSTANTIATE_TEST_SUITE_P( return CheckedDoubleToInt64( static_cast(std::numeric_limits::lowest())); }, - absl::OutOfRangeError("out of int64_t range")}, + absl::nullopt}, {"DoubleInt64MinMinusOneConversionError", [] { return CheckedDoubleToInt64( static_cast(std::numeric_limits::lowest()) - 1.0); }, - absl::OutOfRangeError("out of int64_t range")}, + absl::nullopt}, {"DoubleInt64MinMinus511ConversionError", [] { return CheckedDoubleToInt64( static_cast(std::numeric_limits::lowest()) - 511.0); }, - absl::OutOfRangeError("out of int64_t range")}, + absl::nullopt}, {"InfiniteConversionError", [] { return CheckedDoubleToInt64(std::numeric_limits::infinity()); }, - absl::OutOfRangeError("out of int64_t range")}, + absl::nullopt}, {"NegRangeConversionError", - [] { return CheckedDoubleToInt64(-1.0e99); }, - absl::OutOfRangeError("out of int64_t range")}, + [] { return CheckedDoubleToInt64(-1.0e99); }, absl::nullopt}, {"PosRangeConversionError", [] { return CheckedDoubleToInt64(1.0e99); }, - absl::OutOfRangeError("out of int64_t range")}, + absl::nullopt}, }), [](const testing::TestParamInfo& info) { return info.param.test_name; @@ -223,12 +213,11 @@ INSTANTIATE_TEST_SUITE_P( {"OneAddZero", [] { return CheckedAdd(1UL, 0); }, 1UL}, {"OneAddIntMax", [] { return CheckedAdd(1UL, std::numeric_limits::max()); }, - absl::OutOfRangeError("unsigned integer overflow")}, + absl::nullopt}, // Subtraction tests. {"OneSubOne", [] { return CheckedSub(1UL, 1UL); }, 0}, - {"ZeroSubOne", [] { return CheckedSub(0, 1UL); }, - absl::OutOfRangeError("unsigned integer overflow")}, + {"ZeroSubOne", [] { return CheckedSub(0, 1UL); }, absl::nullopt}, {"OneSubZero", [] { return CheckedSub(1UL, 0); }, 1UL}, // Multiplication tests. @@ -237,19 +226,17 @@ INSTANTIATE_TEST_SUITE_P( {"OneMulZero", [] { return CheckedMul(1UL, 0); }, 0}, {"TwoMulUintMax", [] { return CheckedMul(2UL, std::numeric_limits::max()); }, - absl::OutOfRangeError("unsigned integer overflow")}, + absl::nullopt}, // Division tests. {"TwoDivTwo", [] { return CheckedDiv(2UL, 2UL); }, 1UL}, {"TwoDivFour", [] { return CheckedDiv(2UL, 4UL); }, 0}, - {"OneDivZero", [] { return CheckedDiv(1UL, 0); }, - absl::InvalidArgumentError("divide by zero")}, + {"OneDivZero", [] { return CheckedDiv(1UL, 0); }, absl::nullopt}, // Modulus tests. {"TwoModTwo", [] { return CheckedMod(2UL, 2UL); }, 0}, {"TwoModFour", [] { return CheckedMod(2UL, 4UL); }, 2UL}, - {"OneModZero", [] { return CheckedMod(1UL, 0); }, - absl::InvalidArgumentError("modulus by zero")}, + {"OneModZero", [] { return CheckedMod(1UL, 0); }, absl::nullopt}, // Conversion test cases for int -> uint, double -> uint. {"Int64Conversion", [] { return CheckedInt64ToUint64(1L); }, 1UL}, @@ -259,8 +246,7 @@ INSTANTIATE_TEST_SUITE_P( }, static_cast(std::numeric_limits::max())}, {"NegativeInt64ConversionError", - [] { return CheckedInt64ToUint64(-1L); }, - absl::OutOfRangeError("out of uint64_t range")}, + [] { return CheckedInt64ToUint64(-1L); }, absl::nullopt}, {"DoubleConversion", [] { return CheckedDoubleToUint64(100.1); }, 100UL}, {"DoubleUint64MaxConversionError", @@ -268,13 +254,13 @@ INSTANTIATE_TEST_SUITE_P( return CheckedDoubleToUint64( static_cast(std::numeric_limits::max())); }, - absl::OutOfRangeError("out of uint64_t range")}, + absl::nullopt}, {"DoubleUint64MaxMinus512Conversion", [] { return CheckedDoubleToUint64( static_cast(std::numeric_limits::max() - 512)); }, - absl::OutOfRangeError("out of uint64_t range")}, + absl::nullopt}, {"DoubleUint64MaxMinus1024Conversion", [] { return CheckedDoubleToUint64(static_cast( @@ -286,15 +272,13 @@ INSTANTIATE_TEST_SUITE_P( return CheckedDoubleToUint64( std::numeric_limits::infinity()); }, - absl::OutOfRangeError("out of uint64_t range")}, + absl::nullopt}, {"NegConversionError", [] { return CheckedDoubleToUint64(-1.1); }, - absl::OutOfRangeError("out of uint64_t range")}, + absl::nullopt}, {"NegRangeConversionError", - [] { return CheckedDoubleToUint64(-1.0e99); }, - absl::OutOfRangeError("out of uint64_t range")}, + [] { return CheckedDoubleToUint64(-1.0e99); }, absl::nullopt}, {"PosRangeConversionError", - [] { return CheckedDoubleToUint64(1.0e99); }, - absl::OutOfRangeError("out of uint64_t range")}, + [] { return CheckedDoubleToUint64(1.0e99); }, absl::nullopt}, }), [](const testing::TestParamInfo& info) { return info.param.test_name; @@ -314,71 +298,59 @@ INSTANTIATE_TEST_SUITE_P( [] { return CheckedAdd(absl::Seconds(1), absl::Seconds(1)); }, absl::Seconds(2)}, {"MaxDurationAddOneNano", - [] { - return CheckedAdd( - absl::Nanoseconds(std::numeric_limits::max()), - absl::Nanoseconds(1)); - }, - absl::OutOfRangeError("integer overflow")}, + [] { return CheckedAdd(MaxDuration(), absl::Nanoseconds(1)); }, + absl::nullopt}, {"MinDurationAddMinusOneNano", - [] { - return CheckedAdd( - absl::Nanoseconds(std::numeric_limits::lowest()), - absl::Nanoseconds(-1)); - }, - absl::OutOfRangeError("integer overflow")}, + [] { return CheckedAdd(MinDuration(), absl::Nanoseconds(-1)); }, + absl::nullopt}, {"InfinityAddOneNano", [] { return CheckedAdd(absl::InfiniteDuration(), absl::Nanoseconds(1)); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, {"NegInfinityAddOneNano", [] { return CheckedAdd(-absl::InfiniteDuration(), absl::Nanoseconds(1)); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, {"OneSecondAddInfinity", [] { return CheckedAdd(absl::Nanoseconds(1), absl::InfiniteDuration()); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, {"OneSecondAddNegInfinity", [] { return CheckedAdd(absl::Nanoseconds(1), -absl::InfiniteDuration()); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, // Subtraction tests for duration - duration. {"OneSecondSubOneSecond", [] { return CheckedSub(absl::Seconds(1), absl::Seconds(1)); }, absl::ZeroDuration()}, {"MinDurationSubOneSecond", - [] { - return CheckedSub( - absl::Nanoseconds(std::numeric_limits::lowest()), - absl::Nanoseconds(1)); - }, - absl::OutOfRangeError("integer overflow")}, + [] { return CheckedSub(MinDuration(), absl::Nanoseconds(1)); }, + absl::nullopt}, {"InfinitySubOneNano", [] { return CheckedSub(absl::InfiniteDuration(), absl::Nanoseconds(1)); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, {"NegInfinitySubOneNano", [] { return CheckedSub(-absl::InfiniteDuration(), absl::Nanoseconds(1)); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, {"OneNanoSubInfinity", [] { return CheckedSub(absl::Nanoseconds(1), absl::InfiniteDuration()); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, {"OneNanoSubNegInfinity", [] { return CheckedSub(absl::Nanoseconds(1), -absl::InfiniteDuration()); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, // Subtraction tests for time - time. {"TimeSubOneSecond", @@ -406,43 +378,41 @@ INSTANTIATE_TEST_SUITE_P( absl::FromUnixSeconds(std::numeric_limits::lowest()), absl::FromUnixSeconds(1)); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, {"InfinitePastSubOneSecond", [] { return CheckedSub(absl::InfinitePast(), absl::FromUnixSeconds(1)); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, {"InfiniteFutureSubOneMinusSecond", [] { return CheckedSub(absl::InfiniteFuture(), absl::FromUnixSeconds(-1)); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, {"InfiniteFutureSubInfinitePast", [] { return CheckedSub(absl::InfiniteFuture(), absl::InfinitePast()); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, {"InfinitePastSubInfiniteFuture", [] { return CheckedSub(absl::InfinitePast(), absl::InfiniteFuture()); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, // Negation cases. {"NegateOneSecond", [] { return CheckedNegation(absl::Seconds(1)); }, absl::Seconds(-1)}, - {"NegateMinDuration", - [] { - return CheckedNegation( - absl::Nanoseconds(std::numeric_limits::lowest())); - }, - absl::OutOfRangeError("integer overflow")}, + {"NegateMinDuration", [] { return CheckedNegation(MinDuration()); }, + MaxDuration()}, + {"NegateMaxDuration", [] { return CheckedNegation(MaxDuration()); }, + MinDuration()}, {"NegateInfiniteDuration", [] { return CheckedNegation(absl::InfiniteDuration()); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, {"NegateNegInfiniteDuration", [] { return CheckedNegation(-absl::InfiniteDuration()); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, }), [](const testing::TestParamInfo& info) { return info.param.test_name; }); @@ -475,13 +445,13 @@ INSTANTIATE_TEST_SUITE_P( absl::FromUnixSeconds(std::numeric_limits::max()), absl::Seconds(1)); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, {"MaxTimestampAddOneSecond", [] { return CheckedAdd(absl::FromUnixSeconds(253402300799), absl::Seconds(1)); }, - absl::OutOfRangeError("timestamp overflow")}, + absl::nullopt}, {"TimeWithNanosNegative", [] { return CheckedAdd(absl::FromUnixSeconds(1) + absl::Nanoseconds(1), @@ -501,24 +471,24 @@ INSTANTIATE_TEST_SUITE_P( absl::FromUnixSeconds(1) + absl::Nanoseconds(999999999), absl::InfiniteDuration()); }, - absl::OutOfRangeError("timestamp overflow")}, + absl::nullopt}, {"SecondsAddNegativeInfinity", [] { return CheckedAdd( absl::FromUnixSeconds(1) + absl::Nanoseconds(999999999), -absl::InfiniteDuration()); }, - absl::OutOfRangeError("timestamp overflow")}, + absl::nullopt}, {"InfiniteFutureAddNegativeInfinity", [] { return CheckedAdd(absl::InfiniteFuture(), -absl::InfiniteDuration()); }, - absl::OutOfRangeError("timestamp overflow")}, + absl::nullopt}, {"InfinitePastAddInfinity", [] { return CheckedAdd(absl::InfinitePast(), absl::InfiniteDuration()); }, - absl::OutOfRangeError("timestamp overflow")}, + absl::nullopt}, // Subtraction tests. {"DateSubOneHour", @@ -529,35 +499,35 @@ INSTANTIATE_TEST_SUITE_P( return CheckedSub(absl::FromUnixSeconds(-62135596800), absl::Seconds(1)); }, - absl::OutOfRangeError("timestamp overflow")}, + absl::nullopt}, {"MinIntSubOneViaNanos", [] { return CheckedSub( absl::FromUnixSeconds(std::numeric_limits::min()), absl::Nanoseconds(1)); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, {"MinTimestampSubOneViaNanosScaleOverflow", [] { return CheckedSub( absl::FromUnixSeconds(-62135596800) + absl::Nanoseconds(1), absl::Nanoseconds(999999999)); }, - absl::OutOfRangeError("timestamp overflow")}, + absl::nullopt}, {"SecondsSubInfinity", [] { return CheckedSub( absl::FromUnixSeconds(1) + absl::Nanoseconds(999999999), absl::InfiniteDuration()); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, {"SecondsSubNegInfinity", [] { return CheckedSub( absl::FromUnixSeconds(1) + absl::Nanoseconds(999999999), -absl::InfiniteDuration()); }, - absl::OutOfRangeError("integer overflow")}, + absl::nullopt}, }), [](const testing::TestParamInfo& info) { return info.param.test_name; @@ -583,7 +553,7 @@ INSTANTIATE_TEST_SUITE_P( return CheckedInt64ToInt32( static_cast(std::numeric_limits::max())); }, - absl::OutOfRangeError("out of int32_t range")}, + absl::nullopt}, {"Int32MinConversion", [] { return CheckedInt64ToInt32( @@ -595,7 +565,7 @@ INSTANTIATE_TEST_SUITE_P( return CheckedInt64ToInt32( static_cast(std::numeric_limits::lowest())); }, - absl::OutOfRangeError("out of int32_t range")}, + absl::nullopt}, }), [](const testing::TestParamInfo& info) { return info.param.test_name; }); @@ -622,7 +592,7 @@ INSTANTIATE_TEST_SUITE_P( return CheckedUint64ToUint32( static_cast(std::numeric_limits::max())); }, - absl::OutOfRangeError("out of uint32_t range")}, + absl::nullopt}, }), [](const testing::TestParamInfo& info) { return info.param.test_name; }); diff --git a/runtime/standard/arithmetic_functions.cc b/runtime/standard/arithmetic_functions.cc index a851ceb39..0c265fa89 100644 --- a/runtime/standard/arithmetic_functions.cc +++ b/runtime/standard/arithmetic_functions.cc @@ -36,20 +36,18 @@ Value Add(Type v0, Type v1); template <> Value Add(int64_t v0, int64_t v1) { - auto sum = cel::internal::CheckedAdd(v0, v1); - if (!sum.ok()) { - return ErrorValue(sum.status()); + if (auto sum = cel::internal::CheckedAdd(v0, v1); sum) { + return IntValue(*sum); } - return IntValue(*sum); + return IntOverflowError(); } template <> Value Add(uint64_t v0, uint64_t v1) { - auto sum = cel::internal::CheckedAdd(v0, v1); - if (!sum.ok()) { - return ErrorValue(sum.status()); + if (auto sum = cel::internal::CheckedAdd(v0, v1); sum) { + return UintValue(*sum); } - return UintValue(*sum); + return UintOverflowError(); } template <> @@ -62,20 +60,18 @@ Value Sub(Type v0, Type v1); template <> Value Sub(int64_t v0, int64_t v1) { - auto diff = cel::internal::CheckedSub(v0, v1); - if (!diff.ok()) { - return ErrorValue(diff.status()); + if (auto sum = cel::internal::CheckedSub(v0, v1); sum) { + return IntValue(*sum); } - return IntValue(*diff); + return IntOverflowError(); } template <> Value Sub(uint64_t v0, uint64_t v1) { - auto diff = cel::internal::CheckedSub(v0, v1); - if (!diff.ok()) { - return ErrorValue(diff.status()); + if (auto sum = cel::internal::CheckedSub(v0, v1); sum) { + return UintValue(*sum); } - return UintValue(*diff); + return UintOverflowError(); } template <> @@ -88,20 +84,18 @@ Value Mul(Type v0, Type v1); template <> Value Mul(int64_t v0, int64_t v1) { - auto prod = cel::internal::CheckedMul(v0, v1); - if (!prod.ok()) { - return ErrorValue(prod.status()); + if (auto prod = cel::internal::CheckedMul(v0, v1); prod) { + return IntValue(*prod); } - return IntValue(*prod); + return IntOverflowError(); } template <> Value Mul(uint64_t v0, uint64_t v1) { - auto prod = cel::internal::CheckedMul(v0, v1); - if (!prod.ok()) { - return ErrorValue(prod.status()); + if (auto prod = cel::internal::CheckedMul(v0, v1); prod) { + return UintValue(*prod); } - return UintValue(*prod); + return UintOverflowError(); } template <> @@ -116,22 +110,20 @@ Value Div(Type v0, Type v1); // division by 0 template <> Value Div(int64_t v0, int64_t v1) { - auto quot = cel::internal::CheckedDiv(v0, v1); - if (!quot.ok()) { - return ErrorValue(quot.status()); + if (auto quot = cel::internal::CheckedDiv(v0, v1); quot) { + return IntValue(*quot); } - return IntValue(*quot); + return v1 != int64_t{0} ? IntOverflowError() : IntDivisionByZeroError(); } // Division operations for integer types should check for // division by 0 template <> Value Div(uint64_t v0, uint64_t v1) { - auto quot = cel::internal::CheckedDiv(v0, v1); - if (!quot.ok()) { - return ErrorValue(quot.status()); + if (auto quot = cel::internal::CheckedDiv(v0, v1); quot) { + return UintValue(*quot); } - return UintValue(*quot); + return v1 != uint64_t{0} ? UintOverflowError() : UintDivisionByZeroError(); } template <> @@ -151,20 +143,18 @@ Value Modulo(Type v0, Type v1); // division by 0 template <> Value Modulo(int64_t v0, int64_t v1) { - auto mod = cel::internal::CheckedMod(v0, v1); - if (!mod.ok()) { - return ErrorValue(mod.status()); + if (auto quot = cel::internal::CheckedMod(v0, v1); quot) { + return IntValue(*quot); } - return IntValue(*mod); + return v1 != int64_t{0} ? IntOverflowError() : IntModuloByZeroError(); } template <> Value Modulo(uint64_t v0, uint64_t v1) { - auto mod = cel::internal::CheckedMod(v0, v1); - if (!mod.ok()) { - return ErrorValue(mod.status()); + if (auto quot = cel::internal::CheckedMod(v0, v1); quot) { + return UintValue(*quot); } - return UintValue(*mod); + return v1 != uint64_t{0} ? UintOverflowError() : UintModuloByZeroError(); } // Helper method @@ -211,17 +201,16 @@ absl::Status RegisterArithmeticFunctions(FunctionRegistry& registry, &Modulo))); // Negation group - CEL_RETURN_IF_ERROR( - registry.Register(UnaryFunctionAdapter::CreateDescriptor( - cel::builtin::kNeg, false), - UnaryFunctionAdapter::WrapFunction( - [](int64_t value) -> Value { - auto inv = cel::internal::CheckedNegation(value); - if (!inv.ok()) { - return ErrorValue(inv.status()); - } - return IntValue(*inv); - }))); + CEL_RETURN_IF_ERROR(registry.Register( + UnaryFunctionAdapter::CreateDescriptor(cel::builtin::kNeg, + false), + UnaryFunctionAdapter::WrapFunction( + [](int64_t value) -> Value { + if (auto inv = cel::internal::CheckedNegation(value); inv) { + return IntValue(*inv); + } + return IntOverflowError(); + }))); return registry.Register( UnaryFunctionAdapter::CreateDescriptor(cel::builtin::kNeg, diff --git a/runtime/standard/time_functions.cc b/runtime/standard/time_functions.cc index 1db09141b..4e73032d0 100644 --- a/runtime/standard/time_functions.cc +++ b/runtime/standard/time_functions.cc @@ -312,11 +312,10 @@ absl::Status RegisterCheckedTimeArithmeticFunctions( BinaryFunctionAdapter, absl::Time, absl::Duration>:: WrapFunction( [](absl::Time t1, absl::Duration d2) -> absl::StatusOr { - auto sum = cel::internal::CheckedAdd(t1, d2); - if (!sum.ok()) { - return ErrorValue(sum.status()); + if (auto sum = cel::internal::CheckedAdd(t1, d2); sum) { + return TimestampValue(*sum); } - return TimestampValue(*sum); + return TimestampOverflowError(); }))); CEL_RETURN_IF_ERROR(registry.Register( @@ -325,11 +324,10 @@ absl::Status RegisterCheckedTimeArithmeticFunctions( BinaryFunctionAdapter, absl::Duration, absl::Time>:: WrapFunction( [](absl::Duration d2, absl::Time t1) -> absl::StatusOr { - auto sum = cel::internal::CheckedAdd(t1, d2); - if (!sum.ok()) { - return ErrorValue(sum.status()); + if (auto sum = cel::internal::CheckedAdd(t1, d2); sum) { + return TimestampValue(*sum); } - return TimestampValue(*sum); + return TimestampOverflowError(); }))); CEL_RETURN_IF_ERROR(registry.Register( @@ -340,11 +338,10 @@ absl::Status RegisterCheckedTimeArithmeticFunctions( absl::StatusOr, absl::Duration, absl::Duration>::WrapFunction([](absl::Duration d1, absl::Duration d2) -> absl::StatusOr { - auto sum = cel::internal::CheckedAdd(d1, d2); - if (!sum.ok()) { - return ErrorValue(sum.status()); + if (auto sum = cel::internal::CheckedAdd(d1, d2); sum) { + return DurationValue(*sum); } - return DurationValue(*sum); + return DurationOverflowError(); }))); CEL_RETURN_IF_ERROR(registry.Register( @@ -353,11 +350,10 @@ absl::Status RegisterCheckedTimeArithmeticFunctions( BinaryFunctionAdapter, absl::Time, absl::Duration>:: WrapFunction( [](absl::Time t1, absl::Duration d2) -> absl::StatusOr { - auto diff = cel::internal::CheckedSub(t1, d2); - if (!diff.ok()) { - return ErrorValue(diff.status()); + if (auto diff = cel::internal::CheckedSub(t1, d2); diff) { + return TimestampValue(*diff); } - return TimestampValue(*diff); + return TimestampOverflowError(); }))); CEL_RETURN_IF_ERROR(registry.Register( @@ -367,11 +363,10 @@ absl::Status RegisterCheckedTimeArithmeticFunctions( BinaryFunctionAdapter, absl::Time, absl::Time>:: WrapFunction( [](absl::Time t1, absl::Time t2) -> absl::StatusOr { - auto diff = cel::internal::CheckedSub(t1, t2); - if (!diff.ok()) { - return ErrorValue(diff.status()); + if (auto sum = cel::internal::CheckedSub(t1, t2); sum) { + return DurationValue(*sum); } - return DurationValue(*diff); + return DurationOverflowError(); }))); CEL_RETURN_IF_ERROR(registry.Register( @@ -382,11 +377,10 @@ absl::Status RegisterCheckedTimeArithmeticFunctions( absl::StatusOr, absl::Duration, absl::Duration>::WrapFunction([](absl::Duration d1, absl::Duration d2) -> absl::StatusOr { - auto diff = cel::internal::CheckedSub(d1, d2); - if (!diff.ok()) { - return ErrorValue(diff.status()); + if (auto sum = cel::internal::CheckedSub(d1, d2); sum) { + return DurationValue(*sum); } - return DurationValue(*diff); + return DurationOverflowError(); }))); return absl::OkStatus(); diff --git a/runtime/standard/type_conversion_functions.cc b/runtime/standard/type_conversion_functions.cc index 10b582638..fc146c039 100644 --- a/runtime/standard/type_conversion_functions.cc +++ b/runtime/standard/type_conversion_functions.cc @@ -61,11 +61,10 @@ absl::Status RegisterIntConversionFunctions(FunctionRegistry& registry, status = UnaryFunctionAdapter::RegisterGlobalOverload( cel::builtin::kInt, [](double v) -> Value { - auto conv = cel::internal::CheckedDoubleToInt64(v); - if (!conv.ok()) { - return ErrorValue(conv.status()); + if (auto conv = cel::internal::CheckedDoubleToInt64(v); conv) { + return IntValue(*conv); } - return IntValue(*conv); + return DoubleToIntOutOfRangeError(); }, registry); CEL_RETURN_IF_ERROR(status); @@ -100,11 +99,10 @@ absl::Status RegisterIntConversionFunctions(FunctionRegistry& registry, return UnaryFunctionAdapter::RegisterGlobalOverload( cel::builtin::kInt, [](uint64_t v) -> Value { - auto conv = cel::internal::CheckedUint64ToInt64(v); - if (!conv.ok()) { - return ErrorValue(conv.status()); + if (auto conv = cel::internal::CheckedUint64ToInt64(v); conv) { + return IntValue(*conv); } - return IntValue(*conv); + return UintToIntOutOfRangeError(); }, registry); } @@ -200,11 +198,10 @@ absl::Status RegisterUintConversionFunctions(FunctionRegistry& registry, UnaryFunctionAdapter::RegisterGlobalOverload( cel::builtin::kUint, [](double v) -> Value { - auto conv = cel::internal::CheckedDoubleToUint64(v); - if (!conv.ok()) { - return ErrorValue(conv.status()); + if (auto conv = cel::internal::CheckedDoubleToUint64(v); conv) { + return UintValue(*conv); } - return UintValue(*conv); + return DoubleToUintOutOfRangeError(); }, registry); CEL_RETURN_IF_ERROR(status); @@ -213,11 +210,10 @@ absl::Status RegisterUintConversionFunctions(FunctionRegistry& registry, status = UnaryFunctionAdapter::RegisterGlobalOverload( cel::builtin::kUint, [](int64_t v) -> Value { - auto conv = cel::internal::CheckedInt64ToUint64(v); - if (!conv.ok()) { - return ErrorValue(conv.status()); + if (auto conv = cel::internal::CheckedInt64ToUint64(v); conv) { + return UintValue(*conv); } - return UintValue(*conv); + return IntToUintOutOfRangeError(); }, registry); CEL_RETURN_IF_ERROR(status);