Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
25c7d79
Change writer to store chars written as size_t
mleleszi Oct 7, 2025
362c00e
Prinf internals: use -errno as return values rather than custrom erro…
mleleszi Oct 7, 2025
f8c7569
Refactor printf family functions to use PrintfResult
mleleszi Oct 7, 2025
f8f3553
Formatting
mleleszi Oct 7, 2025
4761e9a
Fix tests
mleleszi Oct 7, 2025
1dd65af
Add IO error test cases for file variants
mleleszi Oct 7, 2025
ef55545
Add overflow test for fprintf
mleleszi Oct 7, 2025
63c710b
Cleanup
mleleszi Oct 7, 2025
e035a15
Add overflow test for vfprintf
mleleszi Oct 8, 2025
56d6968
Baremetal stuff
mleleszi Oct 8, 2025
995bb08
Add test for nullptr write error
mleleszi Oct 8, 2025
eedbad9
Add overflow handling to strfrom functions
mleleszi Oct 8, 2025
242b75c
Cleanup
mleleszi Oct 10, 2025
a403604
Merge branch 'main' into libc-printf-error-handling
mleleszi Oct 10, 2025
492b9f1
Run clang-format
mleleszi Oct 10, 2025
71554f9
Fix unused param error
mleleszi Oct 10, 2025
b03c545
Refactor File to store error_code instead of error flag
mleleszi Oct 12, 2025
6d05774
Map internall err code to errno
mleleszi Oct 12, 2025
d194975
Return system IO errors return values
mleleszi Oct 17, 2025
69e9362
Use ErroOr for print retval instead of custom struct
mleleszi Oct 17, 2025
f8cb702
Fix tests
mleleszi Oct 17, 2025
0e8b25c
Fix baremetal impl
mleleszi Oct 17, 2025
756cb37
Clean up tests, dependencies
mleleszi Oct 18, 2025
463e929
Add platfrom specific error converter implementations
mleleszi Oct 23, 2025
5f3b0af
Clean up dependencies
mleleszi Oct 23, 2025
428c7e9
Add darwin and generic impl of error converter
mleleszi Oct 23, 2025
21df095
Cleanup
mleleszi Oct 31, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions libc/src/stdio/asprintf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,17 @@ LLVM_LIBC_FUNCTION(int, asprintf,
// and pointer semantics, as well as handling
// destruction automatically.
va_end(vlist);
int ret = printf_core::vasprintf_internal(buffer, format, args);
return ret;
auto ret_val = printf_core::vasprintf_internal(buffer, format, args);
if (ret_val.has_error()) {
libc_errno = ret_val.error;
return -1;
}
if (ret_val.value > cpp::numeric_limits<int>::max()) {
libc_errno = EOVERFLOW;
return -1;
}

return static_cast<int>(ret_val.value);
}

} // namespace LIBC_NAMESPACE_DECL
19 changes: 15 additions & 4 deletions libc/src/stdio/baremetal/printf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,24 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
buffer, BUFF_SIZE, &stdout_write_hook, nullptr);
printf_core::Writer<printf_core::WriteMode::FLUSH_TO_STREAM> writer(wb);

int retval = printf_core::printf_main(&writer, format, args);
auto retval = printf_core::printf_main(&writer, format, args);
if (retval.has_error()) {
libc_errno = retval.error;
return -1;
}

int flushval = wb.overflow_write("");
if (flushval != printf_core::WRITE_OK)
retval = flushval;
if (flushval != printf_core::WRITE_OK) {
libc_errno = -flushval;
return -1;
}

return retval;
if (retval.value > cpp::numeric_limits<int>::max()) {
libc_errno = EOVERFLOW;
return -1;
}

return static_cast<int>(retval.value);
}

} // namespace LIBC_NAMESPACE_DECL
19 changes: 15 additions & 4 deletions libc/src/stdio/baremetal/vprintf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,24 @@ LLVM_LIBC_FUNCTION(int, vprintf,
buffer, BUFF_SIZE, &stdout_write_hook, nullptr);
printf_core::Writer<printf_core::WriteMode::FLUSH_TO_STREAM> writer(wb);

