diff --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt --- a/libc/src/__support/CMakeLists.txt +++ b/libc/src/__support/CMakeLists.txt @@ -27,12 +27,19 @@ ctype_utils.h ) +add_header_library( + error_and + HDRS + error_and.h +) + add_header_library( str_to_integer HDRS str_to_integer.h DEPENDS .ctype_utils + .error_and libc.include.errno libc.src.errno.errno libc.src.__support.CPP.limits diff --git a/libc/src/__support/error_and.h b/libc/src/__support/error_and.h new file mode 100644 --- /dev/null +++ b/libc/src/__support/error_and.h @@ -0,0 +1,40 @@ +//===-- A data structure returning a value and an error ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SUPPORT_ERROR_AND_H +#define LLVM_LIBC_SUPPORT_ERROR_AND_H + +namespace __llvm_libc { + +// The ErrorAnd class is used for internal error handling. Some functions may +// return a value, but also intend to set errno. Setting errno may not be +// correct under certain circumstances, it may be better to return an error and +// let the caller resolve it. An example would be str_to_integer, which is used +// for the strtol functions. Those functions set errno to ERANGE when a number +// gets out of range, but still return a value. Printf also uses str_to_integer, +// and should not continue if an out of range number has been found during +// string parsing. By returning an error we avoid interacting with the global +// errno, which may help performance. Additionally, optimization passes may be +// able to remove the error code if the result is discarded. +template class ErrorAnd { + T return_val; + int error_val; + +public: + constexpr ErrorAnd(T val) : return_val(val), error_val(0) {} + constexpr ErrorAnd(T val, int error) : return_val(val), error_val(error) {} + + constexpr bool has_error() { return error_val != 0; } + constexpr int get_error() { return error_val; } + constexpr T get_value() { return return_val; } + + constexpr operator T() { return return_val; } +}; +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SUPPORT_ERROR_AND_H diff --git a/libc/src/__support/str_to_integer.h b/libc/src/__support/str_to_integer.h --- a/libc/src/__support/str_to_integer.h +++ b/libc/src/__support/str_to_integer.h @@ -11,6 +11,7 @@ #include "src/__support/CPP/limits.h" #include "src/__support/ctype_utils.h" +#include "src/__support/error_and.h" #include #include @@ -67,15 +68,16 @@ // convert to. This function is used as the backend for all of the string to int // functions. template -static inline T strtointeger(const char *__restrict src, - char **__restrict str_end, int base) { +static inline ErrorAnd strtointeger(const char *__restrict src, + char **__restrict str_end, int base) { unsigned long long result = 0; bool is_number = false; const char *original_src = src; + int error_val = 0; if (base < 0 || base == 1 || base > 36) { - errno = EINVAL; - return 0; + error_val = EINVAL; + return {0, error_val}; } src = first_non_whitespace(src); @@ -113,19 +115,19 @@ // the result cannot change, but we still need to advance src to the end of // the number. if (result == abs_max) { - errno = ERANGE; + error_val = ERANGE; continue; } if (result > abs_max_div_by_base) { result = abs_max; - errno = ERANGE; + error_val = ERANGE; } else { result = result * base; } if (result > abs_max - cur_digit) { result = abs_max; - errno = ERANGE; + error_val = ERANGE; } else { result = result + cur_digit; } @@ -136,12 +138,23 @@ if (result == abs_max) { if (is_positive || IS_UNSIGNED) - return cpp::numeric_limits::max(); + return {cpp::numeric_limits::max(), error_val}; else // T is signed and there is a negative overflow - return cpp::numeric_limits::min(); + return {cpp::numeric_limits::min(), error_val}; } - return is_positive ? static_cast(result) : -static_cast(result); + return {is_positive ? static_cast(result) : -static_cast(result), + error_val}; +} + +template +static inline T strtointeger_errno(const char *__restrict src, + char **__restrict str_end, int base) { + ErrorAnd result = strtointeger(src, str_end, base); + if (result.has_error()) { + errno = result.get_error(); + } + return result; } } // namespace internal diff --git a/libc/src/inttypes/strtoimax.cpp b/libc/src/inttypes/strtoimax.cpp --- a/libc/src/inttypes/strtoimax.cpp +++ b/libc/src/inttypes/strtoimax.cpp @@ -15,7 +15,7 @@ LLVM_LIBC_FUNCTION(intmax_t, strtoimax, (const char *__restrict str, char **__restrict str_end, int base)) { - return internal::strtointeger(str, str_end, base); + return internal::strtointeger_errno(str, str_end, base); } } // namespace __llvm_libc diff --git a/libc/src/inttypes/strtoumax.cpp b/libc/src/inttypes/strtoumax.cpp --- a/libc/src/inttypes/strtoumax.cpp +++ b/libc/src/inttypes/strtoumax.cpp @@ -15,7 +15,7 @@ LLVM_LIBC_FUNCTION(uintmax_t, strtoumax, (const char *__restrict str, char **__restrict str_end, int base)) { - return internal::strtointeger(str, str_end, base); + return internal::strtointeger_errno(str, str_end, base); } } // namespace __llvm_libc diff --git a/libc/src/stdlib/atoi.cpp b/libc/src/stdlib/atoi.cpp --- a/libc/src/stdlib/atoi.cpp +++ b/libc/src/stdlib/atoi.cpp @@ -13,7 +13,7 @@ namespace __llvm_libc { LLVM_LIBC_FUNCTION(int, atoi, (const char *str)) { - return internal::strtointeger(str, nullptr, 10); + return internal::strtointeger_errno(str, nullptr, 10); } } // namespace __llvm_libc diff --git a/libc/src/stdlib/atol.cpp b/libc/src/stdlib/atol.cpp --- a/libc/src/stdlib/atol.cpp +++ b/libc/src/stdlib/atol.cpp @@ -13,7 +13,7 @@ namespace __llvm_libc { LLVM_LIBC_FUNCTION(long, atol, (const char *str)) { - return internal::strtointeger(str, nullptr, 10); + return internal::strtointeger_errno(str, nullptr, 10); } } // namespace __llvm_libc diff --git a/libc/src/stdlib/atoll.cpp b/libc/src/stdlib/atoll.cpp --- a/libc/src/stdlib/atoll.cpp +++ b/libc/src/stdlib/atoll.cpp @@ -13,7 +13,7 @@ namespace __llvm_libc { LLVM_LIBC_FUNCTION(long long, atoll, (const char *str)) { - return internal::strtointeger(str, nullptr, 10); + return internal::strtointeger_errno(str, nullptr, 10); } } // namespace __llvm_libc diff --git a/libc/src/stdlib/strtol.cpp b/libc/src/stdlib/strtol.cpp --- a/libc/src/stdlib/strtol.cpp +++ b/libc/src/stdlib/strtol.cpp @@ -15,7 +15,7 @@ LLVM_LIBC_FUNCTION(long, strtol, (const char *__restrict str, char **__restrict str_end, int base)) { - return internal::strtointeger(str, str_end, base); + return internal::strtointeger_errno(str, str_end, base); } } // namespace __llvm_libc diff --git a/libc/src/stdlib/strtoll.cpp b/libc/src/stdlib/strtoll.cpp --- a/libc/src/stdlib/strtoll.cpp +++ b/libc/src/stdlib/strtoll.cpp @@ -15,7 +15,7 @@ LLVM_LIBC_FUNCTION(long long, strtoll, (const char *__restrict str, char **__restrict str_end, int base)) { - return internal::strtointeger(str, str_end, base); + return internal::strtointeger_errno(str, str_end, base); } } // namespace __llvm_libc diff --git a/libc/src/stdlib/strtoul.cpp b/libc/src/stdlib/strtoul.cpp --- a/libc/src/stdlib/strtoul.cpp +++ b/libc/src/stdlib/strtoul.cpp @@ -15,7 +15,7 @@ LLVM_LIBC_FUNCTION(unsigned long, strtoul, (const char *__restrict str, char **__restrict str_end, int base)) { - return internal::strtointeger(str, str_end, base); + return internal::strtointeger_errno(str, str_end, base); } } // namespace __llvm_libc diff --git a/libc/src/stdlib/strtoull.cpp b/libc/src/stdlib/strtoull.cpp --- a/libc/src/stdlib/strtoull.cpp +++ b/libc/src/stdlib/strtoull.cpp @@ -15,7 +15,7 @@ LLVM_LIBC_FUNCTION(unsigned long long, strtoull, (const char *__restrict str, char **__restrict str_end, int base)) { - return internal::strtointeger(str, str_end, base); + return internal::strtointeger_errno(str, str_end, base); } } // namespace __llvm_libc diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel --- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel @@ -139,6 +139,14 @@ ], ) +cc_library( + name = "__support_error_and", + hdrs = ["src/__support/error_and.h"], + deps = [ + ":libc_root", + ], +) + cc_library( name = "__support_integer_operations", hdrs = ["src/__support/integer_operations.h"], @@ -155,6 +163,7 @@ hdrs = ["src/__support/str_to_integer.h"], deps = [ ":__support_cpp_limits", + ":__support_error_and", ":__support_ctype_utils", ], )