diff --git a/libcxx/test/libcxx/feature_test_macro/ftm_metadata.sh.py b/libcxx/test/libcxx/feature_test_macro/ftm_metadata.sh.py new file mode 100644 index 000000000000000..1dec1ae612ecb1d --- /dev/null +++ b/libcxx/test/libcxx/feature_test_macro/ftm_metadata.sh.py @@ -0,0 +1,51 @@ +# ===----------------------------------------------------------------------===## +# +# 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 +# +# ===----------------------------------------------------------------------===## + +# RUN: %{python} %s %{libcxx-dir}/utils %{libcxx-dir}/test/libcxx/feature_test_macro/test_data.json + +import sys + +sys.path.append(sys.argv[1]) +from generate_feature_test_macro_components import FeatureTestMacros + + +def test(output, expected): + assert output == expected, f"expected\n{expected}\n\noutput\n{output}" + + +ftm = FeatureTestMacros(sys.argv[2]) +test( + ftm.ftm_metadata, + { + "__cpp_lib_any": { + "headers": ["any"], + "test_suite_guard": None, + "libcxx_guard": None, + }, + "__cpp_lib_barrier": { + "headers": ["barrier"], + "test_suite_guard": "!defined(_LIBCPP_HAS_NO_THREADS) && (!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_SYNC)", + "libcxx_guard": "!defined(_LIBCPP_HAS_NO_THREADS) && _LIBCPP_AVAILABILITY_HAS_SYNC", + }, + "__cpp_lib_format": { + "headers": ["format"], + "test_suite_guard": None, + "libcxx_guard": None, + }, + "__cpp_lib_parallel_algorithm": { + "headers": ["algorithm", "numeric"], + "test_suite_guard": None, + "libcxx_guard": None, + }, + "__cpp_lib_variant": { + "headers": ["variant"], + "test_suite_guard": None, + "libcxx_guard": None, + }, + }, +) diff --git a/libcxx/test/libcxx/feature_test_macro/implemented_ftms.sh.py b/libcxx/test/libcxx/feature_test_macro/implemented_ftms.sh.py index 67353fc41e50985..62a3c46b15db3cb 100644 --- a/libcxx/test/libcxx/feature_test_macro/implemented_ftms.sh.py +++ b/libcxx/test/libcxx/feature_test_macro/implemented_ftms.sh.py @@ -31,7 +31,7 @@ def test(output, expected): "__cpp_lib_barrier": { "c++20": "201907L", "c++23": "201907L", - "c++26": "201907L", + "c++26": "299900L", }, "__cpp_lib_format": { "c++20": None, diff --git a/libcxx/test/libcxx/feature_test_macro/standard_ftms.sh.py b/libcxx/test/libcxx/feature_test_macro/standard_ftms.sh.py index 43c90b131bff16c..231e6072898752a 100644 --- a/libcxx/test/libcxx/feature_test_macro/standard_ftms.sh.py +++ b/libcxx/test/libcxx/feature_test_macro/standard_ftms.sh.py @@ -31,7 +31,7 @@ def test(output, expected): "__cpp_lib_barrier": { "c++20": "201907L", "c++23": "201907L", - "c++26": "201907L", + "c++26": "299900L", }, "__cpp_lib_format": { "c++20": "202110L", diff --git a/libcxx/test/libcxx/feature_test_macro/test_data.json b/libcxx/test/libcxx/feature_test_macro/test_data.json index 1f8bbe5d769b5b2..18c8836fa5149fa 100644 --- a/libcxx/test/libcxx/feature_test_macro/test_data.json +++ b/libcxx/test/libcxx/feature_test_macro/test_data.json @@ -23,6 +23,13 @@ "implemented": true } ] + }, + "c++26": { + "299900": [ + { + "implemented": true + } + ] } }, "headers": [ diff --git a/libcxx/test/libcxx/feature_test_macro/version_header.sh.py b/libcxx/test/libcxx/feature_test_macro/version_header.sh.py new file mode 100644 index 000000000000000..bbb863371f76397 --- /dev/null +++ b/libcxx/test/libcxx/feature_test_macro/version_header.sh.py @@ -0,0 +1,73 @@ +# ===----------------------------------------------------------------------===## +# +# 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 +# +# ===----------------------------------------------------------------------===## + +# RUN: %{python} %s %{libcxx-dir}/utils %{libcxx-dir}/test/libcxx/feature_test_macro/test_data.json + +import sys + +sys.path.append(sys.argv[1]) +from generate_feature_test_macro_components import FeatureTestMacros + + +def test(output, expected): + assert output == expected, f"expected\n{expected}\n\noutput\n{output}" + + +ftm = FeatureTestMacros(sys.argv[2]) +test( + ftm.version_header, + """// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP_VERSION +#define _LIBCPP_VERSION + +#include <__config> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +#if _LIBCPP_STD_VER >= 17 +# define __cpp_lib_any 201606L +# define __cpp_lib_parallel_algorithm 201603L +# define __cpp_lib_variant 202102L +#endif // _LIBCPP_STD_VER >= 17 + +#if _LIBCPP_STD_VER >= 20 +# if !defined(_LIBCPP_HAS_NO_THREADS) && _LIBCPP_AVAILABILITY_HAS_SYNC +# define __cpp_lib_barrier 201907L +# endif +// define __cpp_lib_format 202110L +# undef __cpp_lib_variant +# define __cpp_lib_variant 202106L +#endif // _LIBCPP_STD_VER >= 20 + +#if _LIBCPP_STD_VER >= 23 +// define __cpp_lib_format 202207L +#endif // _LIBCPP_STD_VER >= 23 + +#if _LIBCPP_STD_VER >= 26 +# if !defined(_LIBCPP_HAS_NO_THREADS) && _LIBCPP_AVAILABILITY_HAS_SYNC +# undef __cpp_lib_barrier +# define __cpp_lib_barrier 299900L +# endif +// define __cpp_lib_format 202311L +# undef __cpp_lib_variant +# define __cpp_lib_variant 202306L +#endif // _LIBCPP_STD_VER >= 26 + +#endif // _LIBCPP_VERSION +""", +) diff --git a/libcxx/test/libcxx/feature_test_macro/version_header_implementation.sh.py b/libcxx/test/libcxx/feature_test_macro/version_header_implementation.sh.py new file mode 100644 index 000000000000000..db90206ae770f3a --- /dev/null +++ b/libcxx/test/libcxx/feature_test_macro/version_header_implementation.sh.py @@ -0,0 +1,114 @@ +# ===----------------------------------------------------------------------===## +# +# 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 +# +# ===----------------------------------------------------------------------===## + +# RUN: %{python} %s %{libcxx-dir}/utils %{libcxx-dir}/test/libcxx/feature_test_macro/test_data.json + +import sys + +sys.path.append(sys.argv[1]) +from generate_feature_test_macro_components import FeatureTestMacros + + +def test(output, expected): + assert output == expected, f"expected\n{expected}\n\noutput\n{output}" + + +ftm = FeatureTestMacros(sys.argv[2]) +test( + ftm.version_header_implementation, + { + "17": [ + { + "__cpp_lib_any": { + "value": "201606L", + "implemented": True, + "need_undef": False, + "condition": None, + }, + }, + { + "__cpp_lib_parallel_algorithm": { + "value": "201603L", + "implemented": True, + "need_undef": False, + "condition": None, + }, + }, + { + "__cpp_lib_variant": { + "value": "202102L", + "implemented": True, + "need_undef": False, + "condition": None, + }, + }, + ], + "20": [ + { + "__cpp_lib_barrier": { + "value": "201907L", + "implemented": True, + "need_undef": False, + "condition": "!defined(_LIBCPP_HAS_NO_THREADS) && _LIBCPP_AVAILABILITY_HAS_SYNC", + }, + }, + { + "__cpp_lib_format": { + "value": "202110L", + "implemented": False, + "need_undef": False, + "condition": None, + }, + }, + { + "__cpp_lib_variant": { + "value": "202106L", + "implemented": True, + "need_undef": True, + "condition": None, + }, + }, + ], + "23": [ + { + "__cpp_lib_format": { + "value": "202207L", + "implemented": False, + "need_undef": False, + "condition": None, + }, + }, + ], + "26": [ + { + "__cpp_lib_barrier": { + "value": "299900L", + "implemented": True, + "need_undef": True, + "condition": "!defined(_LIBCPP_HAS_NO_THREADS) && _LIBCPP_AVAILABILITY_HAS_SYNC", + }, + }, + { + "__cpp_lib_format": { + "value": "202311L", + "implemented": False, + "need_undef": False, + "condition": None, + }, + }, + { + "__cpp_lib_variant": { + "value": "202306L", + "implemented": True, + "need_undef": True, + "condition": None, + }, + }, + ], + }, +) diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index 6c42748002aee3b..b041b08f02aac54 100755 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -1956,6 +1956,55 @@ def get_ftms( return result +def generate_version_header_dialect_block(data: Dict[str, Any]) -> str: + """Generates the contents of the version header for a dialect. + + This generates the contents of a + #if _LIBCPP_STD_VER >= XY + #endif // _LIBCPP_STD_VER >= XY + block. + """ + result = "" + for element in data: + for ftm, entry in element.items(): + if not entry["implemented"]: + # When a FTM is not implemented don't add the guards + # or undefine the (possibly) defined macro. + result += f'// define {ftm} {entry["value"]}\n' + else: + need_undef = entry["need_undef"] + if entry["condition"]: + result += f'# if {entry["condition"]}\n' + if entry["need_undef"]: + result += f"# undef {ftm}\n" + result += f'# define {ftm} {entry["value"]}\n' + result += f"# endif\n" + else: + if entry["need_undef"]: + result += f"# undef {ftm}\n" + result += f'# define {ftm} {entry["value"]}\n' + + return result + + +def generate_version_header_implementation(data: Dict[str, Dict[str, Any]]) -> str: + """Generates the body of the version header.""" + + template = """#if _LIBCPP_STD_VER >= {dialect} +{feature_test_macros}#endif // _LIBCPP_STD_VER >= {dialect}""" + + result = [] + for std, ftms in data.items(): + result.append( + template.format( + dialect=std, + feature_test_macros=generate_version_header_dialect_block(ftms), + ) + ) + + return "\n\n".join(result) + + class FeatureTestMacros: """Provides all feature-test macro (FTM) output components. @@ -2107,12 +2156,106 @@ def implemented_ftms(self) -> Dict[str, Dict[str, Any]]: return get_ftms(self.__data, self.std_dialects, True) + @functools.cached_property + def ftm_metadata(self) -> Dict[str, Dict[str, Any]]: + """Returns the metadata of the FTMs defined in the Standard. + + The metadata does not depend on the C++ dialect used. + The result is a dict with the following contents: + - key: Name of the feature test macro. + - value: A dict with the following content: + * headers: The list of headers that should provide the FTM + * test_suite_guard: The condition for testing the FTM in the test suite. + * test_suite_guard: The condition for testing the FTM in the version header. + """ + result = dict() + for feature in self.__data: + entry = dict() + entry["headers"] = feature["headers"] + entry["test_suite_guard"] = feature.get("test_suite_guard", None) + entry["libcxx_guard"] = feature.get("libcxx_guard", None) + result[feature["name"]] = entry + + return result + + @property + def version_header_implementation(self) -> Dict[str, List[Dict[str, Any]]]: + """Generates the body of the version header.""" + result = dict() + for std in self.std_dialects: + result[get_std_number(std)] = list() + + for ftm, values in self.standard_ftms.items(): + need_undef = False + last_value = None + for std, value in values.items(): + # When a newer Standard does not change the value of the macro + # there is no need to redefine it with the same value. + if last_value and value == last_value: + continue + last_value = value + + entry = dict() + entry["value"] = value + entry["implemented"] = self.implemented_ftms[ftm][std] != None + entry["need_undef"] = need_undef + entry["condition"] = self.ftm_metadata[ftm]["libcxx_guard"] + + need_undef = entry["implemented"] + + result[get_std_number(std)].append(dict({ftm: entry})) + + return result + + @property + def version_header(self) -> str: + """Generates the version header.""" + template = """// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP_VERSION +#define _LIBCPP_VERSION + +#include <__config> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +{feature_test_macros} + +#endif // _LIBCPP_VERSION +""" + return template.format( + feature_test_macros=generate_version_header_implementation( + self.version_header_implementation + ) + ) + def main(): produce_version_header() produce_tests() produce_docs() + # Example how to use the new version header generation function to generate + # the file. + if False: + ftm = FeatureTestMacros( + os.path.join( + source_root, "test", "libcxx", "feature_test_macro", "test_data.json" + ) + ) + version_header_path = os.path.join(include_path, "version") + with open(version_header_path, "w", newline="\n") as f: + f.write(ftm.version_header) + if __name__ == "__main__": main()