int retval = printf_core::printf_main(&writer, format, args);
auto retval = printf_core::printf_main(&writer, format, args);
if (retval.has_error()) {
libc_errno = retval.error;
return -1;
}

int flushval = wb.overflow_write("");
if (flushval != printf_core::WRITE_OK)
retval = flushval;
if (flushval != printf_core::WRITE_OK) {
libc_errno = -flushval;
return -1;
}

return retval;
if (retval.value > cpp::numeric_limits<int>::max()) {
libc_errno = EOVERFLOW;
return -1;
}

return static_cast<int>(retval.value);
}

} // namespace LIBC_NAMESPACE_DECL
13 changes: 11 additions & 2 deletions libc/src/stdio/generic/fprintf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,17 @@ LLVM_LIBC_FUNCTION(int, fprintf,
// and pointer semantics, as well as handling
// destruction automatically.
va_end(vlist);
int ret_val = printf_core::vfprintf_internal(stream, format, args);
return ret_val;
auto ret_val = printf_core::vfprintf_internal(stream, format, args);
if (ret_val.has_error()) {
libc_errno = ret_val.error;
return -1;
}
if (ret_val.value > cpp::numeric_limits<int>::max()) {
libc_errno = EOVERFLOW;
return -1;
}

return static_cast<int>(ret_val.value);
}

} // namespace LIBC_NAMESPACE_DECL
13 changes: 11 additions & 2 deletions libc/src/stdio/generic/printf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,18 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
// and pointer semantics, as well as handling
// destruction automatically.
va_end(vlist);
int ret_val = printf_core::vfprintf_internal(
auto ret_val = printf_core::vfprintf_internal(
reinterpret_cast<::FILE *>(PRINTF_STDOUT), format, args);
return ret_val;
if (ret_val.has_error()) {
libc_errno = ret_val.error;
return -1;
}
if (ret_val.value > cpp::numeric_limits<int>::max()) {
libc_errno = EOVERFLOW;
return -1;
}

return static_cast<int>(ret_val.value);
}

} // namespace LIBC_NAMESPACE_DECL
13 changes: 11 additions & 2 deletions libc/src/stdio/generic/vfprintf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,17 @@ LLVM_LIBC_FUNCTION(int, vfprintf,
internal::ArgList args(vlist); // This holder class allows for easier copying
// and pointer semantics, as well as handling
// destruction automatically.
int ret_val = printf_core::vfprintf_internal(stream, format, args);
return ret_val;
auto ret_val = printf_core::vfprintf_internal(stream, format, args);
if (ret_val.has_error()) {
libc_errno = ret_val.error;
return -1;
}
if (ret_val.value > cpp::numeric_limits<int>::max()) {
libc_errno = EOVERFLOW;
return -1;
}

return static_cast<int>(ret_val.value);
}

} // namespace LIBC_NAMESPACE_DECL
13 changes: 11 additions & 2 deletions libc/src/stdio/generic/vprintf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,18 @@ LLVM_LIBC_FUNCTION(int, vprintf,
internal::ArgList args(vlist); // This holder class allows for easier copying
// and pointer semantics, as well as handling
// destruction automatically.
int ret_val = printf_core::vfprintf_internal(
auto ret_val = printf_core::vfprintf_internal(
reinterpret_cast<::FILE *>(PRINTF_STDOUT), format, args);
return ret_val;
if (ret_val.has_error()) {
libc_errno = ret_val.error;
return -1;
}
if (ret_val.value > cpp::numeric_limits<int>::max()) {
libc_errno = EOVERFLOW;
return -1;
}

return static_cast<int>(ret_val.value);
}

} // namespace LIBC_NAMESPACE_DECL
20 changes: 12 additions & 8 deletions libc/src/stdio/printf_core/core_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {

struct PrintfResult {
size_t value;
int error;

constexpr PrintfResult(size_t val) : value(val), error(0) {}
constexpr PrintfResult(size_t val, int error) : value(val), error(error) {}

constexpr bool has_error() { return error != 0; }

constexpr operator size_t() { return value; }
};

// These length modifiers match the length modifiers in the format string, which
// is why they are formatted differently from the rest of the file.
enum class LengthModifier { hh, h, l, ll, j, z, t, L, w, wf, none };
Expand Down Expand Up @@ -132,14 +144,6 @@ template <typename T> LIBC_INLINE constexpr TypeDesc type_desc_from_type() {

// This is the value to be returned by conversions when no error has occurred.
constexpr int WRITE_OK = 0;
// These are the printf return values for when an error has occurred. They are
// all negative, and should be distinct.
constexpr int FILE_WRITE_ERROR = -1;
constexpr int FILE_STATUS_ERROR = -2;
constexpr int NULLPTR_WRITE_ERROR = -3;
constexpr int INT_CONVERSION_ERROR = -4;
constexpr int FIXED_POINT_CONVERSION_ERROR = -5;
constexpr int ALLOCATION_ERROR = -6;
} // namespace printf_core
} // namespace LIBC_NAMESPACE_DECL

