/* * Copyright (c) 2021, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #if defined(AK_OS_SERENITY) && defined(KERNEL) # include #else # include # include #endif namespace AK { class [[nodiscard]] Error { public: ALWAYS_INLINE Error(Error&&) = default; ALWAYS_INLINE Error& operator=(Error&&) = default; static Error from_errno(int code) { VERIFY(code != 0); return Error(code); } // NOTE: For calling this method from within kernel code, we will simply print // the error message and return the errno code. // For calling this method from userspace programs, we will simply return from // the Error::from_string_view method! static Error from_string_view_or_print_error_and_return_errno(StringView string_literal, int code); #ifndef KERNEL static Error from_syscall(StringView syscall_name, int rc) { return Error(syscall_name, rc); } static Error from_string_view(StringView string_literal) { return Error(string_literal); } template T> static Error from_string_view(T) { // `Error::from_string_view(ByteString::formatted(...))` is a somewhat common mistake, which leads to a UAF situation. // If your string outlives this error and _isn't_ a temporary being passed to this function, explicitly call .view() on it to resolve to the StringView overload. static_assert(DependentFalse, "Error::from_string_view(String) is almost always a use-after-free"); VERIFY_NOT_REACHED(); } #endif static Error copy(Error const& error) { return Error(error); } #ifndef KERNEL // NOTE: Prefer `from_string_literal` when directly typing out an error message: // // return Error::from_string_literal("Class: Some failure"); // // If you need to return a static string based on a dynamic condition (like // picking an error from an array), then prefer `from_string_view` instead. template ALWAYS_INLINE static Error from_string_literal(char const (&string_literal)[N]) { return from_string_view(StringView { string_literal, N - 1 }); } // Note: Don't call this from C++; it's here for Jakt interop (as the name suggests). template T> ALWAYS_INLINE static Error __jakt_from_string_literal(T string) { return from_string_view(string); } #endif bool operator==(Error const& other) const { #ifdef KERNEL return m_code == other.m_code; #else return m_code == other.m_code && m_string_literal == other.m_string_literal && m_syscall == other.m_syscall; #endif } int code() const { return m_code; } bool is_errno() const { return m_code != 0; } #ifndef KERNEL bool is_syscall() const { return m_syscall; } StringView string_literal() const { return m_string_literal; } #endif protected: Error(int code) : m_code(code) { } private: #ifndef KERNEL Error(StringView string_literal) : m_string_literal(string_literal) { } Error(StringView syscall_name, int rc) : m_string_literal(syscall_name) , m_code(-rc) , m_syscall(true) { } #endif Error(Error const&) = default; Error& operator=(Error const&) = default; #ifndef KERNEL StringView m_string_literal; #endif int m_code { 0 }; #ifndef KERNEL bool m_syscall { false }; #endif }; template class [[nodiscard]] ErrorOr { template friend class ErrorOr; public: using ResultType = T; using ErrorType = E; ErrorOr() requires(IsSame) : m_value_or_error(Empty {}) { } ALWAYS_INLINE ErrorOr(ErrorOr&&) = default; ALWAYS_INLINE ErrorOr& operator=(ErrorOr&&) = default; ErrorOr(ErrorOr const&) = delete; ErrorOr& operator=(ErrorOr const&) = delete; template ALWAYS_INLINE ErrorOr(ErrorOr&& value) requires(IsConvertible) : m_value_or_error(value.m_value_or_error.visit([](U& v) { return Variant(move(v)); }, [](ErrorType& error) { return Variant(move(error)); })) { } template ALWAYS_INLINE ErrorOr(U&& value) requires( requires { T(declval()); } || requires { ErrorType(declval>()); }) : m_value_or_error(forward(value)) { } #ifdef AK_OS_SERENITY ErrorOr(ErrnoCode code) : m_value_or_error(Error::from_errno(code)) { } #endif T& value() { return m_value_or_error.template get(); } T const& value() const { return m_value_or_error.template get(); } ErrorType& error() { return m_value_or_error.template get(); } ErrorType const& error() const { return m_value_or_error.template get(); } bool is_error() const { return m_value_or_error.template has(); } T release_value() { return move(value()); } ErrorType release_error() { return move(error()); } T release_value_but_fixme_should_propagate_errors() { VERIFY(!is_error()); return release_value(); } private: Variant m_value_or_error; }; template class [[nodiscard]] ErrorOr : public ErrorOr { public: using ResultType = void; using ErrorOr::ErrorOr; }; } #if USING_AK_GLOBALLY using AK::Error; using AK::ErrorOr; #endif