[llvm-branch-commits] [libcxx] [libc++] Implements the new FTM header test generator. (PR #134542)
Mark de Wever via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sun Apr 6 08:04:09 PDT 2025
https://github.com/mordante created https://github.com/llvm/llvm-project/pull/134542
This generator has almost identical output to the existing script. Notable differences are
- conditionally include not yet implemented headers
- removes the synopsis
- uses 2 spaces indent in `# if`
There are a few more test macros added that triggered bugs in existing FTM.
>From 00cf2e7d9b6945ae738f1487e6f4dd9222e31b79 Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Sat, 5 Apr 2025 12:15:10 +0200
Subject: [PATCH] [libc++] Implements the new FTM header test generator.
This generator has almost identical output to the existing script.
Notable differences are
- conditionally include not yet implemented headers
- removes the synopsis
- uses 2 spaces indent in `# if`
There are a few more test macros added that triggered bugs in existing
FTM.
---
.../feature_test_macro/ftm_metadata.sh.py | 36 +-
.../generate_header_test.sh.py | 643 ++++++++++++++++++
.../feature_test_macro/implemented_ftms.sh.py | 9 +-
...implemented_standard_library_headers.sh.py | 32 +
.../feature_test_macro/standard_ftms.sh.py | 14 +-
.../standard_library_headers.sh.py | 33 +
.../libcxx/feature_test_macro/test_data.json | 28 +-
.../feature_test_macro/version_header.sh.py | 8 +-
.../version_header_implementation.sh.py | 22 +-
.../generate_feature_test_macro_components.py | 284 +++++++-
10 files changed, 1092 insertions(+), 17 deletions(-)
create mode 100644 libcxx/test/libcxx/feature_test_macro/generate_header_test.sh.py
create mode 100644 libcxx/test/libcxx/feature_test_macro/implemented_standard_library_headers.sh.py
create mode 100644 libcxx/test/libcxx/feature_test_macro/standard_library_headers.sh.py
diff --git a/libcxx/test/libcxx/feature_test_macro/ftm_metadata.sh.py b/libcxx/test/libcxx/feature_test_macro/ftm_metadata.sh.py
index 52696d8bc3605..7cf35b2a21d93 100644
--- a/libcxx/test/libcxx/feature_test_macro/ftm_metadata.sh.py
+++ b/libcxx/test/libcxx/feature_test_macro/ftm_metadata.sh.py
@@ -27,26 +27,52 @@ def setUp(self):
def test_implementation(self):
expected = {
"__cpp_lib_any": Metadata(
- headers=["any"], test_suite_guard=None, libcxx_guard=None
+ headers=["any"],
+ available_since="c++17",
+ test_suite_guard=None,
+ libcxx_guard=None,
),
"__cpp_lib_barrier": Metadata(
headers=["barrier"],
+ available_since="c++20",
test_suite_guard="!defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC)",
libcxx_guard="_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC",
),
+ "__cpp_lib_clamp": Metadata(
+ headers=["algorithm"],
+ available_since="c++17",
+ test_suite_guard=None,
+ libcxx_guard=None,
+ ),
"__cpp_lib_format": Metadata(
- headers=["format"], test_suite_guard=None, libcxx_guard=None
+ headers=["format"],
+ available_since="c++20",
+ test_suite_guard=None,
+ libcxx_guard=None,
),
"__cpp_lib_parallel_algorithm": Metadata(
headers=["algorithm", "numeric"],
+ available_since="c++17",
+ test_suite_guard=None,
+ libcxx_guard=None,
+ ),
+ "__cpp_lib_to_chars": Metadata(
+ headers=["charconv"],
+ available_since="c++17",
test_suite_guard=None,
libcxx_guard=None,
),
"__cpp_lib_variant": Metadata(
- headers=["variant"], test_suite_guard=None, libcxx_guard=None
+ headers=["variant"],
+ available_since="c++17",
+ test_suite_guard=None,
+ libcxx_guard=None,
),
- "__cpp_lib_missing_FTM_in_older_standard": Metadata(
- headers=[], test_suite_guard=None, libcxx_guard=None
+ "__cpp_lib_zz_missing_FTM_in_older_standard": Metadata(
+ headers=[],
+ available_since="c++17",
+ test_suite_guard=None,
+ libcxx_guard=None,
),
}
self.assertEqual(self.ftm.ftm_metadata, expected)
diff --git a/libcxx/test/libcxx/feature_test_macro/generate_header_test.sh.py b/libcxx/test/libcxx/feature_test_macro/generate_header_test.sh.py
new file mode 100644
index 0000000000000..cca5bae8e7e70
--- /dev/null
+++ b/libcxx/test/libcxx/feature_test_macro/generate_header_test.sh.py
@@ -0,0 +1,643 @@
+# ===----------------------------------------------------------------------===##
+#
+# 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 %t/tests
+
+import os
+import sys
+import unittest
+
+UTILS = sys.argv[1]
+TEST_DATA = sys.argv[2]
+OUTPUT_PATH = sys.argv[3]
+del sys.argv[1:4]
+
+sys.path.append(UTILS)
+from generate_feature_test_macro_components import FeatureTestMacros
+
+
+class Test(unittest.TestCase):
+ def setUp(self):
+ self.ftm = FeatureTestMacros(TEST_DATA)
+ self.maxDiff = None # This causes the diff to be printed when the test fails
+
+ self.expected = dict(
+ {
+ "algorithm": """\
+//===----------------------------------------------------------------------===//
+//
+// 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 test was generated by
+// generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <algorithm>
+
+// Test the feature test macros defined by <algorithm>
+
+// clang-format off
+
+#include <algorithm>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 17
+
+# ifdef __cpp_lib_clamp
+# error "__cpp_lib_clamp should not be defined before c++17"
+# endif
+
+# ifdef __cpp_lib_parallel_algorithm
+# error "__cpp_lib_parallel_algorithm should not be defined before c++17"
+# endif
+
+#elif TEST_STD_VER == 17
+
+# ifndef __cpp_lib_clamp
+# error "__cpp_lib_clamp should be defined in c++17"
+# endif
+# if __cpp_lib_clamp != 201603L
+# error "__cpp_lib_clamp should have the value 201603L in c++17"
+# endif
+
+# ifndef __cpp_lib_parallel_algorithm
+# error "__cpp_lib_parallel_algorithm should be defined in c++17"
+# endif
+# if __cpp_lib_parallel_algorithm != 201603L
+# error "__cpp_lib_parallel_algorithm should have the value 201603L in c++17"
+# endif
+
+#elif TEST_STD_VER == 20
+
+# ifndef __cpp_lib_clamp
+# error "__cpp_lib_clamp should be defined in c++20"
+# endif
+# if __cpp_lib_clamp != 201603L
+# error "__cpp_lib_clamp should have the value 201603L in c++20"
+# endif
+
+# ifndef __cpp_lib_parallel_algorithm
+# error "__cpp_lib_parallel_algorithm should be defined in c++20"
+# endif
+# if __cpp_lib_parallel_algorithm != 201603L
+# error "__cpp_lib_parallel_algorithm should have the value 201603L in c++20"
+# endif
+
+#elif TEST_STD_VER == 23
+
+# ifndef __cpp_lib_clamp
+# error "__cpp_lib_clamp should be defined in c++23"
+# endif
+# if __cpp_lib_clamp != 201603L
+# error "__cpp_lib_clamp should have the value 201603L in c++23"
+# endif
+
+# ifndef __cpp_lib_parallel_algorithm
+# error "__cpp_lib_parallel_algorithm should be defined in c++23"
+# endif
+# if __cpp_lib_parallel_algorithm != 201603L
+# error "__cpp_lib_parallel_algorithm should have the value 201603L in c++23"
+# endif
+
+#elif TEST_STD_VER > 23
+
+# ifndef __cpp_lib_clamp
+# error "__cpp_lib_clamp should be defined in c++26"
+# endif
+# if __cpp_lib_clamp != 201603L
+# error "__cpp_lib_clamp should have the value 201603L in c++26"
+# endif
+
+# ifndef __cpp_lib_parallel_algorithm
+# error "__cpp_lib_parallel_algorithm should be defined in c++26"
+# endif
+# if __cpp_lib_parallel_algorithm != 201603L
+# error "__cpp_lib_parallel_algorithm should have the value 201603L in c++26"
+# endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
+
+""",
+ "any": """\
+//===----------------------------------------------------------------------===//
+//
+// 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 test was generated by
+// generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <any>
+
+// Test the feature test macros defined by <any>
+
+// clang-format off
+
+#include <any>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 17
+
+# ifdef __cpp_lib_any
+# error "__cpp_lib_any should not be defined before c++17"
+# endif
+
+#elif TEST_STD_VER == 17
+
+# ifndef __cpp_lib_any
+# error "__cpp_lib_any should be defined in c++17"
+# endif
+# if __cpp_lib_any != 201606L
+# error "__cpp_lib_any should have the value 201606L in c++17"
+# endif
+
+#elif TEST_STD_VER == 20
+
+# ifndef __cpp_lib_any
+# error "__cpp_lib_any should be defined in c++20"
+# endif
+# if __cpp_lib_any != 201606L
+# error "__cpp_lib_any should have the value 201606L in c++20"
+# endif
+
+#elif TEST_STD_VER == 23
+
+# ifndef __cpp_lib_any
+# error "__cpp_lib_any should be defined in c++23"
+# endif
+# if __cpp_lib_any != 201606L
+# error "__cpp_lib_any should have the value 201606L in c++23"
+# endif
+
+#elif TEST_STD_VER > 23
+
+# ifndef __cpp_lib_any
+# error "__cpp_lib_any should be defined in c++26"
+# endif
+# if __cpp_lib_any != 201606L
+# error "__cpp_lib_any should have the value 201606L in c++26"
+# endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
+
+""",
+ "barrier": """\
+//===----------------------------------------------------------------------===//
+//
+// 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 test was generated by
+// generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// UNSUPPORTED: no-threads
+
+// <barrier>
+
+// Test the feature test macros defined by <barrier>
+
+// clang-format off
+
+#include <barrier>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 17
+
+# ifdef __cpp_lib_barrier
+# error "__cpp_lib_barrier should not be defined before c++20"
+# endif
+
+#elif TEST_STD_VER == 17
+
+# ifdef __cpp_lib_barrier
+# error "__cpp_lib_barrier should not be defined before c++20"
+# endif
+
+#elif TEST_STD_VER == 20
+
+# if !defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC)
+# ifndef __cpp_lib_barrier
+# error "__cpp_lib_barrier should be defined in c++20"
+# endif
+# if __cpp_lib_barrier != 201907L
+# error "__cpp_lib_barrier should have the value 201907L in c++20"
+# endif
+# else
+# ifdef __cpp_lib_barrier
+# error "__cpp_lib_barrier should not be defined when the requirement '!defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC)' is not met!"
+# endif
+# endif
+
+#elif TEST_STD_VER == 23
+
+# if !defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC)
+# ifndef __cpp_lib_barrier
+# error "__cpp_lib_barrier should be defined in c++23"
+# endif
+# if __cpp_lib_barrier != 201907L
+# error "__cpp_lib_barrier should have the value 201907L in c++23"
+# endif
+# else
+# ifdef __cpp_lib_barrier
+# error "__cpp_lib_barrier should not be defined when the requirement '!defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC)' is not met!"
+# endif
+# endif
+
+#elif TEST_STD_VER > 23
+
+# if !defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC)
+# ifndef __cpp_lib_barrier
+# error "__cpp_lib_barrier should be defined in c++26"
+# endif
+# if __cpp_lib_barrier != 299900L
+# error "__cpp_lib_barrier should have the value 299900L in c++26"
+# endif
+# else
+# ifdef __cpp_lib_barrier
+# error "__cpp_lib_barrier should not be defined when the requirement '!defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC)' is not met!"
+# endif
+# endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
+
+""",
+ "charconv": """\
+//===----------------------------------------------------------------------===//
+//
+// 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 test was generated by
+// generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <charconv>
+
+// Test the feature test macros defined by <charconv>
+
+// clang-format off
+
+#if __has_include(<charconv>)
+# include <charconv>
+#endif
+#include "test_macros.h"
+
+#if TEST_STD_VER < 17
+
+# ifdef __cpp_lib_to_chars
+# error "__cpp_lib_to_chars should not be defined before c++17"
+# endif
+
+#elif TEST_STD_VER == 17
+
+# if !defined(_LIBCPP_VERSION)
+# ifndef __cpp_lib_to_chars
+# error "__cpp_lib_to_chars should be defined in c++17"
+# endif
+# if __cpp_lib_to_chars != 201611L
+# error "__cpp_lib_to_chars should have the value 201611L in c++17"
+# endif
+# else
+# ifdef __cpp_lib_to_chars
+# error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
+# endif
+# endif
+
+#elif TEST_STD_VER == 20
+
+# if !defined(_LIBCPP_VERSION)
+# ifndef __cpp_lib_to_chars
+# error "__cpp_lib_to_chars should be defined in c++20"
+# endif
+# if __cpp_lib_to_chars != 201611L
+# error "__cpp_lib_to_chars should have the value 201611L in c++20"
+# endif
+# else
+# ifdef __cpp_lib_to_chars
+# error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
+# endif
+# endif
+
+#elif TEST_STD_VER == 23
+
+# if !defined(_LIBCPP_VERSION)
+# ifndef __cpp_lib_to_chars
+# error "__cpp_lib_to_chars should be defined in c++23"
+# endif
+# if __cpp_lib_to_chars != 201611L
+# error "__cpp_lib_to_chars should have the value 201611L in c++23"
+# endif
+# else
+# ifdef __cpp_lib_to_chars
+# error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
+# endif
+# endif
+
+#elif TEST_STD_VER > 23
+
+# if !defined(_LIBCPP_VERSION)
+# ifndef __cpp_lib_to_chars
+# error "__cpp_lib_to_chars should be defined in c++26"
+# endif
+# if __cpp_lib_to_chars != 201611L
+# error "__cpp_lib_to_chars should have the value 201611L in c++26"
+# endif
+# else
+# ifdef __cpp_lib_to_chars
+# error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
+# endif
+# endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
+
+""",
+ "format": """\
+//===----------------------------------------------------------------------===//
+//
+// 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 test was generated by
+// generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <format>
+
+// Test the feature test macros defined by <format>
+
+// clang-format off
+
+#include <format>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 17
+
+# ifdef __cpp_lib_format
+# error "__cpp_lib_format should not be defined before c++20"
+# endif
+
+#elif TEST_STD_VER == 17
+
+# ifdef __cpp_lib_format
+# error "__cpp_lib_format should not be defined before c++20"
+# endif
+
+#elif TEST_STD_VER == 20
+
+# if !defined(_LIBCPP_VERSION)
+# ifndef __cpp_lib_format
+# error "__cpp_lib_format should be defined in c++20"
+# endif
+# if __cpp_lib_format != 202110L
+# error "__cpp_lib_format should have the value 202110L in c++20"
+# endif
+# else
+# ifdef __cpp_lib_format
+# error "__cpp_lib_format should not be defined because it is unimplemented in libc++!"
+# endif
+# endif
+
+#elif TEST_STD_VER == 23
+
+# if !defined(_LIBCPP_VERSION)
+# ifndef __cpp_lib_format
+# error "__cpp_lib_format should be defined in c++23"
+# endif
+# if __cpp_lib_format != 202207L
+# error "__cpp_lib_format should have the value 202207L in c++23"
+# endif
+# else
+# ifdef __cpp_lib_format
+# error "__cpp_lib_format should not be defined because it is unimplemented in libc++!"
+# endif
+# endif
+
+#elif TEST_STD_VER > 23
+
+# if !defined(_LIBCPP_VERSION)
+# ifndef __cpp_lib_format
+# error "__cpp_lib_format should be defined in c++26"
+# endif
+# if __cpp_lib_format != 202311L
+# error "__cpp_lib_format should have the value 202311L in c++26"
+# endif
+# else
+# ifdef __cpp_lib_format
+# error "__cpp_lib_format should not be defined because it is unimplemented in libc++!"
+# endif
+# endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
+
+""",
+ "numeric": """\
+//===----------------------------------------------------------------------===//
+//
+// 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 test was generated by
+// generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <numeric>
+
+// Test the feature test macros defined by <numeric>
+
+// clang-format off
+
+#include <numeric>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 17
+
+# ifdef __cpp_lib_parallel_algorithm
+# error "__cpp_lib_parallel_algorithm should not be defined before c++17"
+# endif
+
+#elif TEST_STD_VER == 17
+
+# ifndef __cpp_lib_parallel_algorithm
+# error "__cpp_lib_parallel_algorithm should be defined in c++17"
+# endif
+# if __cpp_lib_parallel_algorithm != 201603L
+# error "__cpp_lib_parallel_algorithm should have the value 201603L in c++17"
+# endif
+
+#elif TEST_STD_VER == 20
+
+# ifndef __cpp_lib_parallel_algorithm
+# error "__cpp_lib_parallel_algorithm should be defined in c++20"
+# endif
+# if __cpp_lib_parallel_algorithm != 201603L
+# error "__cpp_lib_parallel_algorithm should have the value 201603L in c++20"
+# endif
+
+#elif TEST_STD_VER == 23
+
+# ifndef __cpp_lib_parallel_algorithm
+# error "__cpp_lib_parallel_algorithm should be defined in c++23"
+# endif
+# if __cpp_lib_parallel_algorithm != 201603L
+# error "__cpp_lib_parallel_algorithm should have the value 201603L in c++23"
+# endif
+
+#elif TEST_STD_VER > 23
+
+# ifndef __cpp_lib_parallel_algorithm
+# error "__cpp_lib_parallel_algorithm should be defined in c++26"
+# endif
+# if __cpp_lib_parallel_algorithm != 201603L
+# error "__cpp_lib_parallel_algorithm should have the value 201603L in c++26"
+# endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
+
+""",
+ "variant": """\
+//===----------------------------------------------------------------------===//
+//
+// 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 test was generated by
+// generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <variant>
+
+// Test the feature test macros defined by <variant>
+
+// clang-format off
+
+#include <variant>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 17
+
+# ifdef __cpp_lib_variant
+# error "__cpp_lib_variant should not be defined before c++17"
+# endif
+
+#elif TEST_STD_VER == 17
+
+# ifndef __cpp_lib_variant
+# error "__cpp_lib_variant should be defined in c++17"
+# endif
+# if __cpp_lib_variant != 202102L
+# error "__cpp_lib_variant should have the value 202102L in c++17"
+# endif
+
+#elif TEST_STD_VER == 20
+
+# if !defined(_LIBCPP_VERSION)
+# ifndef __cpp_lib_variant
+# error "__cpp_lib_variant should be defined in c++20"
+# endif
+# if __cpp_lib_variant != 202106L
+# error "__cpp_lib_variant should have the value 202106L in c++20"
+# endif
+# else
+# ifdef __cpp_lib_variant
+# error "__cpp_lib_variant should not be defined because it is unimplemented in libc++!"
+# endif
+# endif
+
+#elif TEST_STD_VER == 23
+
+# if !defined(_LIBCPP_VERSION)
+# ifndef __cpp_lib_variant
+# error "__cpp_lib_variant should be defined in c++23"
+# endif
+# if __cpp_lib_variant != 202106L
+# error "__cpp_lib_variant should have the value 202106L in c++23"
+# endif
+# else
+# ifdef __cpp_lib_variant
+# error "__cpp_lib_variant should not be defined because it is unimplemented in libc++!"
+# endif
+# endif
+
+#elif TEST_STD_VER > 23
+
+# if !defined(_LIBCPP_VERSION)
+# ifndef __cpp_lib_variant
+# error "__cpp_lib_variant should be defined in c++26"
+# endif
+# if __cpp_lib_variant != 202306L
+# error "__cpp_lib_variant should have the value 202306L in c++26"
+# endif
+# else
+# ifdef __cpp_lib_variant
+# error "__cpp_lib_variant should not be defined because it is unimplemented in libc++!"
+# endif
+# endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
+
+""",
+ }
+ )
+
+ def test_implementation(self):
+ # Generate the output
+ self.ftm.generate_header_test_directory(OUTPUT_PATH)
+
+ for key, value in self.expected.items():
+ # Test whether the per header generate function generates the proper output.
+ self.assertEqual(self.ftm.generate_header_test(key), value)
+
+ # Test whether all header generate function generates the proper output.
+ with open(
+ os.path.join(OUTPUT_PATH, f"{key}.version.compile.pass.cpp"),
+ "r",
+ newline="\n",
+ ) as f:
+ self.assertEqual(f.read(), value)
+
+
+if __name__ == "__main__":
+ unittest.main()
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 4f445d55c883c..1ed7524abf488 100644
--- a/libcxx/test/libcxx/feature_test_macro/implemented_ftms.sh.py
+++ b/libcxx/test/libcxx/feature_test_macro/implemented_ftms.sh.py
@@ -38,6 +38,12 @@ def test_implementation(self):
"c++23": "201907L",
"c++26": "299900L",
},
+ "__cpp_lib_clamp": {
+ "c++17": "201603L",
+ "c++20": "201603L",
+ "c++23": "201603L",
+ "c++26": "201603L",
+ },
"__cpp_lib_format": {},
"__cpp_lib_parallel_algorithm": {
"c++17": "201603L",
@@ -45,13 +51,14 @@ def test_implementation(self):
"c++23": "201603L",
"c++26": "201603L",
},
+ "__cpp_lib_to_chars": {},
"__cpp_lib_variant": {
"c++17": "202102L",
"c++20": "202102L",
"c++23": "202102L",
"c++26": "202102L",
},
- "__cpp_lib_missing_FTM_in_older_standard": {},
+ "__cpp_lib_zz_missing_FTM_in_older_standard": {},
}
self.assertEqual(self.ftm.implemented_ftms, expected)
diff --git a/libcxx/test/libcxx/feature_test_macro/implemented_standard_library_headers.sh.py b/libcxx/test/libcxx/feature_test_macro/implemented_standard_library_headers.sh.py
new file mode 100644
index 0000000000000..4904ba24ccc05
--- /dev/null
+++ b/libcxx/test/libcxx/feature_test_macro/implemented_standard_library_headers.sh.py
@@ -0,0 +1,32 @@
+# ===----------------------------------------------------------------------===##
+#
+# 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(
+ sorted(ftm.implemented_standard_library_headers),
+ [
+ "algorithm",
+ "any",
+ "barrier",
+ "format",
+ "numeric",
+ "variant",
+ ],
+)
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 ac3e284261d03..5c63e3e9d0ad9 100644
--- a/libcxx/test/libcxx/feature_test_macro/standard_ftms.sh.py
+++ b/libcxx/test/libcxx/feature_test_macro/standard_ftms.sh.py
@@ -37,6 +37,12 @@ def test_implementation(self):
"c++23": "201907L",
"c++26": "299900L",
},
+ "__cpp_lib_clamp": {
+ "c++17": "201603L",
+ "c++20": "201603L",
+ "c++23": "201603L",
+ "c++26": "201603L",
+ },
"__cpp_lib_format": {
"c++20": "202110L",
"c++23": "202207L",
@@ -48,13 +54,19 @@ def test_implementation(self):
"c++23": "201603L",
"c++26": "201603L",
},
+ "__cpp_lib_to_chars": {
+ "c++17": "201611L",
+ "c++20": "201611L",
+ "c++23": "201611L",
+ "c++26": "201611L",
+ },
"__cpp_lib_variant": {
"c++17": "202102L",
"c++20": "202106L",
"c++23": "202106L",
"c++26": "202306L",
},
- "__cpp_lib_missing_FTM_in_older_standard": {
+ "__cpp_lib_zz_missing_FTM_in_older_standard": {
"c++17": "2017L",
"c++20": "2020L",
"c++23": "2020L",
diff --git a/libcxx/test/libcxx/feature_test_macro/standard_library_headers.sh.py b/libcxx/test/libcxx/feature_test_macro/standard_library_headers.sh.py
new file mode 100644
index 0000000000000..9cc184bcd9447
--- /dev/null
+++ b/libcxx/test/libcxx/feature_test_macro/standard_library_headers.sh.py
@@ -0,0 +1,33 @@
+# ===----------------------------------------------------------------------===##
+#
+# 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(
+ sorted(ftm.standard_library_headers),
+ [
+ "algorithm",
+ "any",
+ "barrier",
+ "charconv",
+ "format",
+ "numeric",
+ "variant",
+ ],
+)
diff --git a/libcxx/test/libcxx/feature_test_macro/test_data.json b/libcxx/test/libcxx/feature_test_macro/test_data.json
index fd698c08b2daa..b0122163f714f 100644
--- a/libcxx/test/libcxx/feature_test_macro/test_data.json
+++ b/libcxx/test/libcxx/feature_test_macro/test_data.json
@@ -38,6 +38,19 @@
"test_suite_guard": "!defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC)",
"libcxx_guard": "_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC"
},
+ {
+ "name": "__cpp_lib_clamp",
+ "values": {
+ "c++17": {
+ "201603": [
+ {
+ "implemented": true
+ }
+ ]
+ }
+ },
+ "headers": ["algorithm"]
+ },
{
"name": "__cpp_lib_format",
"values": {
@@ -120,6 +133,19 @@
"numeric"
]
},
+ {
+ "name": "__cpp_lib_to_chars",
+ "values": {
+ "c++17": {
+ "201611": [
+ {
+ "implemented": false
+ }
+ ]
+ }
+ },
+ "headers": ["charconv"]
+ },
{
"name": "__cpp_lib_variant",
"values": {
@@ -155,7 +181,7 @@
]
},
{
- "name": "__cpp_lib_missing_FTM_in_older_standard",
+ "name": "__cpp_lib_zz_missing_FTM_in_older_standard",
"values": {
"c++17": {
"2017": [
diff --git a/libcxx/test/libcxx/feature_test_macro/version_header.sh.py b/libcxx/test/libcxx/feature_test_macro/version_header.sh.py
index e3053c18a5211..7ef2079505aee 100644
--- a/libcxx/test/libcxx/feature_test_macro/version_header.sh.py
+++ b/libcxx/test/libcxx/feature_test_macro/version_header.sh.py
@@ -45,9 +45,11 @@ def test_implementeation(self):
#if _LIBCPP_STD_VER >= 17
# define __cpp_lib_any 201606L
+# define __cpp_lib_clamp 201603L
# define __cpp_lib_parallel_algorithm 201603L
+// define __cpp_lib_to_chars 201611L
# define __cpp_lib_variant 202102L
-// define __cpp_lib_missing_FTM_in_older_standard 2017L
+// define __cpp_lib_zz_missing_FTM_in_older_standard 2017L
#endif // _LIBCPP_STD_VER >= 17
#if _LIBCPP_STD_VER >= 20
@@ -56,7 +58,7 @@ def test_implementeation(self):
# endif
// define __cpp_lib_format 202110L
// define __cpp_lib_variant 202106L
-// define __cpp_lib_missing_FTM_in_older_standard 2020L
+// define __cpp_lib_zz_missing_FTM_in_older_standard 2020L
#endif // _LIBCPP_STD_VER >= 20
#if _LIBCPP_STD_VER >= 23
@@ -70,7 +72,7 @@ def test_implementeation(self):
# endif
// define __cpp_lib_format 202311L
// define __cpp_lib_variant 202306L
-// define __cpp_lib_missing_FTM_in_older_standard 2026L
+// define __cpp_lib_zz_missing_FTM_in_older_standard 2026L
#endif // _LIBCPP_STD_VER >= 26
#endif // _LIBCPP_VERSIONH
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
index 2ab6a9be7339b..182aca9862a4f 100644
--- a/libcxx/test/libcxx/feature_test_macro/version_header_implementation.sh.py
+++ b/libcxx/test/libcxx/feature_test_macro/version_header_implementation.sh.py
@@ -35,6 +35,14 @@ def test_implementation(self):
condition=None,
),
},
+ {
+ "__cpp_lib_clamp": VersionHeader(
+ value="201603L",
+ implemented=True,
+ need_undef=False,
+ condition=None,
+ )
+ },
{
"__cpp_lib_parallel_algorithm": VersionHeader(
value="201603L",
@@ -43,6 +51,14 @@ def test_implementation(self):
condition=None,
),
},
+ {
+ "__cpp_lib_to_chars": VersionHeader(
+ value="201611L",
+ implemented=False,
+ need_undef=False,
+ condition=None,
+ ),
+ },
{
"__cpp_lib_variant": VersionHeader(
value="202102L",
@@ -52,7 +68,7 @@ def test_implementation(self):
),
},
{
- "__cpp_lib_missing_FTM_in_older_standard": VersionHeader(
+ "__cpp_lib_zz_missing_FTM_in_older_standard": VersionHeader(
value="2017L",
implemented=False,
need_undef=False,
@@ -86,7 +102,7 @@ def test_implementation(self):
),
},
{
- "__cpp_lib_missing_FTM_in_older_standard": VersionHeader(
+ "__cpp_lib_zz_missing_FTM_in_older_standard": VersionHeader(
value="2020L",
implemented=False,
need_undef=False,
@@ -130,7 +146,7 @@ def test_implementation(self):
),
},
{
- "__cpp_lib_missing_FTM_in_older_standard": VersionHeader(
+ "__cpp_lib_zz_missing_FTM_in_older_standard": VersionHeader(
value="2026L",
implemented=False,
need_undef=False,
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index ecf31f5af7fb2..541e32b62437d 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -10,6 +10,7 @@
List, # Needed for python 3.8 compatibility.
NewType,
Optional,
+ Set,
)
import functools
import json
@@ -1967,6 +1968,7 @@ def produce_docs():
@dataclass
class Metadata:
headers: List[str] = None
+ available_since: Std = None
test_suite_guard: str = None
libcxx_guard: str = None
@@ -1979,6 +1981,12 @@ class VersionHeader:
condition: str = None
+ at dataclass
+class FtmHeaderTest:
+ value: Value = None
+ implemented: bool = None
+ condition: str = None
+
def get_ftms(
data, std_dialects: List[Std], use_implemented_status: bool
) -> Dict[Ftm, Dict[Std, Optional[Value]]]:
@@ -2069,6 +2077,102 @@ def generate_version_header_implementation(
return "\n\n".join(result)
+#
+# The templates used to create a FTM test file
+#
+
+
+ftm_header_test_file_contents = """//===----------------------------------------------------------------------===//
+//
+// 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 test was generated by
+// {script_name}
+// and should not be edited manually.
+
+{lit_markup}// <{header}>
+
+// Test the feature test macros defined by <{header}>
+
+// clang-format off
+
+{include}
+#include "test_macros.h"
+{data}
+
+// clang-format on
+
+"""
+
+
+ftm_header_test_file_include_unconditional = """\
+#include <{header}>\
+"""
+
+ftm_header_test_file_include_conditional = """\
+#if __has_include(<{header}>)
+# include <{header}>
+#endif\
+"""
+
+ftm_header_test_file_dialect_block = """
+#{pp_if} TEST_STD_VER {operator} {dialect}
+{tests}\
+"""
+
+#
+# The templates used for a single FTM block in the tests.
+#
+
+ftm_unavailable_in_dialect = """
+# ifdef {ftm}
+# error "{ftm} should not be defined before {dialect}"
+# endif
+"""
+
+libcxx_ftm_not_implemented = """
+# if !defined(_LIBCPP_VERSION)
+# ifndef {ftm}
+# error "{ftm} should be defined in {dialect}"
+# endif
+# if {ftm} != {value}
+# error "{ftm} should have the value {value} in {dialect}"
+# endif
+# else
+# ifdef {ftm}
+# error "{ftm} should not be defined because it is unimplemented in libc++!"
+# endif
+# endif
+"""
+
+libcxx_ftm_conditionally_implemented = """
+# if {condition}
+# ifndef {ftm}
+# error "{ftm} should be defined in {dialect}"
+# endif
+# if {ftm} != {value}
+# error "{ftm} should have the value {value} in {dialect}"
+# endif
+# else
+# ifdef {ftm}
+# error "{ftm} should not be defined when the requirement '{condition}' is not met!"
+# endif
+# endif
+"""
+
+libcxx_ftm_implemented = """
+# ifndef {ftm}
+# error "{ftm} should be defined in {dialect}"
+# endif
+# if {ftm} != {value}
+# error "{ftm} should have the value {value} in {dialect}"
+# endif
+"""
+
class FeatureTestMacros:
"""Provides all feature-test macro (FTM) output components.
@@ -2199,6 +2303,17 @@ def standard_ftms(self) -> Dict[Ftm, Dict[Std, Optional[Value]]]:
"""
return get_ftms(self.__data, self.std_dialects, False)
+ @functools.cached_property
+ def standard_library_headers(self) -> Set[str]:
+ """Returns a list of headers that contain at least one FTM."""
+
+ result = set()
+ for value in self.ftm_metadata.values():
+ for header in value.headers:
+ result.add(header)
+
+ return list(result)
+
@functools.cached_property
def implemented_ftms(self) -> Dict[Ftm, Dict[Std, Optional[Value]]]:
"""Returns the FTM versions per dialect implemented in libc++.
@@ -2209,6 +2324,33 @@ def implemented_ftms(self) -> Dict[Ftm, Dict[Std, Optional[Value]]]:
return get_ftms(self.__data, self.std_dialects, True)
+ @functools.cached_property
+ def implemented_standard_library_headers(self) -> Set[str]:
+ """Returns a list of headers that contain at least one paper implemented in libc++.
+
+ When a paper is implemented it means the associated header(s) should exist.
+ """
+
+ result = set()
+ for feature in self.__data:
+ for std in self.std_dialects:
+ if std in feature["values"].keys():
+ values = feature["values"][std]
+ assert len(values) > 0, f"{feature['name']}[{std}] has no entries"
+ for value in values:
+ papers = list(values[value])
+ assert (
+ len(papers) > 0
+ ), f"{feature['name']}[{std}][{value}] has no entries"
+ for paper in papers:
+ if paper["implemented"]:
+ for header in self.ftm_metadata[feature["name"]].headers:
+ result.add(header)
+ break
+
+ return result
+
+
def is_implemented(self, ftm: Ftm, std: Std) -> bool:
"""Has the FTM `ftm` been implemented in the dialect `std`?"""
@@ -2222,7 +2364,6 @@ def is_implemented(self, ftm: Ftm, std: Std) -> bool:
return self.implemented_ftms[ftm][std] == self.standard_ftms[ftm][std]
-
@functools.cached_property
def ftm_metadata(self) -> Dict[Ftm, Metadata]:
"""Returns the metadata of the FTMs defined in the Standard.
@@ -2233,6 +2374,7 @@ def ftm_metadata(self) -> Dict[Ftm, Metadata]:
for feature in self.__data:
result[feature["name"]] = Metadata(
feature["headers"],
+ list(feature["values"])[0],
feature.get("test_suite_guard", None),
feature.get("libcxx_guard", None),
)
@@ -2300,14 +2442,148 @@ def version_header(self) -> str:
)
)
+ def header_ftm(self, header: str) -> Dict[Std, List[Dict[Ftm, FtmHeaderTest]]]:
+ """Generates the FTM information for a `header`."""
+
+ result = dict()
+ for std in self.std_dialects:
+ result[get_std_number(std)] = list()
+
+ for ftm, values in self.standard_ftms.items():
+ if not header in self.ftm_metadata[ftm].headers:
+ continue
+
+ last_value = None
+ last_entry = None
+
+ for std in self.std_dialects:
+ if not std in values.keys():
+ result[get_std_number(std)].append(dict({ftm: None}))
+ continue
+
+ result[get_std_number(std)].append(
+ dict(
+ {
+ ftm: FtmHeaderTest(
+ values[std],
+ self.is_implemented(ftm, std),
+ self.ftm_metadata[ftm].test_suite_guard,
+ )
+ }
+ )
+ )
+
+ return result
+
+
+ def generate_header_test_ftm(self, std: Std, ftm: Ftm, value: FtmHeaderTest) -> str:
+ """Adds a single `ftm` test for C++ `std` based on the status information in `value`.
+
+ When std == None this test is generating the TEST_STD_VER < MIN. Where
+ MIN is the minimum version that has a FTM defined. (In the real data
+ this is 14, since FTM have been introduced in C++14.)
+ """
+
+ if std == None or value == None:
+ return ftm_unavailable_in_dialect.format(
+ ftm=ftm, dialect=self.ftm_metadata[ftm].available_since
+ )
+
+ if not value.implemented:
+ return libcxx_ftm_not_implemented.format(
+ ftm=ftm, value=value.value, dialect=std
+ )
+
+ if self.ftm_metadata[ftm].test_suite_guard:
+ return libcxx_ftm_conditionally_implemented.format(
+ ftm=ftm,
+ value=value.value,
+ dialect=std,
+ condition=self.ftm_metadata[ftm].test_suite_guard,
+ )
+
+ return libcxx_ftm_implemented.format(ftm=ftm, value=value.value, dialect=std)
+
+ def generate_header_test_dialect(
+ self, std: Std, data: List[Dict[Ftm, FtmHeaderTest]]
+ ) -> str:
+ """Returns the body a single `std` for the FTM test of a `header`."""
+ return "".join(
+ self.generate_header_test_ftm(std, ftm, value)
+ for element in data
+ for ftm, value in element.items()
+ )
+
+ def generate_lit_markup(self, header:str) -> str:
+ if not header in lit_markup.keys():
+ return ""
+
+ return "\n".join(f"// {markup}" for markup in lit_markup[header]) + "\n\n"
+
+ def generate_header_test(self, header: str) -> str:
+ """Returns the body for the FTM test of a `header`."""
+
+ return ftm_header_test_file_contents.format(
+ script_name=script_name,
+ lit_markup=self.generate_lit_markup(header),
+ header=header,
+ include=(
+ ftm_header_test_file_include_unconditional.format(header=header)
+ if header in self.implemented_standard_library_headers
+ else ftm_header_test_file_include_conditional.format(header=header)
+ ),
+ data=
+ # FTM block before the first Standard that introduced them.
+ # This test the macros are not available before this version.
+ ftm_header_test_file_dialect_block.format(
+ pp_if="if",
+ operator="<",
+ dialect=self.std_dialects[0][3:],
+ tests=self.generate_header_test_dialect(
+ None, next(iter(self.header_ftm(header).values()))
+ ),
+ )
+ +
+ # FTM for all Standards that have FTM defined.
+ # Note in libc++ the TEST_STD_VER contains 99 for the Standard
+ # in development, therefore the last entry uses a different #elif.
+ "".join(
+ ftm_header_test_file_dialect_block.format(
+ pp_if="elif",
+ operator="==" if std != self.std_dialects[-1][3:] else ">",
+ dialect=std
+ if std != self.std_dialects[-1][3:]
+ else self.std_dialects[-2][3:],
+ tests=self.generate_header_test_dialect(f"c++{std}", values),
+ )
+ for std, values in self.header_ftm(header).items()
+ )
+ +
+ # The final #endif for the last #elif block.
+ f"\n#endif // TEST_STD_VER > {self.std_dialects[-2][3:]}",
+ )
+
+ def generate_header_test_directory(self, path: os.path) -> None:
+ """Generates all FTM tests in the directory `path`."""
+
+ if not os.path.exists(path):
+ os.makedirs(path)
+
+ for header in self.standard_library_headers:
+ with open(
+ os.path.join(path, f"{header}.version.compile.pass.cpp"),
+ "w",
+ newline="\n",
+ ) as f:
+ f.write(self.generate_header_test(header))
+
def main():
produce_version_header()
produce_tests()
produce_docs()
- # Example how to use the new version header generation function to generate
- # the file.
+ # Example how to use the new generator to generate the output.
if False:
ftm = FeatureTestMacros(
os.path.join(
@@ -2318,6 +2594,8 @@ def main():
with open(version_header_path, "w", newline="\n") as f:
f.write(ftm.version_header)
+ ftm.generate_header_test_directory(macro_test_path)
+
if __name__ == "__main__":
main()
More information about the llvm-branch-commits
mailing list