[libc-commits] [libc] [libc] Implement towupper and towlower entrypoints (PR #198659)
Michael Jones via libc-commits
libc-commits at lists.llvm.org
Tue May 19 15:00:18 PDT 2026
https://github.com/michaelrj-google created https://github.com/llvm/llvm-project/pull/198659
Following up on #187670 to add public entrypoints for the wctype
conversion functions.
Assisted-by: Automated tooling, human reviewed.
>From 9a73382ea10e8ec4bc01b2cb6db0116566a558d9 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Tue, 19 May 2026 21:53:06 +0000
Subject: [PATCH] [libc] Implement towupper and towlower entrypoints
Following up on #187670 to add public entrypoints for the wctype
conversion functions.
Assisted-by: Automated tooling, human reviewed.
---
libc/config/linux/x86_64/entrypoints.txt | 2 +
libc/include/wctype.yaml | 12 ++++
libc/src/wctype/CMakeLists.txt | 22 ++++++++
libc/src/wctype/towlower.cpp | 28 ++++++++++
libc/src/wctype/towlower.h | 26 +++++++++
libc/src/wctype/towupper.cpp | 28 ++++++++++
libc/src/wctype/towupper.h | 26 +++++++++
libc/test/src/wctype/CMakeLists.txt | 24 +++++++-
libc/test/src/wctype/towlower_test.cpp | 70 ++++++++++++++++++++++++
libc/test/src/wctype/towupper_test.cpp | 70 ++++++++++++++++++++++++
10 files changed, 307 insertions(+), 1 deletion(-)
create mode 100644 libc/src/wctype/towlower.cpp
create mode 100644 libc/src/wctype/towlower.h
create mode 100644 libc/src/wctype/towupper.cpp
create mode 100644 libc/src/wctype/towupper.h
create mode 100644 libc/test/src/wctype/towlower_test.cpp
create mode 100644 libc/test/src/wctype/towupper_test.cpp
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index eda0426ff2578..adfe5b6374907 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -479,6 +479,8 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.wctype.iswprint
libc.src.wctype.iswctype
libc.src.wctype.wctype
+ libc.src.wctype.towlower
+ libc.src.wctype.towupper
# sys/uio.h entrypoints
libc.src.sys.uio.writev
diff --git a/libc/include/wctype.yaml b/libc/include/wctype.yaml
index a5163ecc2d3f2..ac350c1f9956a 100644
--- a/libc/include/wctype.yaml
+++ b/libc/include/wctype.yaml
@@ -88,3 +88,15 @@ functions:
return_type: wctype_t
arguments:
- type: const char*
+ - name: towlower
+ standards:
+ - stdc
+ return_type: wint_t
+ arguments:
+ - type: wint_t
+ - name: towupper
+ standards:
+ - stdc
+ return_type: wint_t
+ arguments:
+ - type: wint_t
diff --git a/libc/src/wctype/CMakeLists.txt b/libc/src/wctype/CMakeLists.txt
index 84c72c7e6b7ae..234676e20739e 100644
--- a/libc/src/wctype/CMakeLists.txt
+++ b/libc/src/wctype/CMakeLists.txt
@@ -153,3 +153,25 @@ add_entrypoint_object(
libc.src.__support.wctype_impl
libc.hdr.types.wctype_t
)
+
+add_entrypoint_object(
+ towlower
+ SRCS
+ towlower.cpp
+ HDRS
+ towlower.h
+ DEPENDS
+ libc.src.__support.wctype_utils
+ libc.hdr.types.wint_t
+)
+
+add_entrypoint_object(
+ towupper
+ SRCS
+ towupper.cpp
+ HDRS
+ towupper.h
+ DEPENDS
+ libc.src.__support.wctype_utils
+ libc.hdr.types.wint_t
+)
diff --git a/libc/src/wctype/towlower.cpp b/libc/src/wctype/towlower.cpp
new file mode 100644
index 0000000000000..8c0667bfee426
--- /dev/null
+++ b/libc/src/wctype/towlower.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of towlower.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/wctype/towlower.h"
+#include "hdr/types/wint_t.h"
+#include "src/__support/common.h"
+#include "src/__support/wctype_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(wint_t, towlower, (wint_t c)) {
+ if (c == static_cast<wint_t>(static_cast<wchar_t>(c))) {
+ return static_cast<wint_t>(internal::tolower(static_cast<wchar_t>(c)));
+ }
+ return c;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/wctype/towlower.h b/libc/src/wctype/towlower.h
new file mode 100644
index 0000000000000..01ed7cf652680
--- /dev/null
+++ b/libc/src/wctype/towlower.h
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation header for towlower.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_WCTYPE_TOWLOWER_H
+#define LLVM_LIBC_SRC_WCTYPE_TOWLOWER_H
+
+#include "hdr/types/wint_t.h"
+#include "src/__support/common.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+wint_t towlower(wint_t c);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_WCTYPE_TOWLOWER_H
diff --git a/libc/src/wctype/towupper.cpp b/libc/src/wctype/towupper.cpp
new file mode 100644
index 0000000000000..9d3ebc747f81c
--- /dev/null
+++ b/libc/src/wctype/towupper.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of towupper.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/wctype/towupper.h"
+#include "hdr/types/wint_t.h"
+#include "src/__support/common.h"
+#include "src/__support/wctype_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(wint_t, towupper, (wint_t c)) {
+ if (c == static_cast<wint_t>(static_cast<wchar_t>(c))) {
+ return static_cast<wint_t>(internal::toupper(static_cast<wchar_t>(c)));
+ }
+ return c;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/wctype/towupper.h b/libc/src/wctype/towupper.h
new file mode 100644
index 0000000000000..9a146c081c0cf
--- /dev/null
+++ b/libc/src/wctype/towupper.h
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation header for towupper.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_WCTYPE_TOWUPPER_H
+#define LLVM_LIBC_SRC_WCTYPE_TOWUPPER_H
+
+#include "hdr/types/wint_t.h"
+#include "src/__support/common.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+wint_t towupper(wint_t c);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_WCTYPE_TOWUPPER_H
diff --git a/libc/test/src/wctype/CMakeLists.txt b/libc/test/src/wctype/CMakeLists.txt
index f7cdcc77482e4..79b5993e00180 100644
--- a/libc/test/src/wctype/CMakeLists.txt
+++ b/libc/test/src/wctype/CMakeLists.txt
@@ -42,7 +42,7 @@ add_libc_test(
libc.src.wctype.iswgraph
)
-add_libc_test(
+add_libc_test(
iswlower_test
SUITE
libc_wctype_unittests
@@ -142,3 +142,25 @@ add_libc_test(
DEPENDS
libc.src.wctype.wctype
)
+
+add_libc_test(
+ towlower_test
+ SUITE
+ libc_wctype_unittests
+ SRCS
+ towlower_test.cpp
+ DEPENDS
+ libc.src.wctype.towlower
+ libc.src.__support.wctype_utils
+)
+
+add_libc_test(
+ towupper_test
+ SUITE
+ libc_wctype_unittests
+ SRCS
+ towupper_test.cpp
+ DEPENDS
+ libc.src.wctype.towupper
+ libc.src.__support.wctype_utils
+)
diff --git a/libc/test/src/wctype/towlower_test.cpp b/libc/test/src/wctype/towlower_test.cpp
new file mode 100644
index 0000000000000..68448e17aac26
--- /dev/null
+++ b/libc/test/src/wctype/towlower_test.cpp
@@ -0,0 +1,70 @@
+//===-- Unittests for towlower --------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "hdr/wchar_macros.h" // for WEOF
+#include "src/__support/wctype_utils.h"
+#include "src/wctype/towlower.h"
+#include "test/UnitTest/Test.h"
+
+TEST(LlvmLibcTowLower, SimpleTest) {
+ // ASCII Conversions
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(L'A'), static_cast<wint_t>(L'a'));
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(L'Z'), static_cast<wint_t>(L'z'));
+
+ // ASCII Unchanged
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(L'a'), static_cast<wint_t>(L'a'));
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(L'z'), static_cast<wint_t>(L'z'));
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(L'1'), static_cast<wint_t>(L'1'));
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(L'\0'), static_cast<wint_t>(L'\0'));
+
+ // WEOF Test
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(WEOF), static_cast<wint_t>(WEOF));
+
+ // Boundary / Out-of-domain Tests (should return unchanged)
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(0xFFFF), static_cast<wint_t>(0xFFFF));
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(0x110000), static_cast<wint_t>(0x110000));
+
+ // Non-ASCII tests
+#if LIBC_CONF_WCTYPE_MODE == 1 // UTF8 Mode
+ // Greek conversions
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(L'Α'), static_cast<wint_t>(L'α')); // alpha
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(L'Ω'), static_cast<wint_t>(L'ω')); // omega
+
+ // Cyrillic conversions
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(L'А'), static_cast<wint_t>(L'а')); // A
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(L'Я'), static_cast<wint_t>(L'я')); // Ya
+
+ // Accented Latin
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(L'É'), static_cast<wint_t>(L'é'));
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(L'Ü'), static_cast<wint_t>(L'ü'));
+
+#if WCHAR_MAX > 0xFFFF
+ // Deseret (Unicode Plane 1) conversions
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(L'\U00010400'),
+ static_cast<wint_t>(L'\U00010428'));
+#endif
+
+ // Already lowercase / unchanged
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(L'α'), static_cast<wint_t>(L'α'));
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(L'а'), static_cast<wint_t>(L'а'));
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(L'é'), static_cast<wint_t>(L'é'));
+#else // ASCII Mode
+ // Non-ASCII should remain unchanged
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(L'Α'), static_cast<wint_t>(L'Α'));
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(L'Ω'), static_cast<wint_t>(L'Ω'));
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(L'А'), static_cast<wint_t>(L'А'));
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(L'Я'), static_cast<wint_t>(L'Я'));
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(L'É'), static_cast<wint_t>(L'É'));
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(L'Ü'), static_cast<wint_t>(L'Ü'));
+
+#if WCHAR_MAX > 0xFFFF
+ EXPECT_EQ(LIBC_NAMESPACE::towlower(L'\U00010400'),
+ static_cast<wint_t>(L'\U00010400'));
+#endif
+#endif
+}
diff --git a/libc/test/src/wctype/towupper_test.cpp b/libc/test/src/wctype/towupper_test.cpp
new file mode 100644
index 0000000000000..e85969a6fa742
--- /dev/null
+++ b/libc/test/src/wctype/towupper_test.cpp
@@ -0,0 +1,70 @@
+//===-- Unittests for towupper --------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "hdr/wchar_macros.h" // for WEOF
+#include "src/__support/wctype_utils.h"
+#include "src/wctype/towupper.h"
+#include "test/UnitTest/Test.h"
+
+TEST(LlvmLibcTowUpper, SimpleTest) {
+ // ASCII Conversions
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(L'a'), static_cast<wint_t>(L'A'));
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(L'z'), static_cast<wint_t>(L'Z'));
+
+ // ASCII Unchanged
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(L'A'), static_cast<wint_t>(L'A'));
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(L'Z'), static_cast<wint_t>(L'Z'));
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(L'1'), static_cast<wint_t>(L'1'));
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(L'\0'), static_cast<wint_t>(L'\0'));
+
+ // WEOF Test
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(WEOF), static_cast<wint_t>(WEOF));
+
+ // Boundary / Out-of-domain Tests (should return unchanged)
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(0xFFFF), static_cast<wint_t>(0xFFFF));
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(0x110000), static_cast<wint_t>(0x110000));
+
+ // Non-ASCII tests
+#if LIBC_CONF_WCTYPE_MODE == 1 // UTF8 Mode
+ // Greek conversions
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(L'α'), static_cast<wint_t>(L'Α')); // alpha
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(L'ω'), static_cast<wint_t>(L'Ω')); // omega
+
+ // Cyrillic conversions
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(L'а'), static_cast<wint_t>(L'А')); // A
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(L'я'), static_cast<wint_t>(L'Я')); // Ya
+
+ // Accented Latin
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(L'é'), static_cast<wint_t>(L'É'));
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(L'ü'), static_cast<wint_t>(L'Ü'));
+
+#if WCHAR_MAX > 0xFFFF
+ // Deseret (Unicode Plane 1) conversions
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(L'\U00010428'),
+ static_cast<wint_t>(L'\U00010400'));
+#endif
+
+ // Already uppercase / unchanged
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(L'Α'), static_cast<wint_t>(L'Α'));
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(L'А'), static_cast<wint_t>(L'А'));
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(L'É'), static_cast<wint_t>(L'É'));
+#else // ASCII Mode
+ // Non-ASCII should remain unchanged
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(L'α'), static_cast<wint_t>(L'α'));
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(L'ω'), static_cast<wint_t>(L'ω'));
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(L'а'), static_cast<wint_t>(L'а'));
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(L'я'), static_cast<wint_t>(L'я'));
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(L'é'), static_cast<wint_t>(L'é'));
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(L'ü'), static_cast<wint_t>(L'ü'));
+
+#if WCHAR_MAX > 0xFFFF
+ EXPECT_EQ(LIBC_NAMESPACE::towupper(L'\U00010428'),
+ static_cast<wint_t>(L'\U00010428'));
+#endif
+#endif
+}
More information about the libc-commits
mailing list