[libc-commits] [libc] 95da135 - Revert "[libc] Support %lc in printf (#169983)"

Joseph Huber via libc-commits libc-commits at lists.llvm.org
Tue Jan 13 18:01:44 PST 2026


Author: Joseph Huber
Date: 2026-01-13T20:01:29-06:00
New Revision: 95da1354d03bf66594d0bd1763b9c9c5fbcf8843

URL: https://github.com/llvm/llvm-project/commit/95da1354d03bf66594d0bd1763b9c9c5fbcf8843
DIFF: https://github.com/llvm/llvm-project/commit/95da1354d03bf66594d0bd1763b9c9c5fbcf8843.diff

LOG: Revert "[libc] Support %lc in printf (#169983)"

This reverts commit 1327c50ef199b481f5326cf6ca3710fc111b70b1.

The printf headers are intended to be header-only, this introduces
external symbol dependencies.

Added: 
    

Modified: 
    libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
    libc/config/config.json
    libc/docs/configure.rst
    libc/docs/dev/printf_behavior.rst
    libc/src/stdio/printf_core/CMakeLists.txt
    libc/src/stdio/printf_core/char_converter.h
    libc/src/stdio/printf_core/core_structs.h
    libc/src/stdio/printf_core/linux/error_mapper.h
    libc/src/stdio/printf_core/parser.h
    libc/test/src/stdio/CMakeLists.txt
    libc/test/src/stdio/sprintf_test.cpp

Removed: 
    libc/config/windows/config.json


################################################################################
diff  --git a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
index 638cfc122d7de..a719f20d532e0 100644
--- a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
+++ b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
@@ -135,10 +135,6 @@ function(_get_compile_options_from_config output_var)
     endif()
   endif()
 
-  if (LIBC_CONF_PRINTF_DISABLE_WIDE)
-    list(APPEND config_options "-DLIBC_COPT_PRINTF_DISABLE_WIDE")
-  endif()
-
   set(${output_var} ${config_options} PARENT_SCOPE)
 endfunction(_get_compile_options_from_config)
 

diff  --git a/libc/config/config.json b/libc/config/config.json
index 296d2e539c23d..f981c433b2c7c 100644
--- a/libc/config/config.json
+++ b/libc/config/config.json
@@ -52,10 +52,6 @@
     "LIBC_CONF_PRINTF_RUNTIME_DISPATCH": {
       "value": true,
       "doc": "Use dynamic dispatch for the output mechanism to reduce code size."
-    },
-    "LIBC_CONF_PRINTF_DISABLE_WIDE": {
-      "value": false,
-      "doc": "Disable handling wide characters for printf and friends."
     }
   },
   "scanf": {

diff  --git a/libc/config/windows/config.json b/libc/config/windows/config.json
deleted file mode 100644
index 6bb09db9d1aab..0000000000000
--- a/libc/config/windows/config.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-    "printf": {
-      "LIBC_CONF_PRINTF_DISABLE_WIDE": {
-        "value": "true",
-        "doc": "Disable handling wide characters for printf and friends."
-      }
-    }
-}

diff  --git a/libc/docs/configure.rst b/libc/docs/configure.rst
index 8f5074ec23475..81888bb07153c 100644
--- a/libc/docs/configure.rst
+++ b/libc/docs/configure.rst
@@ -49,7 +49,6 @@ to learn about the defaults for your platform and target.
     - ``LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_FLOAT320``: Use an alternative printf float implementation based on 320-bit floats
     - ``LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE``: Use large table for better printf long double performance.
     - ``LIBC_CONF_PRINTF_RUNTIME_DISPATCH``: Use dynamic dispatch for the output mechanism to reduce code size.
-    - ``LIBC_CONF_PRINTF_DISABLE_WIDE``: Disable handling of %lc and %ls on unsupported platforms.
 * **"pthread" options**
     - ``LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT``: Default number of spins before blocking if a mutex is in contention (default to 100).
     - ``LIBC_CONF_RWLOCK_DEFAULT_SPIN_COUNT``: Default number of spins before blocking if a rwlock is in contention (default to 100).

