[libc-commits] [libc] [libc] Add iswctype and wctype (PR #191178)

Zile Xiong via libc-commits libc-commits at lists.llvm.org
Thu Apr 9 07:59:54 PDT 2026


https://github.com/xiongzile updated https://github.com/llvm/llvm-project/pull/191178

>From 760a5bb9e9a0968f48f2923057e1ae675287baf9 Mon Sep 17 00:00:00 2001
From: Zile Xiong <xiongzile99 at gmail.com>
Date: Thu, 9 Apr 2026 15:03:36 +0800
Subject: [PATCH] [libc] Add iswctype and wctype

Implement the iswctype and wctype functions from <wctype.h>.

- Add wctype_t type definition.
- Implement wctype to map property strings to classification descriptors.
- Implement iswctype as a dispatcher over existing wide character
  classification functions.
- Add corresponding entrypoints and unit tests.

Refs: https://github.com/llvm/llvm-project/issues/191076
---
 libc/config/baremetal/arm/entrypoints.txt   |   2 +
 libc/config/baremetal/riscv/entrypoints.txt |   2 +
 libc/config/darwin/aarch64/entrypoints.txt  |   2 +
 libc/config/linux/aarch64/entrypoints.txt   |   2 +
 libc/config/linux/arm/entrypoints.txt       |   2 +
 libc/config/linux/riscv/entrypoints.txt     |   2 +
 libc/config/linux/x86_64/entrypoints.txt    |   2 +
 libc/config/windows/entrypoints.txt         |   2 +
 libc/hdr/types/CMakeLists.txt               |   9 ++
 libc/hdr/types/wctype_t.h                   |  24 +++++
 libc/hdr/wctype_overlay.h                   |  69 ++++++++++++
 libc/include/CMakeLists.txt                 |   1 +
 libc/include/llvm-libc-types/CMakeLists.txt |   1 +
 libc/include/llvm-libc-types/wctype_t.h     |  15 +++
 libc/include/wctype.yaml                    |  14 +++
 libc/src/__support/wctype_utils.h           |  79 ++++++++++++++
 libc/src/wctype/CMakeLists.txt              |  21 ++++
 libc/src/wctype/iswctype.cpp                |  23 ++++
 libc/src/wctype/iswctype.h                  |  22 ++++
 libc/src/wctype/wctype.cpp                  |  21 ++++
 libc/src/wctype/wctype.h                    |  22 ++++
 libc/test/src/wctype/CMakeLists.txt         |  20 ++++
 libc/test/src/wctype/iswctype_test.cpp      | 110 ++++++++++++++++++++
 libc/test/src/wctype/wctype_test.cpp        |  63 +++++++++++
 24 files changed, 530 insertions(+)
 create mode 100644 libc/hdr/types/wctype_t.h
 create mode 100644 libc/hdr/wctype_overlay.h
 create mode 100644 libc/include/llvm-libc-types/wctype_t.h
 create mode 100644 libc/src/wctype/iswctype.cpp
 create mode 100644 libc/src/wctype/iswctype.h
 create mode 100644 libc/src/wctype/wctype.cpp
 create mode 100644 libc/src/wctype/wctype.h
 create mode 100644 libc/test/src/wctype/iswctype_test.cpp
 create mode 100644 libc/test/src/wctype/wctype_test.cpp

diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt
index 4cf34764916f7..a2c22ac32e3ac 100644
--- a/libc/config/baremetal/arm/entrypoints.txt
+++ b/libc/config/baremetal/arm/entrypoints.txt
@@ -313,6 +313,8 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.wctype.iswxdigit
     libc.src.wctype.iswpunct
     libc.src.wctype.iswprint
+    libc.src.wctype.iswctype
+    libc.src.wctype.wctype
     # internal entrypoints
     libc.startup.baremetal.init
     libc.startup.baremetal.fini
diff --git a/libc/config/baremetal/riscv/entrypoints.txt b/libc/config/baremetal/riscv/entrypoints.txt
index c4a11c6f87337..d5fc6258aff11 100644
--- a/libc/config/baremetal/riscv/entrypoints.txt
+++ b/libc/config/baremetal/riscv/entrypoints.txt
@@ -310,6 +310,8 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.wctype.iswxdigit
     libc.src.wctype.iswpunct
     libc.src.wctype.iswprint
+    libc.src.wctype.iswctype
+    libc.src.wctype.wctype
 
     # internal entrypoints
     libc.startup.baremetal.init
diff --git a/libc/config/darwin/aarch64/entrypoints.txt b/libc/config/darwin/aarch64/entrypoints.txt
index 0888f4b0d922b..033183427de79 100644
--- a/libc/config/darwin/aarch64/entrypoints.txt
+++ b/libc/config/darwin/aarch64/entrypoints.txt
@@ -113,6 +113,8 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.wctype.iswxdigit
     libc.src.wctype.iswpunct
     libc.src.wctype.iswprint
+    libc.src.wctype.iswctype
+    libc.src.wctype.wctype
 )
 
 if(LLVM_LIBC_FULL_BUILD)
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 567848795be49..73d1b5d307a97 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -385,6 +385,8 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.wctype.iswxdigit
     libc.src.wctype.iswpunct
     libc.src.wctype.iswprint
