serenity/AK/Error.h
Timothy Flynn e897008449 AK: Change ErrorOr to contain a Variant rather than inherit from it
GCC seems to get tripped up over this inheritance when converting from
an ErrorOr<StringView> to the partially specialized ErrorOr<void>. See
the following snippet:

    NEVER_INLINE ErrorOr<StringView> foo()
    {
        auto string = "abc"sv;
        outln("{:p}", string.characters_without_null_termination());
        return string;
    }

    NEVER_INLINE ErrorOr<void> bar()
    {
        auto string = TRY(foo());
        outln("{:p}", string.characters_without_null_termination());

        VERIFY(!string.starts_with('#'));
        return {};
    }

    int main()
    {
        MUST(bar());
    }

On some machines, bar() will contain a StringView whose pointer has had
its upper bits set to 0:

    0x000000010cafd6f8
    0x000000000cafd6f8

I'm not 100% clear on what's happening in the default-generated Variant
destructor that causes this. Probably worth investigating further.

The error would also be alleviated by making the Variant destructor
virtual, but rather than that, let's make ErrorOr simply contain a
Variant rather than inherit from it.

Fixes #15449.
2022-10-07 18:21:40 +01:00

130 lines
3.2 KiB
C++

/*
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/StringView.h>
#include <AK/Try.h>
#include <AK/Variant.h>
#if defined(__serenity__) && defined(KERNEL)
# include <LibC/errno_codes.h>
#else
# include <errno.h>
# include <string.h>
#endif
namespace AK {
class Error {
public:
static Error from_errno(int code) { return Error(code); }
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); }
// 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<size_t N>
ALWAYS_INLINE static Error from_string_literal(char const (&string_literal)[N])
{
return from_string_view(StringView { string_literal, N - 1 });
}
bool operator==(Error const& other) const
{
return m_code == other.m_code && m_string_literal == other.m_string_literal && m_syscall == other.m_syscall;
}
bool is_errno() const { return m_code != 0; }
bool is_syscall() const { return m_syscall; }
int code() const { return m_code; }
StringView string_literal() const { return m_string_literal; }
protected:
Error(int code)
: m_code(code)
{
}
private:
Error(StringView string_literal)
: m_string_literal(string_literal)
{
}
Error(StringView syscall_name, int rc)
: m_code(-rc)
, m_string_literal(syscall_name)
, m_syscall(true)
{
}
int m_code { 0 };
StringView m_string_literal;
bool m_syscall { false };
};
template<typename T, typename ErrorType>
class [[nodiscard]] ErrorOr {
public:
ErrorOr() requires(IsSame<T, Empty>)
: m_value_or_error(Empty {})
{
}
template<typename U>
ALWAYS_INLINE ErrorOr(U&& value) requires(!IsSame<RemoveCVReference<U>, ErrorOr<T, ErrorType>>)
: m_value_or_error(forward<U>(value))
{
}
#ifdef __serenity__
ErrorOr(ErrnoCode code)
: m_value_or_error(Error::from_errno(code))
{
}
#endif
T& value()
{
return m_value_or_error.template get<T>();
}
T const& value() const { return m_value_or_error.template get<T>(); }
ErrorType& error() { return m_value_or_error.template get<ErrorType>(); }
ErrorType const& error() const { return m_value_or_error.template get<ErrorType>(); }
bool is_error() const { return m_value_or_error.template has<ErrorType>(); }
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<T, ErrorType> m_value_or_error;
};
template<typename ErrorType>
class [[nodiscard]] ErrorOr<void, ErrorType> : public ErrorOr<Empty, ErrorType> {
public:
using ErrorOr<Empty, ErrorType>::ErrorOr;
};
}
using AK::Error;
using AK::ErrorOr;