Skip to content

[C2y] Add stdcountof.h #140890

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

AaronBallman
Copy link
Collaborator

@AaronBallman AaronBallman commented May 21, 2025

WG14 N3469 changed _Lengthof to _Countof but it also introduced the <stdcountof.h> header to expose a macro with a non-ugly identifier. GCC vends this header as part of the compiler implementation, so Clang should do the same.

Suggested-by: Alejandro Colomar alx@kernel.org

WG14 N3469 changed _Lengthof to _Countof but it also introduced the
<stdcountof.h> header to expose a macro with a non-ugly identifier.
GCC vends this header as part of the compiler implementation, so Clang
should do the same.
@AaronBallman AaronBallman added clang Clang issues not falling into any other category clang:headers Headers provided by Clang, e.g. for intrinsics c2y labels May 21, 2025
@llvmbot
Copy link
Member

llvmbot commented May 21, 2025

@llvm/pr-subscribers-clang-modules
@llvm/pr-subscribers-backend-x86

@llvm/pr-subscribers-clang

Author: Aaron Ballman (AaronBallman)

Changes

WG14 N3469 changed _Lengthof to _Countof but it also introduced the <stdcountof.h> header to expose a macro with a non-ugly identifier. GCC vends this header as part of the compiler implementation, so Clang should do the same.


Full diff: https://github.com/llvm/llvm-project/pull/140890.diff

3 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+3-1)
  • (added) clang/lib/Headers/stdcountof.h (+15)
  • (modified) clang/test/C/C2y/n3469.c (+11-2)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index b466f3758e0b6..7b2777f711b8d 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -249,7 +249,9 @@ C2y Feature Support
   a conforming extension in earlier C language modes, but not in C++ language
   modes (``std::extent`` and ``std::size`` already provide the same
   functionality but with more granularity). The feature can be tested via
-  ``__has_feature(c_countof)`` or ``__has_extension(c_countof)``.
+  ``__has_feature(c_countof)`` or ``__has_extension(c_countof)``. This also
+  adds the ``<stdcountof.h>`` header file which exposes the ``countof`` macro
+  which expands to ``_Countof``.
 
 C23 Feature Support
 ^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Headers/stdcountof.h b/clang/lib/Headers/stdcountof.h