+    libc.src.wctype.iswctype
+    libc.src.wctype.wctype
 
     # sys/uio.h entrypoints
     libc.src.sys.uio.writev
diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt
index 31dcb31c67c7a..997263f364524 100644
--- a/libc/config/linux/arm/entrypoints.txt
+++ b/libc/config/linux/arm/entrypoints.txt
@@ -205,6 +205,8 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.wctype.iswxdigit
     libc.src.wctype.iswpunct
     libc.src.wctype.iswprint
+    libc.src.wctype.iswctype
+    libc.src.wctype.wctype
 )
 
 if(LLVM_LIBC_FULL_BUILD)
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 997c08ee7de9b..4c12be578b0e5 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -389,6 +389,8 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.wctype.iswxdigit
     libc.src.wctype.iswpunct
     libc.src.wctype.iswprint
+    libc.src.wctype.iswctype
+    libc.src.wctype.wctype
 
     # sys/uio.h entrypoints
     libc.src.sys.uio.writev
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index ea1cd80a1d0f1..9bf2248e49dd9 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -439,6 +439,8 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.wctype.iswxdigit
     libc.src.wctype.iswpunct
     libc.src.wctype.iswprint
+    libc.src.wctype.iswctype
+    libc.src.wctype.wctype
 
     # sys/uio.h entrypoints
     libc.src.sys.uio.writev
diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt
index e8080bc97c59a..7d32fab21fdb4 100644
--- a/libc/config/windows/entrypoints.txt
+++ b/libc/config/windows/entrypoints.txt
@@ -119,6 +119,8 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.wctype.iswxdigit
     libc.src.wctype.iswpunct
     libc.src.wctype.iswprint
