[libcxx-commits] [libcxx] 600462a - [libc++][modules] Adds std.compat module. (#71438)
via libcxx-commits
libcxx-commits at lists.llvm.org
Sat Dec 9 04:51:54 PST 2023
Author: Mark de Wever
Date: 2023-12-09T13:51:50+01:00
New Revision: 600462a2db7c044896122acfb347ce2d4d88271f
URL: https://github.com/llvm/llvm-project/commit/600462a2db7c044896122acfb347ce2d4d88271f
DIFF: https://github.com/llvm/llvm-project/commit/600462a2db7c044896122acfb347ce2d4d88271f.diff
LOG: [libc++][modules] Adds std.compat module. (#71438)
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
Added:
libcxx/modules/std.compat.cppm.in
libcxx/modules/std.compat/cassert.inc
libcxx/modules/std.compat/cctype.inc
libcxx/modules/std.compat/cerrno.inc
libcxx/modules/std.compat/cfenv.inc
libcxx/modules/std.compat/cfloat.inc
libcxx/modules/std.compat/cinttypes.inc
libcxx/modules/std.compat/climits.inc
libcxx/modules/std.compat/clocale.inc
libcxx/modules/std.compat/cmath.inc
libcxx/modules/std.compat/csetjmp.inc
libcxx/modules/std.compat/csignal.inc
libcxx/modules/std.compat/cstdarg.inc
libcxx/modules/std.compat/cstddef.inc
libcxx/modules/std.compat/cstdint.inc
libcxx/modules/std.compat/cstdio.inc
libcxx/modules/std.compat/cstdlib.inc
libcxx/modules/std.compat/cstring.inc
libcxx/modules/std.compat/ctime.inc
libcxx/modules/std.compat/cuchar.inc
libcxx/modules/std.compat/cwchar.inc
libcxx/modules/std.compat/cwctype.inc
libcxx/test/libcxx/module_std_compat.gen.py
libcxx/test/std/modules/std.compat.pass.cpp
libcxx/utils/generate_libcxx_cppm_in.py
libcxx/utils/libcxx/test/modules.py
Modified:
libcxx/modules/CMakeLists.txt
libcxx/modules/CMakeLists.txt.in
libcxx/modules/std.cppm.in
libcxx/test/libcxx/module_std.gen.py
libcxx/test/lit.local.cfg
libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.hpp
libcxx/utils/CMakeLists.txt
Removed:
libcxx/utils/generate_std_cppm_in.py
################################################################################
diff --git a/libcxx/modules/CMakeLists.txt b/libcxx/modules/CMakeLists.txt
index 395226fb372870..fae6448a7eec84 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 dca3b25155a5af..b02b68915b8f4d 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 00000000000000..f199e194e60b16
--- /dev/null
+++ b/libcxx/modules/std.compat.cppm.in
@@ -0,0 +1,208 @@
+// -*- 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
+#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+# include <syncstream>
+#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(<debugging>)
+# error "please update the header information for <debugging> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<debugging>)
+#if __has_include(<flat_map>)
+# error "please 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 "please 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 "please 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 "please 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(<linalg>)
+# error "please update the header information for <linalg> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<linalg>)
+#if __has_include(<rcu>)
+# error "please 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 "please 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 "please 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 "please update the header information for <stdfloat> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<stdfloat>)
+#if __has_include(<text_encoding>)
+# error "please 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 00000000000000..ac0533d14e9a9a
--- /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 00000000000000..56fb45a374a510
--- /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 00000000000000..ac0533d14e9a9a
--- /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 00000000000000..50128463d6a914
--- /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 00000000000000..ac0533d14e9a9a
--- /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 00000000000000..a64c088d0d6f88
--- /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 00000000000000..ac0533d14e9a9a
--- /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 00000000000000..d9785a73794300
--- /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;
+
+ using ::localeconv;
+ using ::setlocale;
+#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 00000000000000..de5379275c5fae
--- /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 00000000000000..1fc42ea3ee0378
--- /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 00000000000000..33af6a9f2b7343
--- /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 00000000000000..3efb34617a8bff
--- /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 00000000000000..94ad036fd8f4a5
--- /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 ::ptr
diff _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 00000000000000..1a74efc70ceaa1
--- /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 00000000000000..1ec3015c9e2a2e
--- /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 00000000000000..9333d848707103
--- /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 00000000000000..090350ae814786
--- /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 00000000000000..92e3403a5e58e2
--- /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 ::
diff time;
+ 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 00000000000000..d1a511cadef184
--- /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 00000000000000..8905aecbdfecc2
--- /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 00000000000000..13aa2b7f3fb746
--- /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 ecb06012603253..b46c52e781f82f 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;
@@ -169,38 +169,38 @@ module;
// *** Headers not yet available ***
#if __has_include(<debugging>)
-# error "update the header information for <debugging> in libcxx/utils/generate_std_cppm_in.py"
-#endif // __has_include(<debugging>)
+# error "please update the header information for <debugging> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<debugging>)
#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 "please 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 "please 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 "please 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 "please 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(<linalg>)
-# error "update the header information for <linalg> in libcxx/utils/generate_std_cppm_in.py"
-#endif // __has_include(<linalg>)
+# error "please update the header information for <linalg> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<linalg>)
#if __has_include(<rcu>)
-# error "update the header information for <rcu> in libcxx/utils/generate_std_cppm_in.py"
-#endif // __has_include(<rcu>)
+# error "please 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 "please 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 "please 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 "please update the header information for <stdfloat> in headers_not_available in utils/libcxx/header_information.py"
+#endif // __has_include(<stdfloat>)
#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 "please 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 d8198cc7058379..8e03d6e5b5b523 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
diff erent 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 00000000000000..c4792db3d71e62
--- /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 4116553b6f7a9a..1ee9086ee22e3c 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 00000000000000..a33ed3b6b64533
--- /dev/null
+++ b/libcxx/test/std/modules/std.compat.pass.cpp
@@ -0,0 +1,18 @@
+//===----------------------------------------------------------------------===//
+//
+// 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**) { 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 fba48d5984ea77..fcb5865adf0d4e 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,21 @@ 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("_")) {
+ // 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)
return false;
@@ -213,27 +245,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_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);
+}
+
+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_global_name_exported_by_std_module(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 +319,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 119bcd3f6f2fe5..5d9e0f3ef9c30e 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_;
+ 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.
+ // 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 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
diff erentiate
+ // between a skipped declaration and a real declaration the skipped
+ // declarations are recorded in an extra variable.
+ std::set<std::string> skip_decls_;
};
} // namespace libcpp
diff --git a/libcxx/utils/CMakeLists.txt b/libcxx/utils/CMakeLists.txt
index 7e597f632b6c46..19bb9851c8674c 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 51%
rename from libcxx/utils/generate_std_cppm_in.py
rename to libcxx/utils/generate_libcxx_cppm_in.py
index 242134773e6891..f957406778d392 100644
--- a/libcxx/utils/generate_std_cppm_in.py
+++ b/libcxx/utils/generate_libcxx_cppm_in.py
@@ -7,20 +7,22 @@
# ===----------------------------------------------------------------------===##
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++ -*-
//===----------------------------------------------------------------------===//
//
@@ -31,7 +33,7 @@
//===----------------------------------------------------------------------===//
// WARNING, this entire header is generated by
-// utils/generate_std_cppm_in.py
+// utils/generate_libcxx_cppm_in.py
// DO NOT MODIFY!
module;
@@ -41,33 +43,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 "please 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 00000000000000..deaac450381c38
--- /dev/null
+++ b/libcxx/utils/libcxx/test/modules.py
@@ -0,0 +1,302 @@
+# ===----------------------------------------------------------------------===##
+#
+# 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
+from dataclasses import dataclass
+
+### 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
diff erent 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"
+
+
+ at dataclass
+class module_test_generator:
+ tmp_prefix: str
+ module_path: str
+ clang_tidy: str
+ clang_tidy_plugin: str
+ compiler: str
+ compiler_flags: str
+
+ 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 \\"{self.module_path}/std/{header}.inc\\"{nl}'
+ if is_c_header:
+ 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
+ # 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)
More information about the libcxx-commits
mailing list