diff  --git a/libc/docs/dev/printf_behavior.rst b/libc/docs/dev/printf_behavior.rst
index ba0578aee3fd8..01ab128a1f238 100644
--- a/libc/docs/dev/printf_behavior.rst
+++ b/libc/docs/dev/printf_behavior.rst
@@ -71,13 +71,6 @@ conversions (%r, %k); any fixed point number conversion will be treated as
 invalid. This reduces code size. This has no effect if the current compiler does
 not support fixed point numbers.
 
-LIBC_COPT_PRINTF_DISABLE_WIDE
---------------------------------
-When set, this flag disables support for wide characters (%lc and %ls). Any
-conversions will be ignored. This reduces code size. This will be set by default
-on windows platforms as current printf implementation does not support UTF-16 wide
-characters.
-
 LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
 ----------------------------------
 When set, this flag disables the nullptr checks in %n and %s.

diff  --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt
index ae93cc754299b..624129b2b36e7 100644
--- a/libc/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/CMakeLists.txt
@@ -43,22 +43,6 @@ if(NOT TARGET ${target_error_mapper})
     set(target_error_mapper libc.src.stdio.printf_core.generic.error_mapper)
 endif()
 
-if(LIBC_CONF_PRINTF_DISABLE_WIDE)
-  set(wchar_deps "")
-  set(parser_wchar_deps "")
-else()
-  set(wchar_deps
-    libc.hdr.types.wchar_t
-    libc.hdr.types.wint_t
-    libc.hdr.wchar_macros
-    libc.src.__support.wchar.wcrtomb
-    libc.src.__support.wchar.mbstate
-  )
-  set(parser_wchar_deps
-    libc.hdr.types.wint_t
-  )
-endif()
-
 add_header_library(
   printf_config
   HDRS
@@ -92,7 +76,6 @@ add_header_library(
     libc.src.__support.CPP.string_view
     libc.src.__support.CPP.type_traits
     libc.src.__support.common
-    ${parser_wchar_deps}
 )
 
 add_header_library(
@@ -128,7 +111,6 @@ add_header_library(
     .printf_config
     .writer
     libc.include.inttypes
-    libc.hdr.limits_macros
     libc.src.__support.big_int
     libc.src.__support.common
     libc.src.__support.CPP.limits
@@ -143,7 +125,6 @@ add_header_library(
     libc.src.__support.uint128
     libc.src.__support.StringUtil.error_to_string
     libc.src.string.memory_utils.inline_memcpy
-    ${wchar_deps}
 )
 
 add_header_library(

diff  --git a/libc/src/stdio/printf_core/char_converter.h b/libc/src/stdio/printf_core/char_converter.h
index e4792c3b23b9f..fd2eb2553887a 100644
--- a/libc/src/stdio/printf_core/char_converter.h
+++ b/libc/src/stdio/printf_core/char_converter.h
@@ -1,4 +1,4 @@
-//===-- Character Converter for printf --------------------------*- C++ -*-===//
+//===-- String Converter for printf -----------------------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -9,15 +9,6 @@
 #ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_CHAR_CONVERTER_H
 #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_CHAR_CONVERTER_H
 
-#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
-#include "hdr/types/wchar_t.h"
-#include "hdr/types/wint_t.h"
-#include "hdr/wchar_macros.h"
-#include "src/__support/wchar/mbstate.h"
-#include "src/__support/wchar/wcrtomb.h"
-#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
-
-#include "hdr/limits_macros.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/converter_utils.h"
 #include "src/stdio/printf_core/core_structs.h"
@@ -29,41 +20,12 @@ namespace printf_core {
 template <WriteMode write_mode>
 LIBC_INLINE int convert_char(Writer<write_mode> *writer,
                              const FormatSection &to_conv) {
+  char c = static_cast<char>(to_conv.conv_val_raw);
 
-  char buffer[MB_LEN_MAX];
-  size_t write_size = 0;
-
-  if (to_conv.length_modifier == LengthModifier::l) {
-#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
-    wint_t wi = static_cast<wint_t>(to_conv.conv_val_raw);
-
-    if (wi == WEOF) {
-      return ILLEGAL_WIDE_CHAR;
-    }
-
-    internal::mbstate mbstate;
-    wchar_t wc = static_cast<wchar_t>(wi);
-    auto ret = internal::wcrtomb(buffer, wc, &mbstate);
-
-    if (!ret.has_value()) {
-      return MB_CONVERSION_ERROR;
-    }
-
-    write_size = ret.value();
-#else
-    // If wide characters are disabled, treat the 'l' modifier as a no-op.
-    buffer[0] = static_cast<char>(to_conv.conv_val_raw);
-    write_size = 1;
-
-#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
-  } else {
-    buffer[0] = static_cast<char>(to_conv.conv_val_raw);
-    write_size = 1;
-  }
+  constexpr int STRING_LEN = 1;
 
-  size_t padding_spaces = to_conv.min_width > static_cast<int>(write_size)
-                              ? to_conv.min_width - static_cast<int>(write_size)
-                              : 0;
+  size_t padding_spaces =
+      to_conv.min_width > STRING_LEN ? to_conv.min_width - STRING_LEN : 0;
 
   // If the padding is on the left side, write the spaces first.
   if (padding_spaces > 0 &&
@@ -71,7 +33,7 @@ LIBC_INLINE int convert_char(Writer<write_mode> *writer,
     RET_IF_RESULT_NEGATIVE(writer->write(' ', padding_spaces));
   }
 
-  RET_IF_RESULT_NEGATIVE(writer->write({buffer, write_size}));
+  RET_IF_RESULT_NEGATIVE(writer->write(c));
 
   // If the padding is on the right side, write the spaces last.
   if (padding_spaces > 0 &&

diff  --git a/libc/src/stdio/printf_core/core_structs.h b/libc/src/stdio/printf_core/core_structs.h
index d93fa962db90e..0d41f2244d8da 100644
--- a/libc/src/stdio/printf_core/core_structs.h
+++ b/libc/src/stdio/printf_core/core_structs.h
@@ -142,8 +142,6 @@ constexpr int INT_CONVERSION_ERROR = -1004;
 constexpr int FIXED_POINT_CONVERSION_ERROR = -1005;
 constexpr int ALLOCATION_ERROR = -1006;
 constexpr int OVERFLOW_ERROR = -1007;
-constexpr int ILLEGAL_WIDE_CHAR = -1008;
-constexpr int MB_CONVERSION_ERROR = -1009;
 
 } // namespace printf_core
 } // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/stdio/printf_core/linux/error_mapper.h b/libc/src/stdio/printf_core/linux/error_mapper.h
index 3449f12593469..3c2fe663072d0 100644
--- a/libc/src/stdio/printf_core/linux/error_mapper.h
+++ b/libc/src/stdio/printf_core/linux/error_mapper.h
@@ -40,9 +40,6 @@ LIBC_INLINE static int internal_error_to_errno(int internal_error) {
     return ENOMEM;
   case OVERFLOW_ERROR:
     return EOVERFLOW;
-  case ILLEGAL_WIDE_CHAR:
-  case MB_CONVERSION_ERROR:
-    return EILSEQ;
   default:
     LIBC_ASSERT(
         false &&

diff  --git a/libc/src/stdio/printf_core/parser.h b/libc/src/stdio/printf_core/parser.h
index a3b62991bcec9..cef9b1ae58fa0 100644
--- a/libc/src/stdio/printf_core/parser.h
+++ b/libc/src/stdio/printf_core/parser.h
@@ -27,9 +27,6 @@
 #ifndef LIBC_COPT_PRINTF_DISABLE_STRERROR
 #include "src/__support/libc_errno.h"
 #endif // LIBC_COPT_PRINTF_DISABLE_STRERROR
-#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
-#include "hdr/types/wint_t.h"
-#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
 
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
@@ -76,9 +73,9 @@ template <typename ArgProvider> class Parser {
   ArgProvider args_cur;
 
 #ifndef LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
-  // args_start stores the start of the va_args, which helps in getting the
-  // number of arguments that have already been passed. args_index is tracked
-  // so that we know which argument args_cur is on.
+  // args_start stores the start of the va_args, which is allows getting the
+  // value of arguments that have already been passed. args_index is tracked so
+  // that we know which argument args_cur is on.
   ArgProvider args_start;
   size_t args_index = 1;
 
@@ -176,17 +173,7 @@ template <typename ArgProvider> class Parser {
         section.has_conv = true;
         break;
       case ('c'):
-        if (section.length_modifier == LengthModifier::l) {
-#ifdef LIBC_COPT_PRINTF_DISABLE_WIDE
-          using WideCharArgType = int;
-#else
-          using WideCharArgType = wint_t;
-#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
-          WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, WideCharArgType,
-                                 conv_index);
-        } else {
-          WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, int, conv_index);
-        }
+        WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, int, conv_index);
         break;
       case ('d'):
       case ('i'):
@@ -587,16 +574,7 @@ template <typename ArgProvider> class Parser {
           conv_size = type_desc_from_type<void>();
           break;
         case ('c'):
-          if (lm == LengthModifier::l) {
-#ifdef LIBC_COPT_PRINTF_DISABLE_WIDE
-            using WideCharArgType = int;
-#else
-            using WideCharArgType = wint_t;
-#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
-            conv_size = type_desc_from_type<WideCharArgType>();
-          } else {
-            conv_size = type_desc_from_type<int>();
-          }
+          conv_size = type_desc_from_type<int>();
           break;
         case ('d'):
         case ('i'):

diff  --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt
index fde2023caaac1..a39428fb8d16c 100644
--- a/libc/test/src/stdio/CMakeLists.txt
+++ b/libc/test/src/stdio/CMakeLists.txt
@@ -137,15 +137,6 @@ if(LIBC_CONF_PRINTF_DISABLE_STRERROR)
   list(APPEND sprintf_test_copts "-DLIBC_COPT_PRINTF_DISABLE_STRERROR")
 endif()
 
-if(LIBC_CONF_PRINTF_DISABLE_WIDE)
-  set(wchar_deps "")
-else()
-  set(wchar_deps
-    libc.hdr.types.wint_t
-    libc.hdr.wchar_macros
-  )
-endif()
-
 add_fp_unittest(
   sprintf_test
   UNIT_TEST_ONLY
@@ -157,7 +148,6 @@ add_fp_unittest(
     libc.src.stdio.sprintf
     libc.src.__support.FPUtil.fp_bits
     libc.include.inttypes
-    ${wchar_deps}
   COMPILE_OPTIONS
     ${sprintf_test_copts}
 )

diff  --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp
index 78186abb9966e..689a38a49f13c 100644
--- a/libc/test/src/stdio/sprintf_test.cpp
+++ b/libc/test/src/stdio/sprintf_test.cpp
@@ -9,13 +9,8 @@
 #include "src/__support/macros/config.h"
 #include "src/stdio/sprintf.h"
 
-#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
-#include "hdr/types/wint_t.h"
-#include "hdr/wchar_macros.h"
-#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
-
 #include "src/__support/FPUtil/FPBits.h"
-#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "src/__support/libc_errno.h"
 #include "test/UnitTest/RoundingModeUtils.h"
 #include "test/UnitTest/Test.h"
 #include <inttypes.h>
@@ -3492,94 +3487,3 @@ TEST(LlvmLibcSPrintfTest, IndexModeParsing) {
                    "why would u do this, this is such   a pain. %");
 }
 #endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
-
-#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
-TEST(LlvmLibcSprintfTest, WideCharConversion) {
-  char buff[16];
-  int written;
-
-  // 1 byte UTF-8 character.
-  written = LIBC_NAMESPACE::sprintf(buff, "%lc", L'A');
-  EXPECT_EQ(written, 1);
-  ASSERT_STREQ_LEN(written, buff, "A");
-
-  // 1 byte UTF-8 character left justified.
-  written = LIBC_NAMESPACE::sprintf(buff, "%-4lc", L'A');
-  EXPECT_EQ(written, 4);
-  ASSERT_STREQ_LEN(written, buff, "A   ");
-
-  // 1 byte UTF-8 character right justified.
-  written = LIBC_NAMESPACE::sprintf(buff, "%4lc", L'A');
-  EXPECT_EQ(written, 4);
-  ASSERT_STREQ_LEN(written, buff, "   A");
-
-  // 2 byte UTF-8 character.
-  written = LIBC_NAMESPACE::sprintf(buff, "%lc", L'¢');
-  EXPECT_EQ(written, 2);
-  ASSERT_STREQ_LEN(written, buff, "¢");
-
-  // 2 byte UTF-8 character left justified.
-  written = LIBC_NAMESPACE::sprintf(buff, "%-4lc", L'¢');
-  EXPECT_EQ(written, 4);
-  ASSERT_STREQ_LEN(written, buff, "¢  ");
-
-  // 2 byte UTF-8 character right justified.
-  written = LIBC_NAMESPACE::sprintf(buff, "%4lc", L'¢');
-  EXPECT_EQ(written, 4);
-  ASSERT_STREQ_LEN(written, buff, "  ¢");
-
-  // Euro sign is a 3-byte UTF-8 character.
-  written = LIBC_NAMESPACE::sprintf(buff, "%lc", L'€');
-  EXPECT_EQ(written, 3);
-  ASSERT_STREQ_LEN(written, buff, "€");
-
-  // Euro sign left justified.
-  written = LIBC_NAMESPACE::sprintf(buff, "%-4lc", L'€');
-  EXPECT_EQ(written, 4);
-  ASSERT_STREQ_LEN(written, buff, "€ ");
-
-  // Euro sign right justified.
-  written = LIBC_NAMESPACE::sprintf(buff, "%4lc", L'€');
-  EXPECT_EQ(written, 4);
-  ASSERT_STREQ_LEN(written, buff, " €");
-
-  // Euro sign right justified.
-  written = LIBC_NAMESPACE::sprintf(buff, "%+4lc", L'€');
-  EXPECT_EQ(written, 4);
-  ASSERT_STREQ_LEN(written, buff, " €");
-
-  // Grinning face emoji is a 4-byte UTF-8 character.
-  written = LIBC_NAMESPACE::sprintf(buff, "%lc", L'😀');
-  EXPECT_EQ(written, 4);
-  ASSERT_STREQ_LEN(written, buff, "😀");
-
-  // Grinning face emoji left justified.
-  written = LIBC_NAMESPACE::sprintf(buff, "%-4lc", L'😀');
-  EXPECT_EQ(written, 4);
-  ASSERT_STREQ_LEN(written, buff, "😀");
-
-  // Grinning face emoji right justified.
-  written = LIBC_NAMESPACE::sprintf(buff, "%4lc", L'😀');
-  EXPECT_EQ(written, 4);
-  ASSERT_STREQ_LEN(written, buff, "😀");
-
-  // Grinning face emoji with smaller width, left justified.
-  written = LIBC_NAMESPACE::sprintf(buff, "%-3lc", L'😀');
-  EXPECT_EQ(written, 4);
-  ASSERT_STREQ_LEN(written, buff, "😀");
-
-  // Grinning face emoji with smaller width, right justified.
-  written = LIBC_NAMESPACE::sprintf(buff, "%3lc", L'😀');
-  EXPECT_EQ(written, 4);
-  ASSERT_STREQ_LEN(written, buff, "😀");
-
-  // WEOF test.
-  EXPECT_EQ(LIBC_NAMESPACE::sprintf(buff, "%lc", WEOF), -1);
-  ASSERT_ERRNO_EQ(EILSEQ);
-
-  // Invalid wide character test
-  EXPECT_EQ(LIBC_NAMESPACE::sprintf(buff, "%lc", static_cast<wint_t>(0x12ffff)),
-            -1);
-  ASSERT_ERRNO_EQ(EILSEQ);
-}
-#endif // LIBC_COPT_PRINTF_DISABLE_WIDE


        


More information about the libc-commits mailing list