Compare commits

..

3 Commits

Author SHA1 Message Date
34a9b35037
Option, Result: Propagate caller locations when erroring out
All checks were successful
continuous-integration/drone/push Build is passing
2023-01-22 12:00:52 +01:00
0126a8fb6e
luna: Add a new SourceLocation class and use that in check() and expect() 2023-01-22 11:43:48 +01:00
129297a0b0
CString: Fix compilation in strnlen() 2023-01-22 11:43:10 +01:00
7 changed files with 100 additions and 51 deletions

View File

@ -4,6 +4,7 @@
#include "arch/Timer.h"
#include "video/TextConsole.h"
#include <luna/Format.h>
#include <luna/SourceLocation.h>
static bool g_debug_enabled = true;
static bool g_serial_enabled = true;
@ -117,13 +118,14 @@ bool log_text_console_enabled()
static bool g_check_already_failed = false;
[[noreturn]] bool __check_failed(const char* file, const char* line, const char* func, const char* expr)
[[noreturn]] bool __check_failed(SourceLocation location, const char* expr)
{
CPU::disable_interrupts();
if (!g_check_already_failed)
{ // Avoid endlessly failing when trying to report a failed check.
g_check_already_failed = true;
kerrorln("ERROR: Check failed at %s:%s, in %s: %s", file, line, func, expr);
kerrorln("ERROR: Check failed at %s:%d, in %s: %s", location.file(), location.line(), location.function(),
expr);
CPU::print_stack_trace();
}
CPU::efficient_halt();

View File

@ -1,6 +1,7 @@
#pragma once
#include <luna/SourceLocation.h>
[[noreturn]] extern bool __check_failed(const char* file, const char* line, const char* func, const char* expr);
[[noreturn]] extern bool __check_failed(SourceLocation location, const char* expr);
#ifndef STRINGIZE_VALUE_OF
#define STRINGIZE(x) #x
@ -10,22 +11,25 @@
// Like check(), but with a custom error message.
#define expect(expr, message) \
do { \
if (!(expr)) [[unlikely]] \
{ \
__check_failed(__FILE__, STRINGIZE_VALUE_OF(__LINE__), __PRETTY_FUNCTION__, message); \
} \
if (!(expr)) [[unlikely]] { __check_failed(SourceLocation::current(), message); } \
} while (0)
#define expect_at(expr, location, message) \
do { \
if (!(expr)) [[unlikely]] { __check_failed(location, message); } \
} while (0)
// Like assert(), but always enabled.
#define check(expr) \
do { \
if (!(expr)) [[unlikely]] \
{ \
__check_failed(__FILE__, STRINGIZE_VALUE_OF(__LINE__), __PRETTY_FUNCTION__, #expr); \
} \
if (!(expr)) [[unlikely]] { __check_failed(SourceLocation::current(), #expr); } \
} while (0)
#define unreachable() \
__check_failed(__FILE__, STRINGIZE_VALUE_OF(__LINE__), __PRETTY_FUNCTION__, "Reached unreachable code")
#define check_at(expr, location) \
do { \
if (!(expr)) [[unlikely]] { __check_failed(location, #expr); } \
} while (0)
#define todo() __check_failed(__FILE__, STRINGIZE_VALUE_OF(__LINE__), __PRETTY_FUNCTION__, "Reached a TODO!")
#define unreachable() __check_failed(SourceLocation::current(), "Reached unreachable code")
#define todo() __check_failed(SourceLocation::current(), "Reached a TODO!")

View File

@ -64,9 +64,9 @@ template <typename T> class Option
return m_has_value;
}
T value() const
T value(SourceLocation caller = SourceLocation::current()) const
{
expect(has_value(), "Option::value called on an empty Option");
expect_at(has_value(), caller, "Option::value called on an empty Option");
return m_storage.fetch_reference();
}
@ -75,9 +75,9 @@ template <typename T> class Option
return m_storage.fetch_reference();
}
T release_value()
T release_value(SourceLocation caller = SourceLocation::current())
{
expect(has_value(), "Option::release_value called on an empty Option");
expect_at(has_value(), caller, "Option::release_value called on an empty Option");
m_has_value = false;
return move(m_storage.fetch_reference());
}
@ -129,9 +129,9 @@ template <typename T> class Option
{
}
ErrorHandle release_error()
ErrorHandle release_error(SourceLocation caller = SourceLocation::current())
{
expect(!has_value(), "Option::release_error called on a non-empty Option");
expect_at(!has_value(), caller, "Option::release_error called on a non-empty Option");
return ErrorHandle {};
}

View File

@ -70,33 +70,33 @@ template <typename T> class Result
return m_value.has_value();
}
int error() const
int error(SourceLocation caller = SourceLocation::current()) const
{
expect(has_error(), "Result::error() called on a Result that holds a value");
expect_at(has_error(), caller, "Result::error() called on a Result that holds a value");
return m_error;
}
Error release_error() const
Error release_error(SourceLocation caller = SourceLocation::current()) const
{
expect(has_error(), "Result::release_error() called on a Result that holds a value");
expect_at(has_error(), caller, "Result::release_error() called on a Result that holds a value");
return { m_error };
}
const char* error_string() const
const char* error_string(SourceLocation caller = SourceLocation::current()) const
{
expect(has_error(), "Result::error_string() called on a Result that holds a value");
expect_at(has_error(), caller, "Result::error_string() called on a Result that holds a value");
return ::error_string(m_error);
}
T value() const
T value(SourceLocation caller = SourceLocation::current()) const
{
expect(has_value(), "Result::value() called on a Result that holds an error");
expect_at(has_value(), caller, "Result::value() called on a Result that holds an error");
return m_value.unchecked_value({});
}
T expect_value(const char* reason) const
T expect_value(const char* reason, SourceLocation caller = SourceLocation::current()) const
{
expect(has_value(), reason);
expect_at(has_value(), caller, reason);
return m_value.unchecked_value({});
}
@ -115,15 +115,15 @@ template <typename T> class Result
return m_value.try_move_value(ref);
}
T release_value()
T release_value(SourceLocation caller = SourceLocation::current())
{
expect(has_value(), "Result::release_value() called on a Result that holds an error");
expect_at(has_value(), caller, "Result::release_value() called on a Result that holds an error");
return m_value.unchecked_release_value({});
}
T expect_release_value(const char* reason)
T expect_release_value(const char* reason, SourceLocation caller = SourceLocation::current())
{
expect(has_value(), reason);
expect_at(has_value(), caller, reason);
return m_value.unchecked_release_value({});
}
@ -188,45 +188,45 @@ template <> class Result<void>
return !m_has_error;
}
int error() const
int error(SourceLocation caller = SourceLocation::current()) const
{
expect(has_error(), "Result::error() called on a Result that holds a value");
expect_at(has_error(), caller, "Result::error() called on a Result that holds a value");
return m_error;
}
Error release_error() const
Error release_error(SourceLocation caller = SourceLocation::current()) const
{
expect(has_error(), "Result::release_error() called on a Result that holds a value");
expect_at(has_error(), caller, "Result::release_error() called on a Result that holds a value");
return { m_error };
}
const char* error_string() const
const char* error_string(SourceLocation caller = SourceLocation::current()) const
{
expect(has_error(), "Result::error_string() called on a Result that holds a value");
expect_at(has_error(), caller, "Result::error_string() called on a Result that holds a value");
return ::error_string(m_error);
}
void value() const
void value(SourceLocation caller = SourceLocation::current()) const
{
expect(has_value(), "Result::value() called on a Result that holds an error");
expect_at(has_value(), caller, "Result::value() called on a Result that holds an error");
return;
}
void expect_value(const char* reason) const
void expect_value(const char* reason, SourceLocation caller = SourceLocation::current()) const
{
expect(has_value(), reason);
expect_at(has_value(), caller, reason);
return;
}
void release_value() const
void release_value(SourceLocation caller = SourceLocation::current()) const
{
expect(has_value(), "Result::release_value() called on a Result that holds an error");
expect_at(has_value(), caller, "Result::release_value() called on a Result that holds an error");
return;
}
void expect_release_value(const char* reason) const
void expect_release_value(const char* reason, SourceLocation caller = SourceLocation::current()) const
{
expect(has_value(), reason);
expect_at(has_value(), caller, reason);
return;
}

View File

@ -0,0 +1,42 @@
#pragma once
#include <luna/Types.h>
#ifndef STRINGIZE_VALUE_OF
#define STRINGIZE(x) #x
#define STRINGIZE_VALUE_OF(x) STRINGIZE(x)
#endif
class SourceLocation
{
public:
constexpr const char* file()
{
return m_file;
}
constexpr u32 line()
{
return m_line;
}
constexpr const char* function()
{
return m_function;
}
constexpr static SourceLocation current(const char* file = __builtin_FILE(), u32 line = __builtin_LINE(),
const char* function = __builtin_FUNCTION())
{
return SourceLocation { file, line, function };
}
private:
constexpr SourceLocation(const char* file, u32 line, const char* function)
: m_file(file), m_line(line), m_function(function)
{
}
const char* m_file;
u32 m_line;
const char* m_function;
};

View File

@ -49,7 +49,7 @@ extern "C"
usize strnlen(const char* str, usize max)
{
const char* i = str;
for (; max, *i; ++i, --max)
for (; *i && max; ++i, --max)
;
return (usize)(i - str);
}

View File

@ -1,11 +1,12 @@
#include <luna/Attributes.h>
#include <luna/SourceLocation.h>
#include <stdio.h>
#include <stdlib.h>
_weak [[noreturn]] bool __check_failed(const char* file, const char* line, const char* func, const char* expr)
_weak [[noreturn]] bool __check_failed(SourceLocation location, const char* expr)
{
// FIXME: Output to standard error instead of standard output.
printf("Check failed at %s:%s in %s: %s\n", file, line, func, expr);
printf("Check failed at %s:%d in %s: %s\n", location.file(), location.line(), location.function(), expr);
abort();
}