diff --git a/common/value.h b/common/value.h index c2ca1c608..f6ce5de1a 100644 --- a/common/value.h +++ b/common/value.h @@ -2609,7 +2609,8 @@ static_assert(std::is_nothrow_swappable_v); inline common_internal::ImplicitlyConvertibleStatus ErrorValueAssign::operator()(absl::Status status) const { - *value_ = ErrorValue(std::move(status)); + *value_ = arena_ != nullptr ? ErrorValue::From(std::move(status), arena_) + : ErrorValue(std::move(status)); return common_internal::ImplicitlyConvertibleStatus(); } diff --git a/common/values/error_value.cc b/common/values/error_value.cc index 8ea6554ec..538ac4234 100644 --- a/common/values/error_value.cc +++ b/common/values/error_value.cc @@ -46,46 +46,106 @@ const absl::Status& DefaultErrorValue() { return *value; } +absl::Status MakeNoSuchFieldError(absl::string_view field) { + return absl::NotFoundError( + absl::StrCat("no_such_field", field.empty() ? "" : " : ", field)); +} + +absl::Status MakeNoSuchKeyError(absl::string_view key) { + return absl::NotFoundError(absl::StrCat("Key not found in map : ", key)); +} + +absl::Status MakeNoSuchTypeError(absl::string_view type) { + return absl::NotFoundError(absl::StrCat("type not found: ", type)); +} + +absl::Status MakeTypeConversionError(absl::string_view from, + absl::string_view to) { + return absl::InvalidArgumentError( + absl::StrCat("type conversion error from '", from, "' to '", to, "'")); +} + +absl::Status MakeIndexOutOfBoundsError(size_t index) { + return absl::InvalidArgumentError( + absl::StrCat("index out of bounds: ", index)); +} + +absl::Status MakeIndexOutOfBoundsError(ptrdiff_t index) { + return absl::InvalidArgumentError( + absl::StrCat("index out of bounds: ", index)); +} + } // namespace -ErrorValue::ErrorValue() : ErrorValue(DefaultErrorValue()) {} +ErrorValue::ErrorValue() : ErrorValue(nullptr, &DefaultErrorValue()) {} ErrorValue NoSuchFieldError(absl::string_view field) { - return ErrorValue(absl::NotFoundError( - absl::StrCat("no_such_field", field.empty() ? "" : " : ", field))); + return ErrorValue(MakeNoSuchFieldError(field)); +} + +ErrorValue NoSuchFieldError(absl::string_view field, + google::protobuf::Arena* absl_nonnull arena) { + return ErrorValue::From(MakeNoSuchFieldError(field), arena); } ErrorValue NoSuchKeyError(absl::string_view key) { - return ErrorValue( - absl::NotFoundError(absl::StrCat("Key not found in map : ", key))); + return ErrorValue(MakeNoSuchKeyError(key)); +} + +ErrorValue NoSuchKeyError(absl::string_view key, + google::protobuf::Arena* absl_nonnull arena) { + return ErrorValue::From(MakeNoSuchKeyError(key), arena); } ErrorValue NoSuchTypeError(absl::string_view type) { - return ErrorValue( - absl::NotFoundError(absl::StrCat("type not found: ", type))); + return ErrorValue(MakeNoSuchTypeError(type)); +} + +ErrorValue NoSuchTypeError(absl::string_view type, + google::protobuf::Arena* absl_nonnull arena) { + return ErrorValue::From(MakeNoSuchTypeError(type), arena); } ErrorValue DuplicateKeyError() { - return ErrorValue(absl::AlreadyExistsError("duplicate key in map")); + static const absl::NoDestructor error( + absl::AlreadyExistsError("duplicate key in map")); + return ErrorValue(nullptr, &*error); } ErrorValue TypeConversionError(absl::string_view from, absl::string_view to) { - return ErrorValue(absl::InvalidArgumentError( - absl::StrCat("type conversion error from '", from, "' to '", to, "'"))); + return ErrorValue(MakeTypeConversionError(from, to)); +} + +ErrorValue TypeConversionError(absl::string_view from, absl::string_view to, + google::protobuf::Arena* absl_nonnull arena) { + return ErrorValue::From(MakeTypeConversionError(from, to), arena); } ErrorValue TypeConversionError(const Type& from, const Type& to) { return TypeConversionError(from.DebugString(), to.DebugString()); } +ErrorValue TypeConversionError(const Type& from, const Type& to, + google::protobuf::Arena* absl_nonnull arena) { + return TypeConversionError(from.DebugString(), to.DebugString(), arena); +} + ErrorValue IndexOutOfBoundsError(size_t index) { - return ErrorValue( - absl::InvalidArgumentError(absl::StrCat("index out of bounds: ", index))); + return ErrorValue(MakeIndexOutOfBoundsError(index)); +} + +ErrorValue IndexOutOfBoundsError(size_t index, + google::protobuf::Arena* absl_nonnull arena) { + return ErrorValue::From(MakeIndexOutOfBoundsError(index), arena); } ErrorValue IndexOutOfBoundsError(ptrdiff_t index) { - return ErrorValue( - absl::InvalidArgumentError(absl::StrCat("index out of bounds: ", index))); + return ErrorValue(MakeIndexOutOfBoundsError(index)); +} + +ErrorValue IndexOutOfBoundsError(ptrdiff_t index, + google::protobuf::Arena* absl_nonnull arena) { + return ErrorValue::From(MakeIndexOutOfBoundsError(index), arena); } bool IsNoSuchField(const ErrorValue& value) { @@ -159,30 +219,28 @@ ErrorValue ErrorValue::Clone(google::protobuf::Arena* absl_nonnull arena) const absl::Status ErrorValue::ToStatus() const& { ABSL_DCHECK(*this); - - if (arena_ == nullptr) { + if (status_ptr_ == nullptr) { return *std::launder( - reinterpret_cast(&status_.val[0])); + reinterpret_cast(&status_val_[0])); } - return *status_.ptr; + return *status_ptr_; } absl::Status ErrorValue::ToStatus() && { ABSL_DCHECK(*this); - - if (arena_ == nullptr) { + if (status_ptr_ == nullptr) { return std::move( - *std::launder(reinterpret_cast(&status_.val[0]))); + *std::launder(reinterpret_cast(&status_val_[0]))); } - return *status_.ptr; + return *status_ptr_; } ErrorValue::operator bool() const { - if (arena_ == nullptr) { - return !std::launder(reinterpret_cast(&status_.val[0])) + if (status_ptr_ == nullptr) { + return !std::launder(reinterpret_cast(&status_val_[0])) ->ok(); } - return status_.ptr != nullptr && !status_.ptr->ok(); + return !status_ptr_->ok(); } void swap(ErrorValue& lhs, ErrorValue& rhs) noexcept { diff --git a/common/values/error_value.h b/common/values/error_value.h index 4e24c866b..7008df7fa 100644 --- a/common/values/error_value.h +++ b/common/values/error_value.h @@ -31,7 +31,6 @@ #include "absl/log/absl_check.h" #include "absl/status/status.h" #include "absl/status/statusor.h" -#include "absl/strings/cord.h" #include "absl/strings/string_view.h" #include "common/arena.h" #include "common/type.h" @@ -45,6 +44,9 @@ namespace cel { class Value; +class ErrorValue; + +ErrorValue DuplicateKeyError(); // `ErrorValue` represents values of the `ErrorType`. class ABSL_ATTRIBUTE_TRIVIAL_ABI ErrorValue final @@ -52,8 +54,22 @@ class ABSL_ATTRIBUTE_TRIVIAL_ABI ErrorValue final public: static constexpr ValueKind kKind = ValueKind::kError; - explicit ErrorValue(absl::Status value) : arena_(nullptr) { - ::new (static_cast(&status_.val[0])) absl::Status(std::move(value)); + // Returns a new ErrorValue created from absl::Status. The resulting storage + // for the underlying representation of ErrorValue is stored on the arena. + [[nodiscard]] + static ErrorValue From(absl::Status value, + google::protobuf::Arena* absl_nonnull arena + ABSL_ATTRIBUTE_LIFETIME_BOUND) { + ABSL_DCHECK(!value.ok()) << "ErrorValue requires a non-OK absl::Status"; + ABSL_DCHECK(arena != nullptr); + return ErrorValue( + arena, google::protobuf::Arena::Create(arena, std::move(value))); + } + + ABSL_DEPRECATED("Use From") + explicit ErrorValue(absl::Status value) + : arena_(nullptr), status_ptr_(nullptr) { + ::new (static_cast(&status_val_[0])) absl::Status(std::move(value)); ABSL_DCHECK(*this) << "ErrorValue requires a non-OK absl::Status"; } @@ -127,80 +143,112 @@ class ABSL_ATTRIBUTE_TRIVIAL_ABI ErrorValue final explicit operator bool() const; private: + friend ErrorValue DuplicateKeyError(); friend class common_internal::ValueMixin; friend struct ArenaTraits; - ErrorValue(google::protobuf::Arena* absl_nonnull arena, + ErrorValue(google::protobuf::Arena* absl_nullable arena, const absl::Status* absl_nonnull status) - : arena_(arena) { - status_.ptr = status; - } + : arena_(arena), status_ptr_(status) {} void CopyConstruct(const ErrorValue& other) { arena_ = other.arena_; - if (arena_ == nullptr) { - ::new (static_cast(&status_.val[0])) absl::Status(*std::launder( - reinterpret_cast(&other.status_.val[0]))); - } else { - status_.ptr = other.status_.ptr; + status_ptr_ = other.status_ptr_; + if (status_ptr_ == nullptr) { + ::new (static_cast(&status_val_[0])) absl::Status(*std::launder( + reinterpret_cast(&other.status_val_[0]))); } } void MoveConstruct(ErrorValue& other) { arena_ = other.arena_; - if (arena_ == nullptr) { - ::new (static_cast(&status_.val[0])) + status_ptr_ = other.status_ptr_; + if (status_ptr_ == nullptr) { + ::new (static_cast(&status_val_[0])) absl::Status(std::move(*std::launder( - reinterpret_cast(&other.status_.val[0])))); - } else { - status_.ptr = other.status_.ptr; + reinterpret_cast(&other.status_val_[0])))); } } void Destruct() { - if (arena_ == nullptr) { - std::launder(reinterpret_cast(&status_.val[0]))->~Status(); + if (status_ptr_ == nullptr) { + std::launder(reinterpret_cast(&status_val_[0]))->~Status(); } } google::protobuf::Arena* absl_nullable arena_; - union { - alignas(absl::Status) char val[sizeof(absl::Status)]; - const absl::Status* absl_nonnull ptr; - } status_; + const absl::Status* absl_nullable status_ptr_ = nullptr; + alignas(absl::Status) char status_val_[sizeof(absl::Status)]; }; +ABSL_DEPRECATED("Use the overload which takes google::protobuf::Arena*") ErrorValue NoSuchFieldError(absl::string_view field); +ErrorValue NoSuchFieldError(absl::string_view field, + google::protobuf::Arena* absl_nonnull arena); +ABSL_DEPRECATED("Use the overload which takes google::protobuf::Arena*") ErrorValue NoSuchKeyError(absl::string_view key); +ErrorValue NoSuchKeyError(absl::string_view key, + google::protobuf::Arena* absl_nonnull arena); +ABSL_DEPRECATED("Use the overload which takes google::protobuf::Arena*") ErrorValue NoSuchTypeError(absl::string_view type); +ErrorValue NoSuchTypeError(absl::string_view type, + google::protobuf::Arena* absl_nonnull arena); ErrorValue DuplicateKeyError(); +ABSL_DEPRECATED("Use the overload which takes google::protobuf::Arena*") ErrorValue TypeConversionError(absl::string_view from, absl::string_view to); +ErrorValue TypeConversionError(absl::string_view from, absl::string_view to, + google::protobuf::Arena* absl_nonnull arena); +ABSL_DEPRECATED("Use the overload which takes google::protobuf::Arena*") ErrorValue TypeConversionError(const Type& from, const Type& to); +ErrorValue TypeConversionError(const Type& from, const Type& to, + google::protobuf::Arena* absl_nonnull arena); +ABSL_DEPRECATED("Use the overload which takes google::protobuf::Arena*") ErrorValue IndexOutOfBoundsError(size_t index); +ErrorValue IndexOutOfBoundsError(size_t index, + google::protobuf::Arena* absl_nonnull arena); +ABSL_DEPRECATED("Use the overload which takes google::protobuf::Arena*") ErrorValue IndexOutOfBoundsError(ptrdiff_t index); +ErrorValue IndexOutOfBoundsError(ptrdiff_t index, + google::protobuf::Arena* absl_nonnull arena); // 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 +ABSL_DEPRECATED("Use the overload which takes google::protobuf::Arena*") +std::enable_if_t, std::is_unsigned, + std::negation>>, + ErrorValue> IndexOutOfBoundsError(T index) { + static_assert(sizeof(T) <= sizeof(size_t)); + return IndexOutOfBoundsError(static_cast(index)); +} +template std::enable_if_t, std::is_unsigned, std::negation>>, ErrorValue> -IndexOutOfBoundsError(T index) { +IndexOutOfBoundsError(T index, google::protobuf::Arena* absl_nonnull arena) { static_assert(sizeof(T) <= sizeof(size_t)); return IndexOutOfBoundsError(static_cast(index)); } template +ABSL_DEPRECATED("Use the overload which takes google::protobuf::Arena*") +std::enable_if_t, std::is_signed, + std::negation>>, + ErrorValue> IndexOutOfBoundsError(T index) { + static_assert(sizeof(T) <= sizeof(ptrdiff_t)); + return IndexOutOfBoundsError(static_cast(index)); +} +template std::enable_if_t, std::is_signed, std::negation>>, ErrorValue> -IndexOutOfBoundsError(T index) { +IndexOutOfBoundsError(T index, google::protobuf::Arena* absl_nonnull arena) { static_assert(sizeof(T) <= sizeof(ptrdiff_t)); return IndexOutOfBoundsError(static_cast(index)); } @@ -215,11 +263,20 @@ bool IsNoSuchKey(const ErrorValue& value); class ErrorValueReturn final { public: + ABSL_DEPRECATED("Use constructor which takes google::protobuf::Arena*") ErrorValueReturn() = default; + explicit ErrorValueReturn(google::protobuf::Arena* absl_nonnull arena) : arena_(arena) { + ABSL_DCHECK(arena != nullptr); + } + ErrorValue operator()(absl::Status status) const { - return ErrorValue(std::move(status)); + return arena_ != nullptr ? ErrorValue::From(std::move(status), arena_) + : ErrorValue(std::move(status)); } + + private: + google::protobuf::Arena* arena_ = nullptr; }; namespace common_internal { @@ -248,26 +305,40 @@ class ErrorValueAssign final { public: ErrorValueAssign() = delete; + ABSL_DEPRECATED("Use constructor which takes google::protobuf::Arena*") explicit ErrorValueAssign(Value& value ABSL_ATTRIBUTE_LIFETIME_BOUND) : ErrorValueAssign(std::addressof(value)) {} + ABSL_DEPRECATED("Use constructor which takes google::protobuf::Arena*") explicit ErrorValueAssign( Value* absl_nonnull value ABSL_ATTRIBUTE_LIFETIME_BOUND) : value_(value) { ABSL_DCHECK(value != nullptr); } + ErrorValueAssign(Value& value ABSL_ATTRIBUTE_LIFETIME_BOUND, + google::protobuf::Arena* absl_nonnull arena) + : ErrorValueAssign(std::addressof(value), arena) {} + + ErrorValueAssign(Value* absl_nonnull value ABSL_ATTRIBUTE_LIFETIME_BOUND, + google::protobuf::Arena* absl_nonnull arena) + : value_(value), arena_(arena) { + ABSL_DCHECK(value != nullptr); + ABSL_DCHECK(arena != nullptr); + } + common_internal::ImplicitlyConvertibleStatus operator()( absl::Status status) const; private: Value* absl_nonnull value_; + google::protobuf::Arena* arena_ = nullptr; }; template <> struct ArenaTraits { static bool trivially_destructible(const ErrorValue& value) { - return value.arena_ != nullptr; + return value.status_ptr_ != nullptr; } };