new file mode 100644
index 0000000000000..5714e6d6ff860
--- /dev/null
+++ b/clang/lib/Headers/stdcountof.h
@@ -0,0 +1,15 @@
+/*===---- stdcountof.h - Standard header for countof -----------------------===
+ *
+ * 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 __STDCOUNTOF_H
+#define __STDCOUNTOF_H
+
+#define countof _Countof
+
+#endif /* __STDCOUNTOF_H */
diff --git a/clang/test/C/C2y/n3469.c b/clang/test/C/C2y/n3469.c
index 3d9ac8e6411e9..4660596614075 100644
--- a/clang/test/C/C2y/n3469.c
+++ b/clang/test/C/C2y/n3469.c
@@ -1,9 +1,9 @@
-// RUN: %clang_cc1 -fsyntax-only -std=c2y -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c2y -verify -ffreestanding %s
 
 /* WG14 N3469: Clang 21
  * The Big Array Size Survey
  *
- * This renames _Lengthof to _Countof.
+ * This renames _Lengthof to _Countof and introduces the stdcountof.h header.
  */
 
 void test() {
@@ -12,3 +12,12 @@ void test() {
                                expected-error {{expected expression}}
 }
 
+#ifdef countof
+#error "why is countof defined as a macro?"
+#endif
+
+#include <stdcountof.h>
+
+#ifndef countof
+#error "why is countof not defined as a macro?"
+#endif

@AaronBallman
Copy link
Collaborator Author

CC @alejandro-colomar

@AaronBallman
Copy link
Collaborator Author

Precommit CI caught a relevant failure that only happens when testing against an install target, so yay for that! I've got a fix coming along with some other updated support files I found when looking at stdalign.h

@AaronBallman AaronBallman requested a review from erichkeane May 21, 2025 13:34
@llvmbot llvmbot added clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:modules C++20 modules and Clang Header Modules labels May 21, 2025
Copy link

github-actions bot commented May 21, 2025

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff HEAD~1 HEAD --extensions h,c,cpp -- clang/lib/Headers/stdcountof.h clang/lib/Lex/ModuleMap.cpp clang/lib/Lex/PPDirectives.cpp clang/test/C/C2y/n3469.c
View the diff from clang-format here.
diff --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp
index be9cab8af..aea71808f 100644
--- a/clang/lib/Lex/ModuleMap.cpp
+++ b/clang/lib/Lex/ModuleMap.cpp
@@ -253,19 +253,19 @@ OptionalFileEntryRef ModuleMap::findHeader(
 /// headers.
 static bool isBuiltinHeaderName(StringRef FileName) {
   return llvm::StringSwitch<bool>(FileName)
-           .Case("float.h", true)
-           .Case("iso646.h", true)
-           .Case("limits.h", true)
-           .Case("stdalign.h", true)
-           .Case("stdarg.h", true)
-           .Case("stdatomic.h", true)
-           .Case("stdbool.h", true)
-           .Case("stdcountof.h", true)
-           .Case("stddef.h", true)
-           .Case("stdint.h", true)
-           .Case("tgmath.h", true)
-           .Case("unwind.h", true)
-           .Default(false);
+      .Case("float.h", true)
+      .Case("iso646.h", true)
+      .Case("limits.h", true)
+      .Case("stdalign.h", true)
+      .Case("stdarg.h", true)
+      .Case("stdatomic.h", true)
+      .Case("stdbool.h", true)
+      .Case("stdcountof.h", true)
+      .Case("stddef.h", true)
+      .Case("stdint.h", true)
+      .Case("tgmath.h", true)
+      .Case("unwind.h", true)
+      .Default(false);
 }
 
 /// Determine whether the given module name is the name of a builtin
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 68f9ca9cb..502dd015a 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -248,50 +248,58 @@ static bool warnByDefaultOnWrongCase(StringRef Include) {
 
   // The standard C/C++ and Posix headers
   return llvm::StringSwitch<bool>(LowerInclude)
-    // C library headers
-    .Cases("assert.h", "complex.h", "ctype.h", "errno.h", "fenv.h", true)
-    .Cases("float.h", "inttypes.h", "iso646.h", "limits.h", "locale.h", true)
-    .Cases("math.h", "setjmp.h", "signal.h", "stdalign.h", "stdarg.h", true)
-    .Cases("stdatomic.h", "stdbool.h", "stdckdint.h", "stdcountof.h", true)
-    .Cases("stddef.h", "stdint.h", "stdio.h", "stdlib.h", "stdnoreturn.h", true)
-    .Cases("string.h", "tgmath.h", "threads.h", "time.h", "uchar.h", true)
-    .Cases("wchar.h", "wctype.h", true)
-
-    // C++ headers for C library facilities
-    .Cases("cassert", "ccomplex", "cctype", "cerrno", "cfenv", true)
-    .Cases("cfloat", "cinttypes", "ciso646", "climits", "clocale", true)
-    .Cases("cmath", "csetjmp", "csignal", "cstdalign", "cstdarg", true)
-    .Cases("cstdbool", "cstddef", "cstdint", "cstdio", "cstdlib", true)
-    .Cases("cstring", "ctgmath", "ctime", "cuchar", "cwchar", true)
-    .Case("cwctype", true)
-
-    // C++ library headers
-    .Cases("algorithm", "fstream", "list", "regex", "thread", true)
-    .Cases("array", "functional", "locale", "scoped_allocator", "tuple", true)
-    .Cases("atomic", "future", "map", "set", "type_traits", true)
-    .Cases("bitset", "initializer_list", "memory", "shared_mutex", "typeindex", true)
-    .Cases("chrono", "iomanip", "mutex", "sstream", "typeinfo", true)
-    .Cases("codecvt", "ios", "new", "stack", "unordered_map", true)
-    .Cases("complex", "iosfwd", "numeric", "stdexcept", "unordered_set", true)
-    .Cases("condition_variable", "iostream", "ostream", "streambuf", "utility", true)
-    .Cases("deque", "istream", "queue", "string", "valarray", true)
-    .Cases("exception", "iterator", "random", "strstream", "vector", true)
-    .Cases("forward_list", "limits", "ratio", "system_error", true)
-
-    // POSIX headers (which aren't also C headers)
-    .Cases("aio.h", "arpa/inet.h", "cpio.h", "dirent.h", "dlfcn.h", true)
-    .Cases("fcntl.h", "fmtmsg.h", "fnmatch.h", "ftw.h", "glob.h", true)
-    .Cases("grp.h", "iconv.h", "langinfo.h", "libgen.h", "monetary.h", true)
-    .Cases("mqueue.h", "ndbm.h", "net/if.h", "netdb.h", "netinet/in.h", true)
-    .Cases("netinet/tcp.h", "nl_types.h", "poll.h", "pthread.h", "pwd.h", true)
-    .Cases("regex.h", "sched.h", "search.h", "semaphore.h", "spawn.h", true)
-    .Cases("strings.h", "stropts.h", "sys/ipc.h", "sys/mman.h", "sys/msg.h", true)
-    .Cases("sys/resource.h", "sys/select.h",  "sys/sem.h", "sys/shm.h", "sys/socket.h", true)
-    .Cases("sys/stat.h", "sys/statvfs.h", "sys/time.h", "sys/times.h", "sys/types.h", true)
-    .Cases("sys/uio.h", "sys/un.h", "sys/utsname.h", "sys/wait.h", "syslog.h", true)
-    .Cases("tar.h", "termios.h", "trace.h", "ulimit.h", true)
-    .Cases("unistd.h", "utime.h", "utmpx.h", "wordexp.h", true)
-    .Default(false);
+      // C library headers
+      .Cases("assert.h", "complex.h", "ctype.h", "errno.h", "fenv.h", true)
+      .Cases("float.h", "inttypes.h", "iso646.h", "limits.h", "locale.h", true)
+      .Cases("math.h", "setjmp.h", "signal.h", "stdalign.h", "stdarg.h", true)
+      .Cases("stdatomic.h", "stdbool.h", "stdckdint.h", "stdcountof.h", true)
+      .Cases("stddef.h", "stdint.h", "stdio.h", "stdlib.h", "stdnoreturn.h",
+             true)
+      .Cases("string.h", "tgmath.h", "threads.h", "time.h", "uchar.h", true)
+      .Cases("wchar.h", "wctype.h", true)
+
+      // C++ headers for C library facilities
+      .Cases("cassert", "ccomplex", "cctype", "cerrno", "cfenv", true)
+      .Cases("cfloat", "cinttypes", "ciso646", "climits", "clocale", true)
+      .Cases("cmath", "csetjmp", "csignal", "cstdalign", "cstdarg", true)
+      .Cases("cstdbool", "cstddef", "cstdint", "cstdio", "cstdlib", true)
+      .Cases("cstring", "ctgmath", "ctime", "cuchar", "cwchar", true)
+      .Case("cwctype", true)
+
+      // C++ library headers
+      .Cases("algorithm", "fstream", "list", "regex", "thread", true)
+      .Cases("array", "functional", "locale", "scoped_allocator", "tuple", true)
+      .Cases("atomic", "future", "map", "set", "type_traits", true)
+      .Cases("bitset", "initializer_list", "memory", "shared_mutex",
+             "typeindex", true)
+      .Cases("chrono", "iomanip", "mutex", "sstream", "typeinfo", true)
+      .Cases("codecvt", "ios", "new", "stack", "unordered_map", true)
+      .Cases("complex", "iosfwd", "numeric", "stdexcept", "unordered_set", true)
+      .Cases("condition_variable", "iostream", "ostream", "streambuf",
+             "utility", true)
+      .Cases("deque", "istream", "queue", "string", "valarray", true)
+      .Cases("exception", "iterator", "random", "strstream", "vector", true)
+      .Cases("forward_list", "limits", "ratio", "system_error", true)
+
+      // POSIX headers (which aren't also C headers)
+      .Cases("aio.h", "arpa/inet.h", "cpio.h", "dirent.h", "dlfcn.h", true)
+      .Cases("fcntl.h", "fmtmsg.h", "fnmatch.h", "ftw.h", "glob.h", true)
+      .Cases("grp.h", "iconv.h", "langinfo.h", "libgen.h", "monetary.h", true)
+      .Cases("mqueue.h", "ndbm.h", "net/if.h", "netdb.h", "netinet/in.h", true)
+      .Cases("netinet/tcp.h", "nl_types.h", "poll.h", "pthread.h", "pwd.h",
+             true)
+      .Cases("regex.h", "sched.h", "search.h", "semaphore.h", "spawn.h", true)
+      .Cases("strings.h", "stropts.h", "sys/ipc.h", "sys/mman.h", "sys/msg.h",
+             true)
+      .Cases("sys/resource.h", "sys/select.h", "sys/sem.h", "sys/shm.h",
+             "sys/socket.h", true)
+      .Cases("sys/stat.h", "sys/statvfs.h", "sys/time.h", "sys/times.h",
+             "sys/types.h", true)
+      .Cases("sys/uio.h", "sys/un.h", "sys/utsname.h", "sys/wait.h", "syslog.h",
+             true)
+      .Cases("tar.h", "termios.h", "trace.h", "ulimit.h", true)
+      .Cases("unistd.h", "utime.h", "utmpx.h", "wordexp.h", true)
+      .Default(false);
 }
 
 /// Find a similar string in `Candidates`.

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still have no problem with this, but it is now into things I have zero confidence reviewing :) Please have someone who has ever used modules review this.

@@ -252,8 +252,8 @@ static bool warnByDefaultOnWrongCase(StringRef Include) {
.Cases("assert.h", "complex.h", "ctype.h", "errno.h", "fenv.h", true)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fact that these aren't tablegened somewhere is frightening!

@@ -260,6 +260,7 @@ static bool isBuiltinHeaderName(StringRef FileName) {
.Case("stdarg.h", true)
.Case("stdatomic.h", true)
.Case("stdbool.h", true)
.Case("stdcountof.h", true)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These too!

@AaronBallman
Copy link
Collaborator Author

I still have no problem with this, but it is now into things I have zero confidence reviewing :) Please have someone who has ever used modules review this.

I've added some modules reviewers, so hopefully they can take a look. Yeah, I agree that tablegen would be a nice thing to have here, but it's a bit odd because the header file itself cannot be table generated.

Copy link
Contributor

@alejandro-colomar alejandro-colomar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

For the first commit:

Suggested-by: Alejandro Colomar <alx@kernel.org>

@AaronBallman
Copy link
Collaborator Author

Thanks!

For the first commit:

Suggested-by: Alejandro Colomar <alx@kernel.org>

Happy to add it, but for my own education: what's that do? :-D

@alejandro-colomar
Copy link
Contributor

alejandro-colomar commented May 21, 2025

Thanks!
For the first commit:

Suggested-by: Alejandro Colomar <alx@kernel.org>

Happy to add it,

Thanks! :)

but for my own education: what's that do? :-D

https://www.kernel.org/doc/html/latest/process/submitting-patches.html#using-reported-by-tested-by-reviewed-by-suggested-by-and-fixes

Quoting that:

A Suggested-by: tag indicates that the patch idea is suggested by
the person named and ensures credit to the person for the idea:
if we diligently credit our idea reporters, they will, hopefully,
be inspired to help us again in the future.  Note, this is one of
only three tags you might be able to use without explicit
permission of the person named (see ‘Tagging people requires
permission’ below for details).

@AaronBallman
Copy link
Collaborator Author

Thanks!
For the first commit:

Suggested-by: Alejandro Colomar <alx@kernel.org>

Happy to add it,

Thanks! :)

but for my own education: what's that do? :-D

https://www.kernel.org/doc/html/latest/process/submitting-patches.html#using-reported-by-tested-by-reviewed-by-suggested-by-and-fixes

Quoting that:

A Suggested-by: tag indicates that the patch idea is suggested by
the person named and ensures credit to the person for the idea:
if we diligently credit our idea reporters, they will, hopefully,
be inspired to help us again in the future.  Note, this is one of
only three tags you might be able to use without explicit
permission of the person named (see ‘Tagging people requires
permission’ below for details).

Ah, it's a Linux kernel thing, that explains why I've not seen it before. Neat idea, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:X86 c2y clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:headers Headers provided by Clang, e.g. for intrinsics clang:modules C++20 modules and Clang Header Modules clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants