[libcxx-commits] [libcxx] [libc++][modules] Adds std.compat module. (PR #71438)

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Thu Nov 23 11:34:58 PST 2023


https://github.com/mordante updated https://github.com/llvm/llvm-project/pull/71438

>From e6aec5f04675cdca13a661ef66eca1c54b610bdb Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Tue, 28 Feb 2023 20:29:26 +0100
Subject: [PATCH 1/2] [libc++][modules] Adds std.compat module.

This adds the std.compat module. The patch contains a bit of refactoring
to avoid code duplication between the std and std.compat module.

Implements parts of
- P2465R3 Standard Library Modules std and std.compat
---
 libcxx/modules/CMakeLists.txt                 |  41 ++-
 libcxx/modules/CMakeLists.txt.in              |  28 ++
 libcxx/modules/std.compat.cppm.in             | 202 ++++++++++++
 libcxx/modules/std.compat/cassert.inc         |  12 +
 libcxx/modules/std.compat/cctype.inc          |  25 ++
 libcxx/modules/std.compat/cerrno.inc          |  12 +
 libcxx/modules/std.compat/cfenv.inc           |  29 ++
 libcxx/modules/std.compat/cfloat.inc          |  12 +
 libcxx/modules/std.compat/cinttypes.inc       |  25 ++
 libcxx/modules/std.compat/climits.inc         |  12 +
 libcxx/modules/std.compat/clocale.inc         |  17 +
 libcxx/modules/std.compat/cmath.inc           | 268 +++++++++++++++
 libcxx/modules/std.compat/csetjmp.inc         |  13 +
 libcxx/modules/std.compat/csignal.inc         |  17 +
 libcxx/modules/std.compat/cstdarg.inc         |  10 +
 libcxx/modules/std.compat/cstddef.inc         |  22 ++
 libcxx/modules/std.compat/cstdint.inc         |  50 +++
 libcxx/modules/std.compat/cstdio.inc          |  61 ++++
 libcxx/modules/std.compat/cstdlib.inc         |  72 ++++
 libcxx/modules/std.compat/cstring.inc         |  36 ++
 libcxx/modules/std.compat/ctime.inc           |  28 ++
 libcxx/modules/std.compat/cuchar.inc          |  28 ++
 libcxx/modules/std.compat/cwchar.inc          |  80 +++++
 libcxx/modules/std.compat/cwctype.inc         |  35 ++
 libcxx/modules/std.cppm.in                    |  42 +--
 libcxx/test/libcxx/module_std.gen.py          | 246 +-------------
 libcxx/test/libcxx/module_std_compat.gen.py   |  62 ++++
 libcxx/test/lit.local.cfg                     |  14 +
 libcxx/test/std/modules/std.compat.pass.cpp   |  21 ++
 .../header_exportable_declarations.cpp        | 129 +++++++-
 .../header_exportable_declarations.hpp        |  34 +-
 libcxx/utils/CMakeLists.txt                   |  13 +-
 ..._cppm_in.py => generate_libcxx_cppm_in.py} |  71 ++--
 libcxx/utils/libcxx/test/modules.py           | 307 ++++++++++++++++++
 34 files changed, 1770 insertions(+), 304 deletions(-)
 create mode 100644 libcxx/modules/std.compat.cppm.in
 create mode 100644 libcxx/modules/std.compat/cassert.inc
 create mode 100644 libcxx/modules/std.compat/cctype.inc
 create mode 100644 libcxx/modules/std.compat/cerrno.inc
 create mode 100644 libcxx/modules/std.compat/cfenv.inc
 create mode 100644 libcxx/modules/std.compat/cfloat.inc
 create mode 100644 libcxx/modules/std.compat/cinttypes.inc
 create mode 100644 libcxx/modules/std.compat/climits.inc
 create mode 100644 libcxx/modules/std.compat/clocale.inc
 create mode 100644 libcxx/modules/std.compat/cmath.inc
 create mode 100644 libcxx/modules/std.compat/csetjmp.inc
 create mode 100644 libcxx/modules/std.compat/csignal.inc
 create mode 100644 libcxx/modules/std.compat/cstdarg.inc
 create mode 100644 libcxx/modules/std.compat/cstddef.inc
 create mode 100644 libcxx/modules/std.compat/cstdint.inc
 create mode 100644 libcxx/modules/std.compat/cstdio.inc
 create mode 100644 libcxx/modules/std.compat/cstdlib.inc
 create mode 100644 libcxx/modules/std.compat/cstring.inc
 create mode 100644 libcxx/modules/std.compat/ctime.inc
 create mode 100644 libcxx/modules/std.compat/cuchar.inc
 create mode 100644 libcxx/modules/std.compat/cwchar.inc
 create mode 100644 libcxx/modules/std.compat/cwctype.inc
 create mode 100644 libcxx/test/libcxx/module_std_compat.gen.py
 create mode 100644 libcxx/test/std/modules/std.compat.pass.cpp
 rename libcxx/utils/{generate_std_cppm_in.py => generate_libcxx_cppm_in.py} (52%)
 create mode 100644 libcxx/utils/libcxx/test/modules.py

diff --git a/libcxx/modules/CMakeLists.txt b/libcxx/modules/CMakeLists.txt
index 395226fb3728700..fae6448a7eec84b 100644
--- a/libcxx/modules/CMakeLists.txt
+++ b/libcxx/modules/CMakeLists.txt
@@ -118,6 +118,30 @@ set(LIBCXX_MODULE_STD_SOURCES
   std/version.inc
 )
 
+set(LIBCXX_MODULE_STD_COMPAT_SOURCES
+  std.compat/cassert.inc
+  std.compat/cctype.inc
+  std.compat/cerrno.inc
+  std.compat/cfenv.inc
+  std.compat/cfloat.inc
+  std.compat/cinttypes.inc
+  std.compat/climits.inc
+  std.compat/clocale.inc
+  std.compat/cmath.inc
+  std.compat/csetjmp.inc
+  std.compat/csignal.inc
+  std.compat/cstdarg.inc
+  std.compat/cstddef.inc
+  std.compat/cstdint.inc
+  std.compat/cstdio.inc
+  std.compat/cstdlib.inc
+  std.compat/cstring.inc
+  std.compat/ctime.inc
+  std.compat/cuchar.inc
+  std.compat/cwchar.inc
+  std.compat/cwctype.inc
+)
+
 # TODO MODULES the CMakeLists.txt in the install directory is only temporary
 # When that is removed the configured file can use the substitution
 # LIBCXX_GENERATED_INCLUDE_TARGET_DIR avoiding this set.
@@ -154,10 +178,25 @@ configure_file(
   @ONLY
 )
 
+set(LIBCXX_MODULE_STD_COMPAT_INCLUDE_SOURCES)
+foreach(file ${LIBCXX_MODULE_STD_COMPAT_SOURCES})
+  set(
+    LIBCXX_MODULE_STD_COMPAT_INCLUDE_SOURCES
+    "${LIBCXX_MODULE_STD_COMPAT_INCLUDE_SOURCES}#include \"${file}\"\n"
+  )
+endforeach()
+
+configure_file(
+  "std.compat.cppm.in"
+  "${LIBCXX_GENERATED_MODULE_DIR}/std.compat.cppm"
+  @ONLY
+)
+
 set(_all_modules)
 list(APPEND _all_modules "${LIBCXX_GENERATED_MODULE_DIR}/CMakeLists.txt")
 list(APPEND _all_modules "${LIBCXX_GENERATED_MODULE_DIR}/std.cppm")
