[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