[flang][OpenMP] Change clause modifier representation in parser (#116656)
[llvm-project.git] / libcxx / utils / generate_feature_test_macro_components.py
blob53fd44291b216a2b43a0271594183b34fb568f68
1 #!/usr/bin/env python
3 import os
4 from builtins import range
5 from functools import reduce
6 from typing import Any, Dict, List # Needed for python 3.8 compatibility.
7 import functools
8 import json
11 def get_libcxx_paths():
12 utils_path = os.path.dirname(os.path.abspath(__file__))
13 script_name = os.path.basename(__file__)
14 assert os.path.exists(utils_path)
15 src_root = os.path.dirname(utils_path)
16 include_path = os.path.join(src_root, "include")
17 assert os.path.exists(include_path)
18 docs_path = os.path.join(src_root, "docs")
19 assert os.path.exists(docs_path)
20 macro_test_path = os.path.join(
21 src_root,
22 "test",
23 "std",
24 "language.support",
25 "support.limits",
26 "support.limits.general",
28 assert os.path.exists(macro_test_path)
29 assert os.path.exists(
30 os.path.join(macro_test_path, "version.version.compile.pass.cpp")
32 return script_name, src_root, include_path, docs_path, macro_test_path
35 script_name, source_root, include_path, docs_path, macro_test_path = get_libcxx_paths()
38 def has_header(h):
39 h_path = os.path.join(include_path, h)
40 return os.path.exists(h_path)
43 def add_version_header(tc):
44 tc["headers"].append("version")
45 return tc
48 # ================ ============================================================
49 # Field Description
50 # ================ ============================================================
51 # name The name of the feature-test macro.
52 # values A dict whose keys are C++ versions and whose values are the
53 # value of the feature-test macro for that C++ version.
54 # (TODO: This isn't a very clean model for feature-test
55 # macros affected by multiple papers.)
56 # headers An array with the headers that should provide the
57 # feature-test macro.
58 # test_suite_guard An optional string field. When this field is provided,
59 # `libcxx_guard` must also be provided. This field is used
60 # only to generate the unit tests for the feature-test macros.
61 # It can't depend on macros defined in <__config> because the
62 # `test/std/` parts of the test suite are intended to be
63 # portable to any C++ standard library implementation, not
64 # just libc++. It may depend on
65 # * macros defined by the compiler itself, or
66 # * macros generated by CMake.
67 # In some cases we add also depend on macros defined in
68 # <__configuration/availability.h>.
69 # libcxx_guard An optional string field. When this field is provided,
70 # `test_suite_guard` must also be provided. This field is used
71 # only to guard the feature-test macro in <version>. It may
72 # be the same as `test_suite_guard`, or it may depend on
73 # macros defined in <__config>.
74 # unimplemented An optional Boolean field with the value `True`. This field
75 # is only used when a feature isn't fully implemented. Once
76 # you've fully implemented the feature, you should remove
77 # this field.
78 # ================ ============================================================
79 feature_test_macros = [
80 add_version_header(x)
81 for x in [
83 "name": "__cpp_lib_adaptor_iterator_pair_constructor",
84 "values": {"c++23": 202106},
85 "headers": ["queue", "stack"],
88 "name": "__cpp_lib_addressof_constexpr",
89 "values": {"c++17": 201603},
90 "headers": ["memory"],
93 "name": "__cpp_lib_allocate_at_least",
94 "values": {
95 # Note LWG3887 Version macro for allocate_at_least
96 "c++23": 202302, # P2652R2 Disallow User Specialization of allocator_traits
98 "headers": ["memory"],
101 "name": "__cpp_lib_allocator_traits_is_always_equal",
102 "values": {"c++17": 201411},
103 "headers": [
104 "deque",
105 "forward_list",
106 "list",
107 "map",
108 "memory",
109 "scoped_allocator",
110 "set",
111 "string",
112 "unordered_map",
113 "unordered_set",
114 "vector",
118 "name": "__cpp_lib_any",
119 "values": {"c++17": 201606},
120 "headers": ["any"],
123 "name": "__cpp_lib_apply",
124 "values": {"c++17": 201603},
125 "headers": ["tuple"],
128 "name": "__cpp_lib_array_constexpr",
129 "values": {"c++17": 201603, "c++20": 201811},
130 "headers": ["array", "iterator"],
133 "name": "__cpp_lib_as_const",
134 "values": {"c++17": 201510},
135 "headers": ["utility"],
138 "name": "__cpp_lib_associative_heterogeneous_erasure",
139 "values": {"c++23": 202110},
140 "headers": ["map", "set", "unordered_map", "unordered_set"],
141 "unimplemented": True,
144 "name": "__cpp_lib_associative_heterogeneous_insertion",
145 "values": {
146 "c++26": 202306 # P2363R5 Extending associative containers with the remaining heterogeneous overloads
148 "headers": ["map", "set", "unordered_map", "unordered_set"],
149 "unimplemented": True,
152 "name": "__cpp_lib_assume_aligned",
153 "values": {"c++20": 201811},
154 "headers": ["memory"],
157 "name": "__cpp_lib_atomic_flag_test",
158 "values": {"c++20": 201907},
159 "headers": ["atomic"],
162 "name": "__cpp_lib_atomic_float",
163 "values": {"c++20": 201711},
164 "headers": ["atomic"],
165 "unimplemented": True,
168 "name": "__cpp_lib_atomic_is_always_lock_free",
169 "values": {"c++17": 201603},
170 "headers": ["atomic"],
173 "name": "__cpp_lib_atomic_lock_free_type_aliases",
174 "values": {"c++20": 201907},
175 "headers": ["atomic"],
178 "name": "__cpp_lib_atomic_min_max",
179 "values": {"c++26": 202403}, # P0493R5: Atomic minimum/maximum
180 "headers": ["atomic"],
181 "unimplemented": True,
184 "name": "__cpp_lib_atomic_ref",
185 "values": {"c++20": 201806},
186 "headers": ["atomic"],
189 "name": "__cpp_lib_atomic_shared_ptr",
190 "values": {"c++20": 201711},
191 "headers": ["atomic"],
192 "unimplemented": True,
195 "name": "__cpp_lib_atomic_value_initialization",
196 "values": {"c++20": 201911},
197 "headers": ["atomic", "memory"],
200 "name": "__cpp_lib_atomic_wait",
201 "values": {"c++20": 201907},
202 "headers": ["atomic"],
203 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_SYNC",
204 "libcxx_guard": "_LIBCPP_AVAILABILITY_HAS_SYNC",
207 "name": "__cpp_lib_barrier",
208 "values": {"c++20": 201907},
209 "headers": ["barrier"],
210 "test_suite_guard": "!defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC)",
211 "libcxx_guard": "_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC",
214 "name": "__cpp_lib_bind_back",
215 "values": {
216 "c++23": 202202,
217 # "c++26": 202306, # P2714R1 Bind front and back to NTTP callables
219 "headers": ["functional"],
222 "name": "__cpp_lib_bind_front",
223 "values": {
224 "c++20": 201907,
225 "c++26": 202306, # P2714R1 Bind front and back to NTTP callables
227 "headers": ["functional"],
230 "name": "__cpp_lib_bit_cast",
231 "values": {"c++20": 201806},
232 "headers": ["bit"],
235 "name": "__cpp_lib_bitops",
236 "values": {"c++20": 201907},
237 "headers": ["bit"],
240 "name": "__cpp_lib_bitset",
241 "values": {"c++26": 202306}, # P2697R1 Interfacing bitset with string_view
242 "headers": ["bitset"],
245 "name": "__cpp_lib_bool_constant",
246 "values": {"c++17": 201505},
247 "headers": ["type_traits"],
250 "name": "__cpp_lib_bounded_array_traits",
251 "values": {"c++20": 201902},
252 "headers": ["type_traits"],
255 "name": "__cpp_lib_boyer_moore_searcher",
256 "values": {"c++17": 201603},
257 "headers": ["functional"],
260 "name": "__cpp_lib_byte",
261 "values": {"c++17": 201603},
262 "headers": ["cstddef"],
265 "name": "__cpp_lib_byteswap",
266 "values": {"c++23": 202110},
267 "headers": ["bit"],
270 "name": "__cpp_lib_char8_t",
271 "values": {"c++20": 201907},
272 "headers": [
273 "atomic",
274 "filesystem",
275 "istream",
276 "limits",
277 "locale",
278 "ostream",
279 "string",
280 "string_view",
282 "test_suite_guard": "defined(__cpp_char8_t)",
283 "libcxx_guard": "_LIBCPP_HAS_CHAR8_T",
286 "name": "__cpp_lib_chrono",
287 "values": {
288 "c++17": 201611,
289 # "c++26": 202306, # P2592R3 Hashing support for std::chrono value classes
291 "headers": ["chrono"],
294 "name": "__cpp_lib_chrono_udls",
295 "values": {"c++14": 201304},
296 "headers": ["chrono"],
299 "name": "__cpp_lib_clamp",
300 "values": {"c++17": 201603},
301 "headers": ["algorithm"],
304 "name": "__cpp_lib_complex_udls",
305 "values": {"c++14": 201309},
306 "headers": ["complex"],
309 "name": "__cpp_lib_concepts",
310 "values": {"c++20": 202002},
311 "headers": ["concepts"],
314 "name": "__cpp_lib_constexpr_algorithms",
315 "values": {
316 "c++20": 201806,
317 # "c++26": 202306, # P2562R1 constexpr Stable Sorting
319 "headers": ["algorithm", "utility"],
322 "name": "__cpp_lib_constexpr_bitset",
323 "values": {"c++23": 202207},
324 "headers": ["bitset"],
327 "name": "__cpp_lib_constexpr_charconv",
328 "values": {"c++23": 202207},
329 "headers": ["charconv"],
332 "name": "__cpp_lib_constexpr_cmath",
333 "values": {"c++23": 202202},
334 "headers": ["cmath", "cstdlib"],
335 "unimplemented": True,
338 "name": "__cpp_lib_constexpr_complex",
339 "values": {"c++20": 201711},
340 "headers": ["complex"],
343 "name": "__cpp_lib_constexpr_dynamic_alloc",
344 "values": {"c++20": 201907},
345 "headers": ["memory"],
348 "name": "__cpp_lib_constexpr_functional",
349 "values": {"c++20": 201907},
350 "headers": ["functional"],
353 "name": "__cpp_lib_constexpr_iterator",
354 "values": {"c++20": 201811},
355 "headers": ["iterator"],
358 "name": "__cpp_lib_constexpr_memory",
359 "values": {"c++20": 201811, "c++23": 202202},
360 "headers": ["memory"],
363 "name": "__cpp_lib_constexpr_new",
364 "values": {"c++26": 202406}, # P2747R2 constexpr placement new
365 "headers": ["new"],
366 "test_suite_guard": "!defined(_LIBCPP_ABI_VCRUNTIME)",
367 "libcxx_guard": "!defined(_LIBCPP_ABI_VCRUNTIME)",
370 "name": "__cpp_lib_constexpr_numeric",
371 "values": {"c++20": 201911},
372 "headers": ["numeric"],
375 "name": "__cpp_lib_constexpr_string",
376 "values": {"c++20": 201907},
377 "headers": ["string"],
380 "name": "__cpp_lib_constexpr_string_view",
381 "values": {"c++20": 201811},
382 "headers": ["string_view"],
385 "name": "__cpp_lib_constexpr_tuple",
386 "values": {"c++20": 201811},
387 "headers": ["tuple"],
390 "name": "__cpp_lib_constexpr_typeinfo",
391 "values": {"c++23": 202106},
392 "headers": ["typeinfo"],
395 "name": "__cpp_lib_constexpr_utility",
396 "values": {"c++20": 201811},
397 "headers": ["utility"],
400 "name": "__cpp_lib_constexpr_vector",
401 "values": {"c++20": 201907},
402 "headers": ["vector"],
405 "name": "__cpp_lib_constrained_equality",
406 "values": {"c++26": 202403}, # P2944R3: Comparisons for reference_wrapper
407 "headers": ["optional", "tuple", "utility", "variant"],
408 "unimplemented": True,
411 "name": "__cpp_lib_containers_ranges",
412 "values": {"c++23": 202202},
413 "headers": [
414 "deque",
415 "forward_list",
416 "list",
417 "map",
418 "queue",
419 "set",
420 "stack",
421 "string",
422 "unordered_map",
423 "unordered_set",
424 "vector",
428 "name": "__cpp_lib_copyable_function",
429 "values": {"c++26": 202306}, # P2548R6 copyable_function
430 "headers": ["functional"],
431 "unimplemented": True,
434 "name": "__cpp_lib_coroutine",
435 "values": {"c++20": 201902},
436 "headers": ["coroutine"],
439 "name": "__cpp_lib_debugging",
440 "values": {
441 "c++26": 202311, # P2546R5 Debugging Support
442 # "c++26": 202403, # P2810R4: is_debugger_present is_replaceable
444 "headers": ["debugging"],
445 "unimplemented": True,
448 "name": "__cpp_lib_default_template_type_for_algorithm_values",
449 "values": {"c++26": 202403}, # P2248R8: Enabling list-initialization for algorithms
450 "headers": ["algorithm", "deque", "forward_list", "list", "ranges", "string", "vector"],
451 "unimplemented": True,
454 "name": "__cpp_lib_destroying_delete",
455 "values": {"c++20": 201806},
456 "headers": ["new"],
457 "test_suite_guard": "TEST_STD_VER > 17 && defined(__cpp_impl_destroying_delete) && __cpp_impl_destroying_delete >= 201806L",
458 "libcxx_guard": "_LIBCPP_STD_VER >= 20 && defined(__cpp_impl_destroying_delete) && __cpp_impl_destroying_delete >= 201806L",
461 "name": "__cpp_lib_enable_shared_from_this",
462 "values": {"c++17": 201603},
463 "headers": ["memory"],
466 "name": "__cpp_lib_endian",
467 "values": {"c++20": 201907},
468 "headers": ["bit"],
471 "name": "__cpp_lib_erase_if",
472 "values": {"c++20": 202002},
473 "headers": [
474 "deque",
475 "forward_list",
476 "list",
477 "map",
478 "set",
479 "string",
480 "unordered_map",
481 "unordered_set",
482 "vector",
486 "name": "__cpp_lib_exchange_function",
487 "values": {"c++14": 201304},
488 "headers": ["utility"],
491 "name": "__cpp_lib_execution",
492 "values": {"c++17": 201603, "c++20": 201902},
493 "headers": ["execution"],
494 "unimplemented": True,
497 "name": "__cpp_lib_expected",
498 "values": {"c++23": 202211},
499 "headers": ["expected"],
502 "name": "__cpp_lib_filesystem",
503 "values": {"c++17": 201703},
504 "headers": ["filesystem"],
505 "test_suite_guard": "!defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_FILESYSTEM && _LIBCPP_AVAILABILITY_HAS_FILESYSTEM_LIBRARY)",
506 "libcxx_guard": "_LIBCPP_HAS_FILESYSTEM && _LIBCPP_AVAILABILITY_HAS_FILESYSTEM_LIBRARY",
509 "name": "__cpp_lib_format",
510 "values": {
511 "c++20": 202110,
512 # "c++23": 202207, Not implemented P2419R2 Clarify handling of encodings in localized formatting of chrono types
513 # "c++26": 202306, P2637R3 Member Visit (implemented)
514 # "c++26": 202311, P2918R2 Runtime format strings II (implemented)
516 # Note these three papers are adopted at the June 2023 meeting and have sequential numbering
517 # 202304 P2510R3 Formatting pointers (Implemented)
518 # 202305 P2757R3 Type-checking format args
519 # 202306 P2637R3 Member Visit
520 "headers": ["format"],
523 "name": "__cpp_lib_format_path",
524 "values": {"c++26": 202403}, # P2845R8: Formatting of std::filesystem::path
525 "headers": ["filesystem"],
526 "unimplemented": True,
529 "name": "__cpp_lib_format_ranges",
530 "values": {"c++23": 202207},
531 "headers": ["format"],
534 "name": "__cpp_lib_format_uchar",
535 "values": {
536 "c++20": 202311 # DR P2909R4 Fix formatting of code units as integers
538 "headers": [
539 "format" # TODO verify this entry since the paper was underspecified.
543 "name": "__cpp_lib_formatters",
544 "values": {"c++23": 202302},
545 "headers": ["stacktrace", "thread"],
546 "unimplemented": True,
549 "name": "__cpp_lib_forward_like",
550 "values": {"c++23": 202207},
551 "headers": ["utility"],
554 "name": "__cpp_lib_freestanding_algorithm",
555 "values": {
556 "c++26": 202311 # P2407R5 Freestanding Library: Partial Classes
558 "headers": ["algorithm"],
559 "unimplemented": True,
562 "name": "__cpp_lib_freestanding_array",
563 "values": {
564 "c++26": 202311 # P2407R5 Freestanding Library: Partial Classes
566 "headers": ["array"],
567 "unimplemented": True,
570 "name": "__cpp_lib_freestanding_cstring",
571 "values": {
572 "c++26": 202306 # P2338R4 Freestanding Library: Character primitives and the C library
573 # 202311 # P2407R5 Freestanding Library: Partial Classes
575 "headers": ["cstring"],
576 "unimplemented": True,
579 "name": "__cpp_lib_freestanding_expected",
580 "values": {
581 "c++26": 202311 # P2833R2 Freestanding Library: inout expected span
583 "headers": ["expected"],
584 "unimplemented": True,
587 "name": "__cpp_lib_freestanding_mdspan",
588 "values": {
589 "c++26": 202311 # P2833R2 Freestanding Library: inout expected span
591 "headers": ["mdspan"],
592 "unimplemented": True,
595 "name": "__cpp_lib_freestanding_optional",
596 "values": {
597 "c++26": 202311 # P2407R5 Freestanding Library: Partial Classes
599 "headers": ["optional"],
600 "unimplemented": True,
603 "name": "__cpp_lib_freestanding_string_view",
604 "values": {
605 "c++26": 202311 # P2407R5 Freestanding Library: Partial Classes
607 "headers": ["string_view"],
608 "unimplemented": True,
611 "name": "__cpp_lib_freestanding_variant",
612 "values": {
613 "c++26": 202311 # P2407R5 Freestanding Library: Partial Classes
615 "headers": ["variant"],
616 "unimplemented": True,
619 "name": "__cpp_lib_fstream_native_handle",
620 "values": {"c++26": 202306}, # P1759R6 Native handles and file streams
621 "headers": ["fstream"],
622 "test_suite_guard": "!defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION)",
623 "libcxx_guard": "_LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION",
626 "name": "__cpp_lib_function_ref",
627 "values": {
628 "c++26": 202306 # P0792R14 function_ref: a type-erased callable reference
630 "headers": ["functional"],
631 "unimplemented": True,
634 "name": "__cpp_lib_gcd_lcm",
635 "values": {"c++17": 201606},
636 "headers": ["numeric"],
639 "name": "__cpp_lib_generate_random",
640 "values": {"c++26": 202403}, # P1068R11: Vector API for random number generation
641 "headers": ["random"],
642 "unimplemented": True,
645 "name": "__cpp_lib_generic_associative_lookup",
646 "values": {"c++14": 201304},
647 "headers": ["map", "set"],
650 "name": "__cpp_lib_generic_unordered_lookup",
651 "values": {"c++20": 201811},
652 "headers": ["unordered_map", "unordered_set"],
655 "name": "__cpp_lib_hardware_interference_size",
656 "values": {"c++17": 201703},
657 "test_suite_guard": "!defined(_LIBCPP_VERSION) || (defined(__GCC_DESTRUCTIVE_SIZE) && defined(__GCC_CONSTRUCTIVE_SIZE))",
658 "libcxx_guard": "defined(__GCC_DESTRUCTIVE_SIZE) && defined(__GCC_CONSTRUCTIVE_SIZE)",
659 "headers": ["new"],
662 "name": "__cpp_lib_has_unique_object_representations",
663 "values": {"c++17": 201606},
664 "headers": ["type_traits"],
667 "name": "__cpp_lib_hazard_pointer",
668 "values": {"c++26": 202306}, # P2530R3 Hazard Pointers for C++26
669 "headers": [
670 "hazard_pointer" # TODO verify this entry since the paper was underspecified.
672 "unimplemented": True,
675 "name": "__cpp_lib_hypot",
676 "values": {"c++17": 201603},
677 "headers": ["cmath"],
680 "name": "__cpp_lib_incomplete_container_elements",
681 "values": {"c++17": 201505},
682 "headers": ["forward_list", "list", "vector"],
685 "name": "__cpp_lib_inplace_vector",
686 "values": {"c++26": 202406}, # P0843R14 inplace_vector
687 "headers": ["inplace_vector"],
688 "unimplemented": True,
691 "name": "__cpp_lib_int_pow2",
692 "values": {"c++20": 202002},
693 "headers": ["bit"],
696 "name": "__cpp_lib_integer_comparison_functions",
697 "values": {"c++20": 202002},
698 "headers": ["utility"],
701 "name": "__cpp_lib_integer_sequence",
702 "values": {"c++14": 201304},
703 "headers": ["utility"],
706 "name": "__cpp_lib_integral_constant_callable",
707 "values": {"c++14": 201304},
708 "headers": ["type_traits"],
711 "name": "__cpp_lib_interpolate",
712 "values": {"c++20": 201902},
713 "headers": ["cmath", "numeric"],
716 "name": "__cpp_lib_invoke",
717 "values": {"c++17": 201411},
718 "headers": ["functional"],
721 "name": "__cpp_lib_invoke_r",
722 "values": {"c++23": 202106},
723 "headers": ["functional"],
726 "name": "__cpp_lib_ios_noreplace",
727 "values": {"c++23": 202207},
728 "headers": ["ios"],
731 "name": "__cpp_lib_is_aggregate",
732 "values": {"c++17": 201703},
733 "headers": ["type_traits"],
736 "name": "__cpp_lib_is_constant_evaluated",
737 "values": {"c++20": 201811},
738 "headers": ["type_traits"],
741 "name": "__cpp_lib_is_final",
742 "values": {"c++14": 201402},
743 "headers": ["type_traits"],
746 "name": "__cpp_lib_is_implicit_lifetime",
747 "values": {"c++23": 202302},
748 "headers": ["type_traits"],
749 "test_suite_guard": "__has_builtin(__builtin_is_implicit_lifetime)",
750 "libcxx_guard": "__has_builtin(__builtin_is_implicit_lifetime)",
753 "name": "__cpp_lib_is_invocable",
754 "values": {"c++17": 201703},
755 "headers": ["type_traits"],
758 "name": "__cpp_lib_is_layout_compatible",
759 "values": {"c++20": 201907},
760 "headers": ["type_traits"],
761 "unimplemented": True,
764 "name": "__cpp_lib_is_nothrow_convertible",
765 "values": {"c++20": 201806},
766 "headers": ["type_traits"],
769 "name": "__cpp_lib_is_null_pointer",
770 "values": {"c++14": 201309},
771 "headers": ["type_traits"],
774 "name": "__cpp_lib_is_pointer_interconvertible",
775 "values": {"c++20": 201907},
776 "headers": ["type_traits"],
777 "unimplemented": True,
780 "name": "__cpp_lib_is_scoped_enum",
781 "values": {"c++23": 202011},
782 "headers": ["type_traits"],
785 "name": "__cpp_lib_is_swappable",
786 "values": {"c++17": 201603},
787 "headers": ["type_traits"],
790 "name": "__cpp_lib_is_virtual_base_of",
791 "values": {
792 "c++26": 202406 # P2985R0 A type trait for detecting virtual base classes
794 "headers": ["type_traits"],
795 "test_suite_guard": "__has_builtin(__builtin_is_virtual_base_of)",
796 "libcxx_guard": "__has_builtin(__builtin_is_virtual_base_of)",
799 "name": "__cpp_lib_is_within_lifetime",
800 # Note this name was changed from "__cpp_lib_within_lifetime" when the paper was adopted
801 # https://github.com/cplusplus/draft/commit/0facada4cadd97e1ba15bfaea76a804f1dc5c309
802 "values": {
803 "c++26": 202306 # P2641R4 Checking if a union alternative is active
805 "headers": ["type_traits"],
806 "unimplemented": True,
809 "name": "__cpp_lib_jthread",
810 "values": {"c++20": 201911},
811 "headers": ["stop_token", "thread"],
812 "test_suite_guard": "!defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC)",
813 "libcxx_guard": "_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC",
816 "name": "__cpp_lib_latch",
817 "values": {"c++20": 201907},
818 "headers": ["latch"],
819 "test_suite_guard": "!defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC)",
820 "libcxx_guard": "_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC",
823 "name": "__cpp_lib_launder",
824 "values": {"c++17": 201606},
825 "headers": ["new"],
828 "name": "__cpp_lib_linalg",
829 "values": {
830 "c++26": 202311 # P1673 A free function linear algebra interface based on the BLAS
832 "headers": ["linalg"],
833 "unimplemented": True,
836 "name": "__cpp_lib_list_remove_return_type",
837 "values": {"c++20": 201806},
838 "headers": ["forward_list", "list"],
841 "name": "__cpp_lib_logical_traits",
842 "values": {"c++17": 201510},
843 "headers": ["type_traits"],
846 "name": "__cpp_lib_make_from_tuple",
847 "values": {"c++17": 201606},
848 "headers": ["tuple"],
851 "name": "__cpp_lib_make_reverse_iterator",
852 "values": {"c++14": 201402},
853 "headers": ["iterator"],
856 "name": "__cpp_lib_make_unique",
857 "values": {"c++14": 201304},
858 "headers": ["memory"],
861 "name": "__cpp_lib_map_try_emplace",
862 "values": {"c++17": 201411},
863 "headers": ["map"],
866 "name": "__cpp_lib_math_constants",
867 "values": {"c++20": 201907},
868 "headers": ["numbers"],
871 "name": "__cpp_lib_math_special_functions",
872 "values": {"c++17": 201603},
873 "headers": ["cmath"],
874 "unimplemented": True,
877 "name": "__cpp_lib_mdspan",
878 "values": {
879 "c++23": 202207,
880 "c++26": 202406, # P2389R2 dextents Index Type Parameter
882 "headers": ["mdspan"],
885 "name": "__cpp_lib_memory_resource",
886 "values": {"c++17": 201603},
887 "headers": ["memory_resource"],
888 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_PMR",
889 "libcxx_guard": "_LIBCPP_AVAILABILITY_HAS_PMR",
892 "name": "__cpp_lib_modules",
893 "values": {"c++23": 202207},
894 "headers": [],
897 "name": "__cpp_lib_move_iterator_concept",
898 "values": {"c++20": 202207},
899 "headers": ["iterator"],
902 "name": "__cpp_lib_move_only_function",
903 "values": {"c++23": 202110},
904 "headers": ["functional"],
905 "unimplemented": True,
908 "name": "__cpp_lib_node_extract",
909 "values": {"c++17": 201606},
910 "headers": ["map", "set", "unordered_map", "unordered_set"],
913 "name": "__cpp_lib_nonmember_container_access",
914 "values": {"c++17": 201411},
915 "headers": [
916 "array",
917 "deque",
918 "forward_list",
919 "iterator",
920 "list",
921 "map",
922 "regex",
923 "set",
924 "string",
925 "unordered_map",
926 "unordered_set",
927 "vector",
931 "name": "__cpp_lib_not_fn",
932 "values": {
933 "c++17": 201603,
934 # "c++26": 202306, # P2714R1 Bind front and back to NTTP callables
936 "headers": ["functional"],
939 "name": "__cpp_lib_null_iterators",
940 "values": {"c++14": 201304},
941 "headers": ["iterator"],
944 "name": "__cpp_lib_optional",
945 "values": {
946 "c++17": 201606,
947 "c++20": 202106, # P2231R1 Missing constexpr in std::optional and std::variant
948 "c++23": 202110, # P0798R8 Monadic operations for std::optional + LWG3621 Remove feature-test macro __cpp_lib_monadic_optional
950 "headers": ["optional"],
953 "name": "__cpp_lib_optional_range_support",
954 "values": {"c++26": 202406}, # P3168R2 Give std::optional Range Support
955 "headers": ["optional"],
956 "unimplemented": True,
959 "name": "__cpp_lib_out_ptr",
960 "values": {
961 "c++23": 202106,
962 "c++26": 202311, # P2833R2 Freestanding Library: inout expected span
964 "headers": ["memory"],
967 "name": "__cpp_lib_parallel_algorithm",
968 "values": {"c++17": 201603},
969 "headers": ["algorithm", "numeric"],
970 "unimplemented": True,
973 "name": "__cpp_lib_philox_engine",
974 "values": {
975 "c++26": 202406
976 }, # P2075R6 Philox as an extension of the C++ RNG engines
977 # Note the paper mentions 202310L as value, which differs from the typical procedure.
978 "headers": ["random"],
979 "unimplemented": True,
982 "name": "__cpp_lib_polymorphic_allocator",
983 "values": {"c++20": 201902},
984 "headers": ["memory_resource"],
985 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_PMR",
986 "libcxx_guard": "_LIBCPP_AVAILABILITY_HAS_PMR",
989 "name": "__cpp_lib_print",
990 "values": {
991 "c++23": 202207,
992 # "c++26": 202403, # P3107R5: Permit an efficient implementation of std::print
993 # "c++26": 202406, # P3235R3 std::print more types faster with less memory
995 "headers": ["ostream", "print"],
998 "name": "__cpp_lib_quoted_string_io",
999 "values": {"c++14": 201304},
1000 "headers": ["iomanip"],
1001 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_LOCALIZATION",
1002 "libcxx_guard": "_LIBCPP_HAS_LOCALIZATION",
1005 "name": "__cpp_lib_ranges",
1006 "values": {
1007 "c++20": 202110, # P2415R2 What is a view?
1008 "c++23": 202406, # P2997R1 Removing the common reference requirement from the indirectly invocable concepts (implemented as a DR against C++20)
1010 "headers": ["algorithm", "functional", "iterator", "memory", "ranges"],
1013 "name": "__cpp_lib_ranges_as_const",
1014 "values": {
1015 "c++23": 202207 # P2278R4 cbegin should always return a constant iterator
1016 # 202311 # DR P2836R1 std::basic_const_iterator should follow its underlying type’s convertibility
1018 "headers": ["ranges"],
1019 "unimplemented": True,
1022 "name": "__cpp_lib_ranges_as_rvalue",
1023 "values": {"c++23": 202207},
1024 "headers": ["ranges"],
1027 "name": "__cpp_lib_ranges_chunk",
1028 "values": {"c++23": 202202},
1029 "headers": ["ranges"],
1030 "unimplemented": True,
1033 "name": "__cpp_lib_ranges_chunk_by",
1034 "values": {"c++23": 202202},
1035 "headers": ["ranges"],
1038 "name": "__cpp_lib_ranges_concat",
1039 "values": {"c++26": 202403}, # P2542R8: views::concat
1040 "headers": ["ranges"],
1041 "unimplemented": True,
1044 "name": "__cpp_lib_ranges_contains",
1045 "values": {"c++23": 202207},
1046 "headers": ["algorithm"],
1049 "name": "__cpp_lib_ranges_find_last",
1050 "values": {"c++23": 202207},
1051 "headers": ["algorithm"],
1054 "name": "__cpp_lib_ranges_iota",
1055 "values": {"c++23": 202202},
1056 "headers": ["numeric"],
1057 "unimplemented": True,
1060 "name": "__cpp_lib_ranges_join_with",
1061 "values": {"c++23": 202202},
1062 "headers": ["ranges"],
1063 "unimplemented": True,
1066 "name": "__cpp_lib_ranges_repeat",
1067 "values": {"c++23": 202207},
1068 "headers": ["ranges"],
1071 "name": "__cpp_lib_ranges_slide",
1072 "values": {"c++23": 202202},
1073 "headers": ["ranges"],
1074 "unimplemented": True,
1077 "name": "__cpp_lib_ranges_starts_ends_with",
1078 "values": {"c++23": 202106},
1079 "headers": ["algorithm"],
1082 "name": "__cpp_lib_ranges_to_container",
1083 "values": {"c++23": 202202},
1084 "headers": ["ranges"],
1087 "name": "__cpp_lib_ranges_zip",
1088 "values": {"c++23": 202110},
1089 "headers": ["ranges", "tuple", "utility"],
1090 "unimplemented": True,
1093 "name": "__cpp_lib_ratio",
1094 "values": {"c++26": 202306}, # P2734R0 Adding the new SI prefixes
1095 "headers": ["ratio"],
1098 "name": "__cpp_lib_raw_memory_algorithms",
1099 "values": {"c++17": 201606},
1100 "headers": ["memory"],
1103 "name": "__cpp_lib_rcu",
1104 "values": {"c++26": 202306}, # P2545R4 Read-Copy Update (RCU)
1105 "headers": [
1106 "rcu" # TODO verify this entry since the paper was underspecified.
1108 "unimplemented": True,
1111 "name": "__cpp_lib_reference_from_temporary",
1112 "values": {"c++23": 202202},
1113 "headers": ["type_traits"],
1114 "unimplemented": True,
1117 "name": "__cpp_lib_reference_wrapper",
1118 "values": {"c++26": 202403}, # P2944R3: Comparisons for reference_wrapper
1119 "headers": ["functional"],
1122 "name": "__cpp_lib_remove_cvref",
1123 "values": {"c++20": 201711},
1124 "headers": ["type_traits"],
1127 "name": "__cpp_lib_result_of_sfinae",
1128 "values": {"c++14": 201210},
1129 "headers": ["functional", "type_traits"],
1132 "name": "__cpp_lib_robust_nonmodifying_seq_ops",
1133 "values": {"c++14": 201304},
1134 "headers": ["algorithm"],
1137 "name": "__cpp_lib_sample",
1138 "values": {"c++17": 201603},
1139 "headers": ["algorithm"],
1142 "name": "__cpp_lib_saturation_arithmetic",
1143 "values": {"c++26": 202311}, # P0543R3 Saturation arithmetic
1144 "headers": ["numeric"],
1147 "name": "__cpp_lib_scoped_lock",
1148 "values": {"c++17": 201703},
1149 "headers": ["mutex"],
1150 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_THREADS",
1151 "libcxx_guard": "_LIBCPP_HAS_THREADS",
1154 "name": "__cpp_lib_semaphore",
1155 "values": {"c++20": 201907},
1156 "headers": ["semaphore"],
1157 "test_suite_guard": "!defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC)",
1158 "libcxx_guard": "_LIBCPP_HAS_THREADS && _LIBCPP_AVAILABILITY_HAS_SYNC",
1161 "name": "__cpp_lib_senders",
1162 "values": {"c++26": 202406}, # P2300R10 std::execution
1163 "headers": ["execution"],
1164 "unimplemented": True,
1167 "name": "__cpp_lib_shared_mutex",
1168 "values": {"c++17": 201505},
1169 "headers": ["shared_mutex"],
1170 "test_suite_guard": "_LIBCPP_HAS_THREADS",
1171 "libcxx_guard": "_LIBCPP_HAS_THREADS",
1174 "name": "__cpp_lib_shared_ptr_arrays",
1175 "values": {"c++17": 201611, "c++20": 201707},
1176 "headers": ["memory"],
1179 "name": "__cpp_lib_shared_ptr_weak_type",
1180 "values": {"c++17": 201606},
1181 "headers": ["memory"],
1184 "name": "__cpp_lib_shared_timed_mutex",
1185 "values": {"c++14": 201402},
1186 "headers": ["shared_mutex"],
1187 "test_suite_guard": "_LIBCPP_HAS_THREADS",
1188 "libcxx_guard": "_LIBCPP_HAS_THREADS",
1191 "name": "__cpp_lib_shift",
1192 "values": {"c++20": 201806},
1193 "headers": ["algorithm"],
1196 "name": "__cpp_lib_smart_ptr_for_overwrite",
1197 "values": {"c++20": 202002},
1198 "headers": ["memory"],
1201 "name": "__cpp_lib_smart_ptr_owner_equality",
1202 "values": {
1203 "c++26": 202306 # P1901R2 Enabling the Use of weak_ptr as Keys in Unordered Associative Containers
1205 "headers": ["memory"],
1206 "unimplemented": True,
1209 "name": "__cpp_lib_source_location",
1210 "values": {"c++20": 201907},
1211 "headers": ["source_location"],
1214 "name": "__cpp_lib_span",
1215 "values": {
1216 "c++20": 202002,
1217 # "c++26": 202311, # P2821R5 span.at()
1218 # 202311 # P2833R2 Freestanding Library: inout expected span
1220 "headers": ["span"],
1223 "name": "__cpp_lib_span_at",
1224 "values": {"c++26": 202311}, # P2821R3 span.at()
1225 "headers": ["span"],
1228 "name": "__cpp_lib_span_initializer_list",
1229 "values": {"c++26": 202311}, # P2447R6 std::span over an initializer list
1230 "headers": ["span"],
1233 "name": "__cpp_lib_spanstream",
1234 "values": {"c++23": 202106},
1235 "headers": ["spanstream"],
1236 "unimplemented": True,
1239 "name": "__cpp_lib_ssize",
1240 "values": {"c++20": 201902},
1241 "headers": ["iterator"],
1244 "name": "__cpp_lib_sstream_from_string_view",
1245 "values": {
1246 "c++26": 202306 # P2495R3 Interfacing stringstreams with string_view
1248 "headers": ["sstream"],
1251 "name": "__cpp_lib_stacktrace",
1252 "values": {"c++23": 202011},
1253 "headers": ["stacktrace"],
1254 "unimplemented": True,
1257 "name": "__cpp_lib_starts_ends_with",
1258 "values": {"c++20": 201711},
1259 "headers": ["string", "string_view"],
1262 "name": "__cpp_lib_stdatomic_h",
1263 "values": {"c++23": 202011},
1264 "headers": ["stdatomic.h"],
1267 "name": "__cpp_lib_string_contains",
1268 "values": {"c++23": 202011},
1269 "headers": ["string", "string_view"],
1272 "name": "__cpp_lib_string_resize_and_overwrite",
1273 "values": {"c++23": 202110},
1274 "headers": ["string"],
1277 "name": "__cpp_lib_string_udls",
1278 "values": {"c++14": 201304},
1279 "headers": ["string"],
1282 "name": "__cpp_lib_string_view",
1283 "values": {
1284 "c++17": 201606,
1285 "c++20": 201803,
1286 "c++26": 202403, # P2591R5: Concatenation of strings and string views
1288 "headers": ["string", "string_view"],
1291 "name": "__cpp_lib_submdspan",
1292 "values": {
1293 "c++26": 202306, # P2630R4: submdspan
1294 # "c++26": 202403, # P2642R6: Padded mdspan layouts
1296 "headers": ["mdspan"],
1297 "unimplemented": True,
1300 "name": "__cpp_lib_syncbuf",
1301 "values": {"c++20": 201803},
1302 "headers": ["syncstream"],
1303 "test_suite_guard": "!defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM)",
1304 "libcxx_guard": "!defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM)",
1307 "name": "__cpp_lib_text_encoding",
1308 "values": {
1309 "c++26": 202306 # P1885R12 Naming Text Encodings to Demystify Them
1311 "headers": ["text_encoding"],
1312 "unimplemented": True,
1315 "name": "__cpp_lib_three_way_comparison",
1316 "values": {"c++20": 201907},
1317 "headers": ["compare"],
1320 "name": "__cpp_lib_to_address",
1321 "values": {"c++20": 201711},
1322 "headers": ["memory"],
1325 "name": "__cpp_lib_to_array",
1326 "values": {"c++20": 201907},
1327 "headers": ["array"],
1330 "name": "__cpp_lib_to_chars",
1331 "values": {
1332 "c++17": 201611,
1333 "c++26": 202306, # P2497R0 Testing for success or failure of <charconv> functions
1335 "headers": ["charconv"],
1336 "unimplemented": True,
1339 "name": "__cpp_lib_to_string",
1340 "values": {"c++26": 202306}, # P2587R3 to_string or not to_string
1341 "headers": ["string"],
1342 "unimplemented": True,
1345 "name": "__cpp_lib_to_underlying",
1346 "values": {"c++23": 202102},
1347 "headers": ["utility"],
1350 "name": "__cpp_lib_transformation_trait_aliases",
1351 "values": {"c++14": 201304},
1352 "headers": ["type_traits"],
1355 "name": "__cpp_lib_transparent_operators",
1356 "values": {"c++14": 201210, "c++17": 201510},
1357 "headers": ["functional", "memory"],
1360 "name": "__cpp_lib_tuple_element_t",
1361 "values": {"c++14": 201402},
1362 "headers": ["tuple"],
1365 "name": "__cpp_lib_tuple_like",
1366 "values": {
1367 "c++23": 202207, # P2165R4 Compatibility between tuple, pair and tuple-like objects
1368 "c++26": 202311, # P2819R2 Add tuple protocol to complex (implemented)
1370 "headers": ["map", "tuple", "unordered_map", "utility"],
1371 "unimplemented": True,
1374 "name": "__cpp_lib_tuples_by_type",
1375 "values": {"c++14": 201304},
1376 "headers": ["tuple", "utility"],
1379 "name": "__cpp_lib_type_identity",
1380 "values": {"c++20": 201806},
1381 "headers": ["type_traits"],
1384 "name": "__cpp_lib_type_trait_variable_templates",
1385 "values": {"c++17": 201510},
1386 "headers": ["type_traits"],
1389 "name": "__cpp_lib_uncaught_exceptions",
1390 "values": {"c++17": 201411},
1391 "headers": ["exception"],
1394 "name": "__cpp_lib_unordered_map_try_emplace",
1395 "values": {"c++17": 201411},
1396 "headers": ["unordered_map"],
1399 "name": "__cpp_lib_unreachable",
1400 "values": {"c++23": 202202},
1401 "headers": ["utility"],
1404 "name": "__cpp_lib_unwrap_ref",
1405 "values": {"c++20": 201811},
1406 "headers": ["functional"],
1409 "name": "__cpp_lib_variant",
1410 "values": {
1411 "c++17": 202102, # std::visit for classes derived from std::variant
1412 "c++20": 202106, # P2231R1 Missing constexpr in std::optional and std::variant
1413 "c++26": 202306, # P2637R3 Member visit
1415 "headers": ["variant"],
1418 "name": "__cpp_lib_void_t",
1419 "values": {"c++17": 201411},
1420 "headers": ["type_traits"],
1425 assert feature_test_macros == sorted(feature_test_macros, key=lambda tc: tc["name"])
1426 for tc in feature_test_macros:
1427 assert tc["headers"] == sorted(tc["headers"]), tc
1428 assert ("libcxx_guard" in tc) == ("test_suite_guard" in tc), tc
1429 valid_keys = ["name", "values", "headers", "libcxx_guard", "test_suite_guard", "unimplemented"]
1430 assert all(key in valid_keys for key in tc.keys()), tc
1432 # Map from each header to the Lit annotations that should be used for
1433 # tests that include that header.
1435 # For example, when threads are not supported, any test that includes
1436 # <thread> should be marked as UNSUPPORTED, because including <thread>
1437 # is a hard error in that case.
1438 lit_markup = {
1439 "barrier": ["UNSUPPORTED: no-threads"],
1440 "filesystem": ["UNSUPPORTED: no-filesystem"],
1441 "fstream": ["UNSUPPORTED: no-localization"],
1442 "iomanip": ["UNSUPPORTED: no-localization"],
1443 "ios": ["UNSUPPORTED: no-localization"],
1444 "iostream": ["UNSUPPORTED: no-localization"],
1445 "istream": ["UNSUPPORTED: no-localization"],
1446 "latch": ["UNSUPPORTED: no-threads"],
1447 "locale": ["UNSUPPORTED: no-localization"],
1448 "mutex": ["UNSUPPORTED: no-threads"],
1449 "ostream": ["UNSUPPORTED: no-localization"],
1450 "print": ["UNSUPPORTED: no-filesystem"],
1451 "regex": ["UNSUPPORTED: no-localization"],
1452 "semaphore": ["UNSUPPORTED: no-threads"],
1453 "shared_mutex": ["UNSUPPORTED: no-threads"],
1454 "sstream": ["UNSUPPORTED: no-localization"],
1455 "syncstream": ["UNSUPPORTED: no-localization"],
1456 "stdatomic.h": ["UNSUPPORTED: no-threads"],
1457 "stop_token": ["UNSUPPORTED: no-threads"],
1458 "thread": ["UNSUPPORTED: no-threads"],
1462 def get_std_dialects():
1463 std_dialects = ["c++14", "c++17", "c++20", "c++23", "c++26"]
1464 return list(std_dialects)
1467 def get_first_std(d):
1468 for s in get_std_dialects():
1469 if s in d.keys():
1470 return s
1471 return None
1474 def get_last_std(d):
1475 rev_dialects = get_std_dialects()
1476 rev_dialects.reverse()
1477 for s in rev_dialects:
1478 if s in d.keys():
1479 return s
1480 return None
1483 def get_std_before(d, std):
1484 std_dialects = get_std_dialects()
1485 candidates = std_dialects[0 : std_dialects.index(std)]
1486 candidates.reverse()
1487 for cand in candidates:
1488 if cand in d.keys():
1489 return cand
1490 return None
1493 def get_value_before(d, std):
1494 new_std = get_std_before(d, std)
1495 if new_std is None:
1496 return None
1497 return d[new_std]
1500 def get_for_std(d, std):
1501 # This catches the C++11 case for which there should be no defined feature
1502 # test macros.
1503 std_dialects = get_std_dialects()
1504 if std not in std_dialects:
1505 return None
1506 # Find the value for the newest C++ dialect between C++14 and std
1507 std_list = list(std_dialects[0 : std_dialects.index(std) + 1])
1508 std_list.reverse()
1509 for s in std_list:
1510 if s in d.keys():
1511 return d[s]
1512 return None
1515 def get_std_number(std):
1516 return std.replace("c++", "")
1520 Functions to produce the <version> header
1524 def produce_macros_definition_for_std(std):
1525 result = ""
1526 indent = 55
1527 for tc in feature_test_macros:
1528 if std not in tc["values"]:
1529 continue
1530 inner_indent = 1
1531 if "test_suite_guard" in tc.keys():
1532 result += "# if %s\n" % tc["libcxx_guard"]
1533 inner_indent += 2
1534 if get_value_before(tc["values"], std) is not None:
1535 assert "test_suite_guard" not in tc.keys()
1536 result += "# undef %s\n" % tc["name"]
1537 line = "#%sdefine %s" % ((" " * inner_indent), tc["name"])
1538 line += " " * (indent - len(line))
1539 line += " %sL" % tc["values"][std]
1540 if "unimplemented" in tc.keys():
1541 line = "// " + line
1542 result += line
1543 result += "\n"
1544 if "test_suite_guard" in tc.keys():
1545 result += "# endif\n"
1546 return result.strip()
1549 def produce_macros_definitions():
1550 macro_definition_template = """#if _LIBCPP_STD_VER >= {std_number}
1551 {macro_definition}
1552 #endif"""
1554 macros_definitions = []
1555 for std in get_std_dialects():
1556 macros_definitions.append(
1557 macro_definition_template.format(
1558 std_number=get_std_number(std),
1559 macro_definition=produce_macros_definition_for_std(std),
1563 return "\n\n".join(macros_definitions)
1566 def chunks(l, n):
1567 """Yield successive n-sized chunks from l."""
1568 for i in range(0, len(l), n):
1569 yield l[i : i + n]
1572 def produce_version_synopsis():
1573 indent = 56
1574 header_indent = 56 + len("20XXYYL ")
1575 result = ""
1577 def indent_to(s, val):
1578 if len(s) >= val:
1579 return s
1580 s += " " * (val - len(s))
1581 return s
1583 line = indent_to("Macro name", indent) + "Value"
1584 line = indent_to(line, header_indent) + "Headers"
1585 result += line + "\n"
1586 for tc in feature_test_macros:
1587 prev_defined_std = get_last_std(tc["values"])
1588 line = "{name: <{indent}}{value}L ".format(
1589 name=tc["name"], indent=indent, value=tc["values"][prev_defined_std]
1591 headers = list(tc["headers"])
1592 headers.remove("version")
1593 for chunk in chunks(headers, 3):
1594 line = indent_to(line, header_indent)
1595 chunk = ["<%s>" % header for header in chunk]
1596 line += " ".join(chunk)
1597 result += line
1598 result += "\n"
1599 line = ""
1600 while True:
1601 prev_defined_std = get_std_before(tc["values"], prev_defined_std)
1602 if prev_defined_std is None:
1603 break
1604 result += "%s%sL // %s\n" % (
1605 indent_to("", indent),
1606 tc["values"][prev_defined_std],
1607 prev_defined_std.replace("c++", "C++"),
1609 return result
1612 def produce_version_header():
1613 template = """// -*- C++ -*-
1614 //===----------------------------------------------------------------------===//
1616 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
1617 // See https://llvm.org/LICENSE.txt for license information.
1618 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
1620 //===----------------------------------------------------------------------===//
1622 #ifndef _LIBCPP_VERSIONH
1623 #define _LIBCPP_VERSIONH
1626 version synopsis
1628 {synopsis}
1632 #include <__config>
1634 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
1635 # pragma GCC system_header
1636 #endif
1638 // clang-format off
1640 {cxx_macros}
1642 // clang-format on
1644 #endif // _LIBCPP_VERSIONH
1647 version_str = template.format(
1648 synopsis=produce_version_synopsis().strip(),
1649 cxx_macros=produce_macros_definitions(),
1651 version_header_path = os.path.join(include_path, "version")
1652 with open(version_header_path, "w", newline="\n") as f:
1653 f.write(version_str)
1657 Functions to produce test files
1660 test_types = {
1661 "undefined": """
1662 # ifdef {name}
1663 # error "{name} should not be defined before {std_first}"
1664 # endif
1665 """,
1666 "test_suite_guard": """
1667 # if {test_suite_guard}
1668 # ifndef {name}
1669 # error "{name} should be defined in {std}"
1670 # endif
1671 # if {name} != {value}
1672 # error "{name} should have the value {value} in {std}"
1673 # endif
1674 # else
1675 # ifdef {name}
1676 # error "{name} should not be defined when the requirement '{test_suite_guard}' is not met!"
1677 # endif
1678 # endif
1679 """,
1680 "unimplemented": """
1681 # if !defined(_LIBCPP_VERSION)
1682 # ifndef {name}
1683 # error "{name} should be defined in {std}"
1684 # endif
1685 # if {name} != {value}
1686 # error "{name} should have the value {value} in {std}"
1687 # endif
1688 # else // _LIBCPP_VERSION
1689 # ifdef {name}
1690 # error "{name} should not be defined because it is unimplemented in libc++!"
1691 # endif
1692 # endif
1693 """,
1694 "defined": """
1695 # ifndef {name}
1696 # error "{name} should be defined in {std}"
1697 # endif
1698 # if {name} != {value}
1699 # error "{name} should have the value {value} in {std}"
1700 # endif
1701 """,
1705 def generate_std_test(test_list, std):
1706 result = ""
1707 for tc in test_list:
1708 val = get_for_std(tc["values"], std)
1709 if val is not None:
1710 val = "%sL" % val
1711 if val is None:
1712 result += test_types["undefined"].format(
1713 name=tc["name"], std_first=get_first_std(tc["values"])
1715 elif "unimplemented" in tc.keys():
1716 result += test_types["unimplemented"].format(
1717 name=tc["name"], value=val, std=std
1719 elif "test_suite_guard" in tc.keys():
1720 result += test_types["test_suite_guard"].format(
1721 name=tc["name"],
1722 value=val,
1723 std=std,
1724 test_suite_guard=tc["test_suite_guard"],
1726 else:
1727 result += test_types["defined"].format(name=tc["name"], value=val, std=std)
1728 return result.strip()
1731 def generate_std_tests(test_list):
1732 std_tests_template = """#if TEST_STD_VER < {first_std_number}
1734 {pre_std_test}
1736 {other_std_tests}
1738 #elif TEST_STD_VER > {penultimate_std_number}
1740 {last_std_test}
1742 #endif // TEST_STD_VER > {penultimate_std_number}"""
1744 std_dialects = get_std_dialects()
1746 other_std_tests = []
1747 for std in std_dialects[:-1]:
1748 other_std_tests.append("#elif TEST_STD_VER == " + get_std_number(std))
1749 other_std_tests.append(generate_std_test(test_list, std))
1751 std_tests = std_tests_template.format(
1752 first_std_number=get_std_number(std_dialects[0]),
1753 pre_std_test=generate_std_test(test_list, "c++11"),
1754 other_std_tests="\n\n".join(other_std_tests),
1755 penultimate_std_number=get_std_number(std_dialects[-2]),
1756 last_std_test=generate_std_test(test_list, std_dialects[-1]),
1759 return std_tests
1762 def generate_synopsis(test_list):
1763 max_name_len = max([len(tc["name"]) for tc in test_list])
1764 indent = max_name_len + 8
1766 def mk_line(prefix, suffix):
1767 return "{prefix: <{max_len}}{suffix}\n".format(
1768 prefix=prefix, suffix=suffix, max_len=indent
1771 result = ""
1772 result += mk_line("/* Constant", "Value")
1773 for tc in test_list:
1774 prefix = " %s" % tc["name"]
1775 for std in [s for s in get_std_dialects() if s in tc["values"].keys()]:
1776 result += mk_line(
1777 prefix, "%sL [%s]" % (tc["values"][std], std.replace("c++", "C++"))
1779 prefix = ""
1780 result += "*/"
1781 return result
1784 def produce_tests():
1785 headers = set([h for tc in feature_test_macros for h in tc["headers"]])
1786 for h in headers:
1787 test_list = [tc for tc in feature_test_macros if h in tc["headers"]]
1788 if not has_header(h):
1789 for tc in test_list:
1790 assert "unimplemented" in tc.keys()
1791 continue
1792 markup = "\n".join("// " + tag for tag in lit_markup.get(h, []))
1793 test_body = """//===----------------------------------------------------------------------===//
1795 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
1796 // See https://llvm.org/LICENSE.txt for license information.
1797 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
1799 //===----------------------------------------------------------------------===//
1801 // WARNING: This test was generated by {script_name}
1802 // and should not be edited manually.
1804 // clang-format off
1805 {markup}
1806 // <{header}>
1808 // Test the feature test macros defined by <{header}>
1810 {synopsis}
1812 #include <{header}>
1813 #include "test_macros.h"
1815 {cxx_tests}
1817 """.format(
1818 script_name=script_name,
1819 header=h,
1820 markup=("\n{}\n".format(markup) if markup else ""),
1821 synopsis=generate_synopsis(test_list),
1822 cxx_tests=generate_std_tests(test_list),
1824 test_name = "{header}.version.compile.pass.cpp".format(header=h)
1825 out_path = os.path.join(macro_test_path, test_name)
1826 with open(out_path, "w", newline="\n") as f:
1827 f.write(test_body)
1831 Produce documentation for the feature test macros
1835 def make_widths(grid):
1836 widths = []
1837 for i in range(0, len(grid[0])):
1838 cell_width = 2 + max(
1839 reduce(lambda x, y: x + y, [[len(row[i])] for row in grid], [])
1841 widths += [cell_width]
1842 return widths
1845 def create_table(grid, indent):
1846 indent_str = " " * indent
1847 col_widths = make_widths(grid)
1848 result = [indent_str + add_divider(col_widths, 2)]
1849 header_flag = 2
1850 for row_i in range(0, len(grid)):
1851 row = grid[row_i]
1852 line = indent_str + " ".join(
1853 [pad_cell(row[i], col_widths[i]) for i in range(0, len(row))]
1855 result.append(line.rstrip())
1856 if row_i == len(grid) - 1:
1857 header_flag = 2
1858 if row[0].startswith("**"):
1859 header_flag += 1
1860 separator = indent_str + add_divider(col_widths, header_flag)
1861 result.append(separator.rstrip())
1862 header_flag = 0
1863 return "\n".join(result)
1866 def add_divider(widths, header_flag):
1867 if header_flag == 3:
1868 return "=".join(["=" * w for w in widths])
1869 if header_flag == 2:
1870 return " ".join(["=" * w for w in widths])
1871 if header_flag == 1:
1872 return "-".join(["-" * w for w in widths])
1873 else:
1874 return " ".join(["-" * w for w in widths])
1877 def pad_cell(s, length, left_align=True):
1878 padding = (length - len(s)) * " "
1879 return s + padding
1882 def get_status_table():
1883 table = [["Macro Name", "Value"]]
1884 for std in get_std_dialects():
1885 table += [["**" + std.replace("c++", "C++") + "**", ""]]
1886 for tc in feature_test_macros:
1887 if std not in tc["values"].keys():
1888 continue
1889 value = "``%sL``" % tc["values"][std]
1890 if "unimplemented" in tc.keys():
1891 value = "*unimplemented*"
1892 table += [["``%s``" % tc["name"], value]]
1893 return table
1896 def produce_docs():
1897 doc_str = """.. _FeatureTestMacroTable:
1899 ==========================
1900 Feature Test Macro Support
1901 ==========================
1903 .. contents::
1904 :local:
1906 Overview
1907 ========
1909 This file documents the feature test macros currently supported by libc++.
1911 .. _feature-status:
1913 Status
1914 ======
1916 .. table:: Current Status
1917 :name: feature-status-table
1918 :widths: auto
1920 {status_tables}
1922 """.format(
1923 status_tables=create_table(get_status_table(), 4)
1926 table_doc_path = os.path.join(docs_path, "FeatureTestMacroTable.rst")
1927 with open(table_doc_path, "w", newline="\n") as f:
1928 f.write(doc_str)
1931 def get_ftms(
1932 data, std_dialects: List[str], use_implemented_status: bool
1933 ) -> Dict[str, Dict[str, Any]]:
1934 """Impementation for FeatureTestMacros.(standard|implemented)_ftms()."""
1935 result = dict()
1936 for feature in data:
1937 last = None
1938 entry = dict()
1939 implemented = True
1940 for std in std_dialects:
1941 if std not in feature["values"].keys():
1942 if last == None:
1943 continue
1944 else:
1945 entry[std] = last
1946 else:
1947 if implemented:
1948 values = feature["values"][std]
1949 assert len(values) > 0, f"{feature['name']}[{std}] has no entries"
1950 for value in values:
1951 papers = list(values[value])
1952 assert (
1953 len(papers) > 0
1954 ), f"{feature['name']}[{std}][{value}] has no entries"
1955 for paper in papers:
1956 if use_implemented_status and not paper["implemented"]:
1957 implemented = False
1958 break
1959 if implemented:
1960 last = f"{value}L"
1961 else:
1962 break
1964 entry[std] = last
1965 result[feature["name"]] = entry
1967 return result
1970 def generate_version_header_dialect_block(data: Dict[str, Any]) -> str:
1971 """Generates the contents of the version header for a dialect.
1973 This generates the contents of a
1974 #if _LIBCPP_STD_VER >= XY
1975 #endif // _LIBCPP_STD_VER >= XY
1976 block.
1978 result = ""
1979 for element in data:
1980 for ftm, entry in element.items():
1981 if not entry["implemented"]:
1982 # When a FTM is not implemented don't add the guards
1983 # or undefine the (possibly) defined macro.
1984 result += f'// define {ftm} {entry["value"]}\n'
1985 else:
1986 need_undef = entry["need_undef"]
1987 if entry["condition"]:
1988 result += f'# if {entry["condition"]}\n'
1989 if entry["need_undef"]:
1990 result += f"# undef {ftm}\n"
1991 result += f'# define {ftm} {entry["value"]}\n'
1992 result += f"# endif\n"
1993 else:
1994 if entry["need_undef"]:
1995 result += f"# undef {ftm}\n"
1996 result += f'# define {ftm} {entry["value"]}\n'
1998 return result
2001 def generate_version_header_implementation(data: Dict[str, Dict[str, Any]]) -> str:
2002 """Generates the body of the version header."""
2004 template = """#if _LIBCPP_STD_VER >= {dialect}
2005 {feature_test_macros}#endif // _LIBCPP_STD_VER >= {dialect}"""
2007 result = []
2008 for std, ftms in data.items():
2009 result.append(
2010 template.format(
2011 dialect=std,
2012 feature_test_macros=generate_version_header_dialect_block(ftms),
2016 return "\n\n".join(result)
2019 class FeatureTestMacros:
2020 """Provides all feature-test macro (FTM) output components.
2022 The class has several generators to use the feature-test macros in libc++:
2023 - FTM status page
2024 - The version header and its tests
2026 This class is not intended to duplicate
2027 https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations#library-feature-test-macros
2028 SD-FeatureTest: Feature-Test Macros and Policies
2030 Historically libc++ did not list all papers affecting a FTM, the new data
2031 structure is able to do that. However there is no intention to add the
2032 historical data. After papers have been implemented this information can be
2033 removed. For example, __cpp_lib_format's value 201907 requires 3 papers,
2034 once implemented it can be reduced to 1 paper and remove the paper number
2035 and title. This would reduce the size of the data.
2037 The input data is stored in the following JSON format:
2038 [ # A list with multiple feature-test macro entries.
2040 # required
2041 # The name of the feature test macro. These names should be unique and
2042 # sorted in the list.
2043 "name": "__cpp_lib_any",
2045 # required
2046 # A map with the value of the FTM based on the language standard. Only
2047 # the versions in which the value of the FTM changes are listed. For
2048 # example, this macro's value does not change in C++20 so it does not
2049 # list C++20. If it changes in C++26, it will have entries for C++17 and
2050 # C++26.
2051 "values": {
2053 # required
2054 # The language standard, also named dialect in this class.
2055 "c++17": {
2057 # required
2058 # The value of the feature test macro. This contains an array with
2059 # one or more papers that need to be implemented before this value
2060 # is considered implemented.
2061 "201606": [
2063 # optional
2064 # Contains the paper number that is part of the FTM version.
2065 "number": "P0220R1",
2067 # optional
2068 # Contains the title of the paper that is part of the FTM
2069 # version.
2070 "title": "Adopt Library Fundamentals V1 TS Components for C++17"
2072 # required
2073 # The implementation status of the paper.
2074 "implemented": true
2080 # required
2081 # A sorted list of headers that should provide the FTM. The header
2082 # <version> is automatically added to this list. This list could be
2083 # empty. For example, __cpp_lib_modules is only present in version.
2084 # Requiring the field makes it easier to detect accidental omission.
2085 "headers": [
2086 "any"
2089 # optional, required when libcxx_guard is present
2090 # This field is used only to generate the unit tests for the
2091 # feature-test macros. It can't depend on macros defined in <__config>
2092 # because the `test/std/` parts of the test suite are intended to be
2093 # portable to any C++ standard library implementation, not just libc++.
2094 # It may depend on
2095 # * macros defined by the compiler itself, or
2096 # * macros generated by CMake.
2097 # In some cases we add also depend on macros defined in
2098 # <__availability>.
2099 "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_PMR"
2101 # optional, required when test_suite_guard is present
2102 # This field is used only to guard the feature-test macro in
2103 # <version>. It may be the same as `test_suite_guard`, or it may
2104 # depend on macros defined in <__config>.
2105 "libcxx_guard": "_LIBCPP_AVAILABILITY_HAS_PMR"
2110 # The JSON data structure.
2111 __data = None
2113 def __init__(self, filename: str):
2114 """Initializes the class with the JSON data in the file 'filename'."""
2115 with open(filename) as f:
2116 self.__data = json.load(f)
2118 @functools.cached_property
2119 def std_dialects(self) -> List[str]:
2120 """Returns the C++ dialects avaiable.
2122 The available dialects are based on the 'c++xy' keys found the 'values'
2123 entries in '__data'. So when WG21 starts to feature-test macros for a
2124 future C++ Standard this dialect will automatically be available.
2126 The return value is a sorted list with the C++ dialects used. Since FTM
2127 were added in C++14 the list will not contain C++03 or C++11.
2129 dialects = set()
2130 for feature in self.__data:
2131 keys = feature["values"].keys()
2132 assert len(keys) > 0, "'values' is empty"
2133 dialects |= keys
2135 return sorted(list(dialects))
2137 @functools.cached_property
2138 def standard_ftms(self) -> Dict[str, Dict[str, Any]]:
2139 """Returns the FTM versions per dialect in the Standard.
2141 This function does not use the 'implemented' flag. The output contains
2142 the versions used in the Standard. When a FTM in libc++ is not
2143 implemented according to the Standard to output may opt to show the
2144 expected value.
2146 The result is a dict with the following content
2147 - key: Name of the feature test macro.
2148 - value: A dict with the following content:
2149 * key: The version of the C++ dialect.
2150 * value: The value of the feature-test macro.
2152 return get_ftms(self.__data, self.std_dialects, False)
2154 @functools.cached_property
2155 def implemented_ftms(self) -> Dict[str, Dict[str, Any]]:
2156 """Returns the FTM versions per dialect implemented in libc++.
2158 Unlike `get_std_dialect_versions` this function uses the 'implemented'
2159 flag. This returns the actual implementation status in libc++.
2161 The result is a dict with the following content
2162 - key: Name of the feature test macro.
2163 - value: A dict with the following content:
2164 * key: The version of the C++ dialect.
2165 * value: The value of the feature-test macro. When a feature-test
2166 macro is not implemented its value is None.
2169 return get_ftms(self.__data, self.std_dialects, True)
2171 @functools.cached_property
2172 def ftm_metadata(self) -> Dict[str, Dict[str, Any]]:
2173 """Returns the metadata of the FTMs defined in the Standard.
2175 The metadata does not depend on the C++ dialect used.
2176 The result is a dict with the following contents:
2177 - key: Name of the feature test macro.
2178 - value: A dict with the following content:
2179 * headers: The list of headers that should provide the FTM
2180 * test_suite_guard: The condition for testing the FTM in the test suite.
2181 * test_suite_guard: The condition for testing the FTM in the version header.
2183 result = dict()
2184 for feature in self.__data:
2185 entry = dict()
2186 entry["headers"] = feature["headers"]
2187 entry["test_suite_guard"] = feature.get("test_suite_guard", None)
2188 entry["libcxx_guard"] = feature.get("libcxx_guard", None)
2189 result[feature["name"]] = entry
2191 return result
2193 @property
2194 def version_header_implementation(self) -> Dict[str, List[Dict[str, Any]]]:
2195 """Generates the body of the version header."""
2196 result = dict()
2197 for std in self.std_dialects:
2198 result[get_std_number(std)] = list()
2200 for ftm, values in self.standard_ftms.items():
2201 last_value = None
2202 last_entry = None
2203 for std, value in values.items():
2204 # When a newer Standard does not change the value of the macro
2205 # there is no need to redefine it with the same value.
2206 if last_value and value == last_value:
2207 continue
2208 last_value = value
2210 entry = dict()
2211 entry["value"] = value
2212 entry["implemented"] = self.implemented_ftms[ftm][std] == self.standard_ftms[ftm][std]
2213 entry["need_undef"] = last_entry is not None and last_entry["implemented"] and entry["implemented"]
2214 entry["condition"] = self.ftm_metadata[ftm]["libcxx_guard"]
2216 last_entry = entry
2217 result[get_std_number(std)].append(dict({ftm: entry}))
2219 return result
2221 @property
2222 def version_header(self) -> str:
2223 """Generates the version header."""
2224 template = """// -*- C++ -*-
2225 //===----------------------------------------------------------------------===//
2227 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
2228 // See https://llvm.org/LICENSE.txt for license information.
2229 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2231 //===----------------------------------------------------------------------===//
2233 #ifndef _LIBCPP_VERSION
2234 #define _LIBCPP_VERSION
2236 #include <__config>
2238 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
2239 # pragma GCC system_header
2240 #endif
2242 {feature_test_macros}
2244 #endif // _LIBCPP_VERSION
2246 return template.format(
2247 feature_test_macros=generate_version_header_implementation(
2248 self.version_header_implementation
2253 def main():
2254 produce_version_header()
2255 produce_tests()
2256 produce_docs()
2258 # Example how to use the new version header generation function to generate
2259 # the file.
2260 if False:
2261 ftm = FeatureTestMacros(
2262 os.path.join(
2263 source_root, "test", "libcxx", "feature_test_macro", "test_data.json"
2266 version_header_path = os.path.join(include_path, "version")
2267 with open(version_header_path, "w", newline="\n") as f:
2268 f.write(ftm.version_header)
2271 if __name__ == "__main__":
2272 main()