+    libc.src.wctype.iswctype
+    libc.src.wctype.wctype
 )
 
 set(TARGET_LIBM_ENTRYPOINTS
diff --git a/libc/hdr/types/CMakeLists.txt b/libc/hdr/types/CMakeLists.txt
index 7e1a8974fa486..7395f70c0d2aa 100644
--- a/libc/hdr/types/CMakeLists.txt
+++ b/libc/hdr/types/CMakeLists.txt
@@ -443,6 +443,15 @@ add_proxy_header_library(
     libc.include.wchar
 )
 
+
+add_proxy_header_library(
+  wctype_t
+  HDRS
+    wctype_t.h
+  FULL_BUILD_DEPENDS
+    libc.include.llvm-libc-types.wctype_t
+)
+
 add_proxy_header_library(
   uid_t
   HDRS
diff --git a/libc/hdr/types/wctype_t.h b/libc/hdr/types/wctype_t.h
new file mode 100644
index 0000000000000..f8e09969227dd
--- /dev/null
+++ b/libc/hdr/types/wctype_t.h
@@ -0,0 +1,24 @@
+//===-- Definition of wctype_t.h
+//--------------------------------------------===//
+//
+// 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_HDR_TYPES_WCTYPE_T_H
+#define LLVM_LIBC_HDR_TYPES_WCTYPE_T_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-types/wctype_t.h"
+
+#else // overlay mode
+
+#include "hdr/wctype_overlay.h"
+
+#endif // LLVM_LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_TYPES_WCTYPE_T_H
diff --git a/libc/hdr/wctype_overlay.h b/libc/hdr/wctype_overlay.h
new file mode 100644
index 0000000000000..6249d9f7242ed
--- /dev/null
+++ b/libc/hdr/wctype_overlay.h
@@ -0,0 +1,69 @@
+//===-- Including wctype.h in overlay mode ---------------------------------===//
+//
+// 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_HDR_WCTYPE_OVERLAY_H
+#define LLVM_LIBC_HDR_WCTYPE_OVERLAY_H
+
+#ifdef LIBC_FULL_BUILD
+#error "This header should only be included in overlay mode"
+#endif
+
+// Overlay mode
+
+// glibc <wctype.h> header might provide extern inline definitions for few
+// functions, causing external alias errors.  They are guarded by
+// `__USE_EXTERN_INLINES` macro.  We temporarily disable `__USE_EXTERN_INLINES`
+// macro by defining `__NO_INLINE__` before including <wctype.h>.
+// And the same with `__USE_FORTIFY_LEVEL`, which will be temporarily disabled
+// with `_FORTIFY_SOURCE`.
+
+#ifdef _FORTIFY_SOURCE
+#define LIBC_OLD_FORTIFY_SOURCE _FORTIFY_SOURCE
+#undef _FORTIFY_SOURCE
+#endif
+
+#ifndef __NO_INLINE__
+#define __NO_INLINE__ 1
+#define LIBC_SET_NO_INLINE
+#endif
+
+#ifdef __USE_EXTERN_INLINES
+#define LIBC_OLD_USE_EXTERN_INLINES
+#undef __USE_EXTERN_INLINES
+#endif
+
+#ifdef __USE_FORTIFY_LEVEL
+#define LIBC_OLD_USE_FORTIFY_LEVEL __USE_FORTIFY_LEVEL
+#undef __USE_FORTIFY_LEVEL
+#define __USE_FORTIFY_LEVEL 0
+#endif
+
+#include <wctype.h>
+
+#ifdef LIBC_OLD_FORTIFY_SOURCE
+#define _FORTIFY_SOURCE LIBC_OLD_FORTIFY_SOURCE
+#undef LIBC_OLD_FORTIFY_SOURCE
+#endif
+
+#ifdef LIBC_SET_NO_INLINE
+#undef __NO_INLINE__
+#undef LIBC_SET_NO_INLINE
+#endif
+
+#ifdef LIBC_OLD_USE_FORTIFY_LEVEL
+#undef __USE_FORTIFY_LEVEL
+#define __USE_FORTIFY_LEVEL LIBC_OLD_USE_FORTIFY_LEVEL
+#undef LIBC_OLD_USE_FORTIFY_LEVEL
+#endif
+
+#ifdef LIBC_OLD_USE_EXTERN_INLINES
+#define __USE_EXTERN_INLINES
+#undef LIBC_OLD_USE_EXTERN_INLINES
+#endif
+
+#endif // LLVM_LIBC_HDR_WCTYPE_OVERLAY_H
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index 3375e4e21e338..66c5a0e6462c5 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -867,6 +867,7 @@ add_header_macro(
   DEPENDS
     .llvm_libc_common_h
     .llvm-libc-types.wint_t
+    .llvm-libc-types.wctype_t
 )
 
 add_header_macro(
diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index 0d6bc0982b847..ea9a507836735 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -166,6 +166,7 @@ add_header(
     libc.include.llvm-libc-macros.stdint_macros
 )
 add_header(wint_t HDR wint_t.h)
+add_header(wctype_t HDR wctype_t.h)
 add_header(sa_family_t HDR sa_family_t.h)
 add_header(socklen_t HDR socklen_t.h)
 add_header(struct_sockaddr_un HDR struct_sockaddr_un.h DEPENDS .sa_family_t)
diff --git a/libc/include/llvm-libc-types/wctype_t.h b/libc/include/llvm-libc-types/wctype_t.h
new file mode 100644
index 0000000000000..b096c0061984a
--- /dev/null
+++ b/libc/include/llvm-libc-types/wctype_t.h
@@ -0,0 +1,15 @@
+//===-- Definition of wctype_t types
+//----------------------------------------===//
+//
+// 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_TYPES_WCTYPE_T_H
+#define LLVM_LIBC_TYPES_WCTYPE_T_H
+
+typedef unsigned int wctype_t;
+
+#endif // LLVM_LIBC_TYPES_WCTYPE_T_H
diff --git a/libc/include/wctype.yaml b/libc/include/wctype.yaml
index 932ff6ac1b138..a5163ecc2d3f2 100644
--- a/libc/include/wctype.yaml
+++ b/libc/include/wctype.yaml
@@ -1,6 +1,7 @@
 header: wctype.h
 types:
   - type_name: wint_t
+  - type_name: wctype_t
 functions:
   - name: iswalpha
     standards:
@@ -74,3 +75,16 @@ functions:
     return_type: int
     arguments:
       - type: wint_t
+  - name: iswctype
+    standards:
+      - stdc
+    return_type: int
+    arguments:
+      - type: wint_t
+      - type: wctype_t
+  - name: wctype
+    standards:
+      - stdc
+    return_type: wctype_t
+    arguments:
+      - type: const char*
diff --git a/libc/src/__support/wctype_utils.h b/libc/src/__support/wctype_utils.h
index a80c78d54c796..fd6a0ec9f4f8d 100644
--- a/libc/src/__support/wctype_utils.h
+++ b/libc/src/__support/wctype_utils.h
@@ -454,6 +454,77 @@ LIBC_INLINE constexpr wchar_t toupper(wchar_t wch) {
   }
 }
 
+LIBC_INLINE constexpr int simple_strcmp(const char *s1, const char *s2) {
+  while (*s1 && (*s1 == *s2)) {
+    ++s1;
+    ++s2;
+  }
+  return *(const char *)s1 - *(const char *)s2;
+}
+
+LIBC_INLINE constexpr unsigned int wctype(const char *property) {
+  if (!property)
+    return 0; // WCTYPE_INVALID
+
+  if (simple_strcmp(property, "alnum") == 0)
+    return 1; // WCTYPE_ALNUM
+  if (simple_strcmp(property, "alpha") == 0)
+    return 2; // WCTYPE_ALPHA
+  if (simple_strcmp(property, "blank") == 0)
+    return 3; // WCTYPE_BLANK
+  if (simple_strcmp(property, "cntrl") == 0)
+    return 4; // WCTYPE_CNTRL
+  if (simple_strcmp(property, "digit") == 0)
+    return 5; // WCTYPE_DIGIT
+  if (simple_strcmp(property, "graph") == 0)
+    return 6; // WCTYPE_GRAPH
+  if (simple_strcmp(property, "lower") == 0)
+    return 7; // WCTYPE_LOWER
+  if (simple_strcmp(property, "print") == 0)
+    return 8; // WCTYPE_PRINT
+  if (simple_strcmp(property, "punct") == 0)
+    return 9; // WCTYPE_PUNCT
+  if (simple_strcmp(property, "space") == 0)
+    return 10; // WCTYPE_SPACE
+  if (simple_strcmp(property, "upper") == 0)
+    return 11; // WCTYPE_UPPER
+  if (simple_strcmp(property, "xdigit") == 0)
+    return 12; // WCTYPE_XDIGIT
+
+  return 0; // WCTYPE_INVALID
+}
+
+LIBC_INLINE constexpr int iswctype(wchar_t c, unsigned int desc) {
+  switch (desc) {
+  case 1:
+    return isalnum(c); // alnum
+  case 2:
+    return isalpha(c); // alpha
+  case 3:
+    return isblank(c); // blank
+  case 4:
+    return iscntrl(c); // cntrl
+  case 5:
+    return isdigit(c); // digit
+  case 6:
+    return isgraph(c); // graph
+  case 7:
+    return islower(c); // lower
+  case 8:
+    return isprint(c); // print
+  case 9:
+    return ispunct(c); // punct
+  case 10:
+    return isspace(c); // space
+  case 11:
+    return isupper(c); // upper
+  case 12:
+    return isxdigit(c); // xdigit
+  default:
+    return 0;
+  }
+}
+
 } // namespace ascii
 
 LIBC_INLINE constexpr bool islower(wchar_t wch) {
@@ -552,6 +623,14 @@ LIBC_INLINE constexpr bool isprint(wchar_t wch) {
 #endif
 }
 
+LIBC_INLINE constexpr bool iswctype(wchar_t c, unsigned int desc) {
+  return ascii::iswctype(c, desc);
+}
+
+LIBC_INLINE constexpr unsigned int wctype(const char *property) {
+  return ascii::wctype(property);
+}
+
 LIBC_INLINE constexpr bool isxdigit(wchar_t wch) {
   // Hexadecimal digits are the same in C.UTF8 as in ASCII
   return ascii::isxdigit(wch);
diff --git a/libc/src/wctype/CMakeLists.txt b/libc/src/wctype/CMakeLists.txt
index 07bfed39980c2..57f9e7753a9f5 100644
--- a/libc/src/wctype/CMakeLists.txt
+++ b/libc/src/wctype/CMakeLists.txt
@@ -130,3 +130,24 @@ add_entrypoint_object(
     libc.src.__support.wctype_utils
     libc.hdr.types.wint_t
 )
+
+add_entrypoint_object(
+  iswctype
+  SRCS
+    iswctype.cpp
+  HDRS
+    iswctype.h
+  DEPENDS
+    libc.src.__support.wctype_utils
+    libc.hdr.types.wint_t
+)
+
+add_entrypoint_object(
+  wctype
+  SRCS
+    wctype.cpp
+  HDRS
+    wctype.h
+  DEPENDS
+    libc.src.__support.wctype_utils
+)
diff --git a/libc/src/wctype/iswctype.cpp b/libc/src/wctype/iswctype.cpp
new file mode 100644
index 0000000000000..87bf0e31fe463
--- /dev/null
+++ b/libc/src/wctype/iswctype.cpp
@@ -0,0 +1,23 @@
+//===-- Implementation of iswctype ----------------------------------------===//
+//
+// 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 "src/wctype/iswctype.h"
+#include "src/__support/common.h"
+#include "src/__support/wctype_utils.h"
+
+#include "hdr/types/wctype_t.h"
+#include "hdr/types/wint_t.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, iswctype, (wint_t c, wctype_t desc)) {
+  return internal::iswctype(static_cast<wchar_t>(c),
+                            static_cast<unsigned int>(desc));
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/wctype/iswctype.h b/libc/src/wctype/iswctype.h
new file mode 100644
index 0000000000000..10a0f3e969c8d
--- /dev/null
+++ b/libc/src/wctype/iswctype.h
@@ -0,0 +1,22 @@
+//===-- Implementation header for iswctype ----------------------*- 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_SRC_WCTYPE_ISWCTYPE_H
+#define LLVM_LIBC_SRC_WCTYPE_ISWCTYPE_H
+
+#include "hdr/types/wctype_t.h"
+#include "hdr/types/wint_t.h"
+#include "src/__support/common.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int iswctype(wint_t c, wctype_t desc);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_WCTYPE_ISWCTYPE_H
diff --git a/libc/src/wctype/wctype.cpp b/libc/src/wctype/wctype.cpp
new file mode 100644
index 0000000000000..59c28dcf5a437
--- /dev/null
+++ b/libc/src/wctype/wctype.cpp
@@ -0,0 +1,21 @@
+//===-- Implementation of wctype ----------------------------------------===//
+//
+// 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 "src/wctype/wctype.h"
+#include "src/__support/common.h"
+#include "src/__support/wctype_utils.h"
+
+#include "hdr/types/wctype_t.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(wctype_t, wctype, (const char *property)) {
+  return internal::wctype(property);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/wctype/wctype.h b/libc/src/wctype/wctype.h
new file mode 100644
index 0000000000000..98300e49f7e03
--- /dev/null
+++ b/libc/src/wctype/wctype.h
@@ -0,0 +1,22 @@
+//===-- Implementation header for iswctype ----------------------*- 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_SRC_WCTYPE_WCTYPE_H
+#define LLVM_LIBC_SRC_WCTYPE_WCTYPE_H
+
+#include "hdr/types/wctype_t.h"
+#include "hdr/types/wint_t.h"
+#include "src/__support/common.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+wctype_t wctype(const char *property);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_WCTYPE_WCTYPE_H
diff --git a/libc/test/src/wctype/CMakeLists.txt b/libc/test/src/wctype/CMakeLists.txt
index a5f85856dfb38..2d02888ad2833 100644
--- a/libc/test/src/wctype/CMakeLists.txt
+++ b/libc/test/src/wctype/CMakeLists.txt
@@ -121,3 +121,23 @@ add_libc_test(
   DEPENDS
     libc.src.wctype.iswprint
 )
+
+add_libc_test(
+  iswctype_test
+  SUITE
+    libc_wctype_unittests
+  SRCS
+    iswctype_test.cpp
+  DEPENDS
+    libc.src.wctype.iswctype
+)
+
+add_libc_test(
+  wctype_test
+  SUITE
+    libc_wctype_unittests
+  SRCS
+    wctype_test.cpp
+  DEPENDS
+    libc.src.wctype.wctype
+)
diff --git a/libc/test/src/wctype/iswctype_test.cpp b/libc/test/src/wctype/iswctype_test.cpp
new file mode 100644
index 0000000000000..39ff350ab6673
--- /dev/null
+++ b/libc/test/src/wctype/iswctype_test.cpp
@@ -0,0 +1,110 @@
+//===-- Unittests for iswctype --------------------------------------------===//
+//
+// 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 "src/wctype/iswctype.h"
+
+#include "test/UnitTest/Test.h"
+
+// Simple tests, already properly tested in
+// libc/test/src/__support/wctype_utils_test.cpp
+
+static constexpr wctype_t WCTYPE_INVALID = static_cast<wctype_t>(0);
+static constexpr wctype_t WCTYPE_ALNUM = static_cast<wctype_t>(1);
+static constexpr wctype_t WCTYPE_ALPHA = static_cast<wctype_t>(2);
+static constexpr wctype_t WCTYPE_BLANK = static_cast<wctype_t>(3);
+static constexpr wctype_t WCTYPE_CNTRL = static_cast<wctype_t>(4);
+static constexpr wctype_t WCTYPE_DIGIT = static_cast<wctype_t>(5);
+static constexpr wctype_t WCTYPE_GRAPH = static_cast<wctype_t>(6);
+static constexpr wctype_t WCTYPE_LOWER = static_cast<wctype_t>(7);
+static constexpr wctype_t WCTYPE_PRINT = static_cast<wctype_t>(8);
+static constexpr wctype_t WCTYPE_PUNCT = static_cast<wctype_t>(9);
+static constexpr wctype_t WCTYPE_SPACE = static_cast<wctype_t>(10);
+static constexpr wctype_t WCTYPE_UPPER = static_cast<wctype_t>(11);
+static constexpr wctype_t WCTYPE_XDIGIT = static_cast<wctype_t>(12);
+
+TEST(LlvmLibciswctype, SimpleTest) {
+  using LIBC_NAMESPACE::iswctype;
+
+  // alnum
+  EXPECT_NE(iswctype('a', WCTYPE_ALNUM), 0);
+  EXPECT_NE(iswctype('Z', WCTYPE_ALNUM), 0);
+  EXPECT_NE(iswctype('5', WCTYPE_ALNUM), 0);
+  EXPECT_EQ(iswctype('!', WCTYPE_ALNUM), 0);
+
+  // alpha
+  EXPECT_NE(iswctype('a', WCTYPE_ALPHA), 0);
+  EXPECT_NE(iswctype('Z', WCTYPE_ALPHA), 0);
+  EXPECT_EQ(iswctype('1', WCTYPE_ALPHA), 0);
+  EXPECT_EQ(iswctype(' ', WCTYPE_ALPHA), 0);
+
+  // blank
+  EXPECT_NE(iswctype(' ', WCTYPE_BLANK), 0);
+  EXPECT_NE(iswctype('\t', WCTYPE_BLANK), 0);
+  EXPECT_EQ(iswctype('\n', WCTYPE_BLANK), 0);
+  EXPECT_EQ(iswctype('A', WCTYPE_BLANK), 0);
+
+  // cntrl
+  EXPECT_NE(iswctype('\0', WCTYPE_CNTRL), 0);
+  EXPECT_NE(iswctype('\n', WCTYPE_CNTRL), 0);
+  EXPECT_NE(iswctype(0x7f, WCTYPE_CNTRL), 0);
+  EXPECT_EQ(iswctype('A', WCTYPE_CNTRL), 0);
+
+  // digit
+  EXPECT_NE(iswctype('0', WCTYPE_DIGIT), 0);
+  EXPECT_NE(iswctype('9', WCTYPE_DIGIT), 0);
+  EXPECT_EQ(iswctype('a', WCTYPE_DIGIT), 0);
+  EXPECT_EQ(iswctype(' ', WCTYPE_DIGIT), 0);
+
+  // graph
+  EXPECT_NE(iswctype('A', WCTYPE_GRAPH), 0);
+  EXPECT_NE(iswctype('1', WCTYPE_GRAPH), 0);
+  EXPECT_NE(iswctype('!', WCTYPE_GRAPH), 0);
+  EXPECT_EQ(iswctype(' ', WCTYPE_GRAPH), 0);
+
+  // lower
+  EXPECT_NE(iswctype('a', WCTYPE_LOWER), 0);
+  EXPECT_NE(iswctype('z', WCTYPE_LOWER), 0);
+  EXPECT_EQ(iswctype('A', WCTYPE_LOWER), 0);
+  EXPECT_EQ(iswctype('1', WCTYPE_LOWER), 0);
+
+  // print
+  EXPECT_NE(iswctype(' ', WCTYPE_PRINT), 0);
+  EXPECT_NE(iswctype('A', WCTYPE_PRINT), 0);
+  EXPECT_NE(iswctype('~', WCTYPE_PRINT), 0);
+  EXPECT_EQ(iswctype('\n', WCTYPE_PRINT), 0);
+
+  // punct
+  EXPECT_NE(iswctype('!', WCTYPE_PUNCT), 0);
+  EXPECT_NE(iswctype('?', WCTYPE_PUNCT), 0);
+  EXPECT_EQ(iswctype('a', WCTYPE_PUNCT), 0);
+  EXPECT_EQ(iswctype('1', WCTYPE_PUNCT), 0);
+
+  // space
+  EXPECT_NE(iswctype(' ', WCTYPE_SPACE), 0);
+  EXPECT_NE(iswctype('\t', WCTYPE_SPACE), 0);
+  EXPECT_NE(iswctype('\n', WCTYPE_SPACE), 0);
+  EXPECT_EQ(iswctype('A', WCTYPE_SPACE), 0);
+
+  // upper
+  EXPECT_NE(iswctype('A', WCTYPE_UPPER), 0);
+  EXPECT_NE(iswctype('Z', WCTYPE_UPPER), 0);
+  EXPECT_EQ(iswctype('a', WCTYPE_UPPER), 0);
+  EXPECT_EQ(iswctype('1', WCTYPE_UPPER), 0);
+
+  // xdigit
+  EXPECT_NE(iswctype('0', WCTYPE_XDIGIT), 0);
+  EXPECT_NE(iswctype('9', WCTYPE_XDIGIT), 0);
+  EXPECT_NE(iswctype('a', WCTYPE_XDIGIT), 0);
+  EXPECT_NE(iswctype('F', WCTYPE_XDIGIT), 0);
+  EXPECT_EQ(iswctype('g', WCTYPE_XDIGIT), 0);
+  EXPECT_EQ(iswctype('?', WCTYPE_XDIGIT), 0);
+
+  // invalid descriptor
+  EXPECT_EQ(iswctype('a', WCTYPE_INVALID), 0);
+  EXPECT_EQ(iswctype('a', static_cast<wctype_t>(999)), 0);
+}
diff --git a/libc/test/src/wctype/wctype_test.cpp b/libc/test/src/wctype/wctype_test.cpp
new file mode 100644
index 0000000000000..22a3f6d1e7e2e
--- /dev/null
+++ b/libc/test/src/wctype/wctype_test.cpp
@@ -0,0 +1,63 @@
+//===-- Unittests for wctype --------------------------------------------===//
+//
+// 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 "src/wctype/wctype.h"
+
+#include "test/UnitTest/Test.h"
+
+// wctype descriptors (must match implementation)
+static constexpr wctype_t WCTYPE_INVALID = static_cast<wctype_t>(0);
+static constexpr wctype_t WCTYPE_ALNUM = static_cast<wctype_t>(1);
+static constexpr wctype_t WCTYPE_ALPHA = static_cast<wctype_t>(2);
+static constexpr wctype_t WCTYPE_BLANK = static_cast<wctype_t>(3);
+static constexpr wctype_t WCTYPE_CNTRL = static_cast<wctype_t>(4);
+static constexpr wctype_t WCTYPE_DIGIT = static_cast<wctype_t>(5);
+static constexpr wctype_t WCTYPE_GRAPH = static_cast<wctype_t>(6);
+static constexpr wctype_t WCTYPE_LOWER = static_cast<wctype_t>(7);
+static constexpr wctype_t WCTYPE_PRINT = static_cast<wctype_t>(8);
+static constexpr wctype_t WCTYPE_PUNCT = static_cast<wctype_t>(9);
+static constexpr wctype_t WCTYPE_SPACE = static_cast<wctype_t>(10);
+static constexpr wctype_t WCTYPE_UPPER = static_cast<wctype_t>(11);
+static constexpr wctype_t WCTYPE_XDIGIT = static_cast<wctype_t>(12);
+
+TEST(LlvmLibcwctype, SimpleTest) {
+  using LIBC_NAMESPACE::wctype;
+
+  auto alnum = wctype("alnum");
+  auto alpha = wctype("alpha");
+  auto blank = wctype("blank");
+  auto cntrl = wctype("cntrl");
+  auto digit = wctype("digit");
+  auto graph = wctype("graph");
+  auto lower = wctype("lower");
+  auto print = wctype("print");
+  auto punct = wctype("punct");
+  auto space = wctype("space");
+  auto upper = wctype("upper");
+  auto xdigit = wctype("xdigit");
+
+  // valid descriptors should be nonzero
+  EXPECT_EQ(alnum, WCTYPE_ALNUM);
+  EXPECT_EQ(alpha, WCTYPE_ALPHA);
+  EXPECT_EQ(blank, WCTYPE_BLANK);
+  EXPECT_EQ(cntrl, WCTYPE_CNTRL);
+  EXPECT_EQ(digit, WCTYPE_DIGIT);
+  EXPECT_EQ(graph, WCTYPE_GRAPH);
+  EXPECT_EQ(lower, WCTYPE_LOWER);
+  EXPECT_EQ(print, WCTYPE_PRINT);
+  EXPECT_EQ(punct, WCTYPE_PUNCT);
+  EXPECT_EQ(space, WCTYPE_SPACE);
+  EXPECT_EQ(upper, WCTYPE_UPPER);
+  EXPECT_EQ(xdigit, WCTYPE_XDIGIT);
+
+  // invalid properties should return zero
+  EXPECT_EQ(wctype(""), WCTYPE_INVALID);
+  EXPECT_EQ(wctype("invalid"), WCTYPE_INVALID);
+  EXPECT_EQ(wctype("Alpha"), WCTYPE_INVALID);
+  EXPECT_EQ(wctype("unknown"), WCTYPE_INVALID);
+}



More information about the libc-commits mailing list