-foreach(file ${LIBCXX_MODULE_STD_SOURCES})
+list(APPEND _all_modules "${LIBCXX_GENERATED_MODULE_DIR}/std.compat.cppm")
+foreach(file ${LIBCXX_MODULE_STD_SOURCES} ${LIBCXX_MODULE_STD_COMPAT_SOURCES})
   set(src "${CMAKE_CURRENT_SOURCE_DIR}/${file}")
   set(dst "${LIBCXX_GENERATED_MODULE_DIR}/${file}")
   add_custom_command(OUTPUT ${dst}
diff --git a/libcxx/modules/CMakeLists.txt.in b/libcxx/modules/CMakeLists.txt.in
index dca3b25155a5af4..b02b68915b8f4d0 100644
--- a/libcxx/modules/CMakeLists.txt.in
+++ b/libcxx/modules/CMakeLists.txt.in
@@ -29,6 +29,8 @@ macro(compile_define_if condition def)
   endif()
 endmacro()
 
+### STD
+
 add_library(std)
 target_sources(std
   PUBLIC FILE_SET cxx_modules TYPE CXX_MODULES FILES
@@ -52,3 +54,29 @@ set_target_properties(std
   PROPERTIES
     OUTPUT_NAME   "c++std"
 )
+
+### STD.COMPAT
+
+add_library(std.compat)
+target_sources(std.compat
+  PUBLIC FILE_SET cxx_modules TYPE CXX_MODULES FILES
+    std.compat.cppm
+)
+
+target_include_directories(std.compat SYSTEM PRIVATE @LIBCXX_CONFIGURED_INCLUDE_DIRS@)
+
+if (NOT @LIBCXX_ENABLE_EXCEPTIONS@)
+  target_compile_options(std.compat PUBLIC -fno-exceptions)
+endif()
+
+target_compile_options(std.compat
+  PUBLIC
+    -nostdinc++
+    -Wno-reserved-module-identifier
+    -Wno-reserved-user-defined-literal
+    @LIBCXX_COMPILE_FLAGS@
+)
+set_target_properties(std.compat
+  PROPERTIES
+    OUTPUT_NAME   "c++std.compat"
+)
diff --git a/libcxx/modules/std.compat.cppm.in b/libcxx/modules/std.compat.cppm.in
new file mode 100644
index 000000000000000..2fc98b86df11e10
--- /dev/null
+++ b/libcxx/modules/std.compat.cppm.in
@@ -0,0 +1,202 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+// WARNING, this entire header is generated by
+// utils/generate_libcxx_cppm_in.py
+// DO NOT MODIFY!
+
+module;
+
+#include <__config>
+
+// The headers of Table 24: C++ library headers [tab:headers.cpp]
+// and the headers of Table 25: C++ headers for C library facilities [tab:headers.cpp.c]
+#include <algorithm>
+#include <any>
+#include <array>
+#if !defined(_LIBCPP_HAS_NO_ATOMIC_HEADER)
+#  include <atomic>
+#endif
+#if !defined(_LIBCPP_HAS_NO_THREADS)
+#  include <barrier>
+#endif
+#include <bit>
+#include <bitset>
+#include <cassert>
+#include <cctype>
+#include <cerrno>
+#include <cfenv>
+#include <cfloat>
+#include <charconv>
+#include <chrono>
+#include <cinttypes>
+#include <climits>
+#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+#  include <clocale>
+#endif
+#include <cmath>
+#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+#  include <codecvt>
+#endif
+#include <compare>
+#include <complex>
+#include <concepts>
+#include <condition_variable>
+#include <coroutine>
+#include <csetjmp>
+#include <csignal>
+#include <cstdarg>
+#include <cstddef>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+#include <cuchar>
+#if !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
+#  include <cwchar>
+#endif
+#if !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
+#  include <cwctype>
+#endif
+#include <deque>
+#include <exception>
+#include <execution>
+#include <expected>
+#include <filesystem>
+#include <format>
+#include <forward_list>
+#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+#  include <fstream>
+#endif
+#include <functional>
+#if !defined(_LIBCPP_HAS_NO_THREADS)
+#  include <future>
+#endif
+#include <initializer_list>
+#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+#  include <iomanip>
+#endif
+#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+#  include <ios>
+#endif
+#include <iosfwd>
+#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+#  include <iostream>
+#endif
+#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+#  include <istream>
+#endif
+#include <iterator>
+#if !defined(_LIBCPP_HAS_NO_THREADS)
+#  include <latch>
+#endif
+#include <limits>
+#include <list>
+#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+#  include <locale>
+#endif
+#include <map>
+#include <mdspan>
+#include <memory>
+#include <memory_resource>
+#include <mutex>
+#include <new>
+#include <numbers>
+#include <numeric>
+#include <optional>
+#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+#  include <ostream>
+#endif
+#include <print>
+#include <queue>
+#include <random>
+#include <ranges>
+#include <ratio>
+#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+#  include <regex>
+#endif
+#include <scoped_allocator>
+#if !defined(_LIBCPP_HAS_NO_THREADS)
+#  include <semaphore>
+#endif
+#include <set>
+#if !defined(_LIBCPP_HAS_NO_THREADS)
+#  include <shared_mutex>
+#endif
+#include <source_location>
+#include <span>
+#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+#  include <sstream>
+#endif
+#include <stack>
+#include <stdexcept>
+#if !defined(_LIBCPP_HAS_NO_THREADS)
+#  include <stop_token>
+#endif
+#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+#  include <streambuf>
+#endif
+#include <string>
+#include <string_view>
+#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+#  include <strstream>
+#endif
+#include <system_error>
+#if !defined(_LIBCPP_HAS_NO_THREADS)
+#  include <thread>
+#endif
+#include <tuple>
+#include <type_traits>
+#include <typeindex>
+#include <typeinfo>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <valarray>
+#include <variant>
+#include <vector>
+#include <version>
+
+// *** Headers not yet available ***
+#if __has_include(<flat_map>)
+#  error "update the header information for <flat_map> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<flat_map>)
+#if __has_include(<flat_set>)
+#  error "update the header information for <flat_set> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<flat_set>)
+#if __has_include(<generator>)
+#  error "update the header information for <generator> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<generator>)
+#if __has_include(<hazard_pointer>)
+#  error "update the header information for <hazard_pointer> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<hazard_pointer>)
+#if __has_include(<rcu>)
+#  error "update the header information for <rcu> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<rcu>)
+#if __has_include(<spanstream>)
+#  error "update the header information for <spanstream> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<spanstream>)
+#if __has_include(<stacktrace>)
+#  error "update the header information for <stacktrace> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<stacktrace>)
+#if __has_include(<stdfloat>)
+#  error "update the header information for <stdfloat> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<stdfloat>)
+#if __has_include(<syncstream>)
+#  error "update the header information for <syncstream> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<syncstream>)
+#if __has_include(<text_encoding>)
+#  error "update the header information for <text_encoding> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<text_encoding>)
+
+export module std.compat;
+
+ at LIBCXX_MODULE_STD_INCLUDE_SOURCES@
+ at LIBCXX_MODULE_STD_COMPAT_INCLUDE_SOURCES@
\ No newline at end of file
diff --git a/libcxx/modules/std.compat/cassert.inc b/libcxx/modules/std.compat/cassert.inc
new file mode 100644
index 000000000000000..ac0533d14e9a9a4
--- /dev/null
+++ b/libcxx/modules/std.compat/cassert.inc
@@ -0,0 +1,12 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+export {
+  // This module exports nothing.
+} // export
diff --git a/libcxx/modules/std.compat/cctype.inc b/libcxx/modules/std.compat/cctype.inc
new file mode 100644
index 000000000000000..56fb45a374a510a
--- /dev/null
+++ b/libcxx/modules/std.compat/cctype.inc
@@ -0,0 +1,25 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+export {
+  using ::isalnum;
+  using ::isalpha;
+  using ::isblank;
+  using ::iscntrl;
+  using ::isdigit;
+  using ::isgraph;
+  using ::islower;
+  using ::isprint;
+  using ::ispunct;
+  using ::isspace;
+  using ::isupper;
+  using ::isxdigit;
+  using ::tolower;
+  using ::toupper;
+} // export
diff --git a/libcxx/modules/std.compat/cerrno.inc b/libcxx/modules/std.compat/cerrno.inc
new file mode 100644
index 000000000000000..ac0533d14e9a9a4
--- /dev/null
+++ b/libcxx/modules/std.compat/cerrno.inc
@@ -0,0 +1,12 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+export {
+  // This module exports nothing.
+} // export
diff --git a/libcxx/modules/std.compat/cfenv.inc b/libcxx/modules/std.compat/cfenv.inc
new file mode 100644
index 000000000000000..50128463d6a9143
--- /dev/null
+++ b/libcxx/modules/std.compat/cfenv.inc
@@ -0,0 +1,29 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+export {
+  // types
+  using ::fenv_t;
+  using ::fexcept_t;
+
+  // functions
+  using ::feclearexcept;
+  using ::fegetexceptflag;
+  using ::feraiseexcept;
+  using ::fesetexceptflag;
+  using ::fetestexcept;
+
+  using ::fegetround;
+  using ::fesetround;
+
+  using ::fegetenv;
+  using ::feholdexcept;
+  using ::fesetenv;
+  using ::feupdateenv;
+} // export
diff --git a/libcxx/modules/std.compat/cfloat.inc b/libcxx/modules/std.compat/cfloat.inc
new file mode 100644
index 000000000000000..ac0533d14e9a9a4
--- /dev/null
+++ b/libcxx/modules/std.compat/cfloat.inc
@@ -0,0 +1,12 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+export {
+  // This module exports nothing.
+} // export
diff --git a/libcxx/modules/std.compat/cinttypes.inc b/libcxx/modules/std.compat/cinttypes.inc
new file mode 100644
index 000000000000000..a64c088d0d6f881
--- /dev/null
+++ b/libcxx/modules/std.compat/cinttypes.inc
@@ -0,0 +1,25 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+export {
+  using ::imaxdiv_t;
+
+  using ::imaxabs;
+  using ::imaxdiv;
+  using ::strtoimax;
+  using ::strtoumax;
+  using ::wcstoimax;
+  using ::wcstoumax;
+
+  // abs is conditionally here, but always present in cmath.cppm. To avoid
+  // conflicing declarations omit the using here.
+
+  // div is conditionally here, but always present in cstdlib.cppm. To avoid
+  // conflicing declarations omit the using here.
+} // export
diff --git a/libcxx/modules/std.compat/climits.inc b/libcxx/modules/std.compat/climits.inc
new file mode 100644
index 000000000000000..ac0533d14e9a9a4
--- /dev/null
+++ b/libcxx/modules/std.compat/climits.inc
@@ -0,0 +1,12 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+export {
+  // This module exports nothing.
+} // export
diff --git a/libcxx/modules/std.compat/clocale.inc b/libcxx/modules/std.compat/clocale.inc
new file mode 100644
index 000000000000000..1a975c560a496d7
--- /dev/null
+++ b/libcxx/modules/std.compat/clocale.inc
@@ -0,0 +1,17 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+export {
+#ifndef _LIBCPP_HAS_NO_LOCALIZATION
+  using ::lconv _LIBCPP_USING_IF_EXISTS;
+
+  using ::localeconv _LIBCPP_USING_IF_EXISTS;
+  using ::setlocale _LIBCPP_USING_IF_EXISTS;
+#endif // _LIBCPP_HAS_NO_LOCALIZATION
+} // export
diff --git a/libcxx/modules/std.compat/cmath.inc b/libcxx/modules/std.compat/cmath.inc
new file mode 100644
index 000000000000000..de5379275c5fae6
--- /dev/null
+++ b/libcxx/modules/std.compat/cmath.inc
@@ -0,0 +1,268 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+export {
+  using ::double_t;
+  using ::float_t;
+
+  using ::acos;
+  using ::acosf;
+  using ::acosl;
+
+  using ::asin;
+  using ::asinf;
+  using ::asinl;
+
+  using ::atan;
+  using ::atanf;
+  using ::atanl;
+
+  using ::atan2;
+  using ::atan2f;
+  using ::atan2l;
+
+  using ::cos;
+  using ::cosf;
+  using ::cosl;
+
+  using ::sin;
+  using ::sinf;
+  using ::sinl;
+
+  using ::tan;
+  using ::tanf;
+  using ::tanl;
+
+  using ::acosh;
+  using ::acoshf;
+  using ::acoshl;
+
+  using ::asinh;
+  using ::asinhf;
+  using ::asinhl;
+
+  using ::atanh;
+  using ::atanhf;
+  using ::atanhl;
+
+  using ::cosh;
+  using ::coshf;
+  using ::coshl;
+
+  using ::sinh;
+  using ::sinhf;
+  using ::sinhl;
+
+  using ::tanh;
+  using ::tanhf;
+  using ::tanhl;
+
+  using ::exp;
+  using ::expf;
+  using ::expl;
+
+  using ::exp2;
+  using ::exp2f;
+  using ::exp2l;
+
+  using ::expm1;
+  using ::expm1f;
+  using ::expm1l;
+
+  using ::frexp;
+  using ::frexpf;
+  using ::frexpl;
+
+  using ::ilogb;
+  using ::ilogbf;
+  using ::ilogbl;
+
+  using ::ldexp;
+  using ::ldexpf;
+  using ::ldexpl;
+
+  using ::log;
+  using ::logf;
+  using ::logl;
+
+  using ::log10;
+  using ::log10f;
+  using ::log10l;
+
+  using ::log1p;
+  using ::log1pf;
+  using ::log1pl;
+
+  using ::log2;
+  using ::log2f;
+  using ::log2l;
+
+  using ::logb;
+  using ::logbf;
+  using ::logbl;
+
+  using ::modf;
+  using ::modff;
+  using ::modfl;
+
+  using ::scalbn;
+  using ::scalbnf;
+  using ::scalbnl;
+
+  using ::scalbln;
+  using ::scalblnf;
+  using ::scalblnl;
+
+  using ::cbrt;
+  using ::cbrtf;
+  using ::cbrtl;
+
+  // [c.math.abs], absolute values
+  using ::abs;
+
+  using ::fabs;
+  using ::fabsf;
+  using ::fabsl;
+
+  using ::hypot;
+  using ::hypotf;
+  using ::hypotl;
+
+  // [c.math.hypot3], three-dimensional hypotenuse
+
+  using ::pow;
+  using ::powf;
+  using ::powl;
+
+  using ::sqrt;
+  using ::sqrtf;
+  using ::sqrtl;
+
+  using ::erf;
+  using ::erff;
+  using ::erfl;
+
+  using ::erfc;
+  using ::erfcf;
+  using ::erfcl;
+
+  using ::lgamma;
+  using ::lgammaf;
+  using ::lgammal;
+
+  using ::tgamma;
+  using ::tgammaf;
+  using ::tgammal;
+
+  using ::ceil;
+  using ::ceilf;
+  using ::ceill;
+
+  using ::floor;
+  using ::floorf;
+  using ::floorl;
+
+  using ::nearbyint;
+  using ::nearbyintf;
+  using ::nearbyintl;
+
+  using ::rint;
+  using ::rintf;
+  using ::rintl;
+
+  using ::lrint;
+  using ::lrintf;
+  using ::lrintl;
+
+  using ::llrint;
+  using ::llrintf;
+  using ::llrintl;
+
+  using ::round;
+  using ::roundf;
+  using ::roundl;
+
+  using ::lround;
+  using ::lroundf;
+  using ::lroundl;
+
+  using ::llround;
+  using ::llroundf;
+  using ::llroundl;
+
+  using ::trunc;
+  using ::truncf;
+  using ::truncl;
+
+  using ::fmod;
+  using ::fmodf;
+  using ::fmodl;
+
+  using ::remainder;
+  using ::remainderf;
+  using ::remainderl;
+
+  using ::remquo;
+  using ::remquof;
+  using ::remquol;
+
+  using ::copysign;
+  using ::copysignf;
+  using ::copysignl;
+
+  using ::nan;
+  using ::nanf;
+  using ::nanl;
+
+  using ::nextafter;
+  using ::nextafterf;
+  using ::nextafterl;
+
+  using ::nexttoward;
+  using ::nexttowardf;
+  using ::nexttowardl;
+
+  using ::fdim;
+  using ::fdimf;
+  using ::fdiml;
+
+  using ::fmax;
+  using ::fmaxf;
+  using ::fmaxl;
+
+  using ::fmin;
+  using ::fminf;
+  using ::fminl;
+
+  using ::fma;
+  using ::fmaf;
+  using ::fmal;
+
+  // [c.math.lerp], linear interpolation
+  // [support.c.headers.other]/1
+  // ...  placed within the global namespace scope, except for the functions
+  // described in [sf.cmath], the std::lerp function overloads ([c.math.lerp])
+  // ...
+
+  // [c.math.fpclass], classification / comparison functions
+  using ::fpclassify;
+  using ::isfinite;
+  using ::isgreater;
+  using ::isgreaterequal;
+  using ::isinf;
+  using ::isless;
+  using ::islessequal;
+  using ::islessgreater;
+  using ::isnan;
+  using ::isnormal;
+  using ::isunordered;
+  using ::signbit;
+
+  // [sf.cmath], mathematical special functions
+} // export
diff --git a/libcxx/modules/std.compat/csetjmp.inc b/libcxx/modules/std.compat/csetjmp.inc
new file mode 100644
index 000000000000000..1fc42ea3ee0378b
--- /dev/null
+++ b/libcxx/modules/std.compat/csetjmp.inc
@@ -0,0 +1,13 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+export {
+  using ::jmp_buf;
+  using ::longjmp;
+} // export
diff --git a/libcxx/modules/std.compat/csignal.inc b/libcxx/modules/std.compat/csignal.inc
new file mode 100644
index 000000000000000..33af6a9f2b7343e
--- /dev/null
+++ b/libcxx/modules/std.compat/csignal.inc
@@ -0,0 +1,17 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+export {
+  using ::sig_atomic_t;
+
+  // [support.signal], signal handlers
+  using ::signal;
+
+  using ::raise;
+} // export
diff --git a/libcxx/modules/std.compat/cstdarg.inc b/libcxx/modules/std.compat/cstdarg.inc
new file mode 100644
index 000000000000000..3efb34617a8bff2
--- /dev/null
+++ b/libcxx/modules/std.compat/cstdarg.inc
@@ -0,0 +1,10 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+export { using ::va_list; } // export
diff --git a/libcxx/modules/std.compat/cstddef.inc b/libcxx/modules/std.compat/cstddef.inc
new file mode 100644
index 000000000000000..94ad036fd8f4a5a
--- /dev/null
+++ b/libcxx/modules/std.compat/cstddef.inc
@@ -0,0 +1,22 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+export {
+  using ::max_align_t;
+  using ::nullptr_t;
+  using ::ptrdiff_t;
+  using ::size_t;
+
+  // [support.c.headers]/1
+  // ...  placed within the global namespace scope, except for ... the
+  // declaration of std::byte ([cstddef.syn]), and the functions and
+  // function templates described in [support.types.byteops]. ...
+
+  // [support.types.byteops], byte type operations
+} // export
diff --git a/libcxx/modules/std.compat/cstdint.inc b/libcxx/modules/std.compat/cstdint.inc
new file mode 100644
index 000000000000000..1a74efc70ceaa1a
--- /dev/null
+++ b/libcxx/modules/std.compat/cstdint.inc
@@ -0,0 +1,50 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+export {
+  // signed
+  using ::int8_t _LIBCPP_USING_IF_EXISTS;
+  using ::int16_t _LIBCPP_USING_IF_EXISTS;
+  using ::int32_t _LIBCPP_USING_IF_EXISTS;
+  using ::int64_t _LIBCPP_USING_IF_EXISTS;
+
+  using ::int_fast16_t;
+  using ::int_fast32_t;
+  using ::int_fast64_t;
+  using ::int_fast8_t;
+
+  using ::int_least16_t;
+  using ::int_least32_t;
+  using ::int_least64_t;
+  using ::int_least8_t;
+
+  using ::intmax_t;
+
+  using ::intptr_t _LIBCPP_USING_IF_EXISTS;
+
+  // unsigned
+  using ::uint8_t _LIBCPP_USING_IF_EXISTS;
+  using ::uint16_t _LIBCPP_USING_IF_EXISTS;
+  using ::uint32_t _LIBCPP_USING_IF_EXISTS;
+  using ::uint64_t _LIBCPP_USING_IF_EXISTS;
+
+  using ::uint_fast16_t;
+  using ::uint_fast32_t;
+  using ::uint_fast64_t;
+  using ::uint_fast8_t;
+
+  using ::uint_least16_t;
+  using ::uint_least32_t;
+  using ::uint_least64_t;
+  using ::uint_least8_t;
+
+  using ::uintmax_t;
+
+  using ::uintptr_t _LIBCPP_USING_IF_EXISTS;
+} // export
diff --git a/libcxx/modules/std.compat/cstdio.inc b/libcxx/modules/std.compat/cstdio.inc
new file mode 100644
index 000000000000000..1ec3015c9e2a2e8
--- /dev/null
+++ b/libcxx/modules/std.compat/cstdio.inc
@@ -0,0 +1,61 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+export {
+  using ::FILE;
+  using ::fpos_t;
+  using ::size_t;
+
+  using ::clearerr;
+  using ::fclose;
+  using ::feof;
+  using ::ferror;
+  using ::fflush;
+  using ::fgetc;
+  using ::fgetpos;
+  using ::fgets;
+  using ::fopen;
+  using ::fprintf;
+  using ::fputc;
+  using ::fputs;
+  using ::fread;
+  using ::freopen;
+  using ::fscanf;
+  using ::fseek;
+  using ::fsetpos;
+  using ::ftell;
+  using ::fwrite;
+  using ::getc;
+  using ::getchar;
+  using ::perror;
+  using ::printf;
+  using ::putc;
+  using ::putchar;
+  using ::puts;
+  using ::remove;
+  using ::rename;
+  using ::rewind;
+  using ::scanf;
+  using ::setbuf;
+  using ::setvbuf;
+  using ::snprintf;
+  using ::sprintf;
+  using ::sscanf;
+  using ::tmpfile;
+  using ::tmpnam;
+  using ::ungetc;
+  using ::vfprintf;
+  using ::vfscanf;
+  using ::vprintf;
+  using ::vscanf;
+  using ::vsnprintf;
+  using ::vsprintf;
+  using ::vsscanf;
+
+} // export
diff --git a/libcxx/modules/std.compat/cstdlib.inc b/libcxx/modules/std.compat/cstdlib.inc
new file mode 100644
index 000000000000000..9333d848707103d
--- /dev/null
+++ b/libcxx/modules/std.compat/cstdlib.inc
@@ -0,0 +1,72 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+export {
+  using ::div_t;
+  using ::ldiv_t;
+  using ::lldiv_t;
+  using ::size_t;
+
+  // [support.start.term], start and termination
+  using ::_Exit;
+  using ::abort;
+  using ::at_quick_exit;
+  using ::atexit;
+  using ::exit;
+  using ::quick_exit;
+
+  using ::getenv;
+  using ::system;
+
+  // [c.malloc], C library memory allocation
+  using ::aligned_alloc;
+  using ::calloc;
+  using ::free;
+  using ::malloc;
+  using ::realloc;
+
+  using ::atof;
+  using ::atoi;
+  using ::atol;
+  using ::atoll;
+  using ::strtod;
+  using ::strtof;
+  using ::strtol;
+  using ::strtold;
+  using ::strtoll;
+  using ::strtoul;
+  using ::strtoull;
+
+  // [c.mb.wcs], multibyte / wide string and character conversion functions
+  using ::mblen;
+#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+  using ::mbstowcs;
+  using ::mbtowc;
+  using ::wcstombs;
+  using ::wctomb;
+#endif
+  // [alg.c.library], C standard library algorithms
+  using ::bsearch;
+  using ::qsort;
+
+  // [c.math.rand], low-quality random number generation
+  using ::rand;
+  using ::srand;
+
+  // [c.math.abs], absolute values
+  using ::abs;
+
+  using ::labs;
+  using ::llabs;
+
+  using ::div;
+  using ::ldiv;
+  using ::lldiv;
+
+} // export
diff --git a/libcxx/modules/std.compat/cstring.inc b/libcxx/modules/std.compat/cstring.inc
new file mode 100644
index 000000000000000..090350ae814786a
--- /dev/null
+++ b/libcxx/modules/std.compat/cstring.inc
@@ -0,0 +1,36 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+export {
+  using ::size_t;
+
+  using ::memchr;
+  using ::memcmp;
+  using ::memcpy;
+  using ::memmove;
+  using ::memset;
+  using ::strcat;
+  using ::strchr;
+  using ::strcmp;
+  using ::strcoll;
+  using ::strcpy;
+  using ::strcspn;
+  using ::strerror;
+  using ::strlen;
+  using ::strncat;
+  using ::strncmp;
+  using ::strncpy;
+  using ::strpbrk;
+  using ::strrchr;
+  using ::strspn;
+  using ::strstr;
+  using ::strtok;
+  using ::strxfrm;
+
+} // export
diff --git a/libcxx/modules/std.compat/ctime.inc b/libcxx/modules/std.compat/ctime.inc
new file mode 100644
index 000000000000000..92e3403a5e58e27
--- /dev/null
+++ b/libcxx/modules/std.compat/ctime.inc
@@ -0,0 +1,28 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+export {
+  using ::clock_t;
+  using ::size_t;
+  using ::time_t;
+
+  using ::timespec;
+  using ::tm;
+
+  using ::asctime;
+  using ::clock;
+  using ::ctime;
+  using ::difftime;
+  using ::gmtime;
+  using ::localtime;
+  using ::mktime;
+  using ::strftime;
+  using ::time;
+  using ::timespec_get;
+} // export
diff --git a/libcxx/modules/std.compat/cuchar.inc b/libcxx/modules/std.compat/cuchar.inc
new file mode 100644
index 000000000000000..d1a511cadef1846
--- /dev/null
+++ b/libcxx/modules/std.compat/cuchar.inc
@@ -0,0 +1,28 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+export {
+  // Note the Standard does not mark these symbols optional, but libc++'s header
+  // does. So this seems strictly not to be conforming.
+
+  // mbstate_t is conditionally here, but always present in cwchar.cppm. To avoid
+  // conflicing declarations omit the using here.
+
+  // size_t is conditionally here, but always present in cstddef.cppm. To avoid
+  // conflicing declarations omit the using here.
+
+#if !defined(_LIBCPP_HAS_NO_C8RTOMB_MBRTOC8)
+  using ::mbrtoc8 _LIBCPP_USING_IF_EXISTS;
+  using ::c8rtomb _LIBCPP_USING_IF_EXISTS;
+#endif
+  using ::mbrtoc16 _LIBCPP_USING_IF_EXISTS;
+  using ::c16rtomb _LIBCPP_USING_IF_EXISTS;
+  using ::mbrtoc32 _LIBCPP_USING_IF_EXISTS;
+  using ::c32rtomb _LIBCPP_USING_IF_EXISTS;
+} // export
diff --git a/libcxx/modules/std.compat/cwchar.inc b/libcxx/modules/std.compat/cwchar.inc
new file mode 100644
index 000000000000000..8905aecbdfecc2a
--- /dev/null
+++ b/libcxx/modules/std.compat/cwchar.inc
@@ -0,0 +1,80 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+export {
+#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+  using ::mbstate_t;
+  using ::size_t;
+  using ::wint_t;
+
+  using ::tm;
+
+  using ::btowc;
+  using ::fgetwc;
+  using ::fgetws;
+  using ::fputwc;
+  using ::fputws;
+  using ::fwide;
+  using ::fwprintf;
+  using ::fwscanf;
+  using ::getwc;
+  using ::getwchar;
+  using ::putwc;
+  using ::putwchar;
+  using ::swprintf;
+  using ::swscanf;
+  using ::ungetwc;
+  using ::vfwprintf;
+  using ::vfwscanf;
+  using ::vswprintf;
+  using ::vswscanf;
+  using ::vwprintf;
+  using ::vwscanf;
+  using ::wcscat;
+  using ::wcschr;
+  using ::wcscmp;
+  using ::wcscoll;
+  using ::wcscpy;
+  using ::wcscspn;
+  using ::wcsftime;
+  using ::wcslen;
+  using ::wcsncat;
+  using ::wcsncmp;
+  using ::wcsncpy;
+  using ::wcspbrk;
+  using ::wcsrchr;
+  using ::wcsspn;
+  using ::wcsstr;
+  using ::wcstod;
+  using ::wcstof;
+  using ::wcstok;
+  using ::wcstol;
+  using ::wcstold;
+  using ::wcstoll;
+  using ::wcstoul;
+  using ::wcstoull;
+  using ::wcsxfrm;
+  using ::wctob;
+  using ::wmemchr;
+  using ::wmemcmp;
+  using ::wmemcpy;
+  using ::wmemmove;
+  using ::wmemset;
+  using ::wprintf;
+  using ::wscanf;
+
+  // [c.mb.wcs], multibyte / wide string and character conversion functions
+  using ::mbrlen;
+  using ::mbrtowc;
+  using ::mbsinit;
+  using ::mbsrtowcs;
+  using ::wcrtomb;
+  using ::wcsrtombs;
+#endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS
+} // export
diff --git a/libcxx/modules/std.compat/cwctype.inc b/libcxx/modules/std.compat/cwctype.inc
new file mode 100644
index 000000000000000..13aa2b7f3fb7461
--- /dev/null
+++ b/libcxx/modules/std.compat/cwctype.inc
@@ -0,0 +1,35 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+export {
+#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+  using ::wctrans_t;
+  using ::wctype_t;
+  using ::wint_t;
+
+  using ::iswalnum;
+  using ::iswalpha;
+  using ::iswblank;
+  using ::iswcntrl;
+  using ::iswctype;
+  using ::iswdigit;
+  using ::iswgraph;
+  using ::iswlower;
+  using ::iswprint;
+  using ::iswpunct;
+  using ::iswspace;
+  using ::iswupper;
+  using ::iswxdigit;
+  using ::towctrans;
+  using ::towlower;
+  using ::towupper;
+  using ::wctrans;
+  using ::wctype;
+#endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS
+} // export
diff --git a/libcxx/modules/std.cppm.in b/libcxx/modules/std.cppm.in
index 65cec804ff122ae..0d97501e0514c88 100644
--- a/libcxx/modules/std.cppm.in
+++ b/libcxx/modules/std.cppm.in
@@ -8,7 +8,7 @@
 //===----------------------------------------------------------------------===//
 
 // WARNING, this entire header is generated by
-// utils/generate_std_cppm_in.py
+// utils/generate_libcxx_cppm_in.py
 // DO NOT MODIFY!
 
 module;
@@ -166,35 +166,35 @@ module;
 
 // *** Headers not yet available ***
 #if __has_include(<flat_map>)
-#  error "update the header information for <flat_map> in libcxx/utils/generate_std_cppm_in.py"
-#endif //   __has_include(<flat_map>)
+#  error "update the header information for <flat_map> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<flat_map>)
 #if __has_include(<flat_set>)
-#  error "update the header information for <flat_set> in libcxx/utils/generate_std_cppm_in.py"
-#endif //   __has_include(<flat_set>)
+#  error "update the header information for <flat_set> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<flat_set>)
 #if __has_include(<generator>)
-#  error "update the header information for <generator> in libcxx/utils/generate_std_cppm_in.py"
-#endif //   __has_include(<generator>)
+#  error "update the header information for <generator> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<generator>)
 #if __has_include(<hazard_pointer>)
-#  error "update the header information for <hazard_pointer> in libcxx/utils/generate_std_cppm_in.py"
-#endif //   __has_include(<hazard_pointer>)
+#  error "update the header information for <hazard_pointer> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<hazard_pointer>)
 #if __has_include(<rcu>)
-#  error "update the header information for <rcu> in libcxx/utils/generate_std_cppm_in.py"
-#endif //   __has_include(<rcu>)
+#  error "update the header information for <rcu> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<rcu>)
 #if __has_include(<spanstream>)
-#  error "update the header information for <spanstream> in libcxx/utils/generate_std_cppm_in.py"
-#endif //   __has_include(<spanstream>)
+#  error "update the header information for <spanstream> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<spanstream>)
 #if __has_include(<stacktrace>)
-#  error "update the header information for <stacktrace> in libcxx/utils/generate_std_cppm_in.py"
-#endif //   __has_include(<stacktrace>)
+#  error "update the header information for <stacktrace> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<stacktrace>)
 #if __has_include(<stdfloat>)
-#  error "update the header information for <stdfloat> in libcxx/utils/generate_std_cppm_in.py"
-#endif //   __has_include(<stdfloat>)
+#  error "update the header information for <stdfloat> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<stdfloat>)
 #if __has_include(<syncstream>)
-#  error "update the header information for <syncstream> in libcxx/utils/generate_std_cppm_in.py"
-#endif //   __has_include(<syncstream>)
+#  error "update the header information for <syncstream> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<syncstream>)
 #if __has_include(<text_encoding>)
-#  error "update the header information for <text_encoding> in libcxx/utils/generate_std_cppm_in.py"
-#endif //   __has_include(<text_encoding>)
+#  error "update the header information for <text_encoding> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<text_encoding>)
 
 export module std;
 
diff --git a/libcxx/test/libcxx/module_std.gen.py b/libcxx/test/libcxx/module_std.gen.py
index d8198cc7058379e..8e03d6e5b5b5235 100644
--- a/libcxx/test/libcxx/module_std.gen.py
+++ b/libcxx/test/libcxx/module_std.gen.py
@@ -21,241 +21,17 @@
 import sys
 
 sys.path.append(sys.argv[1])
-from libcxx.header_information import module_headers
-from libcxx.header_information import header_restrictions
-
-BLOCKLIT = (
-    ""  # block Lit from interpreting a RUN/XFAIL/etc inside the generation script
+from libcxx.test.modules import module_test_generator
+
+generator = module_test_generator(
+    "%t",
+    "%{module}",
+    "%{clang-tidy}",
+    "%{test-tools}/clang_tidy_checks/libcxx-tidy.plugin",
+    "%{cxx}",
+    "%{flags} %{compile_flags}",
 )
 
-# Ignore several declarations found in the includes.
-#
-# Part of these items are bugs other are not yet implemented features.
-SkipDeclarations = dict()
-
-# See comment in the header.
-SkipDeclarations["cuchar"] = ["std::mbstate_t", "std::size_t"]
-
-# Not in the synopsis.
-SkipDeclarations["cwchar"] = ["std::FILE"]
-
-# The operators are added for private types like __iom_t10.
-SkipDeclarations["iomanip"] = ["std::operator<<", "std::operator>>"]
-
-SkipDeclarations["iosfwd"] = ["std::ios_base", "std::vector"]
-
-# This header also provides declarations in the namespace that might be
-# an error.
-SkipDeclarations["filesystem"] = [
-    "std::filesystem::operator==",
-    "std::filesystem::operator!=",
-]
-
-# This is a specialization for a private type
-SkipDeclarations["iterator"] = ["std::pointer_traits"]
-
-# TODO MODULES
-# This definition is declared in string and defined in istream
-# This declaration should be part of string
-SkipDeclarations["istream"] = ["std::getline"]
-
-# P1614 (at many places) and LWG3519 too.
-SkipDeclarations["random"] = [
-    "std::operator!=",
-    # LWG3519 makes these hidden friends.
-    # Note the older versions had the requirement of these operations but not in
-    # the synopsis.
-    "std::operator<<",
-    "std::operator>>",
-    "std::operator==",
-]
-
-# Declared in the forward header since std::string uses std::allocator
-SkipDeclarations["string"] = ["std::allocator"]
-# TODO MODULES remove zombie names
-# https://libcxx.llvm.org/Status/Cxx20.html#note-p0619
-SkipDeclarations["memory"] = [
-    "std::return_temporary_buffer",
-    "std::get_temporary_buffer",
-]
-
-# TODO MODULES this should be part of ios instead
-SkipDeclarations["streambuf"] = ["std::basic_ios"]
-
-# include/__type_traits/is_swappable.h
-SkipDeclarations["type_traits"] = [
-    "std::swap",
-    # TODO MODULES gotten through __functional/unwrap_ref.h
-    "std::reference_wrapper",
-]
-
-# Add declarations in headers.
-#
-# Some headers have their defines in a different header, which may have
-# additional declarations.
-ExtraDeclarations = dict()
-# This declaration is in the ostream header.
-ExtraDeclarations["system_error"] = ["std::operator<<"]
-
-# Adds an extra header file to scan
-#
-#
-ExtraHeader = dict()
-# locale has a file and not a subdirectory
-ExtraHeader["locale"] = "v1/__locale$"
-ExtraHeader["thread"] = "v1/__threading_support$"
-ExtraHeader["ranges"] = "v1/__fwd/subrange.h$"
-
-# The extra header is needed since two headers are required to provide the
-# same definition.
-ExtraHeader["functional"] = "v1/__compare/compare_three_way.h$"
-
-# newline needs to be escaped for the module partition output.
-nl = '\\\\n'
-
-# Create empty file with all parts.
-print(
-    f"""\
-//--- module_std.sh.cpp
-// UNSUPPORTED{BLOCKLIT}: c++03, c++11, c++14, c++17
-// UNSUPPORTED{BLOCKLIT}: libcpp-has-no-std-modules
-// UNSUPPORTED{BLOCKLIT}: clang-modules-build
-
-// REQUIRES{BLOCKLIT}: has-clang-tidy
-
-// The GCC compiler flags are not always compatible with clang-tidy.
-// UNSUPPORTED{BLOCKLIT}: gcc
-
-// RUN{BLOCKLIT}: echo -n > %t.all_partitions
-"""
-)
-
-# Validate all module parts.
-for header in module_headers:
-    # Some headers cannot be included when a libc++ feature is disabled.
-    # In that case include the header conditionally. The header __config
-    # ensures the libc++ feature macros are available.
-    if header in header_restrictions:
-        include = (
-            f"#include <__config>{nl}"
-            + f"#if {header_restrictions[header]}{nl}"
-            + f"#  include <{header}>{nl}"
-            + f"#endif{nl}"
-        )
-    elif header == "chrono":
-        # When localization is disabled the header string is not included.
-        # When string is included chrono's operator""s is a named declaration
-        #   using std::chrono_literals::operator""s;
-        # else it is a named declaration
-        #   using std::operator""s;
-        # TODO MODULES investigate why
-        include = f"#include <string>{nl}#include <chrono>{nl}"
-    else:
-        include = f"#include <{header}>{nl}"
-
-    # Generate a module partition for the header module includes. This
-    # makes it possible to verify that all headers export all their
-    # named declarations.
-    print(
-        f"// RUN{BLOCKLIT}: echo -e \""
-        f"module;{nl}"
-        f"{include}"
-        f"{nl}"
-        f"// Use __libcpp_module_<HEADER> to ensure that modules {nl}"
-        f"// are not named as keywords or reserved names.{nl}"
-        f"export module std:__libcpp_module_{header};{nl}"
-        f'#include \\"%{{module}}/std/{header}.inc\\"{nl}'
-        f"\" > %t.{header}.cppm")
-
-    # Dump the information as found in the module's cppm file.
-    print(
-        f"// RUN{BLOCKLIT}: %{{clang-tidy}} %t.{header}.cppm "
-        "  --checks='-*,libcpp-header-exportable-declarations' "
-        "  -config='{CheckOptions: [ "
-        "    {"
-        "      key: libcpp-header-exportable-declarations.Filename, "
-        f"     value: {header}.inc"
-        "    }, {"
-        "      key: libcpp-header-exportable-declarations.FileType, "
-        "      value: ModulePartition"
-        "    }, "
-        "  ]}' "
-        "  --load=%{test-tools}/clang_tidy_checks/libcxx-tidy.plugin "
-        "  -- %{flags} %{compile_flags} "
-        f"| sort > %t.{header}.module"
-    )
-    print(f"// RUN{BLOCKLIT}: cat  %t.{header}.module >> %t.all_partitions")
-
-    # Dump the information as found in the module by using the header file(s).
-    skip_declarations = " ".join(SkipDeclarations.get(header, []))
-    if skip_declarations:
-        skip_declarations = (
-            "{"
-            "  key: libcpp-header-exportable-declarations.SkipDeclarations, "
-            f' value: "{skip_declarations}" '
-            "}, "
-        )
-
-    extra_declarations = " ".join(ExtraDeclarations.get(header, []))
-    if extra_declarations:
-        extra_declarations = (
-            " {"
-            "  key: libcpp-header-exportable-declarations.ExtraDeclarations, "
-            f' value: "{extra_declarations}" '
-            "}, "
-        )
-
-    extra_header = ExtraHeader.get(header, "")
-    if extra_header:
-        extra_header = (
-            "{"
-            "  key: libcpp-header-exportable-declarations.ExtraHeader, "
-            f' value: "{extra_header}" '
-            "}, "
-        )
-
-    # Clang-tidy needs a file input
-    print(f'// RUN{BLOCKLIT}: echo -e "' f"{include}" f'" > %t.{header}.cpp')
-    print(
-        f"// RUN{BLOCKLIT}: %{{clang-tidy}} %t.{header}.cpp "
-        "  --checks='-*,libcpp-header-exportable-declarations' "
-        "  -config='{CheckOptions: [ "
-        f"   {{key: libcpp-header-exportable-declarations.Filename, value: {header}}}, "
-        "    {key: libcpp-header-exportable-declarations.FileType, value: Header}, "
-        f"   {skip_declarations} {extra_declarations} {extra_header}, "
-        "  ]}' "
-        "  --load=%{test-tools}/clang_tidy_checks/libcxx-tidy.plugin "
-        "  -- %{flags} %{compile_flags} "
-        f" | sort > %t.{header}.include"
-    )
-
-    # Compare the cppm and header file(s) return the same results.
-    print(f"// RUN{BLOCKLIT}: diff -u %t.{header}.module %t.{header}.include")
-
-
-# Merge the data of the parts
-print(f"// RUN{BLOCKLIT}: sort -u -o %t.all_partitions %t.all_partitions")
-
-# Dump the information as found in std.cppm.
-print(
-    f"// RUN{BLOCKLIT}: %{{clang-tidy}} %{{module}}/std.cppm "
-    "  --checks='-*,libcpp-header-exportable-declarations' "
-    "  -config='{CheckOptions: [ "
-    "    {key: libcpp-header-exportable-declarations.Header, value: std.cppm}, "
-    "    {key: libcpp-header-exportable-declarations.FileType, value: Module}, "
-    "  ]}' "
-    f" --load=%{{test-tools}}/clang_tidy_checks/libcxx-tidy.plugin "
-    "  -- %{flags} %{compile_flags} "
-    "  | sort > %t.module"
-)
-
-
-# Compare the sum of the parts with the main module.
-print(f"// RUN{BLOCKLIT}: diff -u %t.all_partitions %t.module")
 
-# Basic smoke test. Import a module and try to compile when using all
-# exported names. This validates the clang-tidy script does not accidentally
-# add named declarations to the list that are not available.
-print(f"// RUN{BLOCKLIT}: echo 'import std;' > %t.compile.pass.cpp")
-print(f"// RUN{BLOCKLIT}: cat %t.all_partitions >> %t.compile.pass.cpp")
-print(f"// RUN{BLOCKLIT}: %{{cxx}} %{{flags}} %{{compile_flags}} -fsyntax-only %t.compile.pass.cpp")
+print("//--- module_std.sh.cpp")
+generator.write_test("std")
diff --git a/libcxx/test/libcxx/module_std_compat.gen.py b/libcxx/test/libcxx/module_std_compat.gen.py
new file mode 100644
index 000000000000000..c4792db3d71e624
--- /dev/null
+++ b/libcxx/test/libcxx/module_std_compat.gen.py
@@ -0,0 +1,62 @@
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+# Test that all named declarations with external linkage match the
+# exported declarations in their associated module partition.
+# Then it tests the sum of the exported declarations in the module
+# partitions matches the export of the std.compat module.
+
+# Note the test of the std.compat module requires all partitions to be tested
+# first. Since lit tests have no dependencies, this means the test needs
+# to be one monolitic test. Since the test doesn't take very long it's
+# not a huge issue.
+
+# RUN: %{python} %s %{libcxx}/utils
+
+import sys
+
+sys.path.append(sys.argv[1])
+from libcxx.test.modules import module_test_generator
+
+generator = module_test_generator(
+    "%t",
+    "%{module}",
+    "%{clang-tidy}",
+    "%{test-tools}/clang_tidy_checks/libcxx-tidy.plugin",
+    "%{cxx}",
+    "%{flags} %{compile_flags}",
+)
+
+
+print("//--- module_std_compat.sh.cpp")
+generator.write_test(
+    "std.compat",
+    [
+        "cassert",
+        "cctype",
+        "cerrno",
+        "cfenv",
+        "cfloat",
+        "cinttypes",
+        "climits",
+        "clocale",
+        "cmath",
+        "csetjmp",
+        "csignal",
+        "cstdarg",
+        "cstddef",
+        "cstdint",
+        "cstdio",
+        "cstdlib",
+        "cstring",
+        "ctime",
+        "cuchar",
+        "cwchar",
+        "cwctype",
+    ],
+)
diff --git a/libcxx/test/lit.local.cfg b/libcxx/test/lit.local.cfg
index 4116553b6f7a9ae..1ee9086ee22e3c5 100644
--- a/libcxx/test/lit.local.cfg
+++ b/libcxx/test/lit.local.cfg
@@ -67,3 +67,17 @@ if (
         "%{link_flags}",
         os.path.join(build, "libc++std.a"),
     )
+
+    config.substitutions = appendToSubstitution(
+        config.substitutions,
+        "%{compile_flags}",
+        "-fprebuilt-module-path="
+        + os.path.join(
+            config.test_exec_root, "__config_module__/CMakeFiles/std.compat.dir"
+        ),
+    )
+    config.substitutions = appendToSubstitution(
+        config.substitutions,
+        "%{link_flags}",
+        os.path.join(build, "libc++std.compat.a"),
+    )
diff --git a/libcxx/test/std/modules/std.compat.pass.cpp b/libcxx/test/std/modules/std.compat.pass.cpp
new file mode 100644
index 000000000000000..a302d16d63540f0
--- /dev/null
+++ b/libcxx/test/std/modules/std.compat.pass.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// UNSUPPORTED: libcpp-has-no-std-modules
+// UNSUPPORTED: clang-modules-build
+
+// A minimal test to validate import works.
+
+import std.compat;
+
+int main(int, char**) {
+  ::printf("Hello modular world");
+  return 0;
+}
diff --git a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
index fba48d5984ea775..debc841fc5c4176 100644
--- a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
@@ -26,7 +26,10 @@ struct clang::tidy::OptionEnumMapping<libcpp::header_exportable_declarations::Fi
     static constexpr std::pair<libcpp::header_exportable_declarations::FileType, llvm::StringRef> Mapping[] = {
         {libcpp::header_exportable_declarations::FileType::Header, "Header"},
         {libcpp::header_exportable_declarations::FileType::ModulePartition, "ModulePartition"},
-        {libcpp::header_exportable_declarations::FileType::Module, "Module"}};
+        {libcpp::header_exportable_declarations::FileType::Module, "Module"},
+        {libcpp::header_exportable_declarations::FileType::CHeader, "CHeader"},
+        {libcpp::header_exportable_declarations::FileType::CompatModulePartition, "CompatModulePartition"},
+        {libcpp::header_exportable_declarations::FileType::CompatModule, "CompatModule"}};
     return ArrayRef(Mapping);
   }
 };