Expand Down
3 changes: 2 additions & 1 deletion libc/src/stdio/printf_core/fixed_converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FIXED_CONVERTER_H
#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FIXED_CONVERTER_H

#include "hdr/errno_macros.h"
#include "include/llvm-libc-macros/stdfix-macros.h"
#include "src/__support/CPP/string_view.h"
#include "src/__support/ctype_utils.h"
Expand Down Expand Up @@ -59,7 +60,7 @@ LIBC_INLINE constexpr uint32_t const_ten_exp(uint32_t exponent) {
READ_FX_BITS(unsigned LENGTH_MODIFIER accum); \
} else { \
LIBC_ASSERT(false && "Invalid conversion name passed to convert_fixed"); \
return FIXED_POINT_CONVERSION_ERROR; \
return -EINVAL; \
} \
} while (false)

Expand Down
3 changes: 2 additions & 1 deletion libc/src/stdio/printf_core/int_converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_INT_CONVERTER_H
#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_INT_CONVERTER_H

#include "hdr/errno_macros.h"
#include "src/__support/CPP/span.h"
#include "src/__support/CPP/string_view.h"
#include "src/__support/ctype_utils.h"
Expand Down Expand Up @@ -91,7 +92,7 @@ LIBC_INLINE int convert_int(Writer<write_mode> *writer,
cpp::array<char, details::num_buf_size()> buf;
auto str = details::num_to_strview(num, buf, to_conv.conv_name);
if (!str)
return INT_CONVERSION_ERROR;
return -ERANGE;

size_t digits_written = str->size();

Expand Down
7 changes: 3 additions & 4 deletions libc/src/stdio/printf_core/printf_main.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ namespace LIBC_NAMESPACE_DECL {
namespace printf_core {

template <WriteMode write_mode>
int printf_main(Writer<write_mode> *writer, const char *__restrict str,
internal::ArgList &args) {
PrintfResult printf_main(Writer<write_mode> *writer, const char *__restrict str,
internal::ArgList &args) {
Parser<internal::ArgList> parser(str, args);
int result = 0;
for (FormatSection cur_section = parser.get_next_section();
Expand All @@ -33,9 +33,8 @@ int printf_main(Writer<write_mode> *writer, const char *__restrict str,
result = convert(writer, cur_section);
else
result = writer->write(cur_section.raw_string);

if (result < 0)
return result;
return {0, -result};
}

return writer->get_chars_written();
Expand Down
20 changes: 11 additions & 9 deletions libc/src/stdio/printf_core/vasprintf_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//

#include "hdr/errno_macros.h"
#include "hdr/func/free.h"
#include "hdr/func/malloc.h"
#include "hdr/func/realloc.h"
Expand All @@ -29,7 +30,7 @@ LIBC_INLINE int resize_overflow_hook(cpp::string_view new_str, void *target) {
if (new_buff == nullptr) {
if (wb->buff != wb->init_buff)
free(wb->buff);
return printf_core::ALLOCATION_ERROR;
return -ENOMEM;
}
if (isBuffOnStack)
inline_memcpy(new_buff, wb->buff, wb->buff_cur);
Expand All @@ -42,27 +43,28 @@ LIBC_INLINE int resize_overflow_hook(cpp::string_view new_str, void *target) {

constexpr size_t DEFAULT_BUFFER_SIZE = 200;

LIBC_INLINE int vasprintf_internal(char **ret, const char *__restrict format,
internal::ArgList args) {
LIBC_INLINE PrintfResult vasprintf_internal(char **ret,
const char *__restrict format,
internal::ArgList args) {
char init_buff_on_stack[DEFAULT_BUFFER_SIZE];
printf_core::WriteBuffer<Mode<WriteMode::RESIZE_AND_FILL_BUFF>::value> wb(
init_buff_on_stack, DEFAULT_BUFFER_SIZE, resize_overflow_hook);
printf_core::Writer writer(wb);

auto ret_val = printf_core::printf_main(&writer, format, args);
if (ret_val < 0) {
if (ret_val.has_error()) {
*ret = nullptr;
return -1;
return ret_val;
}
if (wb.buff == init_buff_on_stack) {
*ret = static_cast<char *>(malloc(ret_val + 1));
*ret = static_cast<char *>(malloc(ret_val.value + 1));
if (ret == nullptr)
return printf_core::ALLOCATION_ERROR;
inline_memcpy(*ret, wb.buff, ret_val);
return {0, ENOMEM};
inline_memcpy(*ret, wb.buff, ret_val.value);
} else {
*ret = wb.buff;
}
(*ret)[ret_val] = '\0';
(*ret)[ret_val.value] = '\0';
return ret_val;
}
} // namespace printf_core
Expand Down
17 changes: 11 additions & 6 deletions libc/src/stdio/printf_core/vfprintf_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_VFPRINTF_INTERNAL_H
#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_VFPRINTF_INTERNAL_H

#include "hdr/errno_macros.h"
#include "src/__support/File/file.h"
#include "src/__support/arg_list.h"
#include "src/__support/macros/attributes.h" // For LIBC_INLINE
Expand Down Expand Up @@ -63,23 +64,27 @@ LIBC_INLINE int file_write_hook(cpp::string_view new_str, void *fp) {
size_t written = internal::fwrite_unlocked(new_str.data(), sizeof(char),
new_str.size(), target_file);
if (written != new_str.size() || internal::ferror_unlocked(target_file))
return FILE_WRITE_ERROR;
return -EIO;
return WRITE_OK;
}

LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream,
const char *__restrict format,
internal::ArgList &args) {
LIBC_INLINE PrintfResult vfprintf_internal(::FILE *__restrict stream,
const char *__restrict format,
internal::ArgList &args) {
constexpr size_t BUFF_SIZE = 1024;
char buffer[BUFF_SIZE];
printf_core::WriteBuffer<Mode<WriteMode::FLUSH_TO_STREAM>::value> wb(
buffer, BUFF_SIZE, &file_write_hook, reinterpret_cast<void *>(stream));
Writer writer(wb);
internal::flockfile(stream);
int retval = printf_main(&writer, format, args);
auto retval = printf_main(&writer, format, args);
if (retval.has_error()) {
internal::funlockfile(stream);
return retval;
}
int flushval = wb.overflow_write("");
if (flushval != WRITE_OK)
retval = flushval;
retval.error = -flushval;
internal::funlockfile(stream);
return retval;
}
Expand Down
7 changes: 4 additions & 3 deletions libc/src/stdio/printf_core/write_int_converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_WRITE_INT_CONVERTER_H
#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_WRITE_INT_CONVERTER_H

#include "hdr/errno_macros.h"
#include "src/__support/macros/config.h"
#include "src/stdio/printf_core/core_structs.h"
#include "src/stdio/printf_core/writer.h"
Expand All @@ -26,14 +27,14 @@ LIBC_INLINE int convert_write_int(Writer<write_mode> *writer,
#ifndef LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
// This is an additional check added by LLVM-libc.
if (to_conv.conv_val_ptr == nullptr)
return NULLPTR_WRITE_ERROR;
return -EINVAL;
#endif // LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS

int written = writer->get_chars_written();
size_t written = writer->get_chars_written();

switch (to_conv.length_modifier) {
case LengthModifier::none:
*reinterpret_cast<int *>(to_conv.conv_val_ptr) = written;
*reinterpret_cast<int *>(to_conv.conv_val_ptr) = static_cast<int>(written);
break;
case LengthModifier::l:
*reinterpret_cast<long *>(to_conv.conv_val_ptr) = written;
Expand Down
Loading
Loading