diff --git a/libcxx/modules/CMakeLists.txt b/libcxx/modules/CMakeLists.txt index 395226fb37287..fae6448a7eec8 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 dca3b25155a5a..b02b68915b8f4 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 0000000000000..f199e194e60b1 --- /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 +#include +#include +#if !defined(_LIBCPP_HAS_NO_ATOMIC_HEADER) +# include +#endif +#if !defined(_LIBCPP_HAS_NO_THREADS) +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include +#endif +#include +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS) +# include +#endif +#if !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS) +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include +#endif +#include +#if !defined(_LIBCPP_HAS_NO_THREADS) +# include +#endif +#include +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include +#endif +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include +#endif +#include +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include +#endif +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include +#endif +#include +#if !defined(_LIBCPP_HAS_NO_THREADS) +# include +#endif +#include +#include +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include +#endif +#include +#include +#include +#include +#include +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include +#endif +#include +#if !defined(_LIBCPP_HAS_NO_THREADS) +# include +#endif +#include +#if !defined(_LIBCPP_HAS_NO_THREADS) +# include +#endif +#include +#include +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include +#endif +#include +#include +#if !defined(_LIBCPP_HAS_NO_THREADS) +# include +#endif +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include +#endif +#include +#include +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include +#endif +#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include +#endif +#include +#if !defined(_LIBCPP_HAS_NO_THREADS) +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// *** Headers not yet available *** +#if __has_include() +# error "please update the header information for in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include() +#if __has_include() +# error "please update the header information for in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include() +#if __has_include() +# error "please update the header information for in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include() +#if __has_include() +# error "please update the header information for in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include() +#if __has_include() +# error "please update the header information for in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include() +#if __has_include() +# error "please update the header information for in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include() +#if __has_include() +# error "please update the header information for in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include() +#if __has_include() +# error "please update the header information for in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include() +#if __has_include() +# error "please update the header information for in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include() +#if __has_include() +# error "please update the header information for in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include() +#if __has_include() +# error "please update the header information for in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include() + +export module std.compat; + +@LIBCXX_MODULE_STD_INCLUDE_SOURCES@ +@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 0000000000000..ac0533d14e9a9 --- /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 0000000000000..56fb45a374a51 --- /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 0000000000000..ac0533d14e9a9 --- /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 0000000000000..50128463d6a91 --- /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 0000000000000..ac0533d14e9a9 --- /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 0000000000000..a64c088d0d6f8 --- /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 0000000000000..ac0533d14e9a9 --- /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 0000000000000..d9785a7379430 --- /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 0000000000000..de5379275c5fa --- /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 0000000000000..1fc42ea3ee037 --- /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 0000000000000..33af6a9f2b734 --- /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 0000000000000..3efb34617a8bf --- /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 0000000000000..94ad036fd8f4a --- /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 0000000000000..1a74efc70ceaa --- /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 0000000000000..1ec3015c9e2a2 --- /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 0000000000000..9333d84870710 --- /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 0000000000000..090350ae81478 --- /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 0000000000000..92e3403a5e58e --- /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 0000000000000..d1a511cadef18 --- /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 0000000000000..8905aecbdfecc --- /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 0000000000000..13aa2b7f3fb74 --- /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 ecb0601260325..b46c52e781f82 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() -# error "update the header information for in libcxx/utils/generate_std_cppm_in.py" -#endif // __has_include() +# error "please update the header information for in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include() #if __has_include() -# error "update the header information for in libcxx/utils/generate_std_cppm_in.py" -#endif // __has_include() +# error "please update the header information for in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include() #if __has_include() -# error "update the header information for in libcxx/utils/generate_std_cppm_in.py" -#endif // __has_include() +# error "please update the header information for in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include() #if __has_include() -# error "update the header information for in libcxx/utils/generate_std_cppm_in.py" -#endif // __has_include() +# error "please update the header information for in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include() #if __has_include() -# error "update the header information for in libcxx/utils/generate_std_cppm_in.py" -#endif // __has_include() +# error "please update the header information for in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include() #if __has_include() -# error "update the header information for in libcxx/utils/generate_std_cppm_in.py" -#endif // __has_include() +# error "please update the header information for in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include() #if __has_include() -# error "update the header information for in libcxx/utils/generate_std_cppm_in.py" -#endif // __has_include() +# error "please update the header information for in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include() #if __has_include() -# error "update the header information for in libcxx/utils/generate_std_cppm_in.py" -#endif // __has_include() +# error "please update the header information for in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include() #if __has_include() -# error "update the header information for in libcxx/utils/generate_std_cppm_in.py" -#endif // __has_include() +# error "please update the header information for in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include() #if __has_include() -# error "update the header information for in libcxx/utils/generate_std_cppm_in.py" -#endif // __has_include() +# error "please update the header information for in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include() #if __has_include() -# error "update the header information for in libcxx/utils/generate_std_cppm_in.py" -#endif // __has_include() +# error "please update the header information for in headers_not_available in utils/libcxx/header_information.py" +#endif // __has_include() export module std; diff --git a/libcxx/test/libcxx/module_std.gen.py b/libcxx/test/libcxx/module_std.gen.py index d8198cc705837..8e03d6e5b5b52 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 {nl}#include {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_
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 0000000000000..c4792db3d71e6 --- /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 4116553b6f7a9..1ee9086ee22e3 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 0000000000000..a33ed3b6b6453 --- /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 fba48d5984ea7..fcb5865adf0d4 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 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 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("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("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 119bcd3f6f2fe..5d9e0f3ef9c30 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 decls_; + std::set 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 differentiate + // between a skipped declaration and a real declaration the skipped + // declarations are recorded in an extra variable. + std::set skip_decls_; }; } // namespace libcpp diff --git a/libcxx/utils/CMakeLists.txt b/libcxx/utils/CMakeLists.txt index 7e597f632b6c4..19bb9851c8674 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 242134773e689..f957406778d39 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 0000000000000..deaac450381c3 --- /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 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" + + +@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 {nl}#include {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_
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)