@@ -39,6 +42,7 @@ header_exportable_declarations::header_exportable_declarations(
       file_type_(Options.get("FileType", header_exportable_declarations::FileType::Unknown)),
       extra_header_(Options.get("ExtraHeader", "")) {
   switch (file_type_) {
+  case header_exportable_declarations::FileType::CHeader:
   case header_exportable_declarations::FileType::Header:
     if (filename_.empty())
       llvm::errs() << "No filename is provided.\n";
@@ -46,10 +50,12 @@ header_exportable_declarations::header_exportable_declarations(
       extra_header_ = "$^"; // Use a never matching regex to silence an error message.
     break;
   case header_exportable_declarations::FileType::ModulePartition:
+  case header_exportable_declarations::FileType::CompatModulePartition:
     if (filename_.empty())
       llvm::errs() << "No filename is provided.\n";
     [[fallthrough]];
   case header_exportable_declarations::FileType::Module:
+  case header_exportable_declarations::FileType::CompatModule:
     if (!extra_header_.empty())
       llvm::errs() << "Extra headers are not allowed for modules.\n";
     if (Options.get("SkipDeclarations"))
@@ -70,7 +76,7 @@ header_exportable_declarations::header_exportable_declarations(
     auto b             = s.begin();
     auto e             = std::find(b, s.end(), ' ');
     while (b != e) {
-      decls_.emplace(b, e);
+      skip_decls_.emplace(b, e);
       if (e == s.end())
         break;
       b = e + 1;
@@ -82,9 +88,10 @@ header_exportable_declarations::header_exportable_declarations(
     for (auto decl : std::views::split(*list, ' ')) {
       std::string s;
       std::ranges::copy(decl, std::back_inserter(s)); // use range based constructor
-      decls_.emplace(std::move(s));
+      skip_decls_.emplace(std::move(s));
     }
 #endif // defined(__clang_major__) && __clang_major__ < 16
+  decls_ = skip_decls_;
 
   list = Options.get("ExtraDeclarations");
   // TODO(LLVM-17) Remove clang 15 work-around.
@@ -94,7 +101,7 @@ header_exportable_declarations::header_exportable_declarations(
     auto b             = s.begin();
     auto e             = std::find(b, s.end(), ' ');
     while (b != e) {
-      std::cout << "using " << std::string_view{b, e} << ";\n";
+      std::cout << "using ::" << std::string_view{b, e} << ";\n";
       if (e == s.end())
         break;
       b = e + 1;
@@ -104,10 +111,16 @@ header_exportable_declarations::header_exportable_declarations(
 #else  // defined(__clang_major__) && __clang_major__ < 16
   if (list)
     for (auto decl : std::views::split(*list, ' '))
-      std::cout << "using " << std::string_view{decl.data(), decl.size()} << ";\n";
+      std::cout << "using ::" << std::string_view{decl.data(), decl.size()} << ";\n";
 #endif // defined(__clang_major__) && __clang_major__ < 16
 }
 
+header_exportable_declarations::~header_exportable_declarations() {
+  for (const auto& name : global_decls_)
+    if (!skip_decls_.contains("std::" + name) && decls_.contains("std::" + name))
+      std::cout << "using ::" << name << ";\n";
+}
+
 void header_exportable_declarations::registerMatchers(clang::ast_matchers::MatchFinder* finder) {
   // there are no public names in the Standard starting with an underscore, so
   // no need to check the strict rules.
@@ -129,10 +142,22 @@ void header_exportable_declarations::registerMatchers(clang::ast_matchers::Match
             .bind("header_exportable_declarations"),
         this);
     break;
+  case FileType::CHeader:
+    // For C headers of the std.compat two matchers are used
+    // - The cheader matcher; in libc++ these are never split in multiple
+    //   headers so limiting the declarations to that header works.
+    // - The header.h; where the declarations of this header are provided
+    //   is not specified and depends on the libc used. Therefore it is not
+    //   possible to restrict the location in a portable way.
+    finder->addMatcher(namedDecl().bind("cheader_exportable_declarations"), this);
+
+    [[fallthrough]];
   case FileType::ModulePartition:
+  case FileType::CompatModulePartition:
     finder->addMatcher(namedDecl(isExpansionInFileMatching(filename_)).bind("header_exportable_declarations"), this);
     break;
   case FileType::Module:
+  case FileType::CompatModule:
     finder->addMatcher(namedDecl().bind("header_exportable_declarations"), this);
     break;
   case header_exportable_declarations::FileType::Unknown:
@@ -156,6 +181,9 @@ void header_exportable_declarations::registerMatchers(clang::ast_matchers::Match
 /// * cstddef has bitwise operators for the type \c byte
 /// * exception has equality operators for the type \c exception_ptr
 /// * initializer_list has the functions \c begin and \c end
+///
+/// \warning In some cases the returned name can be an empty string.
+/// The cause has not been investigated.
 static std::string get_qualified_name(const clang::NamedDecl& decl) {
   std::string result = decl.getQualifiedNameAsString();
 
@@ -166,10 +194,6 @@ static std::string get_qualified_name(const clang::NamedDecl& decl) {
 }
 
 static bool is_viable_declaration(const clang::NamedDecl* decl) {
-  // Declarations nested in records are automatically exported with the record itself.
-  if (!decl->getDeclContext()->isNamespace())
-    return false;
-
   // Declarations that are a subobject of a friend Declaration are automatically exported with the record itself.
   if (decl->getFriendObjectKind() != clang::Decl::FOK_None)
     return false;
@@ -199,13 +223,16 @@ static bool is_viable_declaration(const clang::NamedDecl* decl) {
 /// Returns the name is a reserved name.
 ///
 /// Detected reserved names are names starting with __ or _[A-Z].
-/// These names can be in the namespace std or any namespace inside std. For
-/// example std::ranges contains reserved names to implement the Niebloids.
+/// These names can be in the global namespace, std namespace or any namespace
+/// inside std. For example, std::ranges contains reserved names to implement
+/// the Niebloids.
 ///
-/// This test misses 2 candidates which are not used in libc++
+/// This test misses candidates which are not used in libc++
 /// * any identifier with two underscores not at the start
-/// * a name with a leading underscore in the global namespace
-bool is_reserved_name(const std::string& name) {
+bool is_reserved_name(std::string_view name) {
+  if (name.starts_with("_"))
+    return name.size() > 1 && (name[1] == '_' || std::isupper(name[1]));
+
   std::size_t pos = name.find("::_");
   if (pos == std::string::npos)
     return false;
@@ -213,27 +240,73 @@ bool is_reserved_name(const std::string& name) {
   if (pos + 3 > name.size())
     return false;
 
+  // This is a public name declared in cstdlib.
+  if (name == "std::_Exit")
+    return false;
+
   return name[pos + 3] == '_' || std::isupper(name[pos + 3]);
 }
 
+/// Some declarations in the global namespace are exported from the std module.
+static bool is_valid_global_name(std::string_view name) {
+  static const std::set<std::string_view> valid{
+      "operator delete", "operator delete[]", "operator new", "operator new[]"};
+  return valid.contains(name);
+}
+
+static bool is_valid_declaration_context(
+    const clang::NamedDecl& decl, std::string_view name, header_exportable_declarations::FileType file_type) {
+  if (decl.getDeclContext()->isNamespace())
+    return true;
+
+  if (is_valid_global_name(name))
+    return true;
+
+  return file_type != header_exportable_declarations::FileType::Header;
+}
+
+static bool is_module(header_exportable_declarations::FileType file_type) {
+  switch (file_type) {
+  case header_exportable_declarations::FileType::Module:
+  case header_exportable_declarations::FileType::ModulePartition:
+  case header_exportable_declarations::FileType::CompatModule:
+  case header_exportable_declarations::FileType::CompatModulePartition:
+    return true;
+
+  case header_exportable_declarations::FileType::Header:
+  case header_exportable_declarations::FileType::CHeader:
+    return false;
+
+  case header_exportable_declarations::FileType::Unknown:
+    llvm::errs() << "This should be unreachable.\n";
+    break;
+  }
+}
+
 void header_exportable_declarations::check(const clang::ast_matchers::MatchFinder::MatchResult& result) {
   if (const auto* decl = result.Nodes.getNodeAs<clang::NamedDecl>("header_exportable_declarations"); decl != nullptr) {
     if (!is_viable_declaration(decl))
       return;
 
     std::string name = get_qualified_name(*decl);
+    if (name.empty())
+      return;
+
     if (is_reserved_name(name))
       return;
 
     // For modules only take the declarations exported.
-    if (file_type_ == FileType::ModulePartition || file_type_ == FileType::Module)
+    if (is_module(file_type_))
       if (decl->getModuleOwnershipKind() != clang::Decl::ModuleOwnershipKind::VisibleWhenImported)
         return;
 
+    if (!is_valid_declaration_context(*decl, name, file_type_))
+      return;
+
     if (decls_.contains(name)) {
       // For modules avoid exporting the same named declaration twice. For
       // header files this is common and valid.
-      if (file_type_ == FileType::ModulePartition)
+      if (file_type_ == FileType::ModulePartition || file_type_ == FileType::CompatModulePartition)
         // After the warning the script continues.
         // The test will fail since modules have duplicated entries and headers not.
         llvm::errs() << "Duplicated export of '" << name << "'.\n";
@@ -241,8 +314,30 @@ void header_exportable_declarations::check(const clang::ast_matchers::MatchFinde
         return;
     }
 
-    std::cout << "using " << std::string{name} << ";\n";
+    // For named declarations in std this is valid
+    //   using std::foo;
+    // for named declarations it is invalid to use
+    //   using bar;
+    // Since fully qualifying named declarations in the std namespace is valid
+    // using fully qualified names unconditionally.
+    std::cout << "using ::" << std::string{name} << ";\n";
     decls_.insert(name);
+  } else if (const auto* decl = result.Nodes.getNodeAs<clang::NamedDecl>("cheader_exportable_declarations");
+             decl != nullptr) {
+    if (decl->getDeclContext()->isNamespace())
+      return;
+
+    if (!is_viable_declaration(decl))
+      return;
+
+    std::string name = get_qualified_name(*decl);
+    if (is_reserved_name(name))
+      return;
+
+    if (global_decls_.contains(name))
+      return;
+
+    global_decls_.insert(name);
   }
 }
 
diff --git a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.hpp b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.hpp
index 119bcd3f6f2fe5a..0c97aade6b99110 100644
--- a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.hpp
+++ b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.hpp
@@ -18,15 +18,47 @@ namespace libcpp {
 class header_exportable_declarations : public clang::tidy::ClangTidyCheck {
 public:
   explicit header_exportable_declarations(llvm::StringRef, clang::tidy::ClangTidyContext*);
+  ~header_exportable_declarations();
   void registerMatchers(clang::ast_matchers::MatchFinder*) override;
   void check(const clang::ast_matchers::MatchFinder::MatchResult&) override;
 
-  enum class FileType { Header, ModulePartition, Module, Unknown };
+  enum class FileType {
+    // std module specific
+    Header,
+    CompatModulePartition,
+    Module,
+    // std.compat module specific
+    CHeader,
+    ModulePartition,
+    CompatModule,
+    // invalid value
+    Unknown
+  };
 
 private:
   llvm::StringRef filename_;
   FileType file_type_;
   llvm::StringRef extra_header_;
   std::set<std::string> decls_;
+
+  // The named declarations in .h C headers are "tricky". On POSIX systems
+  // these headers contain POSIX specific functions that do not use a reserved
+  // name. For example, fmemopen is provided by stdio.h.
+  // The way to filter the names that should be provided by the headers is the
+  // following:
+  // - record all named declarations the global namespace
+  // - wait until the header is completely processed
+  // - every named declaration in the global namespace that has a matching
+  //   "export" in the std namespace is exported.
+  //
+  // The only possible place where to process after the header is processed is
+  // the class' destructor.
+  //
+  // It is possible to skip some declarations in the std namespace, these are
+  // added to decls_ before processing. To differenciate between a skipped
+  // declaration and a real declaration the skipped declarations are recorded
+  // in an extra variable.
+  std::set<std::string> skip_decls_;
+  std::set<std::string> global_decls_;
 };
 } // namespace libcpp
diff --git a/libcxx/utils/CMakeLists.txt b/libcxx/utils/CMakeLists.txt
index 7e597f632b6c46d..19bb9851c8674cf 100644
--- a/libcxx/utils/CMakeLists.txt
+++ b/libcxx/utils/CMakeLists.txt
@@ -7,9 +7,19 @@ add_custom_target(libcxx-generate-std-clang-module-header
   COMMENT "Generate the <__std_clang_module> header")
 
 add_custom_target(libcxx-generate-std-cppm-in-file
-  COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/generate_std_cppm_in.py"
+  COMMAND
+        "${Python3_EXECUTABLE}"
+		"${LIBCXX_SOURCE_DIR}/utils/generate_libcxx_cppm_in.py"
+		"std"
   COMMENT "Generate the std.cppm.in file")
 
+add_custom_target(libcxx-generate-std-compat-cppm-in-file
+  COMMAND
+        "${Python3_EXECUTABLE}"
+		"${LIBCXX_SOURCE_DIR}/utils/generate_libcxx_cppm_in.py"
+		"std.compat"
+  COMMENT "Generate the std.compat.cppm.in file")
+
 add_custom_target(libcxx-generate-extended-grapheme-cluster-tables
     COMMAND
         "${Python3_EXECUTABLE}"
@@ -48,6 +58,7 @@ add_custom_target(libcxx-generate-files
     DEPENDS libcxx-generate-feature-test-macros
             libcxx-generate-std-clang-module-header
             libcxx-generate-std-cppm-in-file
+            libcxx-generate-std-compat-cppm-in-file
             libcxx-generate-extended-grapheme-cluster-tables
             libcxx-generate-extended-grapheme-cluster-tests
             libcxx-generate-escaped-output-table
diff --git a/libcxx/utils/generate_std_cppm_in.py b/libcxx/utils/generate_libcxx_cppm_in.py
similarity index 52%
rename from libcxx/utils/generate_std_cppm_in.py
rename to libcxx/utils/generate_libcxx_cppm_in.py
index 522f1dd8161aea2..0a2ea4065129920 100644
--- a/libcxx/utils/generate_std_cppm_in.py
+++ b/libcxx/utils/generate_libcxx_cppm_in.py
@@ -8,20 +8,22 @@
 
 import operator
 import os.path
+import sys
 
 from libcxx.header_information import module_headers
 from libcxx.header_information import header_restrictions
 from libcxx.header_information import headers_not_available
 
 
-libcxx_module_directory = os.path.join(
-    os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "modules"
-)
-with open(
-    os.path.join(libcxx_module_directory, "std.cppm.in"), "w"
-) as std_module_cpp_in:
-    std_module_cpp_in.write(
-        """\
+def write_file(module):
+    libcxx_module_directory = os.path.join(
+        os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "modules"
+    )
+    with open(
+        os.path.join(libcxx_module_directory, f"{module}.cppm.in"), "w"
+    ) as module_cpp_in:
+        module_cpp_in.write(
+            """\
 // -*- C++ -*-
 //===----------------------------------------------------------------------===//
 //
@@ -32,7 +34,7 @@
 //===----------------------------------------------------------------------===//
 
 // WARNING, this entire header is generated by
-// utils/generate_std_cppm_in.py
+// utils/generate_libcxx_cppm_in.py
 // DO NOT MODIFY!
 
 module;
@@ -42,33 +44,46 @@
 // The headers of Table 24: C++ library headers [tab:headers.cpp]
 // and the headers of Table 25: C++ headers for C library facilities [tab:headers.cpp.c]
 """
-    )
-    for header in module_headers:
-        if header in header_restrictions:
-            std_module_cpp_in.write(
-                f"""\
+        )
+        for header in module_headers:
+            if header in header_restrictions:
+                module_cpp_in.write(
+                    f"""\
 #if {header_restrictions[header]}
 #  include <{header}>
 #endif
 """
-            )
-        else:
-            std_module_cpp_in.write(f"#include <{header}>\n")
+                )
+            else:
+                module_cpp_in.write(f"#include <{header}>\n")
 
-    std_module_cpp_in.write("\n// *** Headers not yet available ***\n")
-    for header in sorted(headers_not_available):
-        std_module_cpp_in.write(
-            f"""\
+        module_cpp_in.write("\n// *** Headers not yet available ***\n")
+        for header in sorted(headers_not_available):
+            module_cpp_in.write(
+                f"""\
 #if __has_include(<{header}>)
-#  error "update the header information for <{header}> in libcxx/utils/generate_std_cppm_in.py"
-#endif //   __has_include(<{header}>)
+#  error "update the header information for <{header}> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<{header}>)
 """
-        )
+            )
 
-    std_module_cpp_in.write(
-        """
-export module std;
+        module_cpp_in.write(
+            f"""
+export module {module};
 
 @LIBCXX_MODULE_STD_INCLUDE_SOURCES@
+{'@LIBCXX_MODULE_STD_COMPAT_INCLUDE_SOURCES@' if module == 'std.compat' else ''}"""
+        )
+
+
+if __name__ == "__main__":
+    if len(sys.argv) != 2 or (sys.argv[1] != "std" and sys.argv[1] != "std.compat"):
+        sys.stderr.write(
+            f"""\
+Usage:
+{os.path.basename(__file__)} (std|std.compat)
 """
-    )
+        )
+        sys.exit(1)
+
+    write_file(sys.argv[1])
diff --git a/libcxx/utils/libcxx/test/modules.py b/libcxx/utils/libcxx/test/modules.py
new file mode 100644
index 000000000000000..0b4b22674e0a0d2
--- /dev/null
+++ b/libcxx/utils/libcxx/test/modules.py
@@ -0,0 +1,307 @@
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+from libcxx.header_information import module_headers
+from libcxx.header_information import header_restrictions
+
+### SkipDeclarations
+
+# Ignore several declarations found in the includes.
+#
+# Part of these items are bugs other are not yet implemented features.
+SkipDeclarations = dict()
+
+# See comment in the header.
+SkipDeclarations["cuchar"] = ["std::mbstate_t", "std::size_t"]
+
+# Not in the synopsis.
+SkipDeclarations["cwchar"] = ["std::FILE"]
+
+# The operators are added for private types like __iom_t10.
+SkipDeclarations["iomanip"] = ["std::operator<<", "std::operator>>"]
+
+SkipDeclarations["iosfwd"] = ["std::ios_base", "std::vector"]
+
+# This header also provides declarations in the namespace that might be
+# an error.
+SkipDeclarations["filesystem"] = [
+    "std::filesystem::operator==",
+    "std::filesystem::operator!=",
+]
+
+# This is a specialization for a private type
+SkipDeclarations["iterator"] = ["std::pointer_traits"]
+
+# TODO MODULES
+# This definition is declared in string and defined in istream
+# This declaration should be part of string
+SkipDeclarations["istream"] = ["std::getline"]
+
+# P1614 (at many places) and LWG3519 too.
+SkipDeclarations["random"] = [
+    "std::operator!=",
+    # LWG3519 makes these hidden friends.
+    # Note the older versions had the requirement of these operations but not in
+    # the synopsis.
+    "std::operator<<",
+    "std::operator>>",
+    "std::operator==",
+]
+
+# Declared in the forward header since std::string uses std::allocator
+SkipDeclarations["string"] = ["std::allocator"]
+# TODO MODULES remove zombie names
+# https://libcxx.llvm.org/Status/Cxx20.html#note-p0619
+SkipDeclarations["memory"] = [
+    "std::return_temporary_buffer",
+    "std::get_temporary_buffer",
+]
+
+# TODO MODULES this should be part of ios instead
+SkipDeclarations["streambuf"] = ["std::basic_ios"]
+
+# include/__type_traits/is_swappable.h
+SkipDeclarations["type_traits"] = [
+    "std::swap",
+    # TODO MODULES gotten through __functional/unwrap_ref.h
+    "std::reference_wrapper",
+]
+
+### ExtraDeclarations
+
+# Add declarations in headers.
+#
+# Some headers have their defines in a different header, which may have
+# additional declarations.
+ExtraDeclarations = dict()
+# This declaration is in the ostream header.
+ExtraDeclarations["system_error"] = ["std::operator<<"]
+
+### ExtraHeader
+
+# Adds extra headers file to scan
+#
+# Some C++ headers in libc++ are stored in multiple physical files. There is a
+# pattern to find these files. However there are some exceptions these are
+# listed here.
+ExtraHeader = dict()
+# locale has a file and not a subdirectory
+ExtraHeader["locale"] = "v1/__locale$"
+ExtraHeader["thread"] = "v1/__threading_support$"
+ExtraHeader["ranges"] = "v1/__fwd/subrange.h$"
+
+# The extra header is needed since two headers are required to provide the
+# same definition.
+ExtraHeader["functional"] = "v1/__compare/compare_three_way.h$"
+
+
+# newline needs to be escaped for the module partition output.
+nl = "\\\\n"
+
+
+class module_test_generator:
+    def __init__(
+        self,
+        tmp_prefix,
+        module_path,
+        clang_tidy,
+        clang_tidy_plugin,
+        compiler,
+        compiler_flags,
+    ):
+        self.tmp_prefix = tmp_prefix
+        self.module_path = module_path
+        self.clang_tidy = clang_tidy
+        self.clang_tidy_plugin = clang_tidy_plugin
+        self.compiler = compiler
+        self.compiler_flags = compiler_flags
+
+    def write_lit_configuration(self):
+        print(
+            f"""\
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-std-modules
+// UNSUPPORTED: clang-modules-build
+
+// REQUIRES: has-clang-tidy
+
+// The GCC compiler flags are not always compatible with clang-tidy.
+// UNSUPPORTED: gcc
+
+// RUN: echo -n > {self.tmp_prefix}.all_partitions
+"""
+        )
+
+    def process_module_partition(self, header, is_c_header):
+        # Some headers cannot be included when a libc++ feature is disabled.
+        # In that case include the header conditionally. The header __config
+        # ensures the libc++ feature macros are available.
+        if header in header_restrictions:
+            include = (
+                f"#include <__config>{nl}"
+                f"#if {header_restrictions[header]}{nl}"
+                f"#  include <{header}>{nl}"
+                f"#endif{nl}"
+            )
+        elif header == "chrono":
+            # When localization is disabled the header string is not included.
+            # When string is included chrono's operator""s is a named declaration
+            #   using std::chrono_literals::operator""s;
+            # else it is a named declaration
+            #   using std::operator""s;
+            # TODO MODULES investigate why
+            include = f"#include <string>{nl}#include <chrono>{nl}"
+        else:
+            include = f"#include <{header}>{nl}"
+
+        module_files = f'#include \\"%{{module}}/std/{header}.inc\\"{nl}'
+        if is_c_header:
+            module_files += f'#include \\"%{{module}}/std.compat/{header}.inc\\"{nl}'
+
+        # Generate a module partition for the header module includes. This
+        # makes it possible to verify that all headers export all their
+        # named declarations.
+        print(
+            '// RUN: echo -e "'
+            f"module;{nl}"
+            f"{include}{nl}"
+            f"{nl}"
+            f"// Use __libcpp_module_<HEADER> to ensure that modules{nl}"
+            f"// are not named as keywords or reserved names.{nl}"
+            f"export module std:__libcpp_module_{header};{nl}"
+            f"{module_files}"
+            f'" > {self.tmp_prefix}.{header}.cppm'
+        )
+
+        # Extract the information of the module partition using lang-tidy
+        print(
+            f"// RUN: {self.clang_tidy} {self.tmp_prefix}.{header}.cppm "
+            "  --checks='-*,libcpp-header-exportable-declarations' "
+            "  -config='{CheckOptions: [ "
+            "    {"
+            "      key: libcpp-header-exportable-declarations.Filename, "
+            f"     value: {header}.inc"
+            "    }, {"
+            "      key: libcpp-header-exportable-declarations.FileType, "
+            f"     value: {'CompatModulePartition' if is_c_header else 'ModulePartition'}"
+            "    }, "
+            "  ]}' "
+            f"--load={self.clang_tidy_plugin} "
+            f"-- {self.compiler_flags} "
+            f"| sort > {self.tmp_prefix}.{header}.module"
+        )
+        print(
+            f"// RUN: cat  {self.tmp_prefix}.{header}.module >> {self.tmp_prefix}.all_partitions"
+        )
+
+        return include
+
+    def process_header(self, header, include, is_c_header):
+        # Dump the information as found in the module by using the header file(s).
+        skip_declarations = " ".join(SkipDeclarations.get(header, []))
+        if skip_declarations:
+            skip_declarations = (
+                "{"
+                "  key: libcpp-header-exportable-declarations.SkipDeclarations, "
+                f' value: "{skip_declarations}" '
+                "}, "
+            )
+
+        extra_declarations = " ".join(ExtraDeclarations.get(header, []))
+        if extra_declarations:
+            extra_declarations = (
+                "{"
+                "  key: libcpp-header-exportable-declarations.ExtraDeclarations, "
+                f' value: "{extra_declarations}" '
+                "}, "
+            )
+
+        extra_header = ExtraHeader.get(header, "")
+        if extra_header:
+            extra_header = (
+                "{"
+                "  key: libcpp-header-exportable-declarations.ExtraHeader, "
+                f' value: "{extra_header}" '
+                "}, "
+            )
+
+        # Clang-tidy needs a file input
+        print(f'// RUN: echo -e "' f"{include}" f'" > {self.tmp_prefix}.{header}.cpp')
+        print(
+            f"// RUN: {self.clang_tidy} {self.tmp_prefix}.{header}.cpp "
+            "  --checks='-*,libcpp-header-exportable-declarations' "
+            "  -config='{CheckOptions: [ "
+            "    {"
+            "      key: libcpp-header-exportable-declarations.Filename, "
+            f"     value: {header}"
+            "    }, {"
+            "      key: libcpp-header-exportable-declarations.FileType, "
+            f"     value: {'CHeader' if is_c_header else 'Header'}"
+            "    }, "
+            f"   {skip_declarations} {extra_declarations} {extra_header}, "
+            "  ]}' "
+            f"--load={self.clang_tidy_plugin} "
+            f"-- {self.compiler_flags} "
+            f"| sort > {self.tmp_prefix}.{header}.include"
+        )
+        print(
+            f"// RUN: diff -u {self.tmp_prefix}.{header}.module {self.tmp_prefix}.{header}.include"
+        )
+
+    def process_module(self, module):
+        # Merge the data of the parts
+        print(
+            f"// RUN: sort -u -o {self.tmp_prefix}.all_partitions {self.tmp_prefix}.all_partitions"
+        )
+
+        # Dump the information as found in top-level module.
+        print(
+            f"// RUN: {self.clang_tidy} {self.module_path}/{module}.cppm "
+            "  --checks='-*,libcpp-header-exportable-declarations' "
+            "  -config='{CheckOptions: [ "
+            "    {"
+            "      key: libcpp-header-exportable-declarations.Header, "
+            f"     value: {module}.cppm"
+            "    }, {"
+            "      key: libcpp-header-exportable-declarations.FileType, "
+            "      value: Module"
+            "    }, "
+            "  ]}' "
+            f"--load={self.clang_tidy_plugin} "
+            f"-- {self.compiler_flags} "
+            f"| sort > {self.tmp_prefix}.module"
+        )
+
+        # Compare the sum of the parts with the top-level module.
+        print(
+            f"// RUN: diff -u {self.tmp_prefix}.all_partitions {self.tmp_prefix}.module"
+        )
+
+    # Basic smoke test. Import a module and try to compile when using all
+    # exported names. This validates the clang-tidy script does not
+    # accidentally add named declarations to the list that are not available.
+    def test_module(self, module):
+        print(
+            f"""\
+// RUN: echo 'import {module};' > {self.tmp_prefix}.compile.pass.cpp
+// RUN: cat {self.tmp_prefix}.all_partitions >> {self.tmp_prefix}.compile.pass.cpp
+// RUN: {self.compiler} {self.compiler_flags} -fsyntax-only {self.tmp_prefix}.compile.pass.cpp
+"""
+        )
+
+    def write_test(self, module, c_headers=[]):
+        self.write_lit_configuration()
+
+        # Validate all module parts.
+        for header in module_headers:
+            is_c_header = header in c_headers
+            include = self.process_module_partition(header, is_c_header)
+            self.process_header(header, include, is_c_header)
+
+        self.process_module(module)
+        self.test_module(module)

>From 4358bbf598c608a48dc15e21d270d43f74547688 Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Thu, 23 Nov 2023 20:34:27 +0100
Subject: [PATCH 2/2] Addresses review comments.

---
 libcxx/modules/std.compat/clocale.inc         |  6 ++--
 libcxx/test/std/modules/std.compat.pass.cpp   |  5 +---
 .../header_exportable_declarations.cpp        | 11 +++++--
 .../header_exportable_declarations.hpp        | 24 +++++++--------
 libcxx/utils/libcxx/test/modules.py           | 29 ++++++++-----------
 5 files changed, 36 insertions(+), 39 deletions(-)

diff --git a/libcxx/modules/std.compat/clocale.inc b/libcxx/modules/std.compat/clocale.inc
index 1a975c560a496d7..d9785a73794300c 100644
--- a/libcxx/modules/std.compat/clocale.inc
+++ b/libcxx/modules/std.compat/clocale.inc
@@ -9,9 +9,9 @@
 
 export {
 #ifndef _LIBCPP_HAS_NO_LOCALIZATION
-  using ::lconv _LIBCPP_USING_IF_EXISTS;
+  using ::lconv;
 
-  using ::localeconv _LIBCPP_USING_IF_EXISTS;
-  using ::setlocale _LIBCPP_USING_IF_EXISTS;
+  using ::localeconv;
+  using ::setlocale;
 #endif // _LIBCPP_HAS_NO_LOCALIZATION
 } // export
diff --git a/libcxx/test/std/modules/std.compat.pass.cpp b/libcxx/test/std/modules/std.compat.pass.cpp
index a302d16d63540f0..a33ed3b6b64533a 100644
--- a/libcxx/test/std/modules/std.compat.pass.cpp
+++ b/libcxx/test/std/modules/std.compat.pass.cpp
@@ -15,7 +15,4 @@
 
 import std.compat;
 
-int main(int, char**) {
-  ::printf("Hello modular world");
-  return 0;
-}
+int main(int, char**) { return !(::strlen("Hello modular world") == 19); }
diff --git a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
index debc841fc5c4176..fcb5865adf0d4e1 100644
--- a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
@@ -230,8 +230,13 @@ static bool is_viable_declaration(const clang::NamedDecl* decl) {
 /// This test misses candidates which are not used in libc++
 /// * any identifier with two underscores not at the start
 bool is_reserved_name(std::string_view name) {
-  if (name.starts_with("_"))
+  if (name.starts_with("_")) {
+    // This is a public name declared in cstdlib.
+    if (name == "_Exit")
+      return false;
+
     return name.size() > 1 && (name[1] == '_' || std::isupper(name[1]));
+  }
 
   std::size_t pos = name.find("::_");
   if (pos == std::string::npos)
@@ -248,7 +253,7 @@ bool is_reserved_name(std::string_view name) {
 }
 
 /// Some declarations in the global namespace are exported from the std module.
-static bool is_valid_global_name(std::string_view name) {
+static bool is_global_name_exported_by_std_module(std::string_view name) {
   static const std::set<std::string_view> valid{
       "operator delete", "operator delete[]", "operator new", "operator new[]"};
   return valid.contains(name);
@@ -259,7 +264,7 @@ static bool is_valid_declaration_context(
   if (decl.getDeclContext()->isNamespace())
     return true;
 
-  if (is_valid_global_name(name))
+  if (is_global_name_exported_by_std_module(name))
     return true;
 
   return file_type != header_exportable_declarations::FileType::Header;
diff --git a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.hpp b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.hpp
index 0c97aade6b99110..5d9e0f3ef9c30e2 100644
--- a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.hpp
+++ b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.hpp
@@ -40,25 +40,25 @@ class header_exportable_declarations : public clang::tidy::ClangTidyCheck {
   FileType file_type_;
   llvm::StringRef extra_header_;
   std::set<std::string> decls_;
+  std::set<std::string> global_decls_;
 
-  // The named declarations in .h C headers are "tricky". On POSIX systems
-  // these headers contain POSIX specific functions that do not use a reserved
-  // name. For example, fmemopen is provided by stdio.h.
-  // The way to filter the names that should be provided by the headers is the
-  // following:
+  // The named declarations in .h C headers are "tricky". On POSIX
+  // systems these headers contain POSIX specific functions that do not
+  // use a reserved name. For example, fmemopen is provided by stdio.h.
+  // We filter the names that should be provided by the headers as follows:
   // - record all named declarations the global namespace
   // - wait until the header is completely processed
   // - every named declaration in the global namespace that has a matching
   //   "export" in the std namespace is exported.
   //
-  // The only possible place where to process after the header is processed is
-  // the class' destructor.
+  // The only place where we can do the above while ensuring that all
+  // the declarations in the header have been seen is in the clang tidy
+  // plugin's destructor.
   //
-  // It is possible to skip some declarations in the std namespace, these are
-  // added to decls_ before processing. To differenciate between a skipped
-  // declaration and a real declaration the skipped declarations are recorded
-  // in an extra variable.
+  // It is possible to skip some declarations in the std namespace,
+  // these are added to decls_ before processing. To differentiate
+  // between a skipped declaration and a real declaration the skipped
+  // declarations are recorded in an extra variable.
   std::set<std::string> skip_decls_;
-  std::set<std::string> global_decls_;
 };
 } // namespace libcpp
diff --git a/libcxx/utils/libcxx/test/modules.py b/libcxx/utils/libcxx/test/modules.py
index 0b4b22674e0a0d2..deaac450381c387 100644
--- a/libcxx/utils/libcxx/test/modules.py
+++ b/libcxx/utils/libcxx/test/modules.py
@@ -8,6 +8,7 @@
 
 from libcxx.header_information import module_headers
 from libcxx.header_information import header_restrictions
+from dataclasses import dataclass
 
 ### SkipDeclarations
 
@@ -104,22 +105,14 @@
 nl = "\\\\n"
 
 
+ at dataclass
 class module_test_generator:
-    def __init__(
-        self,
-        tmp_prefix,
-        module_path,
-        clang_tidy,
-        clang_tidy_plugin,
-        compiler,
-        compiler_flags,
-    ):
-        self.tmp_prefix = tmp_prefix
-        self.module_path = module_path
-        self.clang_tidy = clang_tidy
-        self.clang_tidy_plugin = clang_tidy_plugin
-        self.compiler = compiler
-        self.compiler_flags = compiler_flags
+    tmp_prefix: str
+    module_path: str
+    clang_tidy: str
+    clang_tidy_plugin: str
+    compiler: str
+    compiler_flags: str
 
     def write_lit_configuration(self):
         print(
@@ -159,9 +152,11 @@ def process_module_partition(self, header, is_c_header):
         else:
             include = f"#include <{header}>{nl}"
 
-        module_files = f'#include \\"%{{module}}/std/{header}.inc\\"{nl}'
+        module_files = f'#include \\"{self.module_path}/std/{header}.inc\\"{nl}'
         if is_c_header:
-            module_files += f'#include \\"%{{module}}/std.compat/{header}.inc\\"{nl}'
+            module_files += (
+                f'#include \\"{self.module_path}/std.compat/{header}.inc\\"{nl}'
+            )
 
         # Generate a module partition for the header module includes. This
         # makes it possible to verify that all headers export all their



More information about the libcxx-